node-red-contrib-web-worldmap 2.25.0 → 2.26.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,6 @@
1
1
  ### Change Log for Node-RED Worldmap
2
2
 
3
+ - v2.26.0 - Add UTM and MGRS to coordinate display options.
3
4
  - v2.25.0 - Add bounds command to set overall map bounds.
4
5
  - v2.24.3 - Fix geojson incorrect fill.
5
6
  - v2.24.2 - Changes to drawing colours to be more visible.
package/README.md CHANGED
@@ -11,6 +11,7 @@ map web page for plotting "things" on.
11
11
 
12
12
  ### Updates
13
13
 
14
+ - v2.26.0 - Add UTM and MGRS to coordinate display options.
14
15
  - v2.25.0 - Add bounds command to set overall map bounds.
15
16
  - v2.24.3 - Fix geojson incorrect fill.
16
17
  - v2.24.2 - Changes to drawing colours to be more visible.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "node-red-contrib-web-worldmap",
3
- "version": "2.25.0",
3
+ "version": "2.26.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",
@@ -38,7 +38,7 @@
38
38
  <link rel="stylesheet" type="text/css" href="leaflet/Leaflet.Dialog.css">
39
39
  <link rel="stylesheet" type="text/css" href="leaflet/Control.MiniMap.min.css">
40
40
  <link rel="stylesheet" type="text/css" href="leaflet/leaflet-velocity.min.css">
41
- <link rel="stylesheet" type="text/css" href="leaflet/Leaflet.Coordinates.css">
41
+ <link rel="stylesheet" type="text/css" href="leaflet/leaflet.mousecoordinate.css">
42
42
  <link rel="stylesheet" type="text/css" href="leaflet/dialog-polyfill.css"/>
43
43
  <link rel="stylesheet" type="text/css" href="css/worldmap.css"/>
44
44
 
@@ -68,7 +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/Leaflet.Coordinates.js"></script>
71
+ <script src="leaflet/leaflet.mousecoordinate.js"></script>
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>
@@ -18,237 +18,239 @@
18
18
  <script>
19
19
 
20
20
  // TO MAKE THE MAP APPEAR YOU MUST ADD YOUR ACCESS TOKEN FROM https://account.mapbox.com
21
+ // This needs to be set as an environment variable MAPBOXGL_TOKEN available to the Node-RED session on your server
21
22
  mapboxgl.accessToken = '';
22
- //mapboxgl.accessToken = 'pk.eyJ1IjoiZGNlZWpheSIsImEiOiJjaml1aDR6ejQwMml0M3dtdGFlbmEyZHVpIn0.3aD-974hxir335vuLjHOSg';
23
- mapboxgl.accessToken = 'pk.eyJ1Ijoib3NtYnVpbGRpbmdzIiwiYSI6IjNldU0tNDAifQ.c5EU_3V8b87xO24tuWil0w';
24
- // if (process.env.MAPBOXGL_TOKEN) {
25
- // mapboxgl.accessToken = process.env.MAPBOXGL_TOKEN
26
- // }
27
-
28
- if (mapboxgl.accessToken === "") {
29
- alert("To make the map apperar you must add your Access Token from https://account.mapbox.com")
30
- }
31
23
 
24
+ var people = {};
32
25
  var mbstyle = 'mapbox://styles/mapbox/streets-v10';
33
26
  // var mbstyle = 'mapbox://styles/mapbox/light-v10';
34
- // var mbstyle = 'https://data.osmbuildings.org/0.2/anonymous/style.json';
35
-
36
- var map = new mapboxgl.Map({
37
- container: 'map',
38
- style: mbstyle,
39
- center: [-1.3971, 51.0259],
40
- zoom: 16,
41
- pitch: 40,
42
- bearing: 20,
43
- attributionControl: true
44
- });
45
27
 
46
- var people = {};
28
+ fetch('/-worldmap3d-key')
29
+ .then(response => response.json())
30
+ .then(data => {
31
+ mapboxgl.accessToken = data.key;
32
+ if (mapboxgl.accessToken === "") {
33
+ alert("To make the map appear you must add your Access Token from https://account.mapbox.com by setting the MAPBOXGL_TOKEN environment variable on your server.");
34
+ return;
35
+ }
47
36
 
48
- map.on('load', function() {
37
+ var map = new mapboxgl.Map({
38
+ container: 'map',
39
+ style: mbstyle,
40
+ center: [-1.3971, 51.0259],
41
+ zoom: 16,
42
+ pitch: 40,
43
+ bearing: 20,
44
+ attributionControl: true
45
+ });
49
46
 
50
- var layers = map.getStyle().layers;
51
- var firstSymbolId;
52
- for (var i = 0; i < layers.length; i++) {
53
- if (layers[i].type === 'symbol') {
54
- firstSymbolId = layers[i].id;
55
- break;
56
- }
57
- }
58
-
59
- /// Add the base 3D buildings layer
60
- map.addLayer({
61
- 'id': '3d-buildings',
62
- 'source': 'composite',
63
- 'source-layer': 'building',
64
- 'filter': ['==', 'extrude', 'true'],
65
- 'type': 'fill-extrusion',
66
- 'minzoom': 15,
67
- 'paint': {
68
- 'fill-extrusion-color': '#ddd',
69
- 'fill-extrusion-height': [
70
- "interpolate", ["linear"], ["zoom"],
71
- 15, 0, 15.05, ["get", "height"]
72
- ],
73
- 'fill-extrusion-base': [
74
- "interpolate", ["linear"], ["zoom"],
75
- 15, 0, 15.05, ["get", "min_height"]
76
- ],
77
- 'fill-extrusion-opacity': .4
78
- }
79
- }, firstSymbolId);
80
-
81
- // ---- Connect to the Node-RED Events Websocket --------------------
82
-
83
- var connect = function() {
84
- ws = new SockJS(location.pathname.split("index")[0] + 'socket');
85
- ws.onopen = function() {
86
- console.log("CONNECTED");
87
- // if (!inIframe) {
88
- // document.getElementById("foot").innerHTML = "<font color='#494'>"+ibmfoot+"</font>";
89
- // }
90
- ws.send(JSON.stringify({action:"connected"}));
91
- };
92
- ws.onclose = function() {
93
- console.log("DISCONNECTED");
94
- // if (!inIframe) {
95
- // document.getElementById("foot").innerHTML = "<font color='#900'>"+ibmfoot+"</font>";
96
- // }
97
- setTimeout(function() { connect(); }, 2500);
98
- };
99
- ws.onmessage = function(e) {
100
- var data = JSON.parse(e.data);
101
- //console.log("GOT",data);
102
- if (Array.isArray(data)) {
103
- //console.log("ARRAY");
104
- for (var prop in data) {
105
- if (data[prop].command) { doCommand(data[prop].command); delete data[prop].command; }
106
- if (data[prop].hasOwnProperty("name")) { setMarker(data[prop]); }
107
- else { console.log("SKIP A",data[prop]); }
47
+ map.on('load', function() {
48
+ var layers = map.getStyle().layers;
49
+ var firstSymbolId;
50
+ for (var i = 0; i < layers.length; i++) {
51
+ if (layers[i].type === 'symbol') {
52
+ firstSymbolId = layers[i].id;
53
+ break;
108
54
  }
109
55
  }
110
- else {
111
- if (data.command) { doCommand(data.command); delete data.command; }
112
- if (data.hasOwnProperty("name")) { setMarker(data); }
113
- else { console.log("SKIP",data); }
114
- }
115
- };
116
- }
117
- console.log("CONNECT TO",location.pathname + 'socket');
118
- connect();
119
-
120
- var doCommand = function(c) {
121
- console.log("CMD",c);
122
- // Add our own overlay geojson layer if necessary
123
- if (c.hasOwnProperty("map") && c.map.hasOwnProperty("geojson") && c.map.hasOwnProperty("overlay")) {
124
- addGeo(c.map.overlay,c.map.geojson);
125
- }
126
- var clat,clon;
127
- if (c.hasOwnProperty("lat")) { clat = c.lat; }
128
- if (c.hasOwnProperty("lon")) { clon = c.lon; }
129
- if (clat && clon) { map.setCenter([clon,clat]); }
130
- if (c.hasOwnProperty("zoom")) { map.setZoom(c.zoom); }
131
- if (c.hasOwnProperty("pitch")) { map.setPitch(c.pitch); }
132
- if (c.hasOwnProperty("bearing")) { map.setBearing(c.bearing); }
133
- }
134
-
135
- var addGeo = function(o,g) {
136
- map.addLayer({
137
- 'id': o,
138
- 'type': 'fill-extrusion',
139
- 'source': {
140
- 'type': 'geojson',
141
- 'data': g
142
- },
143
- 'paint': {
144
- // https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions
145
- 'fill-extrusion-color': ['get', 'color'],
146
- 'fill-extrusion-height': ['get', 'height'],
147
- 'fill-extrusion-base': ['get', 'base_height'],
148
- 'fill-extrusion-opacity': 0.5
149
- }
150
- });
151
- }
152
56
 
153
- var setMarker = function(d) {
154
- if (d.hasOwnProperty("area")) { return; } // ignore areas for now.
155
- console.log("DATA",d);
156
- if (people.hasOwnProperty(d.name)) {
157
- map.getSource(d.name).setData(getPoints(d)); // Just update existing marker
158
- }
159
- else { // it's a new thing
160
- people[d.name] = d;
57
+ /// Add the base 3D buildings layer
161
58
  map.addLayer({
162
- 'id': d.name,
59
+ 'id': '3d-buildings',
60
+ 'source': 'composite',
61
+ 'source-layer': 'building',
62
+ 'filter': ['==', 'extrude', 'true'],
163
63
  'type': 'fill-extrusion',
164
- 'source': {
165
- 'type': 'geojson',
166
- 'data': getPoints(d)
167
- },
64
+ 'minzoom': 15,
168
65
  'paint': {
169
- // https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions
170
- 'fill-extrusion-color': ['get', 'color'],
171
- 'fill-extrusion-height': ['get', 'height'],
172
- 'fill-extrusion-base': ['get', 'base_height'],
173
- 'fill-extrusion-opacity': 1
66
+ 'fill-extrusion-color': '#ddd',
67
+ 'fill-extrusion-height': [
68
+ "interpolate", ["linear"], ["zoom"],
69
+ 15, 0, 15.05, ["get", "height"]
70
+ ],
71
+ 'fill-extrusion-base': [
72
+ "interpolate", ["linear"], ["zoom"],
73
+ 15, 0, 15.05, ["get", "min_height"]
74
+ ],
75
+ 'fill-extrusion-opacity': .4
174
76
  }
175
- },firstSymbolId);
176
- }
177
- }
178
-
179
- var lookupColor = function(s) {
180
- var c = s.charAt(1);
181
- if (c === "F") { return "#79DFFF"; }
182
- if (c === "N") { return "#A4FFA3"; }
183
- if (c === "U") { return "#FFFF78"; }
184
- if (c === "H") { return "#FF7779"; }
185
- if (c === "S") { return "#FF7779"; }
186
- }
187
-
188
- // create the points for the marker and return the geojson
189
- var getPoints = function(p) {
190
- var fac = 0.000007; // basic size for bock icon in degrees....
191
- var thing = "";
192
- if (p.hasOwnProperty("icon")) {
193
- if (p.icon.indexOf("male") !== -1) { thing = "person"; }
194
- }
195
- p.SDIC = p.SIDC || p.sidc;
196
- if (p.hasOwnProperty("SIDC")) {
197
- if (p.SIDC.indexOf("SFGPU") !== -1) { thing = "person"; }
198
- if (p.SIDC.indexOf("SFGPUC") !== -1) { thing = "block"; }
199
- if (p.SIDC.indexOf("GPEV") !== -1) { thing = "block"; }
200
- p.iconColor = lookupColor(p.SIDC);
201
- }
202
- var t = p.type || thing;
203
- var base = p.height || 0;
204
- if (t === "person") { tall = 3; } // person slightly tall and thin
205
- else if (t === "bar") { base = 0; tall = p.height; } // bar from ground to height
206
- else if (t === "block") { fac = fac * 4; tall = 5; } // block large and cube
207
- else { tall = 2; fac = fac * 2; } // else small cube
208
- //console.log({p},{t},{fac},{base},{tall});
209
- var sin = 1;
210
- var cos = 0;
211
- p.hdg = Number(p.hdg || p.heading);
212
- if (p.hasOwnProperty("hdg") && !isNaN(p.hdg)) {
213
- sin = Math.sin((90 - p.hdg) * Math.PI / 180);
214
- cos = Math.cos((90 - p.hdg) * Math.PI / 180);
215
- }
216
- var dx = 1 * cos - 1 * sin;
217
- var dy = 1 * sin + 1 * cos;
218
- var d = {
219
- "type": "Feature",
220
- "properties": {
221
- "name": p.name,
222
- "type": t,
223
- "color": p.iconColor || "#910000",
224
- "height": base + tall,
225
- "base_height": base
226
- },
227
- "geometry": {
228
- "type": "Polygon",
229
- "coordinates": [
230
- [
231
- [ p.lon + (fac * dx ) / Math.cos( Math.PI / 180 * p.lat ), p.lat + (fac * dy) ],
232
- [ p.lon - (fac * dy ) / Math.cos( Math.PI / 180 * p.lat ), p.lat + (fac * dx) ],
233
- [ p.lon - (fac * dx ) / Math.cos( Math.PI / 180 * p.lat ), p.lat - (fac * dy) ],
234
- [ p.lon + (fac * dy ) / Math.cos( Math.PI / 180 * p.lat ), p.lat - (fac * dx) ],
235
- [ p.lon + (fac * dx ) / Math.cos( Math.PI / 180 * p.lat ), p.lat + (fac * dy) ],
236
- ]
237
- ]
77
+ }, firstSymbolId);
78
+
79
+ // ---- Connect to the Node-RED Events Websocket --------------------
80
+
81
+ var connect = function() {
82
+ ws = new SockJS(location.pathname.split("index")[0] + 'socket');
83
+ ws.onopen = function() {
84
+ console.log("CONNECTED");
85
+ // if (!inIframe) {
86
+ // document.getElementById("foot").innerHTML = "<font color='#494'>"+ibmfoot+"</font>";
87
+ // }
88
+ ws.send(JSON.stringify({action:"connected"}));
89
+ };
90
+ ws.onclose = function() {
91
+ console.log("DISCONNECTED");
92
+ // if (!inIframe) {
93
+ // document.getElementById("foot").innerHTML = "<font color='#900'>"+ibmfoot+"</font>";
94
+ // }
95
+ setTimeout(function() { connect(); }, 2500);
96
+ };
97
+ ws.onmessage = function(e) {
98
+ var data = JSON.parse(e.data);
99
+ //console.log("GOT",data);
100
+ if (Array.isArray(data)) {
101
+ //console.log("ARRAY");
102
+ for (var prop in data) {
103
+ if (data[prop].command) { doCommand(data[prop].command); delete data[prop].command; }
104
+ if (data[prop].hasOwnProperty("name")) { setMarker(data[prop]); }
105
+ else { console.log("SKIP A",data[prop]); }
106
+ }
107
+ }
108
+ else {
109
+ if (data.command) { doCommand(data.command); delete data.command; }
110
+ if (data.hasOwnProperty("name")) { setMarker(data); }
111
+ else { console.log("SKIP",data); }
112
+ }
113
+ };
238
114
  }
239
- }
240
- return d;
241
- }
242
-
243
- document.addEventListener ("keydown", function (ev) {
244
- if (ev.ctrlKey && ev.altKey && ev.code === "Digit3") {
245
- ws.close();
246
- //window.onbeforeunload = null;
247
- window.location.href = "index.html";
248
- }
249
- });
115
+ console.log("CONNECT TO",location.pathname + 'socket');
116
+ connect();
117
+
118
+ var doCommand = function(c) {
119
+ console.log("CMD",c);
120
+ // Add our own overlay geojson layer if necessary
121
+ if (c.hasOwnProperty("map") && c.map.hasOwnProperty("geojson") && c.map.hasOwnProperty("overlay")) {
122
+ addGeo(c.map.overlay,c.map.geojson);
123
+ }
124
+ var clat,clon;
125
+ if (c.hasOwnProperty("lat")) { clat = c.lat; }
126
+ if (c.hasOwnProperty("lon")) { clon = c.lon; }
127
+ if (clat && clon) { map.setCenter([clon,clat]); }
128
+ if (c.hasOwnProperty("zoom")) { map.setZoom(c.zoom); }
129
+ if (c.hasOwnProperty("pitch")) { map.setPitch(c.pitch); }
130
+ if (c.hasOwnProperty("bearing")) { map.setBearing(c.bearing); }
131
+ }
132
+
133
+ var addGeo = function(o,g) {
134
+ map.addLayer({
135
+ 'id': o,
136
+ 'type': 'fill-extrusion',
137
+ 'source': {
138
+ 'type': 'geojson',
139
+ 'data': g
140
+ },
141
+ 'paint': {
142
+ // https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions
143
+ 'fill-extrusion-color': ['get', 'color'],
144
+ 'fill-extrusion-height': ['get', 'height'],
145
+ 'fill-extrusion-base': ['get', 'base_height'],
146
+ 'fill-extrusion-opacity': 0.5
147
+ }
148
+ });
149
+ }
150
+
151
+ var setMarker = function(d) {
152
+ if (d.hasOwnProperty("area")) { return; } // ignore areas for now.
153
+ console.log("DATA",d);
154
+ if (people.hasOwnProperty(d.name)) {
155
+ map.getSource(d.name).setData(getPoints(d)); // Just update existing marker
156
+ }
157
+ else { // it's a new thing
158
+ people[d.name] = d;
159
+ map.addLayer({
160
+ 'id': d.name,
161
+ 'type': 'fill-extrusion',
162
+ 'source': {
163
+ 'type': 'geojson',
164
+ 'data': getPoints(d)
165
+ },
166
+ 'paint': {
167
+ // https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions
168
+ 'fill-extrusion-color': ['get', 'color'],
169
+ 'fill-extrusion-height': ['get', 'height'],
170
+ 'fill-extrusion-base': ['get', 'base_height'],
171
+ 'fill-extrusion-opacity': 1
172
+ }
173
+ },firstSymbolId);
174
+ }
175
+ }
176
+
177
+ var lookupColor = function(s) {
178
+ var c = s.charAt(1);
179
+ if (c === "F") { return "#79DFFF"; }
180
+ if (c === "N") { return "#A4FFA3"; }
181
+ if (c === "U") { return "#FFFF78"; }
182
+ if (c === "H") { return "#FF7779"; }
183
+ if (c === "S") { return "#FF7779"; }
184
+ }
185
+
186
+ // create the points for the marker and return the geojson
187
+ var getPoints = function(p) {
188
+ var fac = 0.000007; // basic size for bock icon in degrees....
189
+ var thing = "";
190
+ if (p.hasOwnProperty("icon")) {
191
+ if (p.icon.indexOf("male") !== -1) { thing = "person"; }
192
+ }
193
+ p.SDIC = p.SIDC || p.sidc;
194
+ if (p.hasOwnProperty("SIDC")) {
195
+ if (p.SIDC.indexOf("SFGPU") !== -1) { thing = "person"; }
196
+ if (p.SIDC.indexOf("SFGPUC") !== -1) { thing = "block"; }
197
+ if (p.SIDC.indexOf("GPEV") !== -1) { thing = "block"; }
198
+ p.iconColor = lookupColor(p.SIDC);
199
+ }
200
+ var t = p.type || thing;
201
+ var base = p.height || 0;
202
+ if (t === "person") { tall = 3; } // person slightly tall and thin
203
+ else if (t === "bar") { base = 0; tall = p.height; } // bar from ground to height
204
+ else if (t === "block") { fac = fac * 4; tall = 5; } // block large and cube
205
+ else { tall = 2; fac = fac * 2; } // else small cube
206
+ //console.log({p},{t},{fac},{base},{tall});
207
+ var sin = 1;
208
+ var cos = 0;
209
+ p.hdg = Number(p.hdg || p.heading);
210
+ if (p.hasOwnProperty("hdg") && !isNaN(p.hdg)) {
211
+ sin = Math.sin((90 - p.hdg) * Math.PI / 180);
212
+ cos = Math.cos((90 - p.hdg) * Math.PI / 180);
213
+ }
214
+ var dx = 1 * cos - 1 * sin;
215
+ var dy = 1 * sin + 1 * cos;
216
+ var d = {
217
+ "type": "Feature",
218
+ "properties": {
219
+ "name": p.name,
220
+ "type": t,
221
+ "color": p.iconColor || "#910000",
222
+ "height": base + tall,
223
+ "base_height": base
224
+ },
225
+ "geometry": {
226
+ "type": "Polygon",
227
+ "coordinates": [
228
+ [
229
+ [ p.lon + (fac * dx ) / Math.cos( Math.PI / 180 * p.lat ), p.lat + (fac * dy) ],
230
+ [ p.lon - (fac * dy ) / Math.cos( Math.PI / 180 * p.lat ), p.lat + (fac * dx) ],
231
+ [ p.lon - (fac * dx ) / Math.cos( Math.PI / 180 * p.lat ), p.lat - (fac * dy) ],
232
+ [ p.lon + (fac * dy ) / Math.cos( Math.PI / 180 * p.lat ), p.lat - (fac * dx) ],
233
+ [ p.lon + (fac * dx ) / Math.cos( Math.PI / 180 * p.lat ), p.lat + (fac * dy) ],
234
+ ]
235
+ ]
236
+ }
237
+ }
238
+ return d;
239
+ }
240
+
241
+ document.addEventListener ("keydown", function (ev) {
242
+ if (ev.ctrlKey && ev.altKey && ev.code === "Digit3") {
243
+ ws.close();
244
+ //window.onbeforeunload = null;
245
+ window.location.href = "index.html";
246
+ }
247
+ });
250
248
 
251
249
  });
250
+
251
+ })
252
+ .catch(error => { console.log("Unable to fetch MAPBOXGL_TOKEN.",error)} );
253
+
252
254
  </script>
253
255
  </body>
254
256
  </html>
@@ -0,0 +1,7 @@
1
+ .leaflet-control-mouseCoordinate{
2
+ text-align:left;
3
+ border-radius:4px;
4
+ color:#333;
5
+ background:rgba(255,255,255,0.7);
6
+ padding:4px 8px 4px 4px
7
+ }