index.js (8668B)
1 import L from 'leaflet'; 2 import {WikimapiaLoader} from './wikimapia-loader'; 3 import './style.css'; 4 import {openPopupWindow} from '~/lib/popup-window'; 5 6 function isPointInPolygon(polygon, p) { 7 var inside = false; 8 var prevNode = polygon[polygon.length - 1], 9 node, i; 10 for (i = 0; i < polygon.length; i++) { 11 node = polygon[i]; 12 if ( 13 ((node[0] <= p[0] && p[0] < prevNode[0]) || (prevNode[0] <= p[0] && p[0] < node[0])) && 14 p[1] < (prevNode[1] - node[1]) * (p[0] - node[0]) / (prevNode[0] - node[0]) + node[1] 15 ) { 16 inside = !inside; 17 } 18 prevNode = node; 19 } 20 return inside; 21 } 22 23 L.Wikimapia = L.GridLayer.extend({ 24 options: { 25 tileSize: 1024, 26 updateWhenIdle: true, 27 tilesBaseUrl: 'http://wikimapia.org/', 28 }, 29 30 initialize: function(options) { 31 L.GridLayer.prototype.initialize.call(this, options); 32 this.loader = null; 33 }, 34 35 onAdd: function(map) { 36 if (!this.loader) { 37 this.loader = new WikimapiaLoader(this.options.tilesBaseUrl, map); 38 } 39 L.GridLayer.prototype.onAdd.call(this, map); 40 this.on('tileunload', this.onTileUnload, this); 41 this.on('tileload', this.onTileLoad, this); 42 map.on('mousemove', this.onMouseMove, this); 43 map.on('click', this.onClick, this); 44 map.on('zoomend', this.onZoomEnd, this); 45 map.on('zoomstart', this.onZoomStart, this); 46 }, 47 48 onRemove: function(map) { 49 map.off('mousemove', this.onMouseMove, this); 50 map.off('click', this.onClick, this); 51 if (this.highlightedPlace) { 52 this._map.removeLayer(this.highlightedPlace.polygon); 53 this._map.removeLayer(this.highlightedPlace.label); 54 this.highlightedPlace = null; 55 } 56 L.TileLayer.prototype.onRemove.call(this, map); 57 this.off('tileunload', this.onTileUnload, this); 58 this.off('tileload', this.onTileLoad, this); 59 map.off('zoomend', this.onZoomEnd, this); 60 map.off('zoomstart', this.onZoomStart, this); 61 }, 62 63 drawTile: function(canvas) { 64 if (!this._map) { 65 return; 66 } 67 const 68 tileData = canvas._tileData, 69 adjustment = canvas._adjustment; 70 if (!tileData) { 71 return; 72 } 73 74 const canvasCtx = canvas.getContext('2d'); 75 canvasCtx.beginPath(); 76 canvasCtx.strokeStyle = '#CFA600'; 77 canvasCtx.lineWidth = 1; 78 for (let place of tileData.places) { 79 let polygon = place.localPolygon; 80 if (!polygon) { 81 continue; 82 } 83 if (adjustment) { 84 let {multiplier, offsetX, offsetY} = adjustment, 85 polygon2 = []; 86 for (let i = 0; i < polygon.length; i++) { 87 let p = polygon[i]; 88 polygon2.push({ 89 x: p.x * multiplier - offsetX, 90 y: p.y * multiplier - offsetY 91 } 92 ); 93 } 94 polygon = polygon2; 95 } 96 canvasCtx.moveTo(polygon[0].x, polygon[0].y); 97 let p; 98 for (let i = 1; i < polygon.length; i++) { 99 p = polygon[i]; 100 canvasCtx.lineTo(p.x, p.y); 101 } 102 canvasCtx.lineTo(polygon[0].x, polygon[0].y); 103 } 104 canvasCtx.stroke(); 105 }, 106 107 createTile: function(coords, done) { 108 const canvas = L.DomUtil.create('canvas', 'leaflet-tile'); 109 canvas.width = this.options.tileSize; 110 canvas.height = this.options.tileSize; 111 let {dataPromise, abortLoading} = this.loader.requestTileData(coords); 112 dataPromise.then((data) => { 113 if (!data.error) { 114 canvas._tileData = data.tileData; 115 canvas._adjustment = data.adjustment; 116 this.drawTile(canvas); 117 this._tileOnLoad(done, canvas); 118 } 119 }); 120 121 canvas._abortLoading = abortLoading; 122 return canvas; 123 }, 124 125 _tileOnLoad: function(done, tile) { 126 // For https://github.com/Leaflet/Leaflet/issues/3332 127 if (L.Browser.ielt9) { 128 setTimeout(L.bind(done, this, null, tile), 0); 129 } else { 130 done(null, tile); 131 } 132 }, 133 134 onTileUnload: function(e) { 135 const tile = e.tile; 136 tile._abortLoading(); 137 delete tile._tileData; 138 delete tile._adjustment; 139 }, 140 141 onTileLoad: function() { 142 if (!this.lastMousePos) { 143 return; 144 } 145 if (this.mapZooming) { 146 return; 147 } 148 this.updateHighlight(); 149 }, 150 151 _tileCoordsFromLatlng: function(latlng) { 152 let layerPoint = this._map.latLngToLayerPoint(latlng); 153 layerPoint = layerPoint.add(this._map.getPixelOrigin()); 154 const tileSize = this.options.tileSize; 155 let coords = { 156 x: Math.floor(layerPoint.x / tileSize), 157 y: Math.floor(layerPoint.y / tileSize), 158 z: this._map.getZoom() 159 }; 160 161 return coords; 162 }, 163 164 getPlaceAtLatlng: function(latlng) { 165 latlng = latlng.wrap(); 166 const tileCoords = this._tileCoordsFromLatlng(latlng); 167 let tile = this._tiles[this._tileCoordsToKey(tileCoords)]; 168 if (!tile) { 169 return null; 170 } 171 const tileData = tile.el._tileData; 172 if (!tileData) { 173 return null; 174 } 175 176 const places = tileData.places; 177 const {lat, lng} = latlng; 178 let bounds, place; 179 for (let i = places.length - 1; i >= 0; i--) { 180 place = places[i]; 181 bounds = place.boundsWESN; 182 if (lng >= bounds[0] && lng <= bounds[1] && lat >= bounds[2] && lat <= bounds[3] && 183 isPointInPolygon(place.polygon, [lat, lng])) { 184 return place; 185 } 186 } 187 return null; 188 }, 189 190 onMouseMove: function(e) { 191 this.lastMousePos = e.latlng; 192 this.updateHighlight(); 193 }, 194 195 onZoomEnd: function() { 196 this.mapZooming = false; 197 this.updateHighlight(); 198 }, 199 onZoomStart: function() { 200 this.mapZooming = true; 201 }, 202 203 updateHighlight: function() { 204 if (!this.lastMousePos) { 205 return; 206 } 207 const place = this.getPlaceAtLatlng(this.lastMousePos); 208 if (this.highlightedPlace && (!place || this.highlightedPlace.id !== place.id)) { 209 this._map.removeLayer(this.highlightedPlace.polygon); 210 this._map.removeLayer(this.highlightedPlace.label); 211 this.highlightedPlace = null; 212 } 213 214 if (place && !this.highlightedPlace) { 215 this.highlightedPlace = { 216 id: place.id, 217 polygon: L.polygon(place.polygon, { 218 weight: 0, 219 color: '#E6B800' 220 } 221 ), 222 label: L.tooltip({className: 'wikimapia-tooltip-wrapper'}, null) 223 }; 224 this.highlightedPlace.label.setLatLng(this.lastMousePos); 225 this.highlightedPlace.polygon.addTo(this._map).bringToBack(); 226 this.highlightedPlace.label.setContent(`<div class="wikimapia-tooltip">${place.title}</div>`); 227 this._map.addLayer(this.highlightedPlace.label); 228 } 229 if (this.highlightedPlace) { 230 this.highlightedPlace.label.setLatLng(this.lastMousePos); 231 } 232 }, 233 234 onClick: function(e) { 235 if (this._map.clickLocked) { 236 return; 237 } 238 const place = this.getPlaceAtLatlng(e.latlng); 239 if (place) { 240 const url = `http://wikimapia.org/${place.id}/ru/`; 241 openPopupWindow(url, 564, 'wikimapia-details'); 242 } 243 }, 244 } 245 );