commit cf57a9f441beef9099fb510cc9acf2c85d8d8d6f
parent 96b46a510f59778e58edc0872096ec72c5e36519
Author: Sergej Orlov <wladimirych@gmail.com>
Date: Fri, 24 Mar 2017 10:31:54 +0300
new control: measure azimuth, distance
Diffstat:
11 files changed, 355 insertions(+), 1 deletion(-)
diff --git a/package.json b/package.json
@@ -59,6 +59,7 @@
"image-promise": "^4.0.1",
"knockout": "^3.4.0",
"leaflet": "1.0.3",
+ "leaflet-rotatedmarker": "^0.1.2",
"load-script": "^1.0.0",
"raven-js": "^3.12.0",
"rbush": "^2.0.1",
diff --git a/src/App.js b/src/App.js
@@ -25,7 +25,7 @@ import getLayers from 'layers';
import 'lib/leaflet.control.layers.events';
import 'lib/leaflet.control.jnx';
import 'lib/leaflet.control.jnx/hash-state';
-
+import 'lib/leaflet.control.azimuth';
function setUp() {
fixAll();
@@ -62,6 +62,8 @@ function setUp() {
new L.Control.Coordinates({position: 'topleft'}).addTo(map);
+ new L.Control.Azimuth({position: 'topleft'}).addTo(map);
+
/////////// controls top-right corner
const layersControl = L.control.layers(null, null, {collapsed: false})
diff --git a/src/lib/leaflet.control.azimuth/1490240499_Arrow_Back.svg b/src/lib/leaflet.control.azimuth/1490240499_Arrow_Back.svg
@@ -0,0 +1 @@
+<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'><svg enable-background="new 0 0 32 32" height="32px" id="Слой_1" version="1.1" viewBox="0 0 32 32" width="32px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path clip-rule="evenodd" d="M31.106,15H3.278l8.325-8.293 c0.391-0.391,0.391-1.024,0-1.414c-0.391-0.391-1.024-0.391-1.414,0l-9.9,9.899c-0.385,0.385-0.385,1.029,0,1.414l9.9,9.9 c0.391,0.391,1.024,0.391,1.414,0c0.391-0.391,0.391-1.024,0-1.414L3.278,17h27.828c0.552,0,1-0.448,1-1 C32.106,15.448,31.658,15,31.106,15z" fill="#121313" fill-rule="evenodd" id="Arrow_Back"/><g/><g/><g/><g/><g/><g/></svg>
+\ No newline at end of file
diff --git a/src/lib/leaflet.control.azimuth/compass-pointing-north-east.svg b/src/lib/leaflet.control.azimuth/compass-pointing-north-east.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!-- Generator: Adobe Illustrator 18.1.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 28 28" style="enable-background:new 0 0 28 28;" xml:space="preserve">
+<g>
+ <path style="fill:#555;" d="M14,0C6.268,0,0,6.268,0,14s6.268,14,14,14s14-6.268,14-14S21.732,0,14,0z M14,26
+ C7.373,26,2,20.627,2,14S7.373,2,14,2s12,5.373,12,12S20.627,26,14,26z"/>
+ <path style="fill:#555;" d="M6.222,21.777c0,0,8.838-3.183,10.606-4.949c1.768-1.768,4.949-10.606,4.949-10.606
+ s-8.838,3.183-10.605,4.95S6.222,21.777,6.222,21.777z M12.586,12.586l2.828,2.828c0.707,0.707-6.363,3.535-6.363,3.535
+ S11.879,11.879,12.586,12.586z"/>
+</g>
+</svg>
diff --git a/src/lib/leaflet.control.azimuth/control.html b/src/lib/leaflet.control.azimuth/control.html
@@ -0,0 +1,21 @@
+<div class="contents">
+ <table>
+ <tr>
+ <td>True azimuth</td>
+ <td data-bind="text: trueAzimuth() !== null ? Math.round(trueAzimuth()) + '°' : '--'"></td>
+ </tr>
+ <tr>
+ <td>Magnetic azimuth</td>
+ <td data-bind="text: magneticAzimuth() !== null ? magneticAzimuth() + '°' : '--'"></td>
+ </tr>
+ <tr>
+ <td>Distance</td>
+ <td data-bind="text: distance() === null ? '--' :
+ distance() > 2000 ? (distance() / 1000).toFixed(2) + ' km' : Math.round(distance()) + ' m'"></td>
+ </tr>
+ </table>
+ <a class="image-button icon-elevation" title="Display elevation porofile"
+ data-bind="click: onProfileButtonClick"></a>
+ <a class="image-button icon-arrow-left" title="Hide"
+ data-bind="click: onMinimizeButonClick"></a>
+</div>
+\ No newline at end of file
diff --git a/src/lib/leaflet.control.azimuth/elevation.png b/src/lib/leaflet.control.azimuth/elevation.png
Binary files differ.
diff --git a/src/lib/leaflet.control.azimuth/index.js b/src/lib/leaflet.control.azimuth/index.js
@@ -0,0 +1,198 @@
+import L from 'leaflet';
+import ko from 'knockout';
+import 'lib/leaflet.control.commons';
+import layout from './control.html';
+import 'lib/controls-styles/controls-styles.css';
+import './style.css';
+import {getDeclination} from 'lib/magnetic-declination';
+//FIXME: replace with vendored version
+import 'leaflet-rotatedmarker';
+import iconPointerStart from './pointer.svg';
+import iconPointerEnd from './pointer-end.svg';
+import 'lib/leaflet.control.elevation-profile';
+
+function radians(x) {
+ return x / 180 * Math.PI;
+}
+
+function degrees(x) {
+ return x / Math.PI * 180;
+}
+
+function calcAzimuth(latlng1, latlng2) {
+ const lat1 = radians(latlng1.lat);
+ const lat2 = radians(latlng2.lat);
+ const lng1 = radians(latlng1.lng);
+ const lng2 = radians(latlng2.lng);
+
+ const y = Math.sin(lng2 - lng1) * Math.cos(lat2);
+ const x = Math.cos(lat1) * Math.sin(lat2) -
+ Math.sin(lat2) * Math.cos(lat2) * Math.cos(lng2 - lng1);
+ let brng = Math.atan2(y, x);
+ brng = degrees(brng);
+ return brng;
+}
+
+
+function calcAngle(latlng1, latlng2) {
+ const p1 = L.Projection.SphericalMercator.project(latlng1);
+ const p2 = L.Projection.SphericalMercator.project(latlng2);
+ const delta = p2.subtract(p1);
+ const angle = Math.atan2(delta.x, delta.y);
+ return degrees(angle);
+}
+
+function roundAzimuth(a) {
+ return (Math.round(a) + 360) % 360
+}
+
+L.Control.Azimuth = L.Control.extend({
+ options: {
+ position: 'bottomleft'
+ },
+
+ initialize: function(options) {
+ L.Control.prototype.initialize.call(this, options);
+ this.trueAzimuth = ko.observable(null);
+ this.magneticAzimuth = ko.observable(null);
+ this.distance = ko.observable(null);
+ this.points = {
+ start: null,
+ end: null
+ };
+ const iconStart = L.icon({iconUrl: iconPointerStart, iconSize: [30, 30]});
+ const iconEnd = L.icon({iconUrl: iconPointerEnd, iconSize: [30, 30]});
+ this.markers = {
+ start: L.marker([0, 0], {icon: iconStart, draggable: true, which: 'start', rotationOrigin: 'center center'})
+ .on('drag', this.onMarkerDrag, this)
+ .on('click', L.DomEvent.stopPropagation),
+ end: L.marker([0, 0], {icon: iconEnd, draggable: true, which: 'end', rotationOrigin: 'center center'})
+ .on('drag', this.onMarkerDrag, this)
+ .on('click', L.DomEvent.stopPropagation)
+ };
+ this.azimuthLine = L.polyline([], {interactive: false, weight: 1.5});
+ },
+
+ onAdd: function(map) {
+ this._map = map;
+ const container = this._container =
+ L.DomUtil.create('div', 'leaflet-control leaflet-control-button leaflet-control-azimuth');
+ this._stopContainerEvents();
+ container.innerHTML = layout;
+ container.title = "Measure bearing, display line of sight";
+ ko.applyBindings(this, container);
+ L.DomEvent.on(container, 'click', this.onClick, this);
+ return container;
+ },
+
+ onClick: function() {
+ this.setEnabled(true);
+ },
+
+ onMinimizeButonClick: function(e) {
+ setTimeout(() => this.setEnabled(false), 0);
+ },
+
+ onMarkerDrag: function(e) {
+ const marker = e.target;
+ this.setPoints({[marker.options.which]: marker.getLatLng()});
+ },
+
+ setEnabled: function(enabled) {
+ if (!!enabled === this.isEnabled()) {
+ return;
+ }
+ if (enabled) {
+ L.DomUtil.addClass(this._container, 'expanded');
+ L.DomUtil.addClass(this._map._container, 'azimuth-control-active');
+ this._map.on('click', this.onMapClick, this);
+ } else {
+ L.DomUtil.removeClass(this._container, 'expanded');
+ L.DomUtil.removeClass(this._map._container, 'azimuth-control-active');
+ this._map.off('click', this.onMapClick, this);
+ this.setPoints({start: null, end: null});
+ }
+
+ },
+
+ isEnabled: function() {
+ return L.DomUtil.hasClass(this._container, 'expanded');
+ },
+
+ setPoints: function(points) {
+ Object.assign(this.points, points);
+ points = this.points;
+ if (points.start) {
+ this.markers.start
+ .setLatLng(points.start)
+ .addTo(this._map);
+ } else {
+ this.markers.start.removeFrom(this._map);
+ }
+ if (points.end) {
+ this.markers.end
+ .setLatLng(points.end)
+ .addTo(this._map);
+ } else {
+ this.markers.end.removeFrom(this._map);
+ }
+ if (points.start && points.end) {
+ const angle = calcAngle(points.start, points.end);
+ this.markers.start
+ .setRotationAngle(angle);
+ this.markers.end
+ .setRotationAngle(angle);
+ this.azimuthLine
+ .setLatLngs([[points.start, points.end]])
+ .addTo(this._map);
+ } else {
+ this.markers.start.setRotationAngle(0);
+ this.azimuthLine.removeFrom(this._map);
+ }
+ this.updateValuesDisplay();
+ },
+
+ updateValuesDisplay: function() {
+ if (this.points.start && this.points.end) {
+ const points = this.points;
+ const azimuth = calcAzimuth(points.start, points.end);
+ this.trueAzimuth(roundAzimuth(azimuth));
+ const declination = getDeclination(points.start.lat, points.start.lng);
+ if (declination !== null) {
+ this.magneticAzimuth(roundAzimuth(azimuth - declination));
+ } else {
+ this.magneticAzimuth(null);
+ }
+ this.distance(points.start.distanceTo(points.end));
+
+ } else {
+ this.distance(null);
+ this.trueAzimuth(null);
+ this.magneticAzimuth(null);
+ }
+ },
+
+ onMapClick: function(e) {
+ if (!this.points.start && !this.points.end) {
+ this.setPoints({start: e.latlng})
+ } else if (this.points.start && !this.points.end) {
+ this.setPoints({end: e.latlng})
+ } else if (this.points.start && this.points.end) {
+ this.setPoints({start: e.latlng, end: null})
+ }
+ },
+
+ onProfileButtonClick: function() {
+ if (this.elevationControl) {
+ this.elevationControl.removeFrom(this._map);
+ }
+
+ this.elevationControl = new L.Control.ElevationProfile(this._map, [this.points.start, this.points.end ], {
+ samplingInterval: 100
+ }
+ );
+ }
+
+ }
+);
+
diff --git a/src/lib/leaflet.control.azimuth/pointer-end.svg b/src/lib/leaflet.control.azimuth/pointer-end.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 30 30" xml:space="preserve">
+<defs>
+ <radialGradient id="grad1">
+ <stop offset="20%" stop-color="#ff0000" stop-opacity="0.6"/>
+ <stop offset="75%" stop-color="#ff0000" stop-opacity="0.3"/>
+ <stop offset="100%" stop-color="#ff0000" stop-opacity="0"/>
+ </radialGradient>
+ </defs>
+
+ <g>
+ <ellipse ry="15" rx="15" id="svg_1" cy="15" cx="15" fill="url(#grad1)"/>
+ <line x1="15" y1="15" x2="22" y2="25" stroke="#3388ff" stroke-width="1.5"/>
+ <line x1="15" y1="15" x2="8" y2="25" stroke="#3388ff" stroke-width="1.5"/>
+ <line x1="15" y1="15" x2="15" y2="30" stroke="#3388ff" stroke-width="1.5"/>
+ </g>
+</svg>
+\ No newline at end of file
diff --git a/src/lib/leaflet.control.azimuth/pointer-start.svg b/src/lib/leaflet.control.azimuth/pointer-start.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 30 30" xml:space="preserve">
+<defs>
+ <radialGradient id="grad1">
+ <stop offset="20%" stop-color="#ff0000" stop-opacity="0.6"/>
+ <stop offset="75%" stop-color="#ff0000" stop-opacity="0.3"/>
+ <stop offset="100%" stop-color="#ff0000" stop-opacity="0"/>
+ </radialGradient>
+ </defs>
+
+ <g>
+ <ellipse ry="15" rx="15" id="svg_1" cy="15" cx="15" fill="url(#grad1)"/>
+
+ <line x1="15" y1="15" x2="22" y2="25" stroke="#3388ff" stroke-width="1.5"/>
+ <line x1="15" y1="15" x2="8" y2="25" stroke="#3388ff" stroke-width="1.5"/>
+ <line x1="15" y1="15" x2="15" y2="0" stroke="#3388ff" stroke-width="1.5"/>
+ </g>
+</svg>
+\ No newline at end of file
diff --git a/src/lib/leaflet.control.azimuth/pointer.svg b/src/lib/leaflet.control.azimuth/pointer.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="iso-8859-1"?>
+<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 30 30" xml:space="preserve">
+<defs>
+ <radialGradient id="grad1">
+ <stop offset="20%" stop-color="#ff0000" stop-opacity="0.6"/>
+ <stop offset="75%" stop-color="#ff0000" stop-opacity="0.3"/>
+ <stop offset="100%" stop-color="#ff0000" stop-opacity="0"/>
+ </radialGradient>
+ </defs>
+
+ <g>
+ <ellipse ry="15" rx="15" id="svg_1" cy="15" cx="15" fill="url(#grad1)"/>
+
+ <line x1="5" y1="15" x2="25" y2="15" stroke="#3388ff" stroke-width="1.5"/>
+ <line x1="15" y1="5" x2="15" y2="25" stroke="#3388ff" stroke-width="1.5"/>
+ </g>
+</svg>
+\ No newline at end of file
diff --git a/src/lib/leaflet.control.azimuth/style.css b/src/lib/leaflet.control.azimuth/style.css
@@ -0,0 +1,59 @@
+.leaflet-control-azimuth {
+ background-image: url('compass-pointing-north-east.svg');
+ background-size: 16px 16px;
+ user-select: none;
+}
+
+.leaflet-control-azimuth.expanded:hover {
+ background-color: white;
+}
+
+.leaflet-control-azimuth .contents {
+ display: none;
+}
+
+.leaflet-control-azimuth.expanded .contents {
+ display: block;
+}
+
+.leaflet-control-azimuth.expanded {
+ background-image: none;
+ width: auto;
+ height: auto;
+ padding: 4px 8px;
+ overflow: hidden;
+}
+
+.leaflet-control-azimuth table{
+ border-collapse: collapse;
+ line-height: 16px;
+ float: left;
+}
+
+.leaflet-control-azimuth td:last-child {
+ padding-left: 4px;
+ text-align: center;
+}
+
+.icon-elevation {
+ background-image: url('elevation.png');
+ background-size: 30px;
+}
+
+.icon-arrow-left {
+ background-image: url('1490240499_Arrow_Back.svg');
+ background-size: 16px;
+}
+
+
+.leaflet-control-azimuth .image-button {
+ float: left;
+ margin-left: 6px;
+ vertical-align: middle;
+ top: 12px;
+ position: relative;
+}
+
+.azimuth-control-active {
+ cursor: crosshair;
+}