新项目中需要用到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基础操作]()