a3ee1beba9
un-“fix” web socket
1176 lines
52 KiB
HTML
1176 lines
52 KiB
HTML
<!DOCTYPE HTML>
|
|
<!--
|
|
Copyright 2015, 2017 IBM Corp.
|
|
|
|
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.
|
|
-->
|
|
<html manifest="worldmap.appcache">
|
|
<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"/>
|
|
|
|
<link rel="shortcut icon" type="image/ico" href="favicon.ico"/>
|
|
|
|
<script type="text/javascript" src="leaflet/socket.io-1.4.5.js"></script>
|
|
<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>
|
|
<script type="text/javascript" src="leaflet/leaflet.markercluster.freezable-src.js"></script>
|
|
<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>
|
|
<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>
|
|
<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"/>
|
|
</head>
|
|
|
|
<body onunload="ws.emit('end');">
|
|
<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>
|
|
</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>
|
|
</div>
|
|
<div id="menu"><table>
|
|
<tr><td><input type='text' name='search' id='search' size='20' style="width:150px;"/> <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>
|
|
</table></div>
|
|
<div id="map"></div>
|
|
<div id="foot"></div>
|
|
<!-- <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
|
|
that haven't been updated get removed.</p>
|
|
<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>
|
|
<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">
|
|
|
|
var startpos = [51.03, -1.379]; // Start location - somewhere in UK :-)
|
|
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;
|
|
var clusterAt = 12;
|
|
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
|
|
var ibmfoot = " © IBM 2015,2017"
|
|
var initialposition = false;
|
|
var inIframe = false;
|
|
var showUserMenu = true;
|
|
|
|
// Create the socket
|
|
var ws = io({path:location.pathname + 'socket.io'});
|
|
|
|
ws.on('connect', function() {
|
|
console.log("CONNECTED");
|
|
if (!inIframe) {
|
|
document.getElementById("foot").innerHTML = "<font color='#494'>"+ibmfoot+"</font>";
|
|
}
|
|
ws.emit("worldmap",{action:"connected"});
|
|
});
|
|
|
|
ws.on('disconnect', function() {
|
|
console.log("DISCONNECTED");
|
|
if (!inIframe) {
|
|
document.getElementById("foot").innerHTML = "<font color='#900'>"+ibmfoot+"</font>";
|
|
}
|
|
setTimeout(function() { ws.connect(); }, 2500);
|
|
});
|
|
|
|
ws.on('error', function() {
|
|
console.log("ERROR");
|
|
if (!inIframe) {
|
|
document.getElementById("foot").innerHTML = "<font color='#C00'>"+ibmfoot+"</font>";
|
|
}
|
|
setTimeout(function() { ws.connect(); }, 2500);
|
|
});
|
|
|
|
ws.on('worldmapdata', function(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); }
|
|
}
|
|
});
|
|
|
|
if ( window.localStorage.hasOwnProperty("lastpos") ) {
|
|
initialposition = true;
|
|
var sp = JSON.parse(window.localStorage.getItem("lastpos"));
|
|
startpos = [ sp.lat, sp.lng ];
|
|
}
|
|
if ( window.localStorage.hasOwnProperty("lastzoom") ) {
|
|
startzoom = window.localStorage.getItem("lastzoom");
|
|
}
|
|
// 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;
|
|
}
|
|
|
|
// Create the Initial Map object.
|
|
map = new L.map('map').setView(startpos, startzoom);
|
|
|
|
// Move some bits around if in an iframe
|
|
if (window.self !== window.top) {
|
|
console.log("IN an iframe");
|
|
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");
|
|
if (showUserMenu) {
|
|
var menuButton = L.easyButton( 'fa-bars fa-lg', function() { toggleMenu(); }, "Toggle menu", "topright").addTo(map);
|
|
}
|
|
}
|
|
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';
|
|
};
|
|
if (!showUserMenu) {
|
|
document.getElementById("bars").style.display="none";
|
|
}
|
|
}
|
|
|
|
if (!inIframe) {
|
|
// Add the fullscreen button
|
|
L.control.fullscreen().addTo(map);
|
|
|
|
// map.on('fullscreenchange', function () {
|
|
// if (map.isFullscreen()) { console.log('entered fullscreen') }
|
|
// else { console.log('exited fullscreen'); }
|
|
// });
|
|
|
|
// Add the locate my position button
|
|
L.easyButton( 'fa-crosshairs fa-lg', function() {
|
|
map.locate({setView:true, maxZoom:16});
|
|
}, "Locate me").addTo(map);
|
|
|
|
// Add the measure/ruler button
|
|
L.Control.measureControl().addTo(map);
|
|
|
|
// Create the clear heatmap button
|
|
var clrHeat = L.easyButton( '<b>Reset Heatmap</b>', function() {
|
|
console.log("Reset heatmap");
|
|
heat.setLatLngs([]);
|
|
}, "Clears the current heatmap", "bottomright");
|
|
}
|
|
|
|
// Handle the dialog for popup help
|
|
var dialog = document.querySelector('dialog');
|
|
dialogPolyfill.registerDialog(dialog);
|
|
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);
|
|
console.log("Saved :",JSON.stringify(map.getCenter()),map.getZoom(),baselayername);
|
|
}
|
|
map.setMaxBounds(mb);
|
|
console.log("Map bounds lock :",lockit);
|
|
}
|
|
|
|
// Remove old markers
|
|
function doTidyUp() {
|
|
var d = parseInt(Date.now()/1000);
|
|
for (var m in markers) {
|
|
if (typeof markers[m].ts != "undefined") {
|
|
//console.log(m,markers[m].ts,markers[m]);
|
|
if (((Number(markers[m].ts) + Number(maxage)) < d) && (markers[m].lay !== "drawing")) {
|
|
//if ((Number(markers[m].ts) + Number(maxage)) < d) {
|
|
//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[markers[m+"_"].lay].removeLayer(polygons[m+"_"]);
|
|
delete polygons[m+"_"];
|
|
}
|
|
delete markers[m];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// move the daylight / nighttime boundary (if enabled) every minute
|
|
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 );
|
|
|
|
// Call tidyup every {maxage} seconds - default 10 mins
|
|
var stale = null;
|
|
function setMaxAge() {
|
|
maxage = document.getElementById('maxage').value;
|
|
if (stale) { clearInterval(stale); }
|
|
if (maxage > 0) { stale = setInterval( function() {doTidyUp()}, (maxage*1000) ); }
|
|
//console.log("Stale time set :",maxage+"s");
|
|
}
|
|
setMaxAge();
|
|
|
|
function setCluster() {
|
|
clusterAt = parseInt(document.getElementById('setclus').value) || 0;
|
|
console.log("clusterAt set:",clusterAt);
|
|
showMapCurrentZoom();
|
|
}
|
|
setCluster();
|
|
|
|
// 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 = " <font color='#ff0'>Found "+marks.length+" results within bounds.</font>";
|
|
} else {
|
|
document.getElementById('searchRes').innerHTML = " <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++;
|
|
setTimeout(moveToMarks, 2500);
|
|
}
|
|
}
|
|
|
|
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([]);
|
|
//}
|
|
//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();
|
|
}
|
|
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);
|
|
overlays["drawing"].bringToBack();
|
|
}
|
|
//else { console.log("layer add :",e.name); }
|
|
ws.emit("worldmap",{action:"addlayer", name:e.name});
|
|
});
|
|
|
|
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.emit("worldmap",{action:"dellayer", name:e.name});
|
|
});
|
|
|
|
map.on('baselayerchange', function(e) {
|
|
//console.log("base layer now :",e.name);
|
|
baselayername = e.name;
|
|
ws.emit("worldmap",{action:"layer", name:e.name});
|
|
});
|
|
|
|
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();
|
|
}
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
|
|
map.on('zoomend', function() {
|
|
showMapCurrentZoom();
|
|
});
|
|
|
|
//map.on('contextmenu', function(e) {
|
|
// ws.emit("worldmap",{action:"rightclick", lat:e.latlng.lat.toFixed(5), lon:e.latlng.lng.toFixed(5)});
|
|
//});
|
|
|
|
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.emit("worldmap",{action:"point", lat:rclk.lat.toFixed(5), lon:rclk.lng.toFixed(5), point:thing});
|
|
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.
|
|
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;
|
|
//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.emit("worldmap",{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)});
|
|
}
|
|
function onLocationError(e) { console.log(e.message); }
|
|
map.on('locationfound', onLocationFound);
|
|
map.on('locationerror', onLocationError);
|
|
|
|
|
|
// 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});
|
|
basemaps["OSM grey"] = osmg;
|
|
var osm = new L.TileLayer(osmUrl, {attribution:osmAttrib});
|
|
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}', {
|
|
attribution: 'Tiles © Esri'
|
|
});
|
|
basemaps["Esri"] = Esri_WorldStreetMap;
|
|
|
|
var Esri_WorldImagery = L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', {
|
|
attribution: 'Tiles © Esri'
|
|
});
|
|
basemaps["Esri Satellite"] = Esri_WorldImagery;
|
|
|
|
var Esri_WorldShadedRelief = L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{z}/{y}/{x}', {
|
|
attribution: 'Tiles © Esri',
|
|
maxNativeZoom: 13
|
|
});
|
|
basemaps["Esri Terrain"] = Esri_WorldShadedRelief;
|
|
|
|
var Esri_OceanBasemap = L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer/tile/{z}/{y}/{x}', {
|
|
attribution: 'Tiles © Esri',
|
|
maxNativeZoom: 10
|
|
});
|
|
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,
|
|
attribution: 'Imagery from <a href="http://giscience.uni-hd.de/">University of Heidelberg</a> — Map data © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>'
|
|
});
|
|
basemaps["Mapsurfer"] = OpenMapSurfer_Roads;
|
|
|
|
// 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> — Map data © <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a>',
|
|
// subdomains: '1234',
|
|
// maxNativeZoom: 17
|
|
// });
|
|
//basemaps["MapQuest OSM"] = MapQuestOpen_OSM;
|
|
|
|
var Esri_NatGeoWorldMap = L.tileLayer('http://server.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}', {
|
|
attribution: 'Tiles © Esri',
|
|
maxNativeZoom: 12
|
|
});
|
|
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,
|
|
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', {
|
|
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;
|
|
|
|
//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'
|
|
//});
|
|
//basemaps["UK OS 1900"] = NLS_OS_1900;
|
|
|
|
//var CartoPos = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
|
|
// attribution: '© <a href="http://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, © <a href="http://cartodb.com/attributions">CartoDB</a>'
|
|
//});
|
|
//basemaps["CartoDB Light"] = CartoPos;
|
|
|
|
// Terrain map of US only
|
|
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;
|
|
|
|
// 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"];
|
|
|
|
// Add the drawing layer for fun...
|
|
layers["drawing"] = new L.FeatureGroup();
|
|
overlays["drawing"] = layers["drawing"];
|
|
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"],
|
|
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.emit("worldmap",{action:"draw",type:type, points:layer._latlngs});
|
|
layers["drawing"].addLayer(layer);
|
|
});
|
|
|
|
// 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> — Map data © <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 © <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 © <a href="http://openweathermap.org">OpenWeatherMap</a>',
|
|
opacity: 0.5
|
|
});
|
|
|
|
overlays["weather"] = L.OWM.current({interval:30, minZoom:8, appId:"9ae11d4f6f9bed61f32fc061f715cc71"});
|
|
|
|
overlays["buildings"] = new OSMBuildings(map).load();
|
|
map.removeLayer(overlays["buildings"]);
|
|
|
|
// Add the shipping navigation markers
|
|
//var OpenSeaMap = L.tileLayer('http://tiles.openseamap.org/seamark/{z}/{x}/{y}.png', {
|
|
// attribution: 'Map data: © <a href="http://www.openseamap.org">OpenSeaMap</a> contributors'
|
|
//});
|
|
//overlays["ship nav"] = OpenSeaMap;
|
|
|
|
if ( window.localStorage.hasOwnProperty("lastlayer") ) {
|
|
if ( basemaps[window.localStorage.getItem("lastlayer")] ) {
|
|
baselayername = window.localStorage.getItem("lastlayer");
|
|
}
|
|
}
|
|
basemaps[baselayername].addTo(map);
|
|
|
|
// Add the layers control widget
|
|
if (!inIframe) {
|
|
var layercontrol = L.control.layers(basemaps, overlays).addTo(map);
|
|
}
|
|
// Layer control based on select box rather than radio buttons.
|
|
//var layercontrol = L.control.selectLayers(basemaps, overlays).addTo(map);
|
|
|
|
// 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.emit("worldmap",{action:"delete", name:dname});
|
|
delete polygons[dname];
|
|
}
|
|
if (typeof markers[dname] != "undefined") {
|
|
ws.emit("worldmap",{action:"delete", name:dname});
|
|
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);
|
|
var ll;
|
|
var lli = null;
|
|
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;
|
|
|
|
var lay = data.layer || "not known";
|
|
if (typeof layers[lay] == "undefined") { // add layer if if doesn't exist
|
|
//layers[lay] = new L.LayerGroup().addTo(map);
|
|
layers[lay] = new L.MarkerClusterGroup({
|
|
maxClusterRadius:50,
|
|
spiderfyDistanceMultiplier:1.8,
|
|
disableClusteringAtZoom:clusterAt,
|
|
//zoomToBoundsOnClick:false
|
|
}).addTo(map);
|
|
overlays[lay] = layers[lay];
|
|
if (!inIframe) {
|
|
layercontrol.addOverlay(layers[lay],lay);
|
|
}
|
|
}
|
|
|
|
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(polyln);
|
|
}
|
|
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(polyarea);
|
|
}
|
|
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);
|
|
});
|
|
}
|
|
polygons[data.name] = ellipse;
|
|
polygons[data.name].lay = lay;
|
|
layers[lay].addLayer(ellipse);
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
//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 }
|
|
|
|
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);
|
|
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)+'"';
|
|
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>';
|
|
var svgplane = "data:image/svg+xml;base64," + btoa(icon);
|
|
var dir = data.hdg || data.bearing;
|
|
myMarker = L.divIcon({
|
|
className:"planeicon",
|
|
iconAnchor: [16, 16],
|
|
html:'<img src="'+svgplane+'" style="width:32px; height:32px; -webkit-transform:rotate('+dir+'deg); -moz-transform:rotate('+dir+'deg);"/>',
|
|
});
|
|
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>';
|
|
}
|
|
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});
|
|
}
|
|
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>';
|
|
var svgcar = "data:image/svg+xml;base64," + btoa(icon);
|
|
var dir = data.hdg || data.bearing;
|
|
myMarker = L.divIcon({
|
|
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);'/>",
|
|
});
|
|
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});
|
|
}
|
|
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});
|
|
}
|
|
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});
|
|
}
|
|
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 });
|
|
}
|
|
else if (data.icon.substr(0,3) === "fa-") {
|
|
var col = data.iconColor || "#910000";
|
|
myMarker = L.divIcon({
|
|
className: data.icon,
|
|
html: '<i class="fa fa-2x '+data.icon+'" style="color:'+col+'"></i>',
|
|
iconSize: [32, 32]
|
|
});
|
|
marker = L.marker(ll, {title:data.name, icon:myMarker});
|
|
}
|
|
else {
|
|
myMarker = L.VectorMarkers.icon({
|
|
icon: data.icon || "circle",
|
|
markerColor: (data.iconColor || "#910000"),
|
|
prefix: 'fa',
|
|
iconColor: 'white'
|
|
});
|
|
marker = L.marker(ll, {title:data.name, icon:myMarker});
|
|
}
|
|
|
|
// 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;
|
|
}
|
|
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/>";
|
|
}
|
|
}
|
|
}
|
|
marker.bindPopup(words);
|
|
marker._popup.dname = data.name;
|
|
marker.ts = parseInt(Date.now()/1000); // save time we got this marker
|
|
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);
|
|
});
|
|
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);
|
|
}
|
|
|
|
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;
|
|
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});
|
|
} 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});
|
|
}
|
|
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;
|
|
layers[lay].addLayer(polygon);
|
|
}
|
|
}
|
|
}
|
|
if (panit) {
|
|
map.setView(ll,map.getZoom());
|
|
}
|
|
}
|
|
}
|
|
|
|
function doCommand(cmd) {
|
|
//console.log("COMMAND",cmd);
|
|
// ignore server side initial command if client position already saved.
|
|
if (cmd.hasOwnProperty("init") && initialposition) {
|
|
return;
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
var existsalready = false;
|
|
// Add a new base map layer
|
|
if (cmd.map && cmd.map.hasOwnProperty("name") && cmd.map.hasOwnProperty("url") && cmd.map.hasOwnProperty("opt")) {
|
|
if (basemaps.hasOwnProperty(cmd.map.name)) { existsalready = true; }
|
|
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);
|
|
}
|
|
}
|
|
else {
|
|
console.log("New Map:",cmd.map.name);
|
|
basemaps[cmd.map.name] = L.tileLayer(cmd.map.url, cmd.map.opt);
|
|
}
|
|
if (!existsalready) {
|
|
layercontrol.addBaseLayer(basemaps[cmd.map.name],cmd.map.name);
|
|
}
|
|
}
|
|
// 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; }
|
|
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);
|
|
}
|
|
}
|
|
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);
|
|
}
|
|
}
|
|
else {
|
|
overlays[cmd.map.overlay] = L.tileLayer(cmd.map.url, cmd.map.opt);
|
|
}
|
|
if (!existsalready) {
|
|
layercontrol.addOverlay(overlays[cmd.map.overlay],cmd.map.overlay);
|
|
}
|
|
overlays[cmd.map.overlay].addTo(map);
|
|
}
|
|
// Swap a base layer
|
|
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);
|
|
}
|
|
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();
|
|
}
|
|
map.setView([clat,clon],czoom);
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|