|
| 1 | +import { TileLayer, Point, Util } from "leaflet"; |
| 2 | + |
| 3 | +function $svg(name, attributes) { |
| 4 | + const e = document.createElementNS("http://www.w3.org/2000/svg", name); |
| 5 | + for (const [k, v] of Object.entries(attributes || {})) |
| 6 | + if (k.startsWith("xlink:")) e.setAttributeNS("http://www.w3.org/1999/xlink", k, v); |
| 7 | + else e.setAttribute(k, v); |
| 8 | + return e; |
| 9 | +} |
| 10 | + |
| 11 | +const defaultMaskUrl = [ |
| 12 | + "data:image/png;base64,", |
| 13 | + "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAEC0lEQVRYR7WX+YuVZRTHP09pZZot", |
| 14 | + "WpiamktpUWJGC6GEoLigRIo/CFr/WgtqGqREuKRptBCUQmkqKuMSU+mMy4zOlOPUK5/LeeT1eu/c", |
| 15 | + "9+rMAy/vMPfe53zP93zPlmjzFEWRAB/PA/EugNqTUvJd+eSLWv6gKAqNPQg8BDwc71EB5j9gALgR", |
| 16 | + "78GUkv9reVoCCMMafAyYADwNTAQeB8YEgH+Ba8Bl4CJwCegB/mkFpCmAoFpvNTQVmBPPDOAZYDzw", |
| 17 | + "SADQewF0A38Ap4GTwLkAcyOl9H8jOhoCCOOPAs8CLwOvA68AzwcLfjY6QuK9Xj6ox8DVAPE7cAj4", |
| 18 | + "FTgPXG/Exl0AwvhYQE/fBBYD84HJwLiIfRZf+feKLwPpj1AcB74HfghGeupB3AGg5LmeanhpGJdy", |
| 19 | + "6VaEVY5ADMsVQBAHgG+AY0BvORz1ABTbc8C7wCpgYcRbLbQUbB0yGTEshsRw7Ab2BhP9OV1vXxpq", |
| 20 | + "V91vA+8Di4L2ezGesWQQZscvwBfAfqAzpXTTL5UBmFLzgPeA1aF4xdau5/UhEoTh6AT2AdsDjHqo", |
| 21 | + "VTXCe/Nb6jcEC0+1EfNWulAT14HfgG3AV2ZGSmkwA5DmWcBaYB3wQhSZVhe387mUy4LGN5ueKaW+", |
| 22 | + "DMC0U3AbgeXAJMAyO5xHFhSkKfkRcNAsSUH/E0H/h8A7wJPDEPtG4K0PhuET4EvgzwzAPNdzAciE", |
| 23 | + "jNyv+BoBsFmdArYAnwNnBCDVUr4G2AS8OgLxz2DUwZnIBEGcygCs+RmANd+UHIkjgLP1AKzrMrAS", |
| 24 | + "+ABYAAxH/jdywHpgp9wa6diRNWDOLwkNWAltwSOhAbvlUeBTYGetIkYDssu9EQzYgCxKVRtP1VBZ", |
| 25 | + "EXuBn4CPoyp25zpgE3oRWB99YGaMXVUvr/I9G9MFYE8w8DNwuxDprTrQe0uxA4hhyH2/ioGhvqP3", |
| 26 | + "1oATkX47gI6U0kC5GRkGBw9L8QpgerAwHFpQ/c6K3wKfAT86PzoXlAHkemBDsh2/FcOno9f9HKdj", |
| 27 | + "Y28FVHi7agUoJYvSnUovisL8nw0si7S0JliWDdG9MKFxu6Cp93U0oiMxFdX2h/qJyJg7fjsXqAdT", |
| 28 | + "07/tFTJRVRN5ENF4B/BdiO+wU3J5Lmw0lOqtApwbU5GTkSBMzTwXNgOiYb2WXjufnpt2dj5D0JUn", |
| 29 | + "oRzTZmO5IGTC4fS1qBEvAVMCXD2QbDjvB3/H7OcYZrrZgBRdbQwrn6EWE71UE3quLmTBWjEtxCnA", |
| 30 | + "PC96cV9sRg4dGnQCdjn5q9lOcJcG6tFFlTQ77A2uZdYK9wNBKc7cM1zNXMXcjDTo0xUCHBhqYa2k", |
| 31 | + "7ACSF1NZ0bBhkAGPVU4Q1noLjhpwQW25KVcCUGYmJih/Z4jyqu64lTejtlb0W3sASziD1RhmAAAA", |
| 32 | + "AElFTkSuQmCC", |
| 33 | +].join(""); |
| 34 | + |
| 35 | +export default class TileLayerWithMask extends TileLayer { |
| 36 | + static { |
| 37 | + this.setDefaultOptions({ |
| 38 | + maskUrl: defaultMaskUrl, |
| 39 | + maskSize: 512, |
| 40 | + }); |
| 41 | + } |
| 42 | + getMaskSize() { |
| 43 | + const s = this.options.maskSize; |
| 44 | + return s instanceof Point ? s : new Point(s, s); |
| 45 | + } |
| 46 | + setCenter(containerPoint) { |
| 47 | + if (arguments.length === 2) { |
| 48 | + this.setCenter(new Point(arguments[0], arguments[1])); |
| 49 | + return; |
| 50 | + } |
| 51 | + if (this._map && this._image) { |
| 52 | + const p = this._map |
| 53 | + .containerPointToLayerPoint(containerPoint) |
| 54 | + .subtract(this.getMaskSize().divideBy(2)); |
| 55 | + this._image.setAttribute("x", p.x); |
| 56 | + this._image.setAttribute("y", p.y); |
| 57 | + } |
| 58 | + } |
| 59 | + _initContainer() { |
| 60 | + if (this._container) return; |
| 61 | + const id = "leaflet-tilelayer-mask-" + Util.stamp(this); |
| 62 | + const size = this.getMaskSize(); |
| 63 | + const image = $svg("image", { |
| 64 | + width: size.x, |
| 65 | + height: size.y, |
| 66 | + "xlink:href": this.options.maskUrl, |
| 67 | + }); |
| 68 | + const mask = $svg("mask", { |
| 69 | + id: id, |
| 70 | + x: "-100%", |
| 71 | + y: "-100%", |
| 72 | + width: "300%", |
| 73 | + height: "300%", |
| 74 | + }); |
| 75 | + const defs = $svg("defs"); |
| 76 | + const container = $svg("g", { |
| 77 | + mask: `url(#${id})`, |
| 78 | + }); |
| 79 | + const rootGroup = this._map.getRenderer(this)._rootGroup; |
| 80 | + |
| 81 | + mask.appendChild(image); |
| 82 | + defs.appendChild(mask); |
| 83 | + rootGroup.appendChild(defs); |
| 84 | + rootGroup.appendChild(container); |
| 85 | + |
| 86 | + this._container = container; |
| 87 | + this._image = image; |
| 88 | + this.setCenter(this._map.getSize().divideBy(2)); |
| 89 | + } |
| 90 | + _updateLevels() { |
| 91 | + const zoom = this._tileZoom; |
| 92 | + if (zoom === undefined) return undefined; |
| 93 | + |
| 94 | + for (const z in this._levels) { |
| 95 | + if (!this._levels[z].el.firstChild && z !== zoom) { |
| 96 | + this._levels[z].el.parentNode.removeChild(this._levels[z].el); |
| 97 | + this._removeTilesAtZoom(z); |
| 98 | + delete this._levels[z]; |
| 99 | + } |
| 100 | + } |
| 101 | + |
| 102 | + let level = this._levels[zoom]; |
| 103 | + if (!level) { |
| 104 | + const map = this._map; |
| 105 | + level = { |
| 106 | + el: this._container.appendChild($svg("g")), |
| 107 | + origin: map.project(map.unproject(map.getPixelOrigin()), zoom).round(), |
| 108 | + zoom: zoom, |
| 109 | + }; |
| 110 | + |
| 111 | + const translate = level.origin |
| 112 | + .multiplyBy(map.getZoomScale(map.getZoom(), level.zoom)) |
| 113 | + .subtract(map._getNewPixelOrigin(map.getCenter(), zoom)) |
| 114 | + .round(); |
| 115 | + level.el.setAttribute("transform", `translate(${translate.x},${translate.y})`); |
| 116 | + |
| 117 | + Util.falseFn(level.el.offsetWidth); |
| 118 | + this._levels[zoom] = level; |
| 119 | + } |
| 120 | + this._level = level; |
| 121 | + return level; |
| 122 | + } |
| 123 | + _addTile(coords, container) { |
| 124 | + const tilePos = this._getTilePos(coords); |
| 125 | + const tileSize = this.getTileSize(); |
| 126 | + const key = this._tileCoordsToKey(coords); |
| 127 | + const url = this.getTileUrl(this._wrapCoords(coords)); |
| 128 | + |
| 129 | + const tile = $svg("image", { |
| 130 | + width: tileSize.x, |
| 131 | + height: tileSize.y, |
| 132 | + x: tilePos.x, |
| 133 | + y: tilePos.y, |
| 134 | + "xlink:href": url, |
| 135 | + }); |
| 136 | + container.appendChild(tile); |
| 137 | + |
| 138 | + this._tiles[key] = { |
| 139 | + el: tile, |
| 140 | + coords: coords, |
| 141 | + current: true, |
| 142 | + }; |
| 143 | + } |
| 144 | +} |
0 commit comments