add covex-hull node and update info
This commit is contained in:
parent
fa132af571
commit
444c1f1fcf
@ -1,6 +1,7 @@
|
|||||||
### Change Log for Node-RED Worldmap
|
### Change Log for Node-RED Worldmap
|
||||||
|
|
||||||
- v2.3.15 - Add heading to default addMarker, allow custom http icon size.
|
- v2.4.1 - Add convex-hull node for grouping objects.
|
||||||
|
- v2.3.16 - Add heading to default addMarker, allow custom http icon size.
|
||||||
- v2.3.13 - Fix geoson feature properties fill color, and better marker handling
|
- v2.3.13 - Fix geoson feature properties fill color, and better marker handling
|
||||||
- v2.3.11 - Better editing of drawing layer, add OpenTopoMap, and better Esri satellite
|
- v2.3.11 - Better editing of drawing layer, add OpenTopoMap, and better Esri satellite
|
||||||
- v2.3.10 - Improve geojson layer and name handling.
|
- v2.3.10 - Improve geojson layer and name handling.
|
||||||
|
@ -11,7 +11,8 @@ map web page for plotting "things" on.
|
|||||||
|
|
||||||
### Updates
|
### Updates
|
||||||
|
|
||||||
- v2.3.15 - Add heading to default addMarker, allow custom http icon size.
|
- v2.4.1 - Add convex-hull node for grouping objects.
|
||||||
|
- v2.3.16 - Add heading to default addMarker, allow custom http icon size.
|
||||||
- v2.3.13 - Fix geoson feature properties fill color, and better marker handling
|
- v2.3.13 - Fix geoson feature properties fill color, and better marker handling
|
||||||
- v2.3.11 - Better editing of drawing layer, add OpenTopoMap, and better Esri satellite
|
- v2.3.11 - Better editing of drawing layer, add OpenTopoMap, and better Esri satellite
|
||||||
- v2.3.10 - Improve geojson layer and name handling.
|
- v2.3.10 - Improve geojson layer and name handling.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "node-red-contrib-web-worldmap",
|
"name": "node-red-contrib-web-worldmap",
|
||||||
"version": "2.3.15",
|
"version": "2.4.1",
|
||||||
"description": "A Node-RED node to provide a web page of a world map for plotting things on.",
|
"description": "A Node-RED node to provide a web page of a world map for plotting things on.",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"cgi": "0.3.1",
|
"cgi": "0.3.1",
|
||||||
|
140
worldmap.html
140
worldmap.html
@ -504,54 +504,96 @@ then by default <code>⌘⇧m</code> - <code>ctrl-shift-m</code> will load the m
|
|||||||
|
|
||||||
|
|
||||||
<script type="text/html" data-template-name="worldmap-tracks">
|
<script type="text/html" data-template-name="worldmap-tracks">
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-input-depth"><i class="fa fa-map-marker"></i> Number of</label>
|
<label for="node-input-depth"><i class="fa fa-map-marker"></i> Number of</label>
|
||||||
points in track <input type="text" id="node-input-depth" style="width:50%" placeholder="number - default 20">
|
points in track <input type="text" id="node-input-depth" style="width:50%" placeholder="number - default 20">
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-input-layer"><i class="fa fa-map"></i> Track Layer</label>
|
<label for="node-input-layer"><i class="fa fa-map"></i> Track Layer</label>
|
||||||
<select id="node-input-layer">
|
<select id="node-input-layer">
|
||||||
<option value="combined">on marker layer</option>
|
<option value="combined">on marker layer</option>
|
||||||
<option value="separate">one per marker layer</option>
|
<option value="separate">one per marker layer</option>
|
||||||
<option value="single">single Track layer</option>
|
<option value="single">single Track layer</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<label for="node-input-name"><i class="fa fa-file"></i> Name</label>
|
<label for="node-input-name"><i class="fa fa-file"></i> Name</label>
|
||||||
<input type="text" id="node-input-name" placeholder="name">
|
<input type="text" id="node-input-name" placeholder="name">
|
||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script type="text/html" data-help-name="worldmap-tracks">
|
<script type="text/html" data-help-name="worldmap-tracks">
|
||||||
<p>Creates tracks lines based on a specified number of previous locations.</p>
|
<p>Creates tracks lines based on a specified number of previous locations.</p>
|
||||||
<p>The number of tracked points can be set per marker by specifying <code>msg.payload.trackpoints</code> as part of the update for a marker.</p>
|
<p>The number of tracked points can be set per marker by specifying <code>msg.payload.trackpoints</code> as part of the update for a marker.</p>
|
||||||
<p>You can also specify the msg.payload.color, weight, opacity and dashArray properties for the track if required.</p>
|
<p>You can also specify the msg.payload.color, weight, opacity and dashArray properties for the track if required.</p>
|
||||||
<p>Holds all the points in memory, so if you have a lot of points held for a
|
<p>Holds all the points in memory, so if you have a lot of points held for a
|
||||||
large depth then memory usage may become excessive.</p>
|
large depth then memory usage may become excessive.</p>
|
||||||
<p>To delete a track send a msg.payload containing both the name of the object and
|
<p>To delete a track send a msg.payload containing both the name of the object and
|
||||||
set deleted to true - for example <code>msg.payload = { "name":"Fred", "deleted":true }</code>.</p>
|
set deleted to true - for example <code>msg.payload = { "name":"Fred", "deleted":true }</code>.</p>
|
||||||
<p>This will also delete the point. If you just want to clear the track set the msg.payload to the
|
<p>This will also delete the point. If you just want to clear the track set the msg.payload to the
|
||||||
name+"_", for example <code>msg.payload = { "name":"Fred_", "deleted":true }</code></p>
|
name+"_", for example <code>msg.payload = { "name":"Fred_", "deleted":true }</code></p>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
RED.nodes.registerType('worldmap-tracks',{
|
||||||
|
category: 'location',
|
||||||
|
color:"darksalmon",
|
||||||
|
defaults: {
|
||||||
|
name: {value:""},
|
||||||
|
depth: {value:20},
|
||||||
|
layer: {value:"combined"}
|
||||||
|
},
|
||||||
|
inputs:1,
|
||||||
|
outputs:1,
|
||||||
|
icon: "white-globe.png",
|
||||||
|
paletteLabel: "tracks",
|
||||||
|
label: function() {
|
||||||
|
return this.name||"tracks";
|
||||||
|
},
|
||||||
|
labelStyle: function() {
|
||||||
|
return this.name?"node_label_italic":"";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
|
||||||
|
<script type="text/html" data-template-name="worldmap-hull">
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-prop"><i class="fa fa-ellipsis-h"></i> Property</label>
|
||||||
|
<input type="text" id="node-input-prop" placeholder="payload property to group">
|
||||||
|
</div>
|
||||||
|
<div class="form-row">
|
||||||
|
<label for="node-input-name"><i class="fa fa-file"></i> Name</label>
|
||||||
|
<input type="text" id="node-input-name" placeholder="name">
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/html" data-help-name="worldmap-hull">
|
||||||
|
<p>Creates a convex-hull polygon around a set of supplied points.</p>
|
||||||
|
<p>Points can be made part of the same shape if they have the same <code>msg.payload.layer</code>
|
||||||
|
by default - this can be configured to another msg.payload property if desired.</p>
|
||||||
|
<p><b>Note</b>: the outgoing msg only contains the polygon to draw, so this node should be wired
|
||||||
|
in parallel to other paths going to the worldmap node.</p>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script type="text/javascript">
|
||||||
|
RED.nodes.registerType('worldmap-hull',{
|
||||||
|
category: 'location',
|
||||||
|
color:"darksalmon",
|
||||||
|
defaults: {
|
||||||
|
name: {value:""},
|
||||||
|
prop: {value:"layer",required:true}
|
||||||
|
},
|
||||||
|
inputs:1,
|
||||||
|
outputs:1,
|
||||||
|
icon: "white-globe.png",
|
||||||
|
paletteLabel: "convex-hull",
|
||||||
|
label: function() {
|
||||||
|
return this.name||"convex-hull";
|
||||||
|
},
|
||||||
|
labelStyle: function() {
|
||||||
|
return this.name?"node_label_italic":"";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
<script type="text/javascript">
|
|
||||||
RED.nodes.registerType('worldmap-tracks',{
|
|
||||||
category: 'location',
|
|
||||||
color:"darksalmon",
|
|
||||||
defaults: {
|
|
||||||
name: {value:""},
|
|
||||||
depth: {value:20},
|
|
||||||
layer: {value:"combined"}
|
|
||||||
},
|
|
||||||
inputs:1,
|
|
||||||
outputs:1,
|
|
||||||
icon: "white-globe.png",
|
|
||||||
paletteLabel: "tracks",
|
|
||||||
label: function() {
|
|
||||||
return this.name||"tracks";
|
|
||||||
},
|
|
||||||
labelStyle: function() {
|
|
||||||
return this.name?"node_label_italic":"";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
88
worldmap.js
88
worldmap.js
@ -366,6 +366,94 @@ module.exports = function(RED) {
|
|||||||
}
|
}
|
||||||
RED.nodes.registerType("worldmap-tracks",WorldMapTracks);
|
RED.nodes.registerType("worldmap-tracks",WorldMapTracks);
|
||||||
|
|
||||||
|
|
||||||
|
var WorldMapHull = function(n) {
|
||||||
|
RED.nodes.createNode(this,n);
|
||||||
|
this.prop = n.prop || "layer";
|
||||||
|
var node = this;
|
||||||
|
node.hulls = {};
|
||||||
|
|
||||||
|
var convexHull = function(points) {
|
||||||
|
var arr = [];
|
||||||
|
for (const val of Object.values(points)) {
|
||||||
|
arr.push(val);
|
||||||
|
}
|
||||||
|
|
||||||
|
arr.sort(function (a, b) {
|
||||||
|
return a.lat != b.lat ? a.lat - b.lat : a.lon - b.lon;
|
||||||
|
});
|
||||||
|
|
||||||
|
var n = arr.length;
|
||||||
|
var hull = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < 2 * n; i++) {
|
||||||
|
var j = i < n ? i : 2 * n - 1 - i;
|
||||||
|
while (hull.length >= 2 && removeMiddle(hull[hull.length - 2], hull[hull.length - 1], arr[j]))
|
||||||
|
hull.pop();
|
||||||
|
hull.push(arr[j]);
|
||||||
|
}
|
||||||
|
|
||||||
|
hull.pop();
|
||||||
|
return hull;
|
||||||
|
}
|
||||||
|
|
||||||
|
var removeMiddle = function(a, b, c) {
|
||||||
|
var cross = (a.lat- b.lat) * (c.lon - b.lon) - (a.lon - b.lon) * (c.lat- b.lat);
|
||||||
|
var dot = (a.lat- b.lat) * (c.lat- b.lat) + (a.lon - b.lon) * (c.lon - b.lon);
|
||||||
|
return cross < 0 || cross == 0 && dot <= 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var doHull = function(msg) {
|
||||||
|
if (msg.hasOwnProperty("payload") && msg.payload.hasOwnProperty("name")) {
|
||||||
|
var newmsg = RED.util.cloneMessage(msg);
|
||||||
|
newmsg.payload = {};
|
||||||
|
newmsg.payload[node.prop] = msg.payload[node.prop] || "unknown";
|
||||||
|
if (msg.payload.deleted === true) {
|
||||||
|
if (node.hulls.hasOwnProperty(newmsg.payload[node.prop])) {
|
||||||
|
delete node.hulls[newmsg.payload[node.prop]][msg.payload.name];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (!msg.payload.hasOwnProperty("lat") || !msg.payload.hasOwnProperty("lon")) { return; }
|
||||||
|
if (!node.hulls.hasOwnProperty(newmsg.payload[node.prop])) {
|
||||||
|
node.hulls[newmsg.payload[node.prop]] = {};
|
||||||
|
}
|
||||||
|
node.hulls[newmsg.payload[node.prop]][msg.payload.name] = {lat:msg.payload.lat,lon:msg.payload.lon};
|
||||||
|
}
|
||||||
|
var convexHullPoints = convexHull(node.hulls[newmsg.payload[node.prop]]);
|
||||||
|
var leafletHull = convexHullPoints.map(function (element) {return ([element.lat,element.lon])})
|
||||||
|
|
||||||
|
if (leafletHull.length > 1) {
|
||||||
|
// if (leafletHull.length === 2) { newmsg.payload.line = leafletHull; }
|
||||||
|
// else {
|
||||||
|
newmsg.payload.area = leafletHull;
|
||||||
|
// }
|
||||||
|
newmsg.payload.name = newmsg.payload[node.prop];
|
||||||
|
newmsg.payload.clickable = true;
|
||||||
|
node.send(newmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
node.on("input", function(m) {
|
||||||
|
if (Array.isArray(m.payload)) {
|
||||||
|
m.payload.forEach(function (pay) {
|
||||||
|
var n = RED.util.cloneMessage(m)
|
||||||
|
n.payload = pay;
|
||||||
|
doHull(n);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
doHull(m);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
node.on("close", function() {
|
||||||
|
node.hulls = {};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
RED.nodes.registerType("worldmap-hull",WorldMapHull);
|
||||||
|
|
||||||
RED.httpNode.get("/.ui-worldmap", function(req, res) {
|
RED.httpNode.get("/.ui-worldmap", function(req, res) {
|
||||||
res.send(ui ? "true": "false");
|
res.send(ui ? "true": "false");
|
||||||
});
|
});
|
||||||
|
@ -1399,6 +1399,7 @@ function setMarker(data) {
|
|||||||
var dir = parseFloat(data.hdg || data.bearing || "0");
|
var dir = parseFloat(data.hdg || data.bearing || "0");
|
||||||
marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag, rotationAngle:dir, rotationOrigin:"center"});
|
marker = L.marker(ll, {title:data.name, icon:myMarker, draggable:drag, rotationAngle:dir, rotationOrigin:"center"});
|
||||||
labelOffset = [sz/2-4,-4];
|
labelOffset = [sz/2-4,-4];
|
||||||
|
delete data.iconSize;
|
||||||
}
|
}
|
||||||
else if (data.icon.substr(0,3) === "fa-") {
|
else if (data.icon.substr(0,3) === "fa-") {
|
||||||
var col = data.iconColor || "#910000";
|
var col = data.iconColor || "#910000";
|
||||||
|
Loading…
Reference in New Issue
Block a user