[160] This specification defines how to parse [[GPX]] files.

;; [205] The original [[GPX]] specification does not define
how to interpret [[GPX]] files.

* Data model

[9] A [DFN[data set]] has 
[DFN[[F[name]]]],
[DFN[[F[description]]]],
[DFN[[F[keywords]]]],
[DFN[[F[generator]]]],
[DFN[[F[timestamp]]]],
[DFN[[F[updated]]]],
[DFN[[F[author]]]],
[DFN[[F[license]]]],
[DFN[[F[minimum latitude]]]],
[DFN[[F[minimum longitude]]]],
[DFN[[F[maximum latitude]]]],
[DFN[[F[maximum longitude]]]],
and
[DFN[[F[time-zone offset]]]],
which are initially [CODE[null]].

[19] A [[data set]] has
[DFN[[F[waypoints]]]],
[DFN[[F[routes]]]],
[DFN[[F[tracks]]]],
and
[DFN[[F[links]]]],
which are initially [[empty][is empty]] [[lists][list]].

[26] A [DFN[point]] has
[DFN[[F[name]]]],
[DFN[[F[description]]]],
[DFN[[F[timestamp]]]],
[DFN[[F[latitude]]]],
[DFN[[F[longitude]]]],
[DFN[[F[elevation]]]],
[DFN[[F[geoid height]]]],
[DFN[[F[magnetic variation]]]],
[DFN[[F[comment]]]],
[DFN[[F[source]]]],
[DFN[[F[symbol name]]]],
[DFN[[F[type]]]],
[DFN[[F[fix]]]],
[DFN[[F[number of satellites]]]],
[DFN[[F[hdop]]]],
[DFN[[F[vdop]]]],
[DFN[[F[age of DGPS data]]]],
[DFN[[F[DGPS ID]]]],
[DFN[[F[speed]]]],
[DFN[[F[accuracy]]]],
[DFN[[F[temperature]]]],
[DFN[[F[water temperature]]]],
[DFN[[F[depth]]]],
[DFN[[F[cadence]]]],
[DFN[[F[distance]]]],
[DFN[[F[to distance]]]],
[DFN[[F[heartrate]]]], 
[DFN[[F[point role]]]],
[DFN[[F[power]]]],
and
[DFN[[F[road type]]]],
which are initially [CODE[null]].

[189] A [[route]] has [DFN[[F[links]]]], which is initially an [[empty][is empty]]
[[list]].

[45] A [DFN[route]] has
[DFN[[F[name]]]],
[DFN[[F[description]]]], 
[DFN[[F[comment]]]],
[DFN[[F[source]]]],
[DFN[[F[type]]]],
and
[DFN[[F[number]]]],
which are initially [CODE[null]].

[62] A [[route]] has [DFN[[F[points]]]]
and
[DFN[[F[links]]]],
which are initially [[empty][is empty]]
[[lists][list]].

[72] A [DFN[track]] has
[DFN[[F[name]]]],
[DFN[[F[description]]]], 
[DFN[[F[comment]]]],
[DFN[[F[source]]]],
[DFN[[F[type]]]],
and
[DFN[[F[number]]]],
which are initially [CODE[null]].

[73] A [[track]] has [DFN[[F[segments]]]]
and
[DFN[[F[links]]]],
which are initially [[empty][is empty]] 
[[lists][list]].

[74] A [DFN[track segment]] has [DFN[[F[points]]]], which is initially an [[empty][is empty]]
[[list]].

[43] A [DFN[person]] has
[DFN[[F[name]]]]
and
[DFN[[F[email]]]],
which are initially [CODE[null]].

[207] A [[person]] has
[DFN[[F[links]]]],
which is initially an [[empty][is empty]] [[list]].

[51] A [DFN[license]] has
[DFN[[F[holder]]]],
[DFN[[F[year]]]], and
[DFN[[F[URL]]]],
which are initially [CODE[null]].

[202] A [DFN[link]] has
[DFN[[F[URL]]]],
[DFN[[F[MIME type]]]],
and
[DFN[[F[text]]]],
which are initially [CODE[null]].

-*-*-

[188] 
A [[track]] is a [DFN[valid timestamped route]] if all of the following
conditions are true:

- Its [F[segments]] is not [[empty][is empty]].
- Its [F[segments]] [[contains]] no [[track segment]]
whose [F[size]] is less than [N[2]].
- Its [F[segments]] [[contains]] no [[track segment]]
that [[contains]] a [[point]] whose
[F[latitude]],
[F[longitude]],
[F[elevation]],
or
[F[timestamp]]
is 
[CODE[null]].
- Its [F[segments]] [[contains]] no [[track segment]] [VAR[segment]]
where there is an integer [VAR[index]] in the range [ [N[1]], [VAR[segment]]'s [F[size]] - 1 ]
such that [VAR[segment]] [ [VAR[index]] - 1 ]'s [F[timestamp]] [[>]] 
[VAR[segment]] [ [VAR[index]] ]'s [F[timestamp]].

* Parsing

[206] An implementation [MUST[MUST]]
use an [[XML parser]] that implements [[XML5]]
to parse a [[GPX]] file.
It [MUST[MUST NOT]] [[fetch]] and process [[external entities][external entity]].

[159] An implementation [MUST[MUST]]
use the steps to [[parse a GPX document]] to parse a [CODE(DOMi)@en[Document]] as
a GPX document.

[1] To [DFN[parse a GPX document]] [VAR[doc]], run these steps:
[FIG(steps)[
= [2] Let [VAR[element]] be [VAR[doc]]'s [F[document element]].
= [4] If [VAR[element]] is an [[element]] whose [F[local name]] is [CODE[gpx]]:
== [6] Return the result of [[parsing a [CODE[gpx]] element][parse a [CODE[gpx]] element]] [VAR[element]].
= [11] Otherwise:
== [12] Return [[null]].
]FIG]

;; [78] These steps ignore any duplicate [CODE(XMLe)@en[link][link (GPX)]]
element.  They also ignore any [CODE(XMLe)@en[link][link (GPX)]] content.

[10] To [DFN[parse a [CODE[gpx]] element]] [VAR[element]], run these steps:
[FIG(steps)[
= [79] Let [VAR[data set]] be a [[data set]].
= [17] Let [VAR[creator]] be [VAR[element]]'s [CODE(XMLa)@en[creator]] [[attribute value]].
= [59] If [VAR[creator]] is not [CODE[null]] and is not the [[empty string]]:
== [71] Set [VAR[data set]]'s [F[generator]] to [VAR[creator]].
= [179] Let [VAR[tzoffset]] be [VAR[element]]'s [CODE(XMLa)@en[tzoffset]]
[[attribute value]] in the [[GPX extension namespace]].
= [182] If [VAR[tzoffset]] is not [CODE[null]] and is not the [[empty string]]:
== [183] Let [VAR[offset]] be the result of 
[[parsing a time-zone offset string][parse a time-zone offset string]]
[VAR[tzoffset]].
== [184] If [VAR[offset]] is not nothing:
=== [181] Set [VAR[data set]]'s [F[time-zone offset]] to [VAR[offset]].
= [14] [[For each]] [[element]] [VAR[child]] in [VAR[element]]'s [F[children]]:
== [15] Switch by [VAR[child]]'s [F[local name]]:
[FIG(switch)[
: [CODE(XMLe)@en[metadata][metadata (GPX)]] :
[FIG(steps)[
= [29] [[For each]] [[element]] [VAR[gc]] in [VAR[child]]'s [F[children]]:
== [20] [[Process][process a value element]] [VAR[gc]] with [VAR[data set]],
using the following table:
[FIG(table)[
:l: [F[local name]]
:f: Field
:r: Rule

:l: [CODE[name][name (GPX)]]
:f: [F[name]]
:r: String

:l: [CODE[desc][desc (GPX)]]
:f: [F[description]]
:r: String

:l: [CODE[keywords][keywords (GPX)]]
:f: [F[keywords]]
:r: String

:l: [CODE[link][link (GPX)]]
:f: [F[links]]
:r: Link

:l: [CODE[author][author (GPX)]]
:f: [F[author]]
:r: Person

:l: [CODE[copyright][copyright (GPX)]]
:f: [F[license]]
:r: License

]FIG]
== [30] If [VAR[gc]]'s [F[local name]] is [CODE[time][time (GPX)]]:
=== [83] If [VAR[gc]]'s [F[namespace]] is [CODE[http://www.topografix.com/GPX/gpx_modified/0/1]]:
==== [88] If [VAR[data set]]'s [F[updated]] is [CODE[null]]:
===== [85] Let [VAR[text]] be [VAR[gc]]'s [F[child text content]].
===== [86] [[Parse a global date and time string][parse a global date and time string]]
[VAR[text]].
===== [87] If a time is returned,
set [VAR[data set]]'s [F[updated]] to the time.
=== [84] Otherwise:
==== [89] If [VAR[data set]]'s [F[timestamp]] is [CODE[null]]:
===== [90] Let [VAR[text]] be [VAR[gc]]'s [F[child text content]].
===== [91] [[Parse a global date and time string][parse a global date and time string]]
[VAR[text]].
===== [92] If a time is returned,
set [VAR[data set]]'s [F[timestamp]] to the time.
== [40] If [VAR[gc]]'s [F[local name]] is [CODE[bounds]]:
=== [97] If [VAR[data set]]'s [F[minimum latitude]] is [CODE[null]]:
==== [98] Set [VAR[data set]]'s [F[minimum latitude]] be the result of applying the
[[rules for parsing a latitude value]] to [VAR[gc]]'s [CODE[minlat]] attribute value.
=== [99] If [VAR[data set]]'s [F[minimum longitude]] is [CODE[null]]:
==== [100] Set [VAR[data set]]'s [F[minimum longitude]] be the result of applying the
[[rules for parsing a longitude value]] to [VAR[gc]]'s [CODE[minlon]] attribute value.
=== [101] If [VAR[data set]]'s [F[maximum latitude]] is [CODE[null]]:
==== [102] Set [VAR[data set]]'s [F[maximum latitude]] be the result of applying the
[[rules for parsing a latitude value]] to [VAR[gc]]'s [CODE[maxlat]] attribute value.
=== [103] If [VAR[data set]]'s [F[maximum longitude]] is [CODE[null]]:
==== [104] Set [VAR[data set]]'s [F[maximum longitude]] be the result of applying the
[[rules for parsing a longitude value]] to [VAR[gc]]'s [CODE[maxlon]] attribute value.
]FIG]
: [CODE(XMLe)@en[wpt]] :
Append the result of [[parsing a point element][parse a point element]] [VAR[child]]
to [VAR[data set]]'s [F[waypoints]].
: [CODE(XMLe)@en[rte]] :
Append the result of [[parsing a route element][parse a route element]] [VAR[child]]
to [VAR[data set]]'s [F[routes]].
: [CODE(XMLe)@en[trk]] :
Append the result of [[parsing a track element][parse a track element]] [VAR[child]]
to [VAR[data set]]'s [F[tracks]].
]FIG]
= [115] Return [VAR[data set]].
]FIG]

[27] To [DFN[parse a point element]] [VAR[element]], run these steps:
[FIG(steps)[
= [25] Let [VAR[point]] be a [[point]].
= [116] Set [VAR[point]]'s [F[latitude]] to the result of applying the
[[rules for parsing a latitude value]] to [VAR[element]]'s [CODE[lat]] [[attribute value]].
= [118] Set [VAR[point]]'s [F[longitude]] to the result of applying the
[[rules for parsing a latitude value]] to [VAR[element]]'s [CODE[lon]] [[attribute value]].
= [185] Let [VAR[road]] be [VAR[element]]'s [CODE(XMLa)@en[road]]
[[attribute value]] in the [[GPX extension namespace]].
= [186] If [VAR[road]] is a [[valid road type string]]:
== [187] Set [VAR[point]]'s [F[road type]] to [VAR[road]].
= 
[218] 
Let [VAR[role]] be [VAR[element]]'s [CODE(XMLa)@en[pointrole]]
[[attribute value]] in the [[GPX extension namespace]].
= [219] If [VAR[role]] is a [[valid point role string]]:
== [220] Set [VAR[point]]'s [F[point role]] to [VAR[role]].
= [21] Set [VAR[to distance]]
be
[VAR[element]]'s [CODE(XMLa)@en[todistance]]
[[attribute value]] in the [[GPX extension namespace]].
= [22] If 
[VAR[to distance]] is ''not'' [CODE[null]]:
== [32] 
Let [VAR[number]] be the result of applying the
[[rules for parsing floating-point number values]] to [VAR[to distance]].
== [31] 
If [VAR[number]] is a number greater than or equal to [N[0]]:
=== [33] 
Set [VAR[point]]'s [F[to distance]]
to [VAR[number]].
= [18] [[For each]] [[element]] [VAR[child]] in [VAR[element]]'s [F[children]]:
== [154] [[Process][process a value element]] [VAR[child]] with [VAR[point]],
using the following table:
[FIG(table)[
:l: [F[local name]]
:f: Field
:r: Rule

:l: [CODE[sat]]
:f: [F[number of satellites]]
:r: Non-negative number

:l: [CODE[dgpsid]]
:f: [F[DGPS ID]]
:r: Non-negative integer

:l: [CODE[ele]]
:f: [F[elevation]]
:r: Number

:l: [CODE[geoidheight]]
:f: [F[geoid height]]
:r: Number

:l: [CODE[hdop]]
:f: [F[hdop]]
:r: Number

:l: [CODE[vdop]]
:f: [F[vdop]]
:r: Number

:l: [CODE[pdop]]
:f: [F[pdop]]
:r: Number

:l: [CODE[ageofdgpsdata]]
:f: [F[age of DGPS data]]
:r: Number

:l: [CODE[speed]]
:f: [F[speed]]
:r: Number

:l: [CODE[magvar]]
:f: [F[magnetic variation]]
:r: Degree

:l: [CODE[time][time (GPX)]]
:f: [F[timestamp]]
:r: Time

:l: [CODE[name][name (GPX)]]
:f: [F[name]]
:r: String

:l: [CODE[desc][desc (GPX)]]
:f: [F[description]]
:r: String

:l: [CODE[cmt]]
:f: [F[comment]]
:r: String

:l: [CODE[src][src (GPX)]]
:f: [F[source]]
:r: String

:l: [CODE[sym]]
:f: [F[symbol name]]
:r: String

:l: [CODE[type][type (GPX)]]
:f: [F[type]]
:r: String

:l: [CODE[fix][fix (GPX)]]
:f: [F[fix]]
:r: String

:l: [CODE[link][link (GPX)]]
:f: [F[links]]
:r: Link
]FIG]
== [141] If [VAR[child]]'s [F[local name]] is [CODE[extensions]]:
=== [143] [[For each]] [[element]] [VAR[gc]] in [VAR[child]]'s [F[children]]:
==== [144] [[Process][process a value element]] [VAR[gc]] with [VAR[point]],
using the following table:
[FIG(table)[
:l: [F[local name]]
:f: Field
:r: Rule

:l: [CODE[cadence]]
:f: [F[cadence]]
:r: Number

:l: [CODE[distance]]
:f: [F[distance]]
:r: Number

:l: [CODE[hr]]
:f: [F[heartrate]]
:r: Number

:l: [CODE[heartrate]]
:f: [F[heartrate]]
:r: Number

:l: [CODE[power]]
:f: [F[power]]
:r: Number

:l: [CODE[temp]]
:f: [F[temperature]]
:r: Number

:l: [CODE[speed]]
:f: [F[speed]]
:r: Number

:l: [CODE[accuracy]]
:f: [F[accuracy]]
:r: Number
]FIG]
==== [145] If [VAR[gc]]'s [F[local name]] is [CODE[TrackPointExtension]]:
===== [146] [[For each]] [[element]] [VAR[ggc]] in [VAR[gc]]'s [F[children]]:
====== [147] [[Process][process a value element]] [VAR[ggc]] with [VAR[point]],
using the following table:
[FIG(table)[
:l: [F[local name]]
:f: Field
:r: Rule

:l: [CODE[atemp]]
:f: [F[temperature]]
:r: Number

:l: [CODE[wtemp]]
:f: [F[water temperature]]
:r: Number

:l: [CODE[depth]]
:f: [F[depth]]
:r: Number

:l: [CODE[hr]]
:f: [F[heartrate]]
:r: Number

:l: [CODE[cad]]
:f: [F[cadence]]
:r: Number

]FIG]
= [161] Return [VAR[point]].
]FIG]

[52] To [DFN[parse a route element]] [VAR[element]], run these steps:
[FIG(steps)[
= [56] Let [VAR[route]] be a [[route]].
= [57] [[For each]] [[element]] [VAR[child]] in [VAR[element]]'s [F[children]]:
== [58] [[Process][process a value element]] [VAR[child]] with [VAR[route]],
using the following table:
[FIG(table)[
:l: [F[local name]]
:f: Field
:r: Rule

:l: [CODE[name][name (GPX)]]
:f: [F[name]]
:r: String

:l: [CODE[desc][desc (GPX)]]
:f: [F[description]]
:r: String

:l: [CODE[cmt]]
:f: [F[comment]]
:r: String

:l: [CODE[src][src (GPX)]]
:f: [F[source]]
:r: String

:l: [CODE[type][type (GPX)]]
:f: [F[type]]
:r: String

:l: [CODE[number][number (GPX)]]
:f: [F[number]]
:r: Non-negative number

:l: [CODE[link][link (GPX)]]
:f: [F[links]]
:r: Link

]FIG]
== [60] If [VAR[child]]'s [F[local name]] is [CODE[rtept]]:
=== [61] Append the result of [[parsing a point element][parse a point element]] [VAR[child]]
to [VAR[route]]'s [F[points]].
= [162] Return [VAR[route]].
]FIG]

[63] To [DFN[parse a track element]] [VAR[element]], run these steps:
[FIG(steps)[
= [64] Let [VAR[track]] be a [[track]].
= [65] [[For each]] [[element]] [VAR[child]] in [VAR[element]]'s [F[children]]:
== [66] [[Process][process a value element]] [VAR[child]] with [VAR[track]],
using the following table:
[FIG(table)[
:l: [F[local name]]
:f: Field
:r: Rule

:l: [CODE[name][name (GPX)]]
:f: [F[name]]
:r: String

:l: [CODE[desc][desc (GPX)]]
:f: [F[description]]
:r: String

:l: [CODE[cmt]]
:f: [F[comment]]
:r: String

:l: [CODE[src][src (GPX)]]
:f: [F[source]]
:r: String

:l: [CODE[type][type (GPX)]]
:f: [F[type]]
:r: String

:l: [CODE[number][number (GPX)]]
:f: [F[number]]
:r: Non-negative number

:l: [CODE[link][link (GPX)]]
:f: [F[links]]
:r: Link

]FIG]
== [67] If [VAR[child]]'s [F[local name]] is [CODE[trkseg]]:
=== [70] Let [VAR[segment]] be a [[track segment]].
=== [76] [[For each]] [[element]] [VAR[gc]] in [VAR[child]]'s [F[children]]:
==== [77] If [VAR[gc]]'s [F[local name]] is [CODE[trkpt]]:
===== [80] Append the result of [[parsing a point element][parse a point element]] [VAR[gc]]
to [VAR[segment]]'s [F[points]].
=== [75] Append [VAR[segment]] to [VAR[track]]'s [F[segments]].
= [163] Return [VAR[track]].
]FIG]

[105] The [DFN[rules for parsing a latitude value]] [VAR[text]] are as given in the following algorithm:
[FIG(steps)[
= [106] Let [VAR[number]] be the result of applying the
[[rules for parsing floating-point number values]] to [VAR[text]].
= [107] If [VAR[number]] is not a number, return [[null]] and abort these steps.
= [108] If [VAR[number]] is greater than [N[90]], or less than [N[-90]],
return [[null]] and abort these steps.
= [109] Return [VAR[number]].
]FIG]

[110] The [DFN[rules for parsing a longitude value]] [VAR[text]] are as given in the following algorithm:
[FIG(steps)[
= [111] Let [VAR[number]] be the result of applying the
[[rules for parsing floating-point number values]] to [VAR[text]].
= [112] If [VAR[number]] is not a number, return [[null]] and abort these steps.
= [113] If [VAR[number]] is greater than [N[180]], or less than [N[-180]],
return [[null]] and abort these steps.
= [114] Return [VAR[number]].
]FIG]

[153] To [DFN[process a value element]] [VAR[element]] with [VAR[object]] and [VAR[table]],
run these steps:
[FIG(steps)[
= [156] Let [VAR[row]] be a row in [VAR[table]], whose "[F[local name]]"
cell value is [VAR[element]]'s [F[local name]].
= [142] If [VAR[row]] is [[null]], abort these steps.
= [155] Let [VAR[field]] be [VAR[row]]'s "Field" cell value.
= [164] If [VAR[object]]'s [VAR[field]]'s value is not [CODE[null]], abort these steps.
= [157] Let [VAR[result]] be the result of switching by [VAR[row]]'s "Rule" cell value:
[FIG(switch)[
: String :
[FIG(steps)[
= [149] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [150] If [VAR[text]] is not the [[empty string]]:
== [151] Return [VAR[text]].
= [148] Otherwise:
== [152] Return [[null]].
]FIG]
: Non-negative number :
[FIG(steps)[
= [135] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [136] Let [VAR[number]] be the result of applying the 
[[rules for parsing non-negative integers]] to [VAR[text]].
= [137] If [VAR[number]] is a number:
== [138] Return [VAR[number]].
= [139] Otherwise:
== [140] Return [[null]].
]FIG]
: Number :
[FIG(steps)[
= [120] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [121] Let [VAR[number]] be the result of applying the
[[rules for parsing floating-point number values]] to [VAR[text]].
= [122] If [VAR[number]] is a number:
== [119] Return [VAR[number]].
= [128] Otherwise:
== [129] Return [[null]].
]FIG]
: Degree :
[FIG(steps)[
= [125] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [126] Let [VAR[number]] be the result of applying the
[[rules for parsing floating-point number values]] to [VAR[text]].
= [127] If [VAR[number]] is a number in the range [ [N[0]], [N[360]] ]:
== [124] Return [VAR[number]].
= [132] Otherwise:
== [133] Return [[null]].
]FIG]
: Year :
[FIG(steps)[
= [36] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [37] If [VAR[text]] is four or more [[ASCII digits]]:
== [38] Let [VAR[year]] be the result of applying the [[rules for parsing non-negative integers]] to [VAR[text]].
== [41] If [VAR[year]] is a positive integer:
=== [39] Return [VAR[year]] and abort these steps.
= [3] Return [[null]].
]FIG]
: Time :
[FIG(steps)[
= [16] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [117] [[Parse a global date and time string][parse a global date and time string]]
[VAR[text]].
= [123] If a time is returned:
== [130] Return the time.
= [131] Otherwise:
== [134] Return [[null]].
]FIG]
: URL Content :
[FIG(steps)[
= [81] Let [VAR[text]] be [VAR[element]]'s [F[child text content]].
= [82] If [VAR[text]] is not the [[empty string]],
[[parse][parse a URL]] [VAR[text]] relative to [VAR[element]]'s [F[node document]].
= [93] If parsed and not failed:
== [94] Return the [[resulting URL string]].
= [95] Otherwise:
== [96] Return [[null]].
]FIG]
: Link :
[FIG(steps)[
= [190] Let [VAR[href]] be [VAR[element]]'s [CODE(XMLa)@en[href]] [[attribute value]].
= [191] If [VAR[href]] is not [[null]],
[[parse][parse a URL]] [VAR[href]] relative to [VAR[element]]'s [F[node document]].
= [192] If parsed and not failed:
== [200] Let [VAR[link]] be a new [[link]].
== [201] Set [VAR[link]]'s [F[URL]] to the [[resulting URL string]].
== [203] [[For each]] [[element]] [VAR[child]] in [VAR[element]]'s [F[children]]:
=== [204] [[Process][process a value element]] [VAR[child]] with [VAR[link]],
using the following table:
[FIG(table)[
:l: [F[local name]]
:f: Field
:r: Rule

:l: [CODE[text][text (GPX)]]
:f: [F[text]]
:r: String

:l: [CODE[type][type (GPX)]]
:f: [F[MIME type]]
:r: String

:l: [CODE[link][link (GPX)]]
:f: [F[URL]]
:r: URL
]FIG]
== [193] Return [VAR[link]].
= [194] Otherwise:
== [195] Return [[null]].
]FIG]
: Person :
[FIG(steps)[
= [42] Let [VAR[person]] be a new [[person]].
= [68] [[For each]] [[element]] [VAR[child]] in [VAR[element]]'s [F[children]]:
== [23] [[Process][process a value element]] [VAR[child]] with [VAR[person]],
using the following table:
[FIG(table)[
:l: [F[local name]]
:f: Field
:r: Rule

:l: [CODE[name][name (GPX)]]
:f: [F[name]]
:r: String

:l: [CODE[link][link (GPX)]]
:f: [F[links]]
:r: Link
]FIG]
== [69] If [VAR[child]]'s [F[local name]] is [CODE[email][email (GPX)]]
and [VAR[person]]'s [F[email]] is [[null]]:
=== [46] Let [VAR[left]] be [VAR[child]]'s [CODE[id][email (GPX)]] attribute value.
=== [47] Let [VAR[right]] be [VAR[child]]'s [CODE[domain][email (GPX)]] attribute value.
=== [48] If [VAR[left]] and [VAR[right]] are not [[null]]:
==== [50] Let [VAR[email]] be [VAR[left]] followed by [CODE[@]] followed by [VAR[right]].
==== [49] Set [VAR[person]]'s [F[email]] to [VAR[email]].
= [44] Return [VAR[person]].
]FIG]
: License :
[FIG(steps)[
= [55] Let [VAR[license]] be a new [[license]].
= [7] Let [VAR[holder]] be
[VAR[element]]'s [CODE[author][copyright (GPX)]] attribute value.
= [8] If [VAR[holder]] is not [[null]] and is not the [[empty string]]:
== [54] Set [VAR[license]]'s [F[holder]] to [VAR[holder]].
= [53] [[For each]] [[element]] [VAR[child]] in [VAR[element]]'s [F[children]]:
== [24] [[Process][process a value element]] [VAR[child]] with [VAR[license]],
using the following table:
[FIG(table)[
:l: [F[local name]]
:f: Field
:r: Rule

:l: [CODE[year]]
:f: [F[year]]
:r: Year

:l: [CODE[license][license (GPX)]]
:f: [F[URL]]
:r: URL Content
]FIG]
= [28] Return [VAR[license]].
]FIG]
]FIG]
= [158] If [VAR[result]] is not [[null]]:
== [196] If [VAR[object]]'s field [VAR[field]] is a [[list]]:
=== [199] [[Append][append]] [VAR[result]] to [VAR[object]]'s field [VAR[field]]'s value.
== [197] Otherwise:
=== [198] Set [VAR[object]]'s field [VAR[field]] to [VAR[result]].
]FIG]

* MIME types

[166] A
[DFN[GPX MIME types]]
is any [[MIME type]] whose [[essence]] is one of the following:

- [CODE[text/gpx]]
- [CODE[application/gpx]]
- [CODE[application/gpx+xml]]
- [CODE[application/x-gpx]]
- [CODE[application/x-gpx+xml]]

[168] 
When an implementation uses a [[MIME type]] to identify GPX,
it [MUST[MUST]] use [CODE[application/gpx+xml]].

[169] 
When an implementation interprets a [[MIME type]],
it [MUST[MUST]] recognize any [[GPX MIME type]] as equivalent to
[CODE[application/gpx+xml]].

;; [170] Therefore, any
[CODE[charset]] parameter is interpreted as an [[XML MIME type]]
[CODE[charset]] parameter.

* Namespaces

[173] 
The [DFN[GPX namespace]] is
[DFN[[CODE[http://www.topografix.com/GPX/1/1]]]].

[174] 
The [DFN[GPX 1.0 namespace]]
is
[DFN[[CODE[http://www.topografix.com/GPX/1/0]]]].
The [[GPX 1.0 namespace]]
[MUST[MUST NOT]] be used.

[171] 
The [DFN[GPX extension namespace]]
is 
[DFN[[CODE[data:,gpx]]]].

;; [13] Most of the parsing steps intentionally ignore [[namespaces][namespace]]
of [[elements][element]].

* Definitions of extensions

[172] 
The [CODE[gpx]] [[element]] in the [[GPX namespace]]
[MAY[MAY]]
have a [CODE[tzoffset]] [[attribute]]
in the [[GPX extension namespace]].
If specified,
its [[value][attribute value]]
[MUST[MUST]]
be a 
[[valid time-zone offset string]].
It is the time-zone offset that is appropriate as
the default time-zone offset
for rendering timestamps in the [[element]].

-*-*-

[214] 
The [CODE[wpt][<wpt>]]
[[element]] in the [[GPX namespace]]
[MAY[MAY]]
have a [CODE[pointrole]] [[attribute]]
in the [[GPX extension namespace]].
If specified,
its [[value][attribute value]]
[MUST[MUST]]
be a
[[valid point role string]].
It is the kind of the point in the context of a race (or similar) event
described by the document.

[215] 
A [DFN[valid point role string]]
is one of followings:

,* [[String][valid point role string]],* Description
, [DFN[[CODE[globalStart]]]] , The first start point of the race.
, [DFN[[CODE[globalGoal]]]], The last goal point of the race.
, [DFN[[CODE[partialStart]]]] , The start point of one of the subsections of the entire route of the race (which is not categorized as [CODE[globalStart]]).
, [DFN[[CODE[partialGoal]]]] , The goal point of one of the subsections of the entire route of the race (which is not categorized as [CODE[globalGoal]]).
, [DFN[[CODE[checkpoint]]]] ," One of intermediate points during the route of the race which serves a remarkable role in the race, typically known as aid station, checkpoint, or gate."
, [DFN[[CODE[observer]]]] , A point that does not belong to any other kind.

[216] 
How to use these roles, including how points are associated with
the routes in the same document, are application dependent.

[NOTE[
[217] 
An application might impose additional context-specific constraints
for GPX document it handles.  For example, an application 
might require that the first point in the route
has to be the same location with the point whose
[CODE[pointrole]] is [CODE[globalStart]].



]NOTE]

-*-*-

[175] 
The [CODE[trkpt]] 
[[element]] in the [[GPX namespace]]
[MAY[MAY]]
have a [CODE[road]] [[attribute]]
in the [[GPX extension namespace]].
If specified,
its [[value][attribute value]]
[MUST[MUST]]
be a 
[[valid road type string]].
It is the kind or condition of the road 
at the point of the [[element]].

[176] A [DFN[valid road type string]] is one of followings:

,* [[String][valid road type string]],* Description
, [DFN[[CODE[p]]]] , A road paved by e.g. concrete or asphalt
, [DFN[[CODE[d]]]] , A dirt road
, [DFN[[CODE[u]]]] , A completely unpaved path

-*-*-

[35] 
The [CODE[trkpt]] 
[[element]] in the [[GPX namespace]]
[MAY[MAY]]
have a [CODE[todistance]] [[attribute]]
in the [[GPX extension namespace]].
If specified,
its [[value][attribute value]]
[MUST[MUST]]
be a 
[[valid floating-point number]]
that 
[[parses][rules for parsing floating-point number values]]
to a number greater than or equal to [N[0]].
It is the distance between this
and the previous
points (i.e. the points represented by this and the nearest [[previous sibling]]
[CODE[trkpt]] [[elements][element]] in the [[GPX namespace]]),
in [[meters][meter]].
If there is no previous point, 
the number [MUST[MUST]] be equal to [N[0]].
If the distance is not known,
the [[attribute]] [MUST[MUST NOT]] be specified.

[209] To [DFN[determine the distance between track points]]
[VAR[point1]] and [VAR[point2]], run these steps:

[FIG(steps)[
= [210] [[Assert]]: 
[VAR[point1]] and [VAR[point2]] 
are both contained in the [F[points]] 
of the same 
[[track segment]]
and 
[VAR[point2]]
immediately
follows
[VAR[point1]]
in the [F[points]].
= [211] 
If [VAR[point2]]'s
[F[to distance]]
is ''not'' 
[CODE[null]]:
== [212] 
Return [VAR[point2]]'s [F[to distance]].
= [213] 
Return the distance between points
([VAR[point1]]'s [F[latitide]], [VAR[point1]]'s [F[longitude]])
and
([VAR[point2]]'s [F[latitide]], [VAR[point2]]'s [F[longitude]]),
determined by an
implementation-dependent algorithm.

]FIG]

* References

[208] 
The term 
[DFN[meter]] <https://www.bipm.org/en/publications/si-brochure/metre.html>
is defined by the
[CITE[SI Brochure: The International System of Units (SI)]].

[165] This specification depends on the [CITE[Infra Standard]].
The terms
[DFN[[[list]]]],
[DFN[[[append]]]],
[DFN[[F[contains]]]],
[DFN[[[for each][For each]]]],
[DFN[[F[size]]]],
and
[DFN[[[is empty]]]],
are defined by the [CITE[Infra Standard]].

[167] 
The terms
[DFN[[[MIME type]]]]
and
[DFN[[[essence]]]]
are defined by the [CITE[MIME Sniffing Standard]].

[5] The terms 
[DFN[[[previous sibling]]]],
[DFN[[[element]]]], [DFN[[[attribute]]]],
[DFN[[[value][attribute value]]][attribute value]],
[DFN[[CODE(DOMi)@en[Document]]]],
[DFN[[F[document element]]]],
[DFN[[F[node document]]]],
[DFN[[F[children]]]], [DFN[[F[local name]]]],
and [DFN[[F[namespace]]]] are defined by the [CITE[DOM Standard]].

[34] The terms [DFN[[[ASCII digits]]]], 
[DFN[[[rules for parsing non-negative integers]]]],
[DFN[[[valid floating-point number]]]],
[DFN[[[rules for parsing floating-point number values]]]],
[DFN[[[parse a global date and time string]]]],
[DFN[[F[child text content]]]],
[DFN[[[valid time-zone offset string]]]],
[DFN[[[parse a time-zone offset string]]]],
[DFN[[[parse a URL]]]],
[DFN[[[resulting URL string]]]],
and
[DFN[[[XML parser]]]],
are defined by the [CITE[HTML Standard]].

[178] 
[[Elements][element]] in the [[GPX namespace]] and
its [[attributes][attribute]]
are defined by the GPX specification
<https://www.topografix.com/gpx.asp>.

* Data and tests

[177] 
There are JSON data files:

[REFS[
- <https://github.com/manakai/data-web-defs/blob/master/data/elements.json>
-- Documentaion <https://github.com/manakai/data-web-defs/blob/master/doc/elements.txt>
- <https://github.com/manakai/data-web-defs/blob/master/data/mime-types.json>
-- Documentaion <https://github.com/manakai/data-web-defs/blob/master/doc/mime-types.txt>
]REFS]

[348] Test data are available at:
<https://github.com/wakaba/tests-web/tree/master/gpx/parsing>.

* License

[347] Per [[CC0]] <https://creativecommons.org/publicdomain/zero/1.0/>, to the
extent possible under law, the author of this specification
has waived all copyright and related or neighboring rights to
this specification.

* Notes

[349] There are implementations:
<https://manakai.github.io/pod/Web/GPX/Parser> ([[Perl]]),
<https://github.com/wakaba/js-gpx-parser> ([[JavaScript]] [SEE[ [CODE[js-gpx-parser]] ]]).
