node-red-contrib-jwht-map/worldmap/index.html

1223 lines
55 KiB
HTML
Raw Normal View History

2016-04-01 18:31:07 +08:00
<!DOCTYPE HTML>
<!--
2017-04-20 20:44:19 +08:00
Copyright 2015, 2017 IBM Corp.
2016-04-01 18:31:07 +08:00
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
2016-06-09 05:07:54 +08:00
<html manifest="worldmap.appcache">
2016-04-01 18:31:07 +08:00
<head>
<title>Node-RED map all the things</title>
<meta name="mobile-web-app-capable" content="yes">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet",type="text/css" href="css/map.css"/>
<link rel="stylesheet",type="text/css" href="leaflet/leaflet.css"/>
<link rel="stylesheet",type="text/css" href="leaflet/font-awesome/css/font-awesome.min.css"/>
<link rel="stylesheet",type="text/css" href="leaflet/Leaflet.vector-markers.css">
<link rel="stylesheet",type="text/css" href="leaflet/MarkerCluster.css">
<link rel="stylesheet",type="text/css" href="leaflet/MarkerCluster.Default.css">
<link rel="stylesheet",type="text/css" href="leaflet/leaflet.draw.css">
<link rel="stylesheet",type="text/css" href="leaflet/leaflet.measurecontrol.css">
<link rel="stylesheet",type="text/css" href="leaflet/easy-button.css">
<link rel="stylesheet",type="text/css" href="leaflet/leaflet.fullscreen.css">
<!-- <link rel="stylesheet" type="text/css" href="leaflet/leaflet-openweathermap.css"/> -->
2016-04-01 18:31:07 +08:00
<link rel="shortcut icon" type="image/ico" href="favicon.ico"/>
<script type="text/javascript" src="leaflet/sockjs.min.js"></script>
2016-04-01 18:31:07 +08:00
<script type="text/javascript" src="leaflet/leaflet.js"></script>
<script type="text/javascript" src="leaflet/Leaflet.vector-markers.min.js"></script>
<script type="text/javascript" src="leaflet/leaflet.boatmarker.js"></script>
<script type="text/javascript" src="leaflet/leaflet.markercluster.js"></script>
2016-06-27 18:55:30 +08:00
<script type="text/javascript" src="leaflet/leaflet.markercluster.freezable-src.js"></script>
2016-04-01 18:31:07 +08:00
<script type="text/javascript" src="leaflet/leaflet.active-layers.min.js"></script>
<script type="text/javascript" src="leaflet/leaflet.select-layers.min.js"></script>
<script type="text/javascript" src="leaflet/leaflet.draw.js"></script>
<script type="text/javascript" src="leaflet/leaflet.measurecontrol.js"></script>
<script type="text/javascript" src="leaflet/easy-button.js"></script>
<script type="text/javascript" src="leaflet/Leaflet.fullscreen.min.js"></script>
2016-04-01 18:31:07 +08:00
<script type="text/javascript" src="leaflet/l.ellipse.min.js"></script>
<script type="text/javascript" src="leaflet/leaflet-heat.js"></script>
<script type="text/javascript" src="leaflet/TileLayer.Grayscale.js"></script>
<script type="text/javascript" src="leaflet/TileLayer.GrayscaleWMS.js"></script>
<script type="text/javascript" src="leaflet/L.Terminator.js"></script>
<script type="text/javascript" src="leaflet/tile.stamen.js"></script>
<!-- <script type="text/javascript" src="leaflet/leaflet-openweathermap.js"></script> -->
2016-10-19 05:37:07 +08:00
<script type="text/javascript" src="leaflet/OSMBuildings-Leaflet.js"></script>
<script type="text/javascript" src="leaflet/dialog-polyfill.js"></script>
<link rel="stylesheet" type="text/css" href="leaflet/dialog-polyfill.css"/>
2016-04-01 18:31:07 +08:00
</head>
<body onunload="ws.send(JSON.stringify({'action':'end'}));">
2016-04-01 18:31:07 +08:00
<div id="topbar">
<a href="http://nodered.org"><img src="images/node-red.png" width="60px" height="24px"/></a>
<span class="topbar"> Node-RED - map all the things</span>
2016-04-01 18:31:07 +08:00
</div>
<div id="results">
<span id="searchRes"></span>
<span id="bars" onclick='toggleMenu()' onmouseover='toggleMenu()'><i class="fa fa-bars fa-2x fa-inverse"></i></span>
2016-04-01 18:31:07 +08:00
</div>
<div id="menu"><table>
<tr><td><input type='text' name='search' id='search' size='20' style="width:150px;"/>&nbsp;<span onclick='doSearch();'><i class="fa fa-search fa-lg"></i></span></td></tr>
<tr><td style="cursor:default"><i class="fa fa-spinner fa-lg fa-fw"></i> Set Max Age <input type='text' name='maxage' id='maxage' value="600" size="5" onchange='setMaxAge();'/>s</td></tr>
<tr><td style="cursor:default"><i class="fa fa-search-plus fa-lg fa-fw"></i> Cluster at zoom <<input type='text' name='setclus' id='setclus' value="10" size="2" onchange='setCluster();'/></td></tr>
<tr><td style="cursor:default"><input type='checkbox' name='panit' onclick='doPanit();'/> Auto Pan Map</td></tr>
<tr><td style="cursor:default"><input type='checkbox' name='lockit' onclick='doLock();'/> Lock Map</td></tr>
<tr><td style="cursor:default"><input type='checkbox' name='heatall' onclick='doHeatAll();'/> Heatmap all layers</td></tr>
<tr><td style="cursor:default"><span id="showHelp" ><i class="fa fa-info fa-lg fa-fw"></i>Help</span></td></tr>
2016-04-01 18:31:07 +08:00
</table></div>
<div id="map"></div>
2016-11-22 16:21:56 +08:00
<div id="foot"></div>
2016-04-01 18:31:07 +08:00
<!-- <div id="heat"><button type="button" onclick="clearHeat();">Clear Heatmap</button></div> -->
<dialog id="helpWindow">
<h3>Node-RED - Map all the things</h3>
<p><i class="fa fa-search fa-lg fa-fw"></i> Search - You may enter a name or partial name of an object to search for.<br/>
The map will then jump to centre on each of the results.</p>
<p><i class="fa fa-spinner fa-lg fa-fw"></i> Set Max Age - You can set the time after which points
2016-04-01 18:31:07 +08:00
that haven't been updated get removed.</p>
2016-05-17 19:22:04 +08:00
<p><i class="fa fa-search-plus fa-lg fa-fw"></i> Cluster at zoom - by default multiple objects will be clustered at zoom levels less than 12.</p>
2016-04-01 18:31:07 +08:00
<p><i class="fa fa-arrows fa-lg fa-fw"></i> Auto Pan - When selected, the map will
automatically move to centre on each data point as they arrive.</p>
<p><i class="fa fa-lock fa-lg fa-fw"></i> Lock Map - When selected will save the
currently displayed area and basemap.<br/>
Reloading the map in the current browser will return to the same view.<br/>
This can be used to set your initial start position.<br/>
While active it also restricts the "auto pan" to within that area.</p>
<p><i class="fa fa-globe fa-lg fa-fw"></i> Heatmap all layers - When selected
all layers whether hidden or not will contribute to the heatmap.<br/>
The default is that only visible layers add to the heatmap.</p>
<button id="exitHelp">Close</button>
</dialog>
<script language="javascript" type="text/javascript">
2016-05-08 18:07:09 +08:00
var startpos = [51.03, -1.379]; // Start location - somewhere in UK :-)
2016-04-01 18:31:07 +08:00
var startzoom = 10;
var map;
var markers = {};
var polygons = {};
var layers = {};
var overlays = {};
var basemaps = {};
var marks = [];
var marksIndex = 0;
var popid = "";
var menuOpen = false;
2016-06-27 18:55:30 +08:00
var clusterAt = 12;
2016-05-08 18:07:09 +08:00
var maxage = 600; // default max age of icons on map in seconds - cleared after 10 mins
var baselayername = "OSM grey"; // Default base layer OSM but uniform grey
2017-04-20 20:44:19 +08:00
var ibmfoot = "&nbsp;&copy; IBM 2015,2017"
var initialposition = false;
2016-11-22 16:21:56 +08:00
var inIframe = false;
var showUserMenu = true;
var showLayerMenu = true;
var layercontrol;
var ws;
2016-04-01 18:31:07 +08:00
2016-05-25 04:34:18 +08:00
// Create the socket
var connect = function() {
ws = new SockJS(location.pathname + 'socket');
ws.onopen = function() {
console.log("CONNECTED");
if (!inIframe) {
document.getElementById("foot").innerHTML = "<font color='#494'>"+ibmfoot+"</font>";
}
ws.send(JSON.stringify({action:"connected"}));
};
ws.onclose = function() {
console.log("DISCONNECTED");
if (!inIframe) {
document.getElementById("foot").innerHTML = "<font color='#900'>"+ibmfoot+"</font>";
}
setTimeout(function() { connect(); }, 2500);
};
ws.onmessage = function(e) {
var data = JSON.parse(e.data);
//console.log("DATA",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",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();
2016-05-25 04:34:18 +08:00
2016-04-01 18:31:07 +08:00
if ( window.localStorage.hasOwnProperty("lastpos") ) {
initialposition = true;
var sp = JSON.parse(window.localStorage.getItem("lastpos"));
2016-04-01 18:31:07 +08:00
startpos = [ sp.lat, sp.lng ];
}
if ( window.localStorage.hasOwnProperty("lastzoom") ) {
startzoom = window.localStorage.getItem("lastzoom");
}
2016-06-27 18:55:30 +08:00
// if ( window.localStorage.hasOwnProperty("clusterat") ) {
// clusterAt = window.localStorage.getItem("clusterat");
// document.getElementById("setclus").value = clusterAt;
// }
if ( window.localStorage.hasOwnProperty("maxage") ) {
maxage = window.localStorage.getItem("maxage");
document.getElementById("maxage").value = maxage;
2016-04-01 18:31:07 +08:00
}
2016-04-01 18:31:07 +08:00
// 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").addTo(map);
// Move some bits around if in an iframe
if (window.self !== window.top) {
console.log("IN an iframe");
2016-11-22 16:21:56 +08:00
inIframe = true;
(document.getElementById("topbar").style.display="none");
(document.getElementById("map").style.top="0px");
(document.getElementById("results").style.right="50px");
(document.getElementById("results").style.top="10px");
(document.getElementById("results").style.zIndex="1");
(document.getElementById("results").style.height="31px");
(document.getElementById("results").style.paddingTop="6px");
(document.getElementById("bars").style.display="none");
(document.getElementById("menu").style.right="8px");
(document.getElementById("menu").style.borderRadius="6px");
}
else {
console.log("NOT in an iframe")
window.onbeforeunload = function(e) {
return 'Reloading will delete all the local markers, including any drawing on the "drawing" layer';
};
2016-11-22 16:21:56 +08:00
if (!showUserMenu) {
document.getElementById("bars").style.display="none";
}
}
2016-11-22 16:21:56 +08:00
if (!inIframe) {
// Add the fullscreen button
L.control.fullscreen().addTo(map);
2016-11-22 16:21:56 +08:00
// map.on('fullscreenchange', function () {
// if (map.isFullscreen()) { console.log('entered fullscreen') }
// else { console.log('exited fullscreen'); }
// });
2016-11-22 16:21:56 +08:00
// Add the locate my position button
L.easyButton( 'fa-crosshairs fa-lg', function() {
map.locate({setView:true, maxZoom:16});
}, "Locate me").addTo(map);
2016-11-22 16:21:56 +08:00
// Add the measure/ruler button
L.Control.measureControl().addTo(map);
2016-11-22 16:21:56 +08:00
// Create the clear heatmap button
var clrHeat = L.easyButton( '<b>Reset Heatmap</b>', function() {
console.log("Reset heatmap");
2016-11-22 16:21:56 +08:00
heat.setLatLngs([]);
}, "Clears the current heatmap", "bottomright");
}
//else { if (showUserMenu) { menuButton.addTo(map); } }
2016-04-01 18:31:07 +08:00
// Handle the dialog for popup help
var dialog = document.querySelector('dialog');
dialogPolyfill.registerDialog(dialog);
2016-04-01 18:31:07 +08:00
document.getElementById('showHelp').onclick = function() {
dialog.show();
};
document.getElementById('exitHelp').onclick = function() {
dialog.close();
};
var panit = false;
function doPanit() {
panit = !panit;
console.log("Panit set :",panit);
}
var heatAll = false;
function doHeatAll() {
heatAll = !heatAll;
console.log("Heatall set :",heatAll);
}
var lockit = false;
var mb = new L.LatLngBounds([[-120,-360],[120,360]]);
function doLock() {
if (lockit) {
lockit = false;
mb = new L.LatLngBounds([[-120,-360],[120,360]]);
}
else {
lockit = true;
mb = map.getBounds();
window.localStorage.setItem("lastpos",JSON.stringify(map.getCenter()));
window.localStorage.setItem("lastzoom", map.getZoom());
window.localStorage.setItem("lastlayer", baselayername);
//window.localStorage.setItem("clusterat", clusterAt);
window.localStorage.setItem("maxage", maxage);
2016-04-01 18:31:07 +08:00
console.log("Saved :",JSON.stringify(map.getCenter()),map.getZoom(),baselayername);
}
map.setMaxBounds(mb);
console.log("Map bounds lock :",lockit);
}
// Remove old markers
function doTidyUp(l) {
2016-04-01 18:31:07 +08:00
var d = parseInt(Date.now()/1000);
for (var m in markers) {
if ((l && (l == markers[m].lay)) || typeof markers[m].ts != "undefined") {
2016-04-01 18:31:07 +08:00
//console.log(m,markers[m].ts,markers[m]);
if ((l && (l == markers[m].lay)) || (markers[m].hasOwnProperty("ts") && (Number(markers[m].ts) < d) && (markers[m].lay !== "_drawing"))) {
2016-04-01 18:31:07 +08:00
//console.log("STALE :",m);
layers[markers[m].lay].removeLayer(markers[m]);
if (typeof polygons[m] != "undefined") {
layers[markers[m].lay].removeLayer(polygons[m]);
delete polygons[m];
}
if (typeof polygons[m+"_"] != "undefined") {
layers[polygons[m+"_"].lay].removeLayer(polygons[m+"_"]);
delete polygons[m+"_"];
}
2016-04-01 18:31:07 +08:00
delete markers[m];
}
if (l) {
layercontrol.removeLayer(layers[l]);
delete overlays[l];
}
2016-04-01 18:31:07 +08:00
}
}
}
2016-05-08 18:07:09 +08:00
// move the daylight / nighttime boundary (if enabled) every minute
2016-04-01 18:31:07 +08:00
function moveTerminator() { // if terminator line plotted move it every minute
if (layers["_daynight"].getLayers().length > 0) {
layers["_daynight"].clearLayers();
layers["_daynight"].addLayer(L.terminator());
}
}
setInterval( function() {moveTerminator()}, 60000 );
2016-05-08 18:07:09 +08:00
// Call tidyup every {maxage} seconds - default 10 mins
2016-04-01 18:31:07 +08:00
var stale = null;
function setMaxAge() {
maxage = document.getElementById('maxage').value;
if (stale) { clearInterval(stale); }
//if (maxage > 0) {
stale = setInterval( function() {doTidyUp()}, 20000); // every 20 secs
//} //every minute
2016-04-01 18:31:07 +08:00
//console.log("Stale time set :",maxage+"s");
}
setMaxAge();
2016-05-17 19:22:04 +08:00
function setCluster() {
2016-06-27 18:55:30 +08:00
clusterAt = parseInt(document.getElementById('setclus').value) || 0;
console.log("clusterAt set:",clusterAt);
showMapCurrentZoom();
2016-05-17 19:22:04 +08:00
}
setCluster();
2016-04-01 18:31:07 +08:00
// Search for markers with names of ....
function doSearch() {
var value = document.getElementById('search').value;
console.log("Search for :",value);
marks = [];
marksIndex = 0;
for (var key in markers) {
if ( (~(key.toLowerCase()).indexOf(value.toLowerCase())) && (mb.contains(markers[key].getLatLng()))) {
marks.push(markers[key]);
}
}
moveToMarks();
if (lockit) {
document.getElementById('searchRes').innerHTML = "&nbsp;<font color='#ff0'>Found "+marks.length+" results within bounds.</font>";
} else {
document.getElementById('searchRes').innerHTML = "&nbsp;<font color='#ff0'>Found "+marks.length+" results.</font>";
}
}
// Jump to a markers position - centralise it on map
function moveToMarks() {
if (marks.length > marksIndex) {
var m = marks[marksIndex];
map.setView(m.getLatLng(), map.getZoom());
m.openPopup();
marksIndex++;
2016-05-08 18:07:09 +08:00
setTimeout(moveToMarks, 2500);
2016-04-01 18:31:07 +08:00
}
}
function toggleMenu() {
menuOpen = !menuOpen;
if (menuOpen) {
document.getElementById("menu").style.display = 'block';
} else {
document.getElementById("menu").style.display = 'none';
}
}
document.getElementById("menu").style.display = 'none';
//function clearHeat() {
//console.log("reset heatmap");
//heat.setLatLngs([]);
2016-04-01 18:31:07 +08:00
//}
//document.getElementById("heat").style.display = 'none';
var popped = false;
var popmark = null;
map.on('popupopen', function(e) {
popped = true;
popmark = e.popup._source;
popid = e.popup.dname;
});
map.on('popupclose', function(e) {
popped = false;
});
map.on('overlayadd', function(e) {
if (typeof overlays[e.name].bringToFront === "function") {
overlays[e.name].bringToFront();
}
2016-04-01 18:31:07 +08:00
if (e.name == "satellite") {
overlays["satellite"].bringToBack();
}
if (e.name == "heatmap") { // show heatmap button when it's layer is added.
//document.getElementById("heat").style.display = 'block';
clrHeat.addTo(map);
}
if (e.name == "day/night") {
//console.log("add daynight");
layers["_daynight"].addLayer(L.terminator());
}
if (e.name == "drawing") {
map.addControl(drawControl);
2018-02-01 06:54:03 +08:00
overlays["drawing"].bringToFront();
2016-04-01 18:31:07 +08:00
}
//else { console.log("layer add :",e.name); }
ws.send(JSON.stringify({action:"addlayer", name:e.name}));
2016-04-01 18:31:07 +08:00
});
map.on('overlayremove', function(e) {
if (e.name == "heatmap") { // hide heatmap button when it's layer is removed.
//document.getElementById("heat").style.display = 'none';
clrHeat.removeFrom(map);
}
if (e.name == "day/night") {
//console.log("del daynight");
layers["_daynight"].clearLayers();
}
if (e.name == "drawing") {
map.removeControl(drawControl);
}
//else console.log("layer del :",e.name);
ws.send(JSON.stringify({action:"dellayer", name:e.name}));
2016-04-01 18:31:07 +08:00
});
map.on('baselayerchange', function(e) {
//console.log("base layer now :",e.name);
baselayername = e.name;
ws.send(JSON.stringify({action:"layer", name:e.name}));
2016-04-01 18:31:07 +08:00
});
2016-06-27 18:55:30 +08:00
function showMapCurrentZoom() {
console.log("ZOOM:",map.getZoom(),". CLUSTER:",clusterAt);
for (var l in layers) {
if (layers[l].hasOwnProperty("_zoom")) {
if (map.getZoom() >= clusterAt) {
layers[l].disableClustering();
}
else {
layers[l].enableClustering();
}
}
}
2016-04-01 18:31:07 +08:00
setTimeout( function() {
for (var key in markers) {
if (polygons[key]) {
var vis = layers[markers[key].lay].getVisibleParent(markers[key]);
if ((vis) && (vis.hasOwnProperty("lay"))) {
polygons[key].setStyle({opacity:1});
}
else {
polygons[key].setStyle({opacity:0});
}
polygons[key].redraw();
}
}
},750);
2016-06-27 18:55:30 +08:00
}
map.on('zoomend', function() {
showMapCurrentZoom();
2016-04-01 18:31:07 +08:00
});
//map.on('contextmenu', function(e) {
// ws.send(JSON.stringify({action:"rightclick", lat:e.latlng.lat.toFixed(5), lon:e.latlng.lng.toFixed(5)}));
2016-04-01 18:31:07 +08:00
//});
var rightmenuMap = L.popup().setContent("<input type='text' id='rinput' onkeydown='if (event.keyCode == 13) addThing();' placeholder='name (,icon, layer)'/>");
var rclk;
var addThing = function() {
var thing = document.getElementById('rinput').value;
console.log(thing);
ws.send(JSON.stringify({action:"point", lat:rclk.lat.toFixed(5), lon:rclk.lng.toFixed(5), point:thing}));
2016-04-01 18:31:07 +08:00
map.closePopup();
var bits = thing.split(",");
var lay = (bits[2] || "drawing").trim();
var icon = (bits[1] || "circle").trim();
var d = {name:bits[0].trim(),layer:lay,icon:icon,lat:rclk.lat,lon:rclk.lng};
setMarker(d);
map.addLayer(layers[lay]);
}
// allow double right click to zoom out
// single right click opens a message window that sends to the socket.
2016-04-01 18:31:07 +08:00
var rclicked = false;
var rtout = null;
map.on('contextmenu', function(e) {
if (rclicked) {
rclicked = false;
clearTimeout(rtout);
map.zoomOut();
}
else {
rclicked = true;
rtout = setTimeout( function() {
rclicked = false;
rclk = e.latlng;
rightmenuMap.setLatLng(e.latlng);
map.openPopup(rightmenuMap);
setTimeout( function() {
document.getElementById('rinput').focus();
}, 200);
}, 300);
}
});
function onLocationFound(e) {
var radius = e.accuracy;
2016-04-01 18:31:07 +08:00
//L.marker(e.latlng).addTo(map).bindPopup("You are within " + radius + " meters from this point").openPopup();
L.circle(e.latlng, radius, {color:"cyan", weight:4, opacity:0.8, fill:false, clickable:false}).addTo(map);
if (e.hasOwnProperty("heading")) {
var lengthAsDegrees = e.speed * 60 / 110540;
var ya = e.latlng.lat + Math.sin((90-e.heading)/180*Math.PI)*lengthAsDegrees*Math.cos(e.latlng.lng/180*Math.PI);
var xa = e.latlng.lng + Math.cos((90-e.heading)/180*Math.PI)*lengthAsDegrees;
var lla = new L.LatLng(ya,xa);
L.polygon([ e.latlng, lla ], {color:"cyan", weight:3, opacity:0.8, clickable:false}).addTo(map);
}
ws.send(JSON.stringify({action:"point", lat:e.latlng.lat.toFixed(5), lon:e.latlng.lng.toFixed(5), point:"self", bearing:e.heading, speed:(e.speed*3.6 || undefined)}));
2016-04-01 18:31:07 +08:00
}
function onLocationError(e) { console.log(e.message); }
map.on('locationfound', onLocationFound);
map.on('locationerror', onLocationError);
2016-04-01 18:31:07 +08:00
// Add all the base layer maps
// Use this for OSM online maps
var osmUrl='http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
//var osmUrl='http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png';
var osmAttrib='Map data © OpenStreetMap contributors';
var osmg = new L.TileLayer.Grayscale(osmUrl, {attribution:osmAttrib});
2016-04-01 18:31:07 +08:00
basemaps["OSM grey"] = osmg;
var osm = new L.TileLayer(osmUrl, {attribution:osmAttrib});
2016-04-01 18:31:07 +08:00
basemaps["OSM"] = osm;
// Extra Leaflet map layers from http://leaflet-extras.github.io/leaflet-providers/preview/
var Esri_WorldStreetMap = L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', {
2016-11-22 16:21:56 +08:00
attribution: 'Tiles &copy; Esri'
2016-04-01 18:31:07 +08:00
});
basemaps["Esri"] = Esri_WorldStreetMap;
var Esri_WorldImagery = L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
2016-11-22 16:21:56 +08:00
attribution: 'Tiles &copy; Esri'
2016-04-01 18:31:07 +08:00
});
basemaps["Esri Satellite"] = Esri_WorldImagery;
2016-04-01 18:31:07 +08:00
var Esri_WorldShadedRelief = L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}', {
2016-11-22 16:21:56 +08:00
attribution: 'Tiles &copy; Esri',
maxNativeZoom: 13
2016-04-01 18:31:07 +08:00
});
basemaps["Esri Terrain"] = Esri_WorldShadedRelief;
var Esri_OceanBasemap = L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer/tile/{z}/{y}/{x}', {
2016-11-22 16:21:56 +08:00
attribution: 'Tiles &copy; Esri',
maxNativeZoom: 10
2016-04-01 18:31:07 +08:00
});
basemaps["Esri Ocean"] = Esri_OceanBasemap;
var OpenMapSurfer_Roads = L.tileLayer('http://korona.geog.uni-heidelberg.de/tiles/roads/x={x}&y={y}&z={z}', {
maxZoom: 18,
2016-11-22 16:21:56 +08:00
attribution: 'Imagery from <a href="http://giscience.uni-hd.de/">University of Heidelberg</a> &mdash; Map data &copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
2016-04-01 18:31:07 +08:00
});
basemaps["Mapsurfer"] = OpenMapSurfer_Roads;
2016-07-24 18:57:22 +08:00
// var MapQuestOpen_OSM = L.tileLayer('http://otile{s}.mqcdn.com/tiles/1.0.0/{type}/{z}/{x}/{y}.{ext}', {
// type: 'map',
// ext: 'jpg',
// attribution: 'Tiles Courtesy of <a href="http://www.mapquest.com/">MapQuest</a> &mdash; Map data &copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
// subdomains: '1234',
// maxNativeZoom: 17
// });
//basemaps["MapQuest OSM"] = MapQuestOpen_OSM;
2016-04-01 18:31:07 +08:00
var Esri_NatGeoWorldMap = L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}', {
2016-11-22 16:21:56 +08:00
attribution: 'Tiles &copy; Esri',
maxNativeZoom: 12
2016-04-01 18:31:07 +08:00
});
basemaps["Nat Geo"] = Esri_NatGeoWorldMap;
var NLS_OS_opendata = L.tileLayer('http://geo.nls.uk/maps/opendata/{z}/{x}/{y}.png', {
attribution: '<a href="http://geo.nls.uk/maps/">National Library of Scotland Historic Maps</a>',
bounds: [[49.6, -12], [61.7, 3]],
minZoom: 1,
maxNativeZoom: 16,
2016-04-01 18:31:07 +08:00
subdomains: '0123'
});
basemaps["UK OS Opendata"] = NLS_OS_opendata;
var NLS_OS_1919_1947 = L.tileLayer( 'http://nls-{s}.tileserver.com/nls/{z}/{x}/{y}.jpg', {
2016-11-22 16:21:56 +08:00
attribution: 'Historical Maps Layer, from <a href="http://maps.nls.uk/projects/api/">NLS Maps</a>',
bounds: [[49.6, -12], [61.7, 3]],
minZoom: 1,
maxZoom: 18,
subdomains: '0123'
});
basemaps["UK OS 1919-47"] = NLS_OS_1919_1947;
2016-04-01 18:31:07 +08:00
//var NLS_OS_1900 = L.tileLayer('http://nls-{s}.tileserver.com/NLS_API/{z}/{x}/{y}.jpg', {
//attribution: '<a href="http://geo.nls.uk/maps/">National Library of Scotland Historic Maps</a>',
//bounds: [[49.6, -12], [61.7, 3]],
//minZoom: 1,
//maxZoom: 18,
//subdomains: '0123'
2016-04-01 18:31:07 +08:00
//});
//basemaps["UK OS 1900"] = NLS_OS_1900;
2016-07-24 18:57:22 +08:00
//var CartoPos = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
// attribution: '&copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, &copy; <a href="http://cartodb.com/attributions">CartoDB</a>'
//});
2016-04-01 18:31:07 +08:00
//basemaps["CartoDB Light"] = CartoPos;
2016-05-08 18:07:09 +08:00
// Terrain map of US only
2016-04-01 18:31:07 +08:00
var usterrainmap = new L.StamenTileLayer('terrain');
basemaps["US terrain"] = usterrainmap;
// Nice watercolour based maps by Stamen Design
var watermap = new L.StamenTileLayer('watercolor');
basemaps["Watercolor"] = watermap;
2016-04-01 18:31:07 +08:00
// Now add the overlays
// Add the day/night overlay
layers["_daynight"] = new L.LayerGroup();
overlays["day/night"] = layers["_daynight"];
// Add the heatmap layer
var heat = L.heatLayer([], {radius:60, gradient:{0.2:'blue', 0.4:'lime', 0.6:'red', 0.8:'yellow', 1:'white'}});
layers["_heat"] = new L.LayerGroup().addLayer(heat);
overlays["heatmap"] = layers["_heat"];
2016-04-01 18:31:07 +08:00
// Add the drawing layer for fun...
layers["_drawing"] = new L.FeatureGroup();
overlays["drawing"] = layers["_drawing"];
2016-04-01 18:31:07 +08:00
var drawControl = new L.Control.Draw({
draw: {
polyline: { shapeOptions: { clickable:false } },
marker: false,
circle: false,
//circle: { shapeOptions: { clickable:false } },
rectangle: { shapeOptions: { clickable:true } },
polygon: { shapeOptions: { clickable:true } },
},
edit: {
featureGroup: layers["_drawing"],
2016-04-01 18:31:07 +08:00
remove: true,
edit: false
}
});
map.on('draw:created', function (e) {
var type = e.layerType;
var layer = e.layer;
//console.log(type, layer._latlngs);
//console.log(JSON.stringify(layer.toGeoJSON()));
ws.send(JSON.stringify({action:"draw",type:type, points:layer._latlngs}));
layers["_drawing"].addLayer(layer);
2016-04-01 18:31:07 +08:00
});
// // Add the roads overlay
// overlays["roads"] = L.tileLayer('http://otile{s}.mqcdn.com/tiles/1.0.0/{type}/{z}/{x}/{y}.{ext}', {
// type: 'hyb',
// ext: 'png',
// attribution: '<a href="http://www.mapquest.com/">MapQuest</a> &mdash; Map data &copy; <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
// subdomains: '1234',
// opacity: 0.9
// });
//
// overlays["rain"] = L.tileLayer('http://{s}.tile.openweathermap.org/map/rain/{z}/{x}/{y}.png', {
// maxZoom: 18,
// attribution: 'Map data &copy; <a href="http://openweathermap.org">OpenWeatherMap</a>',
// opacity: 0.5
// });
//
// overlays["pressure"] = L.tileLayer('http://{s}.tile.openweathermap.org/map/pressure_cntr/{z}/{x}/{y}.png', {
// maxZoom: 18,
// attribution: 'Map data &copy; <a href="http://openweathermap.org">OpenWeatherMap</a>',
// opacity: 0.5
// });
//
// overlays["weather"] = L.OWM.current({interval:30, minZoom:8, appId:"9ae11d4f6f9bed61f32fc061f715cc71"});
2016-04-01 18:31:07 +08:00
2016-10-19 05:37:07 +08:00
overlays["buildings"] = new OSMBuildings(map).load();
map.removeLayer(overlays["buildings"]);
2016-04-01 18:31:07 +08:00
// Add the shipping navigation markers
2016-05-08 18:07:09 +08:00
//var OpenSeaMap = L.tileLayer('http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png', {
// attribution: 'Map data: &copy; <a href="http://www.openseamap.org">OpenSeaMap</a> contributors'
//});
2016-04-01 18:31:07 +08:00
//overlays["ship nav"] = OpenSeaMap;
if ( window.localStorage.hasOwnProperty("lastlayer") ) {
if ( basemaps[window.localStorage.getItem("lastlayer")] ) {
baselayername = window.localStorage.getItem("lastlayer");
}
}
basemaps[baselayername].addTo(map);
// Layer control based on select box rather than radio buttons.
//var layercontrol = L.control.selectLayers(basemaps, overlays).addTo(map);
layercontrol = L.control.layers(basemaps, overlays);
// Add the layers control widget
if (!inIframe) { layercontrol.addTo(map); }
else { showLayerMenu = false;}
2016-04-01 18:31:07 +08:00
// Delete a marker (and notify websocket)
var delMarker = function(dname) {
//console.log("Deleting",dname);
if (typeof polygons[dname] != "undefined") {
layers[polygons[dname].lay].removeLayer(polygons[dname]);
ws.send(JSON.stringify({action:"delete", name:dname}));
2016-04-01 18:31:07 +08:00
delete polygons[dname];
}
if (typeof markers[dname] != "undefined") {
ws.send(JSON.stringify({action:"delete", name:dname}));
2016-04-01 18:31:07 +08:00
layers[markers[dname].lay].removeLayer(markers[dname]);
delete markers[dname];
}
map.closePopup();
}
// the MAIN add something to map function
function setMarker(data) {
//console.log(typeof data, data);
2016-04-01 18:31:07 +08:00
var ll;
var lli = null;
2016-04-01 18:31:07 +08:00
var stay = popped;
var opt = {};
opt.color = data.color || "#910000";
opt.fillColor = data.fillColor || "#910000";
opt.stroke = (data.hasOwnProperty("stroke")) ? data.stroke : true;
opt.weight = data.weight || 2;
opt.opacity = data.opacity || 1;
opt.fillOpacity = data.fillOpacity || 0.2;
opt.clickable = (data.hasOwnProperty("clickable")) ? data.clickable : false;
opt.fill = (data.hasOwnProperty("fill")) ? data.fill : true;
2016-04-01 18:31:07 +08:00
var lay = data.layer || "unknown";
2016-04-01 18:31:07 +08:00
if (typeof layers[lay] == "undefined") { // add layer if if doesn't exist
//layers[lay] = new L.LayerGroup().addTo(map);
2016-06-27 18:55:30 +08:00
layers[lay] = new L.MarkerClusterGroup({
maxClusterRadius:50,
spiderfyDistanceMultiplier:1.8,
disableClusteringAtZoom:clusterAt,
//zoomToBoundsOnClick:false
}).addTo(map);
2016-04-01 18:31:07 +08:00
overlays[lay] = layers[lay];
//if (!inIframe) {
2016-11-22 16:21:56 +08:00
layercontrol.addOverlay(layers[lay],lay);
//}
2016-04-01 18:31:07 +08:00
}
if (typeof markers[data.name] != "undefined") { layers[lay].removeLayer(markers[data.name]); }
if (typeof polygons[data.name] != "undefined") { layers[lay].removeLayer(polygons[data.name]); }
if (data.deleted) { // remove markers we are told to
delMarker(data.name);
}
else if (data.hasOwnProperty("line") && Array.isArray(data.line)) {
delete opt.fill;
if (!data.hasOwnProperty("weight")) { opt.weight = 3; } //Standard settings different for lines
if (!data.hasOwnProperty("opacity")) { opt.opacity = 0.8; }
var polyln = L.polyline(data.line, opt);
if (opt.clickable) { polyln.bindPopup(data.name); }
polygons[data.name] = polyln;
polygons[data.name].lay = lay;
layers[lay].addLayer(polygons[data.name]);
}
else if (data.hasOwnProperty("area") && Array.isArray(data.area)) {
var polyarea = L.polygon(data.area, opt);
if (opt.clickable) { polyarea.bindPopup(data.name); }
polygons[data.name] = polyarea;
polygons[data.name].lay = lay;
layers[lay].addLayer(polygons[data.name]);
2016-04-01 18:31:07 +08:00
}
2016-05-08 18:07:09 +08:00
else if (data.hasOwnProperty("sdlat") && data.hasOwnProperty("sdlon")) {
if (!data.hasOwnProperty("iconColor")) { opt.color = "blue"; } //different standard Color Settings
if (!data.hasOwnProperty("fillColor")) { opt.fillColor = "blue"; }
delete opt.clickable;
var ellipse = L.ellipse(new L.LatLng((data.lat*1), (data.lon*1)), [200000*data.sdlon*Math.cos(data.lat*Math.PI/180), 200000*data.sdlat], 0, opt);
if (data.clickable != false) {
ellipse.on('click', function(e) {
var popup = L.popup()
.setLatLng(new L.LatLng((data.lat*1), (data.lon*1)))
.setContent('<p><b>'+data.name+'</b><br/>lat : '+data.lat+'<br/>lon : '+data.lon+'</p>')
.openOn(map);
});
}
2016-05-08 18:07:09 +08:00
polygons[data.name] = ellipse;
polygons[data.name].lay = lay;
2016-05-08 18:07:09 +08:00
layers[lay].addLayer(ellipse);
}
2016-04-01 18:31:07 +08:00
else {
if (data.hasOwnProperty("radius")) {
if (data.hasOwnProperty("lat") && data.hasOwnProperty("lon")) {
var polycirc = L.circle(new L.LatLng((data.lat*1), (data.lon*1)), data.radius*1, opt);
if (opt.clickable) { polycirc.bindPopup(data.name); }
polygons[data.name] = polycirc;
polygons[data.name].lay = lay;
layers[lay].addLayer(polycirc);
}
}
2016-04-01 18:31:07 +08:00
//console.log("handling",data.name);
if (typeof data.coordinates == "object") { ll = new L.LatLng(data.coordinates[1],data.coordinates[0]); }
else if (data.hasOwnProperty("position") && data.position.hasOwnProperty("lat") && data.position.hasOwnProperty("lon")) {
data.lat = data.position.lat*1;
data.lon = data.position.lon*1;
data.alt = data.position.alt;
if (parseFloat(data.position.alt) == data.position.alt) { data.alt = data.position.alt + " m"; }
delete data.position;
ll = new L.LatLng((data.lat*1), (data.lon*1));
}
else if (data.hasOwnProperty("lat") && data.hasOwnProperty("lon")) { ll = new L.LatLng((data.lat*1), (data.lon*1)); }
else if (data.hasOwnProperty("latitude") && data.hasOwnProperty("longitude")) { ll = new L.LatLng((data.latitude*1), (data.longitude*1)); }
else { console.log("No location:",data); return; }
// Adding new L.LatLng object (lli) when optional intensity value is defined. Only for use in heatmap layer
if (typeof data.coordinates == "object") { lli = new L.LatLng(data.coordinates[2],data.coordinates[1],data.coordinates[0]); }
else if (data.hasOwnProperty("lat") && data.hasOwnProperty("lon") && data.hasOwnProperty("intensity")) { lli = new L.LatLng((data.lat*1), (data.lon*1), (data.intensity*1)); }
else if (data.hasOwnProperty("latitude") && data.hasOwnProperty("longitude") && data.hasOwnProperty("intensity")) { lli = new L.LatLng((data.latitude*1), (data.longitude*1), (data.intensity*1)); }
else { lli = ll }
2016-04-01 18:31:07 +08:00
var words="<b>"+data.name+"</b><br/>";
// Create the icons... handle plane, car, ship, wind, earthquake as specials
var marker, mymarker;
var icon, q;
//console.log("ICON",data.icon);
2016-04-01 18:31:07 +08:00
if (data.icon === "ship") {
marker = L.boatMarker(ll, {
title: data.name,
color: (data.iconColor || "blue")
});
marker.setHeading(data.hdg||data.bearing);
q = 'http://www.bing.com/images/search?q='+data.icon+'%20%2B"'+encodeURIComponent(data.name)+'"';
2016-04-01 18:31:07 +08:00
words += '<a href=\''+q+'\' target="_thingpic">Pictures</a><br>';
}
else if (data.icon === "plane") {
data.iconColor = data.iconColor || "black";
if (data.hasOwnProperty("squawk")) { data.iconColor = "red"; }
icon = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="310px" height="310px" viewBox="0 0 310 310">';
icon += '<path d="M134.875,19.74c0.04-22.771,34.363-22.771,34.34,0.642v95.563L303,196.354v35.306l-133.144-43.821v71.424l30.813,24.072v27.923l-47.501-14.764l-47.501,14.764v-27.923l30.491-24.072v-71.424L3,231.66v-35.306l131.875-80.409V19.74z" fill="'+data.iconColor+'"/></svg>';
2016-04-01 18:31:07 +08:00
var svgplane = "data:image/svg+xml;base64," + btoa(icon);
var dir = data.hdg || data.bearing;
myMarker = L.divIcon({
2016-04-01 18:31:07 +08:00
className:"planeicon",
iconAnchor: [16, 16],
html:'<img src="'+svgplane+'" style="width:32px; height:32px; -webkit-transform:rotate('+dir+'deg); -moz-transform:rotate('+dir+'deg);"/>',
2016-04-01 18:31:07 +08:00
});
marker = L.marker(ll, {title:data.name, icon:myMarker});
//q = 'http://www.bing.com/images/search?q='+data.icon+'%20'+encodeURIComponent(data.name);
//words += '<a href=\''+q+'\' target="_thingpic">Pictures</a><br>';
2016-04-01 18:31:07 +08:00
}
2017-06-28 16:01:03 +08:00
else if (data.icon === "uav") {
data.iconColor = data.iconColor || "black";
if (data.hasOwnProperty("squawk")) { data.iconColor = "red"; }
icon = '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">';
icon+= '<path d="M62 82h-8V64h36c0-5-4-9-9-9H54v-8c0-3 4-5 4-11.1 0-4.4-3.6-8-8-8-4.4 0-8 3.6-8 8 0 5.1 4 8.1 4 11.1V55h-27c-5 0-9 4-9 9h36v18H38c-2.4 0-5 2.3-5 5L50 92l17-5C67 84.3 64.4 82 62 82z" fill="'+data.iconColor+'"/></svg>';
var svguav = "data:image/svg+xml;base64," + btoa(icon);
var dir = data.hdg || data.bearing;
myMarker = L.divIcon({
className:"uavicon",
iconAnchor: [16, 16],
html:'<img src="'+svguav+'" style="width:32px; height:32px; -webkit-transform:rotate('+dir+'deg); -moz-transform:rotate('+dir+'deg);"/>',
});
marker = L.marker(ll, {title:data.name, icon:myMarker});
}
2016-04-18 23:57:31 +08:00
else if (data.icon === "car") {
data.iconColor = data.iconColor || "black";
icon = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="47px" height="47px" viewBox="0 0 47 47">';
icon += '<path d="M29.395,0H17.636c-3.117,0-5.643,3.467-5.643,6.584v34.804c0,3.116,2.526,5.644,5.643,5.644h11.759 c3.116,0,5.644-2.527,5.644-5.644V6.584C35.037,3.467,32.511,0,29.395,0z M34.05,14.188v11.665l-2.729,0.351v-4.806L34.05,14.188z M32.618,10.773c-1.016,3.9-2.219,8.51-2.219,8.51H16.631l-2.222-8.51C14.41,10.773,23.293,7.755,32.618,10.773z M15.741,21.713 v4.492l-2.73-0.349V14.502L15.741,21.713z M13.011,37.938V27.579l2.73,0.343v8.196L13.011,37.938z M14.568,40.882l2.218-3.336 h13.771l2.219,3.336H14.568z M31.321,35.805v-7.872l2.729-0.355v10.048L31.321,35.805z" fill="'+data.iconColor+'"/></svg>';
2016-04-18 23:57:31 +08:00
var svgcar = "data:image/svg+xml;base64," + btoa(icon);
var dir = data.hdg || data.bearing;
myMarker = L.divIcon({
2016-04-18 23:57:31 +08:00
className:"caricon",
iconAnchor: [16, 16],
html:'<img src="'+svgcar+'" style="width:32px; height:32px; -webkit-transform:rotate('+dir+'deg); -moz-transform:rotate('+dir+'deg);"/>',
});
marker = L.marker(ll, {title:data.name, icon:myMarker});
}
else if (data.icon === "arrow") {
data.iconColor = data.iconColor || "black";
icon = '<svg xmlns="http://www.w3.org/2000/svg" width="32px" height="32px" viewBox="0 0 32 32">';
icon += '<path d="m16.2 0.6l-10.9 31 10.7-11.1 10.5 11.1 -10.3-31z" fill="'+data.iconColor+'"/></svg>';
var svgarrow = "data:image/svg+xml;base64," + btoa(icon);
var dir = data.hdg || data.bearing;
myMarker = L.divIcon({
className:"arrowicon",
iconAnchor: [16, 16],
html:"'<img src='"+svgarrow+"' style='width:32px; height:32px; -webkit-transform:translate(0px,-16px) rotate("+dir+"deg); -moz-transform:translate(0px,-16px) rotate("+dir+"deg);'/>",
2016-04-18 23:57:31 +08:00
});
marker = L.marker(ll, {title:data.name, icon:myMarker});
}
else if (data.icon === "wind") {
data.iconColor = data.iconColor || "black";
icon = '<svg xmlns="http://www.w3.org/2000/svg" width="32px" height="32px" viewBox="0 0 32 32">';
icon += '<path d="M16.7 31.7l7-6.9c0.4-0.4 0.4-1 0-1.4 -0.4-0.4-1-0.4-1.4 0l-5.3 5.2V17.3l6.7-6.6c0.2-0.2 0.3-0.5 0.3-0.7v-9c0-0.9-1.1-1.3-1.7-0.7l-6.3 6.2L9.7 0.3C9.1-0.3 8 0.1 8 1.1v8.8c0 0.3 0.1 0.6 0.3 0.8l6.7 6.6v11.3l-5.3-5.2c-0.4-0.4-1-0.4-1.4 0 -0.4 0.4-0.4 1 0 1.4l7 6.9c0.2 0.2 0.5 0.3 0.7 0.3C16.2 32 16.5 31.9 16.7 31.7zM10 9.6V3.4l5 4.9v6.2L10 9.6zM17 8.3l5-4.9v6.2l-5 4.9V8.3z" fill="'+data.iconColor+'"/></svg>';
var svgwind = "data:image/svg+xml;base64," + btoa(icon);
var dir = data.hdg || data.bearing;
myMarker = L.divIcon({
className:"windicon",
iconAnchor: [16, 16],
html:'<img src="'+svgwind+'" style="width:32px; height:32px; -webkit-transform:rotate('+dir+'deg); -moz-transform:rotate('+dir+'deg);"/>',
});
marker = L.marker(ll, {title:data.name, icon:myMarker});
2016-04-18 23:57:31 +08:00
}
else if (data.icon === "satellite") {
data.iconColor = data.iconColor || "black";
icon = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 100 100">';
icon += '<polygon points="38.17 39.4 45.24 32.33 43.34 27.92 24.21 8.78 14.59 18.4 33.72 37.53" fill="'+data.iconColor+'"/>';
icon += '<path d="M69.22 44.57L54.38 29.73c-1.1-1.1-2.91-1.1-4.01 0L35.53 44.57c-1.1 1.1-1.1 2.91 0 4.01l14.84 14.84c1.1 1.1 2.91 1.1 4.01 0l14.84-14.84C70.32 47.47 70.32 45.67 69.22 44.57z" fill="'+data.iconColor+'"/>';
icon += '<polygon points="71.04 55.61 66.58 53.75 59.52 60.82 61.42 65.23 80.55 84.36 90.17 74.75" fill="'+data.iconColor+'"/>';
icon += '<path d="M28.08 55.26l-6.05 0.59C23.11 68.13 32.78 77.94 45 79.22l0.59-6.05C36.26 72.15 28.89 64.66 28.08 55.26z" fill="'+data.iconColor+'"/>';
icon += '<path d="M15.88 56.54L9.83 57.13c1.67 18.06 16.03 32.43 34.08 34.09l0.59-6.04C29.34 83.76 17.29 71.71 15.88 56.54z" fill="'+data.iconColor+'"/>';
icon += '</svg>';
var svglocate = "data:image/svg+xml;base64," + btoa(icon);
myMarker = L.divIcon({
className:"satelliteicon",
iconAnchor: [16, 16],
html:'<img src="'+svglocate+'" style="width:32px; height:32px;"/>',
});
marker = L.marker(ll, {title:data.name, icon:myMarker});
}
2016-06-03 16:11:48 +08:00
else if (data.icon === "locate") {
data.iconColor = data.iconColor || "cyan";
icon = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="468px" height="468px" viewBox="0 0 468 468">';
icon += '<polygon points="32 32 104 32 104 0 0 0 0 104 32 104" fill="'+data.iconColor+'"/>';
icon += '<polygon points="468 0 364 0 364 32 436 32 436 104 468 104" fill="'+data.iconColor+'"/>';
icon += '<polygon points="0 468 104 468 104 436 32 436 32 364 0 364" fill="'+data.iconColor+'"/>';
icon += '<polygon points="436 436 364 436 364 468 468 468 468 364 436 364" fill="'+data.iconColor+'"/>';
//icon += '<circle cx="234" cy="234" r="22" fill="'+data.iconColor+'"/>';
icon += '</svg>';
var svglocate = "data:image/svg+xml;base64," + btoa(icon);
myMarker = L.divIcon({
className:"locateicon",
iconAnchor: [16, 16],
html:'<img src="'+svglocate+'" style="width:32px; height:32px;"/>',
});
marker = L.marker(ll, {title:data.name, icon:myMarker});
}
2016-04-01 18:31:07 +08:00
else if (data.icon === "friend") {
marker = L.marker(ll, { icon: L.divIcon({ className: 'circle f', iconSize: [20, 12] }), title: data.name });
}
else if (data.icon === "hostile") {
marker = L.marker(ll, { icon: L.divIcon({ className: 'circle h', iconSize: [16, 16] }), title: data.name });
}
else if (data.icon === "neutral") {
marker = L.marker(ll, { icon: L.divIcon({ className: 'circle n', iconSize: [16, 16] }), title: data.name });
}
else if (data.icon === "unknown") {
marker = L.marker(ll, { icon: L.divIcon({ className: 'circle', iconSize: [16, 16] }), title: data.name });
}
else if (data.icon === "danger") {
console.log("danger will robinson");
marker = L.marker(ll, { icon: L.divIcon({ className: 'up-triangle' }), title: data.name });
}
else if (data.icon === "earthquake") {
marker = L.marker(ll, { icon: L.divIcon({ className: 'circle e', iconSize: [data.mag*5, data.mag*5] }), title: data.name });
}
2017-09-06 21:39:14 +08:00
else if (data.icon && (data.icon.substr(0,3) === "fa-")) {
var col = data.iconColor || "#910000";
myMarker = L.divIcon({
className:"faicon",
html: '<center><i class="fa fa-fw fa-2x '+data.icon+'" style="color:'+col+'"></i></center>',
iconSize: [30, 30]
});
marker = L.marker(ll, {title:data.name, icon:myMarker});
}
2016-04-01 18:31:07 +08:00
else {
myMarker = L.VectorMarkers.icon({
2016-04-01 18:31:07 +08:00
icon: data.icon || "circle",
markerColor: (data.iconColor || "#910000"),
prefix: 'fa',
iconColor: 'white'
});
marker = L.marker(ll, {title:data.name, icon:myMarker});
2016-04-01 18:31:07 +08:00
}
2017-09-26 22:54:36 +08:00
// var createLabelIcon = function(labelText) {
// return L.marker(new L.LatLng(51.05, -1.35), {icon:L.divIcon({ html:labelText })});
// }
2016-04-01 18:31:07 +08:00
// remove icon from list of properties, then add all others to popup
if (data.hasOwnProperty("icon") || (!data.hasOwnProperty("radius"))) {
if (data.hasOwnProperty("icon")) { delete data.icon; }
if (data.hasOwnProperty("iconColor")) { delete data.iconColor; }
if (data.hasOwnProperty("photourl")) {
words += "<img src=\"" + data.photourl + "\" style=\"width:100%; margin-top:10px;\">";
delete data.photourl;
}
if (data.hasOwnProperty("photoUrl")) {
words += "<img src=\"" + data.photoUrl + "\" style=\"width:100%; margin-top:10px;\">";
delete data.photoUrl;
}
2017-12-14 06:16:09 +08:00
if (data.hasOwnProperty("videoUrl")) {
words += '<video controls autoplay><source src="'+data.videoUrl+'" type="video/mp4">Your browser does not support the video tag.</video>';
delete data.videoUrl;
}
if (data.hasOwnProperty("ttl")) { // save expiry time for this marker
if (data.ttl != 0) {
marker.ts = parseInt(Date.now()/1000) + Number(data.ttl);
}
delete data.ttl;
}
else if (maxage != 0) {
marker.ts = parseInt(Date.now()/1000) + Number(maxage);
}
if (data.hasOwnProperty("weblink")) {
if (typeof data.weblink === "string") {
words += "<b><a href='"+ data.weblink + "' target='_new'>more information...</a></b><br/>";
} else {
words += "<b><a href='"+ data.weblink.url + "' target='_new'>" + data.weblink.name + "</a></b><br/>";
}
delete data.weblink;
}
for (var i in data) {
if ((i != "name") && (i != "length")) {
if (typeof data[i] === "object") {
words += i +" : "+JSON.stringify(data[i])+"<br/>";
} else {
words += i +" : "+data[i]+"<br/>";
}
2016-04-01 18:31:07 +08:00
}
}
2017-12-14 06:16:09 +08:00
marker.bindPopup(words, {keepInView:true, minWidth:348});
marker._popup.dname = data.name;
marker.lay = lay; // and the layer it is on
var rightmenuMarker = L.popup().setContent("<b>"+data.name+"</b><br/><button onclick='delMarker(\""+data.name+"\");'>Delete</button>");
marker.on('contextmenu', function(e) {
rightmenuMarker.setLatLng(e.latlng);
map.openPopup(rightmenuMarker);
});
2017-04-20 20:44:19 +08:00
if ((data.addtoheatmap !== "false") || (!data.hasOwnProperty("addtoheatmap"))) { // Added to give ability to control if points from active layer contribute to heatmap
if (heatAll || map.hasLayer(layers[lay])) { heat.addLatLng(lli); }
}
markers[data.name] = marker;
layers[lay].addLayer(marker);
}
2016-04-01 18:31:07 +08:00
popped = stay;
if (popped) {
if (popid == data.name) { marker.openPopup(); }
else { popmark.openPopup(); }
}
if ((data.hdg != null) && (data.bearing == null)) { data.bearing = data.hdg; delete data.hdg; }
if (data.bearing != null) { // if there is a heading
if (data.speed != null) { data.length = data.speed * 50; } // and a speed
if (data.length != null) {
if (polygons[data.name] != null) { map.removeLayer(polygons[data.name]); }
var x = ll.lng; // X coordinate
var y = ll.lat * 1; // Y coordinate
var ll1 = ll;
2016-04-01 18:31:07 +08:00
var angle = data.bearing * 1;
var lengthAsDegrees = data.length / 110540; // metres in a degree..ish
var polygon = null;
if (data.accuracy != null) {
data.accuracy = Number(data.accuracy);
var y2 = y + Math.sin((90-angle+data.accuracy)/180*Math.PI)*lengthAsDegrees*Math.cos(y/180*Math.PI);
var x2 = x + Math.cos((90-angle+data.accuracy)/180*Math.PI)*lengthAsDegrees;
var ll2 = new L.LatLng(y2,x2);
var y3 = y + Math.sin((90-angle-data.accuracy)/180*Math.PI)*lengthAsDegrees*Math.cos(y/180*Math.PI);
var x3 = x + Math.cos((90-angle-data.accuracy)/180*Math.PI)*lengthAsDegrees;
var ll3 = new L.LatLng(y3,x3);
polygon = L.polygon([ ll1, ll2, ll3 ], {weight:2, color:'#f30', fillOpacity:0.06, clickable:false});
2016-04-01 18:31:07 +08:00
} else {
var ya = y + Math.sin((90-angle)/180*Math.PI)*lengthAsDegrees*Math.cos(y/180*Math.PI);
var xa = x + Math.cos((90-angle)/180*Math.PI)*lengthAsDegrees;
var lla = new L.LatLng(ya,xa);
polygon = L.polygon([ ll1, lla ], {weight:2, color:'#f30', clickable:false});
2016-04-01 18:31:07 +08:00
}
if (typeof layers[lay].getVisibleParent === 'function') {
var vis = layers[lay].getVisibleParent(marker);
if ((polygon !== null) && (vis !== null) && (!vis.hasOwnProperty("lay"))) {
polygon.setStyle({opacity:0});
}
polygons[data.name] = polygon;
polygons[data.name].lay = lay;
2016-04-01 18:31:07 +08:00
layers[lay].addLayer(polygon);
}
}
}
if (panit) {
map.setView(ll,map.getZoom());
}
}
}
function doCommand(cmd) {
2017-11-20 06:35:30 +08:00
//console.log("COMMAND",cmd);
// ignore server side initial command if client position already saved.
if (cmd.hasOwnProperty("init") && initialposition) {
return;
}
if (cmd.hasOwnProperty("clear")) {
doTidyUp(cmd.clear);
}
2016-11-22 16:21:56 +08:00
if (cmd.hasOwnProperty("panit")) {
if (cmd.panit === "true") { panit = true; }
else { panit = false; }
}
if (cmd.hasOwnProperty("showmenu")) {
if ((cmd.showmenu === "hide") && (showUserMenu === true)) {
showUserMenu = false;
map.removeControl(menuButton);
}
else if ((cmd.showmenu === "show") && (showUserMenu === false)) {
showUserMenu = true;
map.addControl(menuButton);
}
}
if (cmd.hasOwnProperty("showlayers")) {
if ((cmd.showlayers === "hide") && (showLayerMenu === true)) {
showLayerMenu = false;
layercontrol.removeFrom(map);
}
else if ((cmd.showlayers === "show") && (showLayerMenu === false)) {
showLayerMenu = true;
layercontrol = L.control.layers(basemaps, overlays).addTo(map);
}
}
var existsalready = false;
// Add a new base map layer
2016-04-01 18:31:07 +08:00
if (cmd.map && cmd.map.hasOwnProperty("name") && cmd.map.hasOwnProperty("url") && cmd.map.hasOwnProperty("opt")) {
if (basemaps.hasOwnProperty(cmd.map.name)) { existsalready = true; }
2017-04-20 20:44:19 +08:00
if (cmd.map.hasOwnProperty("wms")) { // special case for wms
console.log("New WMS:",cmd.map.name);
if (cmd.map.wms === "grey") {
basemaps[cmd.map.name] = L.tileLayer.graywms(cmd.map.url, cmd.map.opt);
}
else {
basemaps[cmd.map.name] = L.tileLayer.wms(cmd.map.url, cmd.map.opt);
}
2017-04-20 20:44:19 +08:00
}
else {
console.log("New Map:",cmd.map.name);
basemaps[cmd.map.name] = L.tileLayer(cmd.map.url, cmd.map.opt);
}
//if (!existsalready && !inIframe) {
if (!existsalready) {
layercontrol.addBaseLayer(basemaps[cmd.map.name],cmd.map.name);
}
}
2018-02-01 06:54:03 +08:00
// Add a new geojson overlay layer
2018-01-03 16:31:02 +08:00
if (cmd.map && cmd.map.hasOwnProperty("overlay") && cmd.map.hasOwnProperty("geojson") ) {
2018-02-01 06:54:03 +08:00
if (overlays.hasOwnProperty(cmd.map.overlay)) {
map.removeLayer(overlays[cmd.map.overlay]);
existsalready = true;
}
2018-01-03 16:31:02 +08:00
overlays[cmd.map.overlay] = L.geoJson(cmd.map.geojson,cmd.map.opt);
if (!existsalready) {
layercontrol.addOverlay(overlays[cmd.map.overlay],cmd.map.overlay);
}
overlays[cmd.map.overlay].addTo(map);
}
2018-02-01 06:54:03 +08:00
// Add a new overlay layer
if (cmd.map && cmd.map.hasOwnProperty("overlay") && cmd.map.hasOwnProperty("url") && cmd.map.hasOwnProperty("opt")) {
console.log("New overlay:",cmd.map.overlay);
if (overlays.hasOwnProperty(cmd.map.overlay)) { existsalready = true; }
2017-04-20 20:44:19 +08:00
if (cmd.map.hasOwnProperty("wms")) { // special case for wms
if (cmd.map.wms === "grey") {
overlays[cmd.map.name] = L.tileLayer.graywms(cmd.map.url, cmd.map.opt);
}
else {
overlays[cmd.map.name] = L.tileLayer.wms(cmd.map.url, cmd.map.opt);
}
2017-04-20 20:44:19 +08:00
}
else if (cmd.map.hasOwnProperty("bounds")) { //Image Overlay in the bounds specified (2D Array)
if (cmd.map.bounds.length === 2 && cmd.map.bounds[0].length === 2 && cmd.map.bounds[1].length === 2) {
overlays[cmd.map.overlay] = new L.imageOverlay(cmd.map.url, L.latLngBounds(cmd.map.bounds), cmd.map.opt);
}
}
2017-04-20 20:44:19 +08:00
else {
overlays[cmd.map.overlay] = L.tileLayer(cmd.map.url, cmd.map.opt);
}
//if (!existsalready && !inIframe) {
if (!existsalready) {
layercontrol.addOverlay(overlays[cmd.map.overlay],cmd.map.overlay);
}
overlays[cmd.map.overlay].addTo(map);
2016-04-01 18:31:07 +08:00
}
// Swap a base layer
2016-04-01 18:31:07 +08:00
if (cmd.layer && basemaps.hasOwnProperty(cmd.layer)) {
map.removeLayer(basemaps[baselayername]);
baselayername = cmd.layer;
basemaps[baselayername].addTo(map);
}
// Add an overlay
if (cmd.layer && overlays.hasOwnProperty(cmd.layer)) {
overlays[cmd.layer].addTo(map);
2016-04-01 18:31:07 +08:00
}
var clat = map.getCenter().lat;
var clon = map.getCenter().lng;
var czoom = map.getZoom();
if (cmd.hasOwnProperty("lat")) { clat = cmd.lat; }
if (cmd.hasOwnProperty("lon")) { clon = cmd.lon; }
if (cmd.hasOwnProperty("zoom")) { czoom = cmd.zoom; }
if (cmd.hasOwnProperty("cluster")) {
//clusterAt = cmd.cluster;
document.getElementById("setclus").value = cmd.cluster;
setCluster();
}
if (cmd.hasOwnProperty("maxage")) {
document.getElementById("maxage").value = cmd.maxage;
setMaxAge();
}
if (cmd.hasOwnProperty("heatmap")) {
heat.setOptions(cmd.heatmap);
heat.redraw();
}
2016-04-01 18:31:07 +08:00
map.setView([clat,clon],czoom);
}
</script>
</body>
</html>