根据坐标为某些网格图块着色
Color certain grid tiles based on coordinates
我想根据坐标为某些网格图块着色。
我创建了以下网格:
<!DOCTYPE html>
<html>
<head>
<title>Color Tiles</title>
<meta charset="utf-8" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.1/dist/leaflet.css" />
<style>
body {
padding: 0;
margin: 0;
}
html,
body,
#map {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://unpkg.com/leaflet@1.0.1/dist/leaflet.js"></script>
<!-- Make sure you put this AFtER leaflet.js, when using with leaflet
<script src="https://unpkg.com/leaflet-geosearch@3.0.0/dist/geosearch.umd.js"></script>
-->
<script>
var map = new L.Map('map', { center: [10, 0], zoom: 2 });
let zoomLevel = map.getZoom()
console.log("zoomLevel " + zoomLevel)
// if(zoomLevel === 1) {
var tiles = new L.GridLayer();
tiles.createTile = function (coords) {
var tile = L.DomUtil.create('canvas', 'leaflet-tile');
var ctx = tile.getContext('2d');
var size = this.getTileSize()
// console.log("size: " + size.toString())
tile.width = size.x
tile.height = size.y
// calculate projection coordinates of top left tile pixel
var nwPoint = coords.scaleBy(size)
// calculate geographic coordinates of top left tile pixel
var nw = map.unproject(nwPoint, coords.z)
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, size.x, 50);
ctx.fillStyle = 'black';
ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(size.x - 1, 0);
ctx.lineTo(size.x - 1, size.y - 1);
ctx.lineTo(0, size.y - 1);
ctx.closePath();
ctx.stroke();
return tile;
}
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © <a href="http://www.osm.org">OpenStreetMap</a>',
}).addTo(map)
tiles.addTo(map)
</script>
</body>
</html>
我想要以下内容:
我尝试直接通过 css 设置样式,这似乎有效。但是,我想根据提供的坐标设置样式。
有什么建议吗?
我正在考虑这样的事情:
已添加:
if (nw.lat > 10) {
ctx.fillStyle = "rgba(0, 0, 0, 0.4)";
ctx.fillRect(0, 50, size.x, size.y - 50);
}
您可以使用任何允许您本地化图块的条件。
<!DOCTYPE html>
<html>
<head>
<title>Color Tiles</title>
<meta charset="utf-8" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.1/dist/leaflet.css" />
<style>
body {
padding: 0;
margin: 0;
}
html,
body,
#map {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://unpkg.com/leaflet@1.0.1/dist/leaflet.js"></script>
<!-- Make sure you put this AFtER leaflet.js, when using with leaflet
<script src="https://unpkg.com/leaflet-geosearch@3.0.0/dist/geosearch.umd.js"></script>
-->
<script>
var map = new L.Map('map', { center: [10, 0], zoom: 2 });
let zoomLevel = map.getZoom()
console.log("zoomLevel " + zoomLevel)
// if(zoomLevel === 1) {
var tiles = new L.GridLayer();
tiles.createTile = function (coords) {
var tile = L.DomUtil.create('canvas', 'leaflet-tile');
var ctx = tile.getContext('2d');
var size = this.getTileSize()
// console.log("size: " + size.toString())
tile.width = size.x
tile.height = size.y
// calculate projection coordinates of top left tile pixel
var nwPoint = coords.scaleBy(size)
// calculate geographic coordinates of top left tile pixel
var nw = map.unproject(nwPoint, coords.z)
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, size.x, 50);
if (nw.lat > 10) {
ctx.fillStyle = "rgba(0, 0, 0, 0.4)";
ctx.fillRect(0, 50, size.x, size.y - 50);
}
ctx.fillStyle = 'black';
ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(size.x - 1, 0);
ctx.lineTo(size.x - 1, size.y - 1);
ctx.lineTo(0, size.y - 1);
ctx.closePath();
ctx.stroke();
return tile;
}
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © <a href="http://www.osm.org">OpenStreetMap</a>',
}).addTo(map)
tiles.addTo(map)
</script>
</body>
</html>
什么类型的坐标?
看看你关于在 turn-based 游戏中使用地图的其他问题,可能对“坐标”的含义有些歧义。您可能指的是 geographic 坐标(即纬度和经度),代表地图或地球上的 point。另一方面,您可能指的是 tile 的坐标(例如,第 4 行,第 5 列,某个 tileset 的缩放级别 3),它代表地图或地球上的一个区域.我将讨论这两种情况的方法。
地理坐标
Leaflet 内置了检测是否特定 LatLng
coordinates are within bounds, by using the LatLngBounds
.contains
method.
的方法
首先,您可以定义一个关键坐标数组,然后从中创建传单LatLng
objects:
var keyCoordinates = [
[ -5.9, -123.9 ],
[ -12, -28.5 ]
];
var leafletKeyCoordinates = keyCoordinates.map(function (c) {
return new L.LatLng(c[0], c[1]);
});
要确定特定图块的边界,您可以计算图块的西南角。来自这部分:
// calculate projection coordinates of top left tile pixel
var nwPoint = coords.scaleBy(size)
// calculate geographic coordinates of top left tile pixel
var nw = map.unproject(nwPoint, coords.z);
添加:
// calculate projection coordinates of bottom right tile pixel
var sePoint = nwPoint.add(size);
// calculate geographic coordinates of bottom right tile pixel
var se = map.unproject(sePoint, coords.z);
var bounds = new L.LatLngBounds(nw, se);
从那里,您可以使用每个图块的边界来确定是否应该对其进行着色。您可以通过在 canvas 上绘图或将 class 应用于图块来完成此操作。对于使用 canvas 的示例,在 createTile
的底部添加:
// if it contains any coordinates, it should be filled
var shouldShade = leafletKeyCoordinates.some(function (a) {
return bounds.contains(a);
});
if (shouldShade) {
ctx.fillStyle = 'rgba(0, 0, 240, 0.3)';
ctx.fillRect(0, 0, size.x, size.y);
}
一起:
<!DOCTYPE html>
<html>
<head>
<title>Color Tiles</title>
<meta charset="utf-8" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.1/dist/leaflet.css" />
<style>
body {
padding: 0;
margin: 0;
}
html,
body,
#map {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://unpkg.com/leaflet@1.0.1/dist/leaflet.js"></script>
<!-- Make sure you put this AFtER leaflet.js, when using with leaflet
<script src="https://unpkg.com/leaflet-geosearch@3.0.0/dist/geosearch.umd.js"></script>
-->
<script>
var map = new L.Map('map', { center: [10, 0], zoom: 2 });
let zoomLevel = map.getZoom()
console.log("zoomLevel " + zoomLevel)
var keyCoordinates = [
[ -5.9, -123.9 ],
[ -12, -28.5 ]
];
var leafletKeyCoordinates = keyCoordinates.map(function (c) {
return new L.LatLng(c[0], c[1]);
});
// if(zoomLevel === 1) {
var tiles = new L.GridLayer();
tiles.createTile = function (coords) {
var tile = L.DomUtil.create('canvas', 'leaflet-tile');
var ctx = tile.getContext('2d');
var size = this.getTileSize()
// console.log("size: " + size.toString())
tile.width = size.x
tile.height = size.y
// calculate projection coordinates of top left tile pixel
var nwPoint = coords.scaleBy(size)
// calculate geographic coordinates of top left tile pixel
var nw = map.unproject(nwPoint, coords.z)
// calculate projection coordinates of bottom right tile pixel
var sePoint = nwPoint.add(size);
// calculate geographic coordinates of bottom right tile pixel
var se = map.unproject(sePoint, coords.z);
var bounds = new L.LatLngBounds(nw, se);
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, size.x, 50);
ctx.fillStyle = 'black';
ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(size.x - 1, 0);
ctx.lineTo(size.x - 1, size.y - 1);
ctx.lineTo(0, size.y - 1);
ctx.closePath();
ctx.stroke();
// if it contains any coordinates, it should be filled
var shouldShade = leafletKeyCoordinates.some(function (a) {
return bounds.contains(a);
});
if (shouldShade) {
ctx.fillStyle = 'rgba(0, 0, 240, 0.3)';
ctx.fillRect(0, 0, size.x, size.y);
}
return tile;
}
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © <a href="http://www.osm.org">OpenStreetMap</a>',
}).addTo(map)
tiles.addTo(map)
</script>
</body>
</html>
平铺坐标
tile的话,列和行直接看coords.x
和coords.y
,缩放级别直接看coords.z
。棘手的部分是,如果有不同级别的缩放,您的用户可能处于与您识别的图块不同的缩放级别。
var tileCoordinates = [
[0, 3, 3], // x, y, and zoom of tile
[28, 6, 5]
];
要在缩放级别之间进行转换,每个缩放级别都会将图块坐标加倍(例如,缩放级别 4 的图块 (0, 2) 由图块 (0, 4)、(0, 5) 表示, (1, 4), (1, 5) 在缩放级别 5))。我们可以使用 Math.pow(2, targetZoom - coords.z)
或 Math.pow(2, coords.z - targetZoom)
来确定要乘以的因子。
var { x, y, z } = coords;
var shouldShade = tileCoordinates.some(function (c) {
let [ tx, ty, tz] = c, multiple, sx, sy;
if (z < tz) {
// map is zoomed out from the target
// you may just want to `return false;` here
multiple = Math.pow(2, tz - z);
sx = Math.floor(tx / multiple);
sy = Math.floor(ty / multiple);
return sx === x && sy === y;
} else if (z > tz) {
// map is zoomed in from the target
multiple = Math.pow(2, z - tz);
sx = Math.floor(x / multiple);
sy = Math.floor(y / multiple);
return sx === tx && sy === ty;
}
return tx === x && ty === y;
});
一起:
<!DOCTYPE html>
<html>
<head>
<title>Color Tiles</title>
<meta charset="utf-8" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.1/dist/leaflet.css" />
<style>
body {
padding: 0;
margin: 0;
}
html,
body,
#map {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://unpkg.com/leaflet@1.0.1/dist/leaflet.js"></script>
<!-- Make sure you put this AFtER leaflet.js, when using with leaflet
<script src="https://unpkg.com/leaflet-geosearch@3.0.0/dist/geosearch.umd.js"></script>
-->
<script>
var map = new L.Map('map', { center: [10, 0], zoom: 2 });
let zoomLevel = map.getZoom()
console.log("zoomLevel " + zoomLevel)
var tileCoordinates = [
[0, 3, 3],
[28, 6, 5]
];
// if(zoomLevel === 1) {
var tiles = new L.GridLayer();
tiles.createTile = function (coords) {
var tile = L.DomUtil.create('canvas', 'leaflet-tile');
var ctx = tile.getContext('2d');
var size = this.getTileSize()
// console.log("size: " + size.toString())
tile.width = size.x
tile.height = size.y
// calculate projection coordinates of top left tile pixel
var nwPoint = coords.scaleBy(size)
// calculate geographic coordinates of top left tile pixel
var nw = map.unproject(nwPoint, coords.z)
// calculate projection coordinates of bottom right tile pixel
var sePoint = nwPoint.add(size);
// calculate geographic coordinates of bottom right tile pixel
var se = map.unproject(sePoint, coords.z);
var bounds = new L.LatLngBounds(nw, se);
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, size.x, 50);
ctx.fillStyle = 'black';
ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(size.x - 1, 0);
ctx.lineTo(size.x - 1, size.y - 1);
ctx.lineTo(0, size.y - 1);
ctx.closePath();
ctx.stroke();
var { x, y, z } = coords;
var shouldShade = tileCoordinates.some(function (c) {
let [ tx, ty, tz] = c, multiple, sx, sy;
if (z < tz) {
// map is zoomed out from the target
// you may just want to `return false;` here
multiple = Math.pow(2, tz - z);
sx = Math.floor(tx / multiple);
sy = Math.floor(ty / multiple);
return sx === x && sy === y;
} else if (z > tz) {
// map is zoomed in from the target
multiple = Math.pow(2, z - tz);
sx = Math.floor(x / multiple);
sy = Math.floor(y / multiple);
return sx === tx && sy === ty;
}
return tx === x && ty === y;
});
if (shouldShade) {
ctx.fillStyle = 'rgba(0, 0, 240, 0.3)';
ctx.fillRect(0, 0, size.x, size.y);
}
return tile;
}
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © <a href="http://www.osm.org">OpenStreetMap</a>',
}).addTo(map)
tiles.addTo(map)
</script>
</body>
</html>
您可以使用 Leaflet 的内部 _tileCoordsToBounds
函数获取图块的边界并检查 latlng 是否在其中。
我扩展了 L.GridLayer
因为它更干净。重要的功能是_isLatLngInCoords
:
L.ColoredTiles = L.GridLayer.extend({
colorLatLngs: [],
initialize (latlngs, options) {
L.Util.setOptions(this, options);
if(latlngs && L.Util.isArray(latlngs) && latlngs.length > 0) {
this.colorLatLngs = latlngs;
}
if(this.options.debug) {
this._showDebugLatLngs();
}
},
createTile (coords) {
var tile = L.DomUtil.create('canvas', 'leaflet-tile');
var ctx = tile.getContext('2d');
var size = this.getTileSize()
// console.log("size: " + size.toString())
tile.width = size.x
tile.height = size.y
// calculate projection coordinates of top left tile pixel
var nwPoint = coords.scaleBy(size)
// calculate geographic coordinates of top left tile pixel
var nw = map.unproject(nwPoint, coords.z)
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, size.x, 50);
ctx.fillStyle = 'black';
ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(size.x - 1, 0);
ctx.lineTo(size.x - 1, size.y - 1);
ctx.lineTo(0, size.y - 1);
ctx.closePath();
ctx.stroke();
if (this._isLatLngInCoords(coords)) {
ctx.fillStyle = 'rgba(0, 0, 240, 0.3)';
ctx.fillRect(0, 0, size.x, size.y);
}
return tile;
},
_isLatLngInCoords(coords) {
var tileBounds = this._tileCoordsToBounds(coords);
return this.colorLatLngs && this.colorLatLngs.some(function (a) {
return tileBounds.contains(a);
});
},
setColorLatLngs(latlngs){
this.colorLatLngs = latlngs;
this.redraw();
if(this.options.debug) {
this._showDebugLatLngs();
}
},
_showDebugLatLngs(){
this.fg = this.fg || L.featureGroup().addTo(map);
this.fg.clearLayers();
this.colorLatLngs && this.colorLatLngs.forEach((latlng)=>{
L.marker(latlng).addTo(this.fg);
})
}
});
然后将其添加到地图中:
var colorLatLngs = [
L.latLng(0,0),
L.latLng(2,2),
];
var tiles = new L.ColoredTiles(colorLatLngs).addTo(map)
我还添加了函数 setColorLatLngs(array)
来更改经纬度并更新彩色图块。
tiles.setColorLatLngs([L.latLng(3,3)]);
并且出于调试目的,我添加了选项 debug: true
以在每个 latlng 上设置标记。因此可以清楚地看到哪些瓷砖应该被着色。
var tiles = new L.ColoredTiles(colorLatLngs, {debug: true}).addTo(map)
演示:
<!DOCTYPE html>
<html>
<head>
<title>Color Tiles</title>
<meta charset="utf-8" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.1/dist/leaflet.css" />
<style>
body {
padding: 0;
margin: 0;
}
html,
body,
#map {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://unpkg.com/leaflet@1.0.1/dist/leaflet.js"></script>
<!-- Make sure you put this AFtER leaflet.js, when using with leaflet
<script src="https://unpkg.com/leaflet-geosearch@3.0.0/dist/geosearch.umd.js"></script>
-->
<script>
var map = new L.Map('map', { center: [10, 0], zoom: 2 });
let zoomLevel = map.getZoom()
console.log("zoomLevel " + zoomLevel)
L.ColoredTiles = L.GridLayer.extend({
colorLatLngs: [],
initialize (latlngs, options) {
L.Util.setOptions(this, options);
if(latlngs && L.Util.isArray(latlngs) && latlngs.length > 0) {
this.colorLatLngs = latlngs;
}
if(this.options.debug) {
this._showDebugLatLngs();
}
},
createTile (coords) {
var tile = L.DomUtil.create('canvas', 'leaflet-tile');
var ctx = tile.getContext('2d');
var size = this.getTileSize()
// console.log("size: " + size.toString())
tile.width = size.x
tile.height = size.y
// calculate projection coordinates of top left tile pixel
var nwPoint = coords.scaleBy(size)
// calculate geographic coordinates of top left tile pixel
var nw = map.unproject(nwPoint, coords.z)
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, size.x, 50);
ctx.fillStyle = 'black';
ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(size.x - 1, 0);
ctx.lineTo(size.x - 1, size.y - 1);
ctx.lineTo(0, size.y - 1);
ctx.closePath();
ctx.stroke();
if (this._isLatLngInCoords(coords)) {
ctx.fillStyle = 'rgba(0, 0, 240, 0.3)';
ctx.fillRect(0, 0, size.x, size.y);
}
return tile;
},
_isLatLngInCoords(coords) {
var tileBounds = this._tileCoordsToBounds(coords);
return this.colorLatLngs && this.colorLatLngs.some(function (a) {
return tileBounds.contains(a);
});
},
setColorLatLngs(latlngs){
this.colorLatLngs = latlngs;
this.redraw();
if(this.options.debug) {
this._showDebugLatLngs();
}
},
_showDebugLatLngs(){
this.fg = this.fg || L.featureGroup().addTo(map);
this.fg.clearLayers();
this.colorLatLngs && this.colorLatLngs.forEach((latlng)=>{
L.marker(latlng).addTo(this.fg);
})
}
});
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © <a href="http://www.osm.org">OpenStreetMap</a>',
}).addTo(map)
var colorLatLngs = [
L.latLng(0,0),
L.latLng(2,2),
];
var tiles = new L.ColoredTiles(colorLatLngs, {debug: true}).addTo(map)
</script>
</body>
</html>
此代码段同时解决了 related question。它根据 TileNumber
个对象的数组决定为哪些图块着色:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>GridLayer Test</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.1/dist/leaflet.css" />
<style>
body {
padding: 0;
margin: 0;
}
html,
body,
#map {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://unpkg.com/leaflet@1.0.1/dist/leaflet.js"></script>
<script>
const numTilesX = 2 ** 17
const numTilesY = 2 ** 17
class TileNumber {
constructor(x, y) {
this.x = x;
this.y = y;
}
equals(other) {
return this.x === other.x && this.y === other.y;
}
}
let coloredTiles = [
new TileNumber(70435, 45249),
new TileNumber(70434, 45248),
new TileNumber(70441, 45245)
]
function latLngToTileNumber(latLng) {
const lngDegrees = latLng.lng;
const latRadians = latLng.lat * (Math.PI/180);
return new L.Point(
numTilesX * ((lngDegrees + 180) / 360),
numTilesY * (1 - Math.log(Math.tan(latRadians) + 1 / Math.cos(latRadians)) / Math.PI) / 2
);
}
const map = new L.Map('map', {center: [48.5748229, 13.4609744], zoom: 16, maxZoom: 19});
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © <a href="https://www.osm.org">OpenStreetMap</a>', maxZoom: 19
}).addTo(map)
const tiles = new L.GridLayer({minZoom: 12});
tiles.createTile = function (coords) {
const tile = L.DomUtil.create('canvas', 'leaflet-tile');
const ctx = tile.getContext('2d');
const size = this.getTileSize();
tile.width = size.x
tile.height = size.y
// calculate projection coordinates of top left tile pixel
const nwPoint = coords.scaleBy(size);
// calculate geographic coordinates of top left tile pixel
const nw = map.unproject(nwPoint, coords.z);
// calculate fraction tile number at top left point
const nwTile = latLngToTileNumber(nw, Math.floor)
// calculate projection coordinates of bottom right tile pixel
const sePoint = new L.Point(nwPoint.x + size.x - 1, nwPoint.y + size.y - 1)
// calculate geographic coordinates of bottom right tile pixel
const se = map.unproject(sePoint, coords.z);
// calculate fractional tile number at bottom right point
const seTile = latLngToTileNumber(se, Math.ceil)
const minTileX = nwTile.x
const maxTileX = seTile.x
const minTileY = nwTile.y
const maxTileY = seTile.y
for (let x = Math.ceil(minTileX) - 1; x <= Math.floor(maxTileX) + 1; x++) {
for (let y = Math.ceil(minTileY) - 1; y <= Math.floor(maxTileY) + 1; y++) {
let tile = new TileNumber(x, y)
const xMinPixel = Math.round(size.x * (x - minTileX) / (maxTileX - minTileX));
const xMaxPixel = Math.round(size.x * (x + 1 - minTileX) / (maxTileX - minTileX));
const yMinPixel = Math.round(size.y * (y - minTileY) / (maxTileY - minTileY));
const yMaxPixel = Math.round(size.y * (y + 1 - minTileY) / (maxTileY - minTileY));
// fill the rectangle with a color
ctx.fillStyle = coloredTiles.some(t => t.equals(tile))
? 'rgba(0, 0, 255, 0.3)'
: 'rgba(255, 255, 255, 0)';
ctx.fillRect(xMinPixel, yMinPixel, xMaxPixel - xMinPixel, yMaxPixel - yMinPixel);
if (coords.z >= 16) {
// draw the white rectangle and text at the top of the cell
ctx.fillStyle = 'white';
ctx.fillRect(xMinPixel, yMinPixel, xMaxPixel - xMinPixel, 28);
ctx.fillStyle = 'black';
ctx.font = "15px Arial"
ctx.fillText(tile.x + "," + tile.y, xMinPixel + 10, yMinPixel + 20, xMaxPixel - xMinPixel);
}
if (coords.z >= 13) {
// draw a border
ctx.strokeStyle = 'black';
ctx.strokeRect(xMinPixel, yMinPixel, xMaxPixel - xMinPixel, yMaxPixel - yMinPixel);
}
}
}
return tile;
}
tiles.addTo(map);
map.on('click', e => {
const fractionalTileNumber = latLngToTileNumber(e.latlng);
const tileNumber = new TileNumber(Math.floor(fractionalTileNumber.x), Math.floor(fractionalTileNumber.y));
console.log("Tile " + tileNumber.x + " " + tileNumber.y + " clicked");
if (coloredTiles.some(t => t.equals(tileNumber))) {
coloredTiles = coloredTiles.filter(t => !t.equals(tileNumber));
} else {
coloredTiles.push(tileNumber);
}
tiles.redraw();
});
</script>
</body>
</html>
我想根据坐标为某些网格图块着色。
我创建了以下网格:
<!DOCTYPE html>
<html>
<head>
<title>Color Tiles</title>
<meta charset="utf-8" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.1/dist/leaflet.css" />
<style>
body {
padding: 0;
margin: 0;
}
html,
body,
#map {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://unpkg.com/leaflet@1.0.1/dist/leaflet.js"></script>
<!-- Make sure you put this AFtER leaflet.js, when using with leaflet
<script src="https://unpkg.com/leaflet-geosearch@3.0.0/dist/geosearch.umd.js"></script>
-->
<script>
var map = new L.Map('map', { center: [10, 0], zoom: 2 });
let zoomLevel = map.getZoom()
console.log("zoomLevel " + zoomLevel)
// if(zoomLevel === 1) {
var tiles = new L.GridLayer();
tiles.createTile = function (coords) {
var tile = L.DomUtil.create('canvas', 'leaflet-tile');
var ctx = tile.getContext('2d');
var size = this.getTileSize()
// console.log("size: " + size.toString())
tile.width = size.x
tile.height = size.y
// calculate projection coordinates of top left tile pixel
var nwPoint = coords.scaleBy(size)
// calculate geographic coordinates of top left tile pixel
var nw = map.unproject(nwPoint, coords.z)
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, size.x, 50);
ctx.fillStyle = 'black';
ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(size.x - 1, 0);
ctx.lineTo(size.x - 1, size.y - 1);
ctx.lineTo(0, size.y - 1);
ctx.closePath();
ctx.stroke();
return tile;
}
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © <a href="http://www.osm.org">OpenStreetMap</a>',
}).addTo(map)
tiles.addTo(map)
</script>
</body>
</html>
我想要以下内容:
我尝试直接通过 css 设置样式,这似乎有效。但是,我想根据提供的坐标设置样式。
有什么建议吗?
我正在考虑这样的事情:
已添加:
if (nw.lat > 10) {
ctx.fillStyle = "rgba(0, 0, 0, 0.4)";
ctx.fillRect(0, 50, size.x, size.y - 50);
}
您可以使用任何允许您本地化图块的条件。
<!DOCTYPE html>
<html>
<head>
<title>Color Tiles</title>
<meta charset="utf-8" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.1/dist/leaflet.css" />
<style>
body {
padding: 0;
margin: 0;
}
html,
body,
#map {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://unpkg.com/leaflet@1.0.1/dist/leaflet.js"></script>
<!-- Make sure you put this AFtER leaflet.js, when using with leaflet
<script src="https://unpkg.com/leaflet-geosearch@3.0.0/dist/geosearch.umd.js"></script>
-->
<script>
var map = new L.Map('map', { center: [10, 0], zoom: 2 });
let zoomLevel = map.getZoom()
console.log("zoomLevel " + zoomLevel)
// if(zoomLevel === 1) {
var tiles = new L.GridLayer();
tiles.createTile = function (coords) {
var tile = L.DomUtil.create('canvas', 'leaflet-tile');
var ctx = tile.getContext('2d');
var size = this.getTileSize()
// console.log("size: " + size.toString())
tile.width = size.x
tile.height = size.y
// calculate projection coordinates of top left tile pixel
var nwPoint = coords.scaleBy(size)
// calculate geographic coordinates of top left tile pixel
var nw = map.unproject(nwPoint, coords.z)
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, size.x, 50);
if (nw.lat > 10) {
ctx.fillStyle = "rgba(0, 0, 0, 0.4)";
ctx.fillRect(0, 50, size.x, size.y - 50);
}
ctx.fillStyle = 'black';
ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(size.x - 1, 0);
ctx.lineTo(size.x - 1, size.y - 1);
ctx.lineTo(0, size.y - 1);
ctx.closePath();
ctx.stroke();
return tile;
}
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © <a href="http://www.osm.org">OpenStreetMap</a>',
}).addTo(map)
tiles.addTo(map)
</script>
</body>
</html>
什么类型的坐标?
看看你关于在 turn-based 游戏中使用地图的其他问题,可能对“坐标”的含义有些歧义。您可能指的是 geographic 坐标(即纬度和经度),代表地图或地球上的 point。另一方面,您可能指的是 tile 的坐标(例如,第 4 行,第 5 列,某个 tileset 的缩放级别 3),它代表地图或地球上的一个区域.我将讨论这两种情况的方法。
地理坐标
Leaflet 内置了检测是否特定 LatLng
coordinates are within bounds, by using the LatLngBounds
.contains
method.
首先,您可以定义一个关键坐标数组,然后从中创建传单LatLng
objects:
var keyCoordinates = [
[ -5.9, -123.9 ],
[ -12, -28.5 ]
];
var leafletKeyCoordinates = keyCoordinates.map(function (c) {
return new L.LatLng(c[0], c[1]);
});
要确定特定图块的边界,您可以计算图块的西南角。来自这部分:
// calculate projection coordinates of top left tile pixel
var nwPoint = coords.scaleBy(size)
// calculate geographic coordinates of top left tile pixel
var nw = map.unproject(nwPoint, coords.z);
添加:
// calculate projection coordinates of bottom right tile pixel
var sePoint = nwPoint.add(size);
// calculate geographic coordinates of bottom right tile pixel
var se = map.unproject(sePoint, coords.z);
var bounds = new L.LatLngBounds(nw, se);
从那里,您可以使用每个图块的边界来确定是否应该对其进行着色。您可以通过在 canvas 上绘图或将 class 应用于图块来完成此操作。对于使用 canvas 的示例,在 createTile
的底部添加:
// if it contains any coordinates, it should be filled
var shouldShade = leafletKeyCoordinates.some(function (a) {
return bounds.contains(a);
});
if (shouldShade) {
ctx.fillStyle = 'rgba(0, 0, 240, 0.3)';
ctx.fillRect(0, 0, size.x, size.y);
}
一起:
<!DOCTYPE html>
<html>
<head>
<title>Color Tiles</title>
<meta charset="utf-8" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.1/dist/leaflet.css" />
<style>
body {
padding: 0;
margin: 0;
}
html,
body,
#map {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://unpkg.com/leaflet@1.0.1/dist/leaflet.js"></script>
<!-- Make sure you put this AFtER leaflet.js, when using with leaflet
<script src="https://unpkg.com/leaflet-geosearch@3.0.0/dist/geosearch.umd.js"></script>
-->
<script>
var map = new L.Map('map', { center: [10, 0], zoom: 2 });
let zoomLevel = map.getZoom()
console.log("zoomLevel " + zoomLevel)
var keyCoordinates = [
[ -5.9, -123.9 ],
[ -12, -28.5 ]
];
var leafletKeyCoordinates = keyCoordinates.map(function (c) {
return new L.LatLng(c[0], c[1]);
});
// if(zoomLevel === 1) {
var tiles = new L.GridLayer();
tiles.createTile = function (coords) {
var tile = L.DomUtil.create('canvas', 'leaflet-tile');
var ctx = tile.getContext('2d');
var size = this.getTileSize()
// console.log("size: " + size.toString())
tile.width = size.x
tile.height = size.y
// calculate projection coordinates of top left tile pixel
var nwPoint = coords.scaleBy(size)
// calculate geographic coordinates of top left tile pixel
var nw = map.unproject(nwPoint, coords.z)
// calculate projection coordinates of bottom right tile pixel
var sePoint = nwPoint.add(size);
// calculate geographic coordinates of bottom right tile pixel
var se = map.unproject(sePoint, coords.z);
var bounds = new L.LatLngBounds(nw, se);
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, size.x, 50);
ctx.fillStyle = 'black';
ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(size.x - 1, 0);
ctx.lineTo(size.x - 1, size.y - 1);
ctx.lineTo(0, size.y - 1);
ctx.closePath();
ctx.stroke();
// if it contains any coordinates, it should be filled
var shouldShade = leafletKeyCoordinates.some(function (a) {
return bounds.contains(a);
});
if (shouldShade) {
ctx.fillStyle = 'rgba(0, 0, 240, 0.3)';
ctx.fillRect(0, 0, size.x, size.y);
}
return tile;
}
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © <a href="http://www.osm.org">OpenStreetMap</a>',
}).addTo(map)
tiles.addTo(map)
</script>
</body>
</html>
平铺坐标
tile的话,列和行直接看coords.x
和coords.y
,缩放级别直接看coords.z
。棘手的部分是,如果有不同级别的缩放,您的用户可能处于与您识别的图块不同的缩放级别。
var tileCoordinates = [
[0, 3, 3], // x, y, and zoom of tile
[28, 6, 5]
];
要在缩放级别之间进行转换,每个缩放级别都会将图块坐标加倍(例如,缩放级别 4 的图块 (0, 2) 由图块 (0, 4)、(0, 5) 表示, (1, 4), (1, 5) 在缩放级别 5))。我们可以使用 Math.pow(2, targetZoom - coords.z)
或 Math.pow(2, coords.z - targetZoom)
来确定要乘以的因子。
var { x, y, z } = coords;
var shouldShade = tileCoordinates.some(function (c) {
let [ tx, ty, tz] = c, multiple, sx, sy;
if (z < tz) {
// map is zoomed out from the target
// you may just want to `return false;` here
multiple = Math.pow(2, tz - z);
sx = Math.floor(tx / multiple);
sy = Math.floor(ty / multiple);
return sx === x && sy === y;
} else if (z > tz) {
// map is zoomed in from the target
multiple = Math.pow(2, z - tz);
sx = Math.floor(x / multiple);
sy = Math.floor(y / multiple);
return sx === tx && sy === ty;
}
return tx === x && ty === y;
});
一起:
<!DOCTYPE html>
<html>
<head>
<title>Color Tiles</title>
<meta charset="utf-8" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.1/dist/leaflet.css" />
<style>
body {
padding: 0;
margin: 0;
}
html,
body,
#map {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://unpkg.com/leaflet@1.0.1/dist/leaflet.js"></script>
<!-- Make sure you put this AFtER leaflet.js, when using with leaflet
<script src="https://unpkg.com/leaflet-geosearch@3.0.0/dist/geosearch.umd.js"></script>
-->
<script>
var map = new L.Map('map', { center: [10, 0], zoom: 2 });
let zoomLevel = map.getZoom()
console.log("zoomLevel " + zoomLevel)
var tileCoordinates = [
[0, 3, 3],
[28, 6, 5]
];
// if(zoomLevel === 1) {
var tiles = new L.GridLayer();
tiles.createTile = function (coords) {
var tile = L.DomUtil.create('canvas', 'leaflet-tile');
var ctx = tile.getContext('2d');
var size = this.getTileSize()
// console.log("size: " + size.toString())
tile.width = size.x
tile.height = size.y
// calculate projection coordinates of top left tile pixel
var nwPoint = coords.scaleBy(size)
// calculate geographic coordinates of top left tile pixel
var nw = map.unproject(nwPoint, coords.z)
// calculate projection coordinates of bottom right tile pixel
var sePoint = nwPoint.add(size);
// calculate geographic coordinates of bottom right tile pixel
var se = map.unproject(sePoint, coords.z);
var bounds = new L.LatLngBounds(nw, se);
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, size.x, 50);
ctx.fillStyle = 'black';
ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(size.x - 1, 0);
ctx.lineTo(size.x - 1, size.y - 1);
ctx.lineTo(0, size.y - 1);
ctx.closePath();
ctx.stroke();
var { x, y, z } = coords;
var shouldShade = tileCoordinates.some(function (c) {
let [ tx, ty, tz] = c, multiple, sx, sy;
if (z < tz) {
// map is zoomed out from the target
// you may just want to `return false;` here
multiple = Math.pow(2, tz - z);
sx = Math.floor(tx / multiple);
sy = Math.floor(ty / multiple);
return sx === x && sy === y;
} else if (z > tz) {
// map is zoomed in from the target
multiple = Math.pow(2, z - tz);
sx = Math.floor(x / multiple);
sy = Math.floor(y / multiple);
return sx === tx && sy === ty;
}
return tx === x && ty === y;
});
if (shouldShade) {
ctx.fillStyle = 'rgba(0, 0, 240, 0.3)';
ctx.fillRect(0, 0, size.x, size.y);
}
return tile;
}
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © <a href="http://www.osm.org">OpenStreetMap</a>',
}).addTo(map)
tiles.addTo(map)
</script>
</body>
</html>
您可以使用 Leaflet 的内部 _tileCoordsToBounds
函数获取图块的边界并检查 latlng 是否在其中。
我扩展了 L.GridLayer
因为它更干净。重要的功能是_isLatLngInCoords
:
L.ColoredTiles = L.GridLayer.extend({
colorLatLngs: [],
initialize (latlngs, options) {
L.Util.setOptions(this, options);
if(latlngs && L.Util.isArray(latlngs) && latlngs.length > 0) {
this.colorLatLngs = latlngs;
}
if(this.options.debug) {
this._showDebugLatLngs();
}
},
createTile (coords) {
var tile = L.DomUtil.create('canvas', 'leaflet-tile');
var ctx = tile.getContext('2d');
var size = this.getTileSize()
// console.log("size: " + size.toString())
tile.width = size.x
tile.height = size.y
// calculate projection coordinates of top left tile pixel
var nwPoint = coords.scaleBy(size)
// calculate geographic coordinates of top left tile pixel
var nw = map.unproject(nwPoint, coords.z)
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, size.x, 50);
ctx.fillStyle = 'black';
ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(size.x - 1, 0);
ctx.lineTo(size.x - 1, size.y - 1);
ctx.lineTo(0, size.y - 1);
ctx.closePath();
ctx.stroke();
if (this._isLatLngInCoords(coords)) {
ctx.fillStyle = 'rgba(0, 0, 240, 0.3)';
ctx.fillRect(0, 0, size.x, size.y);
}
return tile;
},
_isLatLngInCoords(coords) {
var tileBounds = this._tileCoordsToBounds(coords);
return this.colorLatLngs && this.colorLatLngs.some(function (a) {
return tileBounds.contains(a);
});
},
setColorLatLngs(latlngs){
this.colorLatLngs = latlngs;
this.redraw();
if(this.options.debug) {
this._showDebugLatLngs();
}
},
_showDebugLatLngs(){
this.fg = this.fg || L.featureGroup().addTo(map);
this.fg.clearLayers();
this.colorLatLngs && this.colorLatLngs.forEach((latlng)=>{
L.marker(latlng).addTo(this.fg);
})
}
});
然后将其添加到地图中:
var colorLatLngs = [
L.latLng(0,0),
L.latLng(2,2),
];
var tiles = new L.ColoredTiles(colorLatLngs).addTo(map)
我还添加了函数 setColorLatLngs(array)
来更改经纬度并更新彩色图块。
tiles.setColorLatLngs([L.latLng(3,3)]);
并且出于调试目的,我添加了选项 debug: true
以在每个 latlng 上设置标记。因此可以清楚地看到哪些瓷砖应该被着色。
var tiles = new L.ColoredTiles(colorLatLngs, {debug: true}).addTo(map)
演示:
<!DOCTYPE html>
<html>
<head>
<title>Color Tiles</title>
<meta charset="utf-8" />
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.1/dist/leaflet.css" />
<style>
body {
padding: 0;
margin: 0;
}
html,
body,
#map {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<script src="https://unpkg.com/leaflet@1.0.1/dist/leaflet.js"></script>
<!-- Make sure you put this AFtER leaflet.js, when using with leaflet
<script src="https://unpkg.com/leaflet-geosearch@3.0.0/dist/geosearch.umd.js"></script>
-->
<script>
var map = new L.Map('map', { center: [10, 0], zoom: 2 });
let zoomLevel = map.getZoom()
console.log("zoomLevel " + zoomLevel)
L.ColoredTiles = L.GridLayer.extend({
colorLatLngs: [],
initialize (latlngs, options) {
L.Util.setOptions(this, options);
if(latlngs && L.Util.isArray(latlngs) && latlngs.length > 0) {
this.colorLatLngs = latlngs;
}
if(this.options.debug) {
this._showDebugLatLngs();
}
},
createTile (coords) {
var tile = L.DomUtil.create('canvas', 'leaflet-tile');
var ctx = tile.getContext('2d');
var size = this.getTileSize()
// console.log("size: " + size.toString())
tile.width = size.x
tile.height = size.y
// calculate projection coordinates of top left tile pixel
var nwPoint = coords.scaleBy(size)
// calculate geographic coordinates of top left tile pixel
var nw = map.unproject(nwPoint, coords.z)
ctx.fillStyle = 'white';
ctx.fillRect(0, 0, size.x, 50);
ctx.fillStyle = 'black';
ctx.fillText('x: ' + coords.x + ', y: ' + coords.y + ', zoom: ' + coords.z, 20, 20);
ctx.fillText('lat: ' + nw.lat + ', lon: ' + nw.lng, 20, 40);
ctx.strokeStyle = 'black';
ctx.beginPath();
ctx.moveTo(0, 0);
ctx.lineTo(size.x - 1, 0);
ctx.lineTo(size.x - 1, size.y - 1);
ctx.lineTo(0, size.y - 1);
ctx.closePath();
ctx.stroke();
if (this._isLatLngInCoords(coords)) {
ctx.fillStyle = 'rgba(0, 0, 240, 0.3)';
ctx.fillRect(0, 0, size.x, size.y);
}
return tile;
},
_isLatLngInCoords(coords) {
var tileBounds = this._tileCoordsToBounds(coords);
return this.colorLatLngs && this.colorLatLngs.some(function (a) {
return tileBounds.contains(a);
});
},
setColorLatLngs(latlngs){
this.colorLatLngs = latlngs;
this.redraw();
if(this.options.debug) {
this._showDebugLatLngs();
}
},
_showDebugLatLngs(){
this.fg = this.fg || L.featureGroup().addTo(map);
this.fg.clearLayers();
this.colorLatLngs && this.colorLatLngs.forEach((latlng)=>{
L.marker(latlng).addTo(this.fg);
})
}
});
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © <a href="http://www.osm.org">OpenStreetMap</a>',
}).addTo(map)
var colorLatLngs = [
L.latLng(0,0),
L.latLng(2,2),
];
var tiles = new L.ColoredTiles(colorLatLngs, {debug: true}).addTo(map)
</script>
</body>
</html>
此代码段同时解决了 related question。它根据 TileNumber
个对象的数组决定为哪些图块着色:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>GridLayer Test</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.1/dist/leaflet.css" />
<style>
body {
padding: 0;
margin: 0;
}
html,
body,
#map {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div id="map"></div>
<script src="https://unpkg.com/leaflet@1.0.1/dist/leaflet.js"></script>
<script>
const numTilesX = 2 ** 17
const numTilesY = 2 ** 17
class TileNumber {
constructor(x, y) {
this.x = x;
this.y = y;
}
equals(other) {
return this.x === other.x && this.y === other.y;
}
}
let coloredTiles = [
new TileNumber(70435, 45249),
new TileNumber(70434, 45248),
new TileNumber(70441, 45245)
]
function latLngToTileNumber(latLng) {
const lngDegrees = latLng.lng;
const latRadians = latLng.lat * (Math.PI/180);
return new L.Point(
numTilesX * ((lngDegrees + 180) / 360),
numTilesY * (1 - Math.log(Math.tan(latRadians) + 1 / Math.cos(latRadians)) / Math.PI) / 2
);
}
const map = new L.Map('map', {center: [48.5748229, 13.4609744], zoom: 16, maxZoom: 19});
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data © <a href="https://www.osm.org">OpenStreetMap</a>', maxZoom: 19
}).addTo(map)
const tiles = new L.GridLayer({minZoom: 12});
tiles.createTile = function (coords) {
const tile = L.DomUtil.create('canvas', 'leaflet-tile');
const ctx = tile.getContext('2d');
const size = this.getTileSize();
tile.width = size.x
tile.height = size.y
// calculate projection coordinates of top left tile pixel
const nwPoint = coords.scaleBy(size);
// calculate geographic coordinates of top left tile pixel
const nw = map.unproject(nwPoint, coords.z);
// calculate fraction tile number at top left point
const nwTile = latLngToTileNumber(nw, Math.floor)
// calculate projection coordinates of bottom right tile pixel
const sePoint = new L.Point(nwPoint.x + size.x - 1, nwPoint.y + size.y - 1)
// calculate geographic coordinates of bottom right tile pixel
const se = map.unproject(sePoint, coords.z);
// calculate fractional tile number at bottom right point
const seTile = latLngToTileNumber(se, Math.ceil)
const minTileX = nwTile.x
const maxTileX = seTile.x
const minTileY = nwTile.y
const maxTileY = seTile.y
for (let x = Math.ceil(minTileX) - 1; x <= Math.floor(maxTileX) + 1; x++) {
for (let y = Math.ceil(minTileY) - 1; y <= Math.floor(maxTileY) + 1; y++) {
let tile = new TileNumber(x, y)
const xMinPixel = Math.round(size.x * (x - minTileX) / (maxTileX - minTileX));
const xMaxPixel = Math.round(size.x * (x + 1 - minTileX) / (maxTileX - minTileX));
const yMinPixel = Math.round(size.y * (y - minTileY) / (maxTileY - minTileY));
const yMaxPixel = Math.round(size.y * (y + 1 - minTileY) / (maxTileY - minTileY));
// fill the rectangle with a color
ctx.fillStyle = coloredTiles.some(t => t.equals(tile))
? 'rgba(0, 0, 255, 0.3)'
: 'rgba(255, 255, 255, 0)';
ctx.fillRect(xMinPixel, yMinPixel, xMaxPixel - xMinPixel, yMaxPixel - yMinPixel);
if (coords.z >= 16) {
// draw the white rectangle and text at the top of the cell
ctx.fillStyle = 'white';
ctx.fillRect(xMinPixel, yMinPixel, xMaxPixel - xMinPixel, 28);
ctx.fillStyle = 'black';
ctx.font = "15px Arial"
ctx.fillText(tile.x + "," + tile.y, xMinPixel + 10, yMinPixel + 20, xMaxPixel - xMinPixel);
}
if (coords.z >= 13) {
// draw a border
ctx.strokeStyle = 'black';
ctx.strokeRect(xMinPixel, yMinPixel, xMaxPixel - xMinPixel, yMaxPixel - yMinPixel);
}
}
}
return tile;
}
tiles.addTo(map);
map.on('click', e => {
const fractionalTileNumber = latLngToTileNumber(e.latlng);
const tileNumber = new TileNumber(Math.floor(fractionalTileNumber.x), Math.floor(fractionalTileNumber.y));
console.log("Tile " + tileNumber.x + " " + tileNumber.y + " clicked");
if (coloredTiles.some(t => t.equals(tileNumber))) {
coloredTiles = coloredTiles.filter(t => !t.equals(tileNumber));
} else {
coloredTiles.push(tileNumber);
}
tiles.redraw();
});
</script>
</body>
</html>