nakarte

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

index.js (8544B)


      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             this.displayElevation = ko.observable(true);
     37 
     38             this.latlng = ko.observable();
     39             this.format = ko.observable(DEFAULT_FORMAT);
     40             this.formatCode = ko.pureComputed({
     41                 read: () => this.format().code,
     42                 write: (value) => {
     43                     for (let format of this.formats) {
     44                         if (value === format.code) {
     45                             this.format(format);
     46                             break;
     47                         }
     48                     }
     49                 }
     50             });
     51 
     52             this.formattedCoordinates = ko.pureComputed(() => {
     53                 if (this.latlng()) {
     54                     return formats.formatLatLng(this.latlng().wrap(), this.format());
     55                 }
     56                 return UNKNOWN_COORDINATES;
     57             }, this);
     58 
     59             this.wrapperClass = ko.pureComputed(() => this.format().wrapperClass, this);
     60 
     61             this.formatCode.subscribe(this.saveFormatStateToStorage, this);
     62             this.displayElevation.subscribe(this.onDisplayElevationChange, this);
     63         },
     64 
     65         onAdd: function(map) {
     66             const {container, link, barContainer} = makeButtonWithBar(
     67                 'leaflet-contol-coordinates',
     68                 'Show coordinates at cursor',
     69                 'icon-coordinates'
     70             );
     71 
     72             this._map = map;
     73             this._container = container;
     74             this._link = link;
     75 
     76             barContainer.innerHTML = `
     77                 <div data-bind="css: wrapperClass">
     78                     <div class="leaflet-coordinates-container" data-bind="with: formattedCoordinates()">
     79                         <span class="leaflet-coordinates-latitude" data-bind="html: lat"></span>
     80                         <span class="leaflet-coordinates-longitude" data-bind="html: lng"></span>
     81                     </div>
     82                     <hr class="leaflet-coordinates-divider" />
     83                     <div data-bind="foreach: formats">
     84                         <div>
     85                             <label title="">
     86                                 <input type="radio" data-bind="checked: $parent.formatCode, value: code" 
     87                                        class="leaflet-coordinates-format-radio"/>
     88                                 <span data-bind="html: label"></span>
     89                             </label>
     90                         </div>
     91                     </div>
     92                     <hr class="leaflet-coordinates-divider" />
     93                     <label title="">
     94                         <input type="checkbox"
     95                             data-bind="checked: displayElevation"
     96                             class="leaflet-coordinates-elevation-toggle"/>
     97                         Display elevation
     98                     </label>
     99                 </div>
    100             `;
    101 
    102             barContainer.title = "Right click on map to copy coordinates";
    103 
    104             this.loadStateFromStorage();
    105             ko.applyBindings(this, container);
    106             L.DomEvent.on(link, 'click', this.onClick, this);
    107 
    108             return container;
    109         },
    110 
    111         onRemove: function() {
    112             L.DomEvent.off(this._link, 'click', this.onClick, this);
    113         },
    114 
    115         loadStateFromStorage: function() {
    116             const active = safeLocalStorage.leafletCoordinatesActive === '1';
    117             const code = safeLocalStorage.leafletCoordinatesFmt || DEFAULT_FORMAT.code;
    118             const elevationDisplayed = (safeLocalStorage.leafletCoordinatesDisplayElevation ?? '1') === '1';
    119 
    120             this.formatCode(code);
    121             this.displayElevation(elevationDisplayed);
    122             this.setEnabled(active);
    123         },
    124 
    125         saveEnabledStateToStorage: function() {
    126             safeLocalStorage.leafletCoordinatesActive = this.isEnabled() ? '1' : '0';
    127         },
    128 
    129         saveFormatStateToStorage: function() {
    130             safeLocalStorage.leafletCoordinatesFmt = this.formatCode();
    131         },
    132 
    133         saveElevationDisplayStateToStorage: function() {
    134             safeLocalStorage.leafletCoordinatesDisplayElevation = this.displayElevation() ? '1' : '0';
    135         },
    136 
    137         onMouseMove: function(e) {
    138             this.latlng(e.latlng);
    139         },
    140 
    141         setEnabled: function(enabled) {
    142             if (Boolean(enabled) === this.isEnabled()) {
    143                 return;
    144             }
    145             const classFunc = enabled ? 'addClass' : 'removeClass';
    146             const eventFunc = enabled ? 'on' : 'off';
    147             L.DomUtil[classFunc](this._container, 'active');
    148             L.DomUtil[classFunc](this._container, 'highlight');
    149             L.DomUtil[classFunc](this._map._container, 'coordinates-control-active');
    150             this._map[eventFunc]('mousemove', this.onMouseMove, this);
    151             this._map[eventFunc]('contextmenu', this.onMapRightClick, this);
    152             this._map[enabled ? 'addLayer' : 'removeLayer'](this.elevationDisplayLayer);
    153             this._isEnabled = Boolean(enabled);
    154             this.latlng(null);
    155         },
    156 
    157         onMapRightClick: function(e) {
    158             L.DomEvent.stop(e);
    159             function createItem(format, elevation, overrides = {}) {
    160                 const {lat, lng} = formats.formatLatLng(e.latlng.wrap(), format);
    161                 let text = `${lat} ${lng}`;
    162                 if (elevation !== null) {
    163                     text += ` H=${elevation} m`;
    164                 }
    165 
    166                 return {text: `${text} <span class="leaflet-coordinates-menu-fmt">${format.label}</span>`,
    167                     callback: () => copyToClipboard(text, e.originalEvent),
    168                     ...overrides};
    169             }
    170 
    171             const items = [
    172                 createItem(
    173                     this.format(),
    174                     null,
    175                     {
    176                         text: '<b>Copy coordinates to clipboard</b>',
    177                         header: true,
    178                     },
    179                 ),
    180                 ...this.formats.map((format) => createItem(format, null)),
    181             ];
    182             const elevationResult = this.elevationDisplayLayer.getElevation(e.latlng);
    183             if (elevationResult.ready && !elevationResult.error && elevationResult.elevation !== null) {
    184                 const elevation = elevationResult.elevation;
    185                 items.push(
    186                     '-',
    187                     {
    188                         text: `Copy elevation to clipboard: ${elevation}`,
    189                         header: true,
    190                         callback: () => copyToClipboard(elevation, e.originalEvent),
    191                     },
    192                     '-',
    193                     createItem(
    194                         this.format(),
    195                         elevation,
    196                         {
    197                             text: '<b>Copy coordinates with elevation to clipboard</b>',
    198                             header: true,
    199                         },
    200                     ),
    201                     ...this.formats.map((format) => createItem(format, elevation)),
    202                 );
    203             }
    204             new Contextmenu(items).show(e);
    205         },
    206 
    207         isEnabled: function() {
    208             return Boolean(this._isEnabled);
    209         },
    210 
    211         onClick: function() {
    212             this.setEnabled(!this.isEnabled());
    213             this.saveEnabledStateToStorage();
    214         },
    215 
    216         onDisplayElevationChange: function(on) {
    217             this.saveElevationDisplayStateToStorage();
    218             this.elevationDisplayLayer.enableElevationDisplay(on);
    219         },
    220     }
    221 );