Search - You may enter a name, or partial name, or icon name of an object to search for.
The map will then jump to centre on each of the results in turn. If nothing is found locally it will try to
search for a place name if connected to a network.
Set Max Age - You can set the time after which points
that haven't been updated get removed.
Cluster at zoom - lower numbers mean less clustering. 0 means disable totally.
Auto Pan - When selected, the map will
automatically move to centre on each data point as they arrive.
Lock Map - When selected will save the
currently displayed area and basemap.
Reloading the map in the current browser will return to the same view.
This can be used to set your initial start position.
While active it also restricts the "auto pan" and "search" to within that area.
Heatmap all layers - When selected
all layers whether hidden or not will contribute to the heatmap.
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();
}
// 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 = "";
}
else 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);
});
}
}
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 || "#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 lay = data.layer || "unknown";
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(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 !== data.layer) {
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] = 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] = 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] = 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] = polycirc;
}
}
if (polygons[data.name] !== undefined) {
polygons[data.name].lay = lay;
if (opt.clickable) {
var words = ""+data.name+"";
if (data.popup) { var words = words + " " + data.popup; }
polygons[data.name].bindPopup(words, {autoClose:false, closeButton:true, closeOnClick:false, minWidth:200});
}
polygons[data.name] = rightmenu(polygons[data.name]);
layers[lay].addLayer(polygons[data.name]);
}
else {
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; }
//console.log("ICON",data.icon);
if (data.hasOwnProperty("icon")) {
if (data.icon === "ship") {
marker = L.boatMarker(ll, {
title: data.name,
color: (data.iconColor || "blue")
});
marker.setHeading(parseFloat(data.hdg || data.bearing || "0"));
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")) { data.iconColor = "red"; }
icon = '';
var svgplane = "data:image/svg+xml;base64," + btoa(icon);
var dir = parseFloat(data.hdg || data.bearing || "0");
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 === "helicopter") {
data.iconColor = data.iconColor || "black";
if (data.hasOwnProperty("squawk")) { data.iconColor = "red"; }
icon = '';
var svgheli = "data:image/svg+xml;base64," + btoa(icon);
var dir = parseFloat(data.hdg || data.bearing || "0");
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")) { data.iconColor = "red"; }
icon = '';
var svguav = "data:image/svg+xml;base64," + btoa(icon);
var dir = parseFloat(data.hdg || data.bearing || "0");
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 = '';
var svgcar = "data:image/svg+xml;base64," + btoa(icon);
var dir = parseFloat(data.hdg || data.bearing || "0");
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 = '';
var svgarrow = "data:image/svg+xml;base64," + btoa(icon);
var dir = parseFloat(data.hdg || data.bearing || "0");
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 = '';
var svgwind = "data:image/svg+xml;base64," + btoa(icon);
var dir = parseFloat(data.hdg || data.bearing || "0");
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 = '';
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 = '';
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 === "locate") {
data.iconColor = data.iconColor || "cyan";
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?:.*$/)) {
myMarker = L.icon({
iconUrl: data.icon,
iconSize: [32, 32],
iconAnchor: [16, 16],
popupAnchor: [0, -16]
});
marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag});
labelOffset = [12,-4];
}
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],
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],
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:data.name });
// Now that we have a symbol we can ask for the echelon and set the symbol size
var opts = data.options || {};
opts.size = opts.size || iconSz[myMarker.getProperties().echelon] || 30;
opts.size = opts.size * (opts.scale || 1);
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(); }
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);
ws.send(JSON.stringify({action:"move",name:marker.name,layer:marker.lay,icon:marker.icon,iconColor:marker.iconColor,SIDC:marker.SIDC,draggable:true,lat:parseFloat(marker.getLatLng().lat.toFixed(6)),lon:parseFloat(marker.getLatLng().lng.toFixed(6))}));
});
}
// remove icon from list of properties, then add all others to popup
if (data.hasOwnProperty("alt")) { data.alt = +data.alt.toFixed(2); }
if (data.hasOwnProperty("speed")) { data.speed = +data.speed.toFixed(2); }
if (data.hasOwnProperty("SIDC") && data.hasOwnProperty("options")) { delete data.options; }
if (data.hasOwnProperty("icon")) { delete data.icon; }
if (data.hasOwnProperty("iconColor")) { delete data.iconColor; }
if (data.hasOwnProperty("photourl")) {
words += "";
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 (typeof data.weblink === "string") {
words += "more information... ";
} else {
var tgt = data.weblink.target || "_new";
words += "" + data.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;
delete data.lat;
delete data.lon;
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; }
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;
marker.bindPopup(words, {autoClose:false, closeButton:true, closeOnClick:false, minWidth:200});
marker._popup.dname = data.name;
marker.lay = lay; // and the layer it is on
marker.on('click', function(e) {
ws.send(JSON.stringify({action:"click",name:marker.name,layer:marker.lay,icon:marker.icon,iconColor:marker.iconColor,SIDC:marker.SIDC,draggable:true,lat:parseFloat(marker.getLatLng().lat.toFixed(6)),lon:parseFloat(marker.getLatLng().lng.toFixed(6))}));
});
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);
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 = parseFloat(data.speed || "0") * 50; } // and a speed
if (data.length != null) {
if (polygons[data.name] != null) { map.removeLayer(polygons[data.name]); }
var x = ll.lng * 1; // X coordinate
var y = ll.lat * 1; // Y coordinate
var ll1 = ll;
var angle = parseFloat(data.bearing);
var lengthAsDegrees = parseFloat(data.length || "0") / 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:llc||'#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:llc||'#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()); }
if (p === true) { marker.openPopup(); }
}
}
// handle any incoming COMMANDS to control the map remotely
function doCommand(cmd) {
//console.log("COMMAND",cmd);
if (cmd.hasOwnProperty("clear")) {
doTidyUp(cmd.clear);
}
if (cmd.hasOwnProperty("panit")) {
if (cmd.panit == "true") { panit = true; }
else { panit = false; }
document.getElementById("panit").checked = panit;
}
if (cmd.hasOwnProperty("hiderightclick")) {
if (cmd.hiderightclick == "true" || cmd.hiderightclick == true) { hiderightclick = true; }
else { hiderightclick = false; }
}
if (cmd.hasOwnProperty("showmenu")) {
if ((cmd.showmenu === "hide") && (showUserMenu === true)) {
showUserMenu = false;
if (inIframe) {
if (menuButton) {
try { map.removeControl(menuButton); }
catch(e) {}
}
}
else { document.getElementById("bars").style.display="none"; }
}
else if ((cmd.showmenu === "show") && (showUserMenu === false)) {
showUserMenu = true;
if (inIframe) { map.addControl(menuButton); }
else { document.getElementById("bars").style.display="unset"; }
}
}
if (cmd.hasOwnProperty("showlayers")) {
if ((cmd.showlayers === "hide") && (showLayerMenu === true)) {
showLayerMenu = false;
layercontrol.removeFrom(map);
}
else if ((cmd.showlayers === "show") && (showLayerMenu === false)) {
showLayerMenu = true;
layercontrol = L.control.layers(basemaps, overlays).addTo(map);
}
}
if (cmd.hasOwnProperty("grid")) {
if (cmd.grid.hasOwnProperty("showgrid")) {
var changed = false;
if ((cmd.grid.showgrid == "true" || cmd.grid.showgrid == true ) && !showGrid) { changed = true; }
if ((cmd.grid.showgrid == "false" || cmd.grid.showgrid == false ) && showGrid) { changed = true; }
if (changed) {
showGrid = !showGrid;
if (showGrid) { Lgrid.addTo(map); }
else { Lgrid.removeFrom(map); }
}
}
if (cmd.grid.hasOwnProperty("opt")) {
Lgrid.initialize(cmd.grid.opt);
if (showGrid) {
Lgrid.removeFrom(map);
Lgrid.addTo(map);
}
}
}
if (cmd.hasOwnProperty("button")) {
if (cmd.button.icon) {
if (!buttons[cmd.button.name]) {
buttons[cmd.button.name] = L.easyButton( cmd.button.icon, function() {
ws.send(JSON.stringify({action:"button",name:cmd.button.name}));
}, cmd.button.name, { position:cmd.button.position||'topright' }).addTo(map);
}
}
else {
if (buttons[cmd.button.name]) {
buttons[cmd.button.name].removeFrom(map);
delete buttons[cmd.button.name];
}
}
}
if (cmd.hasOwnProperty("contextmenu")) {
if (typeof cmd.contextmenu === "string") {
addmenu = cmd.contextmenu;
rightmenuMap.setContent(addmenu);
}
}
if (cmd.hasOwnProperty("coords")) {
try { coords.removeFrom(map); }
catch(e) {}
if (cmd.coords == "dms") {
coords.options.useDMS = true;
showMouseCoords = "dms";
coords.addTo(map);
}
if (cmd.coords == "deg") {
coords.options.useDMS = false;
showMouseCoords = "deg";
coords.addTo(map);
}
}
var existsalready = false;
// Add a new base map layer
if (cmd.map && cmd.map.hasOwnProperty("name") && cmd.map.hasOwnProperty("url") && cmd.map.hasOwnProperty("opt")) {
console.log("BASE",cmd.map);
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 && !inIframe) {
if (!existsalready) {
layercontrol.addBaseLayer(basemaps[cmd.map.name],cmd.map.name);
}
}
// Remove one or more map layers (base or overlay)
if (cmd.map && cmd.map.hasOwnProperty("delete")) {
if (!Array.isArray(cmd.map.delete)) { cmd.map.delete = [cmd.map.delete]; }
for (var a=0; a < cmd.map.delete.length; a++) {
if (basemaps.hasOwnProperty(cmd.map.delete[a])) { delete basemaps[cmd.map.delete[a]]; }
if (overlays.hasOwnProperty(cmd.map.delete[a])) { delete overlays[cmd.map.delete[a]]; }
}
if (showLayerMenu) {
map.removeControl(layercontrol);
layercontrol = L.control.layers(basemaps, overlays).addTo(map);
}
}
// Add a new geojson overlay layer
if (cmd.map && cmd.map.hasOwnProperty("overlay") && cmd.map.hasOwnProperty("geojson") ) {
if (overlays.hasOwnProperty(cmd.map.overlay)) {
map.removeLayer(overlays[cmd.map.overlay]);
existsalready = true;
}
var opt = cmd.map.opt || { style:function(feature) {
var st = { stroke:true, weight:2, fill:true };
if (feature.hasOwnProperty("properties")) {
st.color = feature.properties.color||feature.properties.roofColor||"black";
if (feature.properties.hasOwnProperty("color")) { delete feature.properties.color; }
if (feature.properties.hasOwnProperty("roofColor")) { delete feature.properties.roofColor; }
}
if (feature.hasOwnProperty("properties") && feature.properties.hasOwnProperty('style')) {
if (feature.properties.style.hasOwnProperty('stroke')) {
st.color = feature.properties.style.stroke;
}
if (feature.properties.style.hasOwnProperty('stroke-width')) {
st.weight = feature.properties.style["stroke-width"];
}
if (feature.properties.style.hasOwnProperty('stroke-opacity')) {
st.opacity = feature.properties.style["stroke-opacity"];
}
if (feature.properties.style.hasOwnProperty('fill')) {
if (feature.properties.style.fill == "none") { st.fill = false; }
else { st.fillColor = feature.properties.style.fill; }
}
if (feature.properties.style.hasOwnProperty('fill-opacity')) {
st.fillOpacity = feature.properties.style["fill-opacity"];
}
}
delete feature.properties.style;
return st;
}};
opt.onEachFeature = function (f,l) {
l.bindPopup('