nakarte

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

index.js (12050B)


      1 import L from 'leaflet';
      2 import './style.css';
      3 import Contextmenu from '~/lib/contextmenu';
      4 import copyToClipboard from '~/lib/clipboardCopy';
      5 import {pulkovo1942ToWgs84} from '~/lib/coordinates-transformations';
      6 
      7 function zeroPad(num, size) {
      8     var s = String(num);
      9     while (s.length < size) {
     10         s = "0" + s;
     11     }
     12     return s;
     13 }
     14 
     15 const bigLetterReplacers = [
     16     /* eslint-disable quote-props */
     17     {
     18         '1': 'А',
     19         '2': 'Б',
     20         '3': 'В',
     21         '4': 'Г'
     22     },
     23     {
     24         '1': 'A',
     25         '2': 'B',
     26         '3': 'C',
     27         '4': 'D'
     28     }
     29     /* eslint-enable quote-props */
     30 ];
     31 
     32 var Nomenclature = {
     33     getQuadName1m: function(column, row, join) {
     34         var name = '';
     35         if (row < 0) {
     36             row = -row - 1;
     37             name += 'x';
     38         }
     39         column += 31;
     40         name += 'ABCDEFGHIJKLMNOPQRSTUV'[row] + '-' + zeroPad(column, 2);
     41         for (var n = 1; n <= join - 1; n++) {
     42             name += ',' + zeroPad(column + n, 2);
     43         }
     44         return [name];
     45     },
     46 
     47     getQuadName500k: function(column, row, join) {
     48         var name1 = this.getQuadName1m(Math.floor(column / 2), Math.floor(row / 2), 1)[0];
     49         var subquad = 2 - (row & 1) * 2 + (column & 1) + 1;
     50         let name2 = '-' + subquad;
     51         if (join > 1) {
     52             name2 += ',' + (subquad + 1);
     53         }
     54         if (join === 4) {
     55             name2 +=
     56                 ',' + this.getQuadName1m(Math.floor((column + 2) / 2), Math.floor(row / 2), 1) + '-' + subquad +
     57                 ',' + (subquad + 1);
     58         }
     59         const names = [name1 + name2];
     60         for (let replacer of bigLetterReplacers) {
     61             let name3 = name2.replace(/\b[1-4]\b/gu, (s) => replacer[s]);
     62             names.push(name1 + name3);
     63         }
     64         return names;
     65     },
     66 
     67     getQuadName100k: function(column, row, join) {
     68         var name = this.getQuadName1m(Math.floor(column / 12), Math.floor(row / 12), 1);
     69         const subRow = row - Math.floor(row / 12) * 12;
     70         const subColumn = column - Math.floor(column / 12) * 12;
     71         var subquad = 132 - subRow * 12 + subColumn + 1;
     72         name = name + '-' + zeroPad(subquad, 3);
     73         if (join > 1) {
     74             name += ',' + zeroPad(subquad + 1, 3);
     75         }
     76         if (join === 4) {
     77             name += ',' + zeroPad(subquad + 2, 3) + ',' + zeroPad(subquad + 3, 3);
     78         }
     79 
     80         return [name];
     81     },
     82 
     83     getQuadName050k: function(column, row, join) {
     84         var name1 = this.getQuadName100k(Math.floor(column / 2), Math.floor(row / 2), 1);
     85         var subquad = 2 - (row & 1) * 2 + (column & 1) + 1;
     86         let name2 = '-' + subquad;
     87         if (join > 1) {
     88             name2 += ',' + (subquad + 1);
     89         }
     90         if (join === 4) {
     91             name2 += ',' + this.getQuadName100k(Math.floor((column + 2) / 2), Math.floor(row / 2), 1) + '-' +
     92                 subquad + ',' + (subquad + 1);
     93         }
     94         const names = [name1 + name2];
     95         for (let replacer of bigLetterReplacers) {
     96             let name3 = name2.replace(/\b[1-4]\b/gu, (s) => replacer[s]);
     97             names.push(name1 + name3);
     98         }
     99         return names;
    100     },
    101 
    102     _getQuads: function(bounds, row_height, column_width, name_factory) {
    103         const margin = 0.002;
    104         bounds = L.latLngBounds(bounds);
    105         var quads = [];
    106         var min_lat = Math.max(bounds.getSouth() - margin, -84);
    107         var max_lat = Math.min(bounds.getNorth() + margin, 84);
    108         var min_row = Math.floor(min_lat / row_height);
    109         const maxCols = 360 / column_width;
    110         for (var row = min_row; row * row_height < max_lat; row++) {
    111             var row_south = row * row_height;
    112             var row_north = row_south + row_height;
    113             var joined_quads;
    114             if (row_south >= 76 || row_north <= -76) {
    115                 joined_quads = 4;
    116             } else if (row_south >= 60 || row_north <= -60) {
    117                 joined_quads = 2;
    118             } else {
    119                 joined_quads = 1;
    120             }
    121             var min_lon = bounds.getWest() - margin;
    122             var max_lon = bounds.getEast() + margin;
    123             var min_column = Math.floor((min_lon + 180) / column_width / joined_quads) * joined_quads -
    124                 Math.round(180 / column_width);
    125             for (var column = min_column; column * column_width < max_lon; column += joined_quads) {
    126                 var column_west = column * column_width;
    127                 var column_east = column_west + column_width * joined_quads;
    128                 var quad_bounds = L.latLngBounds([[row_south, column_west], [row_north, column_east]]);
    129                 // shift column to positive numbers, calc modulo, shift back
    130                 const wrappedColumn = ((column + maxCols / 2) % maxCols + maxCols) % maxCols - maxCols / 2;
    131                 var names = name_factory(wrappedColumn, row, joined_quads);
    132                 quads.push({names: names, bounds: quad_bounds});
    133             }
    134         }
    135         return quads;
    136     },
    137 
    138     getQuads1m: function(bounds) {
    139         return this._getQuads(bounds, 4, 6, this.getQuadName1m);
    140     },
    141 
    142     getQuads500k: function(bounds) {
    143         return this._getQuads(bounds, 2, 3, this.getQuadName500k.bind(this));
    144     },
    145 
    146     getQuads100k: function(bounds) {
    147         return this._getQuads(bounds, 4 / 12, 6 / 12, this.getQuadName100k.bind(this));
    148     },
    149 
    150     getQuads050k: function(bounds) {
    151         return this._getQuads(bounds, 4 / 12 / 2, 6 / 12 / 2, this.getQuadName050k.bind(this));
    152     }
    153 
    154 };
    155 
    156 L.Layer.SovietTopoGrid = L.LayerGroup.extend({
    157         options: {},
    158 
    159         initialize: function(options) {
    160             L.LayerGroup.prototype.initialize.call(this);
    161             L.Util.setOptions(this, options);
    162             this.renderer = L.svg({padding: 0.5});
    163             this._quads = {};
    164             this._updateRenderer = L.Util.throttle(this._updateRenderer, 100, this);
    165             this._update = L.Util.throttle(this._update, 100, this);
    166         },
    167 
    168         onAdd: function(map) {
    169             this._map = map;
    170             map.on('zoomend', this._reset, this);
    171             map.on('move', this._update, this);
    172             map.on('move', this._updateRenderer, this);
    173             this._update();
    174         },
    175 
    176         onRemove: function(map) {
    177             map.off('zoomend', this._reset, this);
    178             map.off('move', this._update, this);
    179             this._cleanupQuads(true);
    180         },
    181 
    182         _addQuad: function(bounds, id, titles, color, layer, scale) {
    183             if (id in this._quads) {
    184                 return;
    185             }
    186             var rect_options = {
    187                 smoothFactor: 0,
    188                 noClip: true,
    189                 interactive: false,
    190                 fill: false,
    191                 opacity: {1: 0.7, 2: 0.4}[layer],
    192                 color: color,
    193                 weight: {1: 1, 2: 3}[layer],
    194                 renderer: this.renderer
    195             };
    196 
    197             const quadPoints = [
    198                 bounds.getSouthWest(),
    199                 bounds.getSouthEast(),
    200                 bounds.getNorthEast(),
    201                 bounds.getNorthWest(),
    202             ].map((latlng) => {
    203                 const latlon = pulkovo1942ToWgs84({lat: latlng.lat, lon: latlng.lng});
    204                 return L.latLng(latlon.lat, latlon.lon);
    205             });
    206             if (quadPoints[0].lng > 0 && quadPoints[1].lng < 0) {
    207                 quadPoints[1] = L.latLng(quadPoints[1].lat, quadPoints[1].lng + 360);
    208                 quadPoints[2] = L.latLng(quadPoints[2].lat, quadPoints[2].lng + 360);
    209             }
    210             var rect = L.polygon(quadPoints, rect_options);
    211             this.addLayer(rect);
    212             if (layer === 1) {
    213                 rect.bringToBack();
    214             }
    215             var objects = [rect];
    216             const title = titles[0].replace(/-/gu, ' &ndash; ');
    217             var html = L.Util.template(`<span style="color:{color}">{title}</span>`, {color: color, title: title});
    218             var icon = L.divIcon({html: html, className: 'leaflet-sovietgrid-quadtitle-' + layer, iconSize: null});
    219             var marker = L.marker(L.latLngBounds(bounds).getCenter(), {icon: icon});
    220             this.addLayer(marker);
    221             objects.push(marker);
    222             this._quads[id] = objects;
    223             marker.on('click contextmenu', this._showContextMenu.bind(this, scale, titles));
    224         },
    225 
    226         _showContextMenu: function(scale, titles, e) {
    227             const scaleString = {
    228                 '1m': '1:1 000 000',
    229                 '500k': '1:500 000',
    230                 '100k': '1:100 000',
    231                 '050k': '1:50 000'
    232             }[scale];
    233             const items = [
    234                 {text: scaleString, header: true},
    235                 {text: 'Click name to copy to clibpoard', header: true},
    236                 {
    237                     text: titles[0],
    238                     callback: () => {
    239                         copyToClipboard(titles[0], e.originalEvent);
    240                     }
    241                 }
    242             ];
    243             if (titles.length > 1) {
    244                 items.push({
    245                         text: titles[1] + ' <span class="leaflet-sovietgrid-lang">RUS</span>',
    246                         callback: () => {
    247                             copyToClipboard(titles[1], e.originalEvent);
    248                         }
    249                     }
    250                 );
    251                 items.push({
    252                         text: titles[2] + ' <span class="leaflet-sovietgrid-lang">LAT</span>',
    253                         callback: () => {
    254                             copyToClipboard(titles[2], e.originalEvent);
    255                         }
    256                     }
    257                 );
    258             }
    259             const menu = new Contextmenu(items);
    260             menu.show(e);
    261         },
    262 
    263         _removeQuad: function(id) {
    264             this._quads[id].forEach(this.removeLayer.bind(this));
    265             delete this._quads[id];
    266         },
    267 
    268         _addQuads: function(quads, scale, color, layer) {
    269             quads.forEach(function(quad) {
    270                     var id = scale + quad.names[0];
    271                     this._addQuad(quad.bounds, id, quad.names, color, layer, scale);
    272                 }.bind(this)
    273             );
    274         },
    275 
    276         _addGrid: function() {
    277             var quads;
    278             var layer;
    279             var map_bbox = this._map.getBounds();
    280             var zoom = this._map.getZoom();
    281 
    282             if (zoom >= 10) {
    283                 quads = Nomenclature.getQuads050k(map_bbox);
    284                 layer = (zoom >= 12) ? 2 : 1;
    285                 this._addQuads(quads, '050k', '#50d', layer);
    286             }
    287 
    288             if (zoom >= 8 && zoom < 12) {
    289                 quads = Nomenclature.getQuads100k(map_bbox);
    290                 layer = (zoom >= 10) ? 2 : 1;
    291                 this._addQuads(quads, '100k', '#d50', layer);
    292             }
    293 
    294             if (zoom >= 6 && zoom < 10) {
    295                 quads = Nomenclature.getQuads500k(map_bbox);
    296                 layer = (zoom >= 8) ? 2 : 1;
    297                 this._addQuads(quads, '500k', '#099', layer);
    298             }
    299 
    300             if (zoom >= 4 && zoom < 8) {
    301                 layer = (zoom >= 6) ? 2 : 1;
    302                 quads = Nomenclature.getQuads1m(map_bbox);
    303                 this._addQuads(quads, '1m', 'blue', layer);
    304             }
    305         },
    306 
    307         _reset: function() {
    308             this._update(true);
    309         },
    310 
    311         _cleanupQuads: function(reset) {
    312             if (reset === true) {
    313                 this.clearLayers();
    314                 this._quads = {};
    315             } else {
    316                 var map_bbox = this._map.getBounds();
    317                 for (var quad_id of Object.keys(this._quads)) {
    318                     var rect = this._quads[quad_id][0];
    319                     if (!map_bbox.intersects(rect.getBounds())) {
    320                         this._removeQuad(quad_id);
    321                     }
    322                 }
    323             }
    324         },
    325 
    326         _updateRenderer: function() {
    327             if (this.renderer._map) {
    328                 this.renderer._update();
    329             }
    330         },
    331 
    332         _update: function(reset) {
    333             this._cleanupQuads(reset);
    334             this._addGrid();
    335         }
    336     }
    337 );