nakarte

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

index.js (11210B)


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