如何扩展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
相关产品推荐
相关产品推荐

