新項目中需要用到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、簡單使用
明白相關資料和工具的地址之後,我們就可以開始開發了。
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)