index.js (3552B)
1 import L from 'leaflet'; 2 3 import Contextmenu from '~/lib/contextmenu'; 4 import {ElevationProvider} from '~/lib/elevations'; 5 import {makeButton} from '~/lib/leaflet.control.commons'; 6 import * as logging from '~/lib/logging'; 7 8 import './style.css'; 9 10 class ExternalMap { 11 constructor(urlTemplate, minZoom, maxZoom) { 12 this.url = urlTemplate; 13 this.minZoom = minZoom; 14 this.maxZoom = maxZoom; 15 } 16 17 async getData(map_) { 18 const {lat, lng} = map_.getCenter(); 19 let zoom = map_.getZoom(); 20 zoom = Math.min(this.maxZoom, zoom); 21 zoom = Math.max(this.minZoom, zoom); 22 return {lat, lng, zoom}; 23 } 24 25 async getUrl(map_) { 26 return L.Util.template(this.url, await this.getData(map_)); 27 } 28 } 29 30 class GoogleEarthMap extends ExternalMap { 31 constructor() { 32 super('https://earth.google.com/web/@{lat},{lng},0a,{dist}d,35y,0h,0t,0r', 0, 100); 33 } 34 35 async getData(map_) { 36 const data = await super.getData(map_); 37 const windowSize = window.innerHeight; 38 const earthPerimeter = 40075016; 39 const degree = Math.PI / 180; 40 const viewAngle = 35; 41 let elevation; 42 try { 43 elevation = (await new ElevationProvider().get([data]))[0]; 44 } catch (e) { 45 elevation = 8000; 46 logging.captureException(e, 'failed to get elevation for GoogleEarth link'); 47 } 48 const mercatorPixelSize = (earthPerimeter / (256 * 2 ** data.zoom)) * Math.cos(data.lat * degree); 49 const distInPixels = windowSize / 2 / Math.tan((viewAngle * degree) / 2); 50 data.dist = distInPixels * mercatorPixelSize + elevation; 51 return data; 52 } 53 } 54 55 const ExternalMaps = L.Control.extend({ 56 options: { 57 externalMaps: [ 58 {title: 'Google', externalMap: new ExternalMap('https://www.google.com/maps/@{lat},{lng},{zoom}z', 3, 21)}, 59 { 60 title: 'Yandex', 61 externalMap: new ExternalMap('https://yandex.ru/maps/?ll={lng}%2C{lat}&z={zoom}', 2, 21), 62 }, 63 { 64 title: 'OpenStreetMap', 65 externalMap: new ExternalMap('https://www.openstreetmap.org/#map={zoom}/{lat}/{lng}', 0, 19), 66 }, 67 {title: 'Google Earth 3D', externalMap: new GoogleEarthMap()}, 68 { 69 title: 'Mapy.cz', 70 externalMap: new ExternalMap('https://en.mapy.cz/turisticka?x={lng}&y={lat}&z={zoom}', 2, 19), 71 }, 72 { 73 title: 'Wikimapia', 74 externalMap: new ExternalMap('https://wikimapia.org/#lat={lat}&lon={lng}&z={zoom}', 3, 22), 75 }, 76 ], 77 }, 78 79 onAdd: function (map) { 80 this._map = map; 81 const {container, link} = makeButton(null, 'View this place on another map', 'icon-external-links'); 82 this._container = container; 83 L.DomEvent.on(link, 'click contextmenu', this.onClick, this); 84 85 const menuItems = [ 86 {text: 'View this place on another map', header: true}, 87 ...this.options.externalMaps.map((it) => ({ 88 text: it.title, 89 callback: this.openExternalMap.bind(this, it.externalMap), 90 })), 91 ]; 92 this.menu = new Contextmenu(menuItems); 93 return container; 94 }, 95 96 onClick: function (e) { 97 this.menu.show(e); 98 }, 99 100 async openExternalMap(externalMap) { 101 const url = await externalMap.getUrl(this._map); 102 window.open(url, '_blank'); 103 }, 104 }); 105 106 export {ExternalMaps};