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, ' – '); 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 );