node-red-contrib-web-worldmap 2.30.2 → 2.31.0

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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,8 @@
1
1
  ### Change Log for Node-RED Worldmap
2
2
 
3
+ - v2.31.0 - Better handling of KML files. Issue #211
4
+
5
+ - v2.30.3 - Fix for iframe height. Issue #210
3
6
  - v2.30.2 - Fix for bad handling of mapbox id. Issue #208
4
7
  - v2.30.1 - Don't resend bounds if not changed. Issue #209
5
8
  - v2.30.0 - Add show/hide ruler option. PR #206
package/README.md CHANGED
@@ -11,6 +11,8 @@ map web page for plotting "things" on.
11
11
 
12
12
  ### Updates
13
13
 
14
+ - v2.31.0 - Better handling of KML files. Issue #211
15
+ - v2.30.3 - Fix for iframe height. Issue #210
14
16
  - v2.30.2 - Fix for bad handling of mapbox id. Issue #208
15
17
  - v2.30.1 - Don't resend bounds if not changed. Issue #209
16
18
  - v2.30.0 - Add show/hide ruler option. PR #206
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-web-worldmap",
3
- "version": "2.30.2",
3
+ "version": "2.31.0",
4
4
  "description": "A Node-RED node to provide a web page of a world map for plotting things on.",
5
5
  "dependencies": {
6
6
  "@turf/bezier-spline": "~6.5.0",
@@ -68,6 +68,7 @@
68
68
  <script src="leaflet/leaflet-side-by-side.js"></script>
69
69
  <script src="leaflet/OSMBuildings-Leaflet.js"></script>
70
70
  <script src="leaflet/leaflet-omnivore.min.js"></script>
71
+ <script src="leaflet/L.KML.js"></script>
71
72
  <script src="leaflet/leaflet.mousecoordinate.js"></script>
72
73
  <script src="leaflet/leaflet.latlng-graticule.js"></script>
73
74
  <script src="leaflet/VectorTileLayer.umd.min.js"></script>
@@ -178,7 +178,7 @@ var do3dMap = function() {
178
178
 
179
179
  // create the points for the marker and return the geojson
180
180
  var getPoints = function(p) {
181
- var fac = 0.000007; // basic size for bock icon in degrees....
181
+ var fac = 0.000007; // basic size for block icon in degrees....
182
182
  var thing = "";
183
183
  if (p.hasOwnProperty("icon")) {
184
184
  if (p.icon.indexOf("male") !== -1) { thing = "person"; }
@@ -0,0 +1,456 @@
1
+ /*!
2
+ Copyright (c) 2011-2015, Pavel Shramov, Bruno Bergot - MIT licence
3
+ */
4
+
5
+ L.KML = L.FeatureGroup.extend({
6
+
7
+ initialize: function (kml) {
8
+ this._kml = kml;
9
+ this._layers = {};
10
+
11
+ if (kml) {
12
+ this.addKML(kml);
13
+ }
14
+ },
15
+
16
+ addKML: function (xml) {
17
+ var layers = L.KML.parseKML(xml);
18
+ if (!layers || !layers.length) return;
19
+ for (var i = 0; i < layers.length; i++) {
20
+ this.fire('addlayer', {
21
+ layer: layers[i]
22
+ });
23
+ this.addLayer(layers[i]);
24
+ }
25
+ this.latLngs = L.KML.getLatLngs(xml);
26
+ this.fire('loaded');
27
+ },
28
+
29
+ latLngs: []
30
+ });
31
+
32
+ L.Util.extend(L.KML, {
33
+
34
+ parseKML: function (xml) {
35
+ var style = this.parseStyles(xml);
36
+ this.parseStyleMap(xml, style);
37
+ var el = xml.getElementsByTagName('Folder');
38
+ var layers = [], l;
39
+ for (var i = 0; i < el.length; i++) {
40
+ if (!this._check_folder(el[i])) { continue; }
41
+ l = this.parseFolder(el[i], style);
42
+ if (l) { layers.push(l); }
43
+ }
44
+ el = xml.getElementsByTagName('Placemark');
45
+ for (var j = 0; j < el.length; j++) {
46
+ if (!this._check_folder(el[j])) { continue; }
47
+ l = this.parsePlacemark(el[j], xml, style);
48
+ if (l) { layers.push(l); }
49
+ }
50
+ el = xml.getElementsByTagName('GroundOverlay');
51
+ for (var k = 0; k < el.length; k++) {
52
+ l = this.parseGroundOverlay(el[k]);
53
+ if (l) { layers.push(l); }
54
+ }
55
+ return layers;
56
+ },
57
+
58
+ // Return false if e's first parent Folder is not [folder]
59
+ // - returns true if no parent Folders
60
+ _check_folder: function (e, folder) {
61
+ e = e.parentNode;
62
+ while (e && e.tagName !== 'Folder')
63
+ {
64
+ e = e.parentNode;
65
+ }
66
+ return !e || e === folder;
67
+ },
68
+
69
+ parseStyles: function (xml) {
70
+ var styles = {};
71
+ var sl = xml.getElementsByTagName('Style');
72
+ for (var i=0, len=sl.length; i<len; i++) {
73
+ var style = this.parseStyle(sl[i]);
74
+ if (style) {
75
+ var styleName = '#' + style.id;
76
+ styles[styleName] = style;
77
+ }
78
+ }
79
+ return styles;
80
+ },
81
+
82
+ parseStyle: function (xml) {
83
+ var style = {}, poptions = {}, ioptions = {}, el, id;
84
+
85
+ var attributes = {color: true, width: true, Icon: true, href: true, hotSpot: true};
86
+
87
+ function _parse (xml) {
88
+ var options = {};
89
+ for (var i = 0; i < xml.childNodes.length; i++) {
90
+ var e = xml.childNodes[i];
91
+ var key = e.tagName;
92
+ if (!attributes[key]) { continue; }
93
+ if (key === 'hotSpot')
94
+ {
95
+ for (var j = 0; j < e.attributes.length; j++) {
96
+ options[e.attributes[j].name] = e.attributes[j].nodeValue;
97
+ }
98
+ } else {
99
+ var value = e.childNodes[0].nodeValue;
100
+ if (key === 'color') {
101
+ options.opacity = parseInt(value.substring(0, 2), 16) / 255.0;
102
+ options.color = '#' + value.substring(6, 8) + value.substring(4, 6) + value.substring(2, 4);
103
+ } else if (key === 'width') {
104
+ options.weight = value;
105
+ } else if (key === 'Icon') {
106
+ ioptions = _parse(e);
107
+ if (ioptions.href) { options.href = ioptions.href; }
108
+ } else if (key === 'href') {
109
+ options.href = value;
110
+ }
111
+ }
112
+ }
113
+ return options;
114
+ }
115
+
116
+ el = xml.getElementsByTagName('LineStyle');
117
+ if (el && el[0]) { style = _parse(el[0]); }
118
+ el = xml.getElementsByTagName('PolyStyle');
119
+ if (el && el[0]) { poptions = _parse(el[0]); }
120
+ if (poptions.color) { style.fillColor = poptions.color; }
121
+ if (poptions.opacity) { style.fillOpacity = poptions.opacity; }
122
+ el = xml.getElementsByTagName('IconStyle');
123
+ if (el && el[0]) { ioptions = _parse(el[0]); }
124
+ if (ioptions.href) {
125
+ style.icon = new L.KMLIcon({
126
+ iconUrl: ioptions.href,
127
+ shadowUrl: null,
128
+ anchorRef: {x: ioptions.x, y: ioptions.y},
129
+ anchorType: {x: ioptions.xunits, y: ioptions.yunits}
130
+ });
131
+ }
132
+
133
+ id = xml.getAttribute('id');
134
+ if (id && style) {
135
+ style.id = id;
136
+ }
137
+
138
+ return style;
139
+ },
140
+
141
+ parseStyleMap: function (xml, existingStyles) {
142
+ var sl = xml.getElementsByTagName('StyleMap');
143
+
144
+ for (var i = 0; i < sl.length; i++) {
145
+ var e = sl[i], el;
146
+ var smKey, smStyleUrl;
147
+
148
+ el = e.getElementsByTagName('key');
149
+ if (el && el[0]) { smKey = el[0].textContent; }
150
+ el = e.getElementsByTagName('styleUrl');
151
+ if (el && el[0]) { smStyleUrl = el[0].textContent; }
152
+
153
+ if (smKey === 'normal')
154
+ {
155
+ existingStyles['#' + e.getAttribute('id')] = existingStyles[smStyleUrl];
156
+ }
157
+ }
158
+
159
+ return;
160
+ },
161
+
162
+ parseFolder: function (xml, style) {
163
+ var el, layers = [], l;
164
+ el = xml.getElementsByTagName('Folder');
165
+ for (var i = 0; i < el.length; i++) {
166
+ if (!this._check_folder(el[i], xml)) { continue; }
167
+ l = this.parseFolder(el[i], style);
168
+ if (l) { layers.push(l); }
169
+ }
170
+ el = xml.getElementsByTagName('Placemark');
171
+ for (var j = 0; j < el.length; j++) {
172
+ if (!this._check_folder(el[j], xml)) { continue; }
173
+ l = this.parsePlacemark(el[j], xml, style);
174
+ if (l) { layers.push(l); }
175
+ }
176
+ el = xml.getElementsByTagName('GroundOverlay');
177
+ for (var k = 0; k < el.length; k++) {
178
+ if (!this._check_folder(el[k], xml)) { continue; }
179
+ l = this.parseGroundOverlay(el[k]);
180
+ if (l) { layers.push(l); }
181
+ }
182
+ if (!layers.length) { return; }
183
+ if (layers.length === 1) { return layers[0]; }
184
+ return new L.FeatureGroup(layers);
185
+ },
186
+
187
+ parsePlacemark: function (place, xml, style, options) {
188
+ var h, i, j, k, el, il, opts = options || {};
189
+
190
+ el = place.getElementsByTagName('styleUrl');
191
+ for (i = 0; i < el.length; i++) {
192
+ var url = el[i].childNodes[0].nodeValue;
193
+ for (var a in style[url]) {
194
+ opts[a] = style[url][a];
195
+ }
196
+ }
197
+
198
+ il = place.getElementsByTagName('Style')[0];
199
+ if (il) {
200
+ var inlineStyle = this.parseStyle(place);
201
+ if (inlineStyle) {
202
+ for (k in inlineStyle) {
203
+ opts[k] = inlineStyle[k];
204
+ }
205
+ }
206
+ }
207
+
208
+ var multi = ['MultiGeometry', 'MultiTrack', 'gx:MultiTrack'];
209
+ for (h in multi) {
210
+ el = place.getElementsByTagName(multi[h]);
211
+ for (i = 0; i < el.length; i++) {
212
+ return this.parsePlacemark(el[i], xml, style, opts);
213
+ }
214
+ }
215
+
216
+ var layers = [];
217
+
218
+ var parse = ['LineString', 'Polygon', 'Point', 'Track', 'gx:Track'];
219
+ for (j in parse) {
220
+ var tag = parse[j];
221
+ el = place.getElementsByTagName(tag);
222
+ for (i = 0; i < el.length; i++) {
223
+ var l = this['parse' + tag.replace(/gx:/, '')](el[i], xml, opts);
224
+ if (l) { layers.push(l); }
225
+ }
226
+ }
227
+
228
+ if (!layers.length) {
229
+ return;
230
+ }
231
+ var layer = layers[0];
232
+ if (layers.length > 1) {
233
+ layer = new L.FeatureGroup(layers);
234
+ }
235
+
236
+ var name, descr = '';
237
+ el = place.getElementsByTagName('name');
238
+ if (el.length && el[0].childNodes.length) {
239
+ name = el[0].childNodes[0].nodeValue;
240
+ }
241
+ el = place.getElementsByTagName('description');
242
+ for (i = 0; i < el.length; i++) {
243
+ for (j = 0; j < el[i].childNodes.length; j++) {
244
+ descr = descr + el[i].childNodes[j].nodeValue;
245
+ }
246
+ }
247
+
248
+ if (name) {
249
+ layer.on('add', function () {
250
+ layer.bindPopup('<h2>' + name + '</h2>' + descr, { className: 'kml-popup'});
251
+ });
252
+ }
253
+
254
+ return layer;
255
+ },
256
+
257
+ parseCoords: function (xml) {
258
+ var el = xml.getElementsByTagName('coordinates');
259
+ return this._read_coords(el[0]);
260
+ },
261
+
262
+ parseLineString: function (line, xml, options) {
263
+ var coords = this.parseCoords(line);
264
+ if (!coords.length) { return; }
265
+ return new L.Polyline(coords, options);
266
+ },
267
+
268
+ parseTrack: function (line, xml, options) {
269
+ var el = xml.getElementsByTagName('gx:coord');
270
+ if (el.length === 0) { el = xml.getElementsByTagName('coord'); }
271
+ var coords = [];
272
+ for (var j = 0; j < el.length; j++) {
273
+ coords = coords.concat(this._read_gxcoords(el[j]));
274
+ }
275
+ if (!coords.length) { return; }
276
+ return new L.Polyline(coords, options);
277
+ },
278
+
279
+ parsePoint: function (line, xml, options) {
280
+ var el = line.getElementsByTagName('coordinates');
281
+ if (!el.length) {
282
+ return;
283
+ }
284
+ var ll = el[0].childNodes[0].nodeValue.split(',');
285
+ return new L.KMLMarker(new L.LatLng(ll[1], ll[0]), options);
286
+ },
287
+
288
+ parsePolygon: function (line, xml, options) {
289
+ var el, polys = [], inner = [], i, coords;
290
+ el = line.getElementsByTagName('outerBoundaryIs');
291
+ for (i = 0; i < el.length; i++) {
292
+ coords = this.parseCoords(el[i]);
293
+ if (coords) {
294
+ polys.push(coords);
295
+ }
296
+ }
297
+ el = line.getElementsByTagName('innerBoundaryIs');
298
+ for (i = 0; i < el.length; i++) {
299
+ coords = this.parseCoords(el[i]);
300
+ if (coords) {
301
+ inner.push(coords);
302
+ }
303
+ }
304
+ if (!polys.length) {
305
+ return;
306
+ }
307
+ if (options.fillColor) {
308
+ options.fill = true;
309
+ }
310
+ if (polys.length === 1) {
311
+ return new L.Polygon(polys.concat(inner), options);
312
+ }
313
+ return new L.MultiPolygon(polys, options);
314
+ },
315
+
316
+ getLatLngs: function (xml) {
317
+ var el = xml.getElementsByTagName('coordinates');
318
+ var coords = [];
319
+ for (var j = 0; j < el.length; j++) {
320
+ // text might span many childNodes
321
+ coords = coords.concat(this._read_coords(el[j]));
322
+ }
323
+ return coords;
324
+ },
325
+
326
+ _read_coords: function (el) {
327
+ var text = '', coords = [], i;
328
+ for (i = 0; i < el.childNodes.length; i++) {
329
+ text = text + el.childNodes[i].nodeValue;
330
+ }
331
+ text = text.split(/[\s\n]+/);
332
+ for (i = 0; i < text.length; i++) {
333
+ var ll = text[i].split(',');
334
+ if (ll.length < 2) {
335
+ continue;
336
+ }
337
+ coords.push(new L.LatLng(ll[1], ll[0]));
338
+ }
339
+ return coords;
340
+ },
341
+
342
+ _read_gxcoords: function (el) {
343
+ var text = '', coords = [];
344
+ text = el.firstChild.nodeValue.split(' ');
345
+ coords.push(new L.LatLng(text[1], text[0]));
346
+ return coords;
347
+ },
348
+
349
+ parseGroundOverlay: function (xml) {
350
+ var latlonbox = xml.getElementsByTagName('LatLonBox')[0];
351
+ var bounds = new L.LatLngBounds(
352
+ [
353
+ latlonbox.getElementsByTagName('south')[0].childNodes[0].nodeValue,
354
+ latlonbox.getElementsByTagName('west')[0].childNodes[0].nodeValue
355
+ ],
356
+ [
357
+ latlonbox.getElementsByTagName('north')[0].childNodes[0].nodeValue,
358
+ latlonbox.getElementsByTagName('east')[0].childNodes[0].nodeValue
359
+ ]
360
+ );
361
+ var attributes = {Icon: true, href: true, color: true};
362
+ function _parse (xml) {
363
+ var options = {}, ioptions = {};
364
+ for (var i = 0; i < xml.childNodes.length; i++) {
365
+ var e = xml.childNodes[i];
366
+ var key = e.tagName;
367
+ if (!attributes[key]) { continue; }
368
+ var value = e.childNodes[0].nodeValue;
369
+ if (key === 'Icon') {
370
+ ioptions = _parse(e);
371
+ if (ioptions.href) { options.href = ioptions.href; }
372
+ } else if (key === 'href') {
373
+ options.href = value;
374
+ } else if (key === 'color') {
375
+ options.opacity = parseInt(value.substring(0, 2), 16) / 255.0;
376
+ options.color = '#' + value.substring(6, 8) + value.substring(4, 6) + value.substring(2, 4);
377
+ }
378
+ }
379
+ return options;
380
+ }
381
+ var options = {};
382
+ options = _parse(xml);
383
+ if (latlonbox.getElementsByTagName('rotation')[0] !== undefined) {
384
+ var rotation = latlonbox.getElementsByTagName('rotation')[0].childNodes[0].nodeValue;
385
+ options.rotation = parseFloat(rotation);
386
+ }
387
+ return new L.RotatedImageOverlay(options.href, bounds, {opacity: options.opacity, angle: options.rotation});
388
+ }
389
+
390
+ });
391
+
392
+ L.KMLIcon = L.Icon.extend({
393
+ options: {
394
+ iconSize: [32, 32],
395
+ iconAnchor: [16, 16],
396
+ },
397
+ _setIconStyles: function (img, name) {
398
+ L.Icon.prototype._setIconStyles.apply(this, [img, name]);
399
+ if( img.complete ) {
400
+ this.applyCustomStyles( img )
401
+ } else {
402
+ img.onload = this.applyCustomStyles.bind(this,img)
403
+ }
404
+
405
+ },
406
+ applyCustomStyles: function(img) {
407
+ var options = this.options;
408
+ this.options.popupAnchor = [0,(-0.83*img.height)];
409
+ if (options.anchorType.x === 'fraction')
410
+ img.style.marginLeft = (-options.anchorRef.x * img.width) + 'px';
411
+ if (options.anchorType.y === 'fraction')
412
+ img.style.marginTop = ((-(1 - options.anchorRef.y) * img.height) + 1) + 'px';
413
+ if (options.anchorType.x === 'pixels')
414
+ img.style.marginLeft = (-options.anchorRef.x) + 'px';
415
+ if (options.anchorType.y === 'pixels')
416
+ img.style.marginTop = (options.anchorRef.y - img.height + 1) + 'px';
417
+ }
418
+ });
419
+
420
+
421
+ L.KMLMarker = L.Marker.extend({
422
+ options: {
423
+ icon: new L.KMLIcon.Default()
424
+ }
425
+ });
426
+
427
+ // Inspired by https://github.com/bbecquet/Leaflet.PolylineDecorator/tree/master/src
428
+ L.RotatedImageOverlay = L.ImageOverlay.extend({
429
+ options: {
430
+ angle: 0
431
+ },
432
+ _reset: function () {
433
+ L.ImageOverlay.prototype._reset.call(this);
434
+ this._rotate();
435
+ },
436
+ _animateZoom: function (e) {
437
+ L.ImageOverlay.prototype._animateZoom.call(this, e);
438
+ this._rotate();
439
+ },
440
+ _rotate: function () {
441
+ if (L.DomUtil.TRANSFORM) {
442
+ // use the CSS transform rule if available
443
+ this._image.style[L.DomUtil.TRANSFORM] += ' rotate(' + this.options.angle + 'deg)';
444
+ } else if (L.Browser.ie) {
445
+ // fallback for IE6, IE7, IE8
446
+ var rad = this.options.angle * (Math.PI / 180),
447
+ costheta = Math.cos(rad),
448
+ sintheta = Math.sin(rad);
449
+ this._image.style.filter += ' progid:DXImageTransform.Microsoft.Matrix(sizingMethod=\'auto expand\', M11=' +
450
+ costheta + ', M12=' + (-sintheta) + ', M21=' + sintheta + ', M22=' + costheta + ')';
451
+ }
452
+ },
453
+ getBounds: function () {
454
+ return this._bounds;
455
+ }
456
+ });
@@ -2480,8 +2480,12 @@ function doCommand(cmd) {
2480
2480
  overlays[cmd.map.overlay].removeFrom(map);
2481
2481
  existsalready = true;
2482
2482
  }
2483
- //var opt = {async:true};
2484
- overlays[cmd.map.overlay] = omnivore.kml.parse(cmd.map.kml, null, custIco());
2483
+ try {
2484
+ const parser = new DOMParser();
2485
+ const kml = parser.parseFromString(cmd.map.kml, 'text/xml');
2486
+ const track = new L.KML(kml);
2487
+ overlays[cmd.map.overlay] = track;
2488
+ } catch(e) { console.log("Failed to parse KML") }
2485
2489
  if (!existsalready) {
2486
2490
  layercontrol.addOverlay(overlays[cmd.map.overlay],cmd.map.overlay);
2487
2491
  }
package/worldmap.js CHANGED
@@ -173,7 +173,7 @@ module.exports = function(RED) {
173
173
  if (height == 0) { height = 10; }
174
174
  var size = ui.getSizes();
175
175
  var frameWidth = (size.sx + size.cx) * width - size.cx;
176
- var frameHeight = (size.sy + size.cy) * height - size.cy;
176
+ var frameHeight = (size.sy + size.cy) * height - size.cy + 40;
177
177
  var url = encodeURI(path.posix.join(RED.settings.httpNodeRoot||RED.settings.httpRoot,config.path));
178
178
  if (config.layer === "MB3d") { url += "/index3d.html"; }
179
179
  var html = `<style>.nr-dashboard-ui_worldmap{padding:0;}</style><div style="overflow:hidden;">