Tidy up readme, fix example

general linting
This commit is contained in:
Dave Conway-Jones 2023-10-15 15:26:05 +01:00
parent 4b4a6ef94e
commit ac40ce1061
No known key found for this signature in database
GPG Key ID: 1DDB0E91A28C2643
2 changed files with 131 additions and 116 deletions

View File

@ -99,6 +99,7 @@ There are also several special icons...
- **bus** : a bus/coach icon that aligns with the heading of travel.
- **uav** : a small uav like icon that aligns with the heading of travel.
- **helicopter** : a small helicopter icon that aligns with the heading of travel.
- **sensor** : a camera icon that points to the heading angle.
- **arrow** : a map GPS arrow type pointer that aligns with the heading of travel.
- **wind** : a wind arrow that points in the direction the wind is coming FROM.
- **satellite** : a small satellite icon.
@ -153,7 +154,7 @@ following the great circle between the two co-ordinates is plotted.
msg.payload = {name:"GC1", color:"#ff00ff", greatcircle:[ [51.464,0], [25.76,-80.18] ] }
Shapes can also have a **popup** property containing html, but you MUST also set a property `clickable:true` in order to allow it to be seen.
Shapes can also have a **popup** property containing html, but you MUST also set a property `clickable:true` in order to allow it to be seen. You can also set **tooltip** to create a label that appears when you hover the mouse over the shape.
There are extra optional properties you can specify - see Options below.
@ -192,7 +193,7 @@ You can add supplemental arc(s) to a marker by adding an **arc** property as bel
Supplemental means that you can also specify a line using a **bearing** and **length** property.
```
msg.payload = { name:"Camera01", icon:"fa-camera", lat:51.05, lon:-1.35,
msg.payload = { name:"Camera01", icon:"sensor", lat:51.05, lon:-1.35,
bearing: 235,
length: 2200,
arc: {
@ -251,7 +252,7 @@ Often geojson may not have a `properties` or `style` property in which case you
2) You can just send a msg.payload containing the geojson itself - but obviously you then can't style it, set the name, layer, etc.
3) You can also add the geojson as a specific overlay, in which case you can also have more control of styles, and per feature customisations. See the section on overlays [below](#to-add-a-new-geojson-overlay). This is the most complex but customisable.
3) You can also add the geojson as a specific overlay, in which case you can also have more control of styles, and per feature customisations. See the section on overlays [below](#to-add-a-new-geojson-overlay). This is the most complex but also the most customisable.
### Options
@ -267,6 +268,7 @@ Areas, Rectangles, Lines, Circles and Ellipses can also specify more optional pr
- **clickable** : boolean - set to true to allow click to show popup.
- **popup** : html string to display in popup (as well as name).
- **editable** : boolean - set to true to allow simple edit/delete right click contextmenu.
- **tooltip** : Text string to display on mouse hover over the shape.
- **contextmenu** : html string to display a more complex right click contextmenu.
- **weight** : the width of the line or outline.
@ -377,7 +379,7 @@ The **worldmap in** node can be used to receive various events from the map. Exa
{ "action": "feedback", "name": "some name", "value": "some value", "lat":51, "lon":0, "layer":"unknown" } // when a user calls the feedback function - see below
If File Drop is enabled - then the map can accept files of type gpx, kml, nvg, jpeg, png and geojson. The file content property will always be a binary buffer. The lat, lon of the cursor drop point will be included. Tracks will be locally rendered on the map. The node-red-node-exif node can be used to extract location information from a jpeg image and then geolocate it back on the map. Png images will be located where they are dropped but can then be dragged if required.
If File Drop is enabled - then the map can accept files of type gpx, kml, nvg, jpeg, png and geojson. The file content property will always be a binary buffer. The lat, lon of the cursor drop point will be included. Tracks will be locally rendered on the map. The `node-red-node-exif` node can be used to extract location information from a jpeg image and then geolocate it back on the map. Png images will be located where they are dropped but can then be dragged if required.
All actions also include a:
`msg._sessionid` property that indicates which client session they came from. Any msg sent out that includes this property will ONLY be sent to that session - so you can target map updates to specific sessions if required.
@ -419,15 +421,15 @@ Optional properties for **msg.payload.command** include
- **lat** - move map to specified latitude.
- **lon** - move map to specified longitude.
- **rotation** - rotate the base map to the specified compass angle.
- **zoom** - move map to specified zoom level (1 - world, 13 to 20 max zoom depending on map).
- **bounds** - if set to an array `[ [ lat(S), lon(W) ], [lat(N), lon(E)] ]` - sets the overall map bounds.
- **rotation** - rotate the base map to the specified compass angle.
- **layer** - set map to specified base layer name - `{"command":{"layer":"Esri"}}`
- **search** - search markers on map for name containing `string`. If not found in existing markers, will then try geocoding looking using Nominatim. An empty string `""` clears the search results. - `{"command":{"search":"Winchester"}}`
- **showlayer** - show the named overlay(s) - `{"command":{"showlayer":"foo"}}` or `{"command":{"showlayer":["foo","bar"]}}`
- **hidelayer** - hide the named overlay(s) - `{"command":{"hidelayer":"bar"}}` or `{"command":{"hidelayer":["bar","another"]}}`
- **side** - add a second map alongside with slide between them. Use the name of a *baselayer* to add - or "none" to remove the control. - `{"command":{"side":"Esri Satellite"}}`
- **split** - once you have split the screen - the split value is the % across the screen of the split line. - `{"command":{"split":50}}`
- **split** - once you have split the screen with the *side* command - the split value is then the % across the screen of the split line. - `{"command":{"split":50}}`
- **map** - Object containing details of a new map layer:
- **name** - name of the map base layer OR **overlay** - name of overlay layer
- **url** - url of the map layer
@ -448,7 +450,7 @@ Optional properties for **msg.payload.command** include
a msg `{"action":"button", "name":"the_button_name"}` to the worldmap in node. If supplied with a `name` property only, it will remove the button. Optional `position` property can be 'bottomright', 'bottomleft', 'topleft' or 'topright' (default). button can also be an array of button objects.
- **contextmenu** - html string to define the right click menu when not on a marker. Defaults to the simple add marker input. Empty string `""` disables this right click.
- **toptitle** - Words to replace title in title bar (if not in iframe)
- **toplogo** - URL to logo image for top tile bar (if not in iframe) - ideally 60px by 24px.
- **toplogo** - URL to logo image for top title bar (if not in iframe) - ideally 60px by 24px.
- **trackme** - Turns on/off the browser self locating. Boolean false = off, true = cyan circle showing accuracy error, or an object like `{"command":{"trackme":{"name":"Dave","icon":"car","iconColor":"blue","layer":"mytrack","accuracy":false}}}`. Usual marker options can be applied.
- **showmenu** - Show or hide the display of the hamberger menu control in the top right . Values can be "show" or "hide". - `{"command":{"showmenu": "hide"}}`
- **showlayers** - Show or hide the display of selectable layers. Does not control the display of an individual layer, rather a users ability to interact with them. Values can be "show" or "hide". - `{"command":{"showlayers": "hide"}}`
@ -508,7 +510,6 @@ Example simple form
[{"id":"7351100bacb1f5fe","type":"function","z":"4aa2ed2fd1b11362","name":"","func":"msg.payload = { command: {\ncontextmenu: String.raw`\nText <input type=\"text\" id=\"sometext\" value=\"hello\"><br/>\nNumber <input type=\"number\" id=\"somenum\" value=\"5\"><br/>\n<input type=\"button\" value=\"Send\" onclick=\n'feedback(\"myform\",{\n \"st\":document.getElementById(\"sometext\").value,\n \"sn\":document.getElementById(\"somenum\").value,\n})'\n>\n`\n}}\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":350,"y":360,"wires":[["a6a82f2e8efc44fc"]]},{"id":"7b595f0c8f6ac710","type":"worldmap in","z":"4aa2ed2fd1b11362","name":"","path":"/worldmap","events":"connect","x":195,"y":360,"wires":[["7351100bacb1f5fe"]]}]
```
See the section on **Utility Functions** for details of the feedback function.
#### To add and remove a legend
@ -604,7 +605,7 @@ The `fit` property is optional, and you can also use `fly` if you wish. If boole
#### To add a new KML, GPX, or TOPOJSON overlay
As per the geojson overlay you can also inject a KML layer, GPX layer or TOPOJSON layer. The syntax is the same but with either a `kml` property containing the KML string - a `gpx` property containing a GPX string - or a `topojson` property containing the topojson.
As with the geojson overlay, you can also inject a KML layer, GPX layer or TOPOJSON layer. The syntax is the same but with either a `kml` property containing the KML string - a `gpx` property containing a GPX string - or a `topojson` property containing the topojson.
msg.payload.command.map = {
"overlay": "myKML",
@ -700,7 +701,7 @@ Feeding this into the tracks node will also remove the tracks stored for that la
### Using a local Map Server (WMS server)
IMHO the easiest map server to make work is the <a href="http://www.mapserver.org/" target="mapinfo">mapserver</a> package in Ubuntu / Debian. Usually you will start with
IMHO the easiest WMS map server to make work is the <a href="http://www.mapserver.org/" target="mapinfo">mapserver</a> package in Ubuntu / Debian. Usually you will start with
sudo apt-get install mapserver-bin cgi-mapserver gdal-bin
@ -730,7 +731,6 @@ You can then add a new WMS Base layer by injecting a message like
"wms": true // set to true for WMS type mapserver
}}}
#### Using a Docker Map Server
You can use a docker container like https://hub.docker.com/r/camptocamp/mapserver, then assuming you have the mapfile 'my-app.map' in the current working directory, you could mount it as:
@ -768,7 +768,7 @@ The following example gets recent earthquakes from USGS, parses the result,
formats up the msg as per above and sends to the node to plot on the map.
It also shows how to zoom and move the map or add a new layer.
[{"id":"86457344.50e6b","type":"inject","z":"745a133b.dd6dec","name":"","topic":"","payload":"","payloadType":"none","repeat":"","crontab":"","once":false,"x":190,"y":2420,"wires":[["9a142026.fa47f"]]},{"id":"9a142026.fa47f","type":"function","z":"745a133b.dd6dec","name":"add new layer","func":"msg.payload = {};\nmsg.payload.command = {};\n\nvar u = 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png';\nvar o = { maxZoom: 19, attribution: '&copy; OpenStreetMap'};\n\nmsg.payload.command.map = {name:\"OSMhot\", url:u, opt:o};\nmsg.payload.command.layer = \"OSMhot\";\n\nreturn msg;","outputs":1,"noerr":0,"x":420,"y":2420,"wires":[["c643e022.1816c"]]},{"id":"c643e022.1816c","type":"worldmap","z":"745a133b.dd6dec","name":"","x":750,"y":2460,"wires":[]},{"id":"2998e233.4ba64e","type":"function","z":"745a133b.dd6dec","name":"USGS Quake monitor csv re-parse","func":"msg.payload.lat = msg.payload.latitude;\nmsg.payload.lon = msg.payload.longitude;\nmsg.payload.layer = \"earthquake\";\nmsg.payload.name = msg.payload.id;\nmsg.payload.icon = \"globe\";\nmsg.payload.iconColor = \"orange\";\n\ndelete msg.payload.latitude;\ndelete msg.payload.longitude;\t\nreturn msg;","outputs":1,"noerr":0,"x":540,"y":2560,"wires":[["c643e022.1816c"]]},{"id":"e72c5732.9fa198","type":"function","z":"745a133b.dd6dec","name":"move and zoom","func":"msg.payload = { command:{layer:\"Esri Terrain\",lat:0,lon:0,zoom:3} };\nreturn msg;","outputs":1,"noerr":0,"x":420,"y":2460,"wires":[["c643e022.1816c"]]},{"id":"12317723.589249","type":"csv","z":"745a133b.dd6dec","name":"","sep":",","hdrin":true,"hdrout":"","multi":"one","ret":"\\n","temp":"","x":390,"y":2500,"wires":[["2998e233.4ba64e"]]},{"id":"10e5e5f0.8daeaa","type":"inject","z":"745a133b.dd6dec","name":"","topic":"","payload":"","payloadType":"none","repeat":"","crontab":"","once":false,"x":190,"y":2460,"wires":[["e72c5732.9fa198"]]},{"id":"b6917d83.d1bac","type":"http request","z":"745a133b.dd6dec","name":"","method":"GET","url":"http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.csv","x":270,"y":2560,"wires":[["12317723.589249"]]},{"id":"3842171.4d487e8","type":"inject","z":"745a133b.dd6dec","name":"Quakes","topic":"","payload":"","payloadType":"none","repeat":"900","crontab":"","once":false,"x":200,"y":2500,"wires":[["b6917d83.d1bac"]]}]
[{"id":"86457344.50e6b","type":"inject","z":"cb7b09e3354afd4c","name":"","repeat":"","crontab":"","once":false,"topic":"","payload":"","payloadType":"none","x":170,"y":500,"wires":[["9a142026.fa47f"]]},{"id":"9a142026.fa47f","type":"function","z":"cb7b09e3354afd4c","name":"add new layer","func":"msg.payload = {};\nmsg.payload.command = {};\n\nvar u = 'http://{s}.tile.openstreetmap.fr/hot/{z}/{x}/{y}.png';\nvar o = { maxZoom: 19, attribution: '&copy; OpenStreetMap'};\n\nmsg.payload.command.map = {name:\"OSMhot\", url:u, opt:o};\nmsg.payload.command.layer = \"OSMhot\";\n\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":500,"wires":[["c643e022.1816c"]]},{"id":"c643e022.1816c","type":"worldmap","z":"cb7b09e3354afd4c","name":"","lat":"30","lon":"0","zoom":"3","layer":"OSMG","cluster":"","maxage":"","usermenu":"show","layers":"show","panit":"false","panlock":"false","zoomlock":"false","hiderightclick":"false","coords":"deg","showgrid":"false","showruler":"false","allowFileDrop":"false","path":"worldmap","overlist":"CO,RA,DN","maplist":"OSMG,OSMH,EsriS","mapname":"","mapurl":"","mapopt":"","mapwms":false,"x":640,"y":540,"wires":[]},{"id":"2998e233.4ba64e","type":"function","z":"cb7b09e3354afd4c","name":"USGS Quake monitor csv re-parse","func":"msg.payload.lat = msg.payload.latitude;\nmsg.payload.lon = msg.payload.longitude;\nmsg.payload.layer = \"earthquake\";\nmsg.payload.name = msg.payload.id;\nmsg.payload.icon = \"globe\";\nmsg.payload.iconColor = \"orange\";\n\ndelete msg.payload.latitude;\ndelete msg.payload.longitude;\t\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":520,"y":640,"wires":[["c643e022.1816c"]]},{"id":"e72c5732.9fa198","type":"function","z":"cb7b09e3354afd4c","name":"move and zoom","func":"msg.payload = { command:{layer:\"Esri Terrain\",lat:0,lon:-90,zoom:2} };\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":400,"y":540,"wires":[["c643e022.1816c"]]},{"id":"12317723.589249","type":"csv","z":"cb7b09e3354afd4c","name":"","sep":",","hdrin":true,"hdrout":"","multi":"one","ret":"\\n","temp":"","x":370,"y":580,"wires":[["2998e233.4ba64e"]]},{"id":"10e5e5f0.8daeaa","type":"inject","z":"cb7b09e3354afd4c","name":"","repeat":"","crontab":"","once":false,"topic":"","payload":"","payloadType":"none","x":170,"y":540,"wires":[["e72c5732.9fa198"]]},{"id":"b6917d83.d1bac","type":"http request","z":"cb7b09e3354afd4c","name":"","method":"GET","url":"http://earthquake.usgs.gov/earthquakes/feed/v1.0/summary/2.5_day.csv","x":250,"y":640,"wires":[["12317723.589249"]]},{"id":"3842171.4d487e8","type":"inject","z":"cb7b09e3354afd4c","name":"Quakes","repeat":"900","crontab":"","once":false,"topic":"","payload":"","payloadType":"none","x":180,"y":580,"wires":[["b6917d83.d1bac"]]}]
---

View File

@ -16,7 +16,7 @@ var buttons = {};
var marksIndex = 0;
var menuOpen = false;
var clusterAt = 0;
var maxage = 900; // default max age of icons on map in seconds - cleared after 10 mins
var maxage = 900; // default max age of icons on map in seconds - cleared after 15 mins
var baselayername = "OSM grey"; // Default base layer OSM but uniform grey
var pagefoot = "&nbsp;&copy; DCJ 2023";
var inIframe = false;
@ -59,31 +59,30 @@ var iconSz = {
var filesAdded = '';
var loadStatic = function(fileName) {
if(filesAdded.indexOf(fileName) !== -1)
return
if (filesAdded.indexOf(fileName) !== -1) { return; }
var head = document.getElementsByTagName('head')[0]
if (fileName.indexOf('js') !== -1) {
var script = document.createElement('script')
script.src = fileName
script.type = 'text/javascript'
console.log("Loading: ",fileName)
head.append(script)
filesAdded += ' ' + fileName
} else if (fileName.indexOf('css') !== -1) {
var style = document.createElement('link')
style.href = fileName
style.type = 'text/css'
style.rel = 'stylesheet'
console.log("Loading: ",fileName)
head.append(style);
filesAdded += ' ' + fileName
} else {
console.log("Unsupported file type: ",fileName)
var script = document.createElement('script');
script.src = fileName;
script.type = 'text/javascript';
console.log("Loading: ",fileName);
head.append(script);
filesAdded += ' ' + fileName;
}
else if (fileName.indexOf('css') !== -1) {
var style = document.createElement('link');
style.href = fileName;
style.type = 'text/css';
style.rel = 'stylesheet';
console.log("Loading: ",fileName);
head.append(style);;
filesAdded += ' ' + fileName;
}
else {
console.log("Unsupported file type: ",fileName);
}
}
// L.PM.setOptIn(true);
// Create the socket
var connect = function() {
// var transports = ["websocket", "xhr-streaming", "xhr-polling"],
@ -118,8 +117,6 @@ 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")) {
@ -130,11 +127,11 @@ var handleData = function(data) {
data = {command:{map:{overlay:"KML", kml:data[prop].payload}}};
doCommand(data.command); return;
}
else { console.log("SKIP A",data[prop]); }
else { console.log("SKIP array item",data[prop]); }
}
// map.fitBounds(bnds.pad(0.25));
}
else {
// Handle some raw string data overlays
if (typeof data === "string" && data.indexOf("<?xml") == 0) {
if (data.indexOf("<nvg") != -1) {
data = {command:{map:{overlay:"NVG", nvg:data}}};
@ -146,6 +143,8 @@ var handleData = function(data) {
data = {command:{map:{overlay:"GPX", gpx:data}}};
}
}
// handle any commands in the data
if (data.command) { doCommand(data.command); delete data.command; }
// handle raw geojson type msg
@ -159,7 +158,7 @@ var handleData = function(data) {
else if (data.hasOwnProperty("event") && data.event.hasOwnProperty("point")) {
doTAKjson(data.event);
}
// handle TAK json (from multicast Protobuf)
// handle TAK json (from multicast Protobuf via tak-ingest node)
else if (data.hasOwnProperty("cotEvent") && data.cotEvent.hasOwnProperty("lat") && data.cotEvent.hasOwnProperty("lon")) {
doTAKMCjson(data.cotEvent);
}
@ -175,7 +174,7 @@ var handleData = function(data) {
}
}
window.onunload = function() { if (ws) ws.close(); }
window.onunload = function() { if (ws) { ws.close(); } }
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);
@ -209,14 +208,6 @@ if (inIframe === true) {
startzoom = window.localStorage.getItem("lastzoom");
}
}
// if ( window.localStorage.hasOwnProperty("clusterat") ) {
// clusterAt = window.localStorage.getItem("clusterat");
// document.getElementById("setclus").value = clusterAt;
// }
// if ( window.localStorage.hasOwnProperty("maxage") ) {
// maxage = window.localStorage.getItem("maxage");
// document.getElementById("maxage").value = maxage;
// }
// Create the Initial Map object.
map = new L.map('map',{
@ -372,7 +363,7 @@ if (inIframe) {
document.getElementById("menu").style.borderRadius="6px";
}
else {
console.log("NOT in an iframe");
//console.log("NOT in an iframe");
if (!showUserMenu) { document.getElementById("bars").style.display="none"; }
// Add the fullscreen button
@ -503,12 +494,12 @@ var edgeAware = function() {
}
edgeLayer.addTo(map)
}
// end of edge function
// end of edgeAware function
var panit = false;
function doPanit(v) {
if (v !== undefined) { panit = v; }
console.log("Panit set :",panit);
// console.log("Panit set :",panit);
}
var heatAll = false;
@ -518,24 +509,23 @@ function doHeatAll(v) {
}
var lockit = false;
var mb = new L.LatLngBounds([[-120,-360],[120,360]]);
var mbnds = 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]]);
mbnds = new L.LatLngBounds([[-120,-360],[120,360]]);
map.dragging.enable();
}
else {
mb = map.getBounds();
mbnds = 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);
map.setMaxBounds(mbnds);
//console.log("Map bounds lock :",lockit);
}
@ -610,7 +600,7 @@ function doSearch() {
marks = [];
marksIndex = 0;
for (var key in markers) {
if ( (~(key.toLowerCase()).indexOf(value.toLowerCase())) && (mb.contains(markers[key].getLatLng()))) {
if ( (~(key.toLowerCase()).indexOf(value.toLowerCase())) && (mbnds.contains(markers[key].getLatLng()))) {
marks.push(markers[key]);
}
if (markers[key].icon === value) {
@ -645,7 +635,8 @@ function doSearch() {
else {
if (lockit) {
document.getElementById('searchResult').innerHTML = "&nbsp;<font color='#ff0'>Found "+marks.length+" results within bounds.</font>";
} else {
}
else {
document.getElementById('searchResult').innerHTML = "&nbsp;<font color='#ff0'>Found "+marks.length+" results.</font>";
}
}
@ -668,7 +659,7 @@ function clearSearch() {
marks = [];
marksIndex = 0;
for (var key in markers) {
if ( (~(key.toLowerCase()).indexOf(value.toLowerCase())) && (mb.contains(markers[key].getLatLng()))) {
if ( (~(key.toLowerCase()).indexOf(value.toLowerCase())) && (mbnds.contains(markers[key].getLatLng()))) {
marks.push(markers[key]);
}
}
@ -694,7 +685,8 @@ function toggleMenu() {
menuOpen = !menuOpen;
if (menuOpen) {
document.getElementById("menu").style.display = 'block';
} else {
}
else {
document.getElementById("menu").style.display = 'none';
dialogue.close();
}
@ -930,7 +922,6 @@ map.on('contextmenu', function(e) {
}
});
// 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);
@ -1271,7 +1262,8 @@ var addOverlays = function(overlist) {
numbers.push(current);
current = 0;
shift = 0;
} else {
}
else {
shift += 5;
}
}
@ -1395,6 +1387,7 @@ var addOverlays = function(overlist) {
if (!inIframe) { layercontrol.addTo(map); }
else { showLayerMenu = false;}
// Add optional mouse co-ordinates display
var coords = L.control.mouseCoordinate({position:"bottomleft"});
// Add an optional legend
@ -1482,7 +1475,6 @@ var editPoly = function(pname,fun) {
})
}
var rangerings = function(latlng, options) {
options = L.extend({
ranges: [250,500,750,1000],
@ -1503,7 +1495,7 @@ var rangerings = function(latlng, options) {
return rings;
}
// the MAIN add something to map function
// the MAIN add marker or shape to map function
function setMarker(data) {
var rightmenu = function(m) {
m.on('click', function(e) {
@ -1630,30 +1622,23 @@ function setMarker(data) {
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);
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]})
}
}
// 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);
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]})
}
}
if (data.hasOwnProperty("drawCount")) { drawCount = data.drawCount; }
// Draw Great circles
if (data.hasOwnProperty("greatcircle") && Array.isArray(data.greatcircle) && data.greatcircle.length === 2) {
delete opt.fill;
opt.vertices = opt.vertices || 20;
@ -1661,25 +1646,20 @@ function setMarker(data) {
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);
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]})
}
}
// 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]*Math.cos(data.lat*Math.PI/180), data.radius[1]], data.tilt || 0, opt);
polycirc = L.ellipse(new L.LatLng((data.lat*1), (data.lon*1)), [data.radius[0], data.radius[1]], data.tilt || 0, opt);
}
else {
@ -1692,27 +1672,41 @@ function setMarker(data) {
}
}
}
// 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 = "<b>"+data.name+"</b>";
if (data.popup) { words = words + "<br/>" + data.popup; }
polygons[data.name].bindPopup(words, {autoClose:false, closeButton:true, closeOnClick:false, minWidth:200});
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); }
//polygons[data.name] = rightmenu(polygons[data.name]); // DCJ Investigate
// 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;
@ -1853,6 +1847,17 @@ function setMarker(data) {
});
marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag});
}
else if (data.icon === "sensor") {
data.iconColor = data.iconColor || "#F39C12";
icon = '<svg viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg"><path fill="'+data.iconColor+'" d="M 478.281 5.437 L 367.741 118.227 L 367.741 84.075 C 367.741 38.352 344.315 1.298 315.417 1.298 L 53.768 1.298 C 24.87 1.298 1.434 38.352 1.434 84.075 L 1.434 415.183 C 1.434 460.893 24.87 497.959 53.768 497.959 L 315.417 497.959 C 344.315 497.959 367.741 460.893 367.741 415.183 L 367.741 381.031 L 478.281 493.808 C 490.714 504.155 498.566 486.571 498.566 476.224 L 498.566 21.993 C 498.566 11.646 491.37 -6.979 478.281 5.437 Z M 341.573 415.183 C 341.573 438.044 329.86 456.571 315.417 456.571 L 53.768 456.571 C 39.314 456.571 27.612 438.044 27.612 415.183 L 27.612 84.075 C 27.612 61.226 39.314 42.687 53.768 42.687 L 315.417 42.687 C 329.86 42.687 341.573 61.226 341.573 84.075 L 341.573 415.183 Z M 472.398 438.975 L 367.741 332.406 L 367.741 166.853 L 472.398 60.27 L 472.398 438.975 Z" style="transform-origin: 250.000025px 249.628505px;" transform="matrix(0, -1, 1, 0, -0.000013709068, 0.000009864569)"/></svg>';
var svgcam = "data:image/svg+xml;base64," + btoa(icon);
myMarker = L.divIcon({
className:"camicon",
iconAnchor: [12, 12],
html:'<img src="'+svgcam+'" style="width:24px; height:24px; -webkit-transform:rotate('+dir+'deg); -moz-transform:rotate('+dir+'deg);"/>',
});
marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag});
}
else if (data.icon === "arrow") {
data.iconColor = data.iconColor || "black";
icon = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="32px" height="32px" viewBox="0 0 32 32">';
@ -1958,7 +1963,7 @@ function setMarker(data) {
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)) {
else if (data.icon.match(/^:.*:$/g)) { // emoji icon :smile:
var em = emojify(data.icon);
var col = data.iconColor ?? "#910000";
myMarker = L.divIcon({
@ -1969,7 +1974,7 @@ function setMarker(data) {
marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag});
labelOffset = [12,-4];
}
else if (data.icon.match(/^https?:.*$|^\//)) {
else if (data.icon.match(/^https?:.*$|^\//)) { // web url icon https://...
var sz = data.iconSize ?? 32;
myMarker = L.icon({
iconUrl: data.icon,
@ -1981,7 +1986,7 @@ function setMarker(data) {
labelOffset = [sz/2-4,-4];
delete data.iconSize;
}
else if (data.icon.substr(0,3) === "fa-") {
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 "; }
@ -1995,7 +2000,7 @@ function setMarker(data) {
marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag});
labelOffset = [8,-8];
}
else if (data.icon.substr(0,3) === "wi-") {
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 "; }
@ -2010,7 +2015,7 @@ function setMarker(data) {
labelOffset = [16,-16];
}
else {
myMarker = L.VectorMarkers.icon({
myMarker = L.VectorMarkers.icon({ // default - fa-icon in a marker shape
icon: data.icon ?? "circle",
markerColor: (data.iconColor ?? "#910000"),
prefix: 'fa',
@ -2020,7 +2025,7 @@ function setMarker(data) {
labelOffset = [6,-6];
}
}
else if (data.hasOwnProperty("SIDC")) {
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
@ -2049,7 +2054,7 @@ function setMarker(data) {
marker = L.marker(ll, { title:data.name, icon:myicon, draggable:drag });
edgeAware();
}
else {
else { // Otherwise just a generic map marker pin
myMarker = L.VectorMarkers.icon({
icon: "circle",
markerColor: (data.iconColor ?? "#910000"),
@ -2094,7 +2099,8 @@ function setMarker(data) {
}
// tidy up altitude
if (data.hasOwnProperty("alt")) {
if (data.hasOwnProperty("alt")||data.hasOwnProperty("altitude")) {
data.alt = data.alt ?? data.altitude;
var reft = new RegExp('feet|ft','i');
var refm = new RegExp('metres|m','i');
if ( reft.test(""+data.alt) ) {
@ -2108,8 +2114,8 @@ function setMarker(data) {
}
}
// remove icon from list of properties, then add all others to popup
if (data.hasOwnProperty("SIDC") && data.hasOwnProperty("options")) { delete data.options; }
// remove items from list of properties, then add all others to popup
if (data.hasOwnProperty("options")) { delete data.options; }
if (data.hasOwnProperty("icon")) { delete data.icon; }
if (data.hasOwnProperty("iconColor")) { delete data.iconColor; }
if (data.hasOwnProperty("photourl")) {
@ -2137,7 +2143,8 @@ function setMarker(data) {
if (!Array.isArray(data.weblink) || !data.weblink.length) {
if (typeof data.weblink === "string") {
words += "<b><a href='"+ data.weblink + "' target='_new'>more information...</a></b><br/>";
} else {
}
else {
var tgt = data.weblink.target || "_new";
words += "<b><a href='"+ data.weblink.url + "' target='"+ tgt + "'>" + data.weblink.name + "</a></b><br/>";
}
@ -2185,10 +2192,11 @@ function setMarker(data) {
}
}
// Add right click contextmenu
marker = rightmenu(marker);
// Add any remaining properties to the info box
var llc = data.lineColor || data.color;
// Delete more already handled properties
var llc = data.lineColor ?? data.color;
delete data.lat;
delete data.lon;
if (data.arc) { delete data.arc; }
@ -2204,15 +2212,17 @@ function setMarker(data) {
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 += '<table>';
for (var i in data) {
if ((i != "name") && (i != "length") && (i != "clickable")) {
if (typeof data[i] === "object") {
//
words += '<tr><td>'+ i +'</td><td>' + JSON.stringify(data[i]) + '</td></tr>';
} else {
}
else {
// words += i +" : "+data[i]+"<br/>";
words += '<tr><td>'+ i +'</td><td>' + data[i] + '</td></tr>';
}
@ -2223,7 +2233,7 @@ function setMarker(data) {
}
words = "<b>"+data.name+"</b><br/>" + words.replace(/\${name}/g,data.name); //"<button style=\"border-radius:4px; float:right; background-color:lightgrey;\" onclick='popped=false;popmark.closePopup();'>X</button><br/>" + words;
var wopt = {autoClose:false, closeButton:true, closeOnClick:false, minWidth:200};
if (words.indexOf('<video ') >=0 || words.indexOf('<img ') >=0 ) { wopt.maxWidth="640"; }
if (words.indexOf('<video ') >=0 || words.indexOf('<img ') >=0 ) { wopt.maxWidth="640"; } // make popup wider if it has an image or video
if (!data.hasOwnProperty("clickable") && data.clickable != false) {
marker.bindPopup(words, wopt);
marker._popup.dname = data.name;
@ -2241,11 +2251,15 @@ function setMarker(data) {
}
markers[data.name] = marker;
layers[lay].addLayer(marker);
var track;
if (data.track !== undefined) { track = data.track; }
else if (data.hdg !== undefined) { track = data.hdg; }
else if (data.heading !== undefined) { track = data.heading; }
else if (data.bearing !== undefined) { track = data.bearing; }
// var track;
// if (data.track !== undefined) { track = data.track; }
// else if (data.hdg !== undefined) { track = data.hdg; }
// else if (data.heading !== undefined) { track = data.heading; }
// else if (data.bearing !== undefined) { track = data.bearing; }
// Now add any leader lines
var track = data.track ?? data.hdg ?? data.heading ?? data.bearing;
if (track != undefined) { // if there is a heading
if (data.speed != null && data.length === undefined) { // and a speed - lets convert to a leader length
data.length = parseFloat(data.speed || "0") * 60;
@ -2278,7 +2292,8 @@ function setMarker(data) {
var x3 = x + Math.cos((90-angle-data.accuracy)/180*Math.PI)*lengthAsDegrees/Math.cos(y/180*Math.PI);
var ll3 = new L.LatLng(y3,x3);
polygon = L.polygon([ ll1, ll2, ll3 ], {weight:2, color:llc||'#900', fillOpacity:0.06, clickable:false});
} else {
}
else {
var ya = y + Math.sin((90-angle)/180*Math.PI)*lengthAsDegrees;
var xa = x + Math.cos((90-angle)/180*Math.PI)*lengthAsDegrees/Math.cos(y/180*Math.PI);
var lla = new L.LatLng(ya,xa);
@ -2336,10 +2351,6 @@ function doCommand(cmd) {
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;
@ -2432,6 +2443,10 @@ function doCommand(cmd) {
if (trackMeButton !== undefined) { trackMeButton.state('track-on'); }
}
}
if (cmd.hasOwnProperty("hiderightclick")) {
if (cmd.hiderightclick == "true" || cmd.hiderightclick == true) { hiderightclick = true; }
else { hiderightclick = false; }
}
if (cmd.hasOwnProperty("contextmenu")) {
if (typeof cmd.contextmenu === "string") {
addmenu = cmd.contextmenu;
@ -2905,7 +2920,7 @@ function doCommand(cmd) {
}
}
}
// Lock the pan so map can be moved
// Lock the pan so map can't be moved
if (cmd.hasOwnProperty("panlock")) {
if (cmd.panlock == "true" || cmd.panlock == true) { lockit = true; }
else { lockit = false; doLock(false); }
@ -2929,7 +2944,6 @@ function doCommand(cmd) {
}
}
}
if (cmd.hasOwnProperty("cluster")) {
clusterAt = cmd.cluster;
document.getElementById("setclus").value = cmd.cluster;
@ -2966,7 +2980,8 @@ function doCommand(cmd) {
if (cmd.bounds.length === 2 && cmd.bounds[0].length === 2 && cmd.bounds[1].length === 2) {
if (cmd.hasOwnProperty("fly") && cmd.fly === true) {
map.flyToBounds(cmd.bounds);
} else {
}
else {
map.fitBounds(cmd.bounds);
}
}