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:
Dave Conway-Jones 2018-10-12 23:58:20 +01:00 committed by GitHub
parent 5f3812a368
commit 3992e05cdc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 106 additions and 47 deletions

View File

@ -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...

View File

@ -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",

View File

@ -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>

View File

@ -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);

View File

@ -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; }

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -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: '&copy; <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"];

View File

@ -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));
};

View File

@ -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