nakarte

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

index.js (7447B)


      1 import L from 'leaflet';
      2 import ko from 'knockout';
      3 import './coordinates.css';
      4 import copyToClipboard from '~/lib/clipboardCopy';
      5 import Contextmenu from '~/lib/contextmenu';
      6 import {makeButtonWithBar} from '~/lib/leaflet.control.commons';
      7 import safeLocalStorage from '~/lib/safe-localstorage';
      8 import '~/lib/controls-styles/controls-styles.css';
      9 import {ElevationLayer} from '~/lib/leaflet.layer.elevation-display';
     10 import * as formats from './formats';
     11 
     12 const DEFAULT_FORMAT = formats.DEGREES;
     13 const UNKNOWN_COORDINATES = {
     14     lat: '-------',
     15     lng: '-------'
     16 };
     17 
     18 L.Control.Coordinates = L.Control.extend({
     19         options: {
     20             position: 'bottomleft'
     21         },
     22 
     23         includes: L.Mixin.Events,
     24 
     25         formats: [
     26             formats.SIGNED_DEGREES,
     27             formats.DEGREES,
     28             formats.DEGREES_AND_MINUTES,
     29             formats.DEGREES_AND_MINUTES_AND_SECONDS
     30         ],
     31 
     32         initialize: function(elevationTilesUrl, options) {
     33             L.Control.prototype.initialize.call(this, options);
     34 
     35             this.elevationDisplayLayer = new ElevationLayer(elevationTilesUrl);
     36 
     37             this.latlng = ko.observable();
     38             this.format = ko.observable(DEFAULT_FORMAT);
     39             this.formatCode = ko.pureComputed({
     40                 read: () => this.format().code,
     41                 write: (value) => {
     42                     for (let format of this.formats) {
     43                         if (value === format.code) {
     44                             this.format(format);
     45                             break;
     46                         }
     47                     }
     48                 }
     49             });
     50 
     51             this.formattedCoordinates = ko.pureComputed(() => {
     52                 if (this.latlng()) {
     53                     return formats.formatLatLng(this.latlng().wrap(), this.format());
     54                 }
     55                 return UNKNOWN_COORDINATES;
     56             }, this);
     57 
     58             this.wrapperClass = ko.pureComputed(() => this.format().wrapperClass, this);
     59 
     60             this.formatCode.subscribe(this.saveStateToStorage, this);
     61         },
     62 
     63         onAdd: function(map) {
     64             const {container, link, barContainer} = makeButtonWithBar(
     65                 'leaflet-contol-coordinates',
     66                 'Show coordinates at cursor',
     67                 'icon-coordinates'
     68             );
     69 
     70             this._map = map;
     71             this._container = container;
     72             this._link = link;
     73 
     74             barContainer.innerHTML = `
     75                 <div data-bind="css: wrapperClass">
     76                     <div class="leaflet-coordinates-container" data-bind="with: formattedCoordinates()">
     77                         <span class="leaflet-coordinates-latitude" data-bind="html: lat"></span>
     78                         <span class="leaflet-coordinates-longitude" data-bind="html: lng"></span>
     79                     </div>
     80                     <hr class="leaflet-coordinates-divider" />
     81                     <div data-bind="foreach: formats">
     82                         <div>
     83                             <label title="">
     84                                 <input type="radio" data-bind="checked: $parent.formatCode, value: code" 
     85                                        class="leaflet-coordinates-format-radio"/>
     86                                 <span data-bind="html: label"></span>
     87                             </label>
     88                         </div>
     89                     </div>
     90                 </div>
     91             `;
     92 
     93             barContainer.title = "Right click on map to copy coordinates";
     94 
     95             this.loadStateFromStorage();
     96             ko.applyBindings(this, container);
     97             L.DomEvent.on(link, 'click', this.onClick, this);
     98 
     99             return container;
    100         },
    101 
    102         onRemove: function() {
    103             L.DomEvent.off(this._link, 'click', this.onClick, this);
    104         },
    105 
    106         loadStateFromStorage: function() {
    107             const active = safeLocalStorage.leafletCoordinatesActive === '1';
    108             const code = safeLocalStorage.leafletCoordinatesFmt || DEFAULT_FORMAT.code;
    109 
    110             this.formatCode(code);
    111             this.setEnabled(active);
    112         },
    113 
    114         saveStateToStorage: function() {
    115             safeLocalStorage.leafletCoordinatesActive = this.isEnabled() ? '1' : '0';
    116             safeLocalStorage.leafletCoordinatesFmt = this.formatCode();
    117         },
    118 
    119         onMouseMove: function(e) {
    120             this.latlng(e.latlng);
    121         },
    122 
    123         setEnabled: function(enabled) {
    124             if (Boolean(enabled) === this.isEnabled()) {
    125                 return;
    126             }
    127             const classFunc = enabled ? 'addClass' : 'removeClass';
    128             const eventFunc = enabled ? 'on' : 'off';
    129             L.DomUtil[classFunc](this._container, 'active');
    130             L.DomUtil[classFunc](this._container, 'highlight');
    131             L.DomUtil[classFunc](this._map._container, 'coordinates-control-active');
    132             this._map[eventFunc]('mousemove', this.onMouseMove, this);
    133             this._map[eventFunc]('contextmenu', this.onMapRightClick, this);
    134             this._map[enabled ? 'addLayer' : 'removeLayer'](this.elevationDisplayLayer);
    135             this._isEnabled = Boolean(enabled);
    136             this.latlng(null);
    137         },
    138 
    139         onMapRightClick: function(e) {
    140             L.DomEvent.stop(e);
    141             function createItem(format, elevation, overrides = {}) {
    142                 const {lat, lng} = formats.formatLatLng(e.latlng.wrap(), format);
    143                 let text = `${lat} ${lng}`;
    144                 if (elevation !== null) {
    145                     text += ` H=${elevation} m`;
    146                 }
    147 
    148                 return {text: `${text} <span class="leaflet-coordinates-menu-fmt">${format.label}</span>`,
    149                     callback: () => copyToClipboard(text, e.originalEvent),
    150                     ...overrides};
    151             }
    152 
    153             const items = [
    154                 createItem(
    155                     this.format(),
    156                     null,
    157                     {
    158                         text: '<b>Copy coordinates to clipboard</b>',
    159                         header: true,
    160                     },
    161                 ),
    162                 ...this.formats.map((format) => createItem(format, null)),
    163             ];
    164             const elevationResult = this.elevationDisplayLayer.getElevation(e.latlng);
    165             if (elevationResult.ready && !elevationResult.error && elevationResult.elevation !== null) {
    166                 const elevation = elevationResult.elevation;
    167                 items.push(
    168                     '-',
    169                     {
    170                         text: `Copy elevation to clipboard: ${elevation}`,
    171                         header: true,
    172                         callback: () => copyToClipboard(elevation, e.originalEvent),
    173                     },
    174                     '-',
    175                     createItem(
    176                         this.format(),
    177                         elevation,
    178                         {
    179                             text: '<b>Copy coordinates with elevation to clipboard</b>',
    180                             header: true,
    181                         },
    182                     ),
    183                     ...this.formats.map((format) => createItem(format, elevation)),
    184                 );
    185             }
    186             new Contextmenu(items).show(e);
    187         },
    188 
    189         isEnabled: function() {
    190             return Boolean(this._isEnabled);
    191         },
    192 
    193         onClick: function() {
    194             this.setEnabled(!this.isEnabled());
    195             this.saveStateToStorage();
    196         }
    197     }
    198 );