UNPKG

node-red-contrib-web-worldmap

Version:

A Node-RED node to provide a web page of a world map for plotting things on.

266 lines (248 loc) 10.6 kB
<!DOCTYPE html> <html> <head> <title>Node-RED 3D Map all the Things</title> <meta charset='utf-8' /> <meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no'/> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <script src="leaflet/sockjs.min.js"></script> <script src='https://api.tiles.mapbox.com/mapbox-gl-js/v2.6.1/mapbox-gl.js'></script> <link href='https://api.tiles.mapbox.com/mapbox-gl-js/v2.6.1/mapbox-gl.css' rel='stylesheet'/> <style> body { margin:0; padding:0; } #map { position:absolute; top:0; bottom:0; width:100%; } </style> </head> <body> <div id='map'></div> <script> // TO MAKE THE MAP APPEAR YOU MUST ADD YOUR ACCESS TOKEN FROM https://account.mapbox.com // This can also be set as an environment variable MAPBOXGL_TOKEN available to the Node-RED session on your server mapboxgl.accessToken = ''; // You can also replace this with a custom style if you like. var mbstyle = 'mapbox://styles/mapbox/streets-v10'; // var mbstyle = 'mapbox://styles/mapbox/light-v10'; var do3dMap = function() { var people = {}; var map = new mapboxgl.Map({ container: 'map', style: mbstyle, center: [-1.3971, 51.0259], zoom: 16, pitch: 40, bearing: 20, attributionControl: true }); map.on('load', function() { var layers = map.getStyle().layers; var firstSymbolId; for (var i = 0; i < layers.length; i++) { if (layers[i].type === 'symbol') { firstSymbolId = layers[i].id; break; } } /// Add the base 3D buildings layer map.addLayer({ 'id': '3d-buildings', 'source': 'composite', 'source-layer': 'building', 'filter': ['==', 'extrude', 'true'], 'type': 'fill-extrusion', 'minzoom': 15, 'paint': { 'fill-extrusion-color': '#ddd', 'fill-extrusion-height': [ "interpolate", ["linear"], ["zoom"], 15, 0, 15.05, ["get", "height"] ], 'fill-extrusion-base': [ "interpolate", ["linear"], ["zoom"], 15, 0, 15.05, ["get", "min_height"] ], 'fill-extrusion-opacity': .4 } }, firstSymbolId); // ---- Connect to the Node-RED Events Websocket -------------------- var connect = function() { ws = new SockJS(location.pathname.split("index")[0] + 'socket'); ws.onopen = function() { console.log("CONNECTED"); // if (!inIframe) { // document.getElementById("foot").innerHTML = "<font color='#494'>"+pagefoot+"</font>"; // } ws.send(JSON.stringify({action:"connected"})); }; ws.onclose = function() { console.log("DISCONNECTED"); // if (!inIframe) { // document.getElementById("foot").innerHTML = "<font color='#900'>"+pagefoot+"</font>"; // } setTimeout(function() { connect(); }, 2500); }; ws.onmessage = function(e) { var data = JSON.parse(e.data); //console.log("GOT",data); if (Array.isArray(data)) { //console.log("ARRAY"); for (var prop in data) { if (data[prop].command) { doCommand(data[prop].command); delete data[prop].command; } if (data[prop].hasOwnProperty("name")) { setMarker(data[prop]); } else { console.log("SKIP A",data[prop]); } } } else { if (data.command) { doCommand(data.command); delete data.command; } if (data.hasOwnProperty("name")) { setMarker(data); } else { console.log("SKIP",data); } } }; } console.log("CONNECT TO",location.pathname + 'socket'); connect(); var doCommand = function(c) { console.log("CMD",c); // Add our own overlay geojson layer if necessary if (c.hasOwnProperty("map") && c.map.hasOwnProperty("geojson") && c.map.hasOwnProperty("overlay")) { addGeo(c.map.overlay,c.map.geojson); } var clat,clon; if (c.hasOwnProperty("lat")) { clat = c.lat; } if (c.hasOwnProperty("lon")) { clon = c.lon; } if (clat && clon) { map.setCenter([clon,clat]); } if (c.hasOwnProperty("zoom")) { map.setZoom(c.zoom); } if (c.hasOwnProperty("pitch")) { map.setPitch(c.pitch); } if (c.hasOwnProperty("bearing")) { map.setBearing(c.bearing); } } var addGeo = function(o,g) { map.addLayer({ 'id': o, 'type': 'fill-extrusion', 'source': { 'type': 'geojson', 'data': g }, 'paint': { // https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions 'fill-extrusion-color': ['get', 'color'], 'fill-extrusion-height': ['get', 'height'], 'fill-extrusion-base': ['get', 'base_height'], 'fill-extrusion-opacity': 0.5 } }); } var setMarker = function(d) { if (d.hasOwnProperty("area")) { return; } // ignore areas for now. console.log("DATA",d); if (people.hasOwnProperty(d.name)) { map.getSource(d.name).setData(getPoints(d)); // Just update existing marker } else { // it's a new thing people[d.name] = d; map.addLayer({ 'id': d.name, 'type': 'fill-extrusion', 'source': { 'type': 'geojson', 'data': getPoints(d) }, 'paint': { // https://www.mapbox.com/mapbox-gl-js/style-spec/#expressions 'fill-extrusion-color': ['get', 'color'], 'fill-extrusion-height': ['get', 'height'], 'fill-extrusion-base': ['get', 'base_height'], 'fill-extrusion-opacity': 1 } },firstSymbolId); } } var lookupColor = function(s) { var c = s.charAt(1); if (c === "F") { return "#79DFFF"; } if (c === "N") { return "#A4FFA3"; } if (c === "U") { return "#FFFF78"; } if (c === "H") { return "#FF7779"; } if (c === "S") { return "#FF7779"; } } // create the points for the marker and return the geojson var getPoints = function(p) { var fac = 0.000007; // basic size for block icon in degrees.... var thing = ""; if (p.hasOwnProperty("icon")) { if (p.icon.indexOf("male") !== -1) { thing = "person"; } } p.SDIC = p.SIDC || p.sidc; if (p.hasOwnProperty("SIDC")) { if (p.SIDC.indexOf("SFGPU") !== -1) { thing = "person"; } if (p.SIDC.indexOf("SFGPUC") !== -1) { thing = "block"; } if (p.SIDC.indexOf("GPEV") !== -1) { thing = "block"; } p.iconColor = lookupColor(p.SIDC); } var t = p.type || thing; var base = p.height || 0; if (t === "person") { tall = 3; } // person slightly tall and thin else if (t === "bar") { base = 0; tall = p.height; } // bar from ground to height else if (t === "block") { fac = fac * 4; tall = 5; } // block large and cube else { tall = 2; fac = fac * 2; } // else small cube //console.log({p},{t},{fac},{base},{tall}); var sin = 1; var cos = 0; p.hdg = Number(p.hdg || p.heading); if (p.hasOwnProperty("hdg") && !isNaN(p.hdg)) { sin = Math.sin((90 - p.hdg) * Math.PI / 180); cos = Math.cos((90 - p.hdg) * Math.PI / 180); } var dx = 1 * cos - 1 * sin; var dy = 1 * sin + 1 * cos; var d = { "type": "Feature", "properties": { "name": p.name, "type": t, "color": p.iconColor || "#910000", "height": base + tall, "base_height": base }, "geometry": { "type": "Polygon", "coordinates": [ [ [ p.lon + (fac * dx ) / Math.cos( Math.PI / 180 * p.lat ), p.lat + (fac * dy) ], [ p.lon - (fac * dy ) / Math.cos( Math.PI / 180 * p.lat ), p.lat + (fac * dx) ], [ p.lon - (fac * dx ) / Math.cos( Math.PI / 180 * p.lat ), p.lat - (fac * dy) ], [ p.lon + (fac * dy ) / Math.cos( Math.PI / 180 * p.lat ), p.lat - (fac * dx) ], [ p.lon + (fac * dx ) / Math.cos( Math.PI / 180 * p.lat ), p.lat + (fac * dy) ], ] ] } } return d; } document.addEventListener ("keydown", function (ev) { if (ev.ctrlKey && ev.altKey && ev.code === "Digit3") { ws.close(); //window.onbeforeunload = null; window.location.href = "index.html"; } }); }); } if (mapboxgl.accessToken !== '') { do3dMap(); } else { fetch('/-worldmap3d-key', { credentials:'same-origin' }) .then(response => response.json()) .then(data => { mapboxgl.accessToken = data.key; if (mapboxgl.accessToken === "") { 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."); return; } do3dMap(); }) .catch(error => { console.log("Unable to fetch MAPBOXGL_TOKEN.",error)} ); } </script> </body> </html>