umap-project 2.4.1__py3-none-any.whl → 2.5.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (199) hide show
  1. umap/__init__.py +1 -1
  2. umap/locale/el/LC_MESSAGES/django.mo +0 -0
  3. umap/locale/el/LC_MESSAGES/django.po +145 -90
  4. umap/locale/en/LC_MESSAGES/django.po +13 -13
  5. umap/locale/eu/LC_MESSAGES/django.mo +0 -0
  6. umap/locale/eu/LC_MESSAGES/django.po +145 -89
  7. umap/locale/hu/LC_MESSAGES/django.mo +0 -0
  8. umap/locale/hu/LC_MESSAGES/django.po +100 -50
  9. umap/static/umap/base.css +5 -2
  10. umap/static/umap/content.css +2 -2
  11. umap/static/umap/css/contextmenu.css +11 -0
  12. umap/static/umap/css/dialog.css +25 -4
  13. umap/static/umap/css/importers.css +2 -0
  14. umap/static/umap/css/panel.css +6 -4
  15. umap/static/umap/css/slideshow.css +69 -0
  16. umap/static/umap/css/tableeditor.css +69 -0
  17. umap/static/umap/css/tooltip.css +3 -3
  18. umap/static/umap/img/16-white.svg +4 -0
  19. umap/static/umap/img/source/16-white.svg +5 -1
  20. umap/static/umap/js/components/alerts/alert.css +11 -11
  21. umap/static/umap/js/components/alerts/alert.js +1 -1
  22. umap/static/umap/js/modules/autocomplete.js +27 -5
  23. umap/static/umap/js/modules/browser.js +20 -14
  24. umap/static/umap/js/modules/caption.js +4 -4
  25. umap/static/umap/js/modules/dompurify.js +2 -3
  26. umap/static/umap/js/modules/facets.js +53 -17
  27. umap/static/umap/js/modules/formatter.js +153 -0
  28. umap/static/umap/js/modules/global.js +25 -16
  29. umap/static/umap/js/modules/help.js +26 -26
  30. umap/static/umap/js/modules/importer.js +10 -10
  31. umap/static/umap/js/modules/importers/communesfr.js +3 -1
  32. umap/static/umap/js/modules/importers/datasets.js +8 -6
  33. umap/static/umap/js/modules/importers/geodatamine.js +14 -14
  34. umap/static/umap/js/modules/importers/overpass.js +19 -15
  35. umap/static/umap/js/modules/orderable.js +2 -2
  36. umap/static/umap/js/modules/request.js +1 -1
  37. umap/static/umap/js/modules/rules.js +26 -11
  38. umap/static/umap/js/modules/schema.js +16 -12
  39. umap/static/umap/js/{umap.share.js → modules/share.js} +58 -103
  40. umap/static/umap/js/modules/slideshow.js +141 -0
  41. umap/static/umap/js/modules/sync/engine.js +3 -3
  42. umap/static/umap/js/modules/sync/updaters.js +10 -11
  43. umap/static/umap/js/modules/sync/websocket.js +1 -1
  44. umap/static/umap/js/modules/tableeditor.js +329 -0
  45. umap/static/umap/js/modules/ui/base.js +93 -0
  46. umap/static/umap/js/modules/ui/contextmenu.js +50 -0
  47. umap/static/umap/js/modules/ui/dialog.js +169 -31
  48. umap/static/umap/js/modules/ui/panel.js +7 -5
  49. umap/static/umap/js/modules/ui/tooltip.js +7 -77
  50. umap/static/umap/js/modules/urls.js +1 -2
  51. umap/static/umap/js/modules/utils.js +36 -16
  52. umap/static/umap/js/umap.controls.js +27 -29
  53. umap/static/umap/js/umap.core.js +19 -15
  54. umap/static/umap/js/umap.datalayer.permissions.js +15 -18
  55. umap/static/umap/js/umap.features.js +113 -131
  56. umap/static/umap/js/umap.forms.js +203 -228
  57. umap/static/umap/js/umap.icon.js +17 -22
  58. umap/static/umap/js/umap.js +117 -107
  59. umap/static/umap/js/umap.layer.js +374 -324
  60. umap/static/umap/js/umap.permissions.js +7 -10
  61. umap/static/umap/js/umap.popup.js +20 -20
  62. umap/static/umap/locale/am_ET.js +22 -5
  63. umap/static/umap/locale/am_ET.json +22 -5
  64. umap/static/umap/locale/ar.js +22 -5
  65. umap/static/umap/locale/ar.json +22 -5
  66. umap/static/umap/locale/ast.js +22 -5
  67. umap/static/umap/locale/ast.json +22 -5
  68. umap/static/umap/locale/bg.js +22 -5
  69. umap/static/umap/locale/bg.json +22 -5
  70. umap/static/umap/locale/br.js +22 -5
  71. umap/static/umap/locale/br.json +22 -5
  72. umap/static/umap/locale/ca.js +56 -39
  73. umap/static/umap/locale/ca.json +56 -39
  74. umap/static/umap/locale/cs_CZ.js +22 -5
  75. umap/static/umap/locale/cs_CZ.json +22 -5
  76. umap/static/umap/locale/da.js +22 -5
  77. umap/static/umap/locale/da.json +22 -5
  78. umap/static/umap/locale/de.js +22 -5
  79. umap/static/umap/locale/de.json +22 -5
  80. umap/static/umap/locale/el.js +27 -10
  81. umap/static/umap/locale/el.json +27 -10
  82. umap/static/umap/locale/en.js +22 -6
  83. umap/static/umap/locale/en.json +22 -6
  84. umap/static/umap/locale/en_US.json +22 -5
  85. umap/static/umap/locale/es.js +22 -6
  86. umap/static/umap/locale/es.json +22 -6
  87. umap/static/umap/locale/et.js +22 -5
  88. umap/static/umap/locale/et.json +22 -5
  89. umap/static/umap/locale/eu.js +167 -150
  90. umap/static/umap/locale/eu.json +167 -150
  91. umap/static/umap/locale/fa_IR.js +22 -5
  92. umap/static/umap/locale/fa_IR.json +22 -5
  93. umap/static/umap/locale/fi.js +22 -5
  94. umap/static/umap/locale/fi.json +22 -5
  95. umap/static/umap/locale/fr.js +22 -6
  96. umap/static/umap/locale/fr.json +22 -6
  97. umap/static/umap/locale/gl.js +22 -5
  98. umap/static/umap/locale/gl.json +22 -5
  99. umap/static/umap/locale/he.js +22 -5
  100. umap/static/umap/locale/he.json +22 -5
  101. umap/static/umap/locale/hr.js +22 -5
  102. umap/static/umap/locale/hr.json +22 -5
  103. umap/static/umap/locale/hu.js +89 -72
  104. umap/static/umap/locale/hu.json +89 -72
  105. umap/static/umap/locale/id.js +22 -5
  106. umap/static/umap/locale/id.json +22 -5
  107. umap/static/umap/locale/is.js +22 -5
  108. umap/static/umap/locale/is.json +22 -5
  109. umap/static/umap/locale/it.js +22 -5
  110. umap/static/umap/locale/it.json +22 -5
  111. umap/static/umap/locale/ja.js +22 -5
  112. umap/static/umap/locale/ja.json +22 -5
  113. umap/static/umap/locale/ko.js +22 -5
  114. umap/static/umap/locale/ko.json +22 -5
  115. umap/static/umap/locale/lt.js +22 -5
  116. umap/static/umap/locale/lt.json +22 -5
  117. umap/static/umap/locale/ms.js +22 -5
  118. umap/static/umap/locale/ms.json +22 -5
  119. umap/static/umap/locale/nl.js +22 -5
  120. umap/static/umap/locale/nl.json +22 -5
  121. umap/static/umap/locale/no.js +22 -5
  122. umap/static/umap/locale/no.json +22 -5
  123. umap/static/umap/locale/pl.js +22 -5
  124. umap/static/umap/locale/pl.json +22 -5
  125. umap/static/umap/locale/pl_PL.json +22 -5
  126. umap/static/umap/locale/pt.js +22 -6
  127. umap/static/umap/locale/pt.json +22 -6
  128. umap/static/umap/locale/pt_BR.js +22 -5
  129. umap/static/umap/locale/pt_BR.json +22 -5
  130. umap/static/umap/locale/pt_PT.js +22 -5
  131. umap/static/umap/locale/pt_PT.json +22 -5
  132. umap/static/umap/locale/ro.js +22 -5
  133. umap/static/umap/locale/ro.json +22 -5
  134. umap/static/umap/locale/ru.js +22 -5
  135. umap/static/umap/locale/ru.json +22 -5
  136. umap/static/umap/locale/sk_SK.js +22 -5
  137. umap/static/umap/locale/sk_SK.json +22 -5
  138. umap/static/umap/locale/sl.js +22 -5
  139. umap/static/umap/locale/sl.json +22 -5
  140. umap/static/umap/locale/sr.js +22 -5
  141. umap/static/umap/locale/sr.json +22 -5
  142. umap/static/umap/locale/sv.js +22 -5
  143. umap/static/umap/locale/sv.json +22 -5
  144. umap/static/umap/locale/th_TH.js +22 -5
  145. umap/static/umap/locale/th_TH.json +22 -5
  146. umap/static/umap/locale/tr.js +22 -5
  147. umap/static/umap/locale/tr.json +22 -5
  148. umap/static/umap/locale/uk_UA.js +22 -5
  149. umap/static/umap/locale/uk_UA.json +22 -5
  150. umap/static/umap/locale/vi.js +22 -5
  151. umap/static/umap/locale/vi.json +22 -5
  152. umap/static/umap/locale/vi_VN.json +22 -5
  153. umap/static/umap/locale/zh.js +22 -5
  154. umap/static/umap/locale/zh.json +22 -5
  155. umap/static/umap/locale/zh_CN.json +22 -5
  156. umap/static/umap/locale/zh_TW.Big5.json +22 -5
  157. umap/static/umap/locale/zh_TW.js +22 -5
  158. umap/static/umap/locale/zh_TW.json +22 -5
  159. umap/static/umap/map.css +9 -153
  160. umap/static/umap/vars.css +15 -0
  161. umap/static/umap/vendors/dompurify/purify.es.js +5 -59
  162. umap/static/umap/vendors/dompurify/purify.es.mjs.map +1 -1
  163. umap/static/umap/vendors/formbuilder/Leaflet.FormBuilder.js +410 -428
  164. umap/static/umap/vendors/geojson-to-gpx/index.js +155 -0
  165. umap/static/umap/vendors/osmtogeojson/osmtogeojson.js +1 -2
  166. umap/static/umap/vendors/togeojson/togeojson.es.js +1109 -0
  167. umap/static/umap/vendors/togeojson/{togeojson.umd.js.map → togeojson.es.mjs.map} +1 -1
  168. umap/static/umap/vendors/tokml/tokml.es.js +895 -0
  169. umap/static/umap/vendors/tokml/tokml.es.mjs.map +1 -0
  170. umap/storage.py +6 -2
  171. umap/templates/umap/components/alerts/alert.html +3 -3
  172. umap/templates/umap/css.html +3 -0
  173. umap/templates/umap/js.html +0 -6
  174. umap/tests/fixtures/categorized_highway.geojson +1 -0
  175. umap/tests/fixtures/test_import_osm_relation.json +130 -0
  176. umap/tests/integration/conftest.py +8 -1
  177. umap/tests/integration/test_browser.py +3 -2
  178. umap/tests/integration/test_categorized_layer.py +141 -0
  179. umap/tests/integration/test_conditional_rules.py +21 -0
  180. umap/tests/integration/test_datalayer.py +9 -4
  181. umap/tests/integration/test_edit_datalayer.py +1 -0
  182. umap/tests/integration/test_edit_polygon.py +1 -1
  183. umap/tests/integration/test_export_map.py +2 -3
  184. umap/tests/integration/test_import.py +22 -0
  185. umap/tests/integration/test_map_preview.py +36 -2
  186. umap/tests/integration/test_tableeditor.py +158 -4
  187. umap/tests/integration/test_websocket_sync.py +2 -2
  188. umap/tests/test_views.py +2 -2
  189. umap/views.py +3 -2
  190. {umap_project-2.4.1.dist-info → umap_project-2.5.0.dist-info}/METADATA +8 -8
  191. {umap_project-2.4.1.dist-info → umap_project-2.5.0.dist-info}/RECORD +194 -184
  192. umap/static/umap/js/umap.slideshow.js +0 -165
  193. umap/static/umap/js/umap.tableeditor.js +0 -118
  194. umap/static/umap/vendors/togeojson/togeojson.umd.js +0 -2
  195. umap/static/umap/vendors/togpx/togpx.js +0 -547
  196. umap/static/umap/vendors/tokml/tokml.js +0 -343
  197. {umap_project-2.4.1.dist-info → umap_project-2.5.0.dist-info}/WHEEL +0 -0
  198. {umap_project-2.4.1.dist-info → umap_project-2.5.0.dist-info}/entry_points.txt +0 -0
  199. {umap_project-2.4.1.dist-info → umap_project-2.5.0.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,1109 @@
1
+ function $(element, tagName) {
2
+ return Array.from(element.getElementsByTagName(tagName));
3
+ }
4
+ function normalizeId(id) {
5
+ return id[0] === "#" ? id : `#${id}`;
6
+ }
7
+ function $ns(element, tagName, ns) {
8
+ return Array.from(element.getElementsByTagNameNS(ns, tagName));
9
+ }
10
+ /**
11
+ * get the content of a text node, if any
12
+ */
13
+ function nodeVal(node) {
14
+ node?.normalize();
15
+ return (node && node.textContent) || "";
16
+ }
17
+ /**
18
+ * Get one Y child of X, if any, otherwise null
19
+ */
20
+ function get1(node, tagName, callback) {
21
+ const n = node.getElementsByTagName(tagName);
22
+ const result = n.length ? n[0] : null;
23
+ if (result && callback)
24
+ callback(result);
25
+ return result;
26
+ }
27
+ function get(node, tagName, callback) {
28
+ const properties = {};
29
+ if (!node)
30
+ return properties;
31
+ const n = node.getElementsByTagName(tagName);
32
+ const result = n.length ? n[0] : null;
33
+ if (result && callback) {
34
+ return callback(result, properties);
35
+ }
36
+ return properties;
37
+ }
38
+ function val1(node, tagName, callback) {
39
+ const val = nodeVal(get1(node, tagName));
40
+ if (val && callback)
41
+ return callback(val) || {};
42
+ return {};
43
+ }
44
+ function $num(node, tagName, callback) {
45
+ const val = parseFloat(nodeVal(get1(node, tagName)));
46
+ if (isNaN(val))
47
+ return undefined;
48
+ if (val && callback)
49
+ return callback(val) || {};
50
+ return {};
51
+ }
52
+ function num1(node, tagName, callback) {
53
+ const val = parseFloat(nodeVal(get1(node, tagName)));
54
+ if (isNaN(val))
55
+ return undefined;
56
+ if (callback)
57
+ callback(val);
58
+ return val;
59
+ }
60
+ function getMulti(node, propertyNames) {
61
+ const properties = {};
62
+ for (const property of propertyNames) {
63
+ val1(node, property, (val) => {
64
+ properties[property] = val;
65
+ });
66
+ }
67
+ return properties;
68
+ }
69
+ function isElement(node) {
70
+ return node?.nodeType === 1;
71
+ }
72
+
73
+ function getLineStyle(node) {
74
+ return get(node, "line", (lineStyle) => {
75
+ const val = Object.assign({}, val1(lineStyle, "color", (color) => {
76
+ return { stroke: `#${color}` };
77
+ }), $num(lineStyle, "opacity", (opacity) => {
78
+ return { "stroke-opacity": opacity };
79
+ }), $num(lineStyle, "width", (width) => {
80
+ // GPX width is in mm, convert to px with 96 px per inch
81
+ return { "stroke-width": (width * 96) / 25.4 };
82
+ }));
83
+ return val;
84
+ });
85
+ }
86
+
87
+ function getExtensions(node) {
88
+ let values = [];
89
+ if (node === null)
90
+ return values;
91
+ for (const child of Array.from(node.childNodes)) {
92
+ if (!isElement(child))
93
+ continue;
94
+ const name = abbreviateName(child.nodeName);
95
+ if (name === "gpxtpx:TrackPointExtension") {
96
+ // loop again for nested garmin extensions (eg. "gpxtpx:hr")
97
+ values = values.concat(getExtensions(child));
98
+ }
99
+ else {
100
+ // push custom extension (eg. "power")
101
+ const val = nodeVal(child);
102
+ values.push([name, parseNumeric(val)]);
103
+ }
104
+ }
105
+ return values;
106
+ }
107
+ function abbreviateName(name) {
108
+ return ["heart", "gpxtpx:hr", "hr"].includes(name) ? "heart" : name;
109
+ }
110
+ function parseNumeric(val) {
111
+ const num = parseFloat(val);
112
+ return isNaN(num) ? val : num;
113
+ }
114
+
115
+ function coordPair$1(node) {
116
+ const ll = [
117
+ parseFloat(node.getAttribute("lon") || ""),
118
+ parseFloat(node.getAttribute("lat") || ""),
119
+ ];
120
+ if (isNaN(ll[0]) || isNaN(ll[1])) {
121
+ return null;
122
+ }
123
+ num1(node, "ele", (val) => {
124
+ ll.push(val);
125
+ });
126
+ const time = get1(node, "time");
127
+ return {
128
+ coordinates: ll,
129
+ time: time ? nodeVal(time) : null,
130
+ extendedValues: getExtensions(get1(node, "extensions")),
131
+ };
132
+ }
133
+
134
+ function extractProperties(node) {
135
+ const properties = getMulti(node, [
136
+ "name",
137
+ "cmt",
138
+ "desc",
139
+ "type",
140
+ "time",
141
+ "keywords",
142
+ ]);
143
+ const extensions = Array.from(node.getElementsByTagNameNS("http://www.garmin.com/xmlschemas/GpxExtensions/v3", "*"));
144
+ for (const child of extensions) {
145
+ if (child.parentNode?.parentNode === node) {
146
+ properties[child.tagName.replace(":", "_")] = nodeVal(child);
147
+ }
148
+ }
149
+ const links = $(node, "link");
150
+ if (links.length) {
151
+ properties.links = links.map((link) => Object.assign({ href: link.getAttribute("href") }, getMulti(link, ["text", "type"])));
152
+ }
153
+ return properties;
154
+ }
155
+
156
+ /**
157
+ * Extract points from a trkseg or rte element.
158
+ */
159
+ function getPoints$1(node, pointname) {
160
+ const pts = $(node, pointname);
161
+ const line = [];
162
+ const times = [];
163
+ const extendedValues = {};
164
+ for (let i = 0; i < pts.length; i++) {
165
+ const c = coordPair$1(pts[i]);
166
+ if (!c) {
167
+ continue;
168
+ }
169
+ line.push(c.coordinates);
170
+ if (c.time)
171
+ times.push(c.time);
172
+ for (const [name, val] of c.extendedValues) {
173
+ const plural = name === "heart" ? name : name.replace("gpxtpx:", "") + "s";
174
+ if (!extendedValues[plural]) {
175
+ extendedValues[plural] = Array(pts.length).fill(null);
176
+ }
177
+ extendedValues[plural][i] = val;
178
+ }
179
+ }
180
+ if (line.length < 2)
181
+ return; // Invalid line in GeoJSON
182
+ return {
183
+ line: line,
184
+ times: times,
185
+ extendedValues: extendedValues,
186
+ };
187
+ }
188
+ /**
189
+ * Extract a LineString geometry from a rte
190
+ * element.
191
+ */
192
+ function getRoute(node) {
193
+ const line = getPoints$1(node, "rtept");
194
+ if (!line)
195
+ return;
196
+ return {
197
+ type: "Feature",
198
+ properties: Object.assign({ _gpxType: "rte" }, extractProperties(node), getLineStyle(get1(node, "extensions"))),
199
+ geometry: {
200
+ type: "LineString",
201
+ coordinates: line.line,
202
+ },
203
+ };
204
+ }
205
+ function getTrack(node) {
206
+ const segments = $(node, "trkseg");
207
+ const track = [];
208
+ const times = [];
209
+ const extractedLines = [];
210
+ for (const segment of segments) {
211
+ const line = getPoints$1(segment, "trkpt");
212
+ if (line) {
213
+ extractedLines.push(line);
214
+ if (line.times && line.times.length)
215
+ times.push(line.times);
216
+ }
217
+ }
218
+ if (extractedLines.length === 0)
219
+ return null;
220
+ const multi = extractedLines.length > 1;
221
+ const properties = Object.assign({ _gpxType: "trk" }, extractProperties(node), getLineStyle(get1(node, "extensions")), times.length
222
+ ? {
223
+ coordinateProperties: {
224
+ times: multi ? times : times[0],
225
+ },
226
+ }
227
+ : {});
228
+ for (const line of extractedLines) {
229
+ track.push(line.line);
230
+ if (!properties.coordinateProperties) {
231
+ properties.coordinateProperties = {};
232
+ }
233
+ const props = properties.coordinateProperties;
234
+ const entries = Object.entries(line.extendedValues);
235
+ for (let i = 0; i < entries.length; i++) {
236
+ const [name, val] = entries[i];
237
+ if (multi) {
238
+ if (!props[name]) {
239
+ props[name] = extractedLines.map((line) => new Array(line.line.length).fill(null));
240
+ }
241
+ props[name][i] = val;
242
+ }
243
+ else {
244
+ props[name] = val;
245
+ }
246
+ }
247
+ }
248
+ return {
249
+ type: "Feature",
250
+ properties: properties,
251
+ geometry: multi
252
+ ? {
253
+ type: "MultiLineString",
254
+ coordinates: track,
255
+ }
256
+ : {
257
+ type: "LineString",
258
+ coordinates: track[0],
259
+ },
260
+ };
261
+ }
262
+ /**
263
+ * Extract a point, if possible, from a given node,
264
+ * which is usually a wpt or trkpt
265
+ */
266
+ function getPoint(node) {
267
+ const properties = Object.assign(extractProperties(node), getMulti(node, ["sym"]));
268
+ const pair = coordPair$1(node);
269
+ if (!pair)
270
+ return null;
271
+ return {
272
+ type: "Feature",
273
+ properties,
274
+ geometry: {
275
+ type: "Point",
276
+ coordinates: pair.coordinates,
277
+ },
278
+ };
279
+ }
280
+ /**
281
+ * Convert GPX to GeoJSON incrementally, returning
282
+ * a [Generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators)
283
+ * that yields output feature by feature.
284
+ */
285
+ function* gpxGen(node) {
286
+ for (const track of $(node, "trk")) {
287
+ const feature = getTrack(track);
288
+ if (feature)
289
+ yield feature;
290
+ }
291
+ for (const route of $(node, "rte")) {
292
+ const feature = getRoute(route);
293
+ if (feature)
294
+ yield feature;
295
+ }
296
+ for (const waypoint of $(node, "wpt")) {
297
+ const point = getPoint(waypoint);
298
+ if (point)
299
+ yield point;
300
+ }
301
+ }
302
+ /**
303
+ *
304
+ * Convert a GPX document to GeoJSON. The first argument, `doc`, must be a GPX
305
+ * document as an XML DOM - not as a string. You can get this using jQuery's default
306
+ * `.ajax` function or using a bare XMLHttpRequest with the `.response` property
307
+ * holding an XML DOM.
308
+ *
309
+ * The output is a JavaScript object of GeoJSON data, same as `.kml` outputs, with the
310
+ * addition of a `_gpxType` property on each `LineString` feature that indicates whether
311
+ * the feature was encoded as a route (`rte`) or track (`trk`) in the GPX document.
312
+ */
313
+ function gpx(node) {
314
+ return {
315
+ type: "FeatureCollection",
316
+ features: Array.from(gpxGen(node)),
317
+ };
318
+ }
319
+
320
+ const EXTENSIONS_NS = "http://www.garmin.com/xmlschemas/ActivityExtension/v2";
321
+ const TRACKPOINT_ATTRIBUTES = [
322
+ ["heartRate", "heartRates"],
323
+ ["Cadence", "cadences"],
324
+ // Extended Trackpoint attributes
325
+ ["Speed", "speeds"],
326
+ ["Watts", "watts"],
327
+ ];
328
+ const LAP_ATTRIBUTES = [
329
+ ["TotalTimeSeconds", "totalTimeSeconds"],
330
+ ["DistanceMeters", "distanceMeters"],
331
+ ["MaximumSpeed", "maxSpeed"],
332
+ ["AverageHeartRateBpm", "avgHeartRate"],
333
+ ["MaximumHeartRateBpm", "maxHeartRate"],
334
+ // Extended Lap attributes
335
+ ["AvgSpeed", "avgSpeed"],
336
+ ["AvgWatts", "avgWatts"],
337
+ ["MaxWatts", "maxWatts"],
338
+ ];
339
+ function getProperties(node, attributeNames) {
340
+ const properties = [];
341
+ for (const [tag, alias] of attributeNames) {
342
+ let elem = get1(node, tag);
343
+ if (!elem) {
344
+ const elements = node.getElementsByTagNameNS(EXTENSIONS_NS, tag);
345
+ if (elements.length) {
346
+ elem = elements[0];
347
+ }
348
+ }
349
+ const val = parseFloat(nodeVal(elem));
350
+ if (!isNaN(val)) {
351
+ properties.push([alias, val]);
352
+ }
353
+ }
354
+ return properties;
355
+ }
356
+ function coordPair(node) {
357
+ const ll = [num1(node, "LongitudeDegrees"), num1(node, "LatitudeDegrees")];
358
+ if (ll[0] === undefined ||
359
+ isNaN(ll[0]) ||
360
+ ll[1] === undefined ||
361
+ isNaN(ll[1])) {
362
+ return null;
363
+ }
364
+ const heartRate = get1(node, "HeartRateBpm");
365
+ const time = nodeVal(get1(node, "Time"));
366
+ get1(node, "AltitudeMeters", (alt) => {
367
+ const a = parseFloat(nodeVal(alt));
368
+ if (!isNaN(a)) {
369
+ ll.push(a);
370
+ }
371
+ });
372
+ return {
373
+ coordinates: ll,
374
+ time: time || null,
375
+ heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null,
376
+ extensions: getProperties(node, TRACKPOINT_ATTRIBUTES),
377
+ };
378
+ }
379
+ function getPoints(node) {
380
+ const pts = $(node, "Trackpoint");
381
+ const line = [];
382
+ const times = [];
383
+ const heartRates = [];
384
+ if (pts.length < 2)
385
+ return null; // Invalid line in GeoJSON
386
+ const extendedProperties = {};
387
+ const result = { extendedProperties };
388
+ for (let i = 0; i < pts.length; i++) {
389
+ const c = coordPair(pts[i]);
390
+ if (c === null)
391
+ continue;
392
+ line.push(c.coordinates);
393
+ const { time, heartRate, extensions } = c;
394
+ if (time)
395
+ times.push(time);
396
+ if (heartRate)
397
+ heartRates.push(heartRate);
398
+ for (const [alias, value] of extensions) {
399
+ if (!extendedProperties[alias]) {
400
+ extendedProperties[alias] = Array(pts.length).fill(null);
401
+ }
402
+ extendedProperties[alias][i] = value;
403
+ }
404
+ }
405
+ if (line.length < 2)
406
+ return null;
407
+ return Object.assign(result, {
408
+ line: line,
409
+ times: times,
410
+ heartRates: heartRates,
411
+ });
412
+ }
413
+ function getLap(node) {
414
+ const segments = $(node, "Track");
415
+ const track = [];
416
+ const times = [];
417
+ const heartRates = [];
418
+ const allExtendedProperties = [];
419
+ let line;
420
+ const properties = Object.assign(Object.fromEntries(getProperties(node, LAP_ATTRIBUTES)), get(node, "Name", (nameElement) => {
421
+ return { name: nodeVal(nameElement) };
422
+ }));
423
+ for (const segment of segments) {
424
+ line = getPoints(segment);
425
+ if (line) {
426
+ track.push(line.line);
427
+ if (line.times.length)
428
+ times.push(line.times);
429
+ if (line.heartRates.length)
430
+ heartRates.push(line.heartRates);
431
+ allExtendedProperties.push(line.extendedProperties);
432
+ }
433
+ }
434
+ for (let i = 0; i < allExtendedProperties.length; i++) {
435
+ const extendedProperties = allExtendedProperties[i];
436
+ for (const property in extendedProperties) {
437
+ if (segments.length === 1) {
438
+ if (line) {
439
+ properties[property] = line.extendedProperties[property];
440
+ }
441
+ }
442
+ else {
443
+ if (!properties[property]) {
444
+ properties[property] = track.map((track) => Array(track.length).fill(null));
445
+ }
446
+ properties[property][i] = extendedProperties[property];
447
+ }
448
+ }
449
+ }
450
+ if (track.length === 0)
451
+ return null;
452
+ if (times.length || heartRates.length) {
453
+ properties.coordinateProperties = Object.assign(times.length
454
+ ? {
455
+ times: track.length === 1 ? times[0] : times,
456
+ }
457
+ : {}, heartRates.length
458
+ ? {
459
+ heart: track.length === 1 ? heartRates[0] : heartRates,
460
+ }
461
+ : {});
462
+ }
463
+ return {
464
+ type: "Feature",
465
+ properties: properties,
466
+ geometry: track.length === 1
467
+ ? {
468
+ type: "LineString",
469
+ coordinates: track[0],
470
+ }
471
+ : {
472
+ type: "MultiLineString",
473
+ coordinates: track,
474
+ },
475
+ };
476
+ }
477
+ /**
478
+ * Incrementally convert a TCX document to GeoJSON. The
479
+ * first argument, `doc`, must be a TCX
480
+ * document as an XML DOM - not as a string.
481
+ */
482
+ function* tcxGen(node) {
483
+ for (const lap of $(node, "Lap")) {
484
+ const feature = getLap(lap);
485
+ if (feature)
486
+ yield feature;
487
+ }
488
+ for (const course of $(node, "Courses")) {
489
+ const feature = getLap(course);
490
+ if (feature)
491
+ yield feature;
492
+ }
493
+ }
494
+ /**
495
+ * Convert a TCX document to GeoJSON. The first argument, `doc`, must be a TCX
496
+ * document as an XML DOM - not as a string.
497
+ */
498
+ function tcx(node) {
499
+ return {
500
+ type: "FeatureCollection",
501
+ features: Array.from(tcxGen(node)),
502
+ };
503
+ }
504
+
505
+ function fixColor(v, prefix) {
506
+ const properties = {};
507
+ const colorProp = prefix == "stroke" || prefix === "fill" ? prefix : prefix + "-color";
508
+ if (v[0] === "#") {
509
+ v = v.substring(1);
510
+ }
511
+ if (v.length === 6 || v.length === 3) {
512
+ properties[colorProp] = "#" + v;
513
+ }
514
+ else if (v.length === 8) {
515
+ properties[prefix + "-opacity"] = parseInt(v.substring(0, 2), 16) / 255;
516
+ properties[colorProp] =
517
+ "#" + v.substring(6, 8) + v.substring(4, 6) + v.substring(2, 4);
518
+ }
519
+ return properties;
520
+ }
521
+
522
+ function numericProperty(node, source, target) {
523
+ const properties = {};
524
+ num1(node, source, (val) => {
525
+ properties[target] = val;
526
+ });
527
+ return properties;
528
+ }
529
+ function getColor(node, output) {
530
+ return get(node, "color", (elem) => fixColor(nodeVal(elem), output));
531
+ }
532
+ function extractIconHref(node) {
533
+ return get(node, "Icon", (icon, properties) => {
534
+ val1(icon, "href", (href) => {
535
+ properties.icon = href;
536
+ });
537
+ return properties;
538
+ });
539
+ }
540
+ function extractIcon(node) {
541
+ return get(node, "IconStyle", (iconStyle) => {
542
+ return Object.assign(getColor(iconStyle, "icon"), numericProperty(iconStyle, "scale", "icon-scale"), numericProperty(iconStyle, "heading", "icon-heading"), get(iconStyle, "hotSpot", (hotspot) => {
543
+ const left = parseFloat(hotspot.getAttribute("x") || "");
544
+ const top = parseFloat(hotspot.getAttribute("y") || "");
545
+ const xunits = hotspot.getAttribute("xunits") || "";
546
+ const yunits = hotspot.getAttribute("yunits") || "";
547
+ if (!isNaN(left) && !isNaN(top))
548
+ return {
549
+ "icon-offset": [left, top],
550
+ "icon-offset-units": [xunits, yunits],
551
+ };
552
+ return {};
553
+ }), extractIconHref(iconStyle));
554
+ });
555
+ }
556
+ function extractLabel(node) {
557
+ return get(node, "LabelStyle", (labelStyle) => {
558
+ return Object.assign(getColor(labelStyle, "label"), numericProperty(labelStyle, "scale", "label-scale"));
559
+ });
560
+ }
561
+ function extractLine(node) {
562
+ return get(node, "LineStyle", (lineStyle) => {
563
+ return Object.assign(getColor(lineStyle, "stroke"), numericProperty(lineStyle, "width", "stroke-width"));
564
+ });
565
+ }
566
+ function extractPoly(node) {
567
+ return get(node, "PolyStyle", (polyStyle, properties) => {
568
+ return Object.assign(properties, get(polyStyle, "color", (elem) => fixColor(nodeVal(elem), "fill")), val1(polyStyle, "fill", (fill) => {
569
+ if (fill === "0")
570
+ return { "fill-opacity": 0 };
571
+ }), val1(polyStyle, "outline", (outline) => {
572
+ if (outline === "0")
573
+ return { "stroke-opacity": 0 };
574
+ }));
575
+ });
576
+ }
577
+ function extractStyle(node) {
578
+ return Object.assign({}, extractPoly(node), extractLine(node), extractLabel(node), extractIcon(node));
579
+ }
580
+
581
+ const toNumber = (x) => Number(x);
582
+ const typeConverters = {
583
+ string: (x) => x,
584
+ int: toNumber,
585
+ uint: toNumber,
586
+ short: toNumber,
587
+ ushort: toNumber,
588
+ float: toNumber,
589
+ double: toNumber,
590
+ bool: (x) => Boolean(x),
591
+ };
592
+ function extractExtendedData(node, schema) {
593
+ return get(node, "ExtendedData", (extendedData, properties) => {
594
+ for (const data of $(extendedData, "Data")) {
595
+ properties[data.getAttribute("name") || ""] = nodeVal(get1(data, "value"));
596
+ }
597
+ for (const simpleData of $(extendedData, "SimpleData")) {
598
+ const name = simpleData.getAttribute("name") || "";
599
+ const typeConverter = schema[name] || typeConverters.string;
600
+ properties[name] = typeConverter(nodeVal(simpleData));
601
+ }
602
+ return properties;
603
+ });
604
+ }
605
+ function getMaybeHTMLDescription(node) {
606
+ const descriptionNode = get1(node, "description");
607
+ for (const c of Array.from(descriptionNode?.childNodes || [])) {
608
+ if (c.nodeType === 4) {
609
+ return {
610
+ description: {
611
+ "@type": "html",
612
+ value: nodeVal(c),
613
+ },
614
+ };
615
+ }
616
+ }
617
+ return {};
618
+ }
619
+ function extractTimeSpan(node) {
620
+ return get(node, "TimeSpan", (timeSpan) => {
621
+ return {
622
+ timespan: {
623
+ begin: nodeVal(get1(timeSpan, "begin")),
624
+ end: nodeVal(get1(timeSpan, "end")),
625
+ },
626
+ };
627
+ });
628
+ }
629
+ function extractTimeStamp(node) {
630
+ return get(node, "TimeStamp", (timeStamp) => {
631
+ return { timestamp: nodeVal(get1(timeStamp, "when")) };
632
+ });
633
+ }
634
+ function extractCascadedStyle(node, styleMap) {
635
+ return val1(node, "styleUrl", (styleUrl) => {
636
+ styleUrl = normalizeId(styleUrl);
637
+ if (styleMap[styleUrl]) {
638
+ return Object.assign({ styleUrl }, styleMap[styleUrl]);
639
+ }
640
+ // For backward-compatibility. Should we still include
641
+ // styleUrl even if it's not resolved?
642
+ return { styleUrl };
643
+ });
644
+ }
645
+
646
+ const removeSpace = /\s*/g;
647
+ const trimSpace = /^\s*|\s*$/g;
648
+ const splitSpace = /\s+/;
649
+ /**
650
+ * Get one coordinate from a coordinate array, if any
651
+ */
652
+ function coord1(value) {
653
+ return value
654
+ .replace(removeSpace, "")
655
+ .split(",")
656
+ .map(parseFloat)
657
+ .filter((num) => !isNaN(num))
658
+ .slice(0, 3);
659
+ }
660
+ /**
661
+ * Get all coordinates from a coordinate array as [[],[]]
662
+ */
663
+ function coord(value) {
664
+ return value
665
+ .replace(trimSpace, "")
666
+ .split(splitSpace)
667
+ .map(coord1)
668
+ .filter((coord) => {
669
+ return coord.length >= 2;
670
+ });
671
+ }
672
+ function gxCoords(node) {
673
+ let elems = $(node, "coord");
674
+ if (elems.length === 0) {
675
+ elems = $ns(node, "coord", "*");
676
+ }
677
+ const coordinates = elems.map((elem) => {
678
+ return nodeVal(elem).split(" ").map(parseFloat);
679
+ });
680
+ if (coordinates.length === 0) {
681
+ return null;
682
+ }
683
+ return {
684
+ geometry: coordinates.length > 2
685
+ ? {
686
+ type: "LineString",
687
+ coordinates,
688
+ }
689
+ : {
690
+ type: "Point",
691
+ coordinates: coordinates[0],
692
+ },
693
+ times: $(node, "when").map((elem) => nodeVal(elem)),
694
+ };
695
+ }
696
+ function fixRing(ring) {
697
+ if (ring.length === 0)
698
+ return ring;
699
+ const first = ring[0];
700
+ const last = ring[ring.length - 1];
701
+ let equal = true;
702
+ for (let i = 0; i < Math.max(first.length, last.length); i++) {
703
+ if (first[i] !== last[i]) {
704
+ equal = false;
705
+ break;
706
+ }
707
+ }
708
+ if (!equal) {
709
+ return ring.concat([ring[0]]);
710
+ }
711
+ return ring;
712
+ }
713
+ function getCoordinates(node) {
714
+ return nodeVal(get1(node, "coordinates"));
715
+ }
716
+ function getGeometry(node) {
717
+ let geometries = [];
718
+ let coordTimes = [];
719
+ for (let i = 0; i < node.childNodes.length; i++) {
720
+ const child = node.childNodes.item(i);
721
+ if (isElement(child)) {
722
+ switch (child.tagName) {
723
+ case "MultiGeometry":
724
+ case "MultiTrack":
725
+ case "gx:MultiTrack": {
726
+ const childGeometries = getGeometry(child);
727
+ geometries = geometries.concat(childGeometries.geometries);
728
+ coordTimes = coordTimes.concat(childGeometries.coordTimes);
729
+ break;
730
+ }
731
+ case "Point": {
732
+ const coordinates = coord1(getCoordinates(child));
733
+ if (coordinates.length >= 2) {
734
+ geometries.push({
735
+ type: "Point",
736
+ coordinates,
737
+ });
738
+ }
739
+ break;
740
+ }
741
+ case "LinearRing":
742
+ case "LineString": {
743
+ const coordinates = coord(getCoordinates(child));
744
+ if (coordinates.length >= 2) {
745
+ geometries.push({
746
+ type: "LineString",
747
+ coordinates,
748
+ });
749
+ }
750
+ break;
751
+ }
752
+ case "Polygon": {
753
+ const coords = [];
754
+ for (const linearRing of $(child, "LinearRing")) {
755
+ const ring = fixRing(coord(getCoordinates(linearRing)));
756
+ if (ring.length >= 4) {
757
+ coords.push(ring);
758
+ }
759
+ }
760
+ if (coords.length) {
761
+ geometries.push({
762
+ type: "Polygon",
763
+ coordinates: coords,
764
+ });
765
+ }
766
+ break;
767
+ }
768
+ case "Track":
769
+ case "gx:Track": {
770
+ const gx = gxCoords(child);
771
+ if (!gx)
772
+ break;
773
+ const { times, geometry } = gx;
774
+ geometries.push(geometry);
775
+ if (times.length)
776
+ coordTimes.push(times);
777
+ break;
778
+ }
779
+ }
780
+ }
781
+ }
782
+ return {
783
+ geometries,
784
+ coordTimes,
785
+ };
786
+ }
787
+
788
+ function geometryListToGeometry(geometries) {
789
+ return geometries.length === 0
790
+ ? null
791
+ : geometries.length === 1
792
+ ? geometries[0]
793
+ : {
794
+ type: "GeometryCollection",
795
+ geometries,
796
+ };
797
+ }
798
+ function getPlacemark(node, styleMap, schema, options) {
799
+ const { coordTimes, geometries } = getGeometry(node);
800
+ const geometry = geometryListToGeometry(geometries);
801
+ if (!geometry && options.skipNullGeometry) {
802
+ return null;
803
+ }
804
+ const feature = {
805
+ type: "Feature",
806
+ geometry,
807
+ properties: Object.assign(getMulti(node, [
808
+ "name",
809
+ "address",
810
+ "visibility",
811
+ "open",
812
+ "phoneNumber",
813
+ "description",
814
+ ]), getMaybeHTMLDescription(node), extractCascadedStyle(node, styleMap), extractStyle(node), extractExtendedData(node, schema), extractTimeSpan(node), extractTimeStamp(node), coordTimes.length
815
+ ? {
816
+ coordinateProperties: {
817
+ times: coordTimes.length === 1 ? coordTimes[0] : coordTimes,
818
+ },
819
+ }
820
+ : {}),
821
+ };
822
+ if (feature.properties?.visibility !== undefined) {
823
+ feature.properties.visibility = feature.properties.visibility !== "0";
824
+ }
825
+ const id = node.getAttribute("id");
826
+ if (id !== null && id !== "")
827
+ feature.id = id;
828
+ return feature;
829
+ }
830
+
831
+ function getGroundOverlayBox(node) {
832
+ const latLonQuad = get1(node, "gx:LatLonQuad");
833
+ if (latLonQuad) {
834
+ const ring = fixRing(coord(getCoordinates(node)));
835
+ return {
836
+ geometry: {
837
+ type: "Polygon",
838
+ coordinates: [ring],
839
+ },
840
+ };
841
+ }
842
+ return getLatLonBox(node);
843
+ }
844
+ const DEGREES_TO_RADIANS = Math.PI / 180;
845
+ function rotateBox(bbox, coordinates, rotation) {
846
+ const center = [(bbox[0] + bbox[2]) / 2, (bbox[1] + bbox[3]) / 2];
847
+ return [
848
+ coordinates[0].map((coordinate) => {
849
+ const dy = coordinate[1] - center[1];
850
+ const dx = coordinate[0] - center[0];
851
+ const distance = Math.sqrt(Math.pow(dy, 2) + Math.pow(dx, 2));
852
+ const angle = Math.atan2(dy, dx) + rotation * DEGREES_TO_RADIANS;
853
+ return [
854
+ center[0] + Math.cos(angle) * distance,
855
+ center[1] + Math.sin(angle) * distance,
856
+ ];
857
+ }),
858
+ ];
859
+ }
860
+ function getLatLonBox(node) {
861
+ const latLonBox = get1(node, "LatLonBox");
862
+ if (latLonBox) {
863
+ const north = num1(latLonBox, "north");
864
+ const west = num1(latLonBox, "west");
865
+ const east = num1(latLonBox, "east");
866
+ const south = num1(latLonBox, "south");
867
+ const rotation = num1(latLonBox, "rotation");
868
+ if (typeof north === "number" &&
869
+ typeof south === "number" &&
870
+ typeof west === "number" &&
871
+ typeof east === "number") {
872
+ const bbox = [west, south, east, north];
873
+ let coordinates = [
874
+ [
875
+ [west, north],
876
+ [east, north],
877
+ [east, south],
878
+ [west, south],
879
+ [west, north], // top left (again)
880
+ ],
881
+ ];
882
+ if (typeof rotation === "number") {
883
+ coordinates = rotateBox(bbox, coordinates, rotation);
884
+ }
885
+ return {
886
+ bbox,
887
+ geometry: {
888
+ type: "Polygon",
889
+ coordinates,
890
+ },
891
+ };
892
+ }
893
+ }
894
+ return null;
895
+ }
896
+ function getGroundOverlay(node, styleMap, schema, options) {
897
+ const box = getGroundOverlayBox(node);
898
+ const geometry = box?.geometry || null;
899
+ if (!geometry && options.skipNullGeometry) {
900
+ return null;
901
+ }
902
+ const feature = {
903
+ type: "Feature",
904
+ geometry,
905
+ properties: Object.assign(
906
+ /**
907
+ * Related to
908
+ * https://gist.github.com/tmcw/037a1cb6660d74a392e9da7446540f46
909
+ */
910
+ { "@geometry-type": "groundoverlay" }, getMulti(node, [
911
+ "name",
912
+ "address",
913
+ "visibility",
914
+ "open",
915
+ "phoneNumber",
916
+ "description",
917
+ ]), getMaybeHTMLDescription(node), extractCascadedStyle(node, styleMap), extractStyle(node), extractIconHref(node), extractExtendedData(node, schema), extractTimeSpan(node), extractTimeStamp(node)),
918
+ };
919
+ if (box?.bbox) {
920
+ feature.bbox = box.bbox;
921
+ }
922
+ if (feature.properties?.visibility !== undefined) {
923
+ feature.properties.visibility = feature.properties.visibility !== "0";
924
+ }
925
+ const id = node.getAttribute("id");
926
+ if (id !== null && id !== "")
927
+ feature.id = id;
928
+ return feature;
929
+ }
930
+
931
+ function getStyleId(style) {
932
+ let id = style.getAttribute("id");
933
+ const parentNode = style.parentNode;
934
+ if (!id &&
935
+ isElement(parentNode) &&
936
+ parentNode.localName === "CascadingStyle") {
937
+ id = parentNode.getAttribute("kml:id") || parentNode.getAttribute("id");
938
+ }
939
+ return normalizeId(id || "");
940
+ }
941
+ function buildStyleMap(node) {
942
+ const styleMap = {};
943
+ for (const style of $(node, "Style")) {
944
+ styleMap[getStyleId(style)] = extractStyle(style);
945
+ }
946
+ for (const map of $(node, "StyleMap")) {
947
+ const id = normalizeId(map.getAttribute("id") || "");
948
+ val1(map, "styleUrl", (styleUrl) => {
949
+ styleUrl = normalizeId(styleUrl);
950
+ if (styleMap[styleUrl]) {
951
+ styleMap[id] = styleMap[styleUrl];
952
+ }
953
+ });
954
+ }
955
+ return styleMap;
956
+ }
957
+ function buildSchema(node) {
958
+ const schema = {};
959
+ for (const field of $(node, "SimpleField")) {
960
+ schema[field.getAttribute("name") || ""] =
961
+ typeConverters[field.getAttribute("type") || ""] ||
962
+ typeConverters["string"];
963
+ }
964
+ return schema;
965
+ }
966
+ const FOLDER_PROPS = [
967
+ "name",
968
+ "visibility",
969
+ "open",
970
+ "address",
971
+ "description",
972
+ "phoneNumber",
973
+ "visibility",
974
+ ];
975
+ function getFolder(node) {
976
+ const meta = {};
977
+ for (const child of Array.from(node.childNodes)) {
978
+ if (isElement(child) && FOLDER_PROPS.includes(child.tagName)) {
979
+ meta[child.tagName] = nodeVal(child);
980
+ }
981
+ }
982
+ return {
983
+ type: "folder",
984
+ meta,
985
+ children: [],
986
+ };
987
+ }
988
+ /**
989
+ * Yield a nested tree with KML folder structure
990
+ *
991
+ * This generates a tree with the given structure:
992
+ *
993
+ * ```js
994
+ * {
995
+ * "type": "root",
996
+ * "children": [
997
+ * {
998
+ * "type": "folder",
999
+ * "meta": {
1000
+ * "name": "Test"
1001
+ * },
1002
+ * "children": [
1003
+ * // ...features and folders
1004
+ * ]
1005
+ * }
1006
+ * // ...features
1007
+ * ]
1008
+ * }
1009
+ * ```
1010
+ *
1011
+ * ### GroundOverlay
1012
+ *
1013
+ * GroundOverlay elements are converted into
1014
+ * `Feature` objects with `Polygon` geometries,
1015
+ * a property like:
1016
+ *
1017
+ * ```json
1018
+ * {
1019
+ * "@geometry-type": "groundoverlay"
1020
+ * }
1021
+ * ```
1022
+ *
1023
+ * And the ground overlay's image URL in the `href`
1024
+ * property. Ground overlays will need to be displayed
1025
+ * with a separate method to other features, depending
1026
+ * on which map framework you're using.
1027
+ */
1028
+ function kmlWithFolders(node, options = {
1029
+ skipNullGeometry: false,
1030
+ }) {
1031
+ const styleMap = buildStyleMap(node);
1032
+ const schema = buildSchema(node);
1033
+ const tree = { type: "root", children: [] };
1034
+ function traverse(node, pointer, options) {
1035
+ if (isElement(node)) {
1036
+ switch (node.tagName) {
1037
+ case "GroundOverlay": {
1038
+ const placemark = getGroundOverlay(node, styleMap, schema, options);
1039
+ if (placemark) {
1040
+ pointer.children.push(placemark);
1041
+ }
1042
+ break;
1043
+ }
1044
+ case "Placemark": {
1045
+ const placemark = getPlacemark(node, styleMap, schema, options);
1046
+ if (placemark) {
1047
+ pointer.children.push(placemark);
1048
+ }
1049
+ break;
1050
+ }
1051
+ case "Folder": {
1052
+ const folder = getFolder(node);
1053
+ pointer.children.push(folder);
1054
+ pointer = folder;
1055
+ break;
1056
+ }
1057
+ }
1058
+ }
1059
+ if (node.childNodes) {
1060
+ for (let i = 0; i < node.childNodes.length; i++) {
1061
+ traverse(node.childNodes[i], pointer, options);
1062
+ }
1063
+ }
1064
+ }
1065
+ traverse(node, tree, options);
1066
+ return tree;
1067
+ }
1068
+ /**
1069
+ * Convert KML to GeoJSON incrementally, returning
1070
+ * a [Generator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators)
1071
+ * that yields output feature by feature.
1072
+ */
1073
+ function* kmlGen(node, options = {
1074
+ skipNullGeometry: false,
1075
+ }) {
1076
+ const styleMap = buildStyleMap(node);
1077
+ const schema = buildSchema(node);
1078
+ for (const placemark of $(node, "Placemark")) {
1079
+ const feature = getPlacemark(placemark, styleMap, schema, options);
1080
+ if (feature)
1081
+ yield feature;
1082
+ }
1083
+ for (const groundOverlay of $(node, "GroundOverlay")) {
1084
+ const feature = getGroundOverlay(groundOverlay, styleMap, schema, options);
1085
+ if (feature)
1086
+ yield feature;
1087
+ }
1088
+ }
1089
+ /**
1090
+ * Convert a KML document to GeoJSON. The first argument, `doc`, must be a KML
1091
+ * document as an XML DOM - not as a string. You can get this using jQuery's default
1092
+ * `.ajax` function or using a bare XMLHttpRequest with the `.response` property
1093
+ * holding an XML DOM.
1094
+ *
1095
+ * The output is a JavaScript object of GeoJSON data. You can convert it to a string
1096
+ * with [JSON.stringify](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify)
1097
+ * or use it directly in libraries.
1098
+ */
1099
+ function kml(node, options = {
1100
+ skipNullGeometry: false,
1101
+ }) {
1102
+ return {
1103
+ type: "FeatureCollection",
1104
+ features: Array.from(kmlGen(node, options)),
1105
+ };
1106
+ }
1107
+
1108
+ export { gpx, gpxGen, kml, kmlGen, kmlWithFolders, tcx, tcxGen };
1109
+ //# sourceMappingURL=togeojson.es.mjs.map