美文网首页
Android加载arcgis在线和离线底图过程中坐标系不同的解

Android加载arcgis在线和离线底图过程中坐标系不同的解

作者: 南太湖小蚂蚁 | 来源:发表于2023-02-17 11:16 被阅读0次

    最近在工作中遇到一个问题,Android中需要加载离线底图TPK和在线底图,在不同的底图间可以自由切换。做过GIS系统开发的同学都知道,如果在不同底图间切换,我们一般是需要保证之前的操作定位和缩放级别的,也就是在前一个底图里面定位到哪里,在下一个底图中也要定位到哪里。但是由于这两套底图的空间坐标系不同,因此如果我在加载的离线底图中操作,定位到某个位置,缩放到某个级别后,再切换到在线底图后,会无法显示。为了解决这个问题,我尝试了一种办法。下面记录一下我的解决过程。

    首先,我定义了一个spinner,下拉框中代表了不同的底图,当点击的时候可以切换到相应的底图。我这里2021年,2018年,2016年,2014年的影像底图都是离线的,但是2022年的影像是在线的。


    我自定义了一个spinner下拉框,并把数据塞到了spinner中。

    List<String> tpk_list = new ArrayList<String>();
    List<String> tpkNameList = new ArrayList();
    tpk_list.add("2021年影像");tpkNameList.add(getString(R.string.IMAGE_2021));
    tpk_list.add("2018年影像");tpkNameList.add(getString(R.string.IMAGE_2018));
    tpk_list.add("2016年影像");tpkNameList.add(getString(R.string.IMAGE_2016));
    tpk_list.add("2014年影像");tpkNameList.add(getString(R.string.IMAGE_2014));
    tpk_list.add("2022年影像(在线)");tpkNameList.add("");
    MySpinner tpkSwitchSpinner = findViewById(R.id.switch_spinner);
    tpkSwitchSpinner.setData(tpk_list);
    

    当点击每个底图后,实现底图的切换,下面是离线底图的切换方法,如果不考虑坐标系的不同,这种方法是可行的。也就是切换之前先获取到当前mapview的viewpoint,然后在切换底图后,再把viewpoint设置到新的mapview中。如下代码就可以实现这个功能:

    tpkSwitchSpinner.setOnItemSelectedListener(new MySpinner.OnItemSelectedListener() {
                @Override
                public void onItemSelected(int pos) {
                    originViewpoint = leftMapView.getCurrentViewpoint(Viewpoint.Type.CENTER_AND_SCALE);
                    switchToOfflineMap(tpkNameList.get(pos));
                }
    });
    
    private void switchToOfflineMap(String tpkName) {
        String path = Const.FILEPATH + "/TPK/"+tpkName;
        Log.d(TAG, path);
        List<Layer> baseLayers = new ArrayList<>();
        TileCache tileCache = new TileCache(path);
        ArcGISTiledLayer arcGISTiledLayer = new ArcGISTiledLayer(tileCache);
        String bzTPKpath = Const.FILEPATH + "/TPK/BZ.tpk";
        TileCache bzTpkCache = new TileCache(bzTPKpath);
        mArcGISTiledLayer = new ArcGISTiledLayer(bzTpkCache);
        baseLayers.add(arcGISTiledLayer);
        baseLayers.add(mArcGISTiledLayer);
        Basemap basemap = new Basemap(baseLayers, null);
        ArcGISMap map = new ArcGISMap(basemap);
        map.loadAsync();
        map.setBackgroundColor(Color.WHITE);
        restoreOriginViewpoint();
        leftMapView.setMap(map);
    }
    
    private void restoreOriginViewpoint() {
        Log.d(TAG, "switchToOtherTpk: " + originViewpoint);
        leftMapView.setViewpointAsync(originViewpoint);
    }
    

    如果仅仅是离线底图,没有问题,因为这些TPK都是同一套坐标系的,可如果加载在线底图后,坐标系很可能是有差别的,因此需要分别进行处理。

    tpkSwitchSpinner.setOnItemSelectedListener(new MySpinner.OnItemSelectedListener() {
        @Override
        public void onItemSelected(int pos) {
            originViewpoint = leftMapView.getCurrentViewpoint(Viewpoint.Type.CENTER_AND_SCALE);
            Log.d(TAG, originViewpoint.getTargetGeometry().getExtent().toString());
            Log.d(TAG, String.valueOf(leftMapView.getMap().getSpatialReference().getWkid()));
            Log.d(TAG, String.valueOf(originViewpoint.getTargetGeometry().getSpatialReference().getWkid()));
    
            if(tpk_list.get(pos).contains("在线")) {
                // 在线底图加载
            }else {
                System.out.println("--------------离线");
                originViewpoint = change(originViewpoint, false);
                Log.d(TAG, originViewpoint.getTargetGeometry().getExtent().toString());
                switchToOfflineMap(tpkNameList.get(pos));
            }
        }
    });
    

    为了保持离线底图的操作点位和缩放级别,切换到在线底图时可以把viewpoint保存下来,把mapview中的map控件的初始viewpoint设置成上一个viewpoint即可。在坐标系相同的情况下是可以这么做的,不过如果坐标系不同的话,上一个viewpoint中的坐标值并不适用于下一个底图的viewpoint。因此,这里可以通过转换viewpoint的坐标系来解决:

    首先获取到viewpoint的extent以及当前的缩放级别,并基于extent的坐标值以及当前坐标系,构建一个Point对象。这里envelope的xmin和xmax,ymin和ymax是一样的,因为这里的extent本身是一个点。

    Envelope envelope = viewPoint.getTargetGeometry().getExtent();
    double scale = viewPoint.getTargetScale();
    System.out.println("target extent------------"+envelope.toString());
    Point p = new Point(envelope.getXMin(), envelope.getYMin(), leftMapView.getSpatialReference());
    

    接下来,将这个Point对象转换成需要切换的底图的坐标系,并保持缩放级别。这里,我的在线底图空间坐标系的WKID是4490的,也就是2000坐标系的,离线底图是墨卡托坐标系的。采用arcgis for android api,GeometryEngine的project方法转换后,返回一个新的viewpoint。

    if(isOnline) {
        Point point=(Point) GeometryEngine.project(p, SpatialReference.create(4490));
        return new Viewpoint(point, scale);
    }else {
        Point point=(Point) GeometryEngine.project(p, SpatialReferences.getWebMercator());
        return new Viewpoint(point, scale);
    }
    

    最后在新的底图中,使用setInitialViewpoint设置为新的viewpoint,就可以实现底图的正常切换了。完整的转换代码如下:

    // 转换Viewpoint坐标系
    private Viewpoint change(Viewpoint viewPoint, boolean isOnline) {
        Envelope envelope = viewPoint.getTargetGeometry().getExtent();
        double scale = viewPoint.getTargetScale();
        System.out.println("target extent------------"+envelope.toString());
        Point p = new Point(envelope.getXMin(), envelope.getYMin(), leftMapView.getSpatialReference());
    
        if(isOnline) {
            Point point=(Point) GeometryEngine.project(p, SpatialReference.create(4490));
            return new Viewpoint(point, scale);
        }else {
            Point point=(Point) GeometryEngine.project(p, SpatialReferences.getWebMercator());
            return new Viewpoint(point, scale);
        }
    }
    

    离线底图切换代码如下:

    private void switchToOfflineMap(String tpkName) {
        String path = Const.FILEPATH + "/TPK/"+tpkName;
        Log.d(TAG, path);
        List<Layer> baseLayers = new ArrayList<>();
        TileCache tileCache = new TileCache(path);
        ArcGISTiledLayer arcGISTiledLayer = new ArcGISTiledLayer(tileCache);
        String bzTPKpath = Const.FILEPATH + "/TPK/BZ.tpk";
        TileCache bzTpkCache = new TileCache(bzTPKpath);
        mArcGISTiledLayer = new ArcGISTiledLayer(bzTpkCache);
        baseLayers.add(arcGISTiledLayer);
        baseLayers.add(mArcGISTiledLayer);
        Basemap basemap = new Basemap(baseLayers, null);
        ArcGISMap map = new ArcGISMap(basemap);
        map.setBackgroundColor(Color.WHITE);
        map.setInitialViewpoint(originViewpoint);
        leftMapView.setMap(map);
    }
    

    spinner的点击事件代码如下:

    tpkSwitchSpinner.setOnItemSelectedListener(new MySpinner.OnItemSelectedListener() {
            @Override
            public void onItemSelected(int pos) {
                originViewpoint = leftMapView.getCurrentViewpoint(Viewpoint.Type.CENTER_AND_SCALE);
                Log.d(TAG, originViewpoint.getTargetGeometry().getExtent().toString());
                Log.d(TAG, String.valueOf(leftMapView.getMap().getSpatialReference().getWkid()));
                Log.d(TAG, String.valueOf(originViewpoint.getTargetGeometry().getSpatialReference().getWkid()));
                if(tpk_list.get(pos).contains("在线")) {
                    System.out.println("--------------在线");
                    originViewpoint = change(originViewpoint, true);
                    Log.d(TAG, originViewpoint.getTargetGeometry().getExtent().toString());
                    List<Layer> baseLayers = new ArrayList<>();
                    // 在线获取底图
                    String url = "http://<img_ip>/MapServer"; // 影像地址
                    ArcGISTiledLayer arcGISTiledLayer = new ArcGISTiledLayer(url);
                    baseLayers.add(arcGISTiledLayer);
                    String bzUrl = "http://<bz_ip>/MapServer"; // 影像注记地址
                    ArcGISTiledLayer bzTiledLayer = new ArcGISTiledLayer(bzUrl);
                    baseLayers.add(bzTiledLayer);
                    Basemap basemap = new Basemap(baseLayers, null);
                    ArcGISMap arcGISMap = new ArcGISMap(basemap);
                    arcGISMap.setBackgroundColor(Color.WHITE);
                    arcGISMap.setInitialViewpoint(originViewpoint);
                    leftMapView.setMap(arcGISMap);
                }else {
                    System.out.println("--------------离线");
                    originViewpoint = change(originViewpoint, false);
                    Log.d(TAG, originViewpoint.getTargetGeometry().getExtent().toString());
                    switchToOfflineMap(tpkNameList.get(pos));
                }
            }
        });
    }
    

    下面看一下效果。我加载2021年的离线底图,选择南京市玄武湖景区,效果如下:



    此时,我切换到2018年的底图



    最后,我再切换到在线的2022年的底图

    可以看到都定位到同样的地方和同样的缩放级别,不过由于我离线底图在此级别下没有注记,所以前两幅图看不到注记,在在线底图中可以看到注记。

    相关文章

      网友评论

          本文标题:Android加载arcgis在线和离线底图过程中坐标系不同的解

          本文链接:https://www.haomeiwen.com/subject/spbmkdtx.html