欢迎加入QQ沟通交流群:186178114(群名:外业数据采集(GIS+GPS))
外业数据采集平台
在室外,通过平板或者手机接收GPS坐标,实时绘制点、线、面数据,以便为后续进行海域监测、土地确权、地图绘图提供有效数据和依据。
Android studio3.0.1+Arcgis for android 100.2.1+GPS
http://www.android-studio.org/index.php/download/hisversion
参考博客地址:https://www.cnblogs.com/xiadewang/p/7820377.html
Arcgis for android是ESRI公司专门为Android手机开发GIS地图软件的一套API,整合广泛的地图和GIS能力在线或离线,包括编辑,分析,地理编码,路由,网络地图管理,数据可视化,移动地图包和矢量平铺层。
https://developers.arcgis.com/android/latest/
http://www.cnblogs.com/gis-luq/p/4760370.html
外业数据采集平台采用的是Android最原生也是最基础的架构,可以理解为MVC,Controller即是Activity和Fragment,但是这两者掌握了Android系统中绝大多数的资源,并且在内部直接控制View,因此传统的Android App一般是以Activity和Fragment为核心,将网络模块,数据库管理模块,文件管理模块,常用工具类等分离成若干工具类包,供Activity和Fragment使用。由于项目不是很大,最后决定采用Android默认的架构,而没有采用MVP或者MVVM架构。
包含最基本的:放大、缩小、旋转、指北针、比例尺等基本功能。
支持导入内置SD卡和外置SD卡中.shp格式的数据。
1 private ShapefileFeatureTable featureLayerShapefile(String filePath, String fileType, boolean 2 isFullExtent, String dapFilePath, boolean isVisible, float opacity, boolean isCheckLayerName) { 3 if (!check(filePath, "SHP", isCheckLayerName)) { 4 return null; 5 } 6 ShapefileFeatureTable shapefileFeatureTable = new ShapefileFeatureTable(filePath); 7 shapefileFeatureTable.loadAsync(); 8 ShapefileFeatureTable finalShapefileFeatureTable = shapefileFeatureTable; 9 shapefileFeatureTable.addDoneLoadingListener(() -> { 10 if (finalShapefileFeatureTable.getLoadStatus() == LoadStatus.LOADED) { 11 // create a feature layer to display the shapefile 12 FeatureLayer featureLayer = new FeatureLayer(finalShapefileFeatureTable); 13 if ("".equals(dapFilePath)) { 14 featureLayer.setDescription(filePath); 15 } else { 16 featureLayer.setDescription(dapFilePath); 17 } 18 featureLayer.setVisible(isVisible); 19 mMapView.getMap().getOperationalLayers().add(featureLayer); 20 if (isFullExtent) { 21 Envelope envelope = LayerUtil.GetLayerFullExtend(featureLayer); 22 if (envelope != null && !envelope.isEmpty()) { 23 mMapView.setViewpointGeometryAsync(envelope 24 , LayerUtil.FullExtendPadding); 25 } 26 } 27 } else { 28 String error = "Shapefile feature table failed to load: " + 29 finalShapefileFeatureTable 30 .getLoadError().toString(); 31 Log.e(TAG, error); 32 } 33 }); 34 35 return shapefileFeatureTable; 36 }
支持导入内置SD卡和外置SD卡中.tif影像格式的数据。
1 private Layer loadLocalTif(ArcGISMap arcGISMap, String filePath, String extendName, boolean 2 isVisible, float opacity, boolean isCheckLayerName) { 3 if (!check(filePath, extendName, isCheckLayerName)) { 4 return null; 5 } 6 Raster raster = new Raster(filePath); 7 if (raster == null) 8 return null; 9 10 final RasterLayer rasterLayer = new RasterLayer(raster); 11 rasterLayer.setName("基础底图"); 12 rasterLayer.setDescription(filePath); 13 rasterLayer.setVisible(isVisible); 14 rasterLayer.setOpacity(opacity); 15 Basemap basemap = new Basemap(rasterLayer); 16 mMapView.getMap().setBasemap(basemap); 17 rasterLayer.addDoneLoadingListener(new Runnable() { 18 @Override 19 public void run() { 20 mMapView.setViewpointGeometryAsync(rasterLayer.getFullExtent(), LayerUtil 21 .FullExtendPadding); 22 mMapView.setViewpointScaleAsync(MyConfig.initialScale); 23 } 24 }); 25 mMapView.setViewpointScaleAsync(MyConfig.initialScale); 26 27 return rasterLayer; 28 }
将所有图层中所有要素范围作为地图的显示范围。
1 Envelope envelope = LayerUtil.GetFullExtend(mMapView); 2 if (envelope != null) { 3 mMapView.setViewpointGeometryAsync(envelope, LayerUtil.FullExtendPadding); 4 }
识别最上层的点、线、面要素。
1 @Override 2 public boolean onSingleTapConfirmed(MotionEvent e) { 3 android.graphics.Point screenPoint = new android.graphics.Point((int) e.getX(), (int) e.getY()); 4 final ListenableFuture<List<IdentifyLayerResult>> identifyFuture = mMapView 5 .identifyLayersAsync( 6 screenPoint, 20, false, 25); 7 identifyFuture.addDoneListener(new Runnable() { 8 @Override 9 public void run() { 10 try { 11 List<IdentifyLayerResult> identifyLayersResults = identifyFuture.get(); 12 for (IdentifyLayerResult identifyLayerResult : identifyLayersResults) { 13 if (identifyLayerResult.getElements().size() > 0) { 14 GeoElement topmostElement = identifyLayerResult.getElements().get(0); 15 if (topmostElement instanceof Feature) { 16 Feature identifiedFeature = (Feature) topmostElement; 17 LayerContent layerContent = identifyLayerResult.getLayerContent(); 18 String layerName = ""; 19 if (layerContent != null) 20 layerName = layerContent.getName(); 21 Map<String, Object> attrs = identifiedFeature.getAttributes(); 22 String[] itemArr = new String[items.size()]; 23 itemArr = items.toArray(itemArr); 24 showIdentifyInfo(itemArr, layerName); 25 break; 26 } 27 } 28 } 29 } catch (InterruptedException | ExecutionException ex) { 30 } 31 } 32 }); 33 34 return true; 35 }
包括图层顺序(置顶、置底、上移、下移)、图层标注(能标注该图层中多个字段)、图层样式(能改变图层中要素的颜色、宽度等信息)、图层移除、图层缩放到(缩放到选择图层的所有要素的范围)等功能;
1 private void loadLayers(boolean isToast,List<String> listChecked) { 2 if (mapView == null) { 3 return; 4 } 5 linearLayoutContent.removeAllViews(); 6 LayerList layers = mapView.getMap().getOperationalLayers(); 7 boolean layersLoaded = initialLayers(layers, 2,listChecked); 8 if(layersLoaded) 9 { 10 linearLayoutContent.addView(ViewUtil.AddDividerView(this)); 11 } 12 Basemap basemap = mapView.getMap().getBasemap(); 13 LayerList baseLayers = basemap.getBaseLayers(); 14 boolean baseLayersLoaded = initialLayers(baseLayers, 1,listChecked); 15 } 16 17 /** 18 * @param layers 19 * @param layerType //1:底图 2:操作类 20 * @return 21 */ 22 private boolean initialLayers(LayerList layers, int layerType,List<String> listChecked) { 23 if (layers == null || layers.size() == 0) { 24 return false; 25 } 26 for (int j = layers.size() - 1; j > -1; j--) { 27 Layer layer = layers.get(j); 28 boolean isChecked=false; 29 if(listChecked!=null) { 30 isChecked= listChecked.contains(layer.getName()); 31 } 32 addItem(layer, layerType,isChecked); 33 } 34 return true; 35 }
1 private void initialLineSymbol(SimpleLineSymbol lineSymbol) 2 { 3 rowFillColor.setVisibility(View.INVISIBLE); 4 rowIsFill.setVisibility(View.INVISIBLE); 5 txtSymbolColor.setText("线颜色:"); 6 txtSymbolSize.setText("线宽度:"); 7 txtType.setText("线样式:"); 8 9 btnSymbolColor.setBackgroundColor(lineSymbol.getColor()); 10 seekBarSymbolSize.seekBar.setProgress((int)lineSymbol.getWidth()); 11 initialColorPick(lineSymbol.getColor(),btnSymbolColor); 12 } 13 14 private SimpleLineSymbol setLineSymbol(SimpleLineSymbol lineSymbol) 15 { 16 lineSymbol.setColor(ViewUtil.GetButtonBackgoundColor(btnSymbolColor)); 17 lineSymbol.setWidth(seekBarSymbolSize.seekBar.getProgress()); 18 return lineSymbol; 19 }
1 private void initialListView() 2 { 3 FeatureLayer featureLayer=(FeatureLayer)layer; 4 List<Field> fieldList= featureLayer.getFeatureTable().getFields(); 5 List<LabelDefinition> labelDefinitionList= featureLayer.getLabelDefinitions(); 6 boolean isLabel=featureLayer.isLabelsEnabled(); 7 for (Field field :fieldList) 8 { 9 CheckBox checkBox=new CheckBox(this); 10 String fileName=field.getAlias(); 11 if(TextUtils.isEmpty(fileName)) 12 { 13 fileName=field.getName(); 14 } 15 checkBox.setText(fileName); 16 checkBox.setTag(field); 17 if(isLabel) 18 { 19 for (LabelDefinition labelDefinition:labelDefinitionList) 20 { 21 String jsonString =labelDefinition.toJson(); 22 try { 23 JSONObject jObject = new JSONObject(jsonString); 24 if(jObject!=null) 25 { 26 JSONObject jObjectItem= jObject.getJSONObject("labelExpressionInfo"); 27 if(jObjectItem!=null) 28 { 29 String expression=jObjectItem.getString("expression"); 30 String[] expressionContent=expression.split("\\."); 31 if(expressionContent.length>1) 32 { 33 String[] expressionField= expressionContent[1].split(";"); 34 if(expressionField.length>0) 35 { 36 if( expressionField[0].equals(field.getName())) 37 { 38 checkBox.setChecked(true); 39 } 40 } 41 } 42 } 43 } 44 } catch (JSONException e) { 45 46 } 47 } 48 } 49 50 listViewLabel.addView(checkBox); 51 } 52 } 53 54 public void btnLabelOk_Click(View view) 55 { 56 FeatureLayer featureLayer = (FeatureLayer) layer; 57 List<Field> checkedFieldList = getCheckedFields(); 58 featureLayer.getLabelDefinitions().clear(); 59 if(checkedFieldList!=null&&checkedFieldList.size()>0) { 60 for (Field field : checkedFieldList) { 61 LabelDefinition labelDefinition = LayerUtil.CreateFillLabelDefinition(field.getName()); 62 featureLayer.getLabelDefinitions().add(labelDefinition); 63 } 64 featureLayer.setLabelsEnabled(true); 65 } 66 else 67 { 68 featureLayer.setLabelsEnabled(false); 69 } 70 finish(); 71 }
通过手在屏幕中点击开始,双击停止,支持动态显示每段线的距离(以米为单位)。
1 public boolean onDoubleTap(MotionEvent point) { 2 3 if (geoType == GeometryType.POLYLINE)//绘制线 4 { 5 if (!lineGeometry.isSketchValid()) { 6 removeGraphic(pCurrGraphic); 7 } else { 8 updateGraphic(lineGeometry.toGeometry()); 9 } 10 PartCollection partCollection = lineGeometry.getParts(); 11 if (partCollection.size() == 0) { 12 return false; 13 } 14 Part part = partCollection.get(partCollection.size() - 1); 15 int count = part.getPointCount(); 16 if (count <= 2) { 17 return false; 18 } 19 double length = GeometryEngine.lengthGeodetic(lineGeometry.toGeometry(), new 20 LinearUnit(LinearUnitId.METERS), GeodeticCurveType.GEODESIC); 21 Graphic lengthGriphic = new Graphic(ptCurrent, getTextSymbol(getFormatString(length, 22 2, "米"), TextSymbol.HorizontalAlignment.RIGHT, TextSymbol.VerticalAlignment 23 .TOP)); 24 drawLayer.getGraphics().add(lengthGriphic); 25 26 } 27 28 }
通过手在屏幕中点击开始,双击停止,会形成一个面要素,并显示长度和面积(以米为单位)。
1 public boolean onDoubleTap(MotionEvent point) { 2 3 if (!polygonGeometry.isSketchValid()) { 4 removeGraphic(pCurrGraphic); 5 return true; 6 } else { 7 updateGraphic(polygonGeometry.toGeometry()); 8 } 9 10 double area = GeometryEngine.areaGeodetic(polygonGeometry.toGeometry(), new AreaUnit 11 (AreaUnitId.SQUARE_METERS), GeodeticCurveType.GEODESIC); 12 Graphic areaGriphic = new Graphic(ptCurrent, getTextSymbol(getFormatString(area, 2, 13 "平方米"), TextSymbol.HorizontalAlignment.LEFT, TextSymbol.VerticalAlignment 14 .BOTTOM)); 15 drawLayer.getGraphics().add(areaGriphic); 16 17 double length = GeometryEngine.lengthGeodetic(polygonGeometry.toPolyline(), new 18 LinearUnit(LinearUnitId.METERS), GeodeticCurveType.GEODESIC); 19 Point ptShift = mapView.screenToLocation(new android.graphics.Point(Math.round(point 20 .getX()), Math.round(point.getY()) + 30)); 21 Graphic lengthGriphic = new Graphic(ptShift, getTextSymbol(getFormatString(length, 22 2, "米"), TextSymbol.HorizontalAlignment.RIGHT, TextSymbol.VerticalAlignment 23 .TOP)); 24 drawLayer.getGraphics().add(lengthGriphic); 25 26 }
清除上面距离测量或者面积测量的图形要素。
1 if (mearsureGraphicsOveray != null) { 2 mearsureGraphicsOveray.getGraphics().clear(); 3 }
点击此按钮,将GPS最近一次接受点的位置置于地图中心位置,并增加图标。
1 public boolean startGPSCurrent(boolean isRequestUpdate) { 2 if (GPSLocationManager.gpsLocationManager == null) { 3 GPSLocationManager.gpsLocationManager = GPSLocationManager.getInstances 4 (MainActivity 5 .this); 6 } 7 if (currentLocationListener == null) { 8 currentLocationListener = new MyGPSCurrentLocationListener(); 9 } 10 if (isRequestUpdate) 11 GPSLocationManager.gpsLocationManager.stop(2); 12 //开启定位 13 GPSLocationManager.gpsLocationManager.start(currentLocationListener, 14 isRequestUpdate, 100, 2); 15 16 return true; 17 } 18 19 20 21 class MyGPSCurrentLocationListener implements GPSLocationListener { 22 @Override 23 public void UpdateLocation(Location location) { 24 if (location != null) { 25 LocationUtil.AddOrUpdateLocation(MainActivity.this, location, 26 locationGraphicsOveray, 27 spatialReference, mMapView); 28 if (GPSLocationManager.gpsLocationManager != null) { 29 GPSLocationManager.gpsLocationManager.stop(2); 30 } 31 } 32 } 33 34 }
将采集到的点、线、面数据导出成.shp格式数据,并支持删除要素功能。
1 public void initialGridData(ShapefileFeatureTable shapefileFeatureTable, GridView gridView, 2 int gridType, Context context, boolean isDisplayToastInf) { 3 List<Field> fileds = shapefileFeatureTable.getFields(); 4 Map<String, Object> gridTitle = new HashMap<String, Object>(); 5 6 for (String field : LayerUtil.mFields) { 7 String key = field; 8 keysList.add(key); 9 String alias = LayerUtil.GetAliasNameByFieldName(key); 10 gridTitle.put(key, alias); 11 } 12 gridDataList.add(gridTitle); 13 QueryParameters qParameters = new QueryParameters(); 14 String whereClause = "1=1"; 15 qParameters.setWhereClause(whereClause); 16 final ListenableFuture<FeatureQueryResult> future = shapefileFeatureTable 17 .queryFeaturesAsync(qParameters); 18 FeatureQueryResult result = null; 19 try { 20 result = future.get(); 21 Iterator<Feature> features = result.iterator(); 22 while (features.hasNext()) { 23 Feature feature = features.next(); 24 Map<String, Object> attrs = feature.getAttributes(); 25 26 Map<String, Object> dd = new HashMap<String, Object>(); 27 for (String field : LayerUtil.mFields) { 28 dd.put(field, String.valueOf(attrs.get(field))); 29 } 30 gridDataList.add(dd); 31 } 32 } catch { 33 34 } 35 String[] keys = keysList.toArray(new String[keysList.size()]); 36 GridViewAdapter adapter = new GridViewAdapter(context, gridDataList); 37 gridView.setAdapter(adapter); 38 }
设置GPS接收坐标间隔以及地图经纬度显示格式。
1 @Override 2 public boolean onPreferenceChange(Preference preference, Object value) { 3 String stringValue = value.toString(); 4 if (preference instanceof ListPreference) { 5 ListPreference listPreference = (ListPreference) preference; 6 if (listPreference.getKey().equals(updateMapFormatKey)) { 7 PreferencesUtil.setPrefString(this.getActivity(), updateMapFormatKey, String.valueOf(stringValue)); 8 } else if (listPreference.getKey().equals(updateCollectIntervalKey)) { 9 PreferencesUtil.setPrefString(this.getActivity(), updateCollectIntervalKey, String.valueOf(stringValue)); 10 MyConfig.mMinTime=Long.parseLong(stringValue); 11 } 12 } 13 14 return true; 15 }
拍取外业测量点的实况,并在照片上增加 经纬度、备注信息,信息录入完毕后,保存即可,为后续数据处理增加依据。
1 public void takePhoto(Activity activity) { 2 int currentapiVersion = android.os.Build.VERSION.SDK_INT; 3 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 4 if (hasSdcard()) { 5 SimpleDateFormat timeStampFormat = new SimpleDateFormat( 6 "yyyy_MM_dd_HH_mm_ss"); 7 String filename = timeStampFormat.format(new Date()); 8 File tempFile = new File(FileUtils.GetDefaultPath(this) + "/" + MyConfig.OutCameraDir, 9 filename + ".jpg"); 10 imageView.setTag(tempFile); 11 if (currentapiVersion < 24) { 12 imageUri = Uri.fromFile(tempFile); 13 intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); 14 } else { 15 ContentValues contentValues = new ContentValues(1); 16 contentValues.put(MediaStore.Images.Media.DATA, tempFile.getAbsolutePath()); 17 imageUri = activity.getContentResolver().insert(MediaStore.Images.Media 18 .EXTERNAL_CONTENT_URI, contentValues); 19 intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri); 20 } 21 } 22 }
根据GPS坐标,实时生成点要素,并随位置变化而变化地图中心点,点击 “结束”会弹出属性保存界面;
根据GPS坐标,实时生成线要素,并随位置变化而变化地图中心点,点击 “结束”会弹出属性保存界面;
根据GPS坐标,实时生成面要素,并随位置变化而变化地图中心点,点击 “结束”会弹出属性保存界面;
1 private void addOrUpdateGeometry(Location point ) { 2 if (point == null || point.isEmpty() || point.getX() == 0 || point.getY() == 0) 3 return; 4 5 6 switch (collectGeometryType) { 7 case POINT: 8 if (collectPointTable == null) { 9 return; 10 } 11 collectFeatures.add(pointF); 12 toastGpsInfo("点采集数据", "", collectFeatures.size()); 13 break; 14 case POLYLINE: 15 if (collectLineTable == null) { 16 return; 17 } 18 lineGeometry.addPoint(point); 19 collectPoints.add(point); 20 if (lineGeometry.isSketchValid()) { 21 if (polylineFeature == null) { 22 polylineFeature = collectLineTable.createFeature(); 23 polylineFeature.setGeometry(lineGeometry.toGeometry()); 24 collectLineTable.addFeatureAsync(polylineFeature); 25 } else { 26 polylineFeature.setGeometry(lineGeometry.toGeometry()); 27 collectLineTable.updateFeatureAsync(polylineFeature); 28 } 29 } 30 toastGpsInfo("线采集数据", "", collectPoints.size()); 31 break; 32 case POLYGON: 33 if (collectAreaTable == null) { 34 return; 35 } 36 polygonGeometry.addPoint(point); 37 collectPoints.add(point); 38 if (polygonGeometry.isSketchValid()) { 39 if (polygonFeature == null) { 40 polygonFeature = collectAreaTable.createFeature(); 41 polygonFeature.setGeometry(polygonGeometry.toGeometry()); 42 collectAreaTable.addFeatureAsync(polygonFeature); 43 } else { 44 polygonFeature.setGeometry(polygonGeometry.toGeometry()); 45 collectAreaTable.updateFeatureAsync(polygonFeature); 46 } 47 } 48 toastGpsInfo("面采集数据", "", collectPoints.size()); 49 break; 50 } 51 }
若有GPS坐标发生偏移,可以根据此功能进行撤销,一直可以撤销到第一个采集到的点。
1 private boolean deleteFirstFeature(ShapefileFeatureTable shapefileFeatureTable, Feature 2 defaultFeature) { 3 if (shapefileFeatureTable == null) 4 return false; 5 if (shapefileFeatureTable.getTotalFeatureCount() == 1) { 6 if (defaultFeature == null) 7 return false; 8 String delFid = ""; 9 Map<String, Object> item = defaultFeature.getAttributes(); 10 if (item != null) { 11 delFid = String.valueOf(item.get(CollectEidtActivity.attrFID)); 12 } 13 QueryParameters qParameters = new QueryParameters(); 14 String whereClause = CollectEidtActivity.attrFID + "=" + delFid; 15 qParameters.setReturnGeometry(true); 16 qParameters.setWhereClause(whereClause); 17 18 final ListenableFuture<FeatureQueryResult> featuresResult = shapefileFeatureTable 19 .queryFeaturesAsync(qParameters); 20 21 FeatureQueryResult features; 22 try { 23 features = featuresResult.get(); 24 if (!features.iterator().hasNext()) { 25 return false; 26 } 27 shapefileFeatureTable 28 .deleteFeaturesAsync(features).get(); 29 return true; 30 } catch (Exception ex) { 31 return false; 32 } 33 } 34 35 return false; 36 }
欢迎加入QQ沟通交流群:186178114(群名:外业数据采集(GIS+GPS)) 欢迎各位前来围观,提出建设性意见,谢谢!
外业数据采集平台(GPS+Android Studio+Arcgis for android 100.2.1)
原文:https://www.cnblogs.com/InProsperity/p/Android_GIS.html