package me.bvn13.sdk.android.gpx
import me.bvn13.sdk.android.gpx.GpxWriter.Companion.DTF
import me.bvn13.sdk.android.gpx.GpxWriter.Companion.HEADER
import me.bvn13.sdk.android.gpx.GpxWriter.Companion.SCHEMA_LOCATION
import me.bvn13.sdk.android.gpx.GpxWriter.Companion.XMLNS
import me.bvn13.sdk.android.gpx.GpxWriter.Companion.XMLNS_XSI
import java.time.Clock
import java.time.OffsetDateTime
import java.time.format.DateTimeFormatter.ISO_LOCAL_DATE_TIME
import java.time.format.DateTimeFormatterBuilder
fun GpxType.toXmlString(): String = this.toXmlString(null)
fun GpxType.toXmlString(clock: Clock?): String = """
$HEADER
${this.metadata.toXmlString()}
${this.wpt?.toXmlString()}
${this.rte?.toXmlString()}
${this.trk?.toXmlString()}
""".trim()
fun MetadataType.toXmlString(): String = """
${this.name}
${this.description}
${this.authorName}
""".trim()
private fun now(clock: Clock?) = OffsetDateTime.now(clock ?: Clock.systemDefaultZone()).format(DTF)
fun WptType.toXmlString(nodeName: String? = null) =
if (nodeName != null) {
this.toXmlString(nodeName)
} else {
this.toXmlString("wpt")
}
@JvmName("toXmlStringNamed")
fun WptType.toXmlString(nodeName: String) = """
<${nodeName} lat="${this.lat}" lon="${this.lon}">
${toXmlString(ele, "ele")}
${toXmlString(time, "time")}
${toXmlString(magvar, "magvar")}
${toXmlString(geoidheight, "geoidheight")}
${toXmlString(name, "name")}
${toXmlString(cmt, "cmt")}
${toXmlString(desc, "desc")}
${toXmlString(src, "src")}
${link?.toXmlString()}
${toXmlString(sym, "sym")}
${toXmlString(type, "type")}
${fix?.toXmlString()}
${toXmlString(sat, "sat")}
${toXmlString(hdop, "hdop")}
${toXmlString(vdop, "vdop")}
${toXmlString(pdop, "pdop")}
${toXmlString(ageofgpsdata, "ageofgpsdata")}
${toXmlString(dgpsid, "dgpsid")}
${extensions?.toXmlString()}
${nodeName}>
""".trim()
fun RteType.toXmlString() = """
${toXmlString(this.name, "name")}
${toXmlString(this.cmt, "cmt")}
${toXmlString(this.desc, "desc")}
${toXmlString(this.src, "src")}
${this.link?.toXmlString()}
${toXmlString(this.number, "number")}
${toXmlString(this.type, "type")}
${this.extensions?.toXmlString()}
${this.rtept?.toXmlString()}
""".trim()
fun TrkType.toXmlString() = """
${toXmlString(this.name, "name")}
${toXmlString(this.cmt, "cmt")}
${toXmlString(this.desc, "desc")}
${toXmlString(this.src, "src")}
${this.link?.toXmlString()}
${toXmlString(this.number, "number")}
${toXmlString(this.type, "type")}
${this.extensions?.toXmlString()}
${this.trkseg?.toXmlString()}
""".trim()
fun List.toXmlString(nodeName: String?) =
this.joinToString(prefix = "", postfix = "", separator = "") {
it.toXmlString(nodeName)
}
@JvmName("toXmlStringWptType")
fun List.toXmlString() =
this.joinToString(prefix = "", postfix = "", separator = "") {
it.toXmlString()
}
@JvmName("toXmlStringLinkType")
fun List.toXmlString() =
this.joinToString(prefix = "", postfix = "", separator = "\n", transform = LinkType::toXmlString)
@JvmName("toXmlStringRteType")
fun List.toXmlString() =
this.joinToString(prefix = "", postfix = "", separator = "", transform = RteType::toXmlString)
@JvmName("toXmlStringTrkType")
fun List.toXmlString() =
this.joinToString(prefix = "", postfix = "", separator = "", transform = TrkType::toXmlString)
@JvmName("toXmlStringTrksegType")
fun List.toXmlString() =
this.joinToString(prefix = "", postfix = "", separator = "", transform = TrksegType::toXmlString)
fun List.toXmlString() =
this.joinToString(
prefix = "\n",
postfix = "\n",
separator = "\n",
transform = ExtensionType::toXmlString
)
fun LinkType.toXmlString() = """
${this.text}
${this.type}
""".trim()
fun FixType.toXmlString() = """
${this.value}
""".trim()
fun ExtensionType.toXmlString() = """
<${this.nodeName}${toXmlString(this.parameters)}>${this.value ?: ""}${this.nodeName}>
""".trim()
fun TrksegType.toXmlString() = """
${this.trkpt?.toXmlString("trkpt")}
""".trim()
fun toXmlString(value: String?, nodeName: String) =
if (value != null) {
"""
<${nodeName}>${value}${nodeName}>
""".trim()
} else {
""
}
fun toXmlString(value: Int?, nodeName: String) =
if (value != null) {
"""
<${nodeName}>${value}${nodeName}>
""".trim()
} else {
""
}
fun toXmlString(value: Double?, nodeName: String) =
if (value != null) {
"""
<${nodeName}>${value}${nodeName}>
""".trim()
} else {
""
}
fun toXmlString(value: OffsetDateTime?, nodeName: String) =
if (value != null) {
"""
<${nodeName}>${value.format(DTF)}${nodeName}>
""".trim()
} else {
""
}
fun toXmlString(value: Map?) =
value?.entries?.joinToString(separator = "") {
" ${it.key}=\"${it.value}\""
} ?: ""
class GpxWriter {
companion object {
const val HEADER = ""
const val XMLNS = "http://www.topografix.com/GPX/1/1"
const val XMLNS_XSI = "http://www.w3.org/2001/XMLSchema-instance"
const val SCHEMA_LOCATION = "http://www.topografix.com/GPX/1/1 http://www.topografix.com/GPX/1/1/gpx.xsd"
internal val DTF = DateTimeFormatterBuilder()
.append(ISO_LOCAL_DATE_TIME) // use the existing formatter for date time
.appendOffset("+HH:MM", "+00:00") // set 'noOffsetText' to desired '+00:00'
.toFormatter()
}
}