You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何扩展OSMDroid 6.1.20以支持EPSG:4326的2:1比例瓦片数据库?

解决方案:扩展OSMDroid支持2:1 X维度的EPSG:4326瓦片数据库

核心问题分析

你遇到的核心矛盾是OSMDroid原生MapTileIndex类硬编码了X/Y维度瓦片数相等的校验逻辑(checkValues方法中用max = 1 << pZoom同时约束X、Y轴范围),但你的EPSG:4326瓦片X轴数量是Y轴的2倍(缩放级别0时X有2个瓦片,Y有1个),导致东半球X索引被判定为非法,无法加载。

具体实现步骤

1. 自定义TileIndex工具类

不要直接修改静态的MapTileIndex,实现适配2:1 X维度的CustomTileIndex类:

public class CustomTileIndex {
    private static final int mMaxZoomLevel = 19; // 按需调整瓦片最大缩放级别

    public static long getTileIndex(final int pZoom, final int pX, final int pY) {
        checkValues(pZoom, pX, pY);
        return (((long) pZoom) << (mMaxZoomLevel * 2))
                + (((long) pX) << mMaxZoomLevel)
                + (long) pY;
    }

    private static void checkValues(final int pZoom, final int pX, final int pY) {
        if (pZoom < 0 || pZoom > mMaxZoomLevel) {
            throw new IllegalArgumentException("Zoom out of range: " + pZoom);
        }
        // X轴瓦片数:缩放级别n时为2^(n+1)(zoom0=2,zoom1=4,以此类推)
        final long maxX = 1 << (pZoom + 1);
        if (pX < 0 || pX >= maxX) {
            throw new IllegalArgumentException("X out of range for zoom " + pZoom + ": " + pX);
        }
        // Y轴保持原生逻辑:缩放级别n时为2^n
        final long maxY = 1 << pZoom;
        if (pY < 0 || pY >= maxY) {
            throw new IllegalArgumentException("Y out of range for zoom " + pZoom + ": " + pY);
        }
    }

    // 结合自定义TileSystem,从经纬度生成自定义瓦片索引
    public static long latLonToTileIndex(double lat, double lon, int zoom, CustomTileSystem tileSystem) {
        Point tile = tileSystem.latLonToTile(lat, lon, zoom);
        return getTileIndex(zoom, tile.x, tile.y);
    }
}

2. 扩展TileProvider适配自定义索引

基于SqliteTileProvider扩展,在瓦片请求环节替换原生索引逻辑:

public class CustomSqliteTileProvider extends SqliteTileProvider {
    private final CustomTileSystem mCustomTileSystem;

    public CustomSqliteTileProvider(Context context, CustomTileSystem tileSystem, String databasePath) {
        super(context, new SimpleRegisterReceiver(context), databasePath);
        mCustomTileSystem = tileSystem;
    }

    @Override
    public void requestTile(long pMapTileIndex, ITileReceiver pReceiver) {
        int zoom = MapTileIndex.getZoom(pMapTileIndex);
        // 从原生索引解析的X/Y是1:1逻辑,转换为2:1的实际X索引
        int originalX = MapTileIndex.getX(pMapTileIndex);
        int y = MapTileIndex.getY(pMapTileIndex);
        
        // 转换为2:1的X值:原生X对应西半球,东半球需叠加2^zoom
        int actualX = originalX * 2;
        // 如果当前视野覆盖东半球,计算对应的X索引(需结合你的TileSystem经纬度转换逻辑调整)
        double centerLon = mMapView.getMapCenter().getLongitude();
        if (centerLon > 0) {
            actualX += (1 << zoom);
        }

        // 生成自定义索引并请求瓦片
        long customIndex = CustomTileIndex.getTileIndex(zoom, actualX, y);
        super.requestTile(customIndex, pReceiver);
    }
}

3. 实现双投影兼容

在MapView中动态切换投影和对应的TileProvider:

// 初始化两种投影的TileSystem和TileProvider
CustomTileSystem epsg4326TileSystem = new CustomTileSystem();
SqliteTileProvider epsg3857TileProvider = new SqliteTileProvider(context, new SimpleRegisterReceiver(context), "path/to/3857_tiles.db");
CustomSqliteTileProvider epsg4326TileProvider = new CustomSqliteTileProvider(context, epsg4326TileSystem, "path/to/4326_tiles.db");

// 切换到EPSG:4326
mMapView.setTileSystem(epsg4326TileSystem);
mMapView.setTileProvider(epsg4326TileProvider);

// 切换回EPSG:3857
mMapView.setTileSystem(new MercatorTileSystem());
mMapView.setTileProvider(epsg3857TileProvider);

4. 关键校验逻辑修正

确保你的CustomTileSystem中经纬度与瓦片索引的转换逻辑完全匹配2:1的X维度:

  • 缩放级别0:经度-180~0 → X=0,0~180 → X=1
  • 缩放级别1:经度-180~-90 → X=0,-90~0 → X=1,0~90 → X=2,90~180 → X=3
  • 以此类推,保证经纬度到瓦片索引的映射无偏差

内容的提问来源于stack exchange,提问作者SupremeMitchell

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.11 22:46:05