coverage-layer.js (5403B)
1 import L from 'leaflet'; 2 import {TiledDataLoader} from '~/lib/tiled-data-loader'; 3 4 const MultiLayer = L.Layer.extend({ 5 initialize: function(layers) { 6 this.layers = layers; 7 }, 8 9 setLayersVisibility: function(e) { 10 if (!this._map) { 11 return; 12 } 13 14 let newZoom; 15 if (e && e.zoom !== undefined) { 16 newZoom = e.zoom; 17 } else { 18 newZoom = this._map.getZoom(); 19 } 20 21 for (let layer of this.layers) { 22 if (layer.minZoom <= newZoom && newZoom <= layer.maxZoom) { 23 this._map.addLayer(layer.layer); 24 } else { 25 this._map.removeLayer(layer.layer); 26 } 27 } 28 }, 29 30 onAdd: function(map) { 31 this._map = map; 32 this.setLayersVisibility(); 33 map.on('zoomend', this.setLayersVisibility, this); 34 }, 35 36 onRemove: function() { 37 for (let layer of this.layers) { 38 this._map.removeLayer(layer.layer); 39 } 40 this._map.off('zoomend', this.setLayersVisibility, this); 41 this._map.off('zoomanim', this.setLayersVisibility, this); 42 this._map = null; 43 } 44 45 }); 46 47 class WikimediaLoader extends TiledDataLoader { 48 constructor(urlTemplate, zoom) { 49 super(); 50 this.url = urlTemplate; 51 this.maxZoom = zoom; 52 } 53 54 getTileUrl(coords) { 55 const data = { 56 x: coords.x, 57 z: coords.z, 58 y: 2 ** coords.z - 1 - coords.y 59 }; 60 return L.Util.template(this.url, data); 61 } 62 63 layerTileToDataTileCoords(layerTileCoords) { 64 let z = layerTileCoords.z; 65 let z2 = null; 66 if (z > this.maxZoom) { 67 z2 = this.maxZoom; 68 } else { 69 return {z, x: layerTileCoords.x, y: layerTileCoords.y}; 70 } 71 72 let multiplier = 1 << (z - z2); 73 return { 74 x: Math.floor(layerTileCoords.x / multiplier), 75 y: Math.floor(layerTileCoords.y / multiplier), 76 z: z2 77 }; 78 } 79 80 makeRequestData(dataTileCoords) { 81 return { 82 url: this.getTileUrl(dataTileCoords), 83 options: { 84 responseType: 'arraybuffer', 85 timeout: 10000, 86 isResponseSuccess: (xhr) => xhr.status === 200 || xhr.status === 404 87 } 88 }; 89 } 90 91 async processResponse(xhr, originalDataTileCoords) { 92 let tileData; 93 if (originalDataTileCoords.z >= this.maxZoom && xhr.status === 200 && xhr.response) { 94 tileData = new Uint16Array(xhr.response); 95 } else { 96 tileData = null; 97 } 98 99 return { 100 tileData, 101 coords: originalDataTileCoords 102 }; 103 } 104 } 105 106 const WikimediaVectorCoverage = L.GridLayer.extend({ 107 options: { 108 tileSize: 256, 109 color: '#ff00ff', 110 }, 111 112 initialize: function(url, options) { 113 L.GridLayer.prototype.initialize.call(this, options); 114 this.loader = new WikimediaLoader(url, 11); 115 }, 116 117 onAdd: function(map) { 118 L.GridLayer.prototype.onAdd.call(this, map); 119 this.on('tileunload', this.onTileUnload, this); 120 }, 121 122 onRemove: function(map) { 123 L.GridLayer.prototype.onRemove.call(this, map); 124 this.off('tileunload', this.onTileUnload, this); 125 }, 126 127 onTileUnload: function(e) { 128 const tile = e.tile; 129 tile._abortLoading(); 130 delete tile._tileData; 131 delete tile._adjustment; 132 }, 133 134 drawTile: function(canvas, _unused_coords) { 135 if (!this._map) { 136 return; 137 } 138 if (!canvas._tileData) { 139 return; 140 } 141 const dataOffset = 5000; 142 const dataExtent = 65535 - 2 * dataOffset; 143 const tileData = canvas._tileData; 144 let {multiplier, offsetX, offsetY} = canvas._adjustment; 145 const canvasCtx = canvas.getContext('2d'); 146 const pixelScale = multiplier * 256 / dataExtent; 147 canvasCtx.fillStyle = this.options.color; 148 for (let i = 0, l = tileData.length; i < l; i += 2) { 149 let x = (tileData[i] - dataOffset) * pixelScale - offsetX * 256; 150 let y = (tileData[i + 1] - dataOffset) * pixelScale - offsetY * 256; 151 canvasCtx.beginPath(); 152 canvasCtx.arc(x, y, 5, 0, 2 * Math.PI); 153 canvasCtx.fill(); 154 } 155 }, 156 157 createTile: function(coords, done) { 158 const canvas = L.DomUtil.create('canvas', 'leaflet-tile'); 159 canvas.width = this.options.tileSize; 160 canvas.height = this.options.tileSize; 161 let {dataPromise, abortLoading} = this.loader.requestTileData(coords); 162 dataPromise.then((data) => { 163 if (!data.error) { 164 canvas._tileData = data.tileData; 165 canvas._adjustment = data.adjustment || {multiplier: 1, offsetX: 0, offsetY: 0}; 166 setTimeout(() => { 167 this.drawTile(canvas, coords); 168 done(null, canvas); 169 }, 1); 170 } 171 }); 172 173 canvas._abortLoading = abortLoading; 174 return canvas; 175 }, 176 } 177 ); 178 179 export {MultiLayer, WikimediaVectorCoverage};