diff --git a/README.md b/README.md
index fc0bbc2..adfafd7 100644
--- a/README.md
+++ b/README.md
@@ -1,5 +1,7 @@
# Android (Kotlin) SDK for manipulating GPX files
+![](https://img.shields.io/maven-central/v/me.bvn13.sdk.android.gpx/GpxAndroidSdk)
+
## About
This is another one SDK for reading and writing (manipulating) GPX files.
@@ -8,6 +10,12 @@ Official GPX format is on [topografix](https://www.topografix.com/GPX/1/1/) site
## Changelog
+### 2023-02-13
+
+1) Fixed missed extensions
+2) ✅ Tested on reading with self written content
+3) ✅ Tested on reading [OsmAnd](https://osmand.net) GPX files
+
### 2022-12-18
1) implemented GPX format reader
diff --git a/pom.xml b/pom.xml
index 176d6b4..dc30e95 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
GpxAndroidSdk
me.bvn13.sdk.android.gpx
- 1.7
+ 1.8
jar
diff --git a/src/main/kotlin/me/bvn13/sdk/android/gpx/ExtensionType.kt b/src/main/kotlin/me/bvn13/sdk/android/gpx/ExtensionType.kt
index fa6dc89..236cb9a 100644
--- a/src/main/kotlin/me/bvn13/sdk/android/gpx/ExtensionType.kt
+++ b/src/main/kotlin/me/bvn13/sdk/android/gpx/ExtensionType.kt
@@ -92,10 +92,13 @@ package me.bvn13.sdk.android.gpx
*
* [parameters] Map of key-value pairs
*/
-class ExtensionType(val nodeName: String, val value: String? = null, val parameters: Map? = null) {
+class ExtensionType(val nodeName: String,
+ val value: String? = null,
+ val parameters: Map? = null,
+ val nested: List? = null) {
init {
- require(value != null || parameters != null) {
- "value or parameters must be specified"
+ require(value != null || parameters != null || nested != null) {
+ "value or parameters or nesting elements must be specified for ${nodeName}"
}
}
@@ -108,6 +111,7 @@ class ExtensionType(val nodeName: String, val value: String? = null, val paramet
if (nodeName != other.nodeName) return false
if (value != other.value) return false
if (parameters != other.parameters) return false
+ if (nested != other.nested) return false
return true
}
@@ -116,8 +120,7 @@ class ExtensionType(val nodeName: String, val value: String? = null, val paramet
var result = nodeName.hashCode()
result = 31 * result + (value?.hashCode() ?: 0)
result = 31 * result + (parameters?.hashCode() ?: 0)
+ result = 31 * result + (nested?.hashCode() ?: 0)
return result
}
-
-
}
diff --git a/src/main/kotlin/me/bvn13/sdk/android/gpx/GpxConstant.kt b/src/main/kotlin/me/bvn13/sdk/android/gpx/GpxConstant.kt
index dd1d716..4ee1377 100644
--- a/src/main/kotlin/me/bvn13/sdk/android/gpx/GpxConstant.kt
+++ b/src/main/kotlin/me/bvn13/sdk/android/gpx/GpxConstant.kt
@@ -6,6 +6,7 @@ import java.time.format.DateTimeFormatterBuilder
class GpxConstant {
companion object {
const val HEADER = ""
+ const val HEADER_EXTENDED = ""
const val VERSION = "1.1"
val DTF =
DateTimeFormatterBuilder().append(ISO_LOCAL_DATE_TIME) // use the existing formatter for date time
diff --git a/src/main/kotlin/me/bvn13/sdk/android/gpx/GpxReader.kt b/src/main/kotlin/me/bvn13/sdk/android/gpx/GpxReader.kt
index 83f1b31..1687ca6 100644
--- a/src/main/kotlin/me/bvn13/sdk/android/gpx/GpxReader.kt
+++ b/src/main/kotlin/me/bvn13/sdk/android/gpx/GpxReader.kt
@@ -3,6 +3,7 @@ package me.bvn13.sdk.android.gpx
import me.bvn13.sdk.android.gpx.GpxConstant.Companion.DTF
import java.io.InputStream
import java.time.OffsetDateTime
+import java.util.stream.Collectors
fun GpxType.Companion.read(dis: InputStream) = GpxReader().read(dis)
@@ -28,7 +29,13 @@ class GpxReader {
private fun readSignature(dis: InputStream, buffer: Container): GpxType {
val container = readUntil(dis, buffer, '\n')
- if ("${GpxConstant.HEADER}\n" != container.buffer.asString()) {
+ val signaturePrepared = container.buffer.asString()
+ .trim()
+ .replace("'", "\"")
+ .replace(" ?", "?")
+ if (GpxConstant.HEADER != signaturePrepared
+ && GpxConstant.HEADER_EXTENDED != signaturePrepared
+ ) {
throw IllegalArgumentException("Wrong xml signature!")
}
return readBeginning(dis, Container.empty(container.position))
@@ -59,10 +66,12 @@ class GpxReader {
return findObject(container.objects, "gpx").let {
return GpxType(
metadata = assembleMetadataType(it.nested),
- creator = findAttributeOrNull(it.attributes, "creator") ?: throw IllegalArgumentException("Gpx.Creator not found"),
- wpt = findObjectsOrNull(it.nested ,"wpt")?.map { assembleWptType(it) },
+ creator = findAttributeOrNull(it.attributes, "creator")
+ ?: throw IllegalArgumentException("Gpx.Creator not found"),
+ wpt = findObjectsOrNull(it.nested, "wpt")?.map { assembleWptType(it) },
rte = findObjectsOrNull(it.nested, "rte")?.map { assembleRteType(it) },
- trk = findObjectsOrNull(it.nested, "trk")?.map { assembleTrkType(it) }
+ trk = findObjectsOrNull(it.nested, "trk")?.map { assembleTrkType(it) },
+ extensions = findObjectOrNull(it.nested, "extensions")?.let { assembleExtensionType(it) }
)
}
}
@@ -71,10 +80,13 @@ class GpxReader {
findObject(objects, "metadata")
.let {
return MetadataType(
- name = findObjectOrNull(it.nested, "name")?.value ?: throw IllegalArgumentException("Gpx.Metadata.Name not found"),
- description = findObjectOrNull(it.nested, "desc")?.value ?: throw IllegalArgumentException("Gpx.Metadata.Description not found"),
- authorName = findObject(it.nested, "author").let { author ->
- findObject(author.nested, "name").value ?: throw IllegalArgumentException("Gpx.Metadata.Author.Name not found")
+ name = findObjectOrNull(it.nested, "name")?.value
+ ?: throw IllegalArgumentException("Gpx.Metadata.Name not found"),
+ description = findObjectOrNull(it.nested, "desc")?.value
+ ?: "",
+ authorName = findObjectOrNull(it.nested, "author").let { author ->
+ findObjectOrNull(author?.nested, "name")?.value
+ ?: ""
}
)
}
@@ -123,7 +135,10 @@ class GpxReader {
cmt = findObjectOrNull(obj.nested, "cmt")?.value,
desc = findObjectOrNull(obj.nested, "desc")?.value,
src = findObjectOrNull(obj.nested, "src")?.value,
- link = findObjectsOrNull(obj.nested, "link")?.let { list -> if (list.isNotEmpty()) list.map { assembleLinkType(it) } else null },
+ link = findObjectsOrNull(
+ obj.nested,
+ "link"
+ )?.let { list -> if (list.isNotEmpty()) list.map { assembleLinkType(it) } else null },
number = findObjectOrNull(obj.nested, "number")?.value?.toInt(),
type = findObjectOrNull(obj.nested, "type")?.value,
extensions = findObjectOrNull(obj.nested, "extensions")?.let { assembleExtensionType(it) },
@@ -140,12 +155,17 @@ class GpxReader {
private fun assembleFixType(value: String): FixType =
FixType.valueOf(value.uppercase())
- private fun assembleExtensionType(obj: XmlObject): ExtensionType =
- ExtensionType(
+ private fun assembleExtensionType(obj: XmlObject): ExtensionType {
+ val nested: List? = obj.nested?.stream()
+ ?.map { assembleExtensionType(it) }
+ ?.collect(Collectors.toList())
+ return ExtensionType(
nodeName = obj.type,
value = if (obj.value == "") null else obj.value,
- parameters = if (obj.attributes.isEmpty()) null else obj.attributes.toSortedMap()
- )
+ parameters = if (obj.attributes.isEmpty()) null else obj.attributes.toSortedMap(),
+ nested = nested
+ )
+ }
private fun assembleTrksegType(obj: XmlObject): TrksegType =
TrksegType(
@@ -186,14 +206,16 @@ class GpxReader {
container = readAttributes(dis, container)
xmlObject.attributes = container.attributes
}
- container = readSkipping(dis, container, SKIPPING_SET)
- if (container.byte!!.toInt() == '<'.code) {
- container = readNestedObjects(dis, container)
- xmlObject.nested = container.objects
- container = readFinishingTag(dis, container, tagName)
- }
- if (container.buffer.asString() != "$tagName>") {
- container = readValue(dis, container, tagName)
+ if (!container.isShortClosing) {
+ container = readSkipping(dis, container, SKIPPING_SET)
+ if (container.byte!!.toInt() == '<'.code) {
+ container = readNestedObjects(dis, container)
+ xmlObject.nested = container.objects
+ container = readFinishingTag(dis, container, tagName)
+ }
+ if (container.buffer.asString() != "$tagName>") {
+ container = readValue(dis, container, tagName)
+ }
}
xmlObject.value = container.buffer.asString().replace("$tagName>", "")
container.objects = listOf(xmlObject)
@@ -221,8 +243,12 @@ class GpxReader {
return container
}
- private fun readFinishingTag(dis: InputStream, buffer: Container, tagName: String): Container =
- readExactly(dis, buffer, "${tagName}>")
+ private fun readFinishingTag(dis: InputStream, buffer: Container, tagName: String): Container {
+ if (buffer.isShortClosing) {
+ return buffer
+ }
+ return readExactly(dis, buffer, "${tagName}>")
+ }
private fun readValue(dis: InputStream, buffer: Container, tagName: String): Container =
readUntil(dis, buffer, "$tagName>")
@@ -236,8 +262,10 @@ class GpxReader {
do {
attributeContainer = readAttribute(dis, attributeContainer)
attributes.putAll(attributeContainer.attributes)
- } while (attributeContainer.attributes.isNotEmpty() && attributeContainer.byte!!.toInt() != '>'.code)
- val result = Container.emptyWithAttributes(attributeContainer.position, attributes)
+ } while (attributeContainer.attributes.isNotEmpty()
+ && attributeContainer.byte!!.toInt() != '>'.code
+ )
+ val result = Container.emptyWithAttributes(attributeContainer.position, attributes, attributeContainer.isShortClosing)
return result
}
@@ -250,8 +278,17 @@ class GpxReader {
val valueAsString = valueContainer.buffer.asString()
val value = valueAsString.substring(0, valueAsString.length - 1)
val result = Container.empty(valueContainer.position)
- val closingContainer = readSkipping(dis, result, SKIPPING_SET)
- val nextBlockContainer = Container.of(closingContainer.byte!!, closingContainer.position)
+ var closingContainer = readSkipping(dis, result, SKIPPING_SET)
+ var isShortClosing = false
+ if (closingContainer.byte!!.toInt() == '/'.code) {
+ val endingContainer = readByte(dis, closingContainer)
+ if (endingContainer.byte!!.toInt() != '>'.code) {
+ throw IllegalArgumentException("There must be valid closing tag at ${endingContainer.position}")
+ }
+ closingContainer = endingContainer
+ isShortClosing = true
+ }
+ val nextBlockContainer = Container.of(closingContainer.byte!!, closingContainer.position, isShortClosing)
nextBlockContainer.attributes = mapOf(name to value)
return nextBlockContainer
}
@@ -321,28 +358,42 @@ class GpxReader {
private fun readByte(dis: InputStream, container: Container): Container {
val ba = ByteArray(1);
if (-1 == dis.read(ba, 0, 1)) {
- throw InterruptedException("EOF")
+ throw InterruptedException("EOF at ${container.position}\nUnparsed data: " + String(container.buffer))
} else if (ba.size != 1) {
- throw InterruptedException("Reading of 1 byte returns ${ba.size} bytes")
+ throw InterruptedException("Reading of 1 byte returns ${ba.size} bytes at ${container.position}")
}
return Container(container.position + 1, ba[0], container.buffer.plus(ba))
}
- class Container(val position: Long = 0, val byte: Byte?, val buffer: ByteArray) {
+ class Container(
+ val position: Long = 0,
+ val byte: Byte?,
+ val buffer: ByteArray,
+ val isShortClosing: Boolean = false
+ ) {
var objects: List? = null
var attributes: Map = HashMap()
var value: String? = null
companion object {
fun empty(): Container = empty(0)
- fun empty(position: Long) = Container(position, null, ByteArray(0))
- fun emptyWithAttributes(position: Long, attributes: Map): Container {
- val container = Container.empty(position)
+ fun empty(position: Long, isShortClosing: Boolean = false) = Container(position, null, ByteArray(0), isShortClosing)
+ fun emptyWithAttributes(position: Long, attributes: Map, isShortClosing: Boolean = false): Container {
+ val container = Container.empty(position, isShortClosing)
container.attributes = attributes
return container
}
- fun of(b: Byte, position: Long) = Container(position, b, ByteArray(1) { _ -> b })
+ fun of(b: Byte, position: Long, isShortClosing: Boolean = false) =
+ Container(position, b, ByteArray(1) { _ -> b }, isShortClosing)
+
+ fun isShortClosing(c: Container, buffer: ByteArray): Container {
+ val container = Container(c.position, c.byte, buffer, isShortClosing = true)
+ container.objects = c.objects
+ container.attributes = c.attributes
+ container.value = c.value
+ return container
+ }
}
override fun toString(): String = this.buffer.asString()
diff --git a/src/main/kotlin/me/bvn13/sdk/android/gpx/GpxWriter.kt b/src/main/kotlin/me/bvn13/sdk/android/gpx/GpxWriter.kt
index 6160f32..a4005e9 100644
--- a/src/main/kotlin/me/bvn13/sdk/android/gpx/GpxWriter.kt
+++ b/src/main/kotlin/me/bvn13/sdk/android/gpx/GpxWriter.kt
@@ -100,6 +100,7 @@ fun GpxType.toXmlString(clock: Clock?): String = """
${this.wpt?.toXmlString() ?: ""}
${this.rte?.toXmlString() ?: ""}
${this.trk?.toXmlString() ?: ""}
+ ${this.extensions?.toXmlString() ?: ""}
""".trim().removeEmptyStrings()
@@ -140,7 +141,7 @@ fun WptType.toXmlString(nodeName: String) = """
${toXmlString(pdop, "pdop")}
${toXmlString(ageofgpsdata, "ageofgpsdata")}
${toXmlString(dgpsid, "dgpsid")}
- ${extensions?.toXmlString() ?: ""}
+ ${extensions?.toXmlString(true) ?: ""}
${nodeName}>
""".trim().removeEmptyStrings()
@@ -153,7 +154,7 @@ fun RteType.toXmlString() = """
${this.link?.toXmlString() ?: ""}
${toXmlString(this.number, "number")}
${toXmlString(this.type, "type")}
- ${this.extensions?.toXmlString() ?: ""}
+ ${this.extensions?.toXmlString(true) ?: ""}
${this.rtept?.toXmlString("rtept") ?: ""}
""".trim().removeEmptyStrings()
@@ -183,13 +184,21 @@ fun FixType.toXmlString() = """
${this.value}
""".trim().removeEmptyStrings()
-fun ExtensionType.toXmlString() = """
+fun ExtensionType.toXmlString() =
+ if ((this.nested?.size ?: 0) > 0) {
+ """
+ <${this.nodeName}${toXmlString(this.parameters)}>${this.nested?.toXmlString() ?: ""}${this.nodeName}>
+ """.trim().removeEmptyStrings()
+ } else {
+ """
<${this.nodeName}${toXmlString(this.parameters)}>${this.value ?: ""}${this.nodeName}>
""".trim().removeEmptyStrings()
+ }
fun TrksegType.toXmlString() = """
${this.trkpt?.toXmlString("trkpt") ?: ""}
+ ${this.extensions?.toXmlString() ?: ""}
""".trim().removeEmptyStrings()
@@ -197,9 +206,17 @@ fun List.toXmlString(nodeName: String?) = this.joinToString(prefix = "\
it.toXmlString(nodeName)
}
-fun List.toXmlString() = this.joinToString(
- prefix = "\n", postfix = "\n", separator = "\n", transform = ExtensionType::toXmlString
-)
+fun List.toXmlString(inGroup: Boolean = false): String {
+ if (inGroup) {
+ return this.joinToString(
+ prefix = "\n", postfix = "\n", separator = "\n", transform = ExtensionType::toXmlString
+ )
+ } else {
+ return this.joinToString(
+ prefix = "\n", postfix = "\n", separator = "\n", transform = ExtensionType::toXmlString
+ )
+ }
+}
@JvmName("toXmlStringWptType")
fun List.toXmlString() = this.joinToString(prefix = "", postfix = "", separator = "") {
diff --git a/src/test/kotlin/me/bvn13/sdk/android/gpx/GpxReaderTest.kt b/src/test/kotlin/me/bvn13/sdk/android/gpx/GpxReaderTest.kt
index a63057d..6c253f9 100644
--- a/src/test/kotlin/me/bvn13/sdk/android/gpx/GpxReaderTest.kt
+++ b/src/test/kotlin/me/bvn13/sdk/android/gpx/GpxReaderTest.kt
@@ -1,5 +1,6 @@
package me.bvn13.sdk.android.gpx
+import org.junit.jupiter.api.Assertions
import org.junit.jupiter.api.DisplayName
import org.junit.jupiter.api.Test
import java.time.*
@@ -409,4 +410,16 @@ class GpxReaderTest {
val gpx = GpxType.read(gpxString.byteInputStream())
assertEquals(gpxType, gpx)
}
+
+ @DisplayName("Read test.gpx (generated in OsmAnd Android application")
+ @Test
+ fun readTestGpx() {
+ val gpxType = GpxType.read(javaClass.classLoader.getResource("test.gpx").openStream())
+ Assertions.assertEquals(1011, gpxType.trk?.get(0)?.trkseg?.get(0)?.trkpt?.size ?: 0)
+ Assertions.assertEquals(1, gpxType.trk?.get(0)?.trkseg?.get(0)?.trkpt?.get(0)?.extensions?.size ?: 0)
+ Assertions.assertEquals(2, gpxType.trk?.get(0)?.trkseg?.get(0)?.extensions?.nested?.size ?: 0)
+ Assertions.assertEquals(223, gpxType.trk?.get(0)?.trkseg?.get(0)?.extensions?.nested?.get(0)?.nested?.size ?: 0)
+ Assertions.assertEquals(159, gpxType.trk?.get(0)?.trkseg?.get(0)?.extensions?.nested?.get(1)?.nested?.size ?: 0)
+ Assertions.assertEquals(1, gpxType.extensions?.nested?.size ?: 0)
+ }
}
\ No newline at end of file
diff --git a/src/test/kotlin/me/bvn13/sdk/android/gpx/ReadWriteTest.kt b/src/test/kotlin/me/bvn13/sdk/android/gpx/ReadWriteTest.kt
new file mode 100644
index 0000000..9600ed5
--- /dev/null
+++ b/src/test/kotlin/me/bvn13/sdk/android/gpx/ReadWriteTest.kt
@@ -0,0 +1,243 @@
+package me.bvn13.sdk.android.gpx
+
+import org.junit.jupiter.api.Assertions
+import org.junit.jupiter.api.DisplayName
+import org.junit.jupiter.api.Test
+import java.io.ByteArrayInputStream
+import java.time.*
+
+class ReadWriteTest {
+
+ @DisplayName("Read-Write test")
+ @Test
+ fun testReadWrite() {
+ val clock = Clock.fixed(
+ LocalDateTime.of(2022, 9, 24, 15, 4, 0, 0).toInstant(ZoneOffset.ofHours(3)), ZoneId.of("Europe/Moscow")
+ )
+
+ val gpxType = GpxType(
+ MetadataType("test name", description = "test description", authorName = "bvn13"),
+ wpt = listOf(
+ WptType(
+ lat = 14.64736838389662,
+ lon = 7.93212890625,
+ ele = 10.toDouble(),
+ time = OffsetDateTime.now(clock),
+ magvar = 3.toDouble(),
+ geoidheight = 45.toDouble(),
+ name = "test point 1",
+ cmt = "comment 1",
+ desc = "description of point 1",
+ link = listOf(
+ LinkType(
+ href = "http://link-to.site.href",
+ text = "text",
+ type = "hyperlink"
+ ),
+ LinkType(
+ href = "http://link2-to.site.href",
+ text = "text2",
+ type = "hyperlink2"
+ )
+ ),
+ src = "source 1",
+ sym = "sym 1",
+ type = "type 1",
+ fix = FixType.DGPS,
+ sat = 1,
+ hdop = 55.toDouble(),
+ vdop = 66.toDouble(),
+ pdop = 77.toDouble(),
+ ageofgpsdata = 44,
+ dgpsid = 88,
+ extensions = listOf(
+ ExtensionType(
+ "extension1",
+ parameters = mapOf(Pair("first", "second"), Pair("third", "fours"))
+ ),
+ ExtensionType(
+ "extension2",
+ parameters = mapOf(Pair("aa", "bb"), Pair("cc", "dd"))
+ )
+ )
+ )
+ ),
+ rte = listOf(
+ RteType(
+ name = "rte name",
+ cmt = "cmt",
+ desc = "desc",
+ src = "src",
+ link = listOf(
+ LinkType(
+ href = "https://new.link.rte",
+ text = "new text rte",
+ type = "hyperlink"
+ )
+ ),
+ number = 1234,
+ type = "route",
+ extensions = listOf(
+ ExtensionType(
+ "ext-1",
+ value = "value1"
+ )
+ ),
+ rtept = listOf(
+ WptType(
+ lat = 14.64736838389662,
+ lon = 7.93212890625,
+ ele = 10.toDouble(),
+ time = OffsetDateTime.now(clock),
+ magvar = 3.toDouble(),
+ geoidheight = 45.toDouble(),
+ name = "test point 1",
+ cmt = "comment 1",
+ desc = "description of point 1",
+ link = listOf(
+ LinkType(
+ href = "http://link-to.site.href",
+ text = "text",
+ type = "hyperlink"
+ ),
+ LinkType(
+ href = "http://link2-to.site.href",
+ text = "text2",
+ type = "hyperlink2"
+ )
+ ),
+ src = "source 1",
+ sym = "sym 1",
+ type = "type 1",
+ fix = FixType.DGPS,
+ sat = 1,
+ hdop = 55.toDouble(),
+ vdop = 66.toDouble(),
+ pdop = 77.toDouble(),
+ ageofgpsdata = 44,
+ dgpsid = 88,
+ extensions = listOf(
+ ExtensionType(
+ "extension1",
+ parameters = mapOf(Pair("first", "second"), Pair("third", "fours"))
+ ),
+ ExtensionType(
+ "extension2",
+ parameters = mapOf(Pair("aa", "bb"), Pair("cc", "dd"))
+ )
+ )
+ )
+ )
+ )
+ ),
+ trk = listOf(
+ TrkType(
+ name = "track 1",
+ cmt = "comment track 1",
+ desc = "desc track 1",
+ src = "src track 1",
+ number = 1234,
+ type = "type 1",
+ trkseg = listOf(
+ TrksegType(
+ listOf(
+ WptType(
+ lat = 14.64736838389662,
+ lon = 7.93212890625,
+ ele = 10.toDouble(),
+ time = OffsetDateTime.now(clock),
+ magvar = 3.toDouble(),
+ geoidheight = 45.toDouble(),
+ name = "test point 1",
+ cmt = "comment 1",
+ desc = "description of point 1",
+ link = listOf(
+ LinkType(
+ href = "http://link-to.site.href",
+ text = "text",
+ type = "hyperlink"
+ ),
+ LinkType(
+ href = "http://link2-to.site.href",
+ text = "text2",
+ type = "hyperlink2"
+ )
+ ),
+ src = "source 1",
+ sym = "sym 1",
+ type = "type 1",
+ fix = FixType.DGPS,
+ sat = 1,
+ hdop = 55.toDouble(),
+ vdop = 66.toDouble(),
+ pdop = 77.toDouble(),
+ ageofgpsdata = 44,
+ dgpsid = 88,
+ extensions = listOf(
+ ExtensionType(
+ "extension1",
+ parameters = mapOf(Pair("first", "second"), Pair("third", "fours"))
+ ),
+ ExtensionType(
+ "extension2",
+ parameters = mapOf(Pair("aa", "bb"), Pair("cc", "dd"))
+ )
+ )
+ ),
+ WptType(
+ lat = 14.64736838389662,
+ lon = 7.93212890625,
+ ele = 10.toDouble(),
+ time = OffsetDateTime.now(clock),
+ magvar = 3.toDouble(),
+ geoidheight = 45.toDouble(),
+ name = "test point 1",
+ cmt = "comment 1",
+ desc = "description of point 1",
+ link = listOf(
+ LinkType(
+ href = "http://link-to.site.href",
+ text = "text",
+ type = "hyperlink"
+ ),
+ LinkType(
+ href = "http://link2-to.site.href",
+ text = "text2",
+ type = "hyperlink2"
+ )
+ ),
+ src = "source 1",
+ sym = "sym 1",
+ type = "type 1",
+ fix = FixType.DGPS,
+ sat = 1,
+ hdop = 55.toDouble(),
+ vdop = 66.toDouble(),
+ pdop = 77.toDouble(),
+ ageofgpsdata = 44,
+ dgpsid = 88,
+ extensions = listOf(
+ ExtensionType(
+ "extension1",
+ parameters = mapOf(Pair("first", "second"), Pair("third", "fours"))
+ ),
+ ExtensionType(
+ "extension2",
+ parameters = mapOf(Pair("aa", "bb"), Pair("cc", "dd"))
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+ )
+
+ val gpx = gpxType.toXmlString(clock)
+ val deserializedGpxType = GpxType.read(ByteArrayInputStream(gpx.toByteArray()))
+
+ Assertions.assertEquals(gpxType, deserializedGpxType)
+ }
+
+}
\ No newline at end of file
diff --git a/src/test/resources/test.gpx b/src/test/resources/test.gpx
new file mode 100644
index 0000000..3632d0f
--- /dev/null
+++ b/src/test/resources/test.gpx
@@ -0,0 +1,6459 @@
+
+
+
+
+
+
+
+Тестовская улица (Пресненский район), Пресненский
+Тестовская улица (Пресненский район), Пресненский
+
+
+2022-09-01_15-39_Thu
+
+
+159
+
+3.6
+
+
+
+159.2
+
+3.6
+
+
+
+159
+
+3.6
+
+
+
+160
+
+3.6
+
+
+
+161
+
+6.4
+
+
+
+161
+
+6.4
+
+
+
+161
+
+6.4
+
+
+
+158.8
+
+6.4
+
+
+
+158.5
+
+6.4
+
+
+
+157.2
+
+6.4
+
+
+
+156.5
+
+6.4
+
+
+
+155.8
+
+6.4
+
+
+
+155.6
+
+6.4
+
+
+
+155.5
+
+6.4
+
+
+
+155.2
+
+6.4
+
+
+
+155
+
+6.4
+
+
+
+154.6
+
+6.4
+
+
+
+154.6
+
+6.4
+
+
+
+154.5
+
+6.4
+
+
+
+153.8
+
+6.4
+
+
+
+153.5
+
+6.4
+
+
+
+152.8
+
+6.4
+
+
+
+152.2
+
+6.4
+
+
+
+151.8
+
+6.4
+
+
+
+151.2
+
+6.4
+
+
+
+151
+
+6.4
+
+
+
+150.5
+
+6.4
+
+
+
+149.6
+
+6.4
+
+
+
+149.5
+
+6.4
+
+
+
+147
+
+6.4
+
+
+
+146
+
+6.4
+
+
+
+145.2
+
+4.2
+
+
+
+145
+
+4.2
+
+
+
+144.5
+
+4.2
+
+
+
+144
+
+4.2
+
+
+
+143
+
+4.2
+
+
+
+143
+
+4.2
+
+
+
+142.5
+
+4.2
+
+
+
+141.5
+
+4.2
+
+
+
+141
+
+4.2
+
+
+
+140.2
+
+4.2
+
+
+
+139.5
+
+8.6
+
+
+
+140
+
+8.6
+
+
+
+140.2
+
+8.6
+
+
+
+140
+
+8.6
+
+
+
+139.8
+
+8.6
+
+
+
+139.9
+
+8.6
+
+
+
+140
+
+8.6
+
+
+
+139.8
+
+8.6
+
+
+
+139
+
+4.2
+
+
+
+137
+
+8.6
+
+
+
+136.8
+
+8.6
+
+
+
+136.2
+
+8.6
+
+
+
+136
+
+8.6
+
+
+
+134
+
+8.6
+
+
+
+134
+
+8.6
+
+
+
+134
+
+8.6
+
+
+
+134
+
+8.6
+
+
+
+134
+
+8.6
+
+
+
+
+1.7
+
+
+
+134
+
+8.6
+
+
+
+133.9
+
+8.6
+
+
+
+133.8
+
+8.6
+
+
+
+133.2
+
+8.6
+
+
+
+132.8
+
+8.6
+
+
+
+133
+
+8.6
+
+
+
+133.5
+
+8.6
+
+
+
+134
+
+8.6
+
+
+
+134.8
+
+8.6
+
+
+
+135.2
+
+8.6
+
+
+
+135.5
+
+8.6
+
+
+
+135.4
+
+8.6
+
+
+
+135.3
+
+8.6
+
+
+
+135.3
+
+8.6
+
+
+
+135
+
+8.6
+
+
+
+135.8
+
+8.6
+
+
+
+136
+
+8.6
+
+
+
+137
+
+8.6
+
+
+
+137
+
+8.6
+
+
+
+137
+
+8.6
+
+
+
+137
+
+8.6
+
+
+
+137
+
+8.6
+
+
+
+137
+
+4.2
+
+
+
+137
+
+4.2
+
+
+
+137
+
+4.2
+
+
+
+137
+
+4.2
+
+
+
+137
+
+4.2
+
+
+
+137
+
+4.2
+
+
+
+137
+
+4.2
+
+
+
+137
+
+4.2
+
+
+
+137
+
+4.2
+
+
+
+
+4.2
+
+
+
+
+4.2
+
+
+
+
+4.2
+
+
+
+
+4.2
+
+
+
+
+4.2
+
+
+
+
+4.2
+
+
+
+140
+
+1.4
+
+
+
+139.8
+
+1.4
+
+
+
+
+4.2
+
+
+
+141
+
+4.2
+
+
+
+141
+
+1.4
+
+
+
+141
+
+4.2
+
+
+
+141.1
+
+4.2
+
+
+
+141.2
+
+4.2
+
+
+
+141.5
+
+4.2
+
+
+
+142.2
+
+4.2
+
+
+
+142
+
+4.2
+
+
+
+142.8
+
+4.2
+
+
+
+142.9
+
+6.4
+
+
+
+142.8
+
+6.4
+
+
+
+142.8
+
+6.4
+
+
+
+143
+
+6.4
+
+
+
+143.2
+
+6.4
+
+
+
+143.2
+
+6.4
+
+
+
+144.1
+
+6.4
+
+
+
+144.3
+
+6.4
+
+
+
+144.5
+
+6.4
+
+
+
+144
+
+6.4
+
+
+
+143
+
+6.4
+
+
+
+143.8
+
+6.4
+
+
+
+143.5
+
+6.4
+
+
+
+142.2
+
+6.4
+
+
+
+140.2
+
+6.4
+
+
+
+140
+
+6.4
+
+
+
+139.2
+
+6.4
+
+
+
+139.5
+
+6.4
+
+
+
+139.8
+
+6.4
+
+
+
+140.2
+
+6.4
+
+
+
+140.8
+
+6.4
+
+
+
+144.5
+
+6.4
+
+
+
+145
+
+6.4
+
+
+
+143.3
+
+6.4
+
+
+
+143
+
+6.4
+
+
+
+142.5
+
+6.4
+
+
+
+141.8
+
+6.4
+
+
+
+141.2
+
+6.4
+
+
+
+140.3
+
+6.4
+
+
+
+140
+
+6.4
+
+
+
+140.8
+
+6.4
+
+
+
+140.2
+
+6.4
+
+
+
+141
+
+3.6
+
+
+
+141
+
+3.6
+
+
+
+143.2
+
+3.6
+
+
+
+143.8
+
+4.2
+
+
+
+144
+
+4.2
+
+
+
+144
+
+5.3
+
+
+
+145.5
+
+6.4
+
+
+
+146.2
+
+6.4
+
+
+
+146.8
+
+6.4
+
+
+
+146.9
+
+6.4
+
+
+
+147
+
+6.4
+
+
+
+146.8
+
+6.4
+
+
+
+147
+
+6.4
+
+
+
+147
+
+6.4
+
+
+
+147
+
+6.4
+
+
+
+147
+
+6.4
+
+
+
+147
+
+6.4
+
+
+
+147
+
+6.4
+
+
+
+147
+
+6.4
+
+
+
+147.6
+
+6.4
+
+
+
+147.8
+
+6.4
+
+
+
+147.8
+
+6.4
+
+
+
+147.9
+
+6.4
+
+
+
+147.9
+
+6.4
+
+
+
+148
+
+6.4
+
+
+
+148
+
+6.4
+
+
+
+148
+
+6.4
+
+
+
+148
+
+6.4
+
+
+
+147.7
+
+6.4
+
+
+
+147.6
+
+6.4
+
+
+
+147.5
+
+6.4
+
+
+
+147.5
+
+6.4
+
+
+
+146.5
+
+6.4
+
+
+
+146
+
+6.4
+
+
+
+144
+
+6.4
+
+
+
+143.2
+
+6.4
+
+
+
+142.5
+
+6.4
+
+
+
+141
+
+6.4
+
+
+
+140
+
+3.1
+
+
+
+139
+
+5.3
+
+
+
+137.2
+
+2.8
+
+
+
+136.8
+
+2.8
+
+
+
+136.5
+
+2.8
+
+
+
+136.8
+
+4.2
+
+
+
+136.1
+
+4.2
+
+
+
+
+4.2
+
+
+
+136
+
+4.2
+
+
+
+136.5
+
+4.2
+
+
+
+137
+
+6.4
+
+
+
+139
+
+6.4
+
+
+
+139.5
+
+6.4
+
+
+
+140
+
+6.4
+
+
+
+141.5
+
+6.4
+
+
+
+142
+
+6.4
+
+
+
+144.2
+
+6.4
+
+
+
+145
+
+6.4
+
+
+
+145.1
+
+6.4
+
+
+
+145.2
+
+6.4
+
+
+
+145
+
+5.6
+
+
+
+145.2
+
+5.6
+
+
+
+145.2
+
+5.6
+
+
+
+145
+
+5.6
+
+
+
+145
+
+5.6
+
+
+
+144
+
+5.6
+
+
+
+144.5
+
+5.6
+
+
+
+144.8
+
+5.6
+
+
+
+145.2
+
+5.6
+
+
+
+145
+
+5.6
+
+
+
+144.8
+
+5.6
+
+
+
+144.8
+
+5.6
+
+
+
+144.8
+
+5.6
+
+
+
+144.5
+
+5.6
+
+
+
+144.2
+
+5.6
+
+
+
+142.8
+
+5.6
+
+
+
+143.2
+
+5.6
+
+
+
+143.8
+
+5.6
+
+
+
+144.2
+
+5.6
+
+
+
+144.8
+
+5.6
+
+
+
+144.4
+
+5.6
+
+
+
+144.3
+
+5.6
+
+
+
+144.1
+
+5.6
+
+
+
+144
+
+5.6
+
+
+
+144
+
+5.6
+
+
+
+143.5
+
+5.6
+
+
+
+143
+
+5.6
+
+
+
+142
+
+3.6
+
+
+
+142.2
+
+3.6
+
+
+
+142.1
+
+3.6
+
+
+
+142
+
+3.6
+
+
+
+142
+
+5.3
+
+
+
+141.9
+
+5.3
+
+
+
+141.8
+
+5.3
+
+
+
+140.8
+
+5.3
+
+
+
+139.8
+
+5.3
+
+
+
+138.2
+
+5.3
+
+
+
+137.8
+
+5.3
+
+
+
+137.2
+
+5.3
+
+
+
+136
+
+5.3
+
+
+
+136
+
+5.3
+
+
+
+135.5
+
+5.3
+
+
+
+135
+
+5.3
+
+
+
+135.8
+
+5.3
+
+
+
+136
+
+5.3
+
+
+
+136
+
+5.3
+
+
+
+136
+
+4.2
+
+
+
+135.5
+
+4.2
+
+
+
+135.4
+
+4.2
+
+
+
+135.2
+
+4.2
+
+
+
+135
+
+4.2
+
+
+
+135
+
+4.2
+
+
+
+135.2
+
+4.2
+
+
+
+136
+
+4.2
+
+
+
+137.5
+
+4.2
+
+
+
+137.7
+
+4.2
+
+
+
+137.9
+
+4.2
+
+
+
+138
+
+4.2
+
+
+
+138
+
+4.2
+
+
+
+138.5
+
+4.2
+
+
+
+138.9
+
+4.2
+
+
+
+139
+
+4.2
+
+
+
+138.8
+
+4.2
+
+
+
+138
+
+5.3
+
+
+
+139.1
+
+6.4
+
+
+
+139.2
+
+6.4
+
+
+
+140.2
+
+6.4
+
+
+
+140.5
+
+6.4
+
+
+
+140.8
+
+6.4
+
+
+
+141.2
+
+6.4
+
+
+
+141.2
+
+6.4
+
+
+
+141.5
+
+6.4
+
+
+
+143.6
+
+6.4
+
+
+
+143.9
+
+6.4
+
+
+
+144
+
+6.4
+
+
+
+144.5
+
+6.4
+
+
+
+144.4
+
+6.4
+
+
+
+144.2
+
+6.4
+
+
+
+144
+
+6.4
+
+
+
+143.8
+
+6.4
+
+
+
+144
+
+6.4
+
+
+
+144
+
+6.4
+
+
+
+145
+
+6.4
+
+
+
+146
+
+6.4
+
+
+
+146.8
+
+6.4
+
+
+
+146.9
+
+6.4
+
+
+
+147
+
+6.4
+
+
+
+147
+
+6.4
+
+
+
+147.4
+
+6.4
+
+
+
+147.7
+
+6.4
+
+
+
+147.7
+
+6.4
+
+
+
+147.8
+
+6.4
+
+
+
+148
+
+6.4
+
+
+
+149.2
+
+6.4
+
+
+
+151
+
+6.4
+
+
+
+152.8
+
+6.4
+
+
+
+153
+
+6.4
+
+
+
+153.2
+
+6.4
+
+
+
+153.6
+
+6.4
+
+
+
+153.6
+
+6.4
+
+
+
+153.7
+
+6.4
+
+
+
+153.7
+
+6.4
+
+
+
+153.8
+
+6.4
+
+
+
+154
+
+6.4
+
+
+
+154
+
+6.4
+
+
+
+154.2
+
+6.4
+
+
+
+155
+
+3.1
+
+
+
+155.2
+
+3.1
+
+
+
+155.8
+
+3.1
+
+
+
+156.5
+
+3.1
+
+
+
+158.2
+
+6.7
+
+
+
+158.2
+
+6.7
+
+
+
+158.3
+
+6.7
+
+
+
+158.3
+
+6.7
+
+
+
+158.4
+
+6.7
+
+
+
+158.6
+
+6.7
+
+
+
+158.7
+
+6.7
+
+
+
+158.8
+
+6.7
+
+
+
+158.9
+
+6.7
+
+
+
+158.9
+
+6.7
+
+
+
+159
+
+6.7
+
+
+
+159.1
+
+6.7
+
+
+
+159.2
+
+6.7
+
+
+
+159.7
+
+6.7
+
+
+
+159.8
+
+6.7
+
+
+
+159.8
+
+6.7
+
+
+
+159.8
+
+6.7
+
+
+
+159.9
+
+6.7
+
+
+
+159.9
+
+6.7
+
+
+
+160
+
+6.7
+
+
+
+160.2
+
+6.7
+
+
+
+160.3
+
+6.7
+
+
+
+160.3
+
+6.7
+
+
+
+160.3
+
+6.7
+
+
+
+160.3
+
+6.7
+
+
+
+160.3
+
+6.7
+
+
+
+160.3
+
+6.7
+
+
+
+160.3
+
+6.7
+
+
+
+160.3
+
+6.7
+
+
+
+160.3
+
+6.7
+
+
+
+160.5
+
+6.7
+
+
+
+160.5
+
+6.7
+
+
+
+160.5
+
+6.7
+
+
+
+160.5
+
+6.7
+
+
+
+160.5
+
+6.7
+
+
+
+160.5
+
+6.7
+
+
+
+160.5
+
+6.7
+
+
+
+160.5
+
+6.7
+
+
+
+160.5
+
+6.7
+
+
+
+160.6
+
+6.7
+
+
+
+160.6
+
+6.7
+
+
+
+160.6
+
+6.7
+
+
+
+160.7
+
+6.7
+
+
+
+160.7
+
+6.7
+
+
+
+160.8
+
+6.7
+
+
+
+160.8
+
+6.7
+
+
+
+160.8
+
+6.7
+
+
+
+160.8
+
+6.7
+
+
+
+160.8
+
+6.7
+
+
+
+160.8
+
+6.7
+
+
+
+160.8
+
+6.7
+
+
+
+160.8
+
+6.7
+
+
+
+160.8
+
+6.7
+
+
+
+160.9
+
+6.7
+
+
+
+160.9
+
+6.7
+
+
+
+160.9
+
+6.7
+
+
+
+161
+
+6.7
+
+
+
+161
+
+6.7
+
+
+
+160.9
+
+6.7
+
+
+
+160.8
+
+6.7
+
+
+
+160.7
+
+6.7
+
+
+
+160.6
+
+6.7
+
+
+
+160.4
+
+6.7
+
+
+
+160
+
+6.7
+
+
+
+160.8
+
+6.7
+
+
+
+160.8
+
+6.7
+
+
+
+160.9
+
+6.7
+
+
+
+160.9
+
+6.7
+
+
+
+161
+
+6.7
+
+
+
+160.8
+
+6.7
+
+
+
+160.6
+
+6.7
+
+
+
+160.5
+
+6.7
+
+
+
+160.5
+
+6.7
+
+
+
+160.4
+
+6.7
+
+
+
+160.3
+
+6.7
+
+
+
+160.2
+
+6.7
+
+
+
+160
+
+6.7
+
+
+
+159.8
+
+6.7
+
+
+
+159.7
+
+6.7
+
+
+
+159.6
+
+6.7
+
+
+
+159.5
+
+6.7
+
+
+
+159.8
+
+6.7
+
+
+
+160
+
+6.7
+
+
+
+160.2
+
+6.7
+
+
+
+160
+
+6.7
+
+
+
+160.2
+
+6.7
+
+
+
+160
+
+6.7
+
+
+
+160
+
+6.7
+
+
+
+160.1
+
+6.7
+
+
+
+160.2
+
+6.7
+
+
+
+160.2
+
+6.7
+
+
+
+160.2
+
+6.7
+
+
+
+160.2
+
+6.7
+
+
+
+160.2
+
+6.7
+
+
+
+160.2
+
+6.7
+
+
+
+160.2
+
+6.7
+
+
+
+160.2
+
+6.7
+
+
+
+160.2
+
+6.7
+
+
+
+160.2
+
+6.7
+
+
+
+160.2
+
+6.7
+
+
+
+160.4
+
+6.7
+
+
+
+160.4
+
+6.7
+
+
+
+160.5
+
+6.7
+
+
+
+160.8
+
+6.7
+
+
+
+160.9
+
+6.7
+
+
+
+160.9
+
+6.7
+
+
+
+160.9
+
+6.7
+
+
+
+160.9
+
+6.7
+
+
+
+160.9
+
+6.7
+
+
+
+161
+
+6.7
+
+
+
+161
+
+6.7
+
+
+
+161.2
+
+6.7
+
+
+
+161.5
+
+6.7
+
+
+
+161.8
+
+6.7
+
+
+
+161.8
+
+6.7
+
+
+
+161.9
+
+6.7
+
+
+
+161.9
+
+6.7
+
+
+
+161.9
+
+6.7
+
+
+
+162
+
+6.7
+
+
+
+162.5
+
+4.2
+
+
+
+162.8
+
+4.2
+
+
+
+163
+
+4.2
+
+
+
+163.1
+
+4.2
+
+
+
+163.2
+
+4.2
+
+
+
+163.2
+
+4.2
+
+
+
+163.2
+
+4.2
+
+
+
+163.2
+
+4.2
+
+
+
+163.2
+
+4.2
+
+
+
+164
+
+4.2
+
+
+
+164.8
+
+4.2
+
+
+
+164.5
+
+4.2
+
+
+
+164.2
+
+4.2
+
+
+
+164.1
+
+4.2
+
+
+
+164.9
+
+4.2
+
+
+
+164.9
+
+4.2
+
+
+
+164.9
+
+4.2
+
+
+
+165
+
+4.2
+
+
+
+165
+
+4.2
+
+
+
+164.9
+
+4.2
+
+
+
+164.2
+
+4.2
+
+
+
+164
+
+4.2
+
+
+
+164
+
+4.2
+
+
+
+164
+
+4.2
+
+
+
+163.1
+
+4.2
+
+
+
+163
+
+4.2
+
+
+
+162
+
+4.2
+
+
+
+162.2
+
+4.2
+
+
+
+162.5
+
+4.2
+
+
+
+163
+
+6.4
+
+
+
+163
+
+6.4
+
+
+
+163
+
+4.2
+
+
+
+163
+
+4.2
+
+
+
+163
+
+4.2
+
+
+
+163
+
+6.4
+
+
+
+162
+
+5
+
+
+
+162.1
+
+5
+
+
+
+162.2
+
+5
+
+
+
+163
+
+5
+
+
+
+163.1
+
+5
+
+
+
+164
+
+5
+
+
+
+163.8
+
+5
+
+
+
+163
+
+5
+
+
+
+163.5
+
+5
+
+
+
+164
+
+5
+
+
+
+163.8
+
+5
+
+
+
+163
+
+5
+
+
+
+163.1
+
+5
+
+
+
+163.1
+
+5
+
+
+
+163.2
+
+5
+
+
+
+163.3
+
+5
+
+
+
+163.4
+
+5
+
+
+
+163.8
+
+5
+
+
+
+164
+
+5
+
+
+
+164
+
+5
+
+
+
+164
+
+5
+
+
+
+163
+
+5
+
+
+
+163
+
+4.2
+
+
+
+163
+
+4.2
+
+
+
+163
+
+4.2
+
+
+
+163
+
+4.2
+
+
+
+163
+
+4.2
+
+
+
+163
+
+4.2
+
+
+
+163
+
+4.2
+
+
+
+163
+
+4.2
+
+
+
+163
+
+4.2
+
+
+
+163
+
+4.2
+
+
+
+163
+
+1.4
+
+
+
+
+4.2
+
+
+
+
+4.2
+
+
+
+
+4.2
+
+
+
+
+4.2
+
+
+
+163
+
+1.4
+
+
+
+164
+
+4.2
+
+
+
+164
+
+4.2
+
+
+
+164
+
+4.2
+
+
+
+164
+
+4.2
+
+
+
+164.2
+
+6.4
+
+
+
+163.5
+
+6.4
+
+
+
+163.5
+
+6.4
+
+
+
+163.4
+
+6.4
+
+
+
+163.4
+
+6.4
+
+
+
+163.4
+
+6.4
+
+
+
+163.4
+
+6.4
+
+
+
+163.4
+
+6.4
+
+
+
+163.2
+
+6.4
+
+
+
+163.1
+
+6.4
+
+
+
+163.1
+
+6.4
+
+
+
+163
+
+6.4
+
+
+
+162.5
+
+6.4
+
+
+
+162.3
+
+6.4
+
+
+
+162.2
+
+6.4
+
+
+
+162.1
+
+6.4
+
+
+
+162
+
+3.6
+
+
+
+163
+
+3.6
+
+
+
+163.8
+
+3.6
+
+
+
+164
+
+3.1
+
+
+
+164
+
+4.2
+
+
+
+165.8
+
+4.2
+
+
+
+166.2
+
+4.2
+
+
+
+166.3
+
+4.2
+
+
+
+166
+
+4.2
+
+
+
+166.8
+
+4.2
+
+
+
+165.8
+
+4.2
+
+
+
+165
+
+4.2
+
+
+
+164.9
+
+4.2
+
+
+
+165
+
+4.2
+
+
+
+164.8
+
+4.2
+
+
+
+164
+
+6.4
+
+
+
+164
+
+6.4
+
+
+
+164
+
+6.4
+
+
+
+164.5
+
+6.4
+
+
+
+165.2
+
+6.4
+
+
+
+165.7
+
+6.4
+
+
+
+166.2
+
+6.4
+
+
+
+166.3
+
+6.4
+
+
+
+166.4
+
+6.4
+
+
+
+166.4
+
+6.4
+
+
+
+167.2
+
+6.4
+
+
+
+167.2
+
+6.4
+
+
+
+167.2
+
+6.4
+
+
+
+167
+
+6.4
+
+
+
+167
+
+6.4
+
+
+
+167
+
+6.4
+
+
+
+168
+
+6.4
+
+
+
+168.2
+
+6.4
+
+
+
+168.5
+
+6.4
+
+
+
+168
+
+6.4
+
+
+
+168
+
+6.4
+
+
+
+168.2
+
+6.4
+
+
+
+168.5
+
+6.4
+
+
+
+168.7
+
+6.4
+
+
+
+168
+
+3.1
+
+
+
+168.2
+
+4.2
+
+
+
+168
+
+4.2
+
+
+
+167.8
+
+4.2
+
+
+
+167.7
+
+4.2
+
+
+
+167.6
+
+4.2
+
+
+
+167.6
+
+4.2
+
+
+
+167.6
+
+4.2
+
+
+
+167.5
+
+4.2
+
+
+
+167.5
+
+4.2
+
+
+
+167.5
+
+4.2
+
+
+
+167.4
+
+4.2
+
+
+
+167.4
+
+4.2
+
+
+
+167.3
+
+4.2
+
+
+
+167.3
+
+4.2
+
+
+
+167.3
+
+4.2
+
+
+
+167.3
+
+4.2
+
+
+
+167.2
+
+4.2
+
+
+
+167.2
+
+4.2
+
+
+
+167.2
+
+4.2
+
+
+
+167.1
+
+4.2
+
+
+
+167.1
+
+4.2
+
+
+
+167.1
+
+4.2
+
+
+
+167
+
+1.4
+
+
+
+167
+
+4.2
+
+
+
+168
+
+4.2
+
+
+
+167.7
+
+4.2
+
+
+
+168
+
+6.4
+
+
+
+168
+
+6.4
+
+
+
+168
+
+6.4
+
+
+
+167.2
+
+6.4
+
+
+
+168
+
+6.4
+
+
+
+168
+
+6.4
+
+
+
+168
+
+6.4
+
+
+
+168
+
+6.4
+
+
+
+168
+
+6.4
+
+
+
+168
+
+6.4
+
+
+
+168
+
+6.4
+
+
+
+168
+
+6.4
+
+
+
+169
+
+6.4
+
+
+
+169
+
+6.4
+
+
+
+169
+
+6.4
+
+
+
+169.1
+
+6.4
+
+
+
+169.2
+
+6.4
+
+
+
+169.2
+
+6.4
+
+
+
+169.2
+
+6.4
+
+
+
+169.7
+
+6.4
+
+
+
+169.8
+
+6.4
+
+
+
+169.8
+
+6.4
+
+
+
+169.8
+
+6.4
+
+
+
+169.9
+
+6.4
+
+
+
+170
+
+6.4
+
+
+
+170
+
+6.4
+
+
+
+170.2
+
+6.4
+
+
+
+170
+
+6.4
+
+
+
+169.8
+
+6.4
+
+
+
+169
+
+6.4
+
+
+
+168.5
+
+6.4
+
+
+
+168
+
+6.4
+
+
+
+167.5
+
+6.4
+
+
+
+167.2
+
+6.4
+
+
+
+167.1
+
+6.4
+
+
+
+167
+
+6.4
+
+
+
+167
+
+6.4
+
+
+
+166.9
+
+6.4
+
+
+
+166.8
+
+6.4
+
+
+
+166.3
+
+6.4
+
+
+
+166.2
+
+6.4
+
+
+
+166
+
+6.4
+
+
+
+166.1
+
+6.4
+
+
+
+167
+
+4.2
+
+
+
+167
+
+4.2
+
+
+
+167
+
+4.2
+
+
+
+167
+
+4.2
+
+
+
+167
+
+4.2
+
+
+
+166.2
+
+4.2
+
+
+
+166.1
+
+4.2
+
+
+
+166
+
+4.2
+
+
+
+166
+
+4.2
+
+
+
+166
+
+4.2
+
+
+
+166
+
+4.2
+
+
+
+166
+
+4.2
+
+
+
+166
+
+4.2
+
+
+
+166
+
+4.2
+
+
+
+166
+
+4.2
+
+
+
+165
+
+4.2
+
+
+
+165
+
+4.2
+
+
+
+166
+
+4.2
+
+
+
+165.3
+
+4.2
+
+
+
+165
+
+4.2
+
+
+
+165.1
+
+4.2
+
+
+
+165.2
+
+4.2
+
+
+
+165.2
+
+4.2
+
+
+
+165
+
+4.2
+
+
+
+165
+
+4.2
+
+
+
+165
+
+4.2
+
+
+
+166
+
+4.2
+
+
+
+167
+
+4.2
+
+
+
+166.5
+
+4.2
+
+
+
+166.2
+
+4.2
+
+
+
+167
+
+4.2
+
+
+
+168
+
+4.2
+
+
+
+168
+
+4.2
+
+
+
+168.1
+
+4.2
+
+
+
+168.1
+
+4.2
+
+
+
+168.1
+
+4.2
+
+
+
+168.1
+
+4.2
+
+
+
+168.2
+
+4.2
+
+
+
+168.2
+
+4.2
+
+
+
+168.2
+
+4.2
+
+
+
+168.2
+
+4.2
+
+
+
+168.2
+
+4.2
+
+
+
+168.2
+
+4.2
+
+
+
+168.2
+
+4.2
+
+
+
+168
+
+4.2
+
+
+
+167.9
+
+4.2
+
+
+
+167.8
+
+4.2
+
+
+
+167.8
+
+4.2
+
+
+
+167.8
+
+4.2
+
+
+
+167.8
+
+4.2
+
+
+
+167.5
+
+6.4
+
+
+
+167.2
+
+6.4
+
+
+
+168
+
+6.4
+
+
+
+168.3
+
+6.4
+
+
+
+168.5
+
+6.4
+
+
+
+168.5
+
+6.4
+
+
+
+169
+
+6.4
+
+
+
+168.9
+
+6.4
+
+
+
+168.6
+
+6.4
+
+
+
+168.5
+
+6.4
+
+
+
+168.2
+
+6.4
+
+
+
+167
+
+6.4
+
+
+
+165.2
+
+6.4
+
+
+
+162.3
+
+6.4
+
+
+
+162.2
+
+6.4
+
+
+
+161.5
+
+6.4
+
+
+
+161.2
+
+6.4
+
+
+
+161
+
+6.4
+
+
+
+161.2
+
+6.4
+
+
+
+161
+
+6.4
+
+
+
+160.9
+
+6.4
+
+
+
+160.8
+
+6.4
+
+
+
+160.8
+
+6.4
+
+
+
+160.5
+
+6.4
+
+
+
+160.2
+
+6.4
+
+
+
+160.1
+
+6.4
+
+
+
+159.8
+
+6.4
+
+
+
+160
+
+6.4
+
+
+
+160.4
+
+6.4
+
+
+
+160.4
+
+6.4
+
+
+
+160.5
+
+6.4
+
+
+
+160.5
+
+6.4
+
+
+
+160.8
+
+6.4
+
+
+
+160.6
+
+6.4
+
+
+
+160.5
+
+6.4
+
+
+
+160.5
+
+6.4
+
+
+
+159.5
+
+6.4
+
+
+
+159
+
+6.4
+
+
+
+158.8
+
+6.4
+
+
+
+158.7
+
+6.4
+
+
+
+158.7
+
+6.4
+
+
+
+158.7
+
+6.4
+
+
+
+158.6
+
+6.4
+
+
+
+158.6
+
+6.4
+
+
+
+158.6
+
+6.4
+
+
+
+158.6
+
+6.4
+
+
+
+158.5
+
+6.4
+
+
+
+158.5
+
+6.4
+
+
+
+158.8
+
+6.4
+
+
+
+159.2
+
+6.4
+
+
+
+159.4
+
+6.4
+
+
+
+159.5
+
+6.4
+
+
+
+160.5
+
+6.4
+
+
+
+159.8
+
+4.2
+
+
+
+160
+
+4.2
+
+
+
+160
+
+4.2
+
+
+
+159.8
+
+4.2
+
+
+
+159.7
+
+4.2
+
+
+
+159.7
+
+4.2
+
+
+
+159.7
+
+4.2
+
+
+
+159.6
+
+4.2
+
+
+
+159.5
+
+4.2
+
+
+
+159.5
+
+4.2
+
+
+
+159.2
+
+4.2
+
+
+
+159.2
+
+4.2
+
+
+
+159.2
+
+4.2
+
+
+
+159.1
+
+4.2
+
+
+
+159.1
+
+4.2
+
+
+
+159.1
+
+4.2
+
+
+
+159.2
+
+4.2
+
+
+
+159
+
+4.2
+
+
+
+159
+
+1.4
+
+
+
+159
+
+4.2
+
+
+
+159
+
+1.4
+
+
+
+159
+
+4.2
+
+
+
+
+4.2
+
+
+
+158
+
+4.2
+
+
+
+158
+
+1.4
+
+
+
+158
+
+4.2
+
+
+
+158
+
+4.2
+
+
+
+158
+
+1.4
+
+
+
+157
+
+4.2
+
+
+
+157
+
+1.4
+
+
+
+157
+
+4.2
+
+
+
+157.4
+
+4.2
+
+
+
+157
+
+4.2
+
+
+
+158
+
+4.2
+
+
+
+158
+
+4.2
+
+
+
+158
+
+4.2
+
+
+
+157.9
+
+4.2
+
+
+
+157.9
+
+4.2
+
+
+
+157.9
+
+4.2
+
+
+
+157.8
+
+4.2
+
+
+
+157.8
+
+4.2
+
+
+
+157.8
+
+4.2
+
+
+
+157.7
+
+4.2
+
+
+
+157.5
+
+4.2
+
+
+
+157
+
+4.2
+
+
+
+157
+
+4.2
+
+
+
+157
+
+4.2
+
+
+
+157
+
+4.2
+
+
+
+157.1
+
+4.2
+
+
+
+157.3
+
+4.2
+
+
+
+157.8
+
+4.2
+
+
+
+158.2
+
+4.2
+
+
+
+157
+
+4.2
+
+
+
+156.9
+
+4.2
+
+
+
+156.8
+
+4.2
+
+
+
+155
+
+4.2
+
+
+
+154.5
+
+4.2
+
+
+
+154
+
+4.2
+
+
+
+152.8
+
+4.2
+
+
+
+152.6
+
+4.2
+
+
+
+152.5
+
+4.2
+
+
+
+152.5
+
+4.2
+
+
+
+152.5
+
+4.2
+
+
+
+152.4
+
+4.2
+
+
+
+152.4
+
+4.2
+
+
+
+152.2
+
+4.2
+
+
+
+151.8
+
+4.2
+
+
+
+152.5
+
+4.2
+
+
+
+152.4
+
+4.2
+
+
+
+152.3
+
+4.2
+
+
+
+152.2
+
+4.2
+
+
+
+152
+
+4.2
+
+
+
+151.8
+
+4.2
+
+
+
+152
+
+4.2
+
+
+
+152.1
+
+4.2
+
+
+
+152.1
+
+4.2
+
+
+
+152.2
+
+4.2
+
+
+
+152.2
+
+4.2
+
+
+
+152.2
+
+4.2
+
+
+
+152.3
+
+4.2
+
+
+
+152.3
+
+4.2
+
+
+
+152.4
+
+4.2
+
+
+
+152.4
+
+4.2
+
+
+
+152.8
+
+4.2
+
+
+
+152.8
+
+4.2
+
+
+
+153.1
+
+4.2
+
+
+
+154.2
+
+4.2
+
+
+
+154.4
+
+4.2
+
+
+
+154.5
+
+4.2
+
+
+
+153.8
+
+4.2
+
+
+
+155
+
+4.2
+
+
+
+155
+
+4.2
+
+
+
+154.9
+
+4.2
+
+
+
+154.6
+
+4.2
+
+
+
+154.4
+
+4.2
+
+
+
+154.2
+
+4.2
+
+
+
+154.2
+
+4.2
+
+
+
+154
+
+4.2
+
+
+
+153.8
+
+4.2
+
+
+
+153
+
+4.2
+
+
+
+152.5
+
+4.2
+
+
+
+152.2
+
+4.2
+
+
+
+152.2
+
+4.2
+
+
+
+152.1
+
+4.2
+
+
+
+152.1
+
+4.2
+
+
+
+152
+
+4.2
+
+
+
+152.1
+
+4.2
+
+
+
+152.1
+
+4.2
+
+
+
+152.2
+
+4.2
+
+
+
+152
+
+4.2
+
+
+
+152
+
+4.2
+
+
+
+153
+
+4.2
+
+
+
+153.2
+
+4.2
+
+
+
+153.2
+
+4.2
+
+
+
+153.2
+
+4.2
+
+
+
+153.6
+
+4.2
+
+
+
+153.7
+
+4.2
+
+
+
+153.7
+
+4.2
+
+
+
+153.7
+
+4.2
+
+
+
+154
+
+4.2
+
+
+
+154.2
+
+4.2
+
+
+
+154.8
+
+4.2
+
+
+
+154.9
+
+4.2
+
+
+
+155
+
+4.2
+
+
+
+154.2
+
+4.2
+
+
+
+154.2
+
+4.2
+
+
+
+154.1
+
+4.2
+
+
+
+154
+
+4.2
+
+
+
+153
+
+4.2
+
+
+
+153
+
+4.2
+
+
+
+152.8
+
+4.2
+
+
+
+152.8
+
+4.2
+
+
+
+152.8
+
+4.2
+
+
+
+152.2
+
+4.2
+
+
+
+152
+
+4.2
+
+
+
+151
+
+4.2
+
+
+
+151
+
+4.2
+
+
+
+151
+
+4.2
+
+
+
+152.1
+
+4.2
+
+
+
+152.2
+
+4.2
+
+
+
+152.6
+
+4.2
+
+
+
+153
+
+4.2
+
+
+
+153
+
+4.2
+
+
+
+153
+
+4.2
+
+
+
+153
+
+4.2
+
+
+
+153.1
+
+4.2
+
+
+
+152.5
+
+4.2
+
+
+
+154.2
+
+4.2
+
+
+
+154
+
+4.2
+
+
+
+153
+
+4.2
+
+
+
+152.5
+
+4.2
+
+
+
+150
+
+4.2
+
+
+
+148.2
+
+4.2
+
+
+
+146.8
+
+4.2
+
+
+
+146
+
+1.4
+
+
+
+146.1
+
+4.2
+
+
+
+145.8
+
+1.4
+
+
+
+146
+
+1.4
+
+
+
+146.2
+
+4.2
+
+
+
+146
+
+4.2
+
+
+
+146
+
+4.2
+
+
+
+146
+
+4.2
+
+
+
+146.8
+
+4.2
+
+
+
+146.5
+
+4.2
+
+
+
+146.2
+
+4.2
+
+
+
+145
+
+4.2
+
+
+
+143.8
+
+6.4
+
+
+
+142.8
+
+6.4
+
+
+
+139.5
+
+6.4
+
+
+
+136.8
+
+6.4
+
+
+
+136.2
+
+6.4
+
+
+
+136.1
+
+6.4
+
+
+
+136
+
+5
+
+
+
+137
+
+3.1
+
+
+
+137
+
+4.2
+
+
+
+137
+
+4.2
+
+
+
+137
+
+4.2
+
+
+
+137
+
+4.2
+
+
+
+137
+
+4.2
+
+
+
+137
+
+4.2
+
+
+
+135.8
+
+4.2
+
+
+
+134.2
+
+4.2
+
+
+
+134.1
+
+4.2
+
+
+
+134
+
+4.2
+
+
+
+134
+
+4.2
+
+
+
+134
+
+4.2
+
+
+
+133.8
+
+4.2
+
+
+
+133.5
+
+4.2
+
+
+
+133.5
+
+4.2
+
+
+
+133.4
+
+4.2
+
+
+
+133.4
+
+4.2
+
+
+
+133.3
+
+4.2
+
+
+
+133.3
+
+4.2
+
+
+
+133.3
+
+4.2
+
+
+
+133.3
+
+4.2
+
+
+
+133.2
+
+4.2
+
+
+
+133.8
+
+4.2
+
+
+
+133.5
+
+4.2
+
+
+
+133.2
+
+4.2
+
+
+
+133
+
+4.2
+
+
+
+132.8
+
+4.2
+
+
+
+132.5
+
+4.2
+
+
+
+132
+
+4.2
+
+
+
+131.1
+
+4.2
+
+
+
+130.9
+
+4.2
+
+
+
+130.8
+
+4.2
+
+
+
+130.7
+
+4.2
+
+
+
+130.6
+
+4.2
+
+
+
+130.5
+
+4.2
+
+
+
+130.8
+
+4.2
+
+
+
+131.5
+
+4.2
+
+
+
+131.5
+
+4.2
+
+
+
+131
+
+4.2
+
+
+
+130.8
+
+4.2
+
+
+
+129.8
+
+4.2
+
+
+
+128.8
+
+4.2
+
+
+
+128
+
+4.2
+
+
+
+127.8
+
+4.2
+
+
+
+128.2
+
+4.2
+
+
+
+128.2
+
+4.2
+
+
+
+128
+
+4.2
+
+
+
+128.2
+
+4.2
+
+
+
+130
+
+4.2
+
+
+
+130
+
+4.2
+
+
+
+130.2
+
+4.2
+
+
+
+130
+
+1.4
+
+
+
+
+4.2
+
+
+
+
+4.2
+
+
+
+130
+
+1.4
+
+
+
+129
+
+4.2
+
+
+
+129.8
+
+4.2
+
+
+
+130
+
+4.2
+
+
+
+130.2
+
+4.2
+
+
+
+130.5
+
+4.2
+
+
+
+130.8
+
+4.2
+
+
+
+130.9
+
+4.2
+
+
+
+131
+
+4.2
+
+
+
+131.3
+
+4.2
+
+
+
+131.6
+
+4.2
+
+
+
+131.7
+
+4.2
+
+
+
+132
+
+4.2
+
+
+
+131.8
+
+4.2
+
+
+
+131.4
+
+4.2
+
+
+
+131.4
+
+4.2
+
+
+
+131.3
+
+4.2
+
+
+
+131.2
+
+4.2
+
+
+
+131
+
+4.2
+
+
+
+131
+
+4.2
+
+
+
+130
+
+4.2
+
+
+
+129.8
+
+4.2
+
+
+
+129.6
+
+4.2
+
+
+
+129.5
+
+4.2
+
+
+
+129.2
+
+4.2
+
+
+
+129.1
+
+4.2
+
+
+
+129.1
+
+4.2
+
+
+
+129.1
+
+4.2
+
+
+
+129
+
+4.2
+
+
+
+129
+
+4.2
+
+
+
+128.9
+
+4.2
+
+
+
+128.8
+
+4.2
+
+
+
+128.5
+
+4.2
+
+
+
+128.8
+
+4.2
+
+
+
+129
+
+4.2
+
+
+
+129
+
+4.2
+
+
+
+129
+
+4.2
+
+
+
+128.5
+
+4.2
+
+
+
+128
+
+4.2
+
+
+
+127.8
+
+4.2
+
+
+
+128
+
+4.2
+
+
+
+127
+
+4.2
+
+
+
+126.8
+
+4.2
+
+
+
+126.2
+
+4.2
+
+
+
+125.8
+
+4.2
+
+
+
+125.7
+
+4.2
+
+
+
+125.6
+
+4.2
+
+
+
+125.6
+
+4.2
+
+
+
+125.5
+
+4.2
+
+
+
+125.4
+
+4.2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+