multimap (#51)
* fix fa-icons links * first go at multimaps * Handle cleaning up old static routes Not great but seems to be the best way... Issue #40 * add some colour buttons * Add topojson countries layer for disconnected use
This commit is contained in:
parent
5f3812a368
commit
3992e05cdc
@ -55,7 +55,7 @@ Optional properties include
|
||||
- **speed** : combined with bearing, draws a vector.
|
||||
- **bearing** : combined with speed, draws a vector.
|
||||
- **accuracy** : combined with bearing, draws a polygon of possible direction.
|
||||
- **icon** : <a href="http://fortawesome.github.io/Font-Awesome/icons/" target="mapinfo">font awesome</a> icon name.
|
||||
- **icon** : <a href="https://fontawesome.com/v4.7.0/icons/" target="mapinfo">font awesome</a> icon name.
|
||||
- **iconColor** : Standard CSS colour name or #rrggbb hex value.
|
||||
- **SIDC** : NATO symbology code (instead of icon). See below.
|
||||
- **building** : OSMbulding GeoJSON feature set to add 2.5D buildings to buildings layer. See below.
|
||||
@ -72,7 +72,7 @@ Any other `msg.payload` properties will be added to the icon popup text box.
|
||||
|
||||
### Icons
|
||||
|
||||
You may select any of the Font Awesome set of [icons](http://fortawesome.github.io/Font-Awesome/icons/).
|
||||
You may select any of the Font Awesome set of [icons](https://fontawesome.com/v4.7.0/icons/).
|
||||
If you use the name without the fa- prefix (eg `male`) you will get the icon inside a generic marker shape. If you use the fa- prefix (eg `fa-male`) you will get the icon on its own.
|
||||
|
||||
There are also several special icons...
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "node-red-contrib-web-worldmap",
|
||||
"version": "1.4.6",
|
||||
"version": "1.4.7",
|
||||
"description": "A Node-RED node to provide a web page of a world map for plotting things on.",
|
||||
"dependencies": {
|
||||
"cgi": "0.3.1",
|
||||
|
@ -35,10 +35,10 @@
|
||||
<option value="Esri Satellite">ESRI Satellite</option>
|
||||
<option value="Esri Terrain">ESRI Terrain</option>
|
||||
<option value="Esri Ocean">ESRI Ocean</option>
|
||||
<option value="Mapsurfer">Mapsurfer</option>
|
||||
<option value="MapQuest OSM">MapQuest OSM</option>
|
||||
<option value="Nat Geo">National Geographic</option>
|
||||
<option value="UK OS Opendata">UK OS Opendata</option>
|
||||
<option value="Hike Bike">Hike Bike OSM</option>
|
||||
<option value="Terrain">Terrain</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
@ -68,16 +68,20 @@
|
||||
<option value="true">True</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-path"><i class="fa fa-globe"></i> Web Path</label>
|
||||
<input type="text" id="node-input-path" placeholder="worldmap">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-file"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="name">
|
||||
</div>
|
||||
<div class="form-tips">Tip: By default <code>⌘⇧m</code> - <code>ctrl-shift-m</code> will load the map in a new tab.</div>
|
||||
<!-- <div class="form-tips">Tip: By default <code>⌘⇧m</code> - <code>ctrl-shift-m</code> will load the map in a new tab.</div>-->
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-help-name="worldmap">
|
||||
<p>Plots "things" on a web map. Needs an internet connection.</p>
|
||||
<p>Shortcut - <code>⌘⇧m</code> - ctrl-shift-m to jump to Map.</p>
|
||||
<!-- <p>Shortcut - <code>⌘⇧m</code> - ctrl-shift-m to jump to Map.</p> -->
|
||||
<p>The minimum <code>msg.payload</code> must contain <code>name</code>, <code>lat</code> and <code>lon</code> properties, e.g.</p>
|
||||
<pre>{name:"Joe", lat:51, lon:-1.05}</pre>
|
||||
<p><code>name</code> must be a unique identifier.</p>
|
||||
@ -108,8 +112,7 @@
|
||||
|
||||
<script type="text/javascript">
|
||||
var lnk = document.location.host+RED.settings.httpNodeRoot+"/worldmap";
|
||||
var re = new RegExp('\/{1,}','g');
|
||||
lnk = lnk.replace(re,'/');
|
||||
lnk = lnk.replace(new RegExp('\/{1,}','g'),'/');
|
||||
if (!RED.hasOwnProperty("actions")) {
|
||||
RED.keyboard.add("*",/* m */ 77,{ctrl:true, shift:true},function() { window.open(document.location.protocol+"//"+lnk) });
|
||||
}
|
||||
@ -130,7 +133,8 @@
|
||||
maxage: {value:""},
|
||||
usermenu: {value:"show"},
|
||||
layers: {value:"show"},
|
||||
panit: {value:"false"}
|
||||
panit: {value:"false"},
|
||||
path: {value:"/worldmap"}
|
||||
},
|
||||
inputs:1,
|
||||
outputs:0,
|
||||
@ -143,16 +147,24 @@
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
info: function() {
|
||||
return 'The map can be found [here]('+RED.settings.httpNodeRoot.slice(0,-1)+'/worldmap).';
|
||||
return 'The map can be found [here]('+RED.settings.httpNodeRoot.slice(0,-1)+this.path+').';
|
||||
},
|
||||
oneditprepare: function() {
|
||||
$( "#node-input-zoom" ).spinner({min:0, max:18});
|
||||
$( "#node-input-cluster" ).spinner({min:0, max:19});
|
||||
if ($("#node-input-path").val() === undefined) {
|
||||
$("#node-input-path").val("worldmap");
|
||||
this.path = "worldmap";
|
||||
}
|
||||
$("#node-input-zoom").spinner({min:0, max:18});
|
||||
$("#node-input-cluster").spinner({min:0, max:19});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<script type="text/x-red" data-template-name="worldmap in">
|
||||
<div class="form-row">
|
||||
<label for="node-input-path"><i class="fa fa-globe"></i> Web Path</label>
|
||||
<input type="text" id="node-input-path" placeholder="worldmap">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-name"><i class="fa fa-file"></i> Name</label>
|
||||
<input type="text" id="node-input-name" placeholder="name">
|
||||
@ -168,7 +180,8 @@
|
||||
category: 'location',
|
||||
color:"darksalmon",
|
||||
defaults: {
|
||||
name: {value:""}
|
||||
name: {value:""},
|
||||
path: {value:"/worldmap"}
|
||||
},
|
||||
inputs:0,
|
||||
outputs:1,
|
||||
@ -180,7 +193,7 @@
|
||||
return this.name?"node_label_italic":"";
|
||||
},
|
||||
info: function() {
|
||||
return 'The map can be found [here]('+RED.settings.httpNodeRoot.slice(0,-1)+'/worldmap).';
|
||||
return 'The map can be found [here]('+RED.settings.httpNodeRoot.slice(0,-1)+this.path+').';
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
47
worldmap.js
47
worldmap.js
@ -19,15 +19,12 @@ module.exports = function(RED) {
|
||||
var path = require("path");
|
||||
var express = require("express");
|
||||
var sockjs = require('sockjs');
|
||||
var socket;
|
||||
var sockets = {};
|
||||
// add the cgi module for serving local maps....
|
||||
RED.httpNode.use("/cgi-bin/mapserv", require('cgi')(__dirname + '/mapserv'));
|
||||
|
||||
var WorldMap = function(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
if (!socket) {
|
||||
var fullPath = path.posix.join(RED.settings.httpNodeRoot, 'worldmap', 'leaflet', 'sockjs.min.js');
|
||||
socket = sockjs.createServer({sockjs_url:fullPath, log:function() {}, transports:"xhr-polling"});
|
||||
socket.installHandlers(RED.server, {prefix:path.posix.join(RED.settings.httpNodeRoot,'/worldmap/socket')});
|
||||
}
|
||||
this.lat = n.lat || "";
|
||||
this.lon = n.lon || "";
|
||||
this.zoom = n.zoom || "";
|
||||
@ -37,12 +34,17 @@ module.exports = function(RED) {
|
||||
this.showmenu = n.usermenu || "show";
|
||||
this.panit = n.panit || "false";
|
||||
this.layers = n.layers || "show";
|
||||
this.path = n.path || "/worldmap";
|
||||
if (!sockets[this.path]) {
|
||||
var fullPath = path.posix.join(RED.settings.httpNodeRoot, this.path, 'leaflet', 'sockjs.min.js');
|
||||
sockets[this.path] = sockjs.createServer({sockjs_url:fullPath, log:function() {}, transports:"xhr-polling"});
|
||||
sockets[this.path].installHandlers(RED.server, {prefix:path.posix.join(RED.settings.httpNodeRoot,this.path,'socket')});
|
||||
}
|
||||
var node = this;
|
||||
var clients = {};
|
||||
//node.log("Serving map from "+__dirname+" as "+RED.settings.httpNodeRoot.slice(0,-1)+"/worldmap");
|
||||
RED.httpNode.use("/worldmap", express.static(__dirname + '/worldmap'));
|
||||
// add the cgi module for serving local maps....
|
||||
RED.httpNode.use("/cgi-bin/mapserv", require('cgi')(__dirname + '/mapserv'));
|
||||
//node.log("Serving map from "+__dirname+" as "+RED.settings.httpNodeRoot.slice(0,-1)+node;path);
|
||||
RED.httpNode.use(node.path, express.static(__dirname + '/worldmap'));
|
||||
|
||||
|
||||
var callback = function(client) {
|
||||
//client.setMaxListeners(0);
|
||||
@ -89,20 +91,27 @@ module.exports = function(RED) {
|
||||
clients[c].end();
|
||||
}
|
||||
}
|
||||
socket.removeListener('connection', callback);
|
||||
sockets[this.path].removeListener('connection', callback);
|
||||
for (var i=0; i < RED.httpNode._router.stack.length; i++) {
|
||||
var r = RED.httpNode._router.stack[i];
|
||||
if ((r.name === "serveStatic") && (r.regexp.test(node.path))) {
|
||||
RED.httpNode._router.stack.splice(i, 1)
|
||||
}
|
||||
}
|
||||
node.status({});
|
||||
});
|
||||
socket.on('connection', callback);
|
||||
sockets[this.path].on('connection', callback);
|
||||
}
|
||||
RED.nodes.registerType("worldmap",WorldMap);
|
||||
|
||||
|
||||
var WorldMapIn = function(n) {
|
||||
RED.nodes.createNode(this,n);
|
||||
if (!socket) {
|
||||
var fullPath = path.posix.join(RED.settings.httpNodeRoot, 'worldmap', 'leaflet', 'sockjs.min.js');
|
||||
socket = sockjs.createServer({sockjs_url:fullPath, prefix:path.posix.join(RED.settings.httpNodeRoot,'/worldmap/socket')});
|
||||
socket.installHandlers(RED.server);
|
||||
this.path = n.path || "/worldmap";
|
||||
if (!sockets[this.path]) {
|
||||
var fullPath = path.posix.join(RED.settings.httpNodeRoot, this.path, 'leaflet', 'sockjs.min.js');
|
||||
sockets[this.path] = sockjs.createServer({sockjs_url:fullPath, prefix:path.posix.join(RED.settings.httpNodeRoot,this.path,'socket')});
|
||||
sockets[this.path].installHandlers(RED.server);
|
||||
}
|
||||
var node = this;
|
||||
var clients = {};
|
||||
@ -113,7 +122,7 @@ module.exports = function(RED) {
|
||||
node.status({fill:"green",shape:"dot",text:"connected "+Object.keys(clients).length});
|
||||
client.on('data', function(message) {
|
||||
message = JSON.parse(message);
|
||||
node.send({payload:message, topic:"worldmap", _sessionid:client.id});
|
||||
node.send({payload:message, topic:node.path.substr(1), _sessionid:client.id});
|
||||
});
|
||||
client.on('close', function() {
|
||||
delete clients[client.id];
|
||||
@ -128,10 +137,10 @@ module.exports = function(RED) {
|
||||
clients[c].end();
|
||||
}
|
||||
}
|
||||
socket.removeListener('connection', callback);
|
||||
sockets[this.path].removeListener('connection', callback);
|
||||
node.status({});
|
||||
});
|
||||
socket.on('connection', callback);
|
||||
sockets[this.path].on('connection', callback);
|
||||
}
|
||||
RED.nodes.registerType("worldmap in",WorldMapIn);
|
||||
|
||||
|
@ -171,3 +171,9 @@ a {
|
||||
-o-transform-origin:0 100%;
|
||||
transform-origin:0 100%;
|
||||
}
|
||||
|
||||
.wm-red { color: #F56361; }
|
||||
.wm-blue { color: #96CCE1; }
|
||||
.wm-green { color: #ADD5A6; }
|
||||
.wm-yellow { color: #F5EF91; }
|
||||
.wm-black { color: #444444; }
|
||||
|
1
worldmap/images/world-110m-flat.json
Normal file
1
worldmap/images/world-110m-flat.json
Normal file
File diff suppressed because one or more lines are too long
1
worldmap/images/world-50m-flat.json
Normal file
1
worldmap/images/world-50m-flat.json
Normal file
File diff suppressed because one or more lines are too long
@ -212,7 +212,15 @@ if ( window.localStorage.hasOwnProperty("maxage") ) {
|
||||
// Create the Initial Map object.
|
||||
map = new L.map('map').setView(startpos, startzoom);
|
||||
|
||||
var menuButton = L.easyButton( 'fa-bars fa-lg', function() { toggleMenu(); }, "Toggle menu", "topright");
|
||||
var menuButton = L.easyButton({states:[{icon:'fa-bars fa-lg', onClick:function() { toggleMenu(); }, title:'Toggle menu'}], position:"topright"});
|
||||
//var colorPickButton = L.easyButton({states:[{icon:'fa-tint fa-lg', onClick:function() { console.log("PICK"); }, title:'Pick Colour'}]});
|
||||
var redButton = L.easyButton('fa-square wm-red', function(btn) { console.log("RED",btn); })
|
||||
var blueButton = L.easyButton('fa-square wm-blue', function(btn) { console.log("BLUE",btn); })
|
||||
var greenButton = L.easyButton('fa-square wm-green', function(btn) { console.log("GREEN",btn); })
|
||||
var yellowButton = L.easyButton('fa-square wm-yellow', function(btn) { console.log("YELLOW",btn); })
|
||||
var blackButton = L.easyButton('fa-square wm-black', function(btn) { console.log("BLACK",btn); })
|
||||
var colorControl = L.easyBar([redButton,blueButton,greenButton,yellowButton,blackButton]);
|
||||
|
||||
|
||||
// Move some bits around if in an iframe
|
||||
if (window.self !== window.top) {
|
||||
@ -264,6 +272,7 @@ if (!inIframe) {
|
||||
}
|
||||
else { if (showUserMenu) { menuButton.addTo(map); } }
|
||||
|
||||
|
||||
// Handle the dialog for popup help
|
||||
var dialog = document.querySelector('dialog');
|
||||
dialogPolyfill.registerDialog(dialog);
|
||||
@ -436,8 +445,11 @@ map.on('overlayadd', function(e) {
|
||||
layers["_daynight"].addLayer(L.terminator());
|
||||
}
|
||||
if (e.name == "drawing") {
|
||||
map.addControl(drawControl);
|
||||
overlays["drawing"].bringToFront();
|
||||
// And the actual draw controls
|
||||
map.addControl(drawControl);
|
||||
// Add the color change button
|
||||
//colorControl.addTo(map);
|
||||
}
|
||||
ws.send(JSON.stringify({action:"addlayer", name:e.name}));
|
||||
});
|
||||
@ -452,6 +464,7 @@ map.on('overlayremove', function(e) {
|
||||
layers["_daynight"].clearLayers();
|
||||
}
|
||||
if (e.name == "drawing") {
|
||||
map.removeControl(colorControl);
|
||||
map.removeControl(drawControl);
|
||||
}
|
||||
//else console.log("layer del :",e.name);
|
||||
@ -623,7 +636,6 @@ var NLS_OS_opendata = L.tileLayer('https://geo.nls.uk/maps/opendata/{z}/{x}/{y}.
|
||||
});
|
||||
basemaps["UK OS Opendata"] = NLS_OS_opendata;
|
||||
|
||||
|
||||
var HikeBike_HikeBike = L.tileLayer('http://{s}.tiles.wmflabs.org/hikebike/{z}/{x}/{y}.png', {
|
||||
maxZoom: 19,
|
||||
attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
||||
@ -658,8 +670,14 @@ basemaps["Terrain"] = new L.StamenTileLayer('terrain');
|
||||
// Nice watercolour based maps by Stamen Design
|
||||
basemaps["Watercolor"] = new L.StamenTileLayer('watercolor');
|
||||
|
||||
|
||||
// Now add the overlays
|
||||
|
||||
// Add the countries (world-110m) for offline use
|
||||
var customTopoLayer = L.geoJson(null, {style: {color:"blue", weight:2, fillColor:"#cf6", fillOpacity:0.04}});
|
||||
layers["_countries"] = omnivore.topojson('images/world-50m-flat.json',null,customTopoLayer);
|
||||
overlays["countries"] = layers["_countries"];
|
||||
|
||||
// Add the day/night overlay
|
||||
layers["_daynight"] = new L.LayerGroup();
|
||||
overlays["day/night"] = layers["_daynight"];
|
||||
|
@ -111,19 +111,22 @@ L.Control.EasyButton = L.Control.extend({
|
||||
// icon: 'fa-circle', // wrapped with <a>
|
||||
// }
|
||||
|
||||
leafletClasses: true // use leaflet styles for the button
|
||||
leafletClasses: true, // use leaflet styles for the button
|
||||
tagName: 'button',
|
||||
},
|
||||
|
||||
|
||||
|
||||
initialize: function(icon, onClick, title, position){
|
||||
|
||||
// Added easy position input - DCJ
|
||||
this.options.position = position || 'topleft';
|
||||
initialize: function(icon, onClick, title, id){
|
||||
|
||||
// clear the states manually
|
||||
this.options.states = [];
|
||||
|
||||
// add id to options
|
||||
if(id != null){
|
||||
this.options.id = id;
|
||||
}
|
||||
|
||||
// storage between state functions
|
||||
this.storage = {};
|
||||
|
||||
@ -164,18 +167,25 @@ L.Control.EasyButton = L.Control.extend({
|
||||
|
||||
_buildButton: function(){
|
||||
|
||||
this.button = L.DomUtil.create('button', '');
|
||||
this.button = L.DomUtil.create(this.options.tagName, '');
|
||||
|
||||
// the next three if statements should be collapsed into the options
|
||||
// when it's time for breaking changes.
|
||||
if (this.tagName === 'button') {
|
||||
this.button.type = 'button';
|
||||
}
|
||||
|
||||
if (this.options.id ){
|
||||
this.button.id = this.options.id;
|
||||
}
|
||||
|
||||
if (this.options.leafletClasses){
|
||||
L.DomUtil.addClass(this.button, 'easy-button-button leaflet-bar-part');
|
||||
L.DomUtil.addClass(this.button, 'easy-button-button leaflet-bar-part leaflet-interactive');
|
||||
}
|
||||
|
||||
// don't let double clicks get to the map
|
||||
// don't let double clicks and mousedown get to the map
|
||||
L.DomEvent.addListener(this.button, 'dblclick', L.DomEvent.stop);
|
||||
L.DomEvent.addListener(this.button, 'mousedown', L.DomEvent.stop);
|
||||
|
||||
// take care of normal clicks
|
||||
L.DomEvent.addListener(this.button,'click', function(e){
|
||||
@ -309,7 +319,7 @@ L.Control.EasyButton = L.Control.extend({
|
||||
});
|
||||
|
||||
L.easyButton = function(/* args will pass automatically */){
|
||||
var args = Array.prototype.concat.apply([L.Control.EasyButton],arguments)
|
||||
var args = Array.prototype.concat.apply([L.Control.EasyButton],arguments);
|
||||
return new (Function.prototype.bind.apply(L.Control.EasyButton, args));
|
||||
};
|
||||
|
||||
|
@ -1,10 +1,11 @@
|
||||
CACHE MANIFEST
|
||||
# date: Oct 3rd 2018 - v1.4.6
|
||||
# date: Oct 12th 2018 - v1.5.0
|
||||
|
||||
CACHE:
|
||||
index.html
|
||||
favicon.ico
|
||||
images/node-red.png
|
||||
images/world-50m-flat.json
|
||||
css/map.css
|
||||
leaflet/L.Terminator.js
|
||||
leaflet/Leaflet.fullscreen.min.js
|
||||
|
Loading…
Reference in New Issue
Block a user