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