umap-project 3.0.4__py3-none-any.whl → 3.0.6__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.

Potentially problematic release.


This version of umap-project might be problematic. Click here for more details.

Files changed (164) 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 +136 -56
  4. umap/locale/en/LC_MESSAGES/django.mo +0 -0
  5. umap/locale/en/LC_MESSAGES/django.po +18 -18
  6. umap/locale/es/LC_MESSAGES/django.mo +0 -0
  7. umap/locale/es/LC_MESSAGES/django.po +136 -56
  8. umap/locale/it/LC_MESSAGES/django.mo +0 -0
  9. umap/locale/it/LC_MESSAGES/django.po +143 -63
  10. umap/locale/nl/LC_MESSAGES/django.mo +0 -0
  11. umap/locale/nl/LC_MESSAGES/django.po +1 -1
  12. umap/models.py +1 -0
  13. umap/settings/base.py +1 -0
  14. umap/static/umap/css/bar.css +1 -1
  15. umap/static/umap/css/tooltip.css +13 -0
  16. umap/static/umap/js/modules/autocomplete.js +7 -8
  17. umap/static/umap/js/modules/browser.js +89 -94
  18. umap/static/umap/js/modules/caption.js +6 -4
  19. umap/static/umap/js/modules/data/features.js +1 -19
  20. umap/static/umap/js/modules/data/layer.js +100 -61
  21. umap/static/umap/js/modules/facets.js +1 -1
  22. umap/static/umap/js/modules/form/fields.js +1 -1
  23. umap/static/umap/js/modules/help.js +4 -0
  24. umap/static/umap/js/modules/importer.js +1 -1
  25. umap/static/umap/js/modules/managers.js +46 -0
  26. umap/static/umap/js/modules/permissions.js +1 -1
  27. umap/static/umap/js/modules/rendering/controls.js +251 -0
  28. umap/static/umap/js/modules/rendering/layers/heat.js +5 -0
  29. umap/static/umap/js/modules/rendering/map.js +21 -10
  30. umap/static/umap/js/modules/rendering/ui.js +0 -1
  31. umap/static/umap/js/modules/rules.js +56 -46
  32. umap/static/umap/js/modules/schema.js +5 -1
  33. umap/static/umap/js/modules/share.js +2 -2
  34. umap/static/umap/js/modules/slideshow.js +1 -1
  35. umap/static/umap/js/modules/sync/engine.js +23 -9
  36. umap/static/umap/js/modules/ui/bar.js +2 -2
  37. umap/static/umap/js/modules/ui/base.js +13 -0
  38. umap/static/umap/js/modules/umap.js +69 -111
  39. umap/static/umap/js/umap.controls.js +0 -310
  40. umap/static/umap/js/umap.core.js +0 -40
  41. umap/static/umap/locale/am_ET.js +8 -3
  42. umap/static/umap/locale/am_ET.json +8 -3
  43. umap/static/umap/locale/ar.js +8 -3
  44. umap/static/umap/locale/ar.json +8 -3
  45. umap/static/umap/locale/ast.js +8 -3
  46. umap/static/umap/locale/ast.json +8 -3
  47. umap/static/umap/locale/bg.js +8 -3
  48. umap/static/umap/locale/bg.json +8 -3
  49. umap/static/umap/locale/br.js +8 -3
  50. umap/static/umap/locale/br.json +8 -3
  51. umap/static/umap/locale/ca.js +20 -17
  52. umap/static/umap/locale/ca.json +20 -17
  53. umap/static/umap/locale/cs_CZ.js +5 -2
  54. umap/static/umap/locale/cs_CZ.json +5 -2
  55. umap/static/umap/locale/da.js +8 -3
  56. umap/static/umap/locale/da.json +8 -3
  57. umap/static/umap/locale/de.js +5 -2
  58. umap/static/umap/locale/de.json +5 -2
  59. umap/static/umap/locale/el.js +92 -87
  60. umap/static/umap/locale/el.json +92 -87
  61. umap/static/umap/locale/en.js +6 -2
  62. umap/static/umap/locale/en.json +6 -2
  63. umap/static/umap/locale/en_US.json +8 -3
  64. umap/static/umap/locale/es.js +18 -15
  65. umap/static/umap/locale/es.json +18 -15
  66. umap/static/umap/locale/et.js +8 -3
  67. umap/static/umap/locale/et.json +8 -3
  68. umap/static/umap/locale/eu.js +5 -2
  69. umap/static/umap/locale/eu.json +5 -2
  70. umap/static/umap/locale/fa_IR.js +5 -2
  71. umap/static/umap/locale/fa_IR.json +5 -2
  72. umap/static/umap/locale/fi.js +8 -3
  73. umap/static/umap/locale/fi.json +8 -3
  74. umap/static/umap/locale/fr.js +5 -2
  75. umap/static/umap/locale/fr.json +5 -2
  76. umap/static/umap/locale/gl.js +5 -2
  77. umap/static/umap/locale/gl.json +5 -2
  78. umap/static/umap/locale/he.js +8 -3
  79. umap/static/umap/locale/he.json +8 -3
  80. umap/static/umap/locale/hr.js +8 -3
  81. umap/static/umap/locale/hr.json +8 -3
  82. umap/static/umap/locale/hu.js +5 -2
  83. umap/static/umap/locale/hu.json +5 -2
  84. umap/static/umap/locale/id.js +8 -3
  85. umap/static/umap/locale/id.json +8 -3
  86. umap/static/umap/locale/is.js +8 -3
  87. umap/static/umap/locale/is.json +8 -3
  88. umap/static/umap/locale/it.js +5 -2
  89. umap/static/umap/locale/it.json +5 -2
  90. umap/static/umap/locale/ja.js +8 -3
  91. umap/static/umap/locale/ja.json +8 -3
  92. umap/static/umap/locale/ko.js +8 -3
  93. umap/static/umap/locale/ko.json +8 -3
  94. umap/static/umap/locale/lt.js +8 -3
  95. umap/static/umap/locale/lt.json +8 -3
  96. umap/static/umap/locale/ms.js +8 -3
  97. umap/static/umap/locale/ms.json +8 -3
  98. umap/static/umap/locale/nl.js +7 -4
  99. umap/static/umap/locale/nl.json +7 -4
  100. umap/static/umap/locale/no.js +8 -3
  101. umap/static/umap/locale/no.json +8 -3
  102. umap/static/umap/locale/pl.js +8 -3
  103. umap/static/umap/locale/pl.json +8 -3
  104. umap/static/umap/locale/pl_PL.json +8 -3
  105. umap/static/umap/locale/pt.js +5 -2
  106. umap/static/umap/locale/pt.json +5 -2
  107. umap/static/umap/locale/pt_BR.js +8 -3
  108. umap/static/umap/locale/pt_BR.json +8 -3
  109. umap/static/umap/locale/pt_PT.js +5 -2
  110. umap/static/umap/locale/pt_PT.json +5 -2
  111. umap/static/umap/locale/ro.js +8 -3
  112. umap/static/umap/locale/ro.json +8 -3
  113. umap/static/umap/locale/ru.js +8 -3
  114. umap/static/umap/locale/ru.json +8 -3
  115. umap/static/umap/locale/sk_SK.js +8 -3
  116. umap/static/umap/locale/sk_SK.json +8 -3
  117. umap/static/umap/locale/sl.js +8 -3
  118. umap/static/umap/locale/sl.json +8 -3
  119. umap/static/umap/locale/sr.js +8 -3
  120. umap/static/umap/locale/sr.json +8 -3
  121. umap/static/umap/locale/sv.js +8 -3
  122. umap/static/umap/locale/sv.json +8 -3
  123. umap/static/umap/locale/th_TH.js +8 -3
  124. umap/static/umap/locale/th_TH.json +8 -3
  125. umap/static/umap/locale/tr.js +8 -3
  126. umap/static/umap/locale/tr.json +8 -3
  127. umap/static/umap/locale/uk_UA.js +8 -3
  128. umap/static/umap/locale/uk_UA.json +8 -3
  129. umap/static/umap/locale/vi.js +8 -3
  130. umap/static/umap/locale/vi.json +8 -3
  131. umap/static/umap/locale/vi_VN.json +8 -3
  132. umap/static/umap/locale/zh.js +8 -3
  133. umap/static/umap/locale/zh.json +8 -3
  134. umap/static/umap/locale/zh_CN.json +8 -3
  135. umap/static/umap/locale/zh_TW.Big5.json +8 -3
  136. umap/static/umap/locale/zh_TW.js +5 -2
  137. umap/static/umap/locale/zh_TW.json +5 -2
  138. umap/static/umap/map.css +9 -31
  139. umap/static/umap/vendors/togeojson/togeojson.es.js +350 -177
  140. umap/static/umap/vendors/togeojson/togeojson.es.mjs.map +1 -1
  141. umap/templates/umap/design_system.html +355 -0
  142. umap/templates/umap/map_init.html +3 -1
  143. umap/tests/base.py +2 -2
  144. umap/tests/fixtures/heatmap_data.json +1044 -0
  145. umap/tests/integration/test_basics.py +9 -1
  146. umap/tests/integration/test_browser.py +3 -3
  147. umap/tests/integration/test_conditional_rules.py +2 -2
  148. umap/tests/integration/test_datalayer.py +0 -1
  149. umap/tests/integration/test_edit_map.py +7 -7
  150. umap/tests/integration/test_facets_browser.py +2 -2
  151. umap/tests/integration/test_heatmap.py +41 -0
  152. umap/tests/integration/test_import.py +58 -1
  153. umap/tests/integration/test_map.py +7 -8
  154. umap/tests/integration/test_optimistic_merge.py +12 -4
  155. umap/tests/integration/test_querystring.py +1 -1
  156. umap/tests/integration/test_remote_data.py +79 -0
  157. umap/tests/integration/test_websocket_sync.py +2 -2
  158. umap/urls.py +1 -0
  159. umap/views.py +8 -1
  160. {umap_project-3.0.4.dist-info → umap_project-3.0.6.dist-info}/METADATA +11 -11
  161. {umap_project-3.0.4.dist-info → umap_project-3.0.6.dist-info}/RECORD +164 -158
  162. {umap_project-3.0.4.dist-info → umap_project-3.0.6.dist-info}/WHEEL +0 -0
  163. {umap_project-3.0.4.dist-info → umap_project-3.0.6.dist-info}/entry_points.txt +0 -0
  164. {umap_project-3.0.4.dist-info → umap_project-3.0.6.dist-info}/licenses/LICENSE +0 -0
@@ -12,7 +12,7 @@ function $ns(element, tagName, ns) {
12
12
  */
13
13
  function nodeVal(node) {
14
14
  node?.normalize();
15
- return (node && node.textContent) || "";
15
+ return node?.textContent || "";
16
16
  }
17
17
  /**
18
18
  * Get one Y child of X, if any, otherwise null
@@ -42,16 +42,16 @@ function val1(node, tagName, callback) {
42
42
  return {};
43
43
  }
44
44
  function $num(node, tagName, callback) {
45
- const val = parseFloat(nodeVal(get1(node, tagName)));
46
- if (isNaN(val))
45
+ const val = Number.parseFloat(nodeVal(get1(node, tagName)));
46
+ if (Number.isNaN(val))
47
47
  return undefined;
48
48
  if (val && callback)
49
49
  return callback(val) || {};
50
50
  return {};
51
51
  }
52
52
  function num1(node, tagName, callback) {
53
- const val = parseFloat(nodeVal(get1(node, tagName)));
54
- if (isNaN(val))
53
+ const val = Number.parseFloat(nodeVal(get1(node, tagName)));
54
+ if (Number.isNaN(val))
55
55
  return undefined;
56
56
  if (callback)
57
57
  callback(val);
@@ -70,20 +70,6 @@ function isElement(node) {
70
70
  return node?.nodeType === 1;
71
71
  }
72
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
73
  function getExtensions(node) {
88
74
  let values = [];
89
75
  if (node === null)
@@ -108,16 +94,16 @@ function abbreviateName(name) {
108
94
  return ["heart", "gpxtpx:hr", "hr"].includes(name) ? "heart" : name;
109
95
  }
110
96
  function parseNumeric(val) {
111
- const num = parseFloat(val);
112
- return isNaN(num) ? val : num;
97
+ const num = Number.parseFloat(val);
98
+ return Number.isNaN(num) ? val : num;
113
99
  }
114
100
 
115
101
  function coordPair$1(node) {
116
102
  const ll = [
117
- parseFloat(node.getAttribute("lon") || ""),
118
- parseFloat(node.getAttribute("lat") || ""),
103
+ Number.parseFloat(node.getAttribute("lon") || ""),
104
+ Number.parseFloat(node.getAttribute("lat") || ""),
119
105
  ];
120
- if (isNaN(ll[0]) || isNaN(ll[1])) {
106
+ if (Number.isNaN(ll[0]) || Number.isNaN(ll[1])) {
121
107
  return null;
122
108
  }
123
109
  num1(node, "ele", (val) => {
@@ -131,7 +117,21 @@ function coordPair$1(node) {
131
117
  };
132
118
  }
133
119
 
134
- function extractProperties(node) {
120
+ function getLineStyle(node) {
121
+ return get(node, "line", (lineStyle) => {
122
+ const val = Object.assign({}, val1(lineStyle, "color", (color) => {
123
+ return { stroke: `#${color}` };
124
+ }), $num(lineStyle, "opacity", (opacity) => {
125
+ return { "stroke-opacity": opacity };
126
+ }), $num(lineStyle, "width", (width) => {
127
+ // GPX width is in mm, convert to px with 96 px per inch
128
+ return { "stroke-width": (width * 96) / 25.4 };
129
+ }));
130
+ return val;
131
+ });
132
+ }
133
+
134
+ function extractProperties(ns, node) {
135
135
  const properties = getMulti(node, [
136
136
  "name",
137
137
  "cmt",
@@ -140,10 +140,9 @@ function extractProperties(node) {
140
140
  "time",
141
141
  "keywords",
142
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);
143
+ for (const [n, url] of ns) {
144
+ for (const child of Array.from(node.getElementsByTagNameNS(url, "*"))) {
145
+ properties[child.tagName.replace(":", "_")] = nodeVal(child)?.trim();
147
146
  }
148
147
  }
149
148
  const links = $(node, "link");
@@ -170,7 +169,7 @@ function getPoints$1(node, pointname) {
170
169
  if (c.time)
171
170
  times.push(c.time);
172
171
  for (const [name, val] of c.extendedValues) {
173
- const plural = name === "heart" ? name : name.replace("gpxtpx:", "") + "s";
172
+ const plural = name === "heart" ? name : `${name.replace("gpxtpx:", "")}s`;
174
173
  if (!extendedValues[plural]) {
175
174
  extendedValues[plural] = Array(pts.length).fill(null);
176
175
  }
@@ -189,20 +188,20 @@ function getPoints$1(node, pointname) {
189
188
  * Extract a LineString geometry from a rte
190
189
  * element.
191
190
  */
192
- function getRoute(node) {
191
+ function getRoute(ns, node) {
193
192
  const line = getPoints$1(node, "rtept");
194
193
  if (!line)
195
194
  return;
196
195
  return {
197
196
  type: "Feature",
198
- properties: Object.assign({ _gpxType: "rte" }, extractProperties(node), getLineStyle(get1(node, "extensions"))),
197
+ properties: Object.assign({ _gpxType: "rte" }, extractProperties(ns, node), getLineStyle(get1(node, "extensions"))),
199
198
  geometry: {
200
199
  type: "LineString",
201
200
  coordinates: line.line,
202
201
  },
203
202
  };
204
203
  }
205
- function getTrack(node) {
204
+ function getTrack(ns, node) {
206
205
  const segments = $(node, "trkseg");
207
206
  const track = [];
208
207
  const times = [];
@@ -211,14 +210,14 @@ function getTrack(node) {
211
210
  const line = getPoints$1(segment, "trkpt");
212
211
  if (line) {
213
212
  extractedLines.push(line);
214
- if (line.times && line.times.length)
213
+ if (line.times?.length)
215
214
  times.push(line.times);
216
215
  }
217
216
  }
218
217
  if (extractedLines.length === 0)
219
218
  return null;
220
219
  const multi = extractedLines.length > 1;
221
- const properties = Object.assign({ _gpxType: "trk" }, extractProperties(node), getLineStyle(get1(node, "extensions")), times.length
220
+ const properties = Object.assign({ _gpxType: "trk" }, extractProperties(ns, node), getLineStyle(get1(node, "extensions")), times.length
222
221
  ? {
223
222
  coordinateProperties: {
224
223
  times: multi ? times : times[0],
@@ -263,8 +262,8 @@ function getTrack(node) {
263
262
  * Extract a point, if possible, from a given node,
264
263
  * which is usually a wpt or trkpt
265
264
  */
266
- function getPoint(node) {
267
- const properties = Object.assign(extractProperties(node), getMulti(node, ["sym"]));
265
+ function getPoint(ns, node) {
266
+ const properties = Object.assign(extractProperties(ns, node), getMulti(node, ["sym"]));
268
267
  const pair = coordPair$1(node);
269
268
  if (!pair)
270
269
  return null;
@@ -283,18 +282,31 @@ function getPoint(node) {
283
282
  * that yields output feature by feature.
284
283
  */
285
284
  function* gpxGen(node) {
286
- for (const track of $(node, "trk")) {
287
- const feature = getTrack(track);
285
+ const n = node;
286
+ const GPXX = "gpxx";
287
+ const GPXX_URI = "http://www.garmin.com/xmlschemas/GpxExtensions/v3";
288
+ // Namespaces
289
+ const ns = [[GPXX, GPXX_URI]];
290
+ const attrs = n.getElementsByTagName("gpx")[0]?.attributes;
291
+ if (attrs) {
292
+ for (const attr of Array.from(attrs)) {
293
+ if (attr.name?.startsWith("xmlns:") && attr.value !== GPXX_URI) {
294
+ ns.push([attr.name, attr.value]);
295
+ }
296
+ }
297
+ }
298
+ for (const track of $(n, "trk")) {
299
+ const feature = getTrack(ns, track);
288
300
  if (feature)
289
301
  yield feature;
290
302
  }
291
- for (const route of $(node, "rte")) {
292
- const feature = getRoute(route);
303
+ for (const route of $(n, "rte")) {
304
+ const feature = getRoute(ns, route);
293
305
  if (feature)
294
306
  yield feature;
295
307
  }
296
- for (const waypoint of $(node, "wpt")) {
297
- const point = getPoint(waypoint);
308
+ for (const waypoint of $(n, "wpt")) {
309
+ const point = getPoint(ns, waypoint);
298
310
  if (point)
299
311
  yield point;
300
312
  }
@@ -346,8 +358,8 @@ function getProperties(node, attributeNames) {
346
358
  elem = elements[0];
347
359
  }
348
360
  }
349
- const val = parseFloat(nodeVal(elem));
350
- if (!isNaN(val)) {
361
+ const val = Number.parseFloat(nodeVal(elem));
362
+ if (!Number.isNaN(val)) {
351
363
  properties.push([alias, val]);
352
364
  }
353
365
  }
@@ -356,23 +368,23 @@ function getProperties(node, attributeNames) {
356
368
  function coordPair(node) {
357
369
  const ll = [num1(node, "LongitudeDegrees"), num1(node, "LatitudeDegrees")];
358
370
  if (ll[0] === undefined ||
359
- isNaN(ll[0]) ||
371
+ Number.isNaN(ll[0]) ||
360
372
  ll[1] === undefined ||
361
- isNaN(ll[1])) {
373
+ Number.isNaN(ll[1])) {
362
374
  return null;
363
375
  }
364
376
  const heartRate = get1(node, "HeartRateBpm");
365
377
  const time = nodeVal(get1(node, "Time"));
366
378
  get1(node, "AltitudeMeters", (alt) => {
367
- const a = parseFloat(nodeVal(alt));
368
- if (!isNaN(a)) {
379
+ const a = Number.parseFloat(nodeVal(alt));
380
+ if (!Number.isNaN(a)) {
369
381
  ll.push(a);
370
382
  }
371
383
  });
372
384
  return {
373
385
  coordinates: ll,
374
386
  time: time || null,
375
- heartRate: heartRate ? parseFloat(nodeVal(heartRate)) : null,
387
+ heartRate: heartRate ? Number.parseFloat(nodeVal(heartRate)) : null,
376
388
  extensions: getProperties(node, TRACKPOINT_ATTRIBUTES),
377
389
  };
378
390
  }
@@ -504,17 +516,18 @@ function tcx(node) {
504
516
 
505
517
  function fixColor(v, prefix) {
506
518
  const properties = {};
507
- const colorProp = prefix == "stroke" || prefix === "fill" ? prefix : prefix + "-color";
519
+ const colorProp = prefix === "stroke" || prefix === "fill" ? prefix : `${prefix}-color`;
508
520
  if (v[0] === "#") {
509
521
  v = v.substring(1);
510
522
  }
511
523
  if (v.length === 6 || v.length === 3) {
512
- properties[colorProp] = "#" + v;
524
+ properties[colorProp] = `#${v}`;
513
525
  }
514
526
  else if (v.length === 8) {
515
- properties[prefix + "-opacity"] = parseInt(v.substring(0, 2), 16) / 255;
527
+ properties[`${prefix}-opacity`] =
528
+ Number.parseInt(v.substring(0, 2), 16) / 255;
516
529
  properties[colorProp] =
517
- "#" + v.substring(6, 8) + v.substring(4, 6) + v.substring(2, 4);
530
+ `#${v.substring(6, 8)}${v.substring(4, 6)}${v.substring(2, 4)}`;
518
531
  }
519
532
  return properties;
520
533
  }
@@ -540,11 +553,11 @@ function extractIconHref(node) {
540
553
  function extractIcon(node) {
541
554
  return get(node, "IconStyle", (iconStyle) => {
542
555
  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") || "");
556
+ const left = Number.parseFloat(hotspot.getAttribute("x") || "");
557
+ const top = Number.parseFloat(hotspot.getAttribute("y") || "");
545
558
  const xunits = hotspot.getAttribute("xunits") || "";
546
559
  const yunits = hotspot.getAttribute("yunits") || "";
547
- if (!isNaN(left) && !isNaN(top))
560
+ if (!Number.isNaN(left) && !Number.isNaN(top))
548
561
  return {
549
562
  "icon-offset": [left, top],
550
563
  "icon-offset-units": [xunits, yunits],
@@ -578,71 +591,6 @@ function extractStyle(node) {
578
591
  return Object.assign({}, extractPoly(node), extractLine(node), extractLabel(node), extractIcon(node));
579
592
  }
580
593
 
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
594
  const removeSpace = /\s*/g;
647
595
  const trimSpace = /^\s*|\s*$/g;
648
596
  const splitSpace = /\s+/;
@@ -653,8 +601,8 @@ function coord1(value) {
653
601
  return value
654
602
  .replace(removeSpace, "")
655
603
  .split(",")
656
- .map(parseFloat)
657
- .filter((num) => !isNaN(num))
604
+ .map(Number.parseFloat)
605
+ .filter((num) => !Number.isNaN(num))
658
606
  .slice(0, 3);
659
607
  }
660
608
  /**
@@ -675,7 +623,7 @@ function gxCoords(node) {
675
623
  elems = $ns(node, "coord", "*");
676
624
  }
677
625
  const coordinates = elems.map((elem) => {
678
- return nodeVal(elem).split(" ").map(parseFloat);
626
+ return nodeVal(elem).split(" ").map(Number.parseFloat);
679
627
  });
680
628
  if (coordinates.length === 0) {
681
629
  return null;
@@ -785,47 +733,92 @@ function getGeometry(node) {
785
733
  };
786
734
  }
787
735
 
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
- };
736
+ const toNumber = (x) => Number(x);
737
+ const typeConverters = {
738
+ string: (x) => x,
739
+ int: toNumber,
740
+ uint: toNumber,
741
+ short: toNumber,
742
+ ushort: toNumber,
743
+ float: toNumber,
744
+ double: toNumber,
745
+ bool: (x) => Boolean(x),
746
+ };
747
+ function extractExtendedData(node, schema) {
748
+ return get(node, "ExtendedData", (extendedData, properties) => {
749
+ for (const data of $(extendedData, "Data")) {
750
+ properties[data.getAttribute("name") || ""] = nodeVal(get1(data, "value"));
751
+ }
752
+ for (const simpleData of $(extendedData, "SimpleData")) {
753
+ const name = simpleData.getAttribute("name") || "";
754
+ const typeConverter = schema[name] || typeConverters.string;
755
+ properties[name] = typeConverter(nodeVal(simpleData));
756
+ }
757
+ return properties;
758
+ });
797
759
  }
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,
760
+ function getMaybeHTMLDescription(node) {
761
+ const descriptionNode = get1(node, "description");
762
+ for (const c of Array.from(descriptionNode?.childNodes || [])) {
763
+ if (c.nodeType === 4) {
764
+ return {
765
+ description: {
766
+ "@type": "html",
767
+ value: nodeVal(c),
818
768
  },
819
- }
820
- : {}),
821
- };
822
- if (feature.properties?.visibility !== undefined) {
823
- feature.properties.visibility = feature.properties.visibility !== "0";
769
+ };
770
+ }
824
771
  }
825
- const id = node.getAttribute("id");
826
- if (id !== null && id !== "")
827
- feature.id = id;
828
- return feature;
772
+ return {};
773
+ }
774
+ function extractTimeSpan(node) {
775
+ return get(node, "TimeSpan", (timeSpan) => {
776
+ return {
777
+ timespan: {
778
+ begin: nodeVal(get1(timeSpan, "begin")),
779
+ end: nodeVal(get1(timeSpan, "end")),
780
+ },
781
+ };
782
+ });
783
+ }
784
+ function extractTimeStamp(node) {
785
+ return get(node, "TimeStamp", (timeStamp) => {
786
+ return { timestamp: nodeVal(get1(timeStamp, "when")) };
787
+ });
788
+ }
789
+ function extractCascadedStyle(node, styleMap) {
790
+ return val1(node, "styleUrl", (styleUrl) => {
791
+ styleUrl = normalizeId(styleUrl);
792
+ if (styleMap[styleUrl]) {
793
+ return Object.assign({ styleUrl }, styleMap[styleUrl]);
794
+ }
795
+ // For backward-compatibility. Should we still include
796
+ // styleUrl even if it's not resolved?
797
+ return { styleUrl };
798
+ });
799
+ }
800
+ var AltitudeMode;
801
+ (function (AltitudeMode) {
802
+ AltitudeMode["ABSOLUTE"] = "absolute";
803
+ AltitudeMode["RELATIVE_TO_GROUND"] = "relativeToGround";
804
+ AltitudeMode["CLAMP_TO_GROUND"] = "clampToGround";
805
+ AltitudeMode["CLAMP_TO_SEAFLOOR"] = "clampToSeaFloor";
806
+ AltitudeMode["RELATIVE_TO_SEAFLOOR"] = "relativeToSeaFloor";
807
+ })(AltitudeMode || (AltitudeMode = {}));
808
+ function processAltitudeMode(mode) {
809
+ switch (mode?.textContent) {
810
+ case AltitudeMode.ABSOLUTE:
811
+ return AltitudeMode.ABSOLUTE;
812
+ case AltitudeMode.CLAMP_TO_GROUND:
813
+ return AltitudeMode.CLAMP_TO_GROUND;
814
+ case AltitudeMode.CLAMP_TO_SEAFLOOR:
815
+ return AltitudeMode.CLAMP_TO_SEAFLOOR;
816
+ case AltitudeMode.RELATIVE_TO_GROUND:
817
+ return AltitudeMode.RELATIVE_TO_GROUND;
818
+ case AltitudeMode.RELATIVE_TO_SEAFLOOR:
819
+ return AltitudeMode.RELATIVE_TO_SEAFLOOR;
820
+ }
821
+ return null;
829
822
  }
830
823
 
831
824
  function getGroundOverlayBox(node) {
@@ -848,7 +841,7 @@ function rotateBox(bbox, coordinates, rotation) {
848
841
  coordinates[0].map((coordinate) => {
849
842
  const dy = coordinate[1] - center[1];
850
843
  const dx = coordinate[0] - center[0];
851
- const distance = Math.sqrt(Math.pow(dy, 2) + Math.pow(dx, 2));
844
+ const distance = Math.sqrt(dy ** 2 + dx ** 2);
852
845
  const angle = Math.atan2(dy, dx) + rotation * DEGREES_TO_RADIANS;
853
846
  return [
854
847
  center[0] + Math.cos(angle) * distance,
@@ -872,10 +865,10 @@ function getLatLonBox(node) {
872
865
  const bbox = [west, south, east, north];
873
866
  let coordinates = [
874
867
  [
875
- [west, north],
876
- [east, north],
877
- [east, south],
878
- [west, south],
868
+ [west, north], // top left
869
+ [east, north], // top right
870
+ [east, south], // top right
871
+ [west, south], // bottom left
879
872
  [west, north], // top left (again)
880
873
  ],
881
874
  ];
@@ -928,6 +921,178 @@ function getGroundOverlay(node, styleMap, schema, options) {
928
921
  return feature;
929
922
  }
930
923
 
924
+ function getNetworkLinkRegion(node) {
925
+ const region = get1(node, "Region");
926
+ if (region) {
927
+ return {
928
+ coordinateBox: getLatLonAltBox(region),
929
+ lod: getLod(node),
930
+ };
931
+ }
932
+ return null;
933
+ }
934
+ function getLod(node) {
935
+ const lod = get1(node, "Lod");
936
+ if (lod) {
937
+ return [
938
+ num1(lod, "minLodPixels") ?? -1,
939
+ num1(lod, "maxLodPixels") ?? -1,
940
+ num1(lod, "minFadeExtent") ?? null,
941
+ num1(lod, "maxFadeExtent") ?? null,
942
+ ];
943
+ }
944
+ return null;
945
+ }
946
+ function getLatLonAltBox(node) {
947
+ const latLonAltBox = get1(node, "LatLonAltBox");
948
+ if (latLonAltBox) {
949
+ const north = num1(latLonAltBox, "north");
950
+ const west = num1(latLonAltBox, "west");
951
+ const east = num1(latLonAltBox, "east");
952
+ const south = num1(latLonAltBox, "south");
953
+ const altitudeMode = processAltitudeMode(get1(latLonAltBox, "altitudeMode") ||
954
+ get1(latLonAltBox, "gx:altitudeMode"));
955
+ if (altitudeMode) {
956
+ console.debug("Encountered an unsupported feature of KML for togeojson: please contact developers for support of altitude mode.");
957
+ }
958
+ if (typeof north === "number" &&
959
+ typeof south === "number" &&
960
+ typeof west === "number" &&
961
+ typeof east === "number") {
962
+ const bbox = [west, south, east, north];
963
+ const coordinates = [
964
+ [
965
+ [west, north], // top left
966
+ [east, north], // top right
967
+ [east, south], // top right
968
+ [west, south], // bottom left
969
+ [west, north], // top left (again)
970
+ ],
971
+ ];
972
+ return {
973
+ bbox,
974
+ geometry: {
975
+ type: "Polygon",
976
+ coordinates,
977
+ },
978
+ };
979
+ }
980
+ }
981
+ return null;
982
+ }
983
+ function getLinkObject(node) {
984
+ /*
985
+ <Link id="ID">
986
+ <!-- specific to Link -->
987
+ <href>...</href> <!-- string -->
988
+ <refreshMode>onChange</refreshMode>
989
+ <!-- refreshModeEnum: onChange, onInterval, or onExpire -->
990
+ <refreshInterval>4</refreshInterval> <!-- float -->
991
+ <viewRefreshMode>never</viewRefreshMode>
992
+ <!-- viewRefreshModeEnum: never, onStop, onRequest, onRegion -->
993
+ <viewRefreshTime>4</viewRefreshTime> <!-- float -->
994
+ <viewBoundScale>1</viewBoundScale> <!-- float -->
995
+ <viewFormat>BBOX=[bboxWest],[bboxSouth],[bboxEast],[bboxNorth]</viewFormat>
996
+ <!-- string -->
997
+ <httpQuery>...</httpQuery> <!-- string -->
998
+ </Link>
999
+ */
1000
+ const linkObj = get1(node, "Link");
1001
+ if (linkObj) {
1002
+ return getMulti(linkObj, [
1003
+ "href",
1004
+ "refreshMode",
1005
+ "refreshInterval",
1006
+ "viewRefreshMode",
1007
+ "viewRefreshTime",
1008
+ "viewBoundScale",
1009
+ "viewFormat",
1010
+ "httpQuery",
1011
+ ]);
1012
+ }
1013
+ return {};
1014
+ }
1015
+ function getNetworkLink(node, styleMap, schema, options) {
1016
+ const box = getNetworkLinkRegion(node);
1017
+ const geometry = box?.coordinateBox?.geometry || null;
1018
+ if (!geometry && options.skipNullGeometry) {
1019
+ return null;
1020
+ }
1021
+ const feature = {
1022
+ type: "Feature",
1023
+ geometry,
1024
+ properties: Object.assign(
1025
+ /**
1026
+ * Related to
1027
+ * https://gist.github.com/tmcw/037a1cb6660d74a392e9da7446540f46
1028
+ */
1029
+ { "@geometry-type": "networklink" }, getMulti(node, [
1030
+ "name",
1031
+ "address",
1032
+ "visibility",
1033
+ "open",
1034
+ "phoneNumber",
1035
+ "styleUrl",
1036
+ "refreshVisibility",
1037
+ "flyToView",
1038
+ "description",
1039
+ ]), getMaybeHTMLDescription(node), extractCascadedStyle(node, styleMap), extractStyle(node), extractIconHref(node), extractExtendedData(node, schema), extractTimeSpan(node), extractTimeStamp(node), getLinkObject(node), box?.lod ? { lod: box.lod } : {}),
1040
+ };
1041
+ if (box?.coordinateBox?.bbox) {
1042
+ feature.bbox = box.coordinateBox.bbox;
1043
+ }
1044
+ if (feature.properties?.visibility !== undefined) {
1045
+ feature.properties.visibility = feature.properties.visibility !== "0";
1046
+ }
1047
+ const id = node.getAttribute("id");
1048
+ if (id !== null && id !== "")
1049
+ feature.id = id;
1050
+ return feature;
1051
+ }
1052
+
1053
+ function geometryListToGeometry(geometries) {
1054
+ return geometries.length === 0
1055
+ ? null
1056
+ : geometries.length === 1
1057
+ ? geometries[0]
1058
+ : {
1059
+ type: "GeometryCollection",
1060
+ geometries,
1061
+ };
1062
+ }
1063
+ function getPlacemark(node, styleMap, schema, options) {
1064
+ const { coordTimes, geometries } = getGeometry(node);
1065
+ const geometry = geometryListToGeometry(geometries);
1066
+ if (!geometry && options.skipNullGeometry) {
1067
+ return null;
1068
+ }
1069
+ const feature = {
1070
+ type: "Feature",
1071
+ geometry,
1072
+ properties: Object.assign(getMulti(node, [
1073
+ "name",
1074
+ "address",
1075
+ "visibility",
1076
+ "open",
1077
+ "phoneNumber",
1078
+ "description",
1079
+ ]), getMaybeHTMLDescription(node), extractCascadedStyle(node, styleMap), extractStyle(node), extractExtendedData(node, schema), extractTimeSpan(node), extractTimeStamp(node), coordTimes.length
1080
+ ? {
1081
+ coordinateProperties: {
1082
+ times: coordTimes.length === 1 ? coordTimes[0] : coordTimes,
1083
+ },
1084
+ }
1085
+ : {}),
1086
+ };
1087
+ if (feature.properties?.visibility !== undefined) {
1088
+ feature.properties.visibility = feature.properties.visibility !== "0";
1089
+ }
1090
+ const id = node.getAttribute("id");
1091
+ if (id !== null && id !== "")
1092
+ feature.id = id;
1093
+ return feature;
1094
+ }
1095
+
931
1096
  function getStyleId(style) {
932
1097
  let id = style.getAttribute("id");
933
1098
  const parentNode = style.parentNode;
@@ -958,8 +1123,7 @@ function buildSchema(node) {
958
1123
  const schema = {};
959
1124
  for (const field of $(node, "SimpleField")) {
960
1125
  schema[field.getAttribute("name") || ""] =
961
- typeConverters[field.getAttribute("type") || ""] ||
962
- typeConverters["string"];
1126
+ typeConverters[field.getAttribute("type") || ""] || typeConverters.string;
963
1127
  }
964
1128
  return schema;
965
1129
  }
@@ -1028,8 +1192,9 @@ function getFolder(node) {
1028
1192
  function kmlWithFolders(node, options = {
1029
1193
  skipNullGeometry: false,
1030
1194
  }) {
1031
- const styleMap = buildStyleMap(node);
1032
- const schema = buildSchema(node);
1195
+ const n = node;
1196
+ const styleMap = buildStyleMap(n);
1197
+ const schema = buildSchema(n);
1033
1198
  const tree = { type: "root", children: [] };
1034
1199
  function traverse(node, pointer, options) {
1035
1200
  if (isElement(node)) {
@@ -1054,6 +1219,13 @@ function kmlWithFolders(node, options = {
1054
1219
  pointer = folder;
1055
1220
  break;
1056
1221
  }
1222
+ case "NetworkLink": {
1223
+ const networkLink = getNetworkLink(node, styleMap, schema, options);
1224
+ if (networkLink) {
1225
+ pointer.children.push(networkLink);
1226
+ }
1227
+ break;
1228
+ }
1057
1229
  }
1058
1230
  }
1059
1231
  if (node.childNodes) {
@@ -1062,7 +1234,7 @@ function kmlWithFolders(node, options = {
1062
1234
  }
1063
1235
  }
1064
1236
  }
1065
- traverse(node, tree, options);
1237
+ traverse(n, tree, options);
1066
1238
  return tree;
1067
1239
  }
1068
1240
  /**
@@ -1073,14 +1245,15 @@ function kmlWithFolders(node, options = {
1073
1245
  function* kmlGen(node, options = {
1074
1246
  skipNullGeometry: false,
1075
1247
  }) {
1076
- const styleMap = buildStyleMap(node);
1077
- const schema = buildSchema(node);
1078
- for (const placemark of $(node, "Placemark")) {
1248
+ const n = node;
1249
+ const styleMap = buildStyleMap(n);
1250
+ const schema = buildSchema(n);
1251
+ for (const placemark of $(n, "Placemark")) {
1079
1252
  const feature = getPlacemark(placemark, styleMap, schema, options);
1080
1253
  if (feature)
1081
1254
  yield feature;
1082
1255
  }
1083
- for (const groundOverlay of $(node, "GroundOverlay")) {
1256
+ for (const groundOverlay of $(n, "GroundOverlay")) {
1084
1257
  const feature = getGroundOverlay(groundOverlay, styleMap, schema, options);
1085
1258
  if (feature)
1086
1259
  yield feature;