在最低缩放级别显示固定的100 m x 100 m网格

Show fixed 100 m x 100 m grid on lowest zoom level(在最低缩放级别显示固定的100 m x 100 m网格)
本文介绍了在最低缩放级别显示固定的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 &copy; <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实现绘制网格:

数据-lang="js"数据-隐藏="假"数据-控制台="真"数据-巴贝尔="假">
<!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 &copy; <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的回答。

这篇关于在最低缩放级别显示固定的100 m x 100 m网格的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本站部分内容来源互联网,如果有图片或者内容侵犯您的权益请联系我们删除!

相关文档推荐

Update another component when Formik form changes(当Formik表单更改时更新另一个组件)
Formik validation isSubmitting / isValidating not getting set to true(Formik验证正在提交/isValiating未设置为True)
React Validation Max Range Using Formik(使用Formik的Reaction验证最大范围)
Validation using Yup to check string or number length(使用YUP检查字符串或数字长度的验证)
Updating initialValues prop on Formik Form does not update input value(更新Formik表单上的初始值属性不会更新输入值)
password validation with yup and formik(使用YUP和Formick进行密码验证)