/* eslint-disable no-undef */ var startpos = [51.05, -1.38]; // Start location - somewhere in UK :-) var startzoom = 10; var ws; var map; var allData = {}; var markers = {}; var polygons = {}; var layers = {}; var overlays = {}; var basemaps = {}; var marks = []; var buttons = {}; var marksIndex = 0; var menuOpen = false; var clusterAt = 0; 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,2021" var inIframe = false; var showUserMenu = true; var showLayerMenu = true; var showMouseCoords = false; var allowFileDrop = false; var minimap; var sidebyside; var layercontrol; var drawingColour = "#910000"; var iconSz = { "Team/Crew": 24, "Squad": 24, "Section": 24, "Platoon/detachment": 26, "Company/battery/troop": 28, "Battalion/squadron": 30, "Regiment/group": 32, "Brigade": 34, "Division": 36, "Corps/MEF": 36, "Army": 40, "Army Group/front": 40, "Region/Theater": 44, "Command": 44 }; // Polyfill assign for IE11 for now if (typeof Object.assign !== 'function') { // Must be writable: true, enumerable: false, configurable: true Object.defineProperty(Object, "assign", { value: function assign(target, varArgs) { // .length of function is 2 'use strict'; if (target === null || target === undefined) { throw new TypeError('Cannot convert undefined or null to object'); } var to = Object(target); for (var index = 1; index < arguments.length; index++) { var nextSource = arguments[index]; if (nextSource !== null && nextSource !== undefined) { for (var nextKey in nextSource) { // Avoid bugs when hasOwnProperty is shadowed if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) { to[nextKey] = nextSource[nextKey]; } } } } return to; }, writable: true, configurable: true }); } // Create the socket var connect = function() { ws = new SockJS(location.pathname.split("index")[0] + 'socket'); ws.onopen = function() { console.log("CONNECTED"); if (!inIframe) { document.getElementById("footer").innerHTML = ""+ibmfoot+""; } ws.send(JSON.stringify({action:"connected",parameters:Object.fromEntries((new URL(location)).searchParams)})); onoffline(); }; ws.onclose = function() { console.log("DISCONNECTED"); if (!inIframe) { document.getElementById("footer").innerHTML = ""+ibmfoot+""; } setTimeout(function() { connect(); }, 2500); }; ws.onmessage = function(e) { try { var data = JSON.parse(e.data); handleData(data); } catch (e) { if (data) { console.log("BAD DATA",data); } } // console.log("DATA",typeof data,data); }; } console.log("CONNECT TO",location.pathname + 'socket'); var handleData = function(data) { if (Array.isArray(data)) { //console.log("ARRAY"); // map.closePopup(); // var bnds= L.latLngBounds([0,0]); 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]); // bnds.extend(markers[data[prop].name].getLatLng()); } else { console.log("SKIP A",data[prop]); } } // map.fitBounds(bnds.pad(0.25)); } else { if (typeof data === "string" && data.indexOf(" { var content = event.target.result; var data; if (content.indexOf("base64") !== -1) { if (content.indexOf("image") === -1) { data = atob(content.split("base64,")[1]); if (data.indexOf('' helpMenu += ' '; helpMenu += ' Set Max Age s'; helpMenu += ' Cluster at zoom <'; helpMenu += ' Auto Pan Map'; helpMenu += ' Lock Map'; helpMenu += ' Heatmap all layers'; if (!inIframe) { helpMenu += 'Help'; } else { helpMenu += '' } document.getElementById('menu').innerHTML = helpMenu; // Add graticule var showGrid = false; var Lgrid = L.latlngGraticule({ font: "Verdana", fontColor: "#666", zoomInterval: [ {start:1, end:2, interval:40}, {start:3, end:3, interval:20}, {start:4, end:4, interval:10}, {start:5, end:7, interval:5}, {start:8, end:20, interval:1} ] }); var panit = false; function doPanit(v) { if (v !== undefined) { panit = v; } console.log("Panit set :",panit); } var heatAll = false; function doHeatAll(v) { if (v !== undefined) { heatall = v; } console.log("Heatall set :",heatAll); } var lockit = false; var mb = new L.LatLngBounds([[-120,-360],[120,360]]); function doLock(v) { if (v !== undefined) { lockit = v; } if (lockit === false) { mb = new L.LatLngBounds([[-120,-360],[120,360]]); map.dragging.enable(); } else { mb = map.getBounds(); map.dragging.disable(); 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(l) { if (l === "heatmap") { heat.setLatLngs([]); } else { var d = parseInt(Date.now()/1000); for (var m in markers) { if ((l && (l == markers[m].lay)) || typeof markers[m].ts != "undefined") { if ((l && (l == markers[m].lay)) || (markers[m].hasOwnProperty("ts") && (Number(markers[m].ts) < d) && (markers[m].lay !== "_drawing"))) { //console.log("STALE :",m); if (typeof polygons[m+"_"] != "undefined") { layers[polygons[m+"_"].lay].removeLayer(polygons[m+"_"]); delete polygons[m+"_"]; } if (typeof polygons[m] != "undefined") { layers[markers[m].lay].removeLayer(polygons[m]); delete polygons[m]; } layers[markers[m].lay].removeLayer(markers[m]); delete markers[m]; } } } if (l) { if (layers[l]) { map.removeLayer(layers[l]); layercontrol.removeLayer(layers[l]); delete layers[l]; } if (overlays[l]) { map.removeLayer(overlays[l]); layercontrol.removeLayer(overlays[l]); delete overlays[l]; } } } } // 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() }, 20000); // check every 20 secs //} //every minute //console.log("Stale time set :",maxage+"s"); } setMaxAge(); // 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 ); // move the rainfall overlay (if enabled) every 10 minutes function moveRainfall() { if (navigator.onLine && map.hasLayer(overlays["rainfall"])) { overlays["rainfall"]["_url"] = 'https://tilecache.rainviewer.com/v2/radar/' + parseInt(Date.now()/600000)*600 + '/256/{z}/{x}/{y}/2/1_1.png'; overlays["rainfall"].redraw(); } } setInterval( function() { moveRainfall() }, 600000 ); function setCluster(v) { clusterAt = v || 0; console.log("clusterAt set:",clusterAt); showMapCurrentZoom(); } // Search for markers with names of ... or icons of ... function doSearch() { var value = document.getElementById('search').value; marks = []; marksIndex = 0; for (var key in markers) { if ( (~(key.toLowerCase()).indexOf(value.toLowerCase())) && (mb.contains(markers[key].getLatLng()))) { marks.push(markers[key]); } if (markers[key].icon === value) { marks.push(markers[key]); } } moveToMarks(); if (marks.length === 0) { // If no markers found let's try a geolookup... var protocol = location.protocol; if (protocol == "file:") { protocol = "https:"; } var searchUrl = protocol + "//nominatim.openstreetmap.org/search?format=json&limit=1&q="; fetch(searchUrl + value) // Call the fetch function passing the url of the API as a parameter .then(function(resp) { return resp.json(); }) .then(function(data) { if (data.length > 0) { var bb = data[0].boundingbox; map.fitBounds([ [bb[0],bb[2]], [bb[1],bb[3]] ]); map.panTo([data[0].lat, data[0].lon]); } else { document.getElementById('searchResult').innerHTML = " Not Found"; } }) .catch(function(err) { if (err.toString() === "TypeError: Failed to fetch") { document.getElementById('searchResult').innerHTML = " Not Found"; } }); } else { if (lockit) { document.getElementById('searchResult').innerHTML = " Found "+marks.length+" results within bounds."; } else { document.getElementById('searchResult').innerHTML = " Found "+marks.length+" results."; } } } // 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); } } // Clear Search With Marker names function clearSearch() { var value = document.getElementById('search').value; marks = []; marksIndex = 0; for (var key in markers) { if ( (~(key.toLowerCase()).indexOf(value.toLowerCase())) && (mb.contains(markers[key].getLatLng()))) { marks.push(markers[key]); } } removeMarks(); if (lockit) { document.getElementById('searchResult').innerHTML = ""; } else { document.getElementById('searchResult').innerHTML = ""; } } function removeMarks() { if (marks.length > marksIndex) { var m = marks[marksIndex]; map.setView(m.getLatLng(), map.getZoom()); m.closePopup(); marksIndex++; } } function toggleMenu() { menuOpen = !menuOpen; if (menuOpen) { document.getElementById("menu").style.display = 'block'; } else { document.getElementById("menu").style.display = 'none'; dialogue.close(); } } function openMenu() { if (!menuOpen) { menuOpen = true; document.getElementById("menu").style.display = 'block'; } } function closeMenu() { if (menuOpen) { menuOpen = false; document.getElementById("menu").style.display = 'none'; } dialogue.close(); } document.getElementById("menu").style.display = 'none'; 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 == "countries") { overlays["countries"].bringToBack(); } if (e.name == "heatmap") { // show heatmap button when it's layer is added. clrHeat.addTo(map); } if (e.name == "day/night") { layers["_daynight"].addLayer(L.terminator()); } if (e.name == "drawing") { overlays["drawing"].bringToFront(); map.addControl(drawControl); map.addControl(colorControl); } ws.send(JSON.stringify({action:"addlayer", name:e.name})); }); map.on('overlayremove', function(e) { if (e.name == "heatmap") { // hide heatmap button when it's layer is removed. clrHeat.removeFrom(map); } if (e.name == "day/night") { layers["_daynight"].clearLayers(); } if (e.name == "drawing") { map.removeControl(colorControl); map.removeControl(drawControl); } ws.send(JSON.stringify({action:"dellayer", name:e.name})); }); map.on('baselayerchange', function(e) { //console.log("base layer now :",e.name); baselayername = e.name; ws.send(JSON.stringify({action:"layer", name:e.name})); }); function showMapCurrentZoom() { console.log("zoom:",map.getZoom(),". clusterAt:",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]) { if (typeof layers[markers[key].lay].getVisibleParent === 'function') { 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}); } } try { if (polygons[key].hasOwnProperty("_layers")) { polygons[key].eachLayer(function(layer) { layer.redraw(); }); } else { polygons[key].redraw(); } } catch(e) { console.log(key,polygons[key],e) } } } },750); } map.on('zoomend', function() { showMapCurrentZoom(); window.localStorage.setItem("lastzoom", map.getZoom()); }); map.on('moveend', function() { window.localStorage.setItem("lastpos",JSON.stringify(map.getCenter())); }); //map.on('contextmenu', function(e) { // ws.send(JSON.stringify({action:"rightclick", lat:e.latlng.lat.toFixed(5), lon:e.latlng.lng.toFixed(5)})); //}); // single right click to add a marker var addmenu = "Add marker
"; var rightmenuMap = L.popup({keepInView:true, minWidth:250}).setContent(addmenu); var rclk; var hiderightclick = false; var addThing = function() { var thing = document.getElementById('rinput').value; map.closePopup(); //popped = false; var bits = thing.split(","); var icon = (bits[1] || "circle").trim(); var lay = (bits[2] || "_drawing").trim(); var colo = (bits[3] || "#910000").trim(); var hdg = parseFloat(bits[4] || 0); var drag = true; var regi = /^[S,G,E,I,O][A-Z]{4}.*/i; // if it looks like a SIDC code var d = {action:"point", name:bits[0].trim(), layer:lay, draggable:drag, lat:rclk.lat, lon:rclk.lng, hdg:hdg}; if (regi.test(icon)) { d.SIDC = (icon.toUpperCase()+"------------").substr(0,12); } else { d.icon = icon; d.iconColor = colo; } ws.send(JSON.stringify(d)); delete d.action; setMarker(d); map.addLayer(layers[lay]); } var feedback = function(n,v,a,c) { if (v === "$form") { v = form; } if (markers[n]) { var fp = markers[n]._latlng; ws.send(JSON.stringify({action:a||"feedback", name:n, value:v, layer:markers[n].lay, lat:fp.lat, lon:fp.lng})); } else { if (n === undefined) { n = "map"; } ws.send(JSON.stringify({action:a||"feedback", name:n, value:v, lat:rclk.lat, lon:rclk.lng,})); } if (c === true) { map.closePopup(); } } var form = {}; var addToForm = function(n,v) { form[n] = v; } // allow double right click to zoom out (if enabled) // single right click opens a message window that adds a marker var rclicked = false; var rtout = null; map.on('contextmenu', function(e) { if (rclicked) { rclicked = false; clearTimeout(rtout); if (map.doubleClickZoom.enabled()) { map.zoomOut(); } } else { rclicked = true; rtout = setTimeout( function() { rclicked = false; if ((hiderightclick !== true) && (addmenu.length > 0)) { rclk = e.latlng; form = {}; rightmenuMap.setLatLng(e.latlng); map.openPopup(rightmenuMap); setTimeout( function() { try { document.getElementById('rinput').focus(); } catch(e) {} }, 200); } }, 300); } }); // Add all the base layer maps if (navigator.onLine) { // Use this for OSM online maps var osmUrl='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png'; //var osmUrl='https://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png'; var osmAttrib='Map data © OpenStreetMap contributors'; var osmg = new L.TileLayer.Grayscale(osmUrl, {attribution:osmAttrib, maxNativeZoom:19, maxZoom:20}); basemaps["OSM grey"] = osmg; var osm = new L.TileLayer(osmUrl, {attribution:osmAttrib, maxNativeZoom:19, maxZoom:20}); basemaps["OSM"] = osm; // Extra Leaflet map layers from https://leaflet-extras.github.io/leaflet-providers/preview/ var Esri_WorldStreetMap = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}', { attribution: 'Tiles © Esri', maxNativeZoom:19, maxZoom:20 }); basemaps["Esri"] = Esri_WorldStreetMap; var Esri_WorldImagery = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', { //var Esri_WorldImagery = L.tileLayer('http://clarity.maptiles.arcgis.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}', { attribution:'Tiles © Esri', maxNativeZoom:17, maxZoom:20 }); basemaps["Esri Satellite"] = Esri_WorldImagery; var Esri_WorldTopoMap = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}', { attribution: 'Tiles © Esri — Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community' }); basemaps["Esri Topography"] = Esri_WorldTopoMap; // var Esri_WorldShadedRelief = L.tileLayer('https://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('https://server.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer/tile/{z}/{y}/{x}', { attribution: 'Tiles © Esri — Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri', maxNativeZoom:13 }); basemaps["Esri Ocean"] = Esri_OceanBasemap; var Esri_WorldGrayCanvas = L.tileLayer('https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Base/MapServer/tile/{z}/{y}/{x}', { attribution: 'Tiles © Esri — Esri, DeLorme, NAVTEQ', maxNativeZoom:13 }); basemaps["Esri Dark Grey"] = Esri_WorldGrayCanvas; // var OpenMapSurfer_Roads = L.tileLayer('https://korona.geog.uni-heidelberg.de/tiles/roads/x={x}&y={y}&z={z}', { // maxZoom: 18, // attribution: 'Imagery from University of Heidelberg — Map data © OpenStreetMap' // }); // basemaps["Mapsurfer"] = OpenMapSurfer_Roads; // var MapQuestOpen_OSM = L.tileLayer('https://otile{s}.mqcdn.com/tiles/1.0.0/{type}/{z}/{x}/{y}.{ext}', { // type: 'map', // ext: 'jpg', // attribution: 'Tiles Courtesy of MapQuest — Map data © OpenStreetMap', // subdomains: '1234', // maxNativeZoom: 17 // }); //basemaps["MapQuest OSM"] = MapQuestOpen_OSM; var Esri_NatGeoWorldMap = L.tileLayer('https://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('https://geo.nls.uk/maps/opendata/{z}/{x}/{y}.png', { attribution: 'National Library of Scotland Historic Maps', bounds: [[49.6, -12], [61.7, 3]], minZoom:1, maxNativeZoom:18, maxZoom:18, subdomains: '0123' }); basemaps["UK OS Opendata"] = NLS_OS_opendata; var Open_Topo_Map = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', { subdomains: 'abc', maxZoom: 19, attribution: '© OpenTopoMap contributors' }); basemaps["Open Topo Map"] = Open_Topo_Map; var HikeBike_HikeBike = L.tileLayer('https://tiles.wmflabs.org/hikebike/{z}/{x}/{y}.png', { maxZoom: 19, attribution: '© OpenStreetMap contributors' }); basemaps["Hike Bike"] = HikeBike_HikeBike; var NLS_OS_1919_1947 = L.tileLayer( 'https://nls-{s}.tileserver.com/nls/{z}/{x}/{y}.jpg', { attribution: 'Historical Maps Layer, from NLS Maps', 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('https://nls-{s}.tileserver.com/NLS_API/{z}/{x}/{y}.jpg', { var NLS_OS_1900 = L.tileLayer('https://nls-{s}.tileserver.com/fpsUZbzrfb5d/{z}/{x}/{y}.jpg', { attribution: 'National Library of Scotland Historic Maps', bounds: [[49.6, -12], [61.7, 3]], minZoom:1, maxNativeZoom:19, maxZoom:20, subdomains: '0123' }); basemaps["UK OS 1900"] = NLS_OS_1900; //var CartoPos = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', { // attribution: '© OpenStreetMap contributors, © CartoDB' //}); //basemaps["CartoDB Light"] = CartoPos; // Nice terrain based maps by Stamen Design var terrainUrl = "https://stamen-tiles-{s}.a.ssl.fastly.net/terrain/{z}/{x}/{y}.jpg"; basemaps["Terrain"] = L.tileLayer(terrainUrl, { subdomains: ['a','b','c','d'], minZoom: 0, maxZoom: 20, type: 'jpg', attribution: 'Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under CC BY SA' }); // Nice watercolour based maps by Stamen Design var watercolorUrl = "https://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg"; basemaps["Watercolor"] = L.tileLayer(watercolorUrl, { subdomains: ['a','b','c','d'], minZoom: 0, maxZoom: 20, type: 'jpg', attribution: 'Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under CC BY SA' }); } // Now add the overlays // Add the drawing layer for fun... layers["_drawing"] = new L.FeatureGroup(); overlays["drawing"] = layers["_drawing"]; map.options.drawControlTooltips = false; var drawCount = 0; var drawControl = new L.Control.Draw({ draw: { polyline: { shapeOptions: { clickable:true } }, marker: false, //circle: false, circle: { shapeOptions: { clickable:true } }, circlemarker: false, rectangle: { shapeOptions: { clickable:true } }, polygon: { shapeOptions: { clickable:true } } } //edit: none // { // featureGroup: layers["_drawing"], // remove: true, // edit: true // } }); var changeDrawColour = function(col) { drawControl.setDrawingOptions({ polyline: { shapeOptions: { color:col } }, circle: { shapeOptions: { color:col } }, rectangle: { shapeOptions: { color:col } }, polygon: { shapeOptions: { color:col } } }); } var shape; map.on('draw:created', function (e) { var name = e.layerType + drawCount; drawCount = drawCount + 1; e.layer.on('contextmenu', function(e) { L.DomEvent.stopPropagation(e); var rmen = L.popup({offset:[0,-12]}).setLatLng(e.latlng); rmen.setContent("
"); map.openPopup(rmen); }); var la, lo, cent; if (e.layer.hasOwnProperty("_latlng")) { la = e.layer._latlng.lat; lo = e.layer._latlng.lng; cent = e.layer._latlng; } else { cent = e.layer.getBounds().getCenter(); } var m = {action:"draw", name:name, layer:"_drawing", options:e.layer.options, radius:e.layer._mRadius, lat:la, lon:lo}; if (e.layer.hasOwnProperty("_latlngs")) { if (e.layer.options.fill === false) { m.line = e.layer._latlngs; } else { m.area = e.layer._latlngs[0]; } } shape = {m:m, layer:e.layer}; polygons[name] = shape.layer; polygons[name].lay = "_drawing"; polygons[name].name = name; layers["_drawing"].addLayer(shape.layer); var rightmenuMarker = L.popup({offset:[0,-12]}).setContent("
"); if (e.layer.options.fill === false && navigator.onLine) { rightmenuMarker = L.popup({offset:[0,-12]}).setContent("
"); } rightmenuMarker.setLatLng(cent); setTimeout(function() {map.openPopup(rightmenuMarker)},25); }); var defaultOptions = function () { var options = {}; options.precision = 5; options.factor = Math.pow(10, options.precision); options.dimension = 2; return options; }; var decode = function (encoded, options) { options = defaultOptions(options); var flatPoints = decodeDeltas(encoded); var points = []; for (var i = 0, len = flatPoints.length; i + (options.dimension - 1) < len;) { var point = []; for (var dim = 0; dim < options.dimension; ++dim) { point.push(flatPoints[i++]); } points.push(point); } return points; } var decodeDeltas = function (encoded, options) { options = defaultOptions(options); var lastNumbers = []; var numbers = decodeFloats(encoded, options); for (var i = 0, len = numbers.length; i < len;) { for (var d = 0; d < options.dimension; ++d, ++i) { numbers[i] = Math.round((lastNumbers[d] = numbers[i] + (lastNumbers[d] || 0)) * options.factor) / options.factor; } } return numbers; } var decodeFloats = function (encoded, options) { options = defaultOptions(options); var numbers = decodeSignedIntegers(encoded); for (var i = 0, len = numbers.length; i < len; ++i) { numbers[i] /= options.factor; } return numbers; } var decodeSignedIntegers = function (encoded) { var numbers = decodeUnsignedIntegers(encoded); for (var i = 0, len = numbers.length; i < len; ++i) { var num = numbers[i]; numbers[i] = (num & 1) ? ~(num >> 1) : (num >> 1); } return numbers; } var decodeUnsignedIntegers = function (encoded) { var numbers = []; var current = 0; var shift = 0; for (var i = 0, len = encoded.length; i < len; ++i) { var b = encoded.charCodeAt(i) - 63; current |= (b & 0x1f) << shift; if (b < 0x20) { numbers.push(current); current = 0; shift = 0; } else { shift += 5; } } return numbers; } var sendRoute = function(n) { var p = (polygons[n]._latlngs.map(function(x) { return x.lng+","+x.lat; })).join(';'); fetch('https://router.project-osrm.org/route/v1/driving/'+p) .then(response => response.json()) .then(data => { if (data.code !== "Ok") { sendDrawing(n); } var r = decode(data.routes[0].geometry).map( x => L.latLng(x[0],x[1]) ); polygons[n]._latlngs = r; shape.m.line = r; sendDrawing(n); }); } var sendDrawing = function(n) { var thing = document.getElementById('dinput').value; map.closePopup(); shape.m.name = thing; delMarker(n,true); polygons[thing] = shape.layer; polygons[thing].lay = "_drawing"; polygons[thing].name = thing; layers["_drawing"].addLayer(shape.layer); ws.send(JSON.stringify(shape.m)); } // Add the countries (world-110m) for offline use var customTopoLayer = L.geoJson(null, {clickable:false, style: {color:"blue", weight:2, fillColor:"#cf6", fillOpacity:0.04}}); layers["_countries"] = omnivore.topojson('images/world-50m-flat.json',null,customTopoLayer); overlays["countries"] = layers["_countries"]; // Add the day/night overlay layers["_daynight"] = new L.LayerGroup(); overlays["day/night"] = layers["_daynight"]; // Add live rain data if (navigator.onLine) { overlays["rainfall"] = new L.TileLayer('https://tilecache.rainviewer.com/v2/radar/' + parseInt(Date.now()/600000)*600 + '/256/{z}/{x}/{y}/2/1_1.png', { tileSize: 256, opacity: 0.4, transparent: true, attribution: 'rainviewer.com' }); // Add the buildings layer // overlays["buildings"] = new OSMBuildings(map).load(); // map.removeLayer(overlays["buildings"]); // Hide it at start // Add Roads // overlays["roads"] = L.tileLayer('https://{s}.tile.openstreetmap.se/hydda/roads_and_labels/{z}/{x}/{y}.png', { // maxZoom: 18, // attribution: 'Tiles courtesy of OpenStreetMap Sweden — Map data © OpenStreetMap', // opacity: 0.8 // }); // // Add Railways // overlays["railways"] = L.tileLayer('https://{s}.tiles.openrailwaymap.org/standard/{z}/{x}/{y}.png', { // maxZoom: 19, // attribution: 'Map data: © OpenStreetMap | Map style: © OpenRailwayMap (CC-BY-SA)' // }); // // Add Public Transport (Buses) // overlays["public transport"] = L.tileLayer('https://openptmap.org/tiles/{z}/{x}/{y}.png', { // maxZoom: 17, // attribution: 'Map data: © OpenPtMap contributors' // }); // Add the OpenSea markers layer overlays["ship nav"] = L.tileLayer('https://tiles.openseamap.org/seamark/{z}/{x}/{y}.png', { maxZoom: 19, attribution: 'Map data: © OpenSeaMap contributors' }); } // 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"]; if (showUserMenu) { if ( window.localStorage.hasOwnProperty("lastlayer") ) { if ( basemaps[window.localStorage.getItem("lastlayer")] ) { baselayername = window.localStorage.getItem("lastlayer"); } } } if (navigator.onLine) { 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;} var coords = L.control.coordinates({ position:"bottomleft", //optional default "bottomright" decimals:4, //optional default 4 decimalSeperator:".", //optional default "." labelTemplateLat:" Lat: {y}", //optional default "Lat: {y}" labelTemplateLng:" Lon: {x}", //optional default "Lng: {x}" enableUserInput:false, //optional default true useDMS:true, //optional default false useLatLngOrder: true, //ordering of labels, default false-> lng-lat }); // Add an optional legend var legend = L.control({ position: "bottomleft" }); // Add the dialog box for messages var dialogue = L.control.dialog({initOpen:false, size:[600,400], anchor:[50,150]}).addTo(map); dialogue.freeze(); var doDialog = function(d) { //console.log("DIALOGUE",d); dialogue.setContent(d); dialogue.open(); } var helpText = '

Node-RED - Map all the things


'; helpText += '

Search - You may enter a name, or partial name, or icon name of an object to search for.'; helpText += 'The map will then jump to centre on each of the results in turn. If nothing is found locally it will try to'; helpText += 'search for a place name if connected to a network.

'; helpText += '

Set Max Age - You can set the time after which points'; helpText += 'that haven\'t been updated get removed.

'; helpText += '

Cluster at zoom - lower numbers mean less clustering. 0 means disable totally.

'; helpText += '

Auto Pan - When selected, the map will'; helpText += 'automatically move to centre on each data point as they arrive.

'; helpText += '

Lock Map - When selected will save the'; helpText += 'currently displayed area and basemap.'; helpText += 'Reloading the map in the current browser will return to the same view.'; helpText += 'This can be used to set your initial start position.'; helpText += 'While active it also restricts the "auto pan" and "search" to within that area.

'; helpText += '

Heatmap all layers - When selected'; helpText += 'all layers whether hidden or not will contribute to the heatmap.'; helpText += 'The default is that only visible layers add to the heatmap.

'; // Delete a marker (and notify websocket) var delMarker = function(dname,note) { if (note) { map.closePopup(); } if (typeof polygons[dname] != "undefined") { layers[polygons[dname].lay].removeLayer(polygons[dname]); delete polygons[dname]; } if (typeof polygons[dname+"_"] != "undefined") { layers[polygons[dname+"_"].lay].removeLayer(polygons[dname+"_"]); delete polygons[dname+"_"]; } if (typeof markers[dname] != "undefined") { layers[markers[dname].lay].removeLayer(markers[dname]); map.removeLayer(markers[dname]); delete markers[dname]; } delete allData[dname]; if (note) { ws.send(JSON.stringify({action:"delete", name:dname, deleted:true})); } } var editPoly = function(pname) { map.closePopup(); editFeatureGroup = L.featureGroup(); editToolbar = new L.EditToolbar({ featureGroup:editFeatureGroup }); editHandler = editToolbar.getModeHandlers()[0].handler; editHandler._map = map; polygons[pname].on("dblclick", function(e) { editHandler.disable(); editFeatureGroup.removeLayer(polygons[pname]); polygons[pname].off("dblclick"); L.DomEvent.stopPropagation(e); var la, lo; if (e.target.hasOwnProperty("_latlng")) { la = e.target._latlng.lat; lo = e.target._latlng.lng; } var m = {action:"draw", name:pname, layer:polygons[pname].lay, options:e.target.options, radius:e.target._mRadius, lat:la, lon:lo}; if (e.target.hasOwnProperty("_latlngs")) { if (e.target.options.fill === false) { m.line = e.target._latlngs; } else { m.area = e.target._latlngs[0]; } } ws.send(JSON.stringify(m)); }) editFeatureGroup.addLayer(polygons[pname]); editHandler.enable(); } var rangerings = function(latlng, options) { options = L.extend({ ranges: [250,500,750,1000], pan: 0, fov: 60, color: '#910000' }, options); var rings = L.featureGroup(); if (typeof options.ranges === "number") { options.ranges = [ options.ranges ]; } for (var i = 0; i < options.ranges.length; i++) { L.semiCircle(latlng, { radius: options.ranges[i], fill: false, color: options.color, weight: options.weight || 1 }).setDirection(options.pan, options.fov).addTo(rings); } return rings; } // the MAIN add something to map function function setMarker(data) { var rightmenu = function(m) { // customise right click context menu var rightcontext = ""; if (polygons[data.name] == undefined) { rightcontext = ""; } if (data.editable) { rightcontext = ""; } if ((data.contextmenu !== undefined) && (typeof data.contextmenu === "string")) { rightcontext = data.contextmenu.replace(/\$name/g,'"'+data.name+'"'); delete data.contextmenu; } if (rightcontext.length > 0) { var rightmenuMarker = L.popup({offset:[0,-12]}).setContent(""+data.name+"
"+rightcontext); if (hiderightclick !== true) { m.on('contextmenu', function(e) { L.DomEvent.stopPropagation(e); rightmenuMarker.setLatLng(e.latlng); map.openPopup(rightmenuMarker); }); } } else { if (hiderightclick !== true) { m.on('contextmenu', function(e) { L.DomEvent.stopPropagation(e); }); } } return m; } // console.log("DATA", typeof data, data); if (data.deleted) { // remove markers we are told to delMarker(data.name); return; } var ll; var lli = null; var opt = {}; opt.color = data.color || data.lineColor || "#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; if (data.hasOwnProperty("dashArray")) { opt.dashArray = data.dashArray; } // Replace building if (data.hasOwnProperty("building")) { if ((data.building === "") && layers.hasOwnProperty("buildings")) { map.removeLayer(layers["buildings"]); layercontrol._update(); layers["buildings"] = overlays["buildings"].set(""); return; } //layers["buildings"] = new OSMBuildings(map).set(data.building); layers["buildings"] = overlays["buildings"].set(data.building); map.addLayer(layers["buildings"]); return; } var lll = "unknown"; if (markers.hasOwnProperty(data.name) && markers[data.name].hasOwnProperty("lay")) { lll = markers[data.name].lay; } var lay = data.layer || lll; if (!data.hasOwnProperty("action") || data.action.indexOf("layer") === -1) { if (typeof layers[lay] == "undefined") { // add layer if if doesn't exist if (clusterAt > 0) { layers[lay] = new L.MarkerClusterGroup({ maxClusterRadius:50, spiderfyDistanceMultiplier:1.8, disableClusteringAtZoom:clusterAt //zoomToBoundsOnClick:false }); } else { layers[lay] = new L.LayerGroup(); } overlays[lay] = layers[lay]; if (showLayerMenu !== false) { layercontrol.addOverlay(layers[lay],lay); } map.addLayer(overlays[lay]); //console.log("ADDED LAYER",lay,layers); } if (!allData.hasOwnProperty(data.name)) { allData[data.name] = {}; } delete data.action; Object.keys(data).forEach(function(key) { if (data[key] == null) { delete allData[data.name][key]; } else { allData[data.name][key] = data[key]; } }); data = Object.assign({},allData[data.name]); } delete data.action; if (typeof markers[data.name] != "undefined") { if (markers[data.name].lay !== lay) { delMarker(data.name); } else { try {layers[lay].removeLayer(markers[data.name]); } catch(e) { console.log("OOPS"); } } } if (typeof polygons[data.name] != "undefined") { layers[lay].removeLayer(polygons[data.name]); } 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); polygons[data.name] = rightmenu(polyln); } else if (data.hasOwnProperty("area") && Array.isArray(data.area)) { var polyarea; if (data.area.length === 2) { polyarea = L.rectangle(data.area, opt); } else { polyarea = L.polygon(data.area, opt); } polygons[data.name] = rightmenu(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"; } 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); polygons[data.name] = rightmenu(ellipse); } else if (data.hasOwnProperty("radius")) { if (data.hasOwnProperty("lat") && data.hasOwnProperty("lon")) { var polycirc; if (Array.isArray(data.radius)) { polycirc = L.ellipse(new L.LatLng((data.lat*1), (data.lon*1)), [data.radius[0]*Math.cos(data.lat*Math.PI/180), data.radius[1]], data.tilt || 0, opt); } else { polycirc = L.circle(new L.LatLng((data.lat*1), (data.lon*1)), data.radius*1, opt); } polygons[data.name] = rightmenu(polycirc); if (!data.hasOwnProperty("icon")) { delete (data.lat); delete (data.lon); } } } else if (data.hasOwnProperty("arc")) { if (data.hasOwnProperty("lat") && data.hasOwnProperty("lon")) { polygons[data.name] = rangerings(new L.LatLng((data.lat*1), (data.lon*1)), data.arc); } } else if (data.hasOwnProperty("geojson")) { doGeojson(data.name,data.geojson,(data.layer || "unknown"),opt); } if (polygons[data.name] !== undefined) { polygons[data.name].lay = lay; if (opt.clickable === true) { var words = ""+data.name+""; if (data.popup) { words = words + "
" + data.popup; } polygons[data.name].bindPopup(words, {autoClose:false, closeButton:true, closeOnClick:false, minWidth:200}); } //polygons[data.name] = rightmenu(polygons[data.name]); // DCJ Investigate layers[lay].addLayer(polygons[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 } // Create the icons... handle plane, car, ship, wind, earthquake as specials var marker, myMarker; var icon, q; var words = ""; var labelOffset = [12,0]; var drag = false; if (data.draggable === true) { drag = true; } if (data.hasOwnProperty("icon")) { var dir = parseFloat(data.hdg || data.heading || data.bearing || "0"); if (data.icon === "ship") { marker = L.boatMarker(ll, { title: data.name, color: (data.iconColor || "blue") }); marker.setHeading(dir); q = 'https://www.bing.com/images/search?q='+data.icon+'%20%2B"'+encodeURIComponent(data.name)+'"'; words += 'Pictures
'; } else if (data.icon === "plane") { data.iconColor = data.iconColor || "black"; if (data.hasOwnProperty("squawk")) { if (data.squawk == 7500 || data.squawk == 7600 || data.squawk == 7700) { data.iconColor = "red"; } } icon = ''; icon += ''; var svgplane = "data:image/svg+xml;base64," + btoa(icon); myMarker = L.divIcon({ className:"planeicon", iconAnchor: [16, 16], html:'' }); marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag}); //q = 'https://www.bing.com/images/search?q='+data.icon+'%20'+encodeURIComponent(data.name); //words += 'Pictures
'; } else if (data.icon === "bus") { dir = dir - 90; var sc = 1; if (dir < -90 || dir >= 90) { sc = -1; } data.iconColor = data.iconColor || "#910000"; var p = "m595.5 97.332-30.898-68.199c-11.141-24.223-35.344-39.762-62.004-39.801h-443.3c-32.738 0.035157-59.266 26.562-59.301 59.305v148.2c0 17.949 14.551 32.5 32.5 32.5h48.5c4.7344 23.309 25.219 40.051 49 40.051s44.266-16.742 49-40.051h242c4.7344 23.309 25.219 40.051 49 40.051s44.266-16.742 49-40.051h53.203c12.348-0.003906 23.219-8.1484 26.699-20 0.72266-2.5391 1.0898-5.1602 1.0977-7.7969v-83.5c-0.003906-7.1445-1.5391-14.203-4.5-20.703zm-545.5 12c-5.5234 0-10-4.4766-10-10v-80c0-5.5195 4.4766-10 10-10h70c5.5234 0 10 4.4805 10 10v80c0 5.5234-4.4766 10-10 10zm80 140c-16.566 0-30-13.43-30-30 0-16.566 13.434-30 30-30s30 13.434 30 30c-0.046875 16.551-13.453 29.953-30 30zm110-150c0 5.5234-4.4766 10-10 10h-70c-5.5234 0-10-4.4766-10-10v-80c0-5.5195 4.4766-10 10-10h70c5.5234 0 10 4.4805 10 10zm110 0c0 5.5234-4.4766 10-10 10h-70c-5.5234 0-10-4.4766-10-10v-80c0-5.5195 4.4766-10 10-10h70c5.5234 0 10 4.4805 10 10zm30 10c-5.5234 0-10-4.4766-10-10v-80c0-5.5195 4.4766-10 10-10h70c5.5234 0 10 4.4805 10 10v80c0 5.5234-4.4766 10-10 10zm90 140c-16.566 0-30-13.43-30-30 0-16.566 13.434-30 30-30s30 13.434 30 30c-0.046875 16.551-13.453 29.953-30 30zm19.199-140c-5.1836-0.46094-9.168-4.793-9.1992-10v-80.086c0-5.4727 4.4375-9.9141 9.9141-9.9141h12.684c18.824 0.050781 35.914 11.012 43.805 28.102l30.898 68.199c1.6133 3.5547 2.5 7.3984 2.6016 11.297z"; icon = ''; icon += ''; var svgbus = "data:image/svg+xml;base64," + btoa(icon); myMarker = L.divIcon({ className:"busicon", iconAnchor: [16, 16], html:'' }); marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag}); } else if (data.icon === "helicopter") { data.iconColor = data.iconColor || "black"; if (data.hasOwnProperty("squawk")) { if (data.squawk == 7500 || data.squawk == 7600 || data.squawk == 7700) { data.iconColor = "red"; } } icon = ''; icon += ''; var svgheli = "data:image/svg+xml;base64," + btoa(icon); myMarker = L.divIcon({ className:"heliicon", iconAnchor: [16, 16], html:'' }); marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag}); } else if (data.icon === "uav") { data.iconColor = data.iconColor || "black"; if (data.hasOwnProperty("squawk")) { if (data.squawk == 7500 || data.squawk == 7600 || data.squawk == 7700) { data.iconColor = "red"; } } icon = ''; icon+= ''; var svguav = "data:image/svg+xml;base64," + btoa(icon); myMarker = L.divIcon({ className:"uavicon", iconAnchor: [16, 16], html:'', }); marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag}); } else if (data.icon === "car") { data.iconColor = data.iconColor || "black"; icon = ''; icon += ''; var svgcar = "data:image/svg+xml;base64," + btoa(icon); myMarker = L.divIcon({ className:"caricon", iconAnchor: [16, 16], html:'', }); marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag}); } else if (data.icon === "arrow") { data.iconColor = data.iconColor || "black"; icon = ''; icon += ''; var svgarrow = "data:image/svg+xml;base64," + btoa(icon); myMarker = L.divIcon({ className:"arrowicon", iconAnchor: [16, 16], html:"'", }); marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag}); } else if (data.icon === "wind") { data.iconColor = data.iconColor || "black"; icon = ''; icon += ''; var svgwind = "data:image/svg+xml;base64," + btoa(icon); myMarker = L.divIcon({ className:"windicon", iconAnchor: [16, 16], html:'', }); marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag}); } else if (data.icon === "satellite") { data.iconColor = data.iconColor || "black"; icon = ''; icon += ''; icon += ''; icon += ''; icon += ''; icon += ''; icon += ''; var svgsat = "data:image/svg+xml;base64," + btoa(icon); myMarker = L.divIcon({ className:"satelliteicon", iconAnchor: [16, 16], html:'', }); marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag}); } else if ((data.icon === "iss") || (data.icon === "ISS")) { data.iconColor = data.iconColor || "black"; icon = ''; icon += ''; icon += ''; var svgiss = "data:image/svg+xml;base64," + btoa(icon); myMarker = L.divIcon({ className:"issicon", iconAnchor: [25, 25], html:'', }); marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag}); } else if (data.icon === "mayflower") { data.iconColor = data.iconColor || "#910000"; icon = ''; var svgmay = "data:image/svg+xml;base64," + btoa(icon); myMarker = L.divIcon({ className:"mayflowericon", iconAnchor: [12, 24], html:'', }); marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag}); } else if (data.icon === "locate") { data.iconColor = data.iconColor || "cyan"; icon = ''; icon += ''; icon += ''; icon += ''; icon += ''; //icon += ''; icon += ''; var svglocate = "data:image/svg+xml;base64," + btoa(icon); myMarker = L.divIcon({ className:"locateicon", iconAnchor: [16, 16], html:'', }); marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag}); labelOffset = [12,-4]; } else if (data.icon === "friend") { marker = L.marker(ll, { icon: L.divIcon({ className: 'circle f', iconSize: [20, 12] }), title: data.name, draggable:drag }); } else if (data.icon === "hostile") { marker = L.marker(ll, { icon: L.divIcon({ className: 'circle h', iconSize: [16, 16] }), title: data.name, draggable:drag }); } else if (data.icon === "neutral") { marker = L.marker(ll, { icon: L.divIcon({ className: 'circle n', iconSize: [16, 16] }), title: data.name, draggable:drag }); } else if (data.icon === "unknown") { marker = L.marker(ll, { icon: L.divIcon({ className: 'circle', iconSize: [16, 16] }), title: data.name, draggable:drag }); } else if (data.icon === "danger") { marker = L.marker(ll, { icon: L.divIcon({ className: 'up-triangle' }), title: data.name, draggable:drag }); } 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, draggable:drag }); } else if (data.icon.match(/^:.*:$/g)) { var em = emojify(data.icon); var col = data.iconColor || "#910000"; myMarker = L.divIcon({ className:"emicon", html: '
'+em+'
', iconSize: [32, 32] }); marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag}); labelOffset = [12,-4]; } else if (data.icon.match(/^https?:.*$/)) { var sz = data.iconSize || 32; myMarker = L.icon({ iconUrl: data.icon, iconSize: [sz, sz], iconAnchor: [sz/2, sz/2], popupAnchor: [0, -sz/2] }); marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag, rotationAngle:dir, rotationOrigin:"center"}); labelOffset = [sz/2-4,-4]; delete data.iconSize; } else if (data.icon.substr(0,3) === "fa-") { var col = data.iconColor || "#910000"; var imod = ""; if (data.icon.indexOf(" ") === -1) { imod = "fa-2x "; } myMarker = L.divIcon({ className:"faicon", html: '
', iconSize: [32, 32], iconAnchor: [16, 12], popupAnchor: [0, -16] }); marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag}); labelOffset = [8,-8]; } else if (data.icon.substr(0,3) === "wi-") { var col = data.iconColor || "#910000"; var imod = ""; if (data.icon.indexOf(" ") === -1) { imod = "wi-2x "; } myMarker = L.divIcon({ className:"wiicon", html: '
', iconSize: [32, 32], iconAnchor: [16, 16], popupAnchor: [0, -16] }); marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag}); labelOffset = [16,-16]; } 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, draggable:drag}); labelOffset = [6,-6]; } } else if (data.hasOwnProperty("SIDC")) { // "SIDC":"SFGPU------E***","name":"1.C2 komp","fullname":"1.C2 komp/FTS/INSS" myMarker = new ms.Symbol( data.SIDC.toUpperCase(), { uniqueDesignation:unescape(encodeURIComponent(data.name)) }); // Now that we have a symbol we can ask for the echelon and set the symbol size var opts = data.options || {}; var sz = 25; if (myMarker.hasOwnProperty("getProperties") && myMarker.getProperties().hasOwnProperty("echelon")) { sz = iconSz[myMarker.getProperties().echelon]; } opts.size = opts.size || sz; opts.size = opts.size * (opts.scale || 1); // escape out any isocodes eg flag symbols var optfields = ["additionalInformation","higherFormation","specialHeadquarters","staffComments","type","uniqueDesignation"]; optfields.forEach(function (item) { if (opts.hasOwnProperty(item)) { opts[item] = unescape(encodeURIComponent(opts[item])); } }); myMarker = myMarker.setOptions(opts); var myicon = L.icon({ iconUrl: myMarker.toDataURL(), iconAnchor: [myMarker.getAnchor().x, myMarker.getAnchor().y], className: "natoicon", }); marker = L.marker(ll, { title:data.name, icon:myicon, draggable:drag }); } else { myMarker = L.VectorMarkers.icon({ icon: "circle", markerColor: (data.iconColor || "#910000"), prefix: 'fa', iconColor: 'white' }); marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag}); labelOffset = [6,-6]; } marker.name = data.name; // var createLabelIcon = function(labelText) { // return L.marker(new L.LatLng(51.05, -1.35), {icon:L.divIcon({ html:labelText })}); // } // send new position at end of move event if point is draggable if (data.draggable === true) { if (data.icon) { marker.icon = data.icon; } if (data.iconColor) { marker.iconColor = data.iconColor; } if (data.SIDC) { marker.SIDC = data.SIDC.toUpperCase(); } var oldll; marker.on('dragstart', function (e) { oldll = marker.getLatLng(); var ola = parseFloat(oldll.lat.toFixed(6)); var olo = parseFloat(oldll.lng.toFixed(6)); oldll = {lat:ola, lon:olo}; }); marker.on('dragend', function (e) { var l = marker.getLatLng().toString().replace('LatLng(','lat, lon : ').replace(')','') marker.setPopupContent(marker.getPopup().getContent().split("lat, lon")[0] + l); var b = marker.getPopup().getContent().split("heading : "); if (b.length === 2) { b = parseFloat(b[1].split(""; delete data.photourl; } if (data.hasOwnProperty("photoUrl")) { words += ""; delete data.photoUrl; } if (data.hasOwnProperty("videoUrl")) { words += ''; 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 (!Array.isArray(data.weblink) || !data.weblink.length) { if (typeof data.weblink === "string") { words += "more information...
"; } else { var tgt = data.weblink.target || "_new"; words += "" + data.weblink.name + "
"; } } else { data.weblink.forEach(function(weblink){ if (typeof weblink === "string") { words += "more information...
"; } else { var tgt = weblink.target || "_new"; words += "" + weblink.name + "
"; } }); } delete data.weblink; } var p; if (data.hasOwnProperty("popped") && (data.popped === true)) { p = true; delete data.popped; } if (data.hasOwnProperty("popped") && (data.popped === false)) { marker.closePopup(); p = false; delete data.popped; } // If .label then use that rather than name tooltip if (data.label) { if (typeof data.label === "boolean" && data.label === true) { marker.bindTooltip(data.name, { permanent:true, direction:"right", offset:labelOffset }); } else if (typeof data.label === "string" && data.label.length > 0) { marker.bindTooltip(data.label, { permanent:true, direction:"right", offset:labelOffset }); } delete marker.options.title; delete data.label; } // otherwise check for .tooltip then use that rather than name tooltip else if (data.tooltip) { if (typeof data.tooltip === "string" && data.tooltip.length > 0) { marker.bindTooltip(data.tooltip, { direction:"bottom", offset:[0,4] }); delete marker.options.title; delete data.tooltip; } } marker = rightmenu(marker); // Add any remaining properties to the info box var llc = data.lineColor || data.color; delete data.lat; delete data.lon; if (data.arc) { delete data.arc; } if (data.layer) { delete data.layer; } if (data.lineColor) { delete data.lineColor; } if (data.color) { delete data.color; } if (data.weight) { delete data.weight; } if (data.tracklength) { delete data.tracklength; } if (data.dashArray) { delete data.dashArray; } if (data.fill) { delete data.fill; } if (data.draggable) { delete data.draggable; } if (!isNaN(data.speed)) { data.speed = data.speed.toFixed(2); } if (data.hasOwnProperty("clickable")) { delete data.clickable; } if (data.hasOwnProperty("fillColor")) { delete data.fillColor; } if (data.hasOwnProperty("radius")) { delete data.radius; } for (var i in data) { if ((i != "name") && (i != "length")) { if (typeof data[i] === "object") { words += i +" : "+JSON.stringify(data[i])+"
"; } else { words += i +" : "+data[i]+"
"; } } } if (data.popup) { words = data.popup; } else { words = words + marker.getLatLng().toString().replace('LatLng(','lat, lon : ').replace(')',''); } words = ""+data.name+"
" + words; //"
" + words; var wopt = {autoClose:false, closeButton:true, closeOnClick:false, minWidth:200}; if (words.indexOf('