nakarte

Source code of https://map.sikmir.ru (fork)
git clone git://git.sikmir.ru/nakarte
Log | Files | Refs | LICENSE

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:
Mpackage.json | 1+
Msrc/App.js | 4+++-
Asrc/lib/leaflet.control.azimuth/1490240499_Arrow_Back.svg | 2++
Asrc/lib/leaflet.control.azimuth/compass-pointing-north-east.svg | 12++++++++++++
Asrc/lib/leaflet.control.azimuth/control.html | 22++++++++++++++++++++++
Asrc/lib/leaflet.control.azimuth/elevation.png | 0
Asrc/lib/leaflet.control.azimuth/index.js | 198+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/lib/leaflet.control.azimuth/pointer-end.svg | 19+++++++++++++++++++
Asrc/lib/leaflet.control.azimuth/pointer-start.svg | 20++++++++++++++++++++
Asrc/lib/leaflet.control.azimuth/pointer.svg | 19+++++++++++++++++++
Asrc/lib/leaflet.control.azimuth/style.css | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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()) + '&deg;' : '--'"></td> + </tr> + <tr> + <td>Magnetic azimuth</td> + <td data-bind="text: magneticAzimuth() !== null ? magneticAzimuth() + '&deg;' : '--'"></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; +}