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
|
||||
|
||||
- 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.11 - Better editing of drawing layer, add OpenTopoMap, and better Esri satellite
|
||||
- v2.3.10 - Improve geojson layer and name handling.
|
||||
|
@ -11,7 +11,8 @@ map web page for plotting "things" on.
|
||||
|
||||
### 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.11 - Better editing of drawing layer, add OpenTopoMap, and better Esri satellite
|
||||
- v2.3.10 - Improve geojson layer and name handling.
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"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.",
|
||||
"dependencies": {
|
||||
"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">
|
||||
<div class="form-row">
|
||||
<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">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-layer"><i class="fa fa-map"></i> Track Layer</label>
|
||||
<select id="node-input-layer">
|
||||
<option value="combined">on marker layer</option>
|
||||
<option value="separate">one per marker layer</option>
|
||||
<option value="single">single Track layer</option>
|
||||
</select>
|
||||
</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>
|
||||
<div class="form-row">
|
||||
<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">
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<label for="node-input-layer"><i class="fa fa-map"></i> Track Layer</label>
|
||||
<select id="node-input-layer">
|
||||
<option value="combined">on marker layer</option>
|
||||
<option value="separate">one per marker layer</option>
|
||||
<option value="single">single Track layer</option>
|
||||
</select>
|
||||
</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-tracks">
|
||||
<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>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
|
||||
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
|
||||
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
|
||||
name+"_", for example <code>msg.payload = { "name":"Fred_", "deleted":true }</code></p>
|
||||
</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-help-name="worldmap-tracks">
|
||||
<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>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
|
||||
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
|
||||
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
|
||||
name+"_", for example <code>msg.payload = { "name":"Fred_", "deleted":true }</code></p>
|
||||
</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>
|
||||
|
88
worldmap.js
88
worldmap.js
@ -366,6 +366,94 @@ module.exports = function(RED) {
|
||||
}
|
||||
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) {
|
||||
res.send(ui ? "true": "false");
|
||||
});
|
||||
|
@ -1399,6 +1399,7 @@ function setMarker(data) {
|
||||
var dir = parseFloat(data.hdg || data.bearing || "0");
|
||||
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-") {
|
||||
var col = data.iconColor || "#910000";
|
||||
|
Loading…
Reference in New Issue
Block a user