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 or shape (and notify websocket)
var delMarker = function(dname,note) {
if (note) { map.closePopup(); }
var pol = false;
if (typeof polygons[dname] != "undefined") {
layers[polygons[dname].lay].removeLayer(polygons[dname]);
delete polygons[dname];
pol = true;
}
if (typeof polygons[dname+"_"] != "undefined") {
layers[polygons[dname+"_"].lay].removeLayer(polygons[dname+"_"]);
delete polygons[dname+"_"];
}
if (typeof markers[dname] != "undefined") {
if (heat && markers[dname].hasOwnProperty("_latlng")) {
try { heat.delLatLng(markers[dname]._latlng); }
catch(e) { }
}
layers[markers[dname].lay].removeLayer(markers[dname]);
map.removeLayer(markers[dname]);
delete markers[dname];
}
delete allData[dname];
if (note) {
if (pol === true) { ws.send(JSON.stringify({action:"drawdelete", name:dname, deleted:true})); }
else { ws.send(JSON.stringify({action:"delete", name:dname, deleted:true})); }
}
}
var editPoly = function(pname,fun) {
map.closePopup();
if (fun === "rot") { polygons[pname].pm.enableRotate(); }
else if (fun === "drag") { polygons[pname].pm.enableLayerDrag(); }
else { polygons[pname].pm.enable(); }
polygons[pname].on("dblclick", function(e) {
if (fun === "rot") { polygons[pname].pm.disableRotate(); }
else if (fun === "drag") { polygons[pname].pm.disableLayerDrag(); }
else { polygons[pname].pm.disable(); }
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.value) { m.value = e.target.value; }
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));
})
}
var rangerings = function(latlng, options) {
options = L.extend({
ranges: [250,500,750,1000],
pan: 0,
fov: 60,
color: '#aaaa00'
}, 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 marker or shape to map function
function setMarker(data) {
var rightmenu = function(m) {
m.on('click', function(e) {
var fb = allData[data["name"]];
fb.action = "click";
ws.send(JSON.stringify(fb));
});
// 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 (allData.hasOwnProperty(data["name"]) && allData[data["name"]].hasOwnProperty("value")) {
for (const item in allData[data["name"]].value) {
rightcontext = rightcontext.replace(new RegExp("\\${"+item+"}","g"),allData[data["name"]].value[item]);
}
}
rightcontext = rightcontext.replace(/\${.*?}/g,'')
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 == true) { // remove markers we are told to
delMarker(data["name"]);
return;
}
var ll;
var lli = null;
var opt = data.options || {};
opt.color = opt.color ?? data.color ?? data.lineColor ?? "#910000";
opt.fillColor = opt.fillColor ?? data.fillColor ?? "#910000";
opt.stroke = opt.stroke ?? (data.hasOwnProperty("stroke")) ? data.stroke : true;
opt.weight = opt.weight ?? data.weight ?? 2;
opt.opacity = opt.opacity ?? data.opacity ?? 1;
if (!data.SIDC) { opt.fillOpacity = opt.fillOpacity ?? data.fillOpacity ?? 0.2; }
opt.clickable = (data.hasOwnProperty("clickable")) ? data.clickable : false;
opt.fill = 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("drawCount")) { drawCount = data.drawCount; }
// Draw lines
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);
}
// Draw Areas
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);
}
// Draw Great circles
if (data.hasOwnProperty("greatcircle") && Array.isArray(data.greatcircle) && data.greatcircle.length === 2) {
delete opt.fill;
opt.vertices = opt.vertices || 20;
if (!data.hasOwnProperty("weight")) { opt.weight = 3; } //Standard settings different for lines
if (!data.hasOwnProperty("opacity")) { opt.opacity = 0.8; }
var greatc = L.Polyline.Arc(data.greatcircle[0], data.greatcircle[1], opt);
var aml = new L.Wrapped.Polyline(greatc._latlngs, opt);
polygons[data["name"]] = rightmenu(aml);
}
// Draw error ellipses
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);
}
// Draw circles and ellipses
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], 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);
}
}
}
// Draw arcs (and range rings)
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);
}
}
// Draw a geojson "shape"
else if (data.hasOwnProperty("geojson")) {
doGeojson(data["name"],data.geojson,(data.layer || "unknown"),opt);
}
// If we created a shape then apply some generic things to it
if (polygons[data["name"]] !== undefined) {
// Set the layer
polygons[data["name"]].lay = lay;
// if clickable then add popup
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:true, minWidth:200});
}
// add a tooltip (if supplied)
if (data.hasOwnProperty("tooltip")) { polygons[data["name"]].bindTooltip(data.tooltip); }
// add to the layers
layers[lay].addLayer(polygons[data["name"]]);
// fly or fit to the bounds if required
if (data.hasOwnProperty("fly") && data.fly === true) {
map.flyToBounds(polygons[data["name"]].getBounds(),{padding:[50,50]})
}
else if (data.hasOwnProperty("fit") && data.fit === true) {
map.fitBounds(polygons[data["name"]].getBounds(),{padding:[50,50]})
}
}
// Now handle the markers
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")) {
if (isNaN(data.lat*1)) { console.log("Invalid lat: lat:",data.lat, " - lon:",data.lon); return; }
if (isNaN(data.lon*1)) { console.log("Invalid lon: lat:",data.lat, " - lon:",data.lon); return; }
ll = new L.LatLng((data.lat*1), (data.lon*1));
}
else if (data.hasOwnProperty("latitude") && data.hasOwnProperty("longitude")) {
if (isNaN(data.latitude*1)) { console.log("Invalid latitude: lat:",data.latitude, " - lon:",data.longitude); return; }
if (isNaN(data.longitude*1)) { console.log("Invalid longitude: lat:",data.latitude, " - lon:",data.longitude); return; }
ll = new L.LatLng((data.latitude*1), (data.longitude*1));
}
else {
// console.log("No location:",data);
return;
}
if (ll.lat === 0 && ll.lng === 0) {
// Add a little wobble so we can zoom into each if required.
console.log(data["name"],"is at null island.");
ll.lat = Math.round(1000000 * ll.lat + Math.random() * 10000 - 5000) / 1000000;
ll.lng = Math.round(1000000 * ll.lng + Math.random() * 10000 - 5000) / 1000000;
}
// 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.track ?? data.hdg ?? data.heading ?? data.bearing ?? "0") + map.getBearing();
if (data.icon === "ship") {
marker = L.boatMarker(ll, {
title: data["name"],
color: (data.iconColor ?? "#5DADE2")
});
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 = '';
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});
}
else if (data.icon === "smallplane") {
data.iconColor = data.iconColor ?? "black";
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});
}
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 = '';
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 = '';
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 = '';
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 === "quad") {
data.iconColor = data.iconColor || "black";
icon = '';
var svgquad = "data:image/svg+xml;base64," + btoa(icon);
myMarker = L.divIcon({
className:"quadicon",
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);
myMarker = L.divIcon({
className:"caricon",
iconAnchor: [16, 16],
html:'',
});
marker = L.marker(ll, {title:data["name"], icon:myMarker, draggable:drag});
}
else if (data.icon === "sensor") {
data.iconColor = data.iconColor || "#F39C12";
icon = '';
var svgcam = "data:image/svg+xml;base64," + btoa(icon);
myMarker = L.divIcon({
className:"camicon",
iconAnchor: [12, 12],
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);
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);
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 === "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 || "#00ffff";
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)) { // emoji icon :smile:
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?:.*$|^\/|^data:image\//)) { // web url icon 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-") { // fa icon
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-") { // weather icon
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({ // default - fa-icon in a marker shape
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")) { // NATO mil2525 icons
// "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","speed","country"];
//const regex = /\p{Extended_Pictographic}/ug;
const regex = /(\u00a9|\u00ae|[\u2000-\u3300]|\ud83c[\ud000-\udfff]|\ud83d[\ud000-\udfff]|\ud83e[\ud000-\udfff])/gi;
optfields.forEach(function (item) {
if (opts.hasOwnProperty(item) && regex.test(opts[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 });
edgeAware();
delete data.options;
}
else { // Otherwise just a generic map marker pin
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;
}
}
// Add right click contextmenu
marker = rightmenu(marker);
// Delete more already handled properties
var llc = data.lineColor ?? data.color;
delete data.lat;
delete data.latitude;
delete data.lon;
delete data.longitude;
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("fillColor")) { delete data.fillColor; }
if (data.hasOwnProperty("radius")) { delete data.radius; }
if (data.hasOwnProperty("greatcircle")) { delete data.greatcircle; }
// then any remaining properties to the info box
if (data.popup) { words = data.popup; }
else {
words += '
';
for (var i in data) {
if ((i != "name") && (i != "length") && (i != "clickable")) {
if (typeof data[i] === "object") {
words += '
'+ i +'
' + JSON.stringify(data[i]) + '
';
}
else {
// words += i +" : "+data[i]+" ";
words += '