Skip to content

Commit 313b195

Browse files
committed
Add TileLayerWithMask.js for Leaflet2.0
1 parent 1ac7ddb commit 313b195

File tree

3 files changed

+260
-11
lines changed

3 files changed

+260
-11
lines changed

README.md

Lines changed: 71 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
1-
# leaflet-tilelayer-mask (v1.0)
1+
# leaflet-tilelayer-mask
22

3-
Leaflet 1.0.x plugin, Leaflet-tilelayer-mask adds mask effect to tilelayer. Older version for Leaflet 0.7.x is available [here](https://github.com/frogcat/leaflet-tilelayer-mask/tree/v0.7).
3+
Leaflet 1.x and 2.x plugin, Leaflet-tilelayer-mask adds mask effect to tilelayer. Older version for Leaflet 0.7.x is available [here](https://github.com/frogcat/leaflet-tilelayer-mask/tree/v0.7).
44

55
![Leaflet-tilelayer-mask](http://frogcat.github.io/leaflet-tilelayer-mask/misc/screenshot.jpg)
66

77
## Demo
88

9-
<https://frogcat.github.io/leaflet-tilelayer-mask/>
9+
- <https://frogcat.github.io/leaflet-tilelayer-mask/> (Leaflet 1.x)
10+
- <https://frogcat.github.io/leaflet-tilelayer-mask/TileLayerWithMask.html> (Leaflet 2.x)
11+
12+
# for Leaflet 1.x
1013

1114
## Usage
1215

@@ -34,20 +37,77 @@ map.on("mousemove", function(e) {
3437
3538
## Options
3639
37-
Option | Type | Default | Description
38-
-------- | ----------------- | ---------------- | -----------------
39-
maskUrl | String | #1 | Url of mask image
40-
maskSize | L.point or Number | L.point(512,512) | mask size
40+
| Option | Type | Default | Description |
41+
| -------- | ----------------- | ---------------- | ----------------- |
42+
| maskUrl | String | #1 | Url of mask image |
43+
| maskSize | L.point or Number | L.point(512,512) | mask size |
4144
4245
Note #1 : Built in image 'data:image/png;base64,...' (white circle with soft edge over transparent background)
4346
4447
## Methods
4548
46-
Method | Returns | Description
47-
----------------------------- | ------- | ----------------------------------------------
48-
setCenter(Number x, Number y) | this | Set the mask center relative to map container.
49-
setCenter(Point p) | this | Expects a L.Point object instead
49+
| Method | Returns | Description |
50+
| ----------------------------- | ------- | ---------------------------------------------- |
51+
| setCenter(Number x, Number y) | this | Set the mask center relative to map container. |
52+
| setCenter(Point p) | this | Expects a L.Point object instead |
5053
5154
## Notice
5255
5356
- Leaflet-tilelayer-mask depends on SVG mask.
57+
58+
# for Leaflet 2.x
59+
60+
[leaflet-tilelayer-mask.js](https://github.com/frogcat/leaflet-tilelayer-mask/blob/master/leaflet-tilelayer-mask.js)
61+
has been rewritten as
62+
[TileLayerWithMask.js](https://github.com/frogcat/leaflet-tilelayer-mask/blob/master/TileLayerWithMask.js),
63+
an ES Module for Leaflet 2.0.
64+
65+
## Usage
66+
67+
```html
68+
<!DOCTYPE html>
69+
<html>
70+
<head>
71+
<meta charset="UTF-8" />
72+
<title>TileLayerWithMask for leaflet 2.0.0-alpha.1</title>
73+
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0" />
74+
<link href="https://unpkg.com/[email protected]/dist/leaflet.css" rel="stylesheet" />
75+
<script type="importmap">
76+
{
77+
"imports": {
78+
"leaflet": "https://unpkg.com/[email protected]/dist/leaflet.js",
79+
"mask": "https://frogcat.github.io/leaflet-tilelayer-mask/TileLayerWithMask.js"
80+
}
81+
}
82+
</script>
83+
</head>
84+
<body>
85+
<div id="map" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0"></div>
86+
<script type="module">
87+
import { Map, TileLayer } from "leaflet";
88+
import TileLayerWithMask from "mask";
89+
90+
const map = new Map("map").setView([35.6323, 139.768815], 15);
91+
92+
new TileLayer("https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png", {
93+
maxZoom: 18,
94+
attribution: '<a href="https://maps.gsi.go.jp/development/ichiran.html">GSI pale</a>',
95+
}).addTo(map);
96+
97+
const mask = new TileLayerWithMask(
98+
"https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/{z}/{x}/{y}.jpg",
99+
{
100+
maxZoom: 18,
101+
attribution:
102+
'<a href="https://maps.gsi.go.jp/development/ichiran.html">GSI seamlessphoto</a>',
103+
maskSize: 800,
104+
}
105+
).addTo(map);
106+
107+
map.on("pointermove", function (e) {
108+
mask.setCenter(e.containerPoint);
109+
});
110+
</script>
111+
</body>
112+
</html>
113+
```

TileLayerWithMask.html

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="UTF-8" />
5+
<title>TileLayerWithMask for leaflet 2.0.0-alpha.1</title>
6+
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0" />
7+
<link href="https://unpkg.com/[email protected]/dist/leaflet.css" rel="stylesheet" />
8+
<script type="importmap">
9+
{
10+
"imports": {
11+
"leaflet": "https://unpkg.com/[email protected]/dist/leaflet.js",
12+
"mask": "./TileLayerWithMask.js"
13+
}
14+
}
15+
</script>
16+
</head>
17+
<body>
18+
<div id="map" style="position: absolute; top: 0; left: 0; right: 0; bottom: 0"></div>
19+
<script type="module">
20+
import { Map, TileLayer } from "leaflet";
21+
import TileLayerWithMask from "mask";
22+
23+
const map = new Map("map").setView([35.6323, 139.768815], 15);
24+
25+
new TileLayer("https://cyberjapandata.gsi.go.jp/xyz/pale/{z}/{x}/{y}.png", {
26+
maxZoom: 18,
27+
attribution: '<a href="https://maps.gsi.go.jp/development/ichiran.html">GSI pale</a>',
28+
}).addTo(map);
29+
30+
const mask = new TileLayerWithMask(
31+
"https://cyberjapandata.gsi.go.jp/xyz/seamlessphoto/{z}/{x}/{y}.jpg",
32+
{
33+
maxZoom: 18,
34+
attribution:
35+
'<a href="https://maps.gsi.go.jp/development/ichiran.html">GSI seamlessphoto</a>',
36+
maskSize: 800,
37+
}
38+
).addTo(map);
39+
40+
map.on("pointermove", function (e) {
41+
mask.setCenter(e.containerPoint);
42+
});
43+
</script>
44+
</body>
45+
</html>

TileLayerWithMask.js

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
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

Comments
 (0)