ArcGIS for Android 加载基础底图

基础底图

移动地图程序的基础,在于“一张图”,外业调绘、导航类、成果展示、统计应用等都需要一张基本的底图来支撑。往往底图的好坏决定了整个移动地图的根基。

加载在线基础底图

在线基础底图包括传统GIS(ArcGIS Server)和WebGIS(Online & Portal)。使用方式上确实不同,传统GIS是通过Layer直接对接地图服务(ArcGIS Server发布的)。WebGIS是同过ArcGISMap来对接WebMap。

1.传统GIS

对于一直使用ArcGIS Server发布地图服务的,可以直接使用地图服务(Layer)来加载在线的地图服务(ArcGIS Server)。

// 在线地图网址
// String web_url = "http://map.geoq.cn/ArcGIS/rest/services/ChinaOnlineCommunity/MapServer";


String url = "http://map.geoq.cn/arcgis/rest/services/ChinaOnlineCommunity/MapServer";
ArcGISTiledLayer arcGISTiledLayer = new ArcGISTiledLayer(url);
Basemap basemap = new Basemap(arcGISTiledLayer);
ArcGISMap arcGISMap = new ArcGISMap(basemap);
mMapView.setMap(arcGISMap);


  // 长春 - 初始化范围(仅仅是本图层)
  Envelope targetExtent = new Envelope(13909984.0, 5437387.0, 13986734.0, 5458866.0,
            SpatialReferences.getWebMercator());
  Viewpoint initViewpoint = new Viewpoint(targetExtent);
  mMapView.getMap().setInitialViewpoint(initViewpoint);

/*
 // 设置初始化范围(tpk图层)
Envelope targetExtent = new Envelope(-302859.47,-183912.51,349580.85,261109.45,SpatialReference.create(21481));
Viewpoint initViewpoint = new Viewpoint(targetExtent);
mapView.getMap().setInitialViewpoint(initViewpoint);
*/

2.WebGIS(Online & Portal)

通过Online或者Portal可以便捷快速的制作出更简洁智能的地图资源,用以表达我们的目的,对于移动端而言这些Web Map可以直接应用。

 String url = "http://www.arcgis.com/home/webmap/viewer.html?webmap=55c1665bcd064552944a9e8296271ec3";
  ArcGISMap arcGISMap = new ArcGISMap(url);
  mMapView.setMap(arcGISMap);

  Basemap basemap = arcGISMap.getBasemap(); //获取底图
  LayerList operationalLayers = arcGISMap.getOperationalLayers(); //获取业务图层

3.在线矢量切片:ArcGISVectorTiledLayer

String url = "https://www.arcgis.com/home/item.html?id=e19e9330bf08490ca8353d76b5e2e658";
ArcGISVectorTiledLayer arcGISVectorTiledLayer = new ArcGISVectorTiledLayer(url);
Basemap basemap = new Basemap(arcGISVectorTiledLayer);
ArcGISMap arcGISMap = new ArcGISMap(basemap);
mMapView.setMap(arcGISMap);
Viewpoint vp = new Viewpoint(47.606726, -122.335564, 72223.819286);
arcGISMap.setInitialViewpoint(vp);

加载离线基础底图

离线基础底图最传统的方式是直接拷贝ArcGIS Server服务的切片成果,存在的问题是无论使用松散型还是紧凑型都包含太多碎小文件,部署不便。

TPK文件便是为了解决多碎小文件问题。但是依然存在文件太大的问题,动辄十G甚至几十G。而矢量切片(VTPK)在于解决文件太大的问题。

1.TPK

       String map_path = null;
        // 加载tpk地图(大于api 24---android 7)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
             map_path =getExternalFilesDir("/TPK/dlst.tpk").getPath();
        } else {
             map_path = Environment.getExternalStorageDirectory().getAbsolutePath() +     
                      "/TPK/dlst.tpk";
        }
        TileCache titleCache = new TileCache(map_path);
        ArcGISTiledLayer arcGISTiledLayer = new ArcGISTiledLayer(titleCache);
        Basemap basemap = new Basemap(arcGISTiledLayer);
        ArcGISMap arcGISMap = new ArcGISMap(basemap);

        

        mMapView.setMap(arcGISMap);

加载多个TPK数据:

       // TPK 底图
        String map_path1 = null;
        String map_path2 = null;
        // 加载tpk地图(大于api 24---android 7)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
             map_path1 =getExternalFilesDir("/TPK/2021.tpk").getPath();
             map_path2 =getExternalFilesDir("/TPK/lw.tpk").getPath();
        } else {
             map_path1 = Environment.getExternalStorageDirectory().getAbsolutePath() + "/TPK/2021.tpk";
             map_path2 =getExternalFilesDir("/TPK/lw.tpk").getPath();
        }

        TileCache titleCache1 = new TileCache(map_path1);
        TileCache titleCache2 = new TileCache(map_path2);
        // 创建Layer列表,用来存放多个tpk数据
        List<Layer> baseLayers = new ArrayList<>();
        ArcGISTiledLayer arcGISTiledLayer1 = new ArcGISTiledLayer(titleCache1);
        ArcGISTiledLayer arcGISTiledLayer2 = new ArcGISTiledLayer(titleCache2);
        baseLayers.add(arcGISTiledLayer1);
        baseLayers.add(arcGISTiledLayer2);
        Basemap basemap= new Basemap(baseLayers,null);

        ArcGISMap arcGISMap = new ArcGISMap();
        arcGISMap.setBasemap(basemap);

        // 设置最小比例尺
        arcGISMap.setMinScale(1500000);
        // 设置最大比例尺
        arcGISMap.setMaxScale(5000);

     
   

        mapView.setMap(arcGISMap);

2.矢量切片:VTPK

String path = "/sdcard/Hymn/basemap/dzzhdjfb.vtpk";
ArcGISVectorTiledLayer mainArcGISVectorTiledLayer = new ArcGISVectorTiledLayer(path);
Basemap mainBasemap = new Basemap(mainArcGISVectorTiledLayer);
ArcGISMap mainArcGISMap = new ArcGISMap(mainBasemap);
mMapView.setMap(mainArcGISMap);

3.MMPK(Basemap)

通过ArcGIS Pro可以制作包含基础底图(Basemap)的MMPK,MMPK文件解析后,基础底图(Basemap)中的图层会解析为MobileBasemapLayer,只提供浏览功能。当然,亚洲字符的支持情况在安卓端不是特别好。

String path = "/sdcard/Hymn/basemap/MobileBasemapLayer.mmpk";
        final MobileMapPackage mobileMapPackage = new MobileMapPackage(path);
        mobileMapPackage.loadAsync();
        mobileMapPackage.addDoneLoadingListener(new Runnable() {
            @Override
            public void run() {
                LoadStatus loadStatus = mobileMapPackage.getLoadStatus();
                if (loadStatus == LoadStatus.LOADED) {
                    List<ArcGISMap> maps = mobileMapPackage.getMaps();
                    ArcGISMap arcGISMap = maps.get(0);
                    Basemap basemap = arcGISMap.getBasemap();
                    LayerList operationalLayers = arcGISMap.getOperationalLayers();
                    mMapView.setMap(arcGISMap);
                }
            }
        });

底图的切换

在Runtime100里,MapView是通过ArcGISMap类来完成图层的管理。

ArcGISMap类是将底图和业务图层分开的,对于底图,ArcGISMap里用了Baemap类来进行管理。

上面已经介绍了加载底图,现在就讲讲底图的切换。

如果我们要切换底图时候,仅需要给ArcGISMap类重新赋值一个底图即可。

 Basemap basemap = new Basemap(layer);
 arcGISMap.setBasemap(basemap);

布局:

<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- The main content view -->
    <FrameLayout
        android:id="@+id/fl_content"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <com.esri.arcgisruntime.mapping.view.MapView
            android:id="@+id/mapview"
            android:layout_width="match_parent"
            android:layout_height="match_parent"></com.esri.arcgisruntime.mapping.view.MapView>
    </FrameLayout>

    <!-- The navigation drawer -->
    <ListView
        android:id="@+id/listview"
        android:layout_width="200dp"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        android:background="@android:color/background_light"
        android:choiceMode="singleChoice"
        android:divider="#BDBDBD"
        android:dividerHeight="1dp" />
</android.support.v4.widget.DrawerLayout>

Activity:

public class ChangeBasemapActivity extends AppCompatActivity {

    @BindView(R.id.mapview)
    MapView mMapview;
    @BindView(R.id.fl_content)
    FrameLayout mFlContent;
    @BindView(R.id.listview)
    ListView mListview;
    @BindView(R.id.drawer_layout)
    DrawerLayout mDrawerLayout;

    private ArcGISMap mArcGISMap;

    private String[] mNavigationDrawerItemTitles;

    private ActionBarDrawerToggle mDrawerToggle;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_change_basemap);
        ButterKnife.bind(this);

        //侧滑页面数据
        mNavigationDrawerItemTitles = getResources().getStringArray(R.array.basemap_types);

        if (getSupportActionBar() != null) {
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            getSupportActionBar().setHomeButtonEnabled(true);
            getSupportActionBar().setTitle("底图切换");
        }

        mArcGISMap = new ArcGISMap(Basemap.Type.TOPOGRAPHIC, 47.6047381, -122.3334255, 12);
        mMapview.setMap(mArcGISMap);

        addDrawerItems();

        setupDrawer();
    }

    private void addDrawerItems() {
        ArrayAdapter<String> mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1,
                mNavigationDrawerItemTitles);
        mListview.setAdapter(mAdapter);

        mListview.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) {
                selectBasemap(position);
            }
        });
    }

    /**
     * Set up the navigation drawer
     */
    private void setupDrawer() {
        mDrawerToggle = new ActionBarDrawerToggle(this, mDrawerLayout, R.string.drawer_open, R.string.drawer_close) {

            /** Called when a drawer has settled in a completely open state. */
            public void onDrawerOpened(View drawerView) {
                super.onDrawerOpened(drawerView);
                //getSupportActionBar().setTitle(mActivityTitle);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
            }

            /** Called when a drawer has settled in a completely closed state. */
            public void onDrawerClosed(View view) {
                super.onDrawerClosed(view);
                invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
            }
        };

        mDrawerToggle.setDrawerIndicatorEnabled(true);
        mDrawerLayout.addDrawerListener(mDrawerToggle);
    }


    /**
     * Select the Basemap item based on position in the navigation drawer
     *
     * @param position order int in navigation drawer
     */
    private void selectBasemap(int position) {
        // update selected item and title, then close the drawer
        mListview.setItemChecked(position, true);
        mDrawerLayout.closeDrawer(mListview);

        // if-else is used because this sample is used elsewhere as a Library module
        if (position == 0) {
            // position 0 = Streets
            mArcGISMap.setBasemap(Basemap.createStreets());
            getSupportActionBar().setTitle(mNavigationDrawerItemTitles[position]);
        } else if (position == 1) {
            // position 1 = Navigation Vector
            mArcGISMap.setBasemap(Basemap.createNavigationVector());
            getSupportActionBar().setTitle(mNavigationDrawerItemTitles[position]);
        } else if (position == 2) {
            // position 2 = Topographic
            mArcGISMap.setBasemap(Basemap.createTopographic());
            getSupportActionBar().setTitle(mNavigationDrawerItemTitles[position]);
        } else if (position == 3) {
            // position 3 = Topographic Vector
            mArcGISMap.setBasemap(Basemap.createTopographicVector());
            getSupportActionBar().setTitle(mNavigationDrawerItemTitles[position]);
        } else if (position == 4) {
            // position 3 = Gray Canvas
            mArcGISMap.setBasemap(Basemap.createLightGrayCanvas());
            getSupportActionBar().setTitle(mNavigationDrawerItemTitles[position]);
        } else if (position == 5) {
            // position 3 = Gray Canvas Vector
            mArcGISMap.setBasemap(Basemap.createLightGrayCanvasVector());
            getSupportActionBar().setTitle(mNavigationDrawerItemTitles[position]);
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Activate the navigation drawer toggle
        return (mDrawerToggle.onOptionsItemSelected(item)) || super.onOptionsItemSelected(item);
    }

    @Override
    protected void onPostCreate(Bundle savedInstanceState) {
        super.onPostCreate(savedInstanceState);
        mDrawerToggle.syncState();
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        mDrawerToggle.onConfigurationChanged(newConfig);
    }

    @Override
    protected void onResume() {
        super.onResume();
        mMapview.resume();
    }

    @Override
    protected void onPause() {
        super.onPause();
        mMapview.pause();

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        mMapview.dispose();
    }

}

遍历mapview上加载的基础图层(.tpk、.tpkx) 

 // 获取地图上加载基础图层(底图)
 LayerList layers =  mapView.getMap().getBasemap().getBaseLayers();
 // 遍历layers
 for (Layer layer:layers) {
     if (layer instanceof ArcGISTiledLayer) {
           ArcGISTiledLayer arcGISTiledLayer = (ArcGISTiledLayer) layer;
           System.out.println(arcGISTiledLayer.getName());
      }
  }

加载三维地图 

注意:最好使用arcgis-android:100.5.0及以上,否则没法加载离线xxx.slpk文件

activity_main.xml:

    <!--三维地图-->
    <com.esri.arcgisruntime.mapping.view.SceneView
        android:id="@+id/sceneView"
        android:visibility="gone"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent">

MainActivity.java:

      mSceneView = findViewById(R.id.sceneView);
       //mSceneView.setAttributionTextVisible(false);  //去掉Esri logo
        // 网络地图
        String brest_buildings = " http://tiles.arcgis.com/tiles/P3ePLMYs2RVChkJx/arcgis/rest/services/Buildings_Brest/SceneServer";
        // add a scene service to the scene for viewing buildings
        ArcGISSceneLayer sceneLayer = new ArcGISSceneLayer(brest_buildings);

        ArcGISScene scene = new ArcGISScene();
        scene.setBasemap(Basemap.createImagery());
        scene.getOperationalLayers().add(sceneLayer);
        mSceneView.setScene(scene);
        // 本地地图
        mSceneView = findViewById(R.id.sceneView);
        ArcGISScene scene = new ArcGISScene(Basemap.createImagery());
        mSceneView.setScene(scene);
        // 路径 \Android\data\com.chy.a2dand3d\files\ThreedData\xxx.slpk
        final IntegratedMeshLayer gironaIntegratedMeshLayer = new IntegratedMeshLayer(
                getExternalFilesDir("ThreedData") + getString(R.string.meshlayer));
        scene.getOperationalLayers().add(gironaIntegratedMeshLayer);


        gironaIntegratedMeshLayer.addDoneLoadingListener(new Runnable() {
            @Override
            public void run() {
                mSceneView.setViewpointAsync(new Viewpoint(gironaIntegratedMeshLayer.getFullExtent()));
            }
        });

 设置三维场景视角镜头:

        /**
         * 设置三维场景视角镜头(camera)
         * @parm latitude 纬度
         * @parm longitude 经度
         * @parm altitude 海拔高度
         * @parm heading 镜头水平朝向 - 0度表示指北,从0度逐渐增加,镜头顺时针旋转,360度回到0度 
           指北
         * @parm pitch 镜头垂直朝向 - 0度表示垂直俯视地球,从0度逐渐增加,镜头沿其水平朝向,从俯 
           视地球朝天空旋转,360度回到0度俯视地球
         * @parm roll 旋转
         * */
        Camera camera = new Camera(43.88, 125.35, 200, 0, 0, 0);
        mSceneView.setViewpointCamera(camera);
    

控制三维垂直视角在一定角度内: 

/**
     * 三维地图垂直视角监听方法
     * */
    private void sceneViewListener(){
        mSceneView.setOnTouchListener(new DefaultSceneViewOnTouchListener(mSceneView){
           private  float touchY=0f;// 触摸屏幕的y值坐标
           private  float currentY=0f;// 当前手指滑动y值坐标
            @Override
            public boolean onTouch(View view,MotionEvent motionEvent) {
                if (motionEvent.getAction() == MotionEvent.ACTION_DOWN){
                    touchY = motionEvent.getY();
                }
                return super.onTouch(view,motionEvent);
            }

            @Override
            public boolean onTwoPointerPitch(MotionEvent motionEvent,double pitchDelta) {
                // 获取当前滑动的y值坐标
                if (motionEvent.getAction() == MotionEvent.ACTION_MOVE){
                    currentY = motionEvent.getY();
                }
                // 求差值
                float dirDis = touchY - currentY;
                // 向上滑动
                if (dirDis > 0 && Math.abs(dirDis) >= 20){
                    // 获取当前三维地图Camera
                    Camera camera = mSceneView.getCurrentViewpointCamera();
                    // 垂直视角角度大于等于80禁止继续扩大角度
                    if (camera.getPitch() >= 80){
                        return false;
                    }
                }

                // 向下滑动
                if (dirDis <= 0 && Math.abs(dirDis) >= 20){
                    // 开启垂直视角角度变化
                    return super.onTwoPointerPitch(motionEvent,pitchDelta);
                }

                return super.onTwoPointerPitch(motionEvent,pitchDelta);
            }
        });
    }

 

  • 2
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值