add covex-hull node and update info

This commit is contained in:
Dave Conway-Jones 2020-07-09 22:09:57 +01:00
parent fa132af571
commit 444c1f1fcf
No known key found for this signature in database
GPG Key ID: 302A6725C594817F
6 changed files with 185 additions and 52 deletions

View File

@ -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.

View File

@ -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.

View File

@ -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",

View File

@ -504,25 +504,25 @@ 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>
@ -532,9 +532,9 @@ then by default <code>⌘⇧m</code> - <code>ctrl-shift-m</code> will load the m
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"> <script type="text/javascript">
RED.nodes.registerType('worldmap-tracks',{ RED.nodes.registerType('worldmap-tracks',{
category: 'location', category: 'location',
color:"darksalmon", color:"darksalmon",
@ -554,4 +554,46 @@ then by default <code>⌘⇧m</code> - <code>ctrl-shift-m</code> will load the m
return this.name?"node_label_italic":""; return this.name?"node_label_italic":"";
} }
}); });
</script> </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>

View File

@ -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");
}); });

View File

@ -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";