osmdroid是一个开源项目,其目的是提供比安卓原生的MapView更为强大的地图组件库。osmdroid支持多种在线或者离线的瓦片地图源以及地图覆盖管理器,用于绘制图标、几何图形以及GPS定位。
osmdroid相比于Android原生MapView的优势在于其丰富的瓦片地图源和地图覆盖管理器了。相比于Arcgis Android Runtime SDK这种专业的GIS开发包在功能上的劣势非常明显,但也有其优势也很明显——轻量。一个使用了Arcgis Android Runtime SDK的APP,如果完整支持arm64-v8a、armeabi-v7a、armeabi、x86的话,apk文件至少60M,仅支持armeabi-v7a也在30M以上,而使用osmdroid的APP的APK通常只有几M。
Arcgis可以说是GIS领域最强大的软件,没有之一。Esri公司也是一家值得尊重的公司,一直在致力于GIS领域新技术的开发以及Arcgis和互联网新科技的融合。ArcgisServer提供了目前市场上最强大最丰富的的GIS领域的服务,包括地图服务,瓦片服务,矢量切片服务,要素服务,分析服务,几何网络服务等等等。
osmdroid主要是一个MapView,矢量切片这种新技术完全不支持,瓦片服务本来就支持,只有地图服务,osmdroid原本不支持,但有潜力。这里说的潜力,是因为Arcgis的地图服务是通过export接口返回的一张图片,在数据源上与瓦片地图没有区别。
我们要以瓦片的形式请求地图服务,重点就在算出每个请求瓦片的bbox,其逻辑代码如下。
public static BoundingBox tile2boundingBox(final int x, final int y, final int zoom)
{
BoundingBox bb = new BoundingBox(tile2lat(y, zoom), tile2lon(x + 1, zoom), tile2lat(y + 1, zoom), tile2lon(x, zoom));
return bb;
}
public static double tile2lon(int x, int z) {
return x / Math.pow(2.0, z) * 360.0 - 180;
}
public static double tile2lat(int y, int z) {
double n = Math.PI - (2.0 * Math.PI * y) / Math.pow(2.0, z);
return Math.toDegrees(Math.atan(Math.sinh(n)));
}
当然如果是莫卡托投影的,就不是经纬度了
public double[] getBoundingBox(int x, int y, int zoom) {
double tileSize = MAP_SIZE / Math.pow(2, zoom);
double minx = TILE_ORIGIN[ORIG_X] + x * tileSize;
double maxx = TILE_ORIGIN[ORIG_X] + (x + 1) * tileSize;
double miny = TILE_ORIGIN[ORIG_Y] - (y + 1) * tileSize;
double maxy = TILE_ORIGIN[ORIG_Y] - y * tileSize;
double[] bbox = new double[4];
bbox[MINX] = minx;
bbox[MINY] = miny;
bbox[MAXX] = maxx;
bbox[MAXY] = maxy;
return bbox;
}
然后照着请求瓦片一样请求就可以了
@Override
public String getTileURLString(long pMapTileIndex) {
String baseUrl = getBaseUrl();
if (forceHttps)
baseUrl = baseUrl.replace("http://", "https://");
if (forceHttp)
baseUrl = baseUrl.replace("https://", "http://");
StringBuilder sb = new StringBuilder(baseUrl);
if (!baseUrl.endsWith("/"))
sb.append("/");
sb.append("export?").append("bbox=");
if (srs.equals("EPSG:900913")) {
double[] bbox = getBoundingBox(MapTileIndex.getX(pMapTileIndex), MapTileIndex.getY(pMapTileIndex), MapTileIndex.getZoom(pMapTileIndex));
sb.append(bbox[MINX]).append(",");
sb.append(bbox[MINY]).append(",");
sb.append(bbox[MAXX]).append(",");
sb.append(bbox[MAXY]);
} else {
BoundingBox boundingBox = tile2boundingBox(MapTileIndex.getX(pMapTileIndex), MapTileIndex.getY(pMapTileIndex), MapTileIndex.getZoom(pMapTileIndex));
sb.append(boundingBox.getLonWest()).append(",");
sb.append(boundingBox.getLatSouth()).append(",");
sb.append(boundingBox.getLonEast()).append(",");
sb.append(boundingBox.getLatNorth());
}
sb.append("&bboxSR=").append(srs.replace("EPSG:","")).append("&size=").append(getTileSizePixels()).append(",").append(getTileSizePixels());
sb.append("&format=png24").append("&transparent=true").append("&dpi=96").append("&f=image");
String str = sb.toString();
return str;
}
这个思路不是我的,而是在osmdroid中自带一个osmdroid-wms的子项目,我心想WMS和ArcgisServer的地图服务原理一致,前者可以后者当然也可以,所以就仔细阅读了一下osmdroid-wms的源码,参照WMSTileSource类写了一个ArcgisServer地图服务的DataSource,核心代码就是上面那些了。