worldwind java对tile服务(谷歌、必应、opensteetmap、高德……)的支持

国内外很多数据源都是tile服务,谷歌、必应、opensteetmap、高德……,这些服务的数据层基本均采用Mercator投影,只是服务地址稍有差异。

比如下面的数据地址为谷歌的一个tile的URL地址:
http://mt1.google.cn/maps/vt?lyrs=s%40689&hl=zh-CN&gl=CN&&x=23&y=11&z=5

谷歌地图数据示例

其中mt后的数字可以是0-7之间的任意整数,可以理解成不同的服务器。xyz后面的数字可以理解成该图片的行列号以及缩放级别。

其它的数据服务地址类似,可能xyz的位置不同,服务器的标识可能不是数字而是abcd等。

数据地址基本上大同小异。

worldwind java对此投影数据服务显然是支持的,其功能通过BasicMercatorTiledImageLayer实现。

worldwind对数据源的访问过程一般如下:

  • 通过makeLevels构建图层所要的信息LevelSet,其核心为TileUrlBuilder
  • TileUrlBuilder构建针对不同tile的url服务地址
  • worldwind java通过url访问服务器资源并下载到本地后加载显示。

理论上讲,构造相应的LevelSet,并构建出相应的TileUrlBuilder即可实现对上述各个数据源的引入,并在worldwind上展示这些数据。

而TileUrlBuilder只是一个接口,并不具备实际构建url的能力,因此,构建一个类,实现此接口即可。

public interface TileUrlBuilder
{
        public URL getURL(Tile tile, String imageFormat) throws java.net.MalformedURLException;
}

典型的改造过程如下:

  • 重写makeLevels函数

典型的例子代码如下:

private static LevelSet makeLevels(TileUrlBuilder ub, String labelName,String datasetName,String suffix,String serviceURL,int numLevelMin,int numLevelMax,int tileWidth,int tileHeight)
{
    AVList params = new AVListImpl();

    params.setValue(AVKey.TILE_WIDTH, tileWidth);
    params.setValue(AVKey.TILE_HEIGHT, tileHeight);
    params.setValue(AVKey.DATA_CACHE_NAME, "Earth/"+labelName);
    params.setValue(AVKey.SERVICE, serviceURL);
    params.setValue(AVKey.DATASET_NAME, datasetName);
    params.setValue(AVKey.FORMAT_SUFFIX, suffix);
    params.setValue(AVKey.NUM_LEVELS, numLevelMax);
    params.setValue(AVKey.NUM_EMPTY_LEVELS, numLevelMin);
    params.setValue(AVKey.LEVEL_ZERO_TILE_DELTA, new LatLon(Angle.fromDegrees(22.5d), Angle.fromDegrees(45d)));
    params.setValue(AVKey.SECTOR, new MercatorSector(-1.0, 1.0, Angle.NEG180, Angle.POS180));
    params.setValue(AVKey.TILE_URL_BUILDER, ub);

    return new LevelSet(params);
}
  • 构建TileUrlBuilder

这里的TileUrlBuilder并没有任何数据源特征。为了保证对上述N个数据源的支持,这里通过建立一个TileUrlBuilderCustom的抽象类,构建上述数据源的一些共同特征。

public abstract class TileUrlBuilderCustom implements TileUrlBuilder {

    protected String urlFormat ;

    protected String serverPre;
    protected String[] serverlist;

    private String strApikey="";


    protected String strXTag = "{x}";
    protected String strYTag = "{y}";
    protected String strZTag = "{z}";
    protected String strServerTag = "{s}";
    protected String strApiKeyTag = "{k}";
    protected String strQuadKeyTagTag = "{quadkey}";


    public TileUrlBuilderCustom()
    {

    }

    public TileUrlBuilderCustom(String urlFormat,String serverPre)
    {
        this.urlFormat = urlFormat;
        this.serverPre = serverPre;
        if(serverPre!="")
            serverlist = serverPre.split("");
    }

    public TileUrlBuilderCustom(String urlFormat,String serverPre,String strApikey)
    {
        this.urlFormat = urlFormat;
        this.serverPre = serverPre;
        if(serverPre!="")
            serverlist = serverPre.split("");
        this.strApikey = strApikey;
    }

    @Override
    public URL getURL(Tile tile, String imageFormat) throws MalformedURLException {
        LatLon center = tile.getSector().getCentroid();
        String urlString = computeTileUrl(center
                .getLatitude().degrees, center.getLongitude().degrees, tile
                .getLevelNumber() + 3);
        URL url = new URL(urlString);
        return url;
    }

    protected String computeTileUrl(double lat, double lon, int zoom) {

        lat = computeTileUrlTMS(lat);

        if (lon > 180.0) {
            lon -= 360.0;
        }

        lon = (180.0 + lon) / 360.0;
        lat = 0.5
                - Math.log(Math.tan((Math.PI / 4.0)
                + ((Math.PI * lat) / (2.0 * 180.0)))) / (2.0 * Math.PI);

        int scale = 1 << (int) zoom;

        int x = (int) (lon * scale);
        int y = (int) (lat * scale);

        String urlFormatTemp = urlFormat;

        urlFormatTemp = computeTileUrl(urlFormatTemp,x,y,zoom);

        urlFormatTemp = urlFormatTemp.replace(strXTag,String.valueOf(x));
        urlFormatTemp = urlFormatTemp.replace(strYTag,String.valueOf(y));
        urlFormatTemp = urlFormatTemp.replace(strZTag,String.valueOf(zoom));

        urlFormatTemp = urlFormatTemp.replace(strApiKeyTag,strApikey);

        urlFormatTemp = urlFormatTemp.replace(strQuadKeyTagTag,TileXyToQuadKey(x,y,zoom));

        return urlFormatTemp;
    }

    abstract protected double computeTileUrlTMS(double lat);
    abstract protected String computeTileUrl(String strTemp,int x, int y, int zoom);

    protected String computeTileUrlRandom(String strTemp,int x, int y, int zoom) {
        Random r = new Random();
        if(serverPre=="")
            return strTemp;
        else {
            strTemp = strTemp.replace(strServerTag,serverlist[r.nextInt(serverPre.length())]);

            return  strTemp;
        }
    }

    protected String TileXyToQuadKey(int tileX, int tileY, int levelId)
    {
        String quadKey="";

        int levelOfDetail = levelId;

        for (int i = levelOfDetail; i > 0; i--)
        {
            char digit = '0';
            long mask = 1 << (i - 1);

            if ((tileX & mask) != 0)
            {
                digit++;
            }

            if ((tileY & mask) != 0)
            {
                digit++;
                digit++;
            }

            quadKey=quadKey+digit;
        }

        return quadKey;
    }
}

上面的代码比较简单。定义了一些占位符,比如tile服务的横纵坐标、缩放级别,服务器标签,ApiKey标签等,在构建url时,这些占位符被替换成相应的实际值。

抽象类毕竟不能直接实例成对象,下面给出了一个对服务器字符串随机处理的派生类,实际上直接调用父类中的computeTileUrlRandom即可。

public class TileUrlBuilderCustomRandomServer extends TileUrlBuilderCustom {

    public TileUrlBuilderCustomRandomServer(String urlFormat,String serverPre) {
        super(urlFormat,serverPre);
    }

    protected double computeTileUrlTMS(double lat)
    {
        return  lat;
    }
    @Override
    protected String computeTileUrl(String strTemp, int x, int y, int zoom) {
        return computeTileUrlRandom(strTemp,x,y,zoom);
    }
}

这个类已经可以保证合成出正确的URL。特殊情况下,比如有些网络服务的服务器地址是根据某些算法计算得出,这个类就不合适,需要在继续派生新类,并重写一些相应的函数。比如对谷歌中国的数据就不能按照这种方式载入,因此,新建一个类如下

public class googleURLBuilder extends TileUrlBuilderCustom {
    protected  String _server;
    protected  String _request;
    protected  String _versionKey;
    private String _version;

    protected static String SecGoogleWord = "Galileo";

    public googleURLBuilder( String _version, String _server, String _request, String _versionKey) {

        super("http://%s%d.google.cn/%s/%s=%s&hl=%s&x={x}%s&y={y}&z={z}&s=%s","");
        this._version = _version;
        this._server = _server;
        this._request = _request;
        this._versionKey = _versionKey;
    }

    @Override
    protected double computeTileUrlTMS(double lat) {
        return lat;
    }

    protected String computeTileUrl(String strTemp,int x, int y, int zoom) {

        String sec1="",sec2="";

        int seclen = ((x * 3) + y) % 8;
        sec2 = SecGoogleWord.substring(0, seclen);
        if (y >= 10000 && y < 100000)
        {
            sec1 = "&s=";
        }

        return String.format(strTemp,_server, (x+2*y)% 4, _request,_versionKey,_version,"zh-CN", sec1, sec2);

    }

}

只要实例化不同的TileUrlBuilder派生类,即可实现worldwind java对不同数据源的数据载入。

必应地图

TileUrlBuilder tb = new TileUrlBuilderCustomRandomServer("http://t{s}.tiles.virtualearth.net/tiles/a{quadkey}.jpg?g=517&token=soep","01234567");

高德地图

TileUrlBuilder tb = new TileUrlBuilderCustomRandomServer("http://webrd0{s}.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scale=1&style=7","1234");

谷歌卫星图

TileUrlBuilder tb = new new googleURLBuilder("190","khm","kh","v");

OSM地图

TileUrlBuilder tb = new TileUrlBuilderCustomRandomServer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png","abc")

把生成的TileUrlBuilder用makeLevels来构建一个LevelSet,即可实现不同种类数据的载入。

网络开放数据集

类似地,可以载入各个数据提供商(谷歌、必应、osm、阿里云……)的普通地图、卫星图、矢量图、地形图……

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值