Add 3d view to dashboard node, better key handling/errors
This commit is contained in:
parent
0f61825466
commit
70bc32d049
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "node-red-contrib-web-worldmap",
|
"name": "node-red-contrib-web-worldmap",
|
||||||
"version": "2.27.3",
|
"version": "2.28.0",
|
||||||
"description": "A Node-RED node to provide a web page of a world map for plotting things on.",
|
"description": "A Node-RED node to provide a web page of a world map for plotting things on.",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@turf/bezier-spline": "~6.5.0",
|
"@turf/bezier-spline": "~6.5.0",
|
||||||
|
@ -173,7 +173,7 @@ If <i>Web Path</i> is left empty, then by default <code>⌘⇧m</code> - <code>c
|
|||||||
<label for="node-input-maplist"><i class="fa fa-list"></i> Map list</label>
|
<label for="node-input-maplist"><i class="fa fa-list"></i> Map list</label>
|
||||||
<input type="text" id="node-input-maplist" placeholder="List of Maps">
|
<input type="text" id="node-input-maplist" placeholder="List of Maps">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row" id="node-input-baselayer">
|
||||||
<label for="node-input-layer"><i class="fa fa-map"></i> Base map</label>
|
<label for="node-input-layer"><i class="fa fa-map"></i> Base map</label>
|
||||||
<input type="text" id="node-input-layer" placeholder="Initial Map">
|
<input type="text" id="node-input-layer" placeholder="Initial Map">
|
||||||
</div>
|
</div>
|
||||||
@ -188,7 +188,7 @@ If <i>Web Path</i> is left empty, then by default <code>⌘⇧m</code> - <code>c
|
|||||||
<input type="checkbox" id="node-input-mapwms" style="display:inline-block; width:20px; vertical-align:baseline;">
|
<input type="checkbox" id="node-input-mapwms" style="display:inline-block; width:20px; vertical-align:baseline;">
|
||||||
Map server uses WMS
|
Map server uses WMS
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row" id="node-input-overlaylist">
|
||||||
<label for="node-input-overlist"><i class="fa fa-map-o"></i> Overlays</label>
|
<label for="node-input-overlist"><i class="fa fa-map-o"></i> Overlays</label>
|
||||||
<input type="text" id="node-input-overlist" placeholder="Initial Overlays">
|
<input type="text" id="node-input-overlist" placeholder="Initial Overlays">
|
||||||
</div>
|
</div>
|
||||||
@ -369,7 +369,7 @@ If <i>Web Path</i> is left empty, then by default <code>⌘⇧m</code> - <code>c
|
|||||||
allowFileDrop: {value:"false"},
|
allowFileDrop: {value:"false"},
|
||||||
path: {value:"/worldmap"},
|
path: {value:"/worldmap"},
|
||||||
overlist: {value:"DR,CO,RA,DN,HM"},
|
overlist: {value:"DR,CO,RA,DN,HM"},
|
||||||
maplist: {value:"OSMG,OSMC,EsriC,EsriS,EsriT,EsriDG,UKOS,SW"},
|
maplist: {value:"OSMG,OSMC,EsriC,EsriS,EsriT,EsriDG,UKOS"},
|
||||||
mapname: {value:""},
|
mapname: {value:""},
|
||||||
mapurl: {value:""},
|
mapurl: {value:""},
|
||||||
mapopt: {value:"", validate:function(v) {try{ v.length===0 || JSON.parse(v); return true;} catch(e) {return false;}}},
|
mapopt: {value:"", validate:function(v) {try{ v.length===0 || JSON.parse(v); return true;} catch(e) {return false;}}},
|
||||||
@ -391,8 +391,8 @@ If <i>Web Path</i> is left empty, then by default <code>⌘⇧m</code> - <code>c
|
|||||||
oneditprepare: function() {
|
oneditprepare: function() {
|
||||||
var mshort;
|
var mshort;
|
||||||
if (this.maplist === undefined) {
|
if (this.maplist === undefined) {
|
||||||
$("#node-input-maplist").val("OSMG,OSMC,EsriC,EsriS,EsriT,EsriO,EsriDG,NatGeo,UKOS,OpTop,SW");
|
$("#node-input-maplist").val("OSMG,OSMC,EsriC,EsriS,EsriT,EsriO,EsriDG,NatGeo,UKOS,OpTop");
|
||||||
this.maplist = "OSMG,OSMC,EsriC,EsriS,EsriT,EsriO,EsriDG,NatGeo,UKOS,OpTop,SW";
|
this.maplist = "OSMG,OSMC,EsriC,EsriS,EsriT,EsriO,EsriDG,NatGeo,UKOS,OpTop";
|
||||||
}
|
}
|
||||||
if (this.overlist === undefined) {
|
if (this.overlist === undefined) {
|
||||||
$("#node-input-overlist").val("DR,CO,RA,DN,HM");
|
$("#node-input-overlist").val("DR,CO,RA,DN,HM");
|
||||||
@ -482,7 +482,7 @@ If <i>Web Path</i> is left empty, then by default <code>⌘⇧m</code> - <code>c
|
|||||||
allowFileDrop: {value:"false"},
|
allowFileDrop: {value:"false"},
|
||||||
path: {value:"/worldmap"},
|
path: {value:"/worldmap"},
|
||||||
overlist: {value:"DR,CO,RA,DN,HM"},
|
overlist: {value:"DR,CO,RA,DN,HM"},
|
||||||
maplist: {value:"OSMG,OSMC,EsriC,EsriS,EsriT,EsriDG,UKOS,SW"},
|
maplist: {value:"OSMG,OSMC,EsriC,EsriS,EsriT,EsriDG,UKOS"},
|
||||||
mapname: {value:""},
|
mapname: {value:""},
|
||||||
mapurl: {value:""},
|
mapurl: {value:""},
|
||||||
mapopt: {value:""},
|
mapopt: {value:""},
|
||||||
@ -502,8 +502,8 @@ If <i>Web Path</i> is left empty, then by default <code>⌘⇧m</code> - <code>c
|
|||||||
oneditprepare: function() {
|
oneditprepare: function() {
|
||||||
var mshort;
|
var mshort;
|
||||||
if (this.maplist === undefined) {
|
if (this.maplist === undefined) {
|
||||||
$("#node-input-maplist").val("OSMG,OSMC,EsriC,EsriS,EsriT,EsriO,EsriDG,NatGeo,UKOS,OpTop,SW");
|
$("#node-input-maplist").val("OSMG,OSMC,EsriC,EsriS,EsriT,EsriO,EsriDG,NatGeo,UKOS,OpTop");
|
||||||
this.maplist = "OSMG,OSMC,EsriC,EsriS,EsriT,EsriO,EsriDG,NatGeo,UKOS,OpTop,SW";
|
this.maplist = "OSMG,OSMC,EsriC,EsriS,EsriT,EsriO,EsriDG,NatGeo,UKOS,OpTop";
|
||||||
}
|
}
|
||||||
if (this.overlist === undefined) {
|
if (this.overlist === undefined) {
|
||||||
$("#node-input-overlist").val("DR,CO,RA,DN,HM");
|
$("#node-input-overlist").val("DR,CO,RA,DN,HM");
|
||||||
@ -512,7 +512,7 @@ If <i>Web Path</i> is left empty, then by default <code>⌘⇧m</code> - <code>c
|
|||||||
$("#node-input-maplist").typedInput({type:"mapitem", types:[{
|
$("#node-input-maplist").typedInput({type:"mapitem", types:[{
|
||||||
value: "mapitem",
|
value: "mapitem",
|
||||||
multiple: true,
|
multiple: true,
|
||||||
options: mlist
|
options: mlist.concat([{ value:"MB3d", label:"Mapbox 3D" }])
|
||||||
}]});
|
}]});
|
||||||
$("#node-input-layer").typedInput({type:"laye", types:[{
|
$("#node-input-layer").typedInput({type:"laye", types:[{
|
||||||
value: "laye",
|
value: "laye",
|
||||||
@ -524,8 +524,20 @@ If <i>Web Path</i> is left empty, then by default <code>⌘⇧m</code> - <code>c
|
|||||||
options: olist
|
options: olist
|
||||||
}]});
|
}]});
|
||||||
$("#node-input-maplist").on('change', function(event, type, value) {
|
$("#node-input-maplist").on('change', function(event, type, value) {
|
||||||
mshort = mlist.filter(e => value.indexOf(e.value)!==-1);
|
if (value.indexOf("MB3d") > -1) {
|
||||||
mshort.push({ value:"Custom", label:"Custom Map Provider" });
|
mshort = [{ value:"MB3d", label:"Mapbox 3D" }];
|
||||||
|
$('#node-input-maplist option').prop('selected', false);
|
||||||
|
$("#node-input-maplist").val("MB3d");
|
||||||
|
this.maplist = "MB3d";
|
||||||
|
$("#node-input-baselayer").hide();
|
||||||
|
$("#node-input-overlaylist").hide();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
$("#node-input-baselayer").show();
|
||||||
|
$("#node-input-overlaylist").show();
|
||||||
|
mshort = mlist.filter(e => value.indexOf(e.value)!==-1);
|
||||||
|
mshort.push({ value:"Custom", label:"Custom Map Provider" });
|
||||||
|
}
|
||||||
$("#node-input-layer").typedInput("types", [{
|
$("#node-input-layer").typedInput("types", [{
|
||||||
value: "laye",
|
value: "laye",
|
||||||
options: mshort
|
options: mshort
|
||||||
|
@ -56,7 +56,13 @@ module.exports = function(RED) {
|
|||||||
node.log("started at "+node.path);
|
node.log("started at "+node.path);
|
||||||
var clients = {};
|
var clients = {};
|
||||||
RED.httpNode.get("/-worldmap3d-key", RED.auth.needsPermission('worldmap3d.read'), function(req, res) {
|
RED.httpNode.get("/-worldmap3d-key", RED.auth.needsPermission('worldmap3d.read'), function(req, res) {
|
||||||
res.send({key:process.env.MAPBOXGL_TOKEN||""});
|
if (process.env.MAPBOXGL_TOKEN) {
|
||||||
|
res.send({key:process.env.MAPBOXGL_TOKEN});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
node.error("No API key set");
|
||||||
|
res.send({key:''})
|
||||||
|
}
|
||||||
});
|
});
|
||||||
RED.httpNode.use(compression());
|
RED.httpNode.use(compression());
|
||||||
RED.httpNode.use(node.path, express.static(__dirname + '/worldmap'));
|
RED.httpNode.use(node.path, express.static(__dirname + '/worldmap'));
|
||||||
@ -167,6 +173,7 @@ module.exports = function(RED) {
|
|||||||
var frameWidth = (size.sx + size.cx) * width - size.cx;
|
var frameWidth = (size.sx + size.cx) * width - size.cx;
|
||||||
var frameHeight = (size.sy + size.cy) * height - size.cy;
|
var frameHeight = (size.sy + size.cy) * height - size.cy;
|
||||||
var url = encodeURI(path.posix.join(RED.settings.httpNodeRoot||RED.settings.httpRoot,config.path));
|
var url = encodeURI(path.posix.join(RED.settings.httpNodeRoot||RED.settings.httpRoot,config.path));
|
||||||
|
if (config.layer === "MB3d") { url += "/index3d.html"; }
|
||||||
var html = `<style>.nr-dashboard-ui_worldmap{padding:0;}</style><div style="overflow:hidden;">
|
var html = `<style>.nr-dashboard-ui_worldmap{padding:0;}</style><div style="overflow:hidden;">
|
||||||
<iframe src="${url}" width="${frameWidth}px" height="${frameHeight}px" style="border:none;"></iframe></div>`;
|
<iframe src="${url}" width="${frameWidth}px" height="${frameHeight}px" style="border:none;"></iframe></div>`;
|
||||||
return html;
|
return html;
|
||||||
|
@ -18,14 +18,236 @@
|
|||||||
<script>
|
<script>
|
||||||
|
|
||||||
// TO MAKE THE MAP APPEAR YOU MUST ADD YOUR ACCESS TOKEN FROM https://account.mapbox.com
|
// TO MAKE THE MAP APPEAR YOU MUST ADD YOUR ACCESS TOKEN FROM https://account.mapbox.com
|
||||||
// This needs to be set as an environment variable MAPBOXGL_TOKEN available to the Node-RED session on your server
|
// This can also be set as an environment variable MAPBOXGL_TOKEN available to the Node-RED session on your server
|
||||||
mapboxgl.accessToken = '';
|
mapboxgl.accessToken = '';
|
||||||
|
|
||||||
var people = {};
|
// You can also replace this with a custom style if you like.
|
||||||
var mbstyle = 'mapbox://styles/mapbox/streets-v10';
|
var mbstyle = 'mapbox://styles/mapbox/streets-v10';
|
||||||
// var mbstyle = 'mapbox://styles/mapbox/light-v10';
|
// var mbstyle = 'mapbox://styles/mapbox/light-v10';
|
||||||
|
|
||||||
fetch('/-worldmap3d-key')
|
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 bock 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(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
mapboxgl.accessToken = data.key;
|
mapboxgl.accessToken = data.key;
|
||||||
@ -33,223 +255,10 @@ fetch('/-worldmap3d-key')
|
|||||||
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.");
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
do3dMap();
|
||||||
var map = new mapboxgl.Map({
|
})
|
||||||
container: 'map',
|
.catch(error => { console.log("Unable to fetch MAPBOXGL_TOKEN.",error)} );
|
||||||
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 bock 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";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
})
|
|
||||||
.catch(error => { console.log("Unable to fetch MAPBOXGL_TOKEN.",error)} );
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
6
worldmap/leaflet/sockjs.min.js
vendored
6
worldmap/leaflet/sockjs.min.js
vendored
File diff suppressed because one or more lines are too long
@ -735,18 +735,21 @@ map.on('contextmenu', function(e) {
|
|||||||
|
|
||||||
// Add all the base layer maps if we are online.
|
// Add all the base layer maps if we are online.
|
||||||
var addBaseMaps = function(maplist,first) {
|
var addBaseMaps = function(maplist,first) {
|
||||||
//console.log("MAPS",first,maplist)
|
// console.log("MAPS",first,maplist)
|
||||||
if (navigator.onLine) {
|
if (navigator.onLine) {
|
||||||
var layerlookup = { OSMG:"OSM grey", OSMC:"OSM", OSMH:"OSM Humanitarian", EsriC:"Esri", EsriS:"Esri Satellite",
|
var layerlookup = { OSMG:"OSM grey", OSMC:"OSM", OSMH:"OSM Humanitarian", EsriC:"Esri", EsriS:"Esri Satellite",
|
||||||
EsriR:"Esri Relief", EsriT:"Esri Topography", EsriO:"Esri Ocean", EsriDG:"Esri Dark Grey", NatGeo: "National Geographic",
|
EsriR:"Esri Relief", EsriT:"Esri Topography", EsriO:"Esri Ocean", EsriDG:"Esri Dark Grey", NatGeo: "National Geographic",
|
||||||
UKOS:"UK OS OpenData", UKOS45:"UK OS 1919-1947", UKOS00:"UK OS 1900", OpTop:"Open Topo Map",
|
UKOS:"UK OS OpenData", OS45:"UK OS 1919-1947", OS00:"UK OS 1900", OpTop:"Open Topo Map",
|
||||||
HB:"Hike Bike OSM", ST:"Stamen Topography", SW: "Stamen Watercolor", AN:"AutoNavi (Chinese)"
|
HB:"Hike Bike OSM", ST:"Stamen Topography", SW:"Stamen Watercolor", AN:"AutoNavi (Chinese)"
|
||||||
}
|
}
|
||||||
|
|
||||||
// Use this for OSM online maps
|
// Use this for OSM online maps
|
||||||
var osmUrl='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
|
var osmUrl='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
|
||||||
var osmAttrib='Map data © OpenStreetMap contributors';
|
var osmAttrib='Map data © OpenStreetMap contributors';
|
||||||
|
|
||||||
|
if (maplist.indexOf("MB3d")!==-1) { // handle the case of 3d by redirecting to that page instead.
|
||||||
|
window.location.href("index3d.html");
|
||||||
|
}
|
||||||
if (maplist.indexOf("OSMG")!==-1) {
|
if (maplist.indexOf("OSMG")!==-1) {
|
||||||
basemaps[layerlookup["OSMG"]] = new L.TileLayer.Grayscale(osmUrl, {
|
basemaps[layerlookup["OSMG"]] = new L.TileLayer.Grayscale(osmUrl, {
|
||||||
attribution:osmAttrib,
|
attribution:osmAttrib,
|
||||||
|
Loading…
Reference in New Issue
Block a user