【问题标题】:leaflet non-geographical map with tilelayer: the map appears to have a vertical offset?带有tilelayer的传单非地理地图:地图似乎有垂直偏移?
【发布时间】:2023-03-17 20:50:02
【问题描述】:

为了维护我为游戏社区挑选的社区项目,我试图了解传单的工作原理。特别是,我正在尝试使用自定义图块服务显示非地理地图。

根据传单文档以及一系列教程和 stackoverflow 答案,我能想到的最好的代码是以下代码。标记被添加到 [0,0] 和 [maxLat, maxLong] 以验证它们是否与地图对齐(我试图在我的项目中解决的问题是它们不是):

<!DOCTYPE HTML>
<html lang="en">

<head>

    <meta charset="utf-8">
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
        integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
        crossorigin=""/>
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
        integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
        crossorigin=""></script>
    <style>
        #map { height: 1080px; width: 1920px; }
    </style>

</head>

<body>

    <div id="map"></div>

    <script>
        var yx = L.latLng;

        var xy = function(x, y) {
        if (L.Util.isArray(x)) {
            return yx(x[1], x[0]);
        }
        return yx(y, x);
        };


        var minZoom = 0;
        var maxZoom = 7;
        var img = [
            18475,
            12791
        ];

        L.CRS.MySimple = L.extend({}, L.CRS.Simple, {
            transformation: new L.Transformation(1 / 128, 0, 1 / 128, 0)
        });

        var bounds = [xy(0, 0), xy(img)];

        var map = L.map("map", {
            attributionControl: false,
            crs: L.CRS.MySimple,
            maxBounds: bounds,
            minZoom: minZoom,
            maxZoom: maxZoom
        });

        L.tileLayer('https://d1w6xpzk1h0aj5.cloudfront.net/kuban/{z}/{x}/{y}.png', {
            bounds: bounds,
            noWrap: true,
            minZoom: minZoom,
            maxZoom: maxZoom,
            tms: true,
            continuousWorld: false,
            maxNativeZoom: 7
        }).addTo(map);

        map.fitBounds(bounds);

        L.marker(L.latLng([ 0, 0 ])).addTo(map);
        L.marker(L.latLng(xy(img))).addTo(map);
    </script>
    
</body>

</html>

如果我在浏览器上打开它,地图会在所有缩放级别上正确显示(即,图块以正确的顺序提供,并且传单正在按预期缝合它们)。问题是地图垂直偏离中心,并且标记虽然水平完全对齐,但有垂直偏移。

这正是我在主项目中也遇到的问题。

我做错了什么,为什么?

【问题讨论】:

  • Hhhmm,图块似乎与游戏世界的左下角对齐,但您的 [0,0] 位于左上角。你确定你有适合这个游戏世界的坐标系吗?
  • 说实话,我什么都不确定。这源于github.com/roccobarbi/il2missionplanner.link,这是我从github.com/gavincabbage/il2missionplanner.com 获得的,因为原始项目被放弃并停止工作。当我尝试将其修补到工作状态时,我正在学习传单。很多设计决策都是从之前的项目中继承下来的。我确定坐标有问题,因为我必须颠倒平铺顺序,否则会显示它们沿 y 轴倒置。
  • 事实上,问题中的代码是尝试从头开始构建一个最小的工作地图,这样我就可以理解什么是坏的以及为什么,我可以尝试设计一个修复程序,使感觉。
  • 请阅读 stackoverflow.com/questions/47381346/…stackoverflow.com/questions/62622737/…stackoverflow.com/questions/62305306/… - 如果可能,请尝试获取游戏中几个地标的坐标,因为这样可以更好地确保瓷砖和CRS 对齐正常。
  • 我反复阅读这些帖子,还有很多其他帖子。它们很有用,但我认为它们不能完全解决我的问题。经过一些更改,我现在发现这个问题实际上只是一个缩放问题。在最大变焦下,我可以非常准确地定位地标。除了它们在任何其他缩放级别上都不能正确映射。

标签: javascript leaflet


【解决方案1】:

这里的问题是您的光栅图像以一种奇怪的方式平铺。

我坚信坐标的原点是左下角,因为 0,0,0 瓦片如何与左下角对齐,在顶部和右侧留下空白:

缩放级别 1 的图块也对齐到左下角:

在缩放级别 2 时,事情变得有趣

现在,如果坐标原点在左下角,则图块坐标原点应该在左下角也是。

事实并非如此。如果它,那么这将是一个 TMS 样式的 tilelayer,其中 tile Y 坐标“向上”,并且 Leaflet 需要具有定义边界的非无限 CRS,如 @987654324 @,一切都会奏效,每个人都会很高兴。

因此,换句话说:第 0 个(垂直)像素位于左下角,但第 0 个(垂直)图块位于左上角。

这种切片切片方式的一个值得注意的结果是最左上角的切片的左上角坐标随缩放级别而变化。

因此,对于缩放级别 0 的图块,最顶部图块的笛卡尔 Y 坐标为 2^15 = 32768,但 z1 为 2^14 = 16384。对于 z2 和 z3,它又是 16384,但对于 z4,它是 7*(2^11) = 14336 ,对于 z5,它是 13*(2^10) = 13312。在一般情况下,它是图像的高度 (12791) 填充到最接近的缩放级别的 2 次方加 7(我相信类似于...Math.pow(2,7+z) * Math.ceil( 12791 / Math.pow(2,7+z) ))。

解决这个问题(我能想到的)最简洁的方法是类似于this other answer,比如

    var map = L.map("map", {

        crs: L.CRS.Simple,
        minZoom: -7,
        maxZoom: 2
    });

    var tiles = L.tileLayer('https://d1w6xpzk1h0aj5.cloudfront.net/kuban/{z}/{x}/{y}.png', {
        minZoom: -7,
        maxNativeZoom: 0,
        zoomOffset: 7
    }).addTo(map);

    tiles.getTileUrl = function(coords) {
        if (coords.z === -7) {coords.y += 1}
        if (coords.z === -6) {coords.y += 1}
        if (coords.z === -5) {coords.y += 2}
        if (coords.z === -4) {coords.y += 4}
        if (coords.z === -3) {coords.y += 7}
        if (coords.z === -2) {coords.y += 13}
        if (coords.z === -1) {coords.y += 25}
        if (coords.z ===  0) {coords.y += 50}
        
        return L.TileLayer.prototype.getTileUrl.bind(tiles)(coords);
    }

我已将其放入 working example, over here


我强烈建议您以更合理的方式将图像分割成图块

如果您的坐标系是笛卡尔坐标系,则切片图块的gravity应该在左下角(即空白区域应该在顶部和右侧),而 0-0任何给定缩放级别的图块应该包含最左下角的像素。

同样,如果您的坐标系类似于屏幕(带有倒 Y 的笛卡尔坐标,请参阅 this answer for an explainer),那么切片瓷砖的重力应该在左上角(即空白空间应该是底部和右侧),并且任何给定缩放级别的 0-0 磁贴应该包含最左上角的像素。

如果不以这种方式切割瓷砖,事情会变得混乱(就像你的情况一样)。

【讨论】:

  • 谢谢一百万。我想这就是你继承几年前被别人切割的瓷砖时得到的结果,我永远不会发现这一点。我一定会按照建议进行,并从完整地图中重新切块。
猜你喜欢
  • 2014-04-02
  • 1970-01-01
  • 1970-01-01
  • 2018-07-15
  • 1970-01-01
  • 2012-12-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多