node-red-contrib-web-worldmap 2.21.1 → 2.21.5

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,9 @@
1
1
  ### Change Log for Node-RED Worldmap
2
2
 
3
+ - v2.21.5 - Fix handling of "old" sjip nav to ship navigat
4
+ - v2.21.4 - Fix speed leader length. Add transparentPixels option.
5
+ - v2.21.3 - Add zoom to bounds action. Adjust map layers max zoom levels.
6
+ - v2.21.2 - Expand ship nav to ship navigation.
3
7
  - v2.21.1 - Fix ui check callback to not use .
4
8
  - v2.21.0 - Let config panel select maps to show, default map and choice of overlays.
5
9
  - v2.20.0 - Add support of .pbf map layers. Issue #123.
package/README.md CHANGED
@@ -11,6 +11,10 @@ map web page for plotting "things" on.
11
11
 
12
12
  ### Updates
13
13
 
14
+ - v2.21.5 - Fix handling of "old" sjip nav to ship navigation
15
+ - v2.21.4 - Fix speed leader length. Add transparentPixels option..
16
+ - v2.21.3 - Add zoom to bounds action. Adjust map layers max zoom levels.
17
+ - v2.21.2 - Expand ship nav to ship navigation.
14
18
  - v2.21.1 - Fix ui check callback to not use .
15
19
  - v2.21.0 - Let config panel select maps to show, default map and choice of overlays.
16
20
  - v2.20.0 - Add support of .pbf map layers. Issue 123.
@@ -31,12 +35,6 @@ map web page for plotting "things" on.
31
35
  - v2.15.3 - Fix panit command to work, try to use alt units, popup alignments.
32
36
  - v2.15.0 - Let speed be text and specify units if required (kt,kn,knots,mph,kmh,kph) default m/s.
33
37
  - v2.14.0 - Let geojson features be clickable if added as overlay.
34
- - v2.13.4 - Fix list of map choices to be in sync. Fix popup auto sizing.
35
- - v2.13.3 - Fix unchanged layer propagation.
36
- - v2.13.2 - Add mayflower icon.
37
- - v2.13.0 - Tidy velocity layer. Feedback any url parameters.
38
- - v2.12.1 - Only show online layer options if we are online.
39
- - v2.12.0 - Add live rainfall radar data layer. Remove some non-loading overlays.
40
38
 
41
39
  - see [CHANGELOG](https://github.com/dceejay/RedMap/blob/master/CHANGELOG.md) for full list of changes.
42
40
 
@@ -37,10 +37,8 @@ Base.isWebSocket = function(request) {
37
37
 
38
38
  Base.validateOptions = function(options, validKeys) {
39
39
  for (var key in options) {
40
- if (options.hasOwnProperty(key)) {
41
40
  if (validKeys.indexOf(key) < 0)
42
41
  throw new Error('Unrecognized option: ' + key);
43
- }
44
42
  }
45
43
  };
46
44
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-web-worldmap",
3
- "version": "2.21.1",
3
+ "version": "2.21.5",
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",
@@ -24,7 +24,7 @@ a {
24
24
  }
25
25
 
26
26
  .leaflet-interactive:hover {
27
- filter: opacity(0.5);
27
+ filter: opacity(0.7);
28
28
  }
29
29
 
30
30
  .topbar {
@@ -72,6 +72,7 @@
72
72
  <script src="leaflet/leaflet.latlng-graticule.js"></script>
73
73
  <script src="leaflet/VectorTileLayer.umd.min.js"></script>
74
74
  <script src="leaflet/Semicircle.js"></script>
75
+ <script src="leaflet/L.TileLayer.PixelFilter.js"></script>
75
76
  <script src="leaflet/dialog-polyfill.js"></script>
76
77
 
77
78
  <script src="images/emoji.js"></script>
@@ -0,0 +1,152 @@
1
+ /*
2
+ * L.TileLayer.PixelFilter
3
+ * https://github.com/greeninfo/L.TileLayer.PixelFilter
4
+ * http://greeninfo-network.github.io/L.TileLayer.PixelFilter/
5
+ */
6
+ L.tileLayerPixelFilter = function (url, options) {
7
+ return new L.TileLayer.PixelFilter(url, options);
8
+ }
9
+
10
+ L.TileLayer.PixelFilter = L.TileLayer.extend({
11
+ // the constructor saves settings and throws a fit if settings are bad, as typical
12
+ // then adds the all-important 'tileload' event handler which basically "detects" an unmodified tile and performs the pxiel-swap
13
+ initialize: function (url, options) {
14
+ options = L.extend({}, L.TileLayer.prototype.options, {
15
+ matchRGBA: null,
16
+ missRGBA: null,
17
+ pixelCodes: [],
18
+ crossOrigin: 'Anonymous', // per issue 15, this is how you do it in Leaflet 1.x
19
+ }, options);
20
+ L.TileLayer.prototype.initialize.call(this, url, options);
21
+ L.setOptions(this, options);
22
+
23
+ // go ahead and save our settings
24
+ this.setMatchRGBA(this.options.matchRGBA);
25
+ this.setMissRGBA(this.options.missRGBA);
26
+ this.setPixelCodes(this.options.pixelCodes);
27
+
28
+ // and add our tile-load event hook which triggers us to do the pixel-swap
29
+ this.on('tileload', function (event) {
30
+ this.applyFiltersToTile(event.tile);
31
+ });
32
+ },
33
+
34
+ // settings setters
35
+ setMatchRGBA: function (rgba) {
36
+ // save the setting
37
+ if (rgba !== null && (typeof rgba !== 'object' || typeof rgba.length !== 'number' || rgba.length !== 4) ) throw "L.TileLayer.PixelSwap expected matchRGBA to be RGBA [r,g,b,a] array or else null";
38
+ this.options.matchRGBA = rgba;
39
+
40
+ // force a redraw, which means new tiles, which mean new tileload events; the circle of life
41
+ this.redraw(true);
42
+ },
43
+ setMissRGBA: function (rgba) {
44
+ // save the setting
45
+ if (rgba !== null && (typeof rgba !== 'object' || typeof rgba.length !== 'number' || rgba.length !== 4) ) throw "L.TileLayer.PixelSwap expected missRGBA to be RGBA [r,g,b,a] array or else null";
46
+ this.options.missRGBA = rgba;
47
+
48
+ // force a redraw, which means new tiles, which mean new tileload events; the circle of life
49
+ this.redraw(true);
50
+ },
51
+ setPixelCodes: function (pixelcodes) {
52
+ // save the setting
53
+ if (typeof pixelcodes !== 'object' || typeof pixelcodes.length !== 'number') throw "L.TileLayer.PixelSwap expected pixelCodes to be a list of triplets: [ [r,g,b], [r,g,b], ... ]";
54
+ this.options.pixelCodes = pixelcodes;
55
+
56
+ // force a redraw, which means new tiles, which mean new tileload events; the circle of life
57
+ this.redraw(true);
58
+ },
59
+
60
+ // extend the _createTile function to add the .crossOrigin attribute, since loading tiles from a separate service is a pretty common need
61
+ // and the Canvas is paranoid about cross-domain image data. see issue #5
62
+ // this is really only for Leaflet 0.7; as of 1.0 L.TileLayer has a crossOrigin setting which we define as a layer option
63
+ _createTile: function () {
64
+ var tile = L.TileLayer.prototype._createTile.call(this);
65
+ tile.crossOrigin = "Anonymous";
66
+ return tile;
67
+ },
68
+
69
+ // the heavy lifting to do the pixel-swapping
70
+ // called upon 'tileload' and passed the IMG element
71
+ // tip: when the tile is saved back to the IMG element that counts as a tileload event too! thus an infinite loop, as wel as comparing the pixelCodes against already-replaced pixels!
72
+ // so, we tag the already-swapped tiles so we know when to quit
73
+ // if the layer is redrawn, it's a new IMG element and that means it would not yet be tagged
74
+ applyFiltersToTile: function (imgelement) {
75
+ // already processed, see note above
76
+ if (imgelement.getAttribute('data-PixelFilterDone')) return;
77
+
78
+ // copy the image data onto a canvas for manipulation
79
+ var width = imgelement.width;
80
+ var height = imgelement.height;
81
+ var canvas = document.createElement("canvas");
82
+ canvas.width = width;
83
+ canvas.height = height;
84
+ var context = canvas.getContext("2d");
85
+ context.drawImage(imgelement, 0, 0);
86
+
87
+ // create our target imagedata
88
+ var output = context.createImageData(width, height);
89
+
90
+ // extract out our RGBA trios into separate numbers, so we don't have to use rgba[i] a zillion times
91
+ var matchRGBA = this.options.matchRGBA, missRGBA = this.options.missRGBA;
92
+ if (matchRGBA !== null) {
93
+ var match_r = matchRGBA[0], match_g = matchRGBA[1], match_b = matchRGBA[2], match_a = matchRGBA[3];
94
+ }
95
+ if (missRGBA !== null) {
96
+ var miss_r = missRGBA[0], miss_g = missRGBA[1], miss_b = missRGBA[2], miss_a = missRGBA[3];
97
+ }
98
+
99
+ // go over our pixel-code list and generate the list of integers that we'll use for RGB matching
100
+ // 1000000*R + 1000*G + B = 123123123 which is an integer, and finding an integer inside an array is a lot faster than finding an array inside an array
101
+ var pixelcodes = [];
102
+ for (var i=0, l=this.options.pixelCodes.length; i<l; i++) {
103
+ var value = 1000000 * this.options.pixelCodes[i][0] + 1000 * this.options.pixelCodes[i][1] + this.options.pixelCodes[i][2];
104
+ pixelcodes.push(value);
105
+ }
106
+
107
+ // iterate over the pixels (each one is 4 bytes, RGBA)
108
+ // and see if they are on our list (recall the "addition" thing so we're comparing integers in an array for performance)
109
+ // per issue #5 catch a failure here, which is likely a cross-domain problem
110
+ try {
111
+ var pixels = context.getImageData(0, 0, width, height).data;
112
+ } catch(e) {
113
+ throw "L.TileLayer.PixelFilter getImageData() failed. Likely a cross-domain issue?";
114
+ }
115
+ for(var i = 0, n = pixels.length; i < n; i += 4) {
116
+ var r = pixels[i ];
117
+ var g = pixels[i+1];
118
+ var b = pixels[i+2];
119
+ var a = pixels[i+3];
120
+
121
+ // bail condition: if the alpha is 0 then it's already transparent, likely nodata, and we should skip it
122
+ if (a == 0) {
123
+ output.data[i ] = 255;
124
+ output.data[i+1] = 255;
125
+ output.data[i+2] = 255;
126
+ output.data[i+3] = 0;
127
+ continue;
128
+ }
129
+
130
+ // default to matching, so that if we are not in fact filtering by code it's an automatic hit
131
+ // number matching trick: 1000000*R + 1000*G + 1*B = 123,123,123 a simple number that either is or isn't on the list
132
+ var match = true;
133
+ if (pixelcodes.length) {
134
+ var sum = 1000000 * r + 1000 * g + b;
135
+ if (-1 === pixelcodes.indexOf(sum)) match = false;
136
+ }
137
+
138
+ // did it match? either way we push a R, a G, and a B onto the image blob
139
+ // if the target RGBA is a null, then we push exactly the same RGBA as we found in the source pixel
140
+ output.data[i ] = match ? (matchRGBA===null ? r : match_r) : (missRGBA===null ? r : miss_r);
141
+ output.data[i+1] = match ? (matchRGBA===null ? g : match_g) : (missRGBA===null ? g : miss_g);
142
+ output.data[i+2] = match ? (matchRGBA===null ? b : match_b) : (missRGBA===null ? b : miss_b);
143
+ output.data[i+3] = match ? (matchRGBA===null ? a : match_a) : (missRGBA===null ? a : miss_a);
144
+ }
145
+
146
+ // write the image back to the canvas, and assign its base64 back into the on-screen tile to visualize the change
147
+ // tag the tile as having already been updated, so we don't process a 'load' event again and re-process a tile that surely won't match any target RGB codes, in an infinite loop!
148
+ context.putImageData(output, 0, 0);
149
+ imgelement.setAttribute('data-PixelFilterDone', true);
150
+ imgelement.src = canvas.toDataURL();
151
+ }
152
+ });
@@ -1 +1 @@
1
- "use strict";L.DomUtil.setTransform||(L.DomUtil.setTransform=function(t,n,e){var i=n||new L.Point(0,0);t.style[L.DomUtil.TRANSFORM]=(L.Browser.ie3d?"translate("+i.x+"px,"+i.y+"px)":"translate3d("+i.x+"px,"+i.y+"px,0)")+(e?" scale("+e+")":"")}),L.CanvasLayer=(L.Layer?L.Layer:L.Class).extend({initialize:function(t){this._map=null,this._canvas=null,this._frame=null,this._delegate=null,L.setOptions(this,t)},delegate:function(t){return this._delegate=t,this},needRedraw:function(){return this._frame||(this._frame=L.Util.requestAnimFrame(this.drawLayer,this)),this},_onLayerDidResize:function(t){this._canvas.width=t.newSize.x,this._canvas.height=t.newSize.y},_onLayerDidMove:function(){var t=this._map.containerPointToLayerPoint([0,0]);L.DomUtil.setPosition(this._canvas,t),this.drawLayer()},getEvents:function(){var t={resize:this._onLayerDidResize,moveend:this._onLayerDidMove};return this._map.options.zoomAnimation&&L.Browser.any3d&&(t.zoomanim=this._animateZoom),t},onAdd:function(t){this._map=t,this._canvas=L.DomUtil.create("canvas","leaflet-layer"),this.tiles={};var n=this._map.getSize();this._canvas.width=n.x,this._canvas.height=n.y;var e=this._map.options.zoomAnimation&&L.Browser.any3d;L.DomUtil.addClass(this._canvas,"leaflet-zoom-"+(e?"animated":"hide")),this.options.pane.appendChild(this._canvas),t.on(this.getEvents(),this);var i=this._delegate||this;i.onLayerDidMount&&i.onLayerDidMount(),this.needRedraw();var o=this;setTimeout(function(){o._onLayerDidMove()},0)},onRemove:function(t){var n=this._delegate||this;n.onLayerWillUnmount&&n.onLayerWillUnmount(),this.options.pane.removeChild(this._canvas),t.off(this.getEvents(),this),this._canvas=null},addTo:function(t){return t.addLayer(this),this},drawLayer:function(){var t=this._map.getSize(),n=this._map.getBounds(),e=this._map.getZoom(),i=this._map.options.crs.project(this._map.getCenter()),o=this._map.options.crs.project(this._map.containerPointToLatLng(this._map.getSize())),a=this._delegate||this;a.onDrawLayer&&a.onDrawLayer({layer:this,canvas:this._canvas,bounds:n,size:t,zoom:e,center:i,corner:o}),this._frame=null},_setTransform:function(t,n,e){var i=n||new L.Point(0,0);t.style[L.DomUtil.TRANSFORM]=(L.Browser.ie3d?"translate("+i.x+"px,"+i.y+"px)":"translate3d("+i.x+"px,"+i.y+"px,0)")+(e?" scale("+e+")":"")},_animateZoom:function(t){var n=this._map.getZoomScale(t.zoom),e=L.Layer?this._map._latLngToNewLayerPoint(this._map.getBounds().getNorthWest(),t.zoom,t.center):this._map._getCenterOffset(t.center)._multiplyBy(-n).subtract(this._map._getMapPanePos());L.DomUtil.setTransform(this._canvas,e,n)}}),L.canvasLayer=function(t){return new L.CanvasLayer(t)},L.Control.Velocity=L.Control.extend({options:{position:"bottomleft",emptyString:"Unavailable",angleConvention:"bearingCCW",showCardinal:!1,speedUnit:"m/s",directionString:"Direction",speedString:"Speed",onAdd:null,onRemove:null},onAdd:function(t){return this._container=L.DomUtil.create("div","leaflet-control-velocity"),L.DomEvent.disableClickPropagation(this._container),t.on("mousemove",this._onMouseMove,this),this._container.innerHTML=this.options.emptyString,this.options.leafletVelocity.options.onAdd&&this.options.leafletVelocity.options.onAdd(),this._container},onRemove:function(t){t.off("mousemove",this._onMouseMove,this),this.options.leafletVelocity.options.onRemove&&this.options.leafletVelocity.options.onRemove()},vectorToSpeed:function(t,n,e){var i=Math.sqrt(Math.pow(t,2)+Math.pow(n,2));return"k/h"===e?this.meterSec2kilometerHour(i):"kt"===e?this.meterSec2Knots(i):i},vectorToDegrees:function(t,n,e){e.endsWith("CCW")&&(n=0<n?n=-n:Math.abs(n));var i=Math.sqrt(Math.pow(t,2)+Math.pow(n,2)),o=180*Math.atan2(t/i,n/i)/Math.PI+180;return"bearingCW"!==e&&"meteoCCW"!==e||360<=(o+=180)&&(o-=360),o},degreesToCardinalDirection:function(t){var n="";return 0<=t&&t<11.25||348.75<=t?n="N":11.25<=t&&t<33.75?n="NNW":33.75<=t&&t<56.25?n="NW":56.25<=t&&t<78.75?n="WNW":78.25<=t&&t<101.25?n="W":101.25<=t&&t<123.75?n="WSW":123.75<=t&&t<146.25?n="SW":146.25<=t&&t<168.75?n="SSW":168.75<=t&&t<191.25?n="S":191.25<=t&&t<213.75?n="SSE":213.75<=t&&t<236.25?n="SE":236.25<=t&&t<258.75?n="ESE":258.75<=t&&t<281.25?n="E":281.25<=t&&t<303.75?n="ENE":303.75<=t&&t<326.25?n="NE":326.25<=t&&t<348.75&&(n="NNE"),n},meterSec2Knots:function(t){return t/.514},meterSec2kilometerHour:function(t){return 3.6*t},_onMouseMove:function(t){var n=this.options.leafletVelocity._map.containerPointToLatLng(L.point(t.containerPoint.x,t.containerPoint.y)),e=this.options.leafletVelocity._windy.interpolatePoint(n.lng,n.lat),i="";if(e&&!isNaN(e[0])&&!isNaN(e[1])&&e[2]){var o=this.vectorToDegrees(e[0],e[1],this.options.angleConvention),a=this.options.showCardinal?" (".concat(this.degreesToCardinalDirection(o),")"):"";i="<strong> ".concat(this.options.velocityType," ").concat(this.options.directionString,": </strong> ").concat(o.toFixed(2),"°").concat(a,", <strong> ").concat(this.options.speedString,": </strong> ").concat(this.vectorToSpeed(e[0],e[1],this.options.speedUnit).toFixed(2)," ").concat(this.options.speedUnit)}else i=this.options.emptyString;this._container.innerHTML=i}}),L.Map.mergeOptions({positionControl:!1}),L.Map.addInitHook(function(){this.options.positionControl&&(this.positionControl=new L.Control.MousePosition,this.addControl(this.positionControl))}),L.control.velocity=function(t){return new L.Control.Velocity(t)},L.VelocityLayer=(L.Layer?L.Layer:L.Class).extend({options:{displayValues:!0,displayOptions:{velocityType:"Velocity",position:"bottomleft",emptyString:"No velocity data"},maxVelocity:10,colorScale:null,data:null},_map:null,_canvasLayer:null,_windy:null,_context:null,_timer:0,_mouseControl:null,initialize:function(t){L.setOptions(this,t)},onAdd:function(t){this._paneName=this.options.paneName||"overlayPane";var n=t._panes.overlayPane;t.getPane&&(n=(n=t.getPane(this._paneName))||t.createPane(this._paneName)),this._canvasLayer=L.canvasLayer({pane:n}).delegate(this),this._canvasLayer.addTo(t),this._map=t},onRemove:function(t){this._destroyWind()},setData:function(t){this.options.data=t,this._windy&&(this._windy.setData(t),this._clearAndRestart()),this.fire("load")},setOpacity:function(t){console.log("this._canvasLayer",this._canvasLayer),this._canvasLayer.setOpacity(t)},setOptions:function(t){this.options=Object.assign(this.options,t),t.hasOwnProperty("displayOptions")&&(this.options.displayOptions=Object.assign(this.options.displayOptions,t.displayOptions),this._initMouseHandler(!0)),t.hasOwnProperty("data")&&(this.options.data=t.data),this._windy&&(this._windy.setOptions(t),t.hasOwnProperty("data")&&this._windy.setData(t.data),this._clearAndRestart()),this.fire("load")},onDrawLayer:function(t,n){var e=this;this._windy?this.options.data&&(this._timer&&clearTimeout(e._timer),this._timer=setTimeout(function(){e._startWindy()},750)):this._initWindy(this)},_startWindy:function(){var t=this._map.getBounds(),n=this._map.getSize();this._windy.start([[0,0],[n.x,n.y]],n.x,n.y,[[t._southWest.lng,t._southWest.lat],[t._northEast.lng,t._northEast.lat]])},_initWindy:function(t){var n=Object.assign({canvas:t._canvasLayer._canvas,map:this._map},t.options);this._windy=new Windy(n),this._context=this._canvasLayer._canvas.getContext("2d"),this._canvasLayer._canvas.classList.add("velocity-overlay"),this.onDrawLayer(),this._map.on("dragstart",t._windy.stop),this._map.on("dragend",t._clearAndRestart),this._map.on("zoomstart",t._windy.stop),this._map.on("zoomend",t._clearAndRestart),this._map.on("resize",t._clearWind),this._initMouseHandler(!1)},_initMouseHandler:function(t){if(t&&(this._map.removeControl(this._mouseControl),this._mouseControl=!1),!this._mouseControl&&this.options.displayValues){var n=this.options.displayOptions||{};(n.leafletVelocity=this)._mouseControl=L.control.velocity(n).addTo(this._map)}},_clearAndRestart:function(){this._context&&this._context.clearRect(0,0,3e3,3e3),this._windy&&this._startWindy()},_clearWind:function(){this._windy&&this._windy.stop(),this._context&&this._context.clearRect(0,0,3e3,3e3)},_destroyWind:function(){this._timer&&clearTimeout(this._timer),this._windy&&this._windy.stop(),this._context&&this._context.clearRect(0,0,3e3,3e3),this._mouseControl&&this._map.removeControl(this._mouseControl),this._mouseControl=null,this._windy=null,this._map.removeLayer(this._canvasLayer)}}),L.velocityLayer=function(t){return new L.VelocityLayer(t)};var Windy=function(b){function o(t,n,e,i,o,a){var r=1-t,s=1-n,l=r*s,h=t*s,c=r*n,d=t*n,p=e[0]*l+i[0]*h+o[0]*c+a[0]*d,u=e[1]*l+i[1]*h+o[1]*c+a[1]*d;return[p,u,Math.sqrt(p*p+u*u)]}function d(t){var n=null,e=null;return t.forEach(function(t){switch(t.header.parameterCategory+","+t.header.parameterNumber){case"1,2":case"2,2":n=t;break;case"1,3":case"2,3":e=t;break;default:t}}),function(t,n){var e=t.data,i=n.data;return{header:t.header,data:function(t){return[e[t],i[t]]},interpolate:o}}(n,e)}function a(i,o,t){function a(t,n){var e=i[Math.round(t)];return e&&e[Math.round(n)]||h}a.release=function(){i=[]},a.randomize=function(t){for(var n,e,i=0;null===a(n=Math.round(Math.floor(Math.random()*o.width)+o.x),e=Math.round(Math.floor(Math.random()*o.height)+o.y))[2]&&i++<30;);return t.x=n,t.y=e,t},t(o,a)}function r(t){return t/180*Math.PI}function s(i,s){var n,e,l=(n=x,e=C,R.indexFor=function(t){return Math.max(0,Math.min(R.length-1,Math.round((t-n)/(e-n)*(R.length-1))))},R),h=l.map(function(){return[]}),t=Math.round(i.width*i.height*D);/android|blackberry|iemobile|ipad|iphone|ipod|opera mini|webos/i.test(navigator.userAgent)&&(t*=T);for(var o="rgba(0, 0, 0, ".concat(O,")"),a=[],r=0;r<t;r++)a.push(s.randomize({age:Math.floor(Math.random()*P)+0}));var c=b.canvas.getContext("2d");c.lineWidth=S,c.fillStyle=o,c.globalAlpha=.6;var d=Date.now();!function t(){M=requestAnimationFrame(t);var n=Date.now(),e=n-d;W<e&&(d=n-e%W,h.forEach(function(t){t.length=0}),a.forEach(function(t){t.age>P&&(s.randomize(t).age=0);var n=t.x,e=t.y,i=s(n,e),o=i[2];if(null===o)t.age=P;else{var a=n+i[0],r=e+i[1];null!==s(a,r)[2]?(t.xt=a,t.yt=r,h[l.indexFor(o)].push(t)):(t.x=a,t.y=r)}t.age+=1}),c.globalCompositeOperation="destination-in",c.fillRect(i.x,i.y,i.width,i.height),c.globalCompositeOperation="lighter",c.globalAlpha=0===O?0:.9*O,h.forEach(function(t,n){0<t.length&&(c.beginPath(),c.strokeStyle=l[n],t.forEach(function(t){c.moveTo(t.x,t.y),c.lineTo(t.xt,t.yt),t.x=t.xt,t.y=t.yt}),c.stroke())}))}()}var u,m,p,y,_,f,v,g,w,M,x=b.minVelocity||0,C=b.maxVelocity||10,l=(b.velocityScale||.005)*(Math.pow(window.devicePixelRatio,1/3)||1),P=b.particleAge||90,S=b.lineWidth||1,D=b.particleMultiplier||1/300,T=Math.pow(window.devicePixelRatio,1/3)||1.6,n=b.frameRate||15,W=1e3/n,O=.97,R=b.colorScale||["rgb(36,104, 180)","rgb(60,157, 194)","rgb(128,205,193 )","rgb(151,218,168 )","rgb(198,231,181)","rgb(238,247,217)","rgb(255,238,159)","rgb(252,217,125)","rgb(255,182,100)","rgb(252,150,75)","rgb(250,112,52)","rgb(245,64,32)","rgb(237,45,28)","rgb(220,24,32)","rgb(180,0,35)"],h=[NaN,NaN,null],c=b.data,N=function(t,n){if(!m)return null;var e,i=z(t-y,360)/f,o=(_-n)/v,a=Math.floor(i),r=a+1,s=Math.floor(o),l=s+1;if(e=m[s]){var h=e[a],c=e[r];if(A(h)&&A(c)&&(e=m[l])){var d=e[a],p=e[r];if(A(d)&&A(p))return u.interpolate(i-a,o-s,h,c,d,p)}}return null},A=function(t){return null!=t},z=function(t,n){return t-n*Math.floor(t/n)},E=function(t,n,e,i,o){var a=2*Math.PI,r=n<0?5:-5,s=e<0?5:-5,l=V(e,n+r),h=V(e+s,n),c=Math.cos(e/360*a);return[(l[0]-i)/r/c,(l[1]-o)/r/c,(h[0]-i)/s,(h[1]-o)/s]},V=function(t,n,e){var i=b.map.latLngToContainerPoint(L.latLng(t,n));return[i.x,i.y]},U=function(){F.field&&F.field.release(),M&&cancelAnimationFrame(M)},F={params:b,start:function(n,e,i,t){var o={south:r(t[0][1]),north:r(t[1][1]),east:r(t[1][0]),west:r(t[0][0]),width:e,height:i};U(),function(t,n){var e=!0;t.length<2&&(e=!1),e||console.log("Windy Error: data must have at least two components (u,v)");var i=(u=d(t)).header;if(i.hasOwnProperty("gridDefinitionTemplate")&&0!=i.gridDefinitionTemplate&&(e=!1),e||console.log("Windy Error: Only data with Latitude_Longitude coordinates is supported"),e=!0,y=i.lo1,_=i.la1,f=i.dx,v=i.dy,g=i.nx,w=i.ny,i.hasOwnProperty("scanMode")){var o=i.scanMode.toString(2),a=(o=("0"+o).slice(-8)).split("").map(Number).map(Boolean);a[0]&&(f=-f),a[1]&&(v=-v),a[2]&&(e=!1),a[3]&&(e=!1),a[4]&&(e=!1),a[5]&&(e=!1),a[6]&&(e=!1),a[7]&&(e=!1),e||console.log("Windy Error: Data with scanMode: "+i.scanMode+" is not supported.")}(p=new Date(i.refTime)).setHours(p.getHours()+i.forecastTime),m=[];for(var r=0,s=360<=Math.floor(g*f),l=0;l<w;l++){for(var h=[],c=0;c<g;c++,r++)h[c]=u.data(r);s&&h.push(h[0]),m[l]=h}n({date:p,interpolate:N})}(c,function(t){!function(w,M,t,e){var x={},n=(t.south-t.north)*(t.west-t.east),C=l*Math.pow(n,.4),P=[],i=M.x;function o(t){for(var n,e,i,o,a,r,s,l,h,c,d,p,u,m=[],y=M.y;y<=M.yMax;y+=2){var _=(d=t,p=y,void 0,[(u=b.map.containerPointToLatLng(L.point(d,p))).lng,u.lat]),f=_[0],v=_[1];if(isFinite(f)){var g=w.interpolate(f,v);g&&(n=x,e=f,i=v,o=t,a=y,r=C,void 0,l=(s=g)[0]*r,h=s[1]*r,c=E(n,e,i,o,a),s[0]=c[0]*l+c[2]*h,s[1]=c[1]*l+c[3]*h,g=s,m[y+1]=m[y]=g)}}P[t+1]=P[t]=m}!function t(){for(var n=Date.now();i<M.width;)if(o(i),i+=2,1e3<Date.now()-n)return void setTimeout(t,25);a(P,M,e)}()}(t,function(t,n,e){var i=t[0],o=t[1],a=Math.round(i[0]),r=Math.max(Math.floor(i[1],0),0);Math.min(Math.ceil(o[0],n),n-1);return{x:a,y:r,xMax:n,yMax:Math.min(Math.ceil(o[1],e),e-1),width:n,height:e}}(n,e,i),o,function(t,n){F.field=n,s(t,n)})})},stop:U,createField:a,interpolatePoint:N,setData:function(t){c=t},setOptions:function(t){t.hasOwnProperty("minVelocity")&&(x=t.minVelocity),t.hasOwnProperty("maxVelocity")&&(C=t.maxVelocity),t.hasOwnProperty("velocityScale")&&(l=(t.velocityScale||.005)*(Math.pow(window.devicePixelRatio,1/3)||1)),t.hasOwnProperty("particleAge")&&(P=t.particleAge),t.hasOwnProperty("lineWidth")&&(S=t.lineWidth),t.hasOwnProperty("particleMultiplier")&&(D=t.particleMultiplier),t.hasOwnProperty("opacity")&&(O=+t.opacity),t.hasOwnProperty("frameRate")&&(n=t.frameRate),W=1e3/n}};return F};window.cancelAnimationFrame||(window.cancelAnimationFrame=function(t){clearTimeout(t)});
1
+ "use strict";L.DomUtil.setTransform||(L.DomUtil.setTransform=function(t,e,n){var i=e||new L.Point(0,0);t.style[L.DomUtil.TRANSFORM]=(L.Browser.ie3d?"translate("+i.x+"px,"+i.y+"px)":"translate3d("+i.x+"px,"+i.y+"px,0)")+(n?" scale("+n+")":"")}),L.CanvasLayer=(L.Layer?L.Layer:L.Class).extend({initialize:function(t){this._map=null,this._canvas=null,this._frame=null,this._delegate=null,L.setOptions(this,t)},delegate:function(t){return this._delegate=t,this},needRedraw:function(){return this._frame||(this._frame=L.Util.requestAnimFrame(this.drawLayer,this)),this},_onLayerDidResize:function(t){this._canvas.width=t.newSize.x,this._canvas.height=t.newSize.y},_onLayerDidMove:function(){var t=this._map.containerPointToLayerPoint([0,0]);L.DomUtil.setPosition(this._canvas,t),this.drawLayer()},getEvents:function(){var t={resize:this._onLayerDidResize,moveend:this._onLayerDidMove};return this._map.options.zoomAnimation&&L.Browser.any3d&&(t.zoomanim=this._animateZoom),t},onAdd:function(t){console.log("canvas onAdd",this),this._map=t,this._canvas=L.DomUtil.create("canvas","leaflet-layer"),this.tiles={};var e=this._map.getSize();this._canvas.width=e.x,this._canvas.height=e.y;var n=this._map.options.zoomAnimation&&L.Browser.any3d;L.DomUtil.addClass(this._canvas,"leaflet-zoom-"+(n?"animated":"hide")),this.options.pane.appendChild(this._canvas),t.on(this.getEvents(),this);var i=this._delegate||this;i.onLayerDidMount&&i.onLayerDidMount(),this.needRedraw();var o=this;setTimeout(function(){o._onLayerDidMove()},0)},onRemove:function(t){var e=this._delegate||this;e.onLayerWillUnmount&&e.onLayerWillUnmount(),this.options.pane.removeChild(this._canvas),t.off(this.getEvents(),this),this._canvas=null},addTo:function(t){return t.addLayer(this),this},drawLayer:function(){var t=this._map.getSize(),e=this._map.getBounds(),n=this._map.getZoom(),i=this._map.options.crs.project(this._map.getCenter()),o=this._map.options.crs.project(this._map.containerPointToLatLng(this._map.getSize())),a=this._delegate||this;a.onDrawLayer&&a.onDrawLayer({layer:this,canvas:this._canvas,bounds:e,size:t,zoom:n,center:i,corner:o}),this._frame=null},_setTransform:function(t,e,n){var i=e||new L.Point(0,0);t.style[L.DomUtil.TRANSFORM]=(L.Browser.ie3d?"translate("+i.x+"px,"+i.y+"px)":"translate3d("+i.x+"px,"+i.y+"px,0)")+(n?" scale("+n+")":"")},_animateZoom:function(t){var e=this._map.getZoomScale(t.zoom),n=L.Layer?this._map._latLngToNewLayerPoint(this._map.getBounds().getNorthWest(),t.zoom,t.center):this._map._getCenterOffset(t.center)._multiplyBy(-e).subtract(this._map._getMapPanePos());L.DomUtil.setTransform(this._canvas,n,e)}}),L.canvasLayer=function(t){return new L.CanvasLayer(t)},L.Control.Velocity=L.Control.extend({options:{position:"bottomleft",emptyString:"Unavailable",angleConvention:"bearingCCW",showCardinal:!1,speedUnit:"m/s",directionString:"Direction",speedString:"Speed",onAdd:null,onRemove:null},onAdd:function(t){return this._container=L.DomUtil.create("div","leaflet-control-velocity"),L.DomEvent.disableClickPropagation(this._container),t.on("mousemove",this._onMouseMove,this),this._container.innerHTML=this.options.emptyString,this.options.leafletVelocity.options.onAdd&&this.options.leafletVelocity.options.onAdd(),this._container},onRemove:function(t){t.off("mousemove",this._onMouseMove,this),this.options.leafletVelocity.options.onRemove&&this.options.leafletVelocity.options.onRemove()},vectorToSpeed:function(t,e,n){var i=Math.sqrt(Math.pow(t,2)+Math.pow(e,2));return"k/h"===n?this.meterSec2kilometerHour(i):"kt"===n?this.meterSec2Knots(i):"mph"===n?this.meterSec2milesHour(i):i},vectorToDegrees:function(t,e,n){n.endsWith("CCW")&&(e=0<e?e=-e:Math.abs(e));var i=Math.sqrt(Math.pow(t,2)+Math.pow(e,2)),o=180*Math.atan2(t/i,e/i)/Math.PI+180;return"bearingCW"!==n&&"meteoCCW"!==n||360<=(o+=180)&&(o-=360),o},degreesToCardinalDirection:function(t){var e="";return 0<=t&&t<11.25||348.75<=t?e="N":11.25<=t&&t<33.75?e="NNW":33.75<=t&&t<56.25?e="NW":56.25<=t&&t<78.75?e="WNW":78.25<=t&&t<101.25?e="W":101.25<=t&&t<123.75?e="WSW":123.75<=t&&t<146.25?e="SW":146.25<=t&&t<168.75?e="SSW":168.75<=t&&t<191.25?e="S":191.25<=t&&t<213.75?e="SSE":213.75<=t&&t<236.25?e="SE":236.25<=t&&t<258.75?e="ESE":258.75<=t&&t<281.25?e="E":281.25<=t&&t<303.75?e="ENE":303.75<=t&&t<326.25?e="NE":326.25<=t&&t<348.75&&(e="NNE"),e},meterSec2Knots:function(t){return t/.514},meterSec2kilometerHour:function(t){return 3.6*t},meterSec2milesHour:function(t){return 2.23694*t},_onMouseMove:function(t){var e=this.options.leafletVelocity._map.containerPointToLatLng(L.point(t.containerPoint.x,t.containerPoint.y)),n=this.options.leafletVelocity._windy.interpolatePoint(e.lng,e.lat),i="";if(n&&!isNaN(n[0])&&!isNaN(n[1])&&n[2]){var o=this.vectorToDegrees(n[0],n[1],this.options.angleConvention),a=this.options.showCardinal?" (".concat(this.degreesToCardinalDirection(o),") "):"";i="<strong> ".concat(this.options.velocityType," ").concat(this.options.directionString,": </strong> ").concat(o.toFixed(2),"°").concat(a,", <strong> ").concat(this.options.velocityType," ").concat(this.options.speedString,": </strong> ").concat(this.vectorToSpeed(n[0],n[1],this.options.speedUnit).toFixed(2)," ").concat(this.options.speedUnit)}else i=this.options.emptyString;this._container.innerHTML=i}}),L.Map.mergeOptions({positionControl:!1}),L.Map.addInitHook(function(){this.options.positionControl&&(this.positionControl=new L.Control.MousePosition,this.addControl(this.positionControl))}),L.control.velocity=function(t){return new L.Control.Velocity(t)},L.VelocityLayer=(L.Layer?L.Layer:L.Class).extend({options:{displayValues:!0,displayOptions:{velocityType:"Velocity",position:"bottomleft",emptyString:"No velocity data"},maxVelocity:10,colorScale:null,data:null},_map:null,_canvasLayer:null,_windy:null,_context:null,_timer:0,_mouseControl:null,initialize:function(t){L.setOptions(this,t)},onAdd:function(t){this._paneName=this.options.paneName||"overlayPane";var e=t._panes.overlayPane;t.getPane&&(e=(e=t.getPane(this._paneName))||t.createPane(this._paneName)),this._canvasLayer=L.canvasLayer({pane:e}).delegate(this),this._canvasLayer.addTo(t),this._map=t},onRemove:function(t){this._destroyWind()},setData:function(t){this.options.data=t,this._windy&&(this._windy.setData(t),this._clearAndRestart()),this.fire("load")},setOpacity:function(t){console.log("this._canvasLayer",this._canvasLayer),this._canvasLayer.setOpacity(t)},setOptions:function(t){this.options=Object.assign(this.options,t),t.hasOwnProperty("displayOptions")&&(this.options.displayOptions=Object.assign(this.options.displayOptions,t.displayOptions),this._initMouseHandler(!0)),t.hasOwnProperty("data")&&(this.options.data=t.data),this._windy&&(this._windy.setOptions(t),t.hasOwnProperty("data")&&this._windy.setData(t.data),this._clearAndRestart()),this.fire("load")},onDrawLayer:function(t,e){var n=this;this._windy?this.options.data&&(this._timer&&clearTimeout(n._timer),this._timer=setTimeout(function(){n._startWindy()},750)):this._initWindy(this)},_startWindy:function(){var t=this._map.getBounds(),e=this._map.getSize();this._windy.start([[0,0],[e.x,e.y]],e.x,e.y,[[t._southWest.lng,t._southWest.lat],[t._northEast.lng,t._northEast.lat]])},_initWindy:function(t){var e=Object.assign({canvas:t._canvasLayer._canvas,map:this._map},t.options);this._windy=new Windy(e),this._context=this._canvasLayer._canvas.getContext("2d"),this._canvasLayer._canvas.classList.add("velocity-overlay"),this.onDrawLayer(),this._map.on("dragstart",t._windy.stop),this._map.on("dragend",t._clearAndRestart),this._map.on("zoomstart",t._windy.stop),this._map.on("zoomend",t._clearAndRestart),this._map.on("resize",t._clearWind),this._initMouseHandler(!1)},_initMouseHandler:function(t){if(t&&(this._map.removeControl(this._mouseControl),this._mouseControl=!1),!this._mouseControl&&this.options.displayValues){var e=this.options.displayOptions||{};(e.leafletVelocity=this)._mouseControl=L.control.velocity(e).addTo(this._map)}},_clearAndRestart:function(){this._context&&this._context.clearRect(0,0,3e3,3e3),this._windy&&this._startWindy()},_clearWind:function(){this._windy&&this._windy.stop(),this._context&&this._context.clearRect(0,0,3e3,3e3)},_destroyWind:function(){this._timer&&clearTimeout(this._timer),this._windy&&this._windy.stop(),this._context&&this._context.clearRect(0,0,3e3,3e3),this._mouseControl&&this._map.removeControl(this._mouseControl),this._mouseControl=null,this._windy=null,this._map.removeLayer(this._canvasLayer)}}),L.velocityLayer=function(t){return new L.VelocityLayer(t)};var Windy=function(S){function o(t,e,n,i,o,a){var r=1-t,s=1-e,l=r*s,h=t*s,c=r*e,d=t*e,p=n[0]*l+i[0]*h+o[0]*c+a[0]*d,u=n[1]*l+i[1]*h+o[1]*c+a[1]*d;return[p,u,Math.sqrt(p*p+u*u)]}function d(t){var e=null,n=null;return t.forEach(function(t){switch(t.header.parameterCategory+","+t.header.parameterNumber){case"1,2":case"2,2":e=t;break;case"1,3":case"2,3":n=t;break;default:t}}),function(t,e){var n=t.data,i=e.data;return{header:t.header,data:function(t){return[n[t],i[t]]},interpolate:o}}(e,n)}function a(i,o,t){function a(t,e){var n=i[Math.round(t)];return n&&n[Math.round(e)]||h}a.release=function(){i=[]},a.randomize=function(t){for(var e,n,i=0;null===a(e=Math.round(Math.floor(Math.random()*o.width)+o.x),n=Math.round(Math.floor(Math.random()*o.height)+o.y))[2]&&i++<30;);return t.x=e,t.y=n,t},t(o,a)}function r(t){return t/180*Math.PI}function s(i,s){var e,n,l=(e=x,n=C,R.indexFor=function(t){return Math.max(0,Math.min(R.length-1,Math.round((t-e)/(n-e)*(R.length-1))))},R),h=l.map(function(){return[]}),t=Math.round(i.width*i.height*D);/android|blackberry|iemobile|ipad|iphone|ipod|opera mini|webos/i.test(navigator.userAgent)&&(t*=T);for(var o="rgba(0, 0, 0, ".concat(O,")"),a=[],r=0;r<t;r++)a.push(s.randomize({age:Math.floor(Math.random()*P)+0}));var c=S.canvas.getContext("2d");c.lineWidth=b,c.fillStyle=o,c.globalAlpha=.6;var d=Date.now();!function t(){M=requestAnimationFrame(t);var e=Date.now(),n=e-d;W<n&&(d=e-n%W,h.forEach(function(t){t.length=0}),a.forEach(function(t){t.age>P&&(s.randomize(t).age=0);var e=t.x,n=t.y,i=s(e,n),o=i[2];if(null===o)t.age=P;else{var a=e+i[0],r=n+i[1];null!==s(a,r)[2]?(t.xt=a,t.yt=r,h[l.indexFor(o)].push(t)):(t.x=a,t.y=r)}t.age+=1}),c.globalCompositeOperation="destination-in",c.fillRect(i.x,i.y,i.width,i.height),c.globalCompositeOperation="lighter",c.globalAlpha=0===O?0:.9*O,h.forEach(function(t,e){0<t.length&&(c.beginPath(),c.strokeStyle=l[e],t.forEach(function(t){c.moveTo(t.x,t.y),c.lineTo(t.xt,t.yt),t.x=t.xt,t.y=t.yt}),c.stroke())}))}()}var u,m,p,y,_,f,v,g,w,M,x=S.minVelocity||0,C=S.maxVelocity||10,l=(S.velocityScale||.005)*(Math.pow(window.devicePixelRatio,1/3)||1),P=S.particleAge||90,b=S.lineWidth||1,D=S.particleMultiplier||1/300,T=Math.pow(window.devicePixelRatio,1/3)||1.6,e=S.frameRate||15,W=1e3/e,O=.97,R=S.colorScale||["rgb(36,104, 180)","rgb(60,157, 194)","rgb(128,205,193 )","rgb(151,218,168 )","rgb(198,231,181)","rgb(238,247,217)","rgb(255,238,159)","rgb(252,217,125)","rgb(255,182,100)","rgb(252,150,75)","rgb(250,112,52)","rgb(245,64,32)","rgb(237,45,28)","rgb(220,24,32)","rgb(180,0,35)"],h=[NaN,NaN,null],c=S.data,N=function(t,e){if(!m)return null;var n,i=z(t-y,360)/f,o=(_-e)/v,a=Math.floor(i),r=a+1,s=Math.floor(o),l=s+1;if(n=m[s]){var h=n[a],c=n[r];if(A(h)&&A(c)&&(n=m[l])){var d=n[a],p=n[r];if(A(d)&&A(p))return u.interpolate(i-a,o-s,h,c,d,p)}}return null},A=function(t){return null!=t},z=function(t,e){return t-e*Math.floor(t/e)},E=function(t,e,n,i,o){var a=2*Math.PI,r=e<0?5:-5,s=n<0?5:-5,l=V(n,e+r),h=V(n+s,e),c=Math.cos(n/360*a);return[(l[0]-i)/r/c,(l[1]-o)/r/c,(h[0]-i)/s,(h[1]-o)/s]},V=function(t,e,n){var i=S.map.latLngToContainerPoint(L.latLng(t,e));return[i.x,i.y]},U=function(){F.field&&F.field.release(),M&&cancelAnimationFrame(M)},F={params:S,start:function(e,n,i,t){var o={south:r(t[0][1]),north:r(t[1][1]),east:r(t[1][0]),west:r(t[0][0]),width:n,height:i};U(),function(t,e){var n=!0;t.length<2&&(n=!1),n||console.log("Windy Error: data must have at least two components (u,v)");var i=(u=d(t)).header;if(i.hasOwnProperty("gridDefinitionTemplate")&&0!=i.gridDefinitionTemplate&&(n=!1),n||console.log("Windy Error: Only data with Latitude_Longitude coordinates is supported"),n=!0,y=i.lo1,_=i.la1,f=i.dx,v=i.dy,g=i.nx,w=i.ny,i.hasOwnProperty("scanMode")){var o=i.scanMode.toString(2),a=(o=("0"+o).slice(-8)).split("").map(Number).map(Boolean);a[0]&&(f=-f),a[1]&&(v=-v),a[2]&&(n=!1),a[3]&&(n=!1),a[4]&&(n=!1),a[5]&&(n=!1),a[6]&&(n=!1),a[7]&&(n=!1),n||console.log("Windy Error: Data with scanMode: "+i.scanMode+" is not supported.")}(p=new Date(i.refTime)).setHours(p.getHours()+i.forecastTime),m=[];for(var r=0,s=360<=Math.floor(g*f),l=0;l<w;l++){for(var h=[],c=0;c<g;c++,r++)h[c]=u.data(r);s&&h.push(h[0]),m[l]=h}e({date:p,interpolate:N})}(c,function(t){!function(w,M,t,n){var x={},e=(t.south-t.north)*(t.west-t.east),C=l*Math.pow(e,.4),P=[],i=M.x;function o(t){for(var e,n,i,o,a,r,s,l,h,c,d,p,u,m=[],y=M.y;y<=M.yMax;y+=2){var _=(d=t,p=y,void 0,[(u=S.map.containerPointToLatLng(L.point(d,p))).lng,u.lat]),f=_[0],v=_[1];if(isFinite(f)){var g=w.interpolate(f,v);g&&(e=x,n=f,i=v,o=t,a=y,r=C,void 0,l=(s=g)[0]*r,h=s[1]*r,c=E(e,n,i,o,a),s[0]=c[0]*l+c[2]*h,s[1]=c[1]*l+c[3]*h,g=s,m[y+1]=m[y]=g)}}P[t+1]=P[t]=m}!function t(){for(var e=Date.now();i<M.width;)if(o(i),i+=2,1e3<Date.now()-e)return void setTimeout(t,25);a(P,M,n)}()}(t,function(t,e,n){var i=t[0],o=t[1],a=Math.round(i[0]),r=Math.max(Math.floor(i[1],0),0);Math.min(Math.ceil(o[0],e),e-1);return{x:a,y:r,xMax:e,yMax:Math.min(Math.ceil(o[1],n),n-1),width:e,height:n}}(e,n,i),o,function(t,e){F.field=e,s(t,e)})})},stop:U,createField:a,interpolatePoint:N,setData:function(t){c=t},setOptions:function(t){t.hasOwnProperty("minVelocity")&&(x=t.minVelocity),t.hasOwnProperty("maxVelocity")&&(C=t.maxVelocity),t.hasOwnProperty("velocityScale")&&(l=(t.velocityScale||.005)*(Math.pow(window.devicePixelRatio,1/3)||1)),t.hasOwnProperty("particleAge")&&(P=t.particleAge),t.hasOwnProperty("lineWidth")&&(b=t.lineWidth),t.hasOwnProperty("particleMultiplier")&&(D=t.particleMultiplier),t.hasOwnProperty("opacity")&&(O=+t.opacity),t.hasOwnProperty("frameRate")&&(e=t.frameRate),W=1e3/e}};return F};window.cancelAnimationFrame||(window.cancelAnimationFrame=function(t){clearTimeout(t)});
@@ -110,7 +110,12 @@ var handleData = function(data) {
110
110
  }
111
111
  }
112
112
  if (data.command) { doCommand(data.command); delete data.command; }
113
- if (data.hasOwnProperty("type") && data.type.indexOf("Feature") === 0) { doGeojson("geojson",data); }
113
+ if (data.hasOwnProperty("type") && data.type.indexOf("Feature") === 0) {
114
+ if (data.hasOwnProperty('properties') && data.properties.hasOwnProperty('title')) {
115
+ doGeojson(data.properties.title,data)
116
+ }
117
+ else { doGeojson("geojson",data); }
118
+ }
114
119
  else if (data.hasOwnProperty("name")) { setMarker(data); }
115
120
  else {
116
121
  if (JSON.stringify(data) !== '{}') {
@@ -593,7 +598,7 @@ map.on('baselayerchange', function(e) {
593
598
  });
594
599
 
595
600
  function showMapCurrentZoom() {
596
- //console.log("zoom:",map.getZoom());
601
+ //console.log("ZOOM:",map.getZoom());
597
602
  for (var l in layers) {
598
603
  if (layers[l].hasOwnProperty("_zoom")) {
599
604
  if (map.getZoom() >= clusterAt) {
@@ -635,12 +640,12 @@ map.on('zoomend', function() {
635
640
  showMapCurrentZoom();
636
641
  window.localStorage.setItem("lastzoom", map.getZoom());
637
642
  var b = map.getBounds();
638
- ws.send(JSON.stringify({action:"bounds", south:b._southWest.lat, west:b._southWest.lng, north:b._northEast.lat, east:b._northEast.lng }));
643
+ ws.send(JSON.stringify({action:"bounds", south:b._southWest.lat, west:b._southWest.lng, north:b._northEast.lat, east:b._northEast.lng, zoom:map.getZoom() }));
639
644
  });
640
645
  map.on('moveend', function() {
641
646
  window.localStorage.setItem("lastpos",JSON.stringify(map.getCenter()));
642
647
  var b = map.getBounds();
643
- ws.send(JSON.stringify({action:"bounds", south:b._southWest.lat, west:b._southWest.lng, north:b._northEast.lat, east:b._northEast.lng }));
648
+ ws.send(JSON.stringify({action:"bounds", south:b._southWest.lat, west:b._southWest.lng, north:b._northEast.lat, east:b._northEast.lng, zoom:map.getZoom() }));
644
649
  });
645
650
 
646
651
  //map.on('contextmenu', function(e) {
@@ -735,7 +740,7 @@ var addBaseMaps = function(maplist,first) {
735
740
  //console.log("MAPS",first,maplist)
736
741
  if (navigator.onLine) {
737
742
  var layerlookup = { OSMG:"OSM grey", OSMC:"OSM", OSMH:"OSM Humanitarian", EsriC:"Esri", EsriS:"Esri Satellite",
738
- EsriT:"Esri Topography", EsriO:"Esri Ocean", EsriDG:"Esri Dark Grey", NatGeo: "National Geographic",
743
+ EsriR:"Esri Relief", EsriT:"Esri Topography", EsriO:"Esri Ocean", EsriDG:"Esri Dark Grey", NatGeo: "National Geographic",
739
744
  UKOS:"UK OS OpenData", UKOS45:"UK OS 1919-1947", UKOS00:"UK OS 1900", OpTop:"Open Topo Map",
740
745
  HB:"Hike Bike OSM", ST:"Stamen Topography", SW: "Stamen Watercolor", AN:"AutoNavi (Chinese)" }
741
746
 
@@ -794,21 +799,24 @@ var addBaseMaps = function(maplist,first) {
794
799
  if (maplist.indexOf("EsriR")!==-1) {
795
800
  basemaps[layerlookup["EsriR"]] = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}', {
796
801
  attribution:'Tiles &copy; Esri',
797
- maxNativeZoom:13
802
+ maxNativeZoom:13,
803
+ maxZoom:16
798
804
  });
799
805
  }
800
806
 
801
807
  if (maplist.indexOf("EsriO")!==-1) {
802
808
  basemaps[layerlookup["EsriO"]] = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer/tile/{z}/{y}/{x}', {
803
809
  attribution:'Tiles &copy; Esri &mdash; Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri',
804
- maxNativeZoom:13
810
+ maxNativeZoom:10,
811
+ maxZoom:13
805
812
  });
806
813
  }
807
814
 
808
815
  if (maplist.indexOf("EsriDG")!==-1) {
809
816
  basemaps[layerlookup["EsriDG"]] = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Base/MapServer/tile/{z}/{y}/{x}', {
810
817
  attribution: 'Tiles &copy; Esri &mdash; Esri, DeLorme, NAVTEQ',
811
- maxNativeZoom:13
818
+ maxNativeZoom:16,
819
+ maxZoom:18
812
820
  });
813
821
  }
814
822
 
@@ -1144,7 +1152,7 @@ var addOverlays = function(overlist) {
1144
1152
 
1145
1153
  // Add the OpenSea markers layer
1146
1154
  if (overlist.indexOf("SN")!==-1) {
1147
- overlays["ship nav"] = L.tileLayer('https://tiles.openseamap.org/seamark/{z}/{x}/{y}.png', {
1155
+ overlays["ship navigation"] = L.tileLayer('https://tiles.openseamap.org/seamark/{z}/{x}/{y}.png', {
1148
1156
  maxZoom: 19,
1149
1157
  attribution: 'Map data: &copy; <a href="https://www.openseamap.org">OpenSeaMap</a> contributors'
1150
1158
  });
@@ -1980,7 +1988,7 @@ function setMarker(data) {
1980
1988
  else if (data.heading !== undefined) { track = data.heading; }
1981
1989
  else if (data.bearing !== undefined) { track = data.bearing; }
1982
1990
  if (track != undefined) { // if there is a heading
1983
- if (data.speed != null && !data.length) { // and a speed - lets convert to a leader length
1991
+ if (data.speed != null && data.length === undefined) { // and a speed - lets convert to a leader length
1984
1992
  data.length = parseFloat(data.speed || "0") * 60;
1985
1993
  var re1 = new RegExp('kn|knot|kt','i');
1986
1994
  var re2 = new RegExp('kph|kmh','i');
@@ -1989,7 +1997,7 @@ function setMarker(data) {
1989
1997
  else if ( re2.test(""+data.speed) ) { data.length = data.length * 0.44704; }
1990
1998
  else if ( re3.test(""+data.speed) ) { data.length = data.length * 0.277778; }
1991
1999
  }
1992
- if (data.length != null) {
2000
+ if (data.length !== undefined) {
1993
2001
  if (polygons[data.name] != null && !polygons[data.name].hasOwnProperty("_layers")) {
1994
2002
  map.removeLayer(polygons[data.name]);
1995
2003
  }
@@ -2449,6 +2457,11 @@ function doCommand(cmd) {
2449
2457
  else if (cmd.map.url.slice(-4).toLowerCase() === ".pbf") {
2450
2458
  overlays[cmd.map.overlay] = VectorTileLayer(cmd.map.url, cmd.map.opt);
2451
2459
  }
2460
+ else if (cmd.map.hasOwnProperty("transparentPixels")) {
2461
+ cmd.map.opt.pixelCodes = cmd.map.transparentPixels;
2462
+ cmd.map.opt.matchRGBA = [ 0,0,0,0 ];
2463
+ overlays[cmd.map.overlay] = L.tileLayerPixelFilter(cmd.map.url, cmd.map.opt);
2464
+ }
2452
2465
  else {
2453
2466
  overlays[cmd.map.overlay] = L.tileLayer(cmd.map.url, cmd.map.opt);
2454
2467
  }
@@ -2500,6 +2513,8 @@ function doCommand(cmd) {
2500
2513
  // Turn on an existing overlay(s)
2501
2514
  if (cmd.hasOwnProperty("showlayer")) {
2502
2515
  if (typeof cmd.showlayer === "string") { cmd.showlayer = [ cmd.showlayer ]; }
2516
+ var sn = cmd.showlayer.indexOf("ship nav");
2517
+ if (sn !== -1) { cmd.showlayer[sn] = "ship navigation"; }
2503
2518
  for (var i=0; i < cmd.showlayer.length; i++) {
2504
2519
  if (overlays.hasOwnProperty(cmd.showlayer[i])) {
2505
2520
  map.addLayer(overlays[cmd.showlayer[i]]);
@@ -2509,6 +2524,8 @@ function doCommand(cmd) {
2509
2524
  // Turn off an existing overlay(s)
2510
2525
  if (cmd.hasOwnProperty("hidelayer")) {
2511
2526
  if (typeof cmd.hidelayer === "string") { cmd.hidelayer = [ cmd.hidelayer ]; }
2527
+ var sn = cmd.hidelayer.indexOf("ship nav");
2528
+ if (sn !== -1) { cmd.hidelayer[sn] = "ship navigation"; }
2512
2529
  for (var i=0; i < cmd.hidelayer.length; i++) {
2513
2530
  if (overlays.hasOwnProperty(cmd.hidelayer[i])) {
2514
2531
  map.removeLayer(overlays[cmd.hidelayer[i]]);
@@ -2563,6 +2580,7 @@ function doCommand(cmd) {
2563
2580
 
2564
2581
  // handle any incoming GEOJSON directly - may style badly
2565
2582
  function doGeojson(n,g,l,o) {
2583
+ //console.log("GEOJSON",n,g,l,o)
2566
2584
  var lay = l || g.name || "unknown";
2567
2585
  // if (!basemaps[lay]) {
2568
2586
  var opt = { style: function(feature) {
@@ -2594,12 +2612,29 @@ function doGeojson(n,g,l,o) {
2594
2612
  return st;
2595
2613
  }}
2596
2614
  opt.pointToLayer = function (feature, latlng) {
2597
- var myMarker = L.VectorMarkers.icon({
2598
- icon: feature.properties["marker-symbol"] || "circle",
2599
- markerColor: (feature.properties["marker-color"] || "#910000"),
2600
- prefix: 'fa',
2601
- iconColor: 'white'
2602
- });
2615
+ var myMarker;
2616
+ if (feature.properties.hasOwnProperty("SIDC")) {
2617
+ myMarker = new ms.Symbol( feature.properties.SIDC.toUpperCase(), {
2618
+ uniqueDesignation:unescape(encodeURIComponent(feature.properties.title)) ,
2619
+ country:feature.properties.country,
2620
+ direction:feature.properties.bearing,
2621
+ additionalInformation:feature.properties.modifier,
2622
+ size:24
2623
+ });
2624
+ myMarker = L.icon({
2625
+ iconUrl: myMarker.toDataURL(),
2626
+ iconAnchor: [myMarker.getAnchor().x, myMarker.getAnchor().y],
2627
+ className: "natoicon",
2628
+ });
2629
+ }
2630
+ else {
2631
+ myMarker = L.VectorMarkers.icon({
2632
+ icon: feature.properties["marker-symbol"] || "circle",
2633
+ markerColor: (feature.properties["marker-color"] || "#910000"),
2634
+ prefix: 'fa',
2635
+ iconColor: 'white'
2636
+ });
2637
+ }
2603
2638
  if (!feature.properties.hasOwnProperty("title")) {
2604
2639
  feature.properties.title = feature.properties["marker-symbol"];
2605
2640
  }
@@ -42,6 +42,7 @@
42
42
  <script src="leaflet/Leaflet.Coordinates.js"></script>
43
43
  <script src="leaflet/leaflet.latlng-graticule.js"></script>
44
44
  <script src="leaflet/Semicircle.js"></script>
45
+ <script src="leaflet/L.TileLayer.PixelFilter.js"></script>
45
46
  <script src="leaflet/dialog-polyfill.js"></script>
46
47
 
47
48
  <script src="images/emoji.js"></script>
package/worldmap.html CHANGED
@@ -293,7 +293,7 @@ If <i>Web Path</i> is left empty, then by default <code>⌘⇧m</code> - <code>c
293
293
  <p>Icons of type <i>plane</i>, <i>ship</i>, <i>car</i>, <i>uav</i> or <i>arrow</i> will use built in SVG icons that align to the
294
294
  <code>bearing</code> value.</p>
295
295
  <p>Font Awesome (<a href="https://fontawesome.com/v4.7.0/icons/" target="_new">fa-icons 4.7</a>) can also be used, as can
296
- NATO symbology codes (SIDC), or <a href="https://github.com/dceejay/RedMap/blob/master/emojilist.md" target="_new">:emoji name:</a>,
296
+ NATO symbology codes (<a href="https://spatialillusions.com/unitgenerator/">SIDC</a>), or <a href="https://github.com/dceejay/RedMap/blob/master/emojilist.md" target="_new">:emoji name:</a>,
297
297
  or the url of a small icon image (32x32)</p>
298
298
  <p>See the <a href="https://www.npmjs.com/package/node-red-contrib-web-worldmap" target="_new">README</a> for further
299
299
  details and examples of icons and commands for drawing <b>lines</b> and <b>areas</b>, and to <b>add layers</b> and