Switch ruler so it's independent of Draw

This commit is contained in:
Dave Conway-Jones 2019-05-06 18:19:26 +01:00
parent db13a2c10b
commit a92c0ced5b
No known key found for this signature in database
GPG Key ID: 9E7F9C73F5168CD4
9 changed files with 246 additions and 132 deletions

View File

@ -1,5 +1,6 @@
### Change Log for Node-RED Worldmap
- v2.0.7-beta - Switch Ruler control to be independent of Draw library.
- v2.0.6-beta - Re-enable editing of draw layer, add rectangles to lines and areas. Make individual objects editable.
- v2.0.5-beta - Fix clustering on zoom (update old library)
- v2.0.4-beta - Add helicopter icon. Correct Leaflet.Coordinates file name. Fix right contextmenu.

View File

@ -9,6 +9,7 @@ map web page for plotting "things" on.
### Updates
- v2.0.7-beta - Switch Ruler control to be independent of Draw library.
- v2.0.6-beta - Re-enable editing of draw layer, add rectangles to lines and areas. Make individual objects editable.
- v2.0.5-beta - Fix clustering on zoom (update old library)
- v2.0.4-beta - Add helicopter icon. Correct Leaflet.Coordinates file name. Fix right contextmenu.

View File

@ -1,6 +1,6 @@
{
"name": "node-red-contrib-web-worldmap",
"version": "2.0.6-beta",
"version": "2.0.7-beta",
"description": "A Node-RED node to provide a web page of a world map for plotting things on.",
"dependencies": {
"cgi": "0.3.1",

View File

@ -30,7 +30,7 @@
<link rel="stylesheet" type="text/css" href="leaflet/MarkerCluster.css">
<link rel="stylesheet" type="text/css" href="leaflet/MarkerCluster.Default.css">
<link rel="stylesheet" type="text/css" href="leaflet/leaflet.draw.css">
<link rel="stylesheet" type="text/css" href="leaflet/leaflet.measurecontrol.css">
<link rel="stylesheet" type="text/css" href="leaflet/leaflet-ruler.css">
<link rel="stylesheet" type="text/css" href="leaflet/easy-button.css">
<link rel="stylesheet" type="text/css" href="leaflet/leaflet.fullscreen.css">
<link rel="stylesheet" type="text/css" href="leaflet/Leaflet.Dialog.css">
@ -47,7 +47,7 @@
<script src="leaflet/leaflet.active-layers.min.js"></script>
<script src="leaflet/leaflet.select-layers.min.js"></script>
<script src="leaflet/leaflet.draw.js"></script>
<script src="leaflet/leaflet.measurecontrol.js"></script>
<script src="leaflet/leaflet-ruler.js"></script>
<script src="leaflet/easy-button.js"></script>
<script src="leaflet/Leaflet.fullscreen.min.js"></script>
<script src="leaflet/Leaflet.Dialog.js"></script>
@ -241,7 +241,11 @@ if (showUserMenu) {
// Create the Initial Map object.
map = new L.map('map').setView(startpos, startzoom);
// Create some buttons
var menuButton = L.easyButton({states:[{icon:'fa-bars fa-lg', onClick:function() { toggleMenu(); }, title:'Toggle menu'}], position:"topright"});
var fullscreenButton = L.control.fullscreen();
var rulerButton = L.control.ruler({position:"topleft"});
//var colorPickButton = L.easyButton({states:[{icon:'fa-tint fa-lg', onClick:function() { console.log("PICK"); }, title:'Pick Colour'}]});
var redButton = L.easyButton('fa-square wm-red', function(btn) { console.log("RED",btn); })
var blueButton = L.easyButton('fa-square wm-blue', function(btn) { console.log("BLUE",btn); })
@ -254,41 +258,24 @@ var colorControl = L.easyBar([redButton,blueButton,greenButton,yellowButton,blac
if (window.self !== window.top) {
console.log("IN an iframe");
inIframe = true;
(document.getElementById("topbar").style.display="none");
(document.getElementById("map").style.top="0px");
(document.getElementById("results").style.right="50px");
(document.getElementById("results").style.top="10px");
(document.getElementById("results").style.zIndex="1");
(document.getElementById("results").style.height="31px");
(document.getElementById("results").style.paddingTop="6px");
(document.getElementById("bars").style.display="none");
(document.getElementById("menu").style.right="8px");
(document.getElementById("menu").style.borderRadius="6px");
if (showUserMenu) { menuButton.addTo(map); }
document.getElementById("topbar").style.display="none";
document.getElementById("map").style.top="0px";
document.getElementById("results").style.right="50px";
document.getElementById("results").style.top="10px";
document.getElementById("results").style.zIndex="1";
document.getElementById("results").style.height="31px";
document.getElementById("results").style.paddingTop="6px";
document.getElementById("bars").style.display="none";
document.getElementById("menu").style.right="8px";
document.getElementById("menu").style.borderRadius="6px";
}
else {
console.log("NOT in an iframe");
if (!showUserMenu) {
document.getElementById("bars").style.display="none";
}
}
if (!showUserMenu) { document.getElementById("bars").style.display="none"; }
// Add graticule
var showGrid = false;
var Lgrid = L.latlngGraticule({
font: "Verdana",
fontColor: "#666",
zoomInterval: [
{start:1, end:2, interval:40},
{start:3, end:3, interval:20},
{start:4, end:4, interval:10},
{start:5, end:7, interval:5},
{start:8, end:20, interval:1}
]
});
if (!inIframe) {
// Add the fullscreen button
L.control.fullscreen().addTo(map);
fullscreenButton.addTo(map);
// Add the locate my position button
L.easyButton( 'fa-crosshairs fa-lg', function() {
@ -312,7 +299,7 @@ if (!inIframe) {
map.on('locationerror', onLocationError);
// Add the measure/ruler button
L.Control.measureControl().addTo(map);
rulerButton.addTo(map);
// Create the clear heatmap button
var clrHeat = L.easyButton( '<b>Reset Heatmap</b>', function() {
@ -320,8 +307,20 @@ if (!inIframe) {
heat.setLatLngs([]);
}, "Clears the current heatmap", "bottomright");
}
else { if (showUserMenu) { menuButton.addTo(map); } }
// Add graticule
var showGrid = false;
var Lgrid = L.latlngGraticule({
font: "Verdana",
fontColor: "#666",
zoomInterval: [
{start:1, end:2, interval:40},
{start:3, end:3, interval:20},
{start:4, end:4, interval:10},
{start:5, end:7, interval:5},
{start:8, end:20, interval:1}
]
});
// Handle the dialog for help box
var dialog = document.querySelector('dialog');

View File

@ -1,93 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="13"
height="13"
id="svg2"
version="1.1"
inkscape:version="0.48.3.1 r9886"
sodipodi:docname="draw-measure.svg"
inkscape:export-filename="/tmp/draw-measure.png"
inkscape:export-xdpi="90"
inkscape:export-ydpi="90">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="43.663174"
inkscape:cx="3.4182411"
inkscape:cy="6.8035094"
inkscape:document-units="px"
inkscape:current-layer="g3789"
showgrid="false"
inkscape:window-width="1680"
inkscape:window-height="1026"
inkscape:window-x="0"
inkscape:window-y="24"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-1039.3622)">
<g
id="g3789">
<g
id="g3796"
transform="matrix(0.97836577,-0.20688263,0.20688263,0.97836577,-216.07864,24.814971)">
<rect
style="fill:#ffffff;fill-opacity:0;stroke:#000000;stroke-width:1;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:0.78431373;stroke-dasharray:none;stroke-dashoffset:0"
id="rect2985"
width="10"
height="4"
x="1.5993246"
y="1043.0298" />
<path
style="fill:none;stroke:#000000;stroke-width:0.99999976px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.78431373"
d="m 3.5993245,1044.0298 0,0.5"
id="path3755"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.99999976px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.78431373"
d="m 5.5993244,1044.0298 0,0.5"
id="path3755-7"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.99999976px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.78431373"
d="m 7.5993244,1044.0298 0,0.5"
id="path3755-5"
inkscape:connector-curvature="0" />
<path
style="fill:none;stroke:#000000;stroke-width:0.99999976px;stroke-linecap:square;stroke-linejoin:miter;stroke-opacity:0.78431373"
d="m 9.5993244,1044.0298 0,0.5"
id="path3755-3"
inkscape:connector-curvature="0" />
</g>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 3.2 KiB

View File

@ -0,0 +1,38 @@
.leaflet-ruler {
height: 30px;
width: 30px;
background-image: url("images/measure-control.png");
background-repeat: no-repeat;
background-position: center;
}
.leaflet-ruler-clicked {
height: 30px;
width: 30px;
background-repeat: no-repeat;
background-position: center;
background-image: url("images/measure-control.png");
border-color: #009100 !important;
}
.leaflet-bar {
background-color: #ffffff;
}
.leaflet-control {
cursor: pointer;
}
.result-tooltip {
background-color: white;
border-width: 2px;
border-color: #e14040;
font-size: smaller;
}
.moving-tooltip {
background-color: rgba(255, 255, 255, .7);
background-clip: padding-box;
opacity: 0.5;
border: dotted;
border-color: #e14040;
font-size: smaller;
}
.plus-length {
padding-left: 45px;
}

View File

@ -0,0 +1,172 @@
(function(factory, window){
"use strict";
if (typeof define === 'function' && define.amd) {
define(['leaflet'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(require('leaflet'));
}
if (typeof window !== 'undefined' && window.L) {
window.L.Ruler = factory(L);
}
}(function (L) {
"use strict";
L.Control.Ruler = L.Control.extend({
options: {
position: 'topright',
circleMarker: {
color: 'red',
radius: 2
},
lineStyle: {
color: 'red',
dashArray: '1,6'
},
lengthUnit: {
display: 'km',
decimal: 2,
factor: null,
label: 'Distance:'
},
angleUnit: {
display: '&deg;',
decimal: 2,
factor: null,
label: 'Bearing:'
}
},
onAdd: function(map) {
this._map = map;
this._container = L.DomUtil.create('div', 'leaflet-bar');
this._container.classList.add('leaflet-ruler');
L.DomEvent.disableClickPropagation(this._container);
L.DomEvent.on(this._container, 'click', this._toggleMeasure, this);
this._choice = false;
this._defaultCursor = this._map._container.style.cursor;
this._allLayers = L.layerGroup();
return this._container;
},
onRemove: function() {
L.DomEvent.off(this._container, 'click', this._toggleMeasure, this);
},
_toggleMeasure: function() {
this._choice = !this._choice;
this._clickedLatLong = null;
this._clickedPoints = [];
this._totalLength = 0;
if (this._choice){
this._map.doubleClickZoom.disable();
L.DomEvent.on(this._map._container, 'keydown', this._escape, this);
L.DomEvent.on(this._map._container, 'dblclick', this._closePath, this);
this._container.classList.add("leaflet-ruler-clicked");
this._clickCount = 0;
this._tempLine = L.featureGroup().addTo(this._allLayers);
this._tempPoint = L.featureGroup().addTo(this._allLayers);
this._pointLayer = L.featureGroup().addTo(this._allLayers);
this._polylineLayer = L.featureGroup().addTo(this._allLayers);
this._allLayers.addTo(this._map);
this._map._container.style.cursor = 'crosshair';
this._map.on('click', this._clicked, this);
this._map.on('mousemove', this._moving, this);
}
else {
this._map.doubleClickZoom.enable();
L.DomEvent.off(this._map._container, 'keydown', this._escape, this);
L.DomEvent.off(this._map._container, 'dblclick', this._closePath, this);
this._container.classList.remove("leaflet-ruler-clicked");
this._map.removeLayer(this._allLayers);
this._allLayers = L.layerGroup();
this._map._container.style.cursor = this._defaultCursor;
this._map.off('click', this._clicked, this);
this._map.off('mousemove', this._moving, this);
}
},
_clicked: function(e) {
this._clickedLatLong = e.latlng;
this._clickedPoints.push(this._clickedLatLong);
L.circleMarker(this._clickedLatLong, this.options.circleMarker).addTo(this._pointLayer);
if(this._clickCount > 0 && !e.latlng.equals(this._clickedPoints[this._clickedPoints.length - 2])){
if (this._movingLatLong){
L.polyline([this._clickedPoints[this._clickCount-1], this._movingLatLong], this.options.lineStyle).addTo(this._polylineLayer);
}
var text;
this._totalLength += this._result.Distance;
if (this._clickCount > 1){
text = '<b>' + this.options.angleUnit.label + '</b>&nbsp;' + this._result.Bearing.toFixed(this.options.angleUnit.decimal) + '&nbsp;' + this.options.angleUnit.display + '<br><b>' + this.options.lengthUnit.label + '</b>&nbsp;' + this._totalLength.toFixed(this.options.lengthUnit.decimal) + '&nbsp;' + this.options.lengthUnit.display;
}
else {
text = '<b>' + this.options.angleUnit.label + '</b>&nbsp;' + this._result.Bearing.toFixed(this.options.angleUnit.decimal) + '&nbsp;' + this.options.angleUnit.display + '<br><b>' + this.options.lengthUnit.label + '</b>&nbsp;' + this._result.Distance.toFixed(this.options.lengthUnit.decimal) + '&nbsp;' + this.options.lengthUnit.display;
}
L.circleMarker(this._clickedLatLong, this.options.circleMarker).bindTooltip(text, {permanent: true, className: 'result-tooltip'}).addTo(this._pointLayer).openTooltip();
}
this._clickCount++;
},
_moving: function(e) {
if (this._clickedLatLong){
L.DomEvent.off(this._container, 'click', this._toggleMeasure, this);
this._movingLatLong = e.latlng;
if (this._tempLine){
this._map.removeLayer(this._tempLine);
this._map.removeLayer(this._tempPoint);
}
var text;
this._addedLength = 0;
this._tempLine = L.featureGroup();
this._tempPoint = L.featureGroup();
this._tempLine.addTo(this._map);
this._tempPoint.addTo(this._map);
this._calculateBearingAndDistance();
this._addedLength = this._result.Distance + this._totalLength;
L.polyline([this._clickedLatLong, this._movingLatLong], this.options.lineStyle).addTo(this._tempLine);
if (this._clickCount > 1){
text = '<b>' + this.options.angleUnit.label + '</b>&nbsp;' + this._result.Bearing.toFixed(this.options.angleUnit.decimal) + '&nbsp;' + this.options.angleUnit.display + '<br><b>' + this.options.lengthUnit.label + '</b>&nbsp;' + this._addedLength.toFixed(this.options.lengthUnit.decimal) + '&nbsp;' + this.options.lengthUnit.display + '<br><div class="plus-length">(+' + this._result.Distance.toFixed(this.options.lengthUnit.decimal) + ')</div>';
}
else {
text = '<b>' + this.options.angleUnit.label + '</b>&nbsp;' + this._result.Bearing.toFixed(this.options.angleUnit.decimal) + '&nbsp;' + this.options.angleUnit.display + '<br><b>' + this.options.lengthUnit.label + '</b>&nbsp;' + this._result.Distance.toFixed(this.options.lengthUnit.decimal) + '&nbsp;' + this.options.lengthUnit.display;
}
L.circleMarker(this._movingLatLong, this.options.circleMarker).bindTooltip(text, {sticky: true, offset: L.point(0, -40) ,className: 'moving-tooltip'}).addTo(this._tempPoint).openTooltip();
}
},
_escape: function(e) {
if (e.keyCode === 27){
if (this._clickCount > 0){
this._closePath();
}
else {
this._choice = true;
this._toggleMeasure();
}
}
},
_calculateBearingAndDistance: function() {
var f1 = this._clickedLatLong.lat, l1 = this._clickedLatLong.lng, f2 = this._movingLatLong.lat, l2 = this._movingLatLong.lng;
var toRadian = Math.PI / 180;
// haversine formula
// bearing
var y = Math.sin((l2-l1)*toRadian) * Math.cos(f2*toRadian);
var x = Math.cos(f1*toRadian)*Math.sin(f2*toRadian) - Math.sin(f1*toRadian)*Math.cos(f2*toRadian)*Math.cos((l2-l1)*toRadian);
var brng = Math.atan2(y, x)*((this.options.angleUnit.factor ? this.options.angleUnit.factor/2 : 180)/Math.PI);
brng += brng < 0 ? (this.options.angleUnit.factor ? this.options.angleUnit.factor : 360) : 0;
// distance
var R = this.options.lengthUnit.factor ? 6371 * this.options.lengthUnit.factor : 6371; // kilometres
var deltaF = (f2 - f1)*toRadian;
var deltaL = (l2 - l1)*toRadian;
var a = Math.sin(deltaF/2) * Math.sin(deltaF/2) + Math.cos(f1*toRadian) * Math.cos(f2*toRadian) * Math.sin(deltaL/2) * Math.sin(deltaL/2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var distance = R * c;
this._result = {
Bearing: brng,
Distance: distance
};
},
_closePath: function() {
this._map.removeLayer(this._tempLine);
this._map.removeLayer(this._tempPoint);
this._choice = false;
L.DomEvent.on(this._container, 'click', this._toggleMeasure, this);
this._toggleMeasure();
}
});
L.control.ruler = function(options) {
return new L.Control.Ruler(options);
};
}, window));

View File

@ -1,3 +0,0 @@
.leaflet-control-draw-measure {
background-image: url(images/measure-control.png);
}

View File

@ -1 +0,0 @@
(function(factory,window){if(typeof define==="function"&&define.amd){define(["leaflet"],function(L){factory(L,window.toGeoJSON)})}else if(typeof exports==="object"){module.exports=function(L){if(L===undefined){if(typeof window!=="undefined"){L=require("leaflet")}}factory(L);return L}}else if(typeof window!=="undefined"&&window.L){factory(window.L)}})(function(L){L.Polyline.Measure=L.Draw.Polyline.extend({addHooks:function(){L.Draw.Polyline.prototype.addHooks.call(this);if(this._map){this._markerGroup=new L.LayerGroup;this._map.addLayer(this._markerGroup);this._markers=[];this._map.on("click",this._onClick,this);this._startShape()}},removeHooks:function(){L.Draw.Polyline.prototype.removeHooks.call(this);this._clearHideErrorTimeout();this._map.off("pointermove",this._onMouseMove,this).off("mousemove",this._onMouseMove,this).off("click",this._onClick,this);this._clearGuides();this._container.style.cursor="";this._removeShape()},_startShape:function(){this._drawing=true;this._poly=new L.Polyline([],this.options.shapeOptions);this._poly._onClick=function(){};this._container.style.cursor="crosshair";this._updateTooltip();this._map.on("pointermove",this._onMouseMove,this).on("mousemove",this._onMouseMove,this)},_finishShape:function(){this._drawing=false;this._cleanUpShape();this._clearGuides();this._updateTooltip();this._map.off("pointermove",this._onMouseMove,this).off("mousemove",this._onMouseMove,this);this._container.style.cursor=""},_removeShape:function(){if(!this._poly)return;this._map.removeLayer(this._poly);delete this._poly;this._markers.splice(0);this._markerGroup.clearLayers()},_onClick:function(){if(!this._drawing){this._removeShape();this._startShape();return}},_getTooltipText:function(){var labelText=L.Draw.Polyline.prototype._getTooltipText.call(this);if(!this._drawing){labelText.text=""}return labelText}});L.Control.MeasureControl=L.Control.extend({statics:{TITLE:"Measure distances"},options:{position:"topleft",handler:{}},toggle:function(){if(this.handler.enabled()){this.handler.disable.call(this.handler)}else{this.handler.enable.call(this.handler)}},onAdd:function(map){var link=null;var className="leaflet-control-draw";this._container=L.DomUtil.create("div","leaflet-bar");this.handler=new L.Polyline.Measure(map,this.options.handler);this.handler.on("enabled",function(){this.enabled=true;L.DomUtil.addClass(this._container,"enabled")},this);this.handler.on("disabled",function(){delete this.enabled;L.DomUtil.removeClass(this._container,"enabled")},this);link=L.DomUtil.create("a",className+"-measure",this._container);link.href="#";link.title=L.Control.MeasureControl.TITLE;L.DomEvent.addListener(link,"click",L.DomEvent.stopPropagation).addListener(link,"click",L.DomEvent.preventDefault).addListener(link,"click",this.toggle,this);return this._container}});L.Map.mergeOptions({measureControl:false});L.Map.addInitHook(function(){if(this.options.measureControl){this.measureControl=L.Control.measureControl().addTo(this)}});L.Control.measureControl=function(options){return new L.Control.MeasureControl(options)}},window);