ArcGIS for Android 地图图文查询
阅读原文时间:2023年07月09日阅读:2

ArcGIS for Android 地图图文查询

1.1. 创建新工程

  • 新建一个空活动项目

  • 选择语言、平台,修改命名等

1.2. 添加ArcGIS SDK

  • build.gradle (Project: <project name>)添加

    maven {
        url 'https://esri.jfrog.io/artifactory/arcgis'
    }
  • build.gradle (Module: <module name>)添加

    implementation 'com.esri.arcgisruntime:arcgis-android:100.10.0'
  • Gradle更新:Sync Project with Gradle Files

  • AndroidManifest.xml添加

    //网络权限
    <uses-permission android:name="android.permission.INTERNET" />
    //use a MapView (2D) require at least OpenGL ES 2.x:
    <uses-feature android:glEsVersion="0x00020000" android:required="true" />
  • appdbuild.gradle(Module:app)的android部分指定Java版本

    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }

1.3. 添加MapView地图控件

  • 修改activity_main.xml,替换TextView

        <com.esri.arcgisruntime.mapping.view.MapView
                android:id="@+id/mapView"
                android:layout_height="fill_parent"
                android:layout_width="fill_parent"
                tools:ignore="MissingConstraints">
        </com.esri.arcgisruntime.mapping.view.MapView>

2.1.定义变量

定义相关变量

    private MapView mMapView;
    private Callout mCallout;
    private ServiceFeatureTable mServiceFeatureTable;

2.2.添加在线图层

通过新建ServiceFeatureTable实例添加在线图层

        mServiceFeatureTable =new ServiceFeatureTable(getResources().getString(R.string.us_daytime_population_url));
        mFeatureLayer=new FeatureLayer(mServiceFeatureTable);
        mFeatureLayer.setOpacity(0.8f);
        mFeatureLayer.setMaxScale(10000);

        SimpleLineSymbol lineSymbol=new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.BLACK, 1);
        SimpleFillSymbol fillSymbol=new SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, Color.YELLOW, lineSymbol);
        mFeatureLayer.setRenderer(new SimpleRenderer(fillSymbol));

        map.getOperationalLayers().add(mFeatureLayer);

        mMapView.setViewpointCenterAsync(new Point(-11000000,5000000, SpatialReferences.getWebMercator()),100000000);
        mCallout=mMapView.getCallout();

2.3.添加点击(touch)监听

       mMapView.setOnTouchListener(new DefaultMapViewOnTouchListener(this,mMapView){
            @Override
            public boolean onSingleTapConfirmed(MotionEvent e) {
                if (mCallout.isShowing()){
                    mCallout.dismiss();
                }
                final android.graphics.Point screenPoint =new android.graphics.Point(Math.round(e.getX()),Math.round(e.getY()));
                int tolerance = 10;
                final ListenableFuture<IdentifyLayerResult> identifyLayerResultListenableFuture=
                        mMapView.identifyLayerAsync(mFeatureLayer,screenPoint,tolerance,false,1);
                identifyLayerResultListenableFuture.addDoneListener(()->{
                    try {
                        IdentifyLayerResult identifyLayerResult=identifyLayerResultListenableFuture.get();
                        TextView calloutContent = new TextView(getApplicationContext());
                        calloutContent.setTextColor(Color.BLACK);
                        calloutContent.setSingleLine(false);
                        calloutContent.setVerticalScrollBarEnabled(true);
                        calloutContent.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
                        calloutContent.setMovementMethod(new ScrollingMovementMethod());
                        calloutContent.setLines(identifyLayerResult.getElements().get(0).getAttributes().size());
                        for (GeoElement element:identifyLayerResult.getElements()){
                            Feature feature =(Feature) element;
                            Map<String,Object> attr =feature.getAttributes();
                            Set<String> keys=attr.keySet();
                            for (String key:keys){
                                Object value =attr.get(key);
                                if (value instanceof GregorianCalendar){
                                    SimpleDateFormat simpleDateFormat=new SimpleDateFormat("dd-MMM-yyyy", Locale.US);
                                    value=simpleDateFormat.format(((GregorianCalendar) value).getTime());
                                }
                                calloutContent.append(key+" | "+value+"\n");
                            }
                            Envelope envelope=feature.getGeometry().getExtent();
                            mMapView.setViewpointGeometryAsync(envelope,10);
                            mCallout.setLocation(envelope.getCenter());
                            mCallout.setContent(calloutContent);
                            mCallout.show();
                        }
                    }catch (Exception e1){
                        Log.e(getResources().getString(R.string.app_name),"Select feature fail : "+e1.getMessage());
                    }
                });

                return super.onSingleTapConfirmed(e);
            }

        });

2.4.重写onPause()onResume()onDestroy()函数

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

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

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

2.5.编译测试

3.1.定义变量

定义相关变量

    private MapView mMapView;
    private ServiceFeatureTable mServiceFeatureTable;
    private FeatureLayer mFeatureLayer;

3.2.添加在线图层

通过新建ServiceFeatureTable实例添加在线图层

        mServiceFeatureTable =new ServiceFeatureTable(getResources().getString(R.string.us_daytime_population_url));
        mFeatureLayer=new FeatureLayer(mServiceFeatureTable);
        mFeatureLayer.setOpacity(0.8f);
        mFeatureLayer.setMaxScale(10000);

        SimpleLineSymbol lineSymbol=new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.BLACK, 1);
        SimpleFillSymbol fillSymbol=new SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, Color.YELLOW, lineSymbol);
        mFeatureLayer.setRenderer(new SimpleRenderer(fillSymbol));

        map.getOperationalLayers().add(mFeatureLayer);

        mMapView.setViewpointCenterAsync(new Point(-11000000,5000000, SpatialReferences.getWebMercator()),100000000);

3.3.添加搜索框

res文件夹下添加searchable.xml

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
            android:label="@string/app_name"
            android:hint="@string/search_hint" >
</searchable>

menu文件夹下创建menu_main.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/tools"
      tools:context=".MainActivity">

    <item
            android:id="@+id/action_search"
            android:title="@string/action_search"
            app:actionViewClass="androidx.appcompat.widget.SearchView"
            app:showAsAction="ifRoom" />

</menu>

AndriodManifest.xml中的application中添加

        <activity
                android:name=".MainActivity"
                android:label="@string/app_name"
                android:launchMode="singleTop">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.SEARCH" />
            </intent-filter>

            <meta-data
                    android:name="android.app.searchable"
                    android:resource="@xml/searchable" />
        </activity>

3.4.重写onCreateOptionsMenu()函数

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        // get the SearchView and set the searchable configuration
        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
        // assumes current activity is the searchable activity
        searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        searchView.setIconifiedByDefault(false);
        return true;
    }

3.5.重写onNewIntent()函数

    @Override
    protected void onNewIntent(Intent intent) {
        setIntent(intent);
        if (Intent.ACTION_SEARCH.equals(intent.getAction())){
            String searchString=intent.getStringExtra(SearchManager.QUERY);
            searchForState(searchString);
        }
    }

    private void searchForState(String searchString) {
        mFeatureLayer.clearSelection();

        QueryParameters query = new QueryParameters();
        query.setWhereClause(searchString.toUpperCase());
        final ListenableFuture<FeatureQueryResult> future = mServiceFeatureTable.queryFeaturesAsync(query);
        future.addDoneListener(()->{
            try {
                FeatureQueryResult result=future.get();
                Iterator<Feature> resultIterator=result.iterator();
                int size=0;
                for (int i=0;resultIterator.hasNext();i++){
                    Feature feature =resultIterator.next();
                    Envelope envelope=feature.getGeometry().getExtent();
                    mMapView.setViewpointGeometryAsync(envelope,10);
                    mFeatureLayer.selectFeature(feature);
                    size++;
                }
                if (size>0){
                    Toast.makeText(this,"Found : "+size+"  record(s)",Toast.LENGTH_LONG).show();
                } else {
                    Toast.makeText(this,"No states found with name: "+searchString,Toast.LENGTH_LONG).show();
                }

            }catch (Exception e){
                String error = "Feature search failed for: " + searchString + ". Error: " + e.getMessage();
                Toast.makeText(this, error, Toast.LENGTH_LONG).show();
                Log.e("Search for states error :", error);
            }
        });
    }

3.6.编译测试

4.1.资源文件

strings.xml文件:

<resources>
    <string name="app_name">EX04</string>
    <string name="sample_service_url">https://sampleserver6.arcgisonline.com/arcgis/rest/services/Recreation/FeatureServer/0</string>
    <string name="API_KEY">YOU_ArcGIS_API_KEY</string>
    <string name="us_daytime_population_url">https://services.arcgis.com/jIL9msH9OI208GCb/arcgis/rest/services/USA_Daytime_Population_2016/FeatureServer/0</string>
    <string name="action_search">Search</string>
    <string name="search_hint">Type search opinions</string>
</resources>

searchable.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<searchable xmlns:android="http://schemas.android.com/apk/res/android"
            android:label="@string/app_name"
            android:hint="@string/search_hint" >
</searchable>

menu_main.xml文件:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:app="http://schemas.android.com/apk/res-auto"
      xmlns:tools="http://schemas.android.com/tools"
      tools:context=".MainActivity">

    <item
            android:id="@+id/action_search"
            android:title="@string/action_search"
            app:actionViewClass="androidx.appcompat.widget.SearchView"
            app:showAsAction="ifRoom" />

</menu>

activity_main.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    <com.esri.arcgisruntime.mapping.view.MapView
            android:id="@+id/mapView"
            android:layout_height="fill_parent"
            android:layout_width="fill_parent"
            tools:ignore="MissingConstraints">
    </com.esri.arcgisruntime.mapping.view.MapView>

</androidx.constraintlayout.widget.ConstraintLayout>

AndroidManifest.xml文件:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.example.ex04">

    <uses-permission android:name="android.permission.INTERNET"/>

    <uses-feature
            android:glEsVersion="0x00020000"
            android:required="true"/>

    <application
            android:allowBackup="true"
            android:icon="@mipmap/ic_launcher"
            android:label="@string/app_name"
            android:roundIcon="@mipmap/ic_launcher_round"
            android:supportsRtl="true"
            android:theme="@style/AppTheme">

        <activity
                android:name=".MainActivity"
                android:label="@string/app_name"
                android:launchMode="singleTop">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.SEARCH" />
            </intent-filter>

            <meta-data
                    android:name="android.app.searchable"
                    android:resource="@xml/searchable" />
        </activity>
    </application>

</manifest>

4.2.代码文件

MainActivity.java文件:

package com.example.ex04;

import android.app.DownloadManager;
import android.app.SearchManager;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.text.method.ScrollingMovementMethod;
import android.util.Log;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import androidx.appcompat.widget.SearchView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import com.esri.arcgisruntime.ArcGISRuntimeEnvironment;
import com.esri.arcgisruntime.concurrent.ListenableFuture;
import com.esri.arcgisruntime.data.Feature;
import com.esri.arcgisruntime.data.FeatureQueryResult;
import com.esri.arcgisruntime.data.QueryParameters;
import com.esri.arcgisruntime.data.ServiceFeatureTable;
import com.esri.arcgisruntime.geometry.Envelope;
import com.esri.arcgisruntime.geometry.Point;
import com.esri.arcgisruntime.geometry.SpatialReference;
import com.esri.arcgisruntime.geometry.SpatialReferences;
import com.esri.arcgisruntime.layers.FeatureLayer;
import com.esri.arcgisruntime.mapping.ArcGISMap;
import com.esri.arcgisruntime.mapping.BasemapStyle;
import com.esri.arcgisruntime.mapping.GeoElement;
import com.esri.arcgisruntime.mapping.Viewpoint;
import com.esri.arcgisruntime.mapping.view.Callout;
import com.esri.arcgisruntime.mapping.view.DefaultMapViewOnTouchListener;
import com.esri.arcgisruntime.mapping.view.IdentifyLayerResult;
import com.esri.arcgisruntime.mapping.view.MapView;
import com.esri.arcgisruntime.symbology.SimpleFillSymbol;
import com.esri.arcgisruntime.symbology.SimpleLineSymbol;
import com.esri.arcgisruntime.symbology.SimpleRenderer;

import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.FutureTask;

public class MainActivity extends AppCompatActivity {

    private MapView mMapView;
    private Callout mCallout;
    private ServiceFeatureTable mServiceFeatureTable;
    private FeatureLayer mFeatureLayer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ArcGISRuntimeEnvironment.setApiKey(getResources().getString(R.string.API_KEY));

        mMapView=findViewById(R.id.mapView);
        final ArcGISMap map =new ArcGISMap(BasemapStyle.ARCGIS_TOPOGRAPHIC);
        mMapView.setMap(map);
//        mMapView.setViewpoint(new Viewpoint(34.057386,-117.191455,10000000));
//        mCallout=mMapView.getCallout();
//        mServiceFeatureTable=new ServiceFeatureTable(getResources().getString(R.string.sample_service_url));
//        final FeatureLayer featureLayer=new FeatureLayer(mServiceFeatureTable);
//        map.getOperationalLayers().add(featureLayer);
        mServiceFeatureTable =new ServiceFeatureTable(getResources().getString(R.string.us_daytime_population_url));
        mFeatureLayer=new FeatureLayer(mServiceFeatureTable);
        mFeatureLayer.setOpacity(0.8f);
        mFeatureLayer.setMaxScale(10000);

        SimpleLineSymbol lineSymbol=new SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.BLACK, 1);
        SimpleFillSymbol fillSymbol=new SimpleFillSymbol(SimpleFillSymbol.Style.SOLID, Color.YELLOW, lineSymbol);
        mFeatureLayer.setRenderer(new SimpleRenderer(fillSymbol));

        map.getOperationalLayers().add(mFeatureLayer);

        mMapView.setViewpointCenterAsync(new Point(-11000000,5000000, SpatialReferences.getWebMercator()),100000000);
        mCallout=mMapView.getCallout();
        mMapView.setOnTouchListener(new DefaultMapViewOnTouchListener(this,mMapView){
            @Override
            public boolean onSingleTapConfirmed(MotionEvent e) {
                if (mCallout.isShowing()){
                    mCallout.dismiss();
                }
                final android.graphics.Point screenPoint =new android.graphics.Point(Math.round(e.getX()),Math.round(e.getY()));
                int tolerance = 10;
                final ListenableFuture<IdentifyLayerResult> identifyLayerResultListenableFuture=
                        mMapView.identifyLayerAsync(mFeatureLayer,screenPoint,tolerance,false,1);
                identifyLayerResultListenableFuture.addDoneListener(()->{
                    try {
                        IdentifyLayerResult identifyLayerResult=identifyLayerResultListenableFuture.get();
                        TextView calloutContent = new TextView(getApplicationContext());
                        calloutContent.setTextColor(Color.BLACK);
                        calloutContent.setSingleLine(false);
                        calloutContent.setVerticalScrollBarEnabled(true);
                        calloutContent.setScrollBarStyle(View.SCROLLBARS_INSIDE_INSET);
                        calloutContent.setMovementMethod(new ScrollingMovementMethod());
                        calloutContent.setLines(identifyLayerResult.getElements().get(0).getAttributes().size());
                        for (GeoElement element:identifyLayerResult.getElements()){
                            Feature feature =(Feature) element;
                            Map<String,Object> attr =feature.getAttributes();
                            Set<String> keys=attr.keySet();
                            for (String key:keys){
                                Object value =attr.get(key);
                                if (value instanceof GregorianCalendar){
                                    SimpleDateFormat simpleDateFormat=new SimpleDateFormat("dd-MMM-yyyy", Locale.US);
                                    value=simpleDateFormat.format(((GregorianCalendar) value).getTime());
                                }
                                calloutContent.append(key+" | "+value+"\n");
                            }
                            Envelope envelope=feature.getGeometry().getExtent();
                            mMapView.setViewpointGeometryAsync(envelope,10);
                            mCallout.setLocation(envelope.getCenter());
                            mCallout.setContent(calloutContent);
                            mCallout.show();
                        }
                    }catch (Exception e1){
                        Log.e(getResources().getString(R.string.app_name),"Select feature fail : "+e1.getMessage());
                    }
                });

                return super.onSingleTapConfirmed(e);
            }

        });
    }

    @Override
    protected void onNewIntent(Intent intent) {
        setIntent(intent);
        if (Intent.ACTION_SEARCH.equals(intent.getAction())){
            String searchString=intent.getStringExtra(SearchManager.QUERY);
            searchForState(searchString);
        }
    }

    private void searchForState(String searchString) {
        mFeatureLayer.clearSelection();

        QueryParameters query = new QueryParameters();
        query.setWhereClause(searchString.toUpperCase());
        final ListenableFuture<FeatureQueryResult> future = mServiceFeatureTable.queryFeaturesAsync(query);
        future.addDoneListener(()->{
            try {
                FeatureQueryResult result=future.get();
                Iterator<Feature> resultIterator=result.iterator();
                int size=0;
                for (int i=0;resultIterator.hasNext();i++){
                    Feature feature =resultIterator.next();
                    Envelope envelope=feature.getGeometry().getExtent();
                    mMapView.setViewpointGeometryAsync(envelope,10);
                    mFeatureLayer.selectFeature(feature);
                    size++;
                }
                if (size>0){
                    Toast.makeText(this,"Found : "+size+"  record(s)",Toast.LENGTH_LONG).show();
                } else {
                    Toast.makeText(this,"No states found with name: "+searchString,Toast.LENGTH_LONG).show();
                }

            }catch (Exception e){
                String error = "Feature search failed for: " + searchString + ". Error: " + e.getMessage();
                Toast.makeText(this, error, Toast.LENGTH_LONG).show();
                Log.e("Search for states error :", error);
            }
        });
    }
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        // get the SearchView and set the searchable configuration
        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
        SearchView searchView = (SearchView) menu.findItem(R.id.action_search).getActionView();
        // assumes current activity is the searchable activity
        searchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
        searchView.setIconifiedByDefault(false);
        return true;
    }

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

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

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

}