GIS地圖學習筆記二之Android開發

新項目中需要用到GIS地圖,觸及自己的知識盲區。所以有必要去學習一下,這裏把學習過程與思考記錄一下。

我這裏使用的是ArcGIS Runtime SDK 100.2.0,ArcGIS Runtime還有一個版本10.2.*,這個版本的ArcGIS Runtime SDK 的使用可以參考《ArcGIS Runtime SDK for Android開發筆記》系列blog。


1、找資料

1、適用於Android的ArcGIS Runtime SDK爲Android設備開發GIS地圖,這裏有android開發的流程,SDK的相關API。ArcGIS Runtime SDKs

這裏寫圖片描述

2、GIS地圖製作工具–ArcGIS Pro

這裏寫圖片描述


2、簡單使用

明白相關資料和工具的地址之後,我們就可以開始開發了。

1、在項目的build.gradle文件中,添加以下代碼

allprojects {
    repositories {
        jcenter()
        // Add the Esri public Bintray Maven repository
        maven {
            url 'https://esri.bintray.com/arcgis'
        }
    }
}

2、在app Module的build.gradle文件中,添加以下代碼

 //arcgis-android
 compile 'com.esri.arcgisruntime:arcgis-android:100.1.0'

3、在項目的清單文件AndroidManifest.xml中,添加以下權限

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-feature android:glEsVersion="0x00020000" android:required="true" />

uses-feature說明:

AndroidManifest中的uses-feature配置用來聲明一個app在運行時所依賴的外部的硬件或軟件特徵(feature),uses-feature還提供了一個required屬性配置,表示此項依賴的軟硬件特徵是否是必須的,當它設置爲true表示此app運行時必須使用此項特徵,如果沒有則無法工作,如果它設置爲false,表示應用在運行時需要用到這些特徵,但如果沒有,應用可能會有一部分功能會受到影響,但大部分功能還是可以正常工作。例如一個拍照app,它使用時必須開啓設備的攝像頭,在沒有攝像頭的機器上任何功能都無法使用,這就需要通過uses-feature來聲明該應用需要攝像頭,並將required設置爲true。再比如一個支付app,它支持掃碼支付的功能,這項功能同樣需要開啓設備的攝像頭,因此需要通過uses-feature聲明該應用需要攝像頭,但如果一個設備沒有攝像頭,僅意味着掃碼支付的功能無法使用,其他支付方式仍然可以使用,這時就可以設置required屬性爲false,表明此項feature的需求不是必須的。

佈局文件

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

代碼中獲取GIS地圖信息

mMapView = (MapView) findViewById(R.id.mapView);
ArcGISMap map = new ArcGISMap(Basemap.Type.TOPOGRAPHIC, 34.056295, -117.195800, 16); 
mMapView.setMap(map);

注意加上下面的代碼

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

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

到這裏裏的程序應該已經可以顯示出Gis地圖了。


3、相關概念

離線地圖

新項目會經常性的在無信號地區進行相關操作,所以需要使用離線地圖。ArcGis關於離線地圖的說明。

您的應用程序可以使用OfflineMapTask創建地圖的本地副本。此任務請求生成脫機數據所需的所有服務,然後將該數據下載並存儲到您的設備中。在地圖下載後,您的應用可以脫機並開始使用離線地圖。有關如何使用OfflineMapTask的更多詳細信息,請參閱創建離線地圖文檔的服務部分。

在版本100.1中,OfflineMapTask支持以下圖層類型:平鋪服務,要素服務和要素集合

移動地圖包

可以使用ArcGIS Pro將離線地圖打包到移動地圖包(.mmpk文件)中。 每個軟件包可以包含多個地圖,相關的圖層和數據,以及可選的網絡和定位器。 將移動地圖包下載或裝載到設備後,可以使用MobileMapPackage類打開包並開始使用離線地圖。 有關如何使用移動地圖包的更多詳細信息,請參閱創建離線地圖文檔。

數據源

有很多的地理數據來源。

1、在線數據源由在線GIS Web服務提供,其中包括地圖服務功能服務

ArcGIS地圖服務分爲兩大類:平鋪式動態式。平鋪服務根據圖層的平鋪方案提供預先生成(緩存)的圖片。可以將瓦片作爲柵格圖像瓦片或作爲矢量數據瓦片來提供。然後該圖層在客戶端應用程序中組裝這些圖片。您可以使用平鋪圖層訪問平鋪的地圖服務。但是,動態地圖服務提供了按照客戶要求的即時創建的地圖圖像。您可以使用地圖圖像層訪問動態地圖服務

功能服務允許您訪問地圖中的各個功能。要素服務(例如來自ArcGIS Enterprise和ArcGIS Online的要素服務)會返回來自單個圖層的要素集,以響應地圖的可見範圍或屬性或空間查詢。您可以使用要素圖層來訪問此類服務

ArcGIS圖像服務通過Web服務提供對柵格數據的訪問。圖像服務可以像地圖服務一樣緩存以提高性能當圖像服務被緩存時,它支持動態訪問和平鋪訪問動態訪問提供訪問數據,查詢和下載,以及訪問單個項目。它還提供訪問數據用於處理和分析,而平鋪訪問提供了更快和可擴展的預加載作爲平鋪服務。要將圖像服務作爲動態圖像服務訪問,請使用柵格圖層,該圖層允許您檢索圖像服務的元數據,並使用具有柵格函數的數據進行分析。要將緩存的圖像服務作爲平鋪服務訪問,請使用平鋪圖層,該圖層僅允許您檢索平鋪服務的元數據並顯示平鋪圖像。

2、離線數據源,您還可以訪問位於本地的數據源,例如移動地圖包或平鋪包。這些本地數據源也具有特定類型的層來訪問數據。

圖層

下圖顯示了API中的主要圖層類和繼承關係。 斜體的類名是抽象的。

這裏寫圖片描述

通用圖層屬性

所有圖層類都從Layer類繼承公共屬性。這個類實現了Loadable接口,它提供了加載圖層資源的異步模式。該類還實現了LayerContent接口。下面列出了該類及其接口的一些常見屬性:

使用getName方法顯示圖層的名稱。
使用getDescription方法查看圖層的描述。
使用getFullExtent方法獲取圖層的完整範圍。
使用getSpatialReference方法確定圖層使用的空間參考。
使用setVisible方法隱藏和顯示圖層。
使用opacity屬性控制圖層透明或不透明的使用setOpacity方法。
使用setMinScale和setMaxScale方法更改可見圖層的比​​例範圍閾值。
具有基於圖像的數據源的圖層可以是圖像調整圖層(繼承自ImageAdjustmentLayer),其允許在運行時調整圖層的亮度,對比度和伽馬值。基礎數據源不會更改。

每個圖層類都用作底圖或操作圖層。

……….


4、離線地圖加載

4-1、tpk-離線圖層包加載

手機中的離線地圖碎片位置/data/data/com.cnbs.gisdemo/arcgis/GisTest.tpk
這裏寫圖片描述

mMapView = (MapView) findViewById(R.id.mapView);
Utils utils = new Utils();
//tpk--緩存顯示
TileCache tileCache = new TileCache(utils.Save_Path  + "/" +  utils.File_name);
ArcGISTiledLayer tiledLayer = new ArcGISTiledLayer(tileCache);
tiledLayer.setMinScale(8000);
tiledLayer.setMaxScale(1600);
Basemap basemap = new Basemap(tiledLayer);
ArcGISMap map = new ArcGISMap(basemap);
mMapView.setMap(map);

4-2、mmpk-離線地圖包加載

private void loadMobileMapPackage(String mmpkFile){
        mapPackage = new MobileMapPackage(mmpkFile);
        mapPackage.loadAsync();
        mapPackage.addDoneLoadingListener(new Runnable() {
            @Override
            public void run() {
                if(mapPackage.getLoadStatus() == LoadStatus.LOADED && mapPackage.getMaps().size() > 0){
                    mMapView.setMap(mapPackage.getMaps().get(0));
                }else{
                    // Log an issue if the mobile map package fails to load
                }
            }
        });
    }

5、地圖相關操作

5-0、 在地圖點擊的位置繪製圖標

 mMapView.setOnTouchListener(new DefaultMapViewOnTouchListener(this, mMapView) {
            @Override
            public boolean  onSingleTapConfirmed(MotionEvent v) {
                android.graphics.Point screenPoint=new android.graphics.Point(Math.round(v.getX()), Math.round(v.getY()));
                Point clickPoint = mMapView.screenToLocation(screenPoint);
                //獲取點擊位置的經緯度
                String x = CoordinateFormatter.toLatitudeLongitude(clickPoint, CoordinateFormatter.LatitudeLongitudeFormat.DECIMAL_DEGREES, 4);
                GraphicsOverlay graphicsOverlay_1=new GraphicsOverlay();
                //加個點
//                SimpleMarkerSymbol pointSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.Style.DIAMOND, Color.RED, 10);
                //加個圖標
                BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.ic_map_blue);
                PictureMarkerSymbol pointSymbol = new PictureMarkerSymbol(drawable);

                Graphic pointGraphic = new Graphic(clickPoint,pointSymbol);
                graphicsOverlay_1.getGraphics().add(pointGraphic);
                mMapView.getGraphicsOverlays().add(graphicsOverlay_1);
                return true;
            }
        });

點擊事件對象MotionEvent

    MotionEvent {
    action=ACTION_UP, 
    id[0]=0, 
    x[0]=455.5782, 
    y[0]=1095.3904, 
    toolType[0]=TOOL_TYPE_FINGER, 
    buttonState=0, 
    metaState=0, 
    flags=0x0, 
    edgeFlags=0x0, 
    pointerCount=1, 
    historySize=0, 
    eventTime=351739375, 
    downTime=351739296, 
    deviceId=5, 
    source=0x1002 }

5-1、 顯示點擊位置的經緯度

要顯示某個點的經緯度,只需要使用這個方法即可

String x = CoordinateFormatter.toLatitudeLongitude(clickPoint, CoordinateFormatter.LatitudeLongitudeFormat.DECIMAL_DEGREES, 4);

返回字符串示例

38.0431N 114.5119E

5-2、 在地圖上繪製點和線

  • 用直角座標系畫
private void addGraphicsOverlay() {
        // point graphic
        Point pointGeometry0 = new Point(12724154.362253, 3573937.672715, SpatialReferences.getWebMercator());
        Point pointGeometry1 = new Point(12724178.110558, 3573928.336934, SpatialReferences.getWebMercator());
        Point pointGeometry2 = new Point(12724205.001669, 3573832.994711, SpatialReferences.getWebMercator());
        // red diamond point symbol
//        SimpleMarkerSymbol pointSymbol = new SimpleMarkerSymbol(SimpleMarkerSymbol.Style.DIAMOND, Color.RED, 10);
        BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.ic_map_blue);
        PictureMarkerSymbol pointSymbol = new PictureMarkerSymbol(drawable);
        // create graphic for point
        Graphic pointGraphic0 = new Graphic(pointGeometry0);
        Graphic pointGraphic1 = new Graphic(pointGeometry1);
        Graphic pointGraphic2 = new Graphic(pointGeometry2);
        // create a graphic overlay for the point
        GraphicsOverlay pointGraphicOverlay = new GraphicsOverlay();
        // create simple renderer
        SimpleRenderer pointRenderer = new SimpleRenderer(pointSymbol);
        pointGraphicOverlay.setRenderer(pointRenderer);
        // add graphic to overlay
        pointGraphicOverlay.getGraphics().add(pointGraphic0);
        pointGraphicOverlay.getGraphics().add(pointGraphic1);
        pointGraphicOverlay.getGraphics().add(pointGraphic2);
        // add graphics overlay to the MapView
        mMapView.getGraphicsOverlays().add(pointGraphicOverlay);

        // line graphic
        PolylineBuilder lineGeometry = new PolylineBuilder(SpatialReferences.getWebMercator());
        lineGeometry.addPoint(12724178.110558, 3573928.336934);
        lineGeometry.addPoint(12724205.001669, 3573832.994711);
        // solid blue line symbol
        SimpleLineSymbol lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, R.color.colorAccent, 2);
        // create graphic for polyline
        Graphic lineGraphic = new Graphic(lineGeometry.toGeometry());
        // create graphic overlay for polyline
        GraphicsOverlay lineGraphicOverlay = new GraphicsOverlay();
        // create simple renderer
        SimpleRenderer lineRenderer = new SimpleRenderer(lineSymbol);
        // add graphic to overlay
        lineGraphicOverlay.setRenderer(lineRenderer);
        // add graphic to overlay
        lineGraphicOverlay.getGraphics().add(lineGraphic);
        // add graphics overlay to the MapView
        mMapView.getGraphicsOverlays().add(lineGraphicOverlay);

    }
  • 用地理座標系(經緯度)畫
    和上面的是一樣的,只是創建點的方式不一樣
Point pointGeometry0 = CoordinateFormatter.fromLatitudeLongitude("30.5449N 114.3034E", null);
Point pointGeometry1 = CoordinateFormatter.fromLatitudeLongitude("30.5459N 114.3035E", null);
Point pointGeometry2 = CoordinateFormatter.fromLatitudeLongitude("30.5469N 114.3036E", null);
  • 畫線就有點不同了
Point pointGeometry0 = CoordinateFormatter.fromLatitudeLongitude("30.5469N 114.3036E", null);
Point pointGeometry1 = CoordinateFormatter.fromLatitudeLongitude("30.5459N 114.3035E", null);
Point pointGeometry2 = CoordinateFormatter.fromLatitudeLongitude("30.5449N 114.3034E", null);
PointCollection borderCAtoNV = new PointCollection(SpatialReferences.getWgs84());
borderCAtoNV.add(pointGeometry0);
borderCAtoNV.add(pointGeometry1);
borderCAtoNV.add(pointGeometry2);
Polyline polyline = new Polyline(borderCAtoNV);

SimpleLineSymbol lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, R.color.colorAccent, 2);
SimpleRenderer lineRenderer = new SimpleRenderer(lineSymbol);
lineGraphic = new Graphic(polyline, lineSymbol);
lineGraphicOverlay = new GraphicsOverlay();
lineGraphicOverlay.setRenderer(lineRenderer);
lineGraphicOverlay.getGraphics().add(lineGraphic);
mMapView.getGraphicsOverlays().add(lineGraphicOverlay);

5-3、 選中的要素閃爍

handler.postDelayed(runnable, 1000);
private boolean isShow;
    private int recLen = 5;//設置有效時間
    Handler handler = new Handler();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            isShow = !isShow;
            recLen--;
            handler.postDelayed(runnable, 1000);
            ListenableList<GraphicsOverlay> list = mMapView.getGraphicsOverlays();
            GraphicsOverlay overlay = list.get(1);
            overlay.setSelectionColor(R.color.colorPrimary);
            overlay.setVisible(isShow);
            if (recLen <= 0) {
                recLen = 5;//重置
                Message message = new Message();
                message.what = 1;
                handlerStop.sendMessage(message);
            }
        }
};

    final Handler handlerStop = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    handler.removeCallbacks(runnable);
                    ListenableList<GraphicsOverlay> list = mMapView.getGraphicsOverlays();
                    GraphicsOverlay overlay = list.get(1);
                    overlay.setSelectionColor(R.color.colorAccent);
                    overlay.setVisible(true);
                    break;
            }
            super.handleMessage(msg);
        }

    };
 @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacks(runnable);
    }

5-4、 設置地圖中心點

  Point point1 = new Point(12724178.110558, 3573932.336934, SpatialReferences.getWebMercator());
        mMapView.setViewpointCenterAsync(point1,3800);

5-5、切換圖層

在Runtime100裏,MapView是通過ArcGISMap類來完成圖層的管理。

首先是底圖的加載。ArcGISMap類是將底圖和業務圖層分開的,對於底圖,ArcGISMap裏用了Baemap類來進行管理。
例子就是上面的4、離線地圖加載

手機中的離線地圖碎片位置`/data/data/com.cnbs.gisdemo/arcgis/GisTest.tpk`
mMapView = (MapView) findViewById(R.id.mapView);
Utils utils = new Utils();
//tpk--緩存顯示
TileCache tileCache = new TileCache(utils.Save_Path  + "/" +  utils.File_name);
ArcGISTiledLayer tiledLayer = new ArcGISTiledLayer(tileCache);
tiledLayer.setMinScale(8000);
tiledLayer.setMaxScale(1600);
Basemap basemap = new Basemap(tiledLayer);
ArcGISMap map = new ArcGISMap(basemap);
mMapView.setMap(map);

我們要切換底圖時候,僅需要給ArcGISMap類重新賦值一個底圖即可。

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        Basemap basemap = new Basemap(layer);
        map.setBasemap(basemap);
        mMapView.setMap(arcGISMap);
    }
});

5-6、地圖要素點擊事件

    //添加點擊事件
        MapViewTouchListener mMapViewTouchListener = new MapViewTouchListener(this, mMapView);
        mMapView.setOnTouchListener(mMapViewTouchListener);
        //添加覆蓋物
        addGraphicsOverlay();
class MapViewTouchListener extends DefaultMapViewOnTouchListener {

        public MapViewTouchListener(Context context, MapView mapView) {
            super(context, mapView);
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            // get the screen point where user tapped
            android.graphics.Point screenPoint = new android.graphics.Point((int) e.getX(), (int) e.getY());
            final ListenableFuture<List<IdentifyGraphicsOverlayResult>> overlaysAsync = mMapView.identifyGraphicsOverlaysAsync(screenPoint, 10.0, false, 2);
            overlaysAsync.addDoneListener(new Runnable() {
                @Override
                public void run() {
                    try {
                        List<IdentifyGraphicsOverlayResult> overlayResultList = overlaysAsync.get();
                        if (!overlayResultList.isEmpty() && overlayResultList.size() >= 0) {
                            //如果只取第一個點到的,就不用for循環了,直接get(0);如果需要取所有點擊到的覆蓋物,就可以用for循環
                               int size = overlayResultList.size();
                           if (size > 1) {
                                showTV.setText("你點到了 --" + size + "個元素");
                            } else {
                                showTV.setText("GISDemo");
                            }
//                            for (int i = 0; i < size; i++) {
                            List<Graphic> graphics = overlayResultList.get(0).getGraphics();
//                                List<Graphic> graphics = overlayResultList.get(i).getGraphics();
                            if (!graphics.isEmpty() && graphics.size() >= 0) {
                                Graphic graphic = graphics.get(0);//取點擊的第一個
                                Map<String, Object> map = graphic.getAttributes();
                                String hint = (String) map.get("hint");
                                Toast.makeText(getApplicationContext(), "你點到了 - " + hint, Toast.LENGTH_SHORT).show();
                            }
                        }

                    } catch (InterruptedException | ExecutionException ie) {
                        ie.printStackTrace();
                    }
                }
            });
            return super.onSingleTapConfirmed(e);
        }

    }

5-7、切換圖層時,地圖保持原位置

加上點擊事件限制是爲了防止用戶暴力點擊,因爲圖層加載需要時間;方法是獲取地圖當前中心點和縮放比,切換底圖後重新賦值

private long clickTime = 0;
...
 case R.id.layer_change:     //切換圖層需要加載地圖,不讓用戶切換的太快,防止暴力點擊
                if (System.currentTimeMillis() - clickTime > 1000) {
                    clickTime = System.currentTimeMillis();
                    //計算中心點
                    int measuredWidth = mMapView.getMeasuredWidth();
                    int measuredHeight = mMapView.getMeasuredHeight();
                    android.graphics.Point point = new android.graphics.Point(measuredWidth / 2, measuredHeight / 2);
                    Point centerDot = mMapView.screenToLocation(point);
                    double mapScale = mMapView.getMapScale();
                    //我們要切換底圖時候,僅需要給ArcGISMap類重新賦值一個底圖即可。
                    isStreetMap = !isStreetMap;
                    if (isStreetMap) {
                        String streetUrl = httpMethods.getMap_Street_URL();
                        loadBasemap(streetUrl);
                    } else {
                        String satelliteUrl = httpMethods.getMap_Satellite_URL();
                        loadBasemap(satelliteUrl);
                    }
                    if (centerDot == null)
                        centerDot = CoordinateFormatter.fromLatitudeLongitude(center, SpatialReferences.getWgs84());
                    mMapView.setViewpointCenterAsync(centerDot, mapScale);
                }
                break;
 //加載地圖,提成方法,方便切換底圖
    private void loadBasemap(String url) {
        //地圖服務端地址
        ArcGISTiledLayer tiledLayer = new ArcGISTiledLayer(url);
        tiledLayer.setMinScale(MinScale);  //控制縮小,數值越大,縮小倍數越大,看的範圍越廣
        tiledLayer.setMaxScale(MaxScale);   //控制放大,數值越小,放大倍數越高
        Basemap basemap = new Basemap(tiledLayer);
        mArcGISMap = new ArcGISMap(basemap);
        mMapView.setMap(mArcGISMap);
    }

6、完整代碼

寫完不放完整代碼,總感覺不太好。O(∩_∩)O哈哈哈~

public class MainActivity extends BaseActivity {
    @BindView(R.id.header_title)
    TextView headerTitle;
    @BindView(R.id.header_right_img)
    ImageView headerRightImg;
    @BindView(R.id.header_left_img)
    ImageView headerLeftImg;
    @BindView(mapView)
    MapView mMapView;
    @BindView(R.id.activity_main)
    RelativeLayout activityMain;
    private GraphicsOverlay pointGraphicOverlay;
    private GraphicsOverlay lineGraphicOverlay;

    private static double MinScale = 5000000;
    private static double MaxScale = 1000;
    private double CurrentScale = 9000;
    private double ScaleChange = 1000;        //每次放大或縮小的尺寸
    private ArcGISMap mArcGISMap;
    private String userId;
    private List<MapTaskPointBean> mapTaskPoints;
    private String center = "38.048,114.507250";  //石家莊中心
    private MyApplication instance;
    private boolean isStreetMap = true;
    private boolean isOffline;
    private String SDPath = Environment.getExternalStorageDirectory() + "/CableInspection/";
    private LocationManager locationManager;
    private String provider;
    private final String MAX = "+";
    private final String MIN = "-";
    private Location mLocation;
    private int taskType = -1;
    private int taskId = -1;
    private int objectTypeId = -1;
    private String taskName = "";
    private GraphicsOverlay overlayDevice;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ButterKnife.bind(this);
        instance = MyApplication.getInstance();
        UserBean userBean = instance.getUserInfo();
        userId = userBean == null ? "-1" : userBean.getUserId();
        isStreetMap = instance.getMapType();
        initArcGis();
        initView();
        hasCurrentLocation();//打開地圖時默認定位到當前位置
        initMap();
    }

    //可見的時候,地圖上的標識物要繪製
    @Override
    protected void onResume() {
        super.onResume();
        mMapView.resume();
        mMapView.getGraphicsOverlays().clear();
        if (overlayDevice != null) mMapView.getGraphicsOverlays().add(overlayDevice);
        if (mapTaskPoints != null) mapTaskPoints.clear();
        initData();
        //添加覆蓋物
        addGraphicsOverlay();
    }

    //獲取ArcGis的許可
    private void initArcGis() {
        ArcGISRuntimeEnvironment.setLicense("runtimelite,1000,rud4163659509,none,1JPJD4SZ8L4HC2EN0229");
    }

    private void initView() {
        headerTitle.setText(R.string.app_name_all);
        headerLeftImg.setVisibility(View.VISIBLE);
        headerRightImg.setVisibility(View.VISIBLE);
        headerLeftImg.setImageResource(R.mipmap.user_mag);
    }

    //在地圖上顯示當前任務的位置
    private void initData() {
        List<TaskList> taskLists = DataSupport.where("user_id =? and task_status =?",
                userId + "", MConstant.Task_Current + "").find(TaskList.class);
        if (taskLists != null && taskLists.size() > 0) {
            TaskList list = taskLists.get(0);
            int taskType = list.getTask_type();
            int taskId = list.getTask_id();
            //正序排序,以保證通道、本體在其他任務點的前面獲取
            List<TaskPoint> taskPoints = DataSupport
                    .where("user_id =? and task_id =? and task_type =?", userId + "", taskId + "", taskType + "")
                    .find(TaskPoint.class);
            if (taskPoints == null || taskPoints.size() <= 0) return;
            //任務點集合
            mapTaskPoints = new ArrayList<>();
            for (int i = 0; i < taskPoints.size(); i++) {
                TaskPoint taskPoint = taskPoints.get(i);
                int taskPointId = taskPoint.getTask_point_id();
                int typeId = taskPoint.getObject_type_id();
                String location = taskPoint.getTask_point_location();
                if (TextUtils.isEmpty(location)) return;
                String[] split = location.split(";");
                if (i == 0) center = split[0].replace(",", " ");  //用第一個任務點作爲當前地圖的中心點
                switch (typeId) {
                    case MConstant.ObjType_td:  //通道----管網
                        if (split.length > 1) {
                            List<Point> line = new ArrayList<>();
                            MapTaskPointBean mapTaskPointBean = new MapTaskPointBean();
                            for (int j = 0; j < split.length; j++) {
                                String dot = split[j];
                                String replace = dot.replace(",", " ");
                                Point lineDot = CoordinateFormatter.fromLatitudeLongitude(replace, SpatialReferences.getWgs84());
                                line.add(lineDot);
                            }
                            mapTaskPointBean.setTaskType(taskType);
                            mapTaskPointBean.setTaskId(taskId);
                            mapTaskPointBean.setTaskPointId(taskPointId);
                            mapTaskPointBean.setDot(false);//線
                            mapTaskPointBean.setLine(line);
                            mapTaskPoints.add(mapTaskPointBean);
                        }
                        break;
                    case MConstant.ObjType_gj:   //工井
                    case MConstant.ObjType_fhq:   //防火牆
                    case MConstant.ObjType_mhzz:   //滅火裝置
                        if (split.length > 0) {
                            MapTaskPointBean mapTaskPointBean = new MapTaskPointBean();
                            String dot = split[0];
                            String replace = dot.replace(",", " ");
                            if (i == taskPoints.size() / 2) {
                                center = replace;
                            }
                            Point point = CoordinateFormatter.fromLatitudeLongitude(replace, SpatialReferences.getWgs84());
                            mapTaskPointBean.setTaskType(taskType);
                            mapTaskPointBean.setTaskId(taskId);
                            mapTaskPointBean.setTaskPointId(taskPointId);
                            mapTaskPointBean.setDot(true);//點
                            mapTaskPointBean.setPoint(point);
                            mapTaskPoints.add(mapTaskPointBean);
                        }
                        break;
                    case MConstant.ObjType_bt:   //本體
                        break;
                }
            }
        }
    }

    private long clickTime = 0;

    @OnClick({R.id.header_left_img, R.id.header_right_img, R.id.map_xj_tv, R.id.map_xq_tv,
            R.id.layer_change, R.id.map_location, R.id.scale_max, R.id.scale_min})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.header_left_img:
                startActivity(new Intent(MainActivity.this, UserActivity.class));
                break;
            case R.id.header_right_img:
                startActivity(new Intent(MainActivity.this, SettingActivity.class));
                break;
            case R.id.map_xj_tv:
                Intent intentTaskXJList = new Intent(MainActivity.this, TaskListActivity.class);
                intentTaskXJList.putExtra(MConstant.taskList, MConstant.xjTask);
                startActivity(intentTaskXJList);
                break;
            case R.id.map_xq_tv:
                Intent intentTaskXQList = new Intent(MainActivity.this, TaskListActivity.class);
                intentTaskXQList.putExtra(MConstant.taskList, MConstant.xqTask);
                startActivity(intentTaskXQList);
                break;
            case R.id.layer_change:     //切換圖層需要加載地圖,不讓用戶切換的太快,防止暴力點擊
                if (!MyUtils.isNetWorkConnected(this)) {
                    new CenterHintToast(MainActivity.this, "你當前處於離線狀態,不能切換圖層");
                    return;
                }
                if (System.currentTimeMillis() - clickTime > 1000) {
                    clickTime = System.currentTimeMillis();
                    //計算中心點
                    int measuredWidth = mMapView.getMeasuredWidth();
                    int measuredHeight = mMapView.getMeasuredHeight();
                    android.graphics.Point point = new android.graphics.Point(measuredWidth / 2, measuredHeight / 2);
                    Point centerDot = mMapView.screenToLocation(point);
                    double mapScale = mMapView.getMapScale();
                    double rotation = mMapView.getMapRotation();
                    //重新賦值-loadBasemap方法中使用
                    if (centerDot != null){
                        center = CoordinateFormatter.toLatitudeLongitude(centerDot, CoordinateFormatter.LatitudeLongitudeFormat.DECIMAL_DEGREES, 4);
                    }
                    CurrentScale = mapScale;
                    //我們要切換底圖時候,僅需要給ArcGISMap類重新賦值一個底圖即可。
                    isStreetMap = !isStreetMap;
                    if (isStreetMap) {
                        String streetUrl = instance.getMapStreetURL();
                        loadBasemap(streetUrl);
                    } else {
                        String satelliteUrl = instance.getMapSatelliteURL();
                        loadBasemap(satelliteUrl);
                    }
                    mMapView.setViewpointRotationAsync(rotation);
                }
                break;
            case R.id.map_location:
                if (isOffline) return;
                hasCurrentLocation();
                break;
            case R.id.scale_max:  // + 運算
                changeScale(MAX);
                break;
            case R.id.scale_min: // - 運算
                changeScale(MIN);
                break;
        }
    }

    //改變地圖縮放比例--按圖層
    private void changeScale(String type) {
        TileInfoBean bean = instance.getTileInfoBean();
        if (bean == null) {
            new CenterHintToast(MainActivity.this, "獲取縮放比例失敗");
            return;
        }
        List<TileInfoBean.LodsBean> lods = bean.getLods();
        if (lods == null || lods.size() <= 0) return;
        double mapScale = mMapView.getMapScale();
        double fastScale = lods.get(0).getScale();
        double lastScale = lods.get(lods.size() - 1).getScale();
        if (mapScale>=fastScale&&MIN.equals(type))return;
        if (mapScale<=lastScale&&MAX.equals(type))return;
        for (int i = 0; i < lods.size() - 1; i++) {
            if (MAX.equals(type)) {  // + 運算(放大地圖、顯示區域變小)
                if (mapScale <= lods.get(i).getScale() && mapScale > lods.get(i+1).getScale()) {
                    CurrentScale = lods.get(i+1).getScale();
                }
            } else if (MIN.equals(type)) {  // - 運算(縮小地圖、顯示區域變大)
                if (mapScale < lods.get(i).getScale() && mapScale >= lods.get(i + 1).getScale()) {
                    CurrentScale = lods.get(i).getScale();
                }
            }
        }
        mMapView.setViewpointScaleAsync(CurrentScale);
    }

    //獲取定位的權限
    private void hasCurrentLocation() {
        locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        //獲取可用的位置提供器
        List<String> allProviders = locationManager.getAllProviders();
        if (allProviders.contains(LocationManager.GPS_PROVIDER)) {
            provider = LocationManager.GPS_PROVIDER;
        } else if (allProviders.contains(LocationManager.NETWORK_PROVIDER)) {
            provider = LocationManager.NETWORK_PROVIDER;
            new CenterHintToast(MainActivity.this, "爲了定位的準確性\n請開啓GPS定位服務");
        } else {
            new CenterHintToast(MainActivity.this, "請在 設置 中開啓定位服務");
            return;
        }
        //權限檢測
        String[] permissionStr = {Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION};
        if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                && ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(MainActivity.this, permissionStr, 111);
        } else {
            showLocation();
        }
    }

    //定位到當前點
    @SuppressLint("MissingPermission")
    private void showLocation() {
        mLocation = locationManager.getLastKnownLocation(provider);
        locationManager.requestLocationUpdates(provider, 1000, 0, new LocationListener() {
            @Override
            public void onLocationChanged(Location location) {
                mLocation = location;
                if (mLocation != null) {
                    drawLocationImg();
                    center = mLocation.getLatitude() + " " + mLocation.getLongitude();
                }
            }

            @Override
            public void onStatusChanged(String provider, int status, Bundle extras) {

            }

            @Override
            public void onProviderEnabled(String provider) {

            }

            @Override
            public void onProviderDisabled(String provider) {
                new CenterHintToast(MainActivity.this, "請在 設置 中開啓GPS定位服務");
            }
        });
        if (mLocation != null) {
            drawLocationImg();
            center = mLocation.getLatitude() + " " + mLocation.getLongitude();
        }
        //設置地圖中心點
        Point point = CoordinateFormatter.fromLatitudeLongitude(center, SpatialReferences.getWgs84());
        mMapView.setViewpointCenterAsync(point, CurrentScale);
    }

    //繪製當前點的圖標
    private void drawLocationImg() {
        String pointStr = mLocation.getLatitude() + " " + mLocation.getLongitude();
        Point point = CoordinateFormatter.fromLatitudeLongitude(pointStr, SpatialReferences.getWgs84());
        Graphic graphic = new Graphic(point);
        if (overlayDevice == null) {     //顯示自己當前位置
            overlayDevice = new GraphicsOverlay();
            BitmapDrawable drawable = (BitmapDrawable) getResources().getDrawable(R.drawable.device_location);
            final PictureMarkerSymbol pointSymbol = new PictureMarkerSymbol(drawable);
            SimpleRenderer pointRenderer = new SimpleRenderer(pointSymbol);
            overlayDevice.setRenderer(pointRenderer);
            overlayDevice.getGraphics().add(graphic);
            mMapView.getGraphicsOverlays().add(overlayDevice);
        } else {
            overlayDevice.getGraphics().remove(0);
            overlayDevice.getGraphics().add(graphic);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        switch (requestCode) {
            case 111:
                if (grantResults.length > 0) {   //有權限被拒絕
                    List<String> deniedPermissions = new ArrayList<>();
                    for (int i = 0; i < grantResults.length; i++) {
                        int grantResult = grantResults[i];
                        String permission = permissions[i];
                        if (grantResult != PackageManager.PERMISSION_GRANTED) {
                            deniedPermissions.add(permission);
                        }
                    }
                    if (deniedPermissions.isEmpty()) {   //全部授權成功
                        showLocation();
                    } else {
                        new CenterHintToast(MainActivity.this, "請在 設置-應用管理 中開啓此應用的定位權限。");
                    }
                }
                break;
            default:
                break;
        }
    }

    //加載地圖,提成方法,方便切換底圖,異步執行
    private void loadBasemap(final String url) {
   /*     //如果用戶處於離線狀態,不管之前的地圖狀態直接加載離線包
        if (isOffline) {
            String filename = MyUtils.Save_Path + "/" + MyUtils.File_name;
            if (!(new File(filename)).exists()) return;//防止閃退
            DBTiledLayer tiledLayer = DBTiledLayer.init(filename);
            tiledLayer.setMinScale(MinScale);  //控制縮小,數值越大,縮小倍數越大,看的範圍越廣
            tiledLayer.setMaxScale(MaxScale);   //控制放大,數值越小,放大倍數越高
            Basemap basemap = new Basemap(tiledLayer);
            mArcGISMap = new ArcGISMap(basemap);
        }*/
        new Thread(new Runnable() {
            @Override
            public void run() {
                new ReturnJson(url,mHandler);    //存地圖縮放比例
            }
        }).start();
    }

    //--獲取地圖配置信息成功
    @SuppressLint("HandlerLeak")
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what){
                case 13:
                    Bundle data = msg.getData();
                    String url = data.getString("url");
                    MinScale = data.getDouble("fastScale");
                    MaxScale = data.getDouble("lastScale");
                    ArcGISTiledLayer tiledLayer = new ArcGISTiledLayer(url);
                    tiledLayer.setMinScale(MinScale);  //控制縮小,數值越大,縮小倍數越大,看的範圍越廣
                    tiledLayer.setMaxScale(MaxScale);   //控制放大,數值越小,放大倍數越高
                    Basemap basemap = new Basemap(tiledLayer);
                    mArcGISMap = new ArcGISMap(basemap);
                    mMapView.setMap(mArcGISMap);
                    Point point = CoordinateFormatter.fromLatitudeLongitude(center, SpatialReferences.getWgs84());
                    mMapView.setViewpointCenterAsync(point, CurrentScale);
                    break;
            }
        }
    };

    private void initMap() {
        //加載地圖底圖
        if (isStreetMap) {
            String streetUrl = instance.getMapStreetURL();
            loadBasemap(streetUrl);
        } else {
            String satelliteUrl = instance.getMapSatelliteURL();
            loadBasemap(satelliteUrl);
        }
        //添加點擊事件
        MapViewTouchListener mMapViewTouchListener = new MapViewTouchListener(this, mMapView);
        mMapView.setOnTouchListener(mMapViewTouchListener);
    }

    //點擊地圖覆蓋物
    class MapViewTouchListener extends DefaultMapViewOnTouchListener {

        public MapViewTouchListener(Context context, MapView mapView) {
            super(context, mapView);
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            // get the screen point where user tapped
            android.graphics.Point screenPoint = new android.graphics.Point((int) e.getX(), (int) e.getY());
            final ListenableFuture<List<IdentifyGraphicsOverlayResult>> overlaysAsync = mMapView.identifyGraphicsOverlaysAsync(screenPoint, 20.0, false, 2);
            overlaysAsync.addDoneListener(new Runnable() {
                @Override
                public void run() {
                    try {
                        List<IdentifyGraphicsOverlayResult> overlayResultList = overlaysAsync.get();
                        if (!overlayResultList.isEmpty() && overlayResultList.size() >= 0) {
                            //如果只取第一個點到的,就不用for循環了,直接get(0);如果需要取所有點擊到的覆蓋物,就可以用for循環
                            int size = overlayResultList.size();
                            List<Graphic> graphics = overlayResultList.get(0).getGraphics();
                            if (!graphics.isEmpty() && graphics.size() >= 0) {
                                Graphic graphic = graphics.get(0);//取點擊的第一個
                                Map<String, Object> map = graphic.getAttributes();
                                int taskPointId = (int) map.get("taskPointId");
                                showPopupWindow(taskPointId);
                            }
                        }
                    } catch (InterruptedException | ExecutionException ie) {
                        ie.printStackTrace();
                    }
                }
            });
            return super.onSingleTapConfirmed(e);
        }
    }

    public void showPopupWindow(final int taskPointId) {
        View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.map_task_info_pop, null);
        final PopupWindow popupWindow = new PopupWindow(view, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, true);
        popupWindow.setTouchable(true);
        popupWindow.setAnimationStyle(R.style.popup_style);
        popupWindow.setBackgroundDrawable(getResources().getDrawable(R.drawable.shape_main_map_pop));
        popupWindow.showAsDropDown(headerTitle);
        TextView taskPointName = (TextView) view.findViewById(R.id.taskPoint_name);
        TextView taskPointLocation = (TextView) view.findViewById(R.id.taskPoint_location);
        TextView taskPointLeft = (TextView) view.findViewById(R.id.taskPoint_left);
        TextView taskPointRight = (TextView) view.findViewById(R.id.taskPoint_right);
        //取任務點的相關信息
        List<TaskList> taskLists = DataSupport.where("user_id =? and task_status =?",
                userId + "", MConstant.Task_Current + "").find(TaskList.class);
        if (taskLists != null && taskLists.size() > 0) {
            TaskList list = taskLists.get(0);
            taskType = list.getTask_type();
            taskId = list.getTask_id();
            taskName = list.getTask_name();
            List<TaskPoint> taskPoints = DataSupport
                    .where("user_id =? and task_id =? and task_type =? and task_point_id =?",
                            userId + "", taskId + "", taskType + "", taskPointId + "")
                    .find(TaskPoint.class);
            if (taskPoints != null && taskPoints.size() > 0) {
                TaskPoint point = taskPoints.get(0);
                objectTypeId = point.getObject_type_id();
                List<ObjectAttribute> attributes = DataSupport.where("user_id=? and task_type=? and task_id=? and task_point_id=?",
                        userId, taskType + "", taskId + "", taskPointId + "").find(ObjectAttribute.class);
                if (attributes != null && attributes.size() > 0) {
                    ObjectAttribute attribute = attributes.get(0);
                    String att_info2 = attribute.getAtt_info2();
                    String[] split = att_info2.split(":");
                    String att_info1 = attribute.getAtt_info1();
                    String[] split1 = att_info1.split(":");
                    if (split.length > 1 && split1.length > 1) {
                        taskPointName.setText(split[1] + ": " + split1[1]);
                    }
                    taskPointLocation.setText(attribute.getAtt_info11());
                }
            }
        }
        taskPointLeft.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //任務詳情
                Intent intent = new Intent(MainActivity.this, TaskInfoActivity.class);
                intent.putExtra("taskName", taskName);
                intent.putExtra("taskType", taskType);
                intent.putExtra("taskId", taskId);
                startActivity(intent);
            }
        });
        taskPointRight.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //缺陷錄入
                Intent intent = new Intent(MainActivity.this, TaskRecordActivity.class);
                intent.putExtra("objTypeId", objectTypeId);
                intent.putExtra("taskPointId", taskPointId);
                intent.putExtra("taskType", taskType);
                intent.putExtra("taskId", taskId);
                intent.putExtra("taskStatus", MConstant.Task_Current);
                startActivity(intent);
            }
        });
    }

    //添加點線覆蓋物
    private void addGraphicsOverlay() {
        if (mLocation != null) drawLocationImg();
        //-----------畫點-------------
        pointGraphicOverlay = new GraphicsOverlay();
        BitmapDrawable drawable0 = (BitmapDrawable) getResources().getDrawable(R.drawable.dot_map);
        final PictureMarkerSymbol pointSymbol0 = new PictureMarkerSymbol(drawable0);
        SimpleRenderer pointRenderer = new SimpleRenderer(pointSymbol0);
        pointGraphicOverlay.setRenderer(pointRenderer);
        //--------------畫線---------------
        lineGraphicOverlay = new GraphicsOverlay();
        SimpleLineSymbol lineSymbol = new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, R.color.colorPrimary, 3);
        SimpleRenderer lineRenderer = new SimpleRenderer(lineSymbol);
        lineGraphicOverlay.setRenderer(lineRenderer);
        //添加需要繪製的數據
        if (mapTaskPoints == null || mapTaskPoints.size() <= 0) return;
        for (MapTaskPointBean mapTaskPoint : mapTaskPoints) {
            boolean dot = mapTaskPoint.isDot();
            if (dot) {//畫點
                Map<String, Object> map = new HashMap<>();
                map.put("taskPointId", mapTaskPoint.getTaskPointId());
                Point point = mapTaskPoint.getPoint();
                Graphic graphic = new Graphic(point, map, pointSymbol0);
                pointGraphicOverlay.getGraphics().add(graphic);
            } else {//畫線
                PointCollection pointCollection = new PointCollection(SpatialReferences.getWgs84());
                Map<String, Object> map = new HashMap<>();
                map.put("taskPointId", mapTaskPoint.getTaskPointId());
                List<Point> line = mapTaskPoint.getLine();
                for (Point point : line) {
                    pointCollection.add(point);
                }
                Polyline polyline = new Polyline(pointCollection);
                Graphic graphic = new Graphic(polyline, map, lineSymbol);
                lineGraphicOverlay.getGraphics().add(graphic);
            }
        }

        // add graphics overlay to the MapView
        mMapView.getGraphicsOverlays().add(lineGraphicOverlay);
        mMapView.getGraphicsOverlays().add(pointGraphicOverlay);
        //設置地圖中心點
        Point point = CoordinateFormatter.fromLatitudeLongitude(center, SpatialReferences.getWgs84());
        mMapView.setViewpointCenterAsync(point, CurrentScale);
        //------------------閃爍---------------------
        handler.postDelayed(runnable, 1000);
    }

    private boolean isShow = true;
    private int recLen = 4;//設置閃爍次數
    Handler handler = new Handler();
    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            isShow = !isShow;
            recLen--;
            handler.postDelayed(runnable, 1000);
            //顯示或消失
            if (lineGraphicOverlay != null) lineGraphicOverlay.setVisible(isShow);
            if (pointGraphicOverlay != null) pointGraphicOverlay.setVisible(isShow);
            if (recLen <= 0) {
                recLen = 4;//重置時要和初始值一致
                Message message = new Message();
                message.what = 1;
                handlerStop.sendMessage(message);
            }
        }
    };

    final Handler handlerStop = new Handler() {
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case 1:
                    handler.removeCallbacks(runnable);
                    if (lineGraphicOverlay != null) lineGraphicOverlay.setVisible(true);
                    if (pointGraphicOverlay != null) pointGraphicOverlay.setVisible(true);
                    break;
            }
            super.handleMessage(msg);
        }

    };

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

    @Override
    protected void onDestroy() {
        super.onDestroy();
        handler.removeCallbacks(runnable);
    }

    /**
     * 連續點擊兩次退出應用,時間間隔不超過5s
     */
    private long exitTime = 0;

    @Override
    public void onBackPressed() {
        if (System.currentTimeMillis() - exitTime > 2000) {
            Toast.makeText(this, "再按一次退出程序", Toast.LENGTH_SHORT).show();
            exitTime = System.currentTimeMillis();
        } else {
            instance.setMapType(isStreetMap);
            ActivityCollector.finishAll();
        }
    }

}

8、補充

關於GIS地圖在android上的使用也可以看這個作者的系列博文,安卓智能地圖開發與實施系列,作者:大蝦盧
[ArcGIS for Android Runtime 100基礎操作](https://blog.csdn.net/bit_kaki/article/category/6509445

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章