在最低缩放级别显示固定的100 m x 100 m网格
我正在使用Lamlet和OpenStreetMap在由100m x 100m瓷砖组成的世界地图上创建一个固定的网格。基本上,我正在创建一个基于回合的游戏,玩家应该能够点击某个磁贴,然后显示一个上下文菜单。服务器将知道玩家已打开某个位置的磁贴。
我尝试了以下操作:
数据-lang="js"数据-隐藏="假"数据-控制台="真"数据-巴贝尔="假"><!DOCTYPE html>
<html>
<head>
<title>GridLayer Test</title>
<meta charset="utf-8" />
<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>
var map = new L.Map('map', { center: [10, 0], zoom: 2 });
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()
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 = 'red';
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>',
minNativeZoom: 1,
maxNativeZoom: 1,
}).addTo(map)
tiles.addTo(map)
</script>
</body>
</html>
正如您所看到的,当我使用minNativeZoom
放大或缩小时,网格发生了变化。不过,我想把格子修好,宽100米乘100米。
我还尝试仅在zoomLevel = 18
时返回tile
。这不起作用。
有什么建议我做错了吗?
感谢您的回复!
解决方案
您可以使用以下createTile
实现绘制网格:
<!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>
注意事项:
- 因为地球不是平的,所以不可能用矩形网格来完全覆盖它。所以我做了最接近它的事情,沿着纬度和经度线绘制网格边界。因此,瓷砖将朝着赤道变得更大(覆盖更多平方米),向两极变小。
- 每个网格单元格都有一个唯一的
TileNumber
(x和y坐标,从西北角的0,0开始)。 - 为了演示在地图上的单击,我将的
TileNumber
写入日志,并切换瓷砖的彩色/非彩色状态。当然,这可以被任何其他可以想象到的功能所取代,包括与服务器通信。
由于此代码段包括根据网格单元格的TileNumber
(存储在数组中)对其进行着色的功能,因此我也将此作为对question about coloring grid tiles的回答。
相关文章