ArcgisRuntime Android has been used in the project recently, so I plan to make a summary, which not only deepens my impression of this part of knowledge, but also facilitates the reference of students who are not familiar with it. The version used in this article is ArcGIS – Android :100.6.0′, the example part both refer to the official documents and API introduction, but also refer to the column of always make people want to kill me, of course, more or some of their own experience. Let’s start with the introduction
Arcgis
An introduction to the sample- Locate relevant
- Map editor
- Common interface
- Map access
- 3 d map
- Hot like figure
A,Arcgis
An introduction to the sample
layout
<? xml version="1.0" encoding="utf-8"? > <FrameLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.vincent.arcgisdemo.ui.MainActivity">
<com.esri.arcgisruntime.mapping.view.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ffffff"/>
</FrameLayout>
Copy the code
code
class MainActivity :AppCompatActivity(){
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main) EasyAndroid.init(this) val levelOfDetail = 16 val map = ArcGISMap( Basemap. Type. TOPOGRAPHIC, 30.671475859566514, 104.07567785156248 / / latitude, levelOfDetail / / / / precision zoom level (can only be set up, can't get, And must be greater than 0) mapview. map = map} Override funonResume() {
mapView.resume()
super.onResume()
}
override fun onPause() {
mapView.pause()
super.onPause()
}
override fun onDestroy() {
mapView.dispose()
super.onDestroy()
}
}
Copy the code
Sets the map background color
Val mainBackgroundGrid = BackgroundGrid() // sets the background color mainbackgroundgrid. color = -0x1 // sets the BackgroundGrid color MainBackgroundGrid. GridLineColor = 0 x1 / / / / set the background grid line width unit (dp) mainBackgroundGrid. GridLineWidth = 0 f mapView.backgroundGrid = mainBackgroundGridCopy the code
According toArcgis
Based on the map
Val levelOfDetail = 16 val map = ArcGISMap (Basemap. Type. TOPOGRAPHIC, 30.671475859566514, // Latitude 104.07567785156248,// Precision levelOfDetail// Zoom level (set only, cannot be obtained, and must be greater than 0)) mapView.map = mapCopy the code
Load the base map layer
val url = "https://www.arcgis.com/home/item.html?id=7675d44bb1e4428aa2c30a9b68f97822"
map.basemap.baseLayers.add(ArcGISTiledLayer(url))
Copy the code
Added map drawing listener
/ / add the map state changes to monitor mapView. AddDrawStatusChangedListener {/ / map Another is to draw DrawStatus IN_PROGRESSif(it. DrawStatus == DrawStatus.COMPLETED) {// Start initLocal()}}Copy the code
Map zoom out
Note that this operation is asynchronous.
. / / zoom in iv_add setOnClickListener {mapView. SetViewpointScaleAsync (mapView. MapScale * 0.5)} / / narrow map iv_reduction.setOnClickListener { mapView.setViewpointScaleAsync(mapView.mapScale * 2) }Copy the code
Second, positioning related
Since it is a map, it must involve positioning, and the following content is about positioning.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
...
mapView.map = map
initLocal()
}
private fun initLocal() {/ / application runtime permissions (here to apply for positioning) EasyPermissions. Create (Manifest. Permission. WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION) .callback {if(it) {/ / positioning val mLocationDisplay = mapView. LocationDisplay / / positioning display mode mLocationDisplay autoPanMode = LocationDisplay. AutoPanMode. RECENTER. / / asynchronous positioning mLocationDisplay startAsync ()}else{
EasyToast.DEFAULT.show("Please open relevant required permissions for subsequent testing")
}
}
.request(this)
}
Copy the code
COMPASS_NAVIGATION
Pedestrian navigationLocation symbols are fixed to specific locations on the screen as the user walks and always point to the top edge of the device, best suited for WayPoint navigation.
NAVIGATION
Vehicle navigationBest for in-car navigation, the location symbol is fixed to a specific location on the screen and always points to the top of the screen.
OFF
User location symbols move with location, but the map does not
RECENTER
When the position symbol moves out of the “drift range”, the position symbol remains on the screen by re-centering the position symbol. (Third party explanation: When the user’s position is within the current map range, the user’s position symbol will move with the position change, but the map will not move; When the user is at the edge of the map, the map will automatically pan and the user’s current position will be in the center of the display map.
What if we then need to get the latitude and longitude information of the current anchor point and modify the marker? Next up:
val mLocationDisplay = mapView.locationDisplay mLocationDisplay.autoPanMode = LocationDisplay.AutoPanMode.RECENTER mLocationDisplay.startAsync() val pinStarBlueDrawable = ContextCompat.getDrawable(this, R.mipmap.icon_marker_blue) as BitmapDrawable? / / map image val campsiteSymbol = PictureMarkerSymbol. CreateAsync (pinStarBlueDrawable). The get () MLocationDisplay. AddLocationChangedListener {event - > / / see the returned location information EasyLog. DEFAULT. E (event. The location. The position. The toString ()) / / change the default icon mLocationDisplay defaultSymbol = campsiteSymbol / / mLocationDisplay isShowLocation =false/ / hide symbols mLocationDisplay isShowPingAnimation =false// Hide the symbol animation for position updates}Copy the code
Look at the address information:
And what we’re seeing is localization every three seconds, which is a pretty high frequency. What if we only need to locate once? Then after successful positioning can be closed:
if (mLocationDisplay.isStarted)
mLocationDisplay.stop()
Copy the code
Note:
After several tests, it was found that closing positioning was not controlled: In the test of Hongmi mobile phone, it could not be closed after successful positioning, and it was found that direct positioning failed after using OFF positioning mode. When using Redrice 4X test, it was found that both OFF and RECENTER tests could locate successfully, but the location could not be closed all the time. The Mi 9 tested the same as the Redmi 4X. Finally, the expectation of displaying marker at the custom position is not realized, and the following exception finally occurs:
The 2019-09-29 19:59:29. 027, 2795-8055 / com. Vincent. Arcgisdemo A/art: art/runtime/indirect_reference_table.cc:137] JNI ERROR (app bug): Weak global reference table overflow (Max = 51200) 2019-09-29 19:59:29. 027, 2795-8055 / com. Vincent. Arcgisdemo A/art: art/runtime/indirect_reference_table.cc:137] weak global reference table dump: The 2019-09-29 19:59:29. 027, 2795-8055 / com. Vincent. Arcgisdemo A/art: art/runtime/indirect_reference_table.cc:137] please find table dumpindropbox: 2795-weak global reference-table-overflow-dump ... The 2019-09-29 19:59:29. 421, 2795-8055 / com. Vincent. Arcgisdemo A/art: art/runtime/runtime.cc:423] at com.vincent.arcgisdemo.ui.MainActivity.showMarker(MainActivity.kt:113) 2019-09-29 19:59:29. 421, 2795-8055 / com. Vincent. Arcgisdemo A/art: art/runtime/runtime.cc:423] at com.vincent.arcgisdemo.ui.MainActivity.access$showMarker(MainActivity. Kt: 23) 19:59:29. 2019-09-29, 421, 2795-8055 / com. Vincent. Arcgisdemo A/art: art/runtime/runtime.cc:423] at com.vincent.arcgisdemo.ui.MainActivity$initLocalThe $1The $1.onLocationChanged(MainActivity.kt:95)
Copy the code
Source:
class MainActivity : AppCompatActivity() {
private val mGraphicsOverlay = GraphicsOverlay()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(com.vincent.arcgisdemo.R.layout.activity_main) EasyAndroid.init(this) val mainBackgroundGrid = BackgroundGrid() mainBackgroundGrid.color = -0x1 mainBackgroundGrid.gridLineColor = -0x1 mainBackgroundGrid.gridLineWidth = 0f mapView.backgroundGrid = mainBackgroundGrid val levelOfDetail = 16 val map = ArcGISMap (Basemap. The TOPOGRAPHIC, 30.671475859566514, 104.07567785156248 / / latitude, levelOfDetail / / / / precision zoom level (can only be set up, can't get, And must be greater than 0)) // val url ="https://www.arcgis.com/home/item.html?id=7675d44bb1e4428aa2c30a9b68f97822"map.basemap.baseLayers.add(ArcGISTiledLayer(url)) // val map = ArcGISMap(Basemap(ArcGISVectorTiledLayer(url))) // val vp = Viewpoint (47.606726, 122.335564, 72223.819286) / / map. InitialViewpoint = vp mapView. Map = map / / state changes to monitor mapView. Add map addDrawStatusChangedListener {/ / Drawstatus.in_progress drawStatus.in_progress drawStatus.in_progress drawStatus.in_progress drawStatus.in_progressif(it) drawStatus = = PLETED DrawStatus.COM {initLocal ()}} mapView. GraphicsOverlays. Add (mGraphicsOverlay)} / to start listening to * * * * Official documents require the following two permissions, three in the example, Difference is that the Manifest. Permission. ACCESS_COARSE_LOCATION * / private fun initLocal () = EasyPermissions. Create ( Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION ) .callback {if(it) { val mLocationDisplay = mapView.locationDisplay mLocationDisplay.autoPanMode = LocationDisplay.AutoPanMode.RECENTER mLocationDisplay.startAsync() val pinStarBlueDrawable = ContextCompat.getDrawable( this, com.vincent.arcgisdemo.R.mipmap.icon_marker_blue ) as BitmapDrawable? val campsiteSymbol = PictureMarkerSymbol.createAsync(pinStarBlueDrawable).get() MLocationDisplay. AddLocationChangedListener {event - > / / check the returned location information EasyLog.DEFAULT.e(event.location.position.toString()) mLocationDisplay.defaultSymbol = campsiteSymbol // mLocationDisplay.isShowLocation =false. / / / / hide symbols mLocationDisplay isShowPingAnimation =false// Hide the symbol animation for position updatesif (mLocationDisplay.isStarted) {
mLocationDisplay.stop()
}
showMarker(event.location.position.x, event.location.position.y)
}
} else {
EasyToast.DEFAULT.show("Please open relevant required permissions for subsequent testing")
}
}
.request(this)
private fun showMarker(x: Double, y: Double) {
EasyLog.DEFAULT.e("x${x} y${y}")
val pinStarBlueDrawable =
ContextCompat.getDrawable(
this,
com.vincent.arcgisdemo.R.mipmap.icon_marker_red
) as BitmapDrawable?
val campsiteSymbol = PictureMarkerSymbol.createAsync(pinStarBlueDrawable).get()
campsiteSymbol.loadAsync()
val attributes = HashMap<String, Any>()
val pointGraphic =
Graphic(Point(x, y), attributes, campsiteSymbol)
mGraphicsOverlay.graphics.add(pointGraphic)
}
override fun onResume() {
mapView.resume()
super.onResume()
}
override fun onPause() {
mapView.pause()
super.onPause()
}
override fun onDestroy() {
mapView.dispose()
super.onDestroy()
}
}
Copy the code
Edit the map
Basic operation
Basic operations refer to operations commonly used in maps, eight of which are described below:
- Plot points
- Draw a line (polyline segment)
- Draw curves (draw directly from finger track)
- Draw polygons
- Draw the circle
- Draw pictures
marker
- Rendering text
- Draws custom annotations
1. The plot points
The logic is very simple, is to click on the map to draw a point, so you need to set a click event on the map, but the map is not an ordinary View, see the code for specific examples:
mapView.onTouchListener = object : DefaultMapViewOnTouchListener(this,mapView){ override fun onSingleTapConfirmed(e: MotionEvent?) : Boolean { e? :super.onSingleTapConfirmed(e)if(drawType ! = -1){ e? :return falseval clickPoint = mMapView.screenToLocation( android.graphics.Point(e.x.roundToInt(),e.y.roundToInt())) when(drawType){ 0 -> MapUtil.drawPoint(clickPoint) } }returnSuper. OnSingleTapConfirmed (e)}} / / map tool object MapUtil {val mGraphicsOverlay = GraphicsOverlay () / / draw some fun drawPoint(p: Point) {/ / SimpleMarkerSymbol. Style has the following six value, Represent different shapes / / SimpleMarkerSymbol. Style. CIRCLE round / / SimpleMarkerSymbol Style. The CROSS CROSS symbols / / SimpleMarkerSymbol Style. DIAMOND Diamond / / SimpleMarkerSymbol. Style. SQUARE SQUARE / / SimpleMarkerSymbol style.css. TRIANGLE TRIANGLE / / SimpleMarkerSymbol style.css. X X shape, val simpleMarkerSymbol = SimpleMarkerSymbol(SimpleMarkerSymbol.Style.CIRCLE, Color.RED, 20f) val graphic = Graphic(p, SimpleMarkerSymbol) / / clear on one point mGraphicsOverlay. Graphics. Clear () mGraphicsOverlay. Graphics. The add (graphic)}}Copy the code
2. Draw a line
The logic of drawing a line is similar to that of drawing points, except that points are strung together into a line. The code is shown below:
DrawPoint (drawPoint){0 -> MapUtil. DrawPoint (clickPoint) {0 -> MapUtil. DrawPoint (clickPoint)} Mpointcollection.add (p) val polyline = polyline (mPointCollection / / SimpleLineSymbol. Style line shape / / SimpleLineSymbol. Style. The DASH - / / SimpleLineSymbol. The style.css. DASH_DOT -, -, - / / SimpleLineSymbol. Style. DASH_DOT_DOT -, -, -, -, - / / SimpleLineSymbol. The style.css. DOT... / / SimpleLineSymbol. Style. NULL don't show / / SimpleLineSymbol. Style. The SOLID line val SimpleLineSymbol = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID , Color.BLUE, 3f); val graphic = Graphic(polyline, simpleLineSymbol) mGraphicsOverlay.graphics.add(graphic) }Copy the code
3. Draw a curve
The logic of drawing a curve is exactly the same as that of drawing a straight line, except that the points are denser. Now that need to collect more points than draw a straight line, then line the collection point method is definitely not line, but fortunately the DefaultMapViewOnTouchListener also provides a onScroll methods, is the map scroll event callback methods, we look at the source code:
mapView.onTouchListener = object : DefaultMapViewOnTouchListener(this,mapView){ override fun onSingleTapConfirmed(e: MotionEvent?) : Boolean { e? :super.onSingleTapConfirmed(e)if(drawType ! = -1){ e? :return falseval clickPoint = mMapView.screenToLocation( android.graphics.Point(e.x.roundToInt(),e.y.roundToInt())) when(drawType){ 0 -> MapUtil.drawPoint(clickPoint) 1 -> MapUtil.drawLine(clickPoint) } }returnsuper.onSingleTapConfirmed(e) } override fun onScroll( e1: MotionEvent? , e2: MotionEvent? , distanceX: Float, distanceY: Float ): Boolean {if(drawType == 2){ e1? :return falsee2? :return falseval p1 = mMapView.screenToLocation( android.graphics.Point(e1.x.roundToInt(),e1.y.roundToInt())) val p2 = MMapView. ScreenToLocation (android. Graphics. Point (e2. X.r oundToInt (), e2, y.r oundToInt ())) MapUtil. DrawCurves (p1, p2) / / returntrueMaps will no longer scroll while slidingreturn true
}
returnsuper.onScroll(e1, e2, distanceX, Val mGraphicsOverlay = GraphicsOverlay() private val MPointCollection = PointCollection (SpatialReferences getWebMercator (), / / draw the curve fun drawCurves (p1: Point, p2: Point) { mPointCollection.add(p1) mPointCollection.add(p2) val polyline = Polyline(mPointCollection) val simpleLineSymbol = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.BLUE, 3f); val graphic = Graphic(polyline, simpleLineSymbol) mGraphicsOverlay.graphics.add(graphic) } }Copy the code
From the code, we can see if we are collecting more points, and then drawing all the points with a straight line in the normal way.
4. Draw polygons
In fact, drawing a polygon is to combine lines into a surface, but there are many kinds of surface composition as well as line shapes. As it is a surface, it is demonstrated here through patterns:
SimpleFillSymbol.Style.BACKWARD_DIAGONAL
SimpleFillSymbol.Style.FORWARD_DIAGONAL
SimpleFillSymbol.Style.DIAGONAL_CROSS
SimpleFillSymbol.Style.HORIZONTAL
SimpleFillSymbol.Style.VERTICAL
SimpleFillSymbol.Style.CROSS
SimpleFillSymbol.Style.SOLID
Color fill
SimpleFillSymbol.Style.NULL
With these background fillings in mind, let’s see how polygons are drawn:
when(drawType){ 0 -> MapUtil.drawPoint(clickPoint) 1 -> MapUtil.drawLine(clickPoint) 3 -> MapUtil drawPolygon(clickPoint)} MapUtil drawPolygon(p: Point) {mGraphicsOverlay. Graphics. Clear () mPointCollection. Add (p) / / a Point can't make a faceif (mPointCollection.size == 1) {
drawPoint(p)
return} val polygon = Polygon(mPointCollection) val lineSymbol = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.GREEN, 3.0 f) val simpleFillSymbol = simpleFillSymbol (simpleFillSymbol. Style. SOLID, Color, YELLOW, lineSymbol) val graphic = Graphic(polygon, simpleFillSymbol) mGraphicsOverlay.graphics.add(graphic) } }Copy the code
Effect preview:
5. Draw the circle
In fact, circle drawing and polygon drawing are different, but circle drawing is based on the center and radius to confirm the position of the circle. When we draw a circle with a compass, the position of a circle can be determined by the point where the two tips are located. It is similar here. The first point determines the location of the center of the circle, and the second point determines the radius. Based on this reasoning, we can get the following code:
when(drawType){ 0 -> MapUtil.drawPoint(clickPoint) 1 -> MapUtil.drawLine(clickPoint) 3 -> MapUtil. DrawPolygon (clickPoint) 4 -> MapUtil. DrawCircle (clickPoint)} Point) { mGraphicsOverlay.graphics.clear()if(mpointCollection.size == 50) mpointCollection.clear () mpointCollection.add (p) // Only the center of the circle is determinedif (mPointCollection.size == 1) {
drawPoint(p)
return} val x = mPointCollection[0].x - mPointCollection[1].x val y = mPointCollection[0].y - Y val radius = SQRT (x.pet (2.0) + y.pet (2.0)) val Center = mPointCollection[0] Mpointcollection.clear () // Retrieves the point of the circle based on the center and radiusfor (point ingetPoints(center, Radius) {mpointCollect.add (point)} val polygon = polygon (mPointCollection) // Edge val lineSymbol = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.GREEN, 3.0 f) / / fill style Fill Color fill border val simpleFillSymbol = simpleFillSymbol (simpleFillSymbol. Style. SOLID, Color, YELLOW, lineSymbol) val graphic = Graphic(polygon, SimpleFillSymbol) mGraphicsOverlay. Graphics. The add (graphic)} / * * * calculated through the center and radius of circular edge point set * * @ param center * @ param radius * @return*/ private fun getPoints(center: Point, radius: Double): Array<Point? > { val points = arrayOfNulls<Point>(50) var sin: Double var cos: Double var x: Double var y: Doublefor (i in0.. 40) {sine = kotlin.math. Sine (math.pi * 2.0 * I / 50) cos = kotlin.math * sin y = center.y + radius * cos points[i] = Point(x, y) }return points
}
}
Copy the code
Draw circles After calculating points, draw circles and the specific APi and regular polygons are the same. Here is a preview of the effect:
6. Draw picturesmarker
Marker drawing a picture marker is similar to adding an icon to a fixed point on a map. It is also simple to implement:
when(drawType){ 0 -> MapUtil.drawPoint(clickPoint) 1 -> MapUtil.drawLine(clickPoint) 3 -> MapUtil.drawPolygon(clickPoint) 4 -> MapUtil.drawCircle(clickPoint) 5 -> MapUtil. DrawMarker (clickPoint,this@MainActivity)} Point, context: Context) {/ / for drawable resource val pinStarBlueDrawable = ContextCompat. GetDrawable (Context, R.mipmap.icon_marker_red) as BitmapDrawable? Val campsiteSymbol = PictureMarkerSymbol("Picture network address") val campsiteSymbol = PictureMarkerSymbol. CreateAsync (pinStarBlueDrawable). The get () / / asynchronous loading campsiteSymbol loadAsync (val) Attributes = HashMap<String, Any>() MGraphicsOverlay campsiteSymbol) / / added to the layer. The graphics. The add (pointGraphic)}}Copy the code
6. Draw text markers
Drawing text tags is as simple as drawing image tags.
when(drawType){ 0 -> MapUtil.drawPoint(clickPoint) 1 -> MapUtil.drawLine(clickPoint) 3 -> MapUtil.drawPolygon(clickPoint) 4 -> MapUtil.drawCircle(clickPoint) 5 -> MapUtil. DrawMarker (clickPoint,this@MainActivity) 6 -> MapUtil. DrawText (clickPoint) fun drawText(p: Point) {/ / horizontal has LEFT in the right/LEFT TextSymbol/level. The HorizontalAlignment. LEFT. / / level TextSymbol HorizontalAlignment. The CENTER/right/level TextSymbol. HorizontalAlignment. RIGHT / / vertical support 3 / / vertical TextSymbol VerticalAlignment. TOP / / vertical TextSymbol. VerticalAlignment. MIDDLE / / under vertical TextSymbol VerticalAlignment. BOTTOM val TextSymbol = TextSymbol (20 f,"Tagged text", Color.RED, TextSymbol.HorizontalAlignment.CENTER, TextSymbol. VerticalAlignment. MIDDLE) / / generated drawings val graphic = graphic (p, TextSymbol) / / clear before contents mGraphicsOverlay. Graphics. Clear () / / added to the layer mGraphicsOverlay graphics. The add (graphic)}}Copy the code
Results the preview
7. Draw custom annotations
Although image markers are supported and text content is supported, what if you want to add a marker that contains the image and text on top of the layer? This leads to the following custom annotations. In a simple sentence, it is to display annotation information through a custom View. The specific example is as follows:
when(drawType){ 0 -> MapUtil.drawPoint(clickPoint) 1 -> MapUtil.drawLine(clickPoint) 3 -> MapUtil.drawPolygon(clickPoint) 4 -> MapUtil.drawCircle(clickPoint) 5 -> MapUtil.drawMarker(clickPoint,this@MainActivity) 6 -> MapUtil.drawText(clickPoint) 7 -> MapUtil. DrawCallout (clickPoint,mapView,this@MainActivity)} MapView,context: Context) { val callout = mapView.calloutif(callout.isShowing){ callout.dismiss() } val view = LayoutInflater.from(context).inflate(R.layout.callout_delete_layout, null,false)
view.setOnClickListener {
callout.dismiss()
EasyToast.DEFAULT.show("Close flag")
}
callout.location = p
callout.content = view
callout.show()
}
}
Copy the code
Preview effect (note that sharp and rounded corners are set on the map) :
Sketch editorSketchEditor
The sketch editor does not support drawing circles, other graphics are no problem, and it is very easy to use, directly read the comments:
Private val mSketchEditor = SketchEditor() instantiates the map... SketchEditor = mSketchEditor val builder2 = // add the sketchEditor to the map XPopup.Builder(this).watchView(btn_sketch) btn_sketch.setOnClickListener { builder2.asAttachList( arrayOf("Single point"."More"."Line"."Polygon"."Draw lines freehand."."Freehand polygons."."Step up"."Next step"), null
) { position, _ ->
MapUtil.restDrawStatus()
when (position) {
0 -> mSketchEditor.start(SketchCreationMode.POINT)
1 -> mSketchEditor.start(SketchCreationMode.MULTIPOINT)
2 -> mSketchEditor.start(SketchCreationMode.POLYLINE)
3 -> mSketchEditor.start(SketchCreationMode.POLYGON)
4 -> mSketchEditor.start(SketchCreationMode.FREEHAND_LINE)
5 -> mSketchEditor.start(SketchCreationMode.FREEHAND_POLYGON)
6 -> if (mSketchEditor.canUndo()) mSketchEditor.undo()
7 -> if(msketcheditor.canredo ()) msketcheditor.redo ()}}.show()} btn_rest.setonClickListener {// Reset the first drawingif(drawType ! = -1) { drawType = -1 MapUtil.restDrawStatus() }else{// Draw successfully (no graphics)if(! MSketchEditor. IsSketchValid) {/ / reset mSketchEditor. Stop ()return@set} val sketchGeometry = msketcheditor.geometry msketcheditor.stop ()if(sketchGeometry ! // Create a graphic from the sketch editor val Graphic = graphic (sketchGeometry) // Assign symbols according to the geometry typeif (graphic.geometry.geometryType == GeometryType.POLYGON) {
val mLineSymbol =
SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, 0xFF8800, 4f)
val mFillSymbol =
SimpleFillSymbol(SimpleFillSymbol.Style.CROSS, 0x40FFA9A9, mLineSymbol)
graphic.symbol = mFillSymbol
} else if (graphic.geometry.geometryType == GeometryType.POLYLINE) {
val mLineSymbol =
SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, 0xFF8800, 4f)
graphic.symbol = mLineSymbol
} else if(graphic.geometry.geometryType == GeometryType.POINT || graphic.geometry.geometryType == GeometryType.MULTIPOINT ) { val mPointSymbol = SimpleMarkerSymbol( SimpleMarkerSymbol.Style.SQUARE, 0xFF0000, . 20 f) graphic symbol = mPointSymbol} / / add graphics to the graphic overlay mGraphicsOverlay. Graphics. The add (graphic)}}}Copy the code
Preview:
Coordinate calculation
The above is a simple drawing, but it is obviously inadequate for development because we do not get the exact latitude and longitude of the drawing point. For latitude and longitude, the following explanation is more appropriate:
Because only the United States has the GLOBAL positioning System (GPS) in the world at present, when we actually do the project, the coordinate data we get are usually the coordinate system established for the use of GPS global positioning system, that is, we call 84 coordinates. Based on China’s national conditions, these real coordinates have been artificially added bias processing, can be published and released. So, the background returns 84 coordinates, and in order to display the correct position on the map, you need to do a coordinate conversion. The original
In the first method above, we get the screen coordinate android.graphics.point, and then convert it to the projection coordinate (of course, map coordinates can also be converted to screen coordinates). At this time, the longitude and latitude value of this coordinate is still not correct, we also need to convert the projection coordinate to space coordinates. This is where the hero of this section comes in!
1.GeometryEngine
Coordinate transformation
- Gets default map coordinates based on projection coordinates
Point wgsPoint = (Point) GeometryEngine.project(dp, mMapView.getSpatialReference(), null);
Copy the code
- will
J02
Coordinates toG84
coordinates
val projectedPoint = GeometryEngine.project(clickPoint,SpatialReference.create(4236)) as Point
// SpatialReference.create(4236) = SpatialReferences.getWgs84()
Copy the code
- will
G84
Coordinates toJ02
coordinates
GCJ02: Also known as Mars coordinate system, it is a geographical coordinate system developed by China National Bureau of Surveying and Mapping, and obtained by WGS84 after encryption.
Since GcJo2 is an encrypted result, it does not have the same WID specification as WSG84, but there are ways to implement it.
package com.vincent.arcgisdemo.util import com.esri.arcgisruntime.geometry.Point import kotlin.math.abs import Kotlin.math. cos import kotlin.math.sin import kotlin.math. SQRT /** * <p> @date 2019/10/10001 <p> * <p>@update 2019/10/10001 <p> * <p> 1<p> ** / object TransformUtil {/** * private fun outOfChina(lat: Double, LNG: Double): Boolean {if(LNG < 72.004 | | LNG > 137.8347) {return true
}
returnLat < 0.8293 | | lat > 55.8271} / / decryption latitude private fun transformLat (x: Double, y: Double) : Double {var ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * SQRT (abs(x)) ret += (20.0 * sin(6.0 *) X * math.pi) + 20.0 * sin(2.0 * x * math.pi)) * 2.0/3.0 ret += (20.0 * sin(y * math.pi) + 40.0 * sin(y / 3.0 *) Math.pi)) * 2.0/3.0 ret += (160.0 * sin(y / 12.0 * math.pi) + 320 * sin(y * math.pi / 30.0)) * 2.0/3.0returnPrivate fun transformLon(x: Double, y: Double): Double {var ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * SQRT (ABS (x)) ret += (20.0 * sin(6.0 * x *) Math. PI) + 20.0 * sin (2.0 * x * Math. PI)) * 2.0/3.0 ret + = (20.0 * sin (x * Math. PI) + 40.0 * sin (x / 3.0 x Math. PI)) * 2.0/3.0 ret += (150.0 * sin(x / 12.0 * math.pi) + 300.0 * sin(x / 30.0 * math.pi)) * 2.0/3.0returnRet} /** * Measure the difference between latitude and longitude * @Param Lat latitude * @Param LNG longitude * @return*/ private fun delta(lat: Double, LNG: Double): DoubleArray {val delta = DoubleArray(2) val a = 6378137.0 val EE = 0.00669342162296594323 val dLat = transformLat(LNG - Lat-35.0) val dLng = transformLon(LNG-105.0, Var magic = sin(radLat) magic = 1 - EE * magic * magic val sqrtMagic = SQRT (magic) delta[0] = dLat * 180.0 / (a * (1-EE)/(Magic * sqrtMagic) * math.pi) delta[1] = dLng * 180.0 / (a / sqrtMagic * cos(radLat) * Math.PI)returnDelta} /** * WSG84 to GCJ02 coordinates * @param Lat latitude * @Param LNG longitude */ fun wsG84toGCJ02Point(latitude:Double,longitude:Double) : Point {if (outOfChina(latitude, longitude)) {
return Point(latitude, longitude)
}
val delta = delta(latitude, longitude)
returnPoint(latitude + delta[0], Longitude longitude longitude longitude longitude longitude longitude longitude longitude longitude gcJo2toWGS84Point(latitude:Double,longitude:Double):Point {if (TransformUtil.outOfChina(latitude, longitude)) {
return Point(latitude, longitude)
}
val delta = delta(latitude, longitude)
return Point(latitude - delta[0], longitude - delta[1])
}
}
Copy the code
Calculate the length of the
- Calculates the length of a given polyline
GeometryEngine.length ( polyline:Polyline):Double
Copy the code
- Calculate the geometric length of the earth
Note: LinearUnit — the unit of measurement for the return value. If it is null, then we default to linear units of Id METERS
GeometryEngine.lengthGeodetic ( geometry:Geometry, lengthUnit:LinearUnit, curveType:GeodeticCurveType):Double
Copy the code
Calculating area
GeometryEngine.area ( polygon:Polygon):Double
GeometryEngine.area ( envelope:Envelope):Double
GeometryEngine.areaGeodetic ( geometry:Geometry, areaUnit:AreaUnit, curveType:GeodeticCurveType):Double
Copy the code
Computes the boundaries of a given geometry
GeometryEngine.boundary (geometry:Geometry):Geometry
Copy the code
Merges the ranges of two given geometries
GeometryEngine.com bineExtents (geometry1: Geometry, geometry2: Geometry) : EnvelopeCopy the code
Merges the range of geometric sets
Contains (Container :Geometry, within:Geometry):BooleanCopy the code
Strengthen a given geometry by drawing points between existing vertices in the geometry.
GeometryEngine.densify ( geometry:Geometry, maxSegmentLength:Double):Geometry
GeometryEngine.densifyGeodetic ( geometry:Geometry, maxSegmentLength:Double,lengthUnit:LinearUnit, curveType:GeodeticCurveType):Geometry
Copy the code
Whether two graphs contain
Whether the latter includes the former
GeometryEngine.within ( within:Geometry, container:Geometry):Boolean
Copy the code
4. Introduction of common interfaces
In the previous example we saw that the latitude and longitude of the click point is obtained by clicking on the event, so why not click on the event using the view. OnClickListener interface? Then introduce everyone including DefaultMapViewOnTouchListener interface!
MapScaleChangedListener
This interface has a callback method, mapScaleChanged, which is called when the scale of the map is changed. When will this interface be used? Remember when initializing the map, we set the default map zoom level to 16. What is the maximum and minimum map zoom level? It is said on the Internet that 0~20, but I did not find relevant documents, and I did not find the maximum check value when I checked the source code during the initialization process (there is a judgment that it cannot be less than 0), because the final implementation method is native method. We also didn’t find an Api to get the zoom level, so we can use this callback to handle the scale and zoom level issues to limit the zoom and prevent the map from zooming in and out indefinitely.
Note: the mapScaleChanged method is called frequently, and the scale method setViewpointScaleAsync that resets the scale is asynchronous, so the interaction here will be less than ideal. Solution within DefaultMapViewOnTouchListener finally.
MapRotationChangedListener
This interface also has only one callback method, the mapRotationChanged method. Called when the map is rotated, which is also a frequently invoked method. If you need to update the map after rotation, refer to this interface.
DefaultMapViewOnTouchListener
DefaultMapViewOnTouchListener
It is a complex implementation class. You can take a look at this diagram first:
Of course, it is not necessary to completely understand the use of every method, just need to understand their own needs.
Click onSingleTapConfirmed in the project through this method to achieve a single click callback, interested students can also learn about the onSingleTapUp method
Long press onLongPress this callback has no objection, the project has not found any problems
Double-click onDoubleTouchDrag this method name is somewhat ambiguous because it only has a double-click callback, not a two-finger swipe callback
This method can be used in conjunction with onScaleBegin to determine the current scale size when scaling. If scaling is no longer supported, consume the event directly
This method can be used in conjunction with onScaleEnd to determine the current scale size at the beginning of scaling. If scaling is no longer supported, consume the event directly
A finger swipe onScroll is the default slide event.ACTION_MOVE, but the onScroll method is called back when the slide ends
OnFling is called when the slide ends, unlike the Fling method in the View
These are the interface callbacks I used for maps in my project.
5. Access to map of heaven and earth
This is relatively simple, go to the official map of the world to register an account, and then apply for an application, and then get the key into the tool class can be used (currently found not checked signature). The renderings below use the data of Map of Heaven and Earth, not the map data of Arcgis official website.
Code:
package com.vincent.arcgisdemo.util
import com.esri.arcgisruntime.arcgisservices.LevelOfDetail
import com.esri.arcgisruntime.arcgisservices.TileInfo
import com.esri.arcgisruntime.geometry.Envelope
import com.esri.arcgisruntime.geometry.Point
import com.esri.arcgisruntime.geometry.SpatialReference
import com.esri.arcgisruntime.layers.WebTiledLayer
object TianDiTuMethodsClass {
val key = "58a2d8db46a9ea6d009a************"
private val SubDomain = arrayOf("t0"."t1"."t2"."t3"."t4"."t5"."t6"."t7")
private val URL_VECTOR_2000 =
"http://{subDomain}.tianditu.com/DataServer?T=vec_c&x={col}&y={row}&l={level}&tk=$key"
private val URL_VECTOR_ANNOTATION_CHINESE_2000 =
"http://{subDomain}.tianditu.com/DataServer?T=cva_c&x={col}&y={row}&l={level}&tk=$key"
private val URL_VECTOR_ANNOTATION_ENGLISH_2000 =
"http://{subDomain}.tianditu.com/DataServer?T=eva_c&x={col}&y={row}&l={level}&tk=$key"
private val URL_IMAGE_2000 =
"http://{subDomain}.tianditu.com/DataServer?T=img_c&x={col}&y={row}&l={level}&tk=$key"
private val URL_IMAGE_ANNOTATION_CHINESE_2000 =
"http://{subDomain}.tianditu.com/DataServer?T=cia_c&x={col}&y={row}&l={level}&tk=$key"
private val URL_IMAGE_ANNOTATION_ENGLISH_2000 =
"http://{subDomain}.tianditu.com/DataServer?T=eia_c&x={col}&y={row}&l={level}&tk=$key"
private val URL_TERRAIN_2000 =
"http://{subDomain}.tianditu.com/DataServer?T=ter_c&x={col}&y={row}&l={level}&tk=$key"
private val URL_TERRAIN_ANNOTATION_CHINESE_2000 =
"http://{subDomain}.tianditu.com/DataServer?T=cta_c&x={col}&y={row}&l={level}&tk=$key"
private val URL_VECTOR_MERCATOR =
"http://{subDomain}.tianditu.com/DataServer?T=vec_w&x={col}&y={row}&l={level}&tk=$key"
private val URL_VECTOR_ANNOTATION_CHINESE_MERCATOR =
"http://{subDomain}.tianditu.com/DataServer?T=cva_w&x={col}&y={row}&l={level}&tk=$key"
private val URL_VECTOR_ANNOTATION_ENGLISH_MERCATOR =
"http://{subDomain}.tianditu.com/DataServer?T=eva_w&x={col}&y={row}&l={level}&tk=$key"
private val URL_IMAGE_MERCATOR =
"http://{subDomain}.tianditu.com/DataServer?T=img_w&x={col}&y={row}&l={level}&tk=$key"
private val URL_IMAGE_ANNOTATION_CHINESE_MERCATOR =
"http://{subDomain}.tianditu.com/DataServer?T=cia_w&x={col}&y={row}&l={level}&tk=$key"
private val URL_IMAGE_ANNOTATION_ENGLISH_MERCATOR =
"http://{subDomain}.tianditu.com/DataServer?T=eia_w&x={col}&y={row}&l={level}&tk=$key"
private val URL_TERRAIN_MERCATOR =
"http://{subDomain}.tianditu.com/DataServer?T=ter_w&x={col}&y={row}&l={level}&tk=$key"
private val URL_TERRAIN_ANNOTATION_CHINESE_MERCATOR =
"http://{subDomain}.tianditu.com/DataServer?T=cta_w&x={col}&y={row}&l={level}&tk=$key"
private val DPI = 96
private val minZoomLevel = 1
private val maxZoomLevel = 18
private val tileWidth = 256
private val tileHeight = 256
private val LAYER_NAME_VECTOR = "vec"
private val LAYER_NAME_VECTOR_ANNOTATION_CHINESE = "cva"
private val LAYER_NAME_VECTOR_ANNOTATION_ENGLISH = "eva"
private val LAYER_NAME_IMAGE = "img"
private val LAYER_NAME_IMAGE_ANNOTATION_CHINESE = "cia"
private val LAYER_NAME_IMAGE_ANNOTATION_ENGLISH = "eia"
private val LAYER_NAME_TERRAIN = "ter"
private val LAYER_NAME_TERRAIN_ANNOTATION_CHINESE = "cta"private val SRID_2000 = SpatialReference.create(4490) private val SRID_MERCATOR = SpatialReference.create(102100) Private val X_MIN_2000 = -180.0 private val Y_MIN_2000 = -90.0 private val X_MAX_2000 = 180.0 private val Y_MAX_2000 = 90.0 private val X_MIN_MERCATOR = -20037508.3427892 Private val Y_MIN_MERCATOR = -20037508.3427892 private val X_MIN_MERCATOR = -20037508.3427892 private val X_MIN_MERCATOR = -20037508.3427892 X_MAX_MERCATOR = 20037508.3427892 private val Y_MAX_MERCATOR = 20037508.3427892 private val ORIGIN_2000 = Point (180.0, 90.0, SRID_2000) private val ORIGIN_MERCATOR = Point(-20037508.3427892, 20037508.3427892, SRID_MERCATOR) private val ENVELOPE_2000 = Envelope(X_MIN_2000, Y_MIN_2000, X_MAX_2000, Y_MAX_2000, SRID_2000) private val ENVELOPE_MERCATOR = Envelope(X_MIN_MERCATOR, Y_MIN_MERCATOR, X_MAX_MERCATOR, Y_MAX_MERCATOR, SRID_MERCATOR) private val SCALES = doubleArrayOf(2.958293554545656E8, 1.479146777272828E8, 7.39573388636414E7, 3.69786694318207E7, 1.848933471591035E7, 9244667.357955175, 4622333.678977588, 2311166.839488794, 1155583.419744397, 577791.7098721985, 288895.85493609926, 144447.92746804963, 72223.96373402482, 36111.98186701241, 18055.990933506204, 9027.995466753102, 4513.997733376551, 2256.998866688275, Private val RESOLUTIONS_MERCATOR = doubleArrayOf(78271.51696402048, 39135.75848201024, 19567.87924100512, 9783.93962050256, 4891.96981025128, 2445.98490512564, 1222.99245256282, 611.49622628141, 305.748113140705, 152.8740565703525, 76.43702828517625, 38.21851414258813, 19.109257071294063, 9.554628535647032, 4.777314267823516, 2.388657133911758, 1.194328566955879, 0.5971642834779395, Private val RESOLUTIONS_2000 = doubleArrayOf(0.7031249999985, 0.351562499999994, 0.17578124999999997, 0.08789062500000014, 0.04394531250000007, 0.021972656250000007, 0.01098632812500002, 0.00549316406250001, 0.0027465820312500017, 0.0013732910156250009, 0.000686645507812499, 0.0003433227539062495, 0.00017166137695312503, 0.00008583068847656251, 0.000042915344238281406, 0.000021457672119140645, 0.000010728836059570307, 0.000005364418029785169) fun CreateTianDiTuTiledLayer(layerType: String): WebTiledLayer {return CreateTianDiTuTiledLayer(getTianDiTuLayerType(layerType));
}
fun CreateTianDiTuTiledLayer(layerType: LayerType): WebTiledLayer {
var webTiledLayer: WebTiledLayer? = null
var mainUrl = ""
var mainName = ""
var mainTileInfo: TileInfo? = null
var mainEnvelope: Envelope? = null
var mainIs2000 = false
when (layerType) {
LayerType.TIANDITU_VECTOR_2000 -> {
mainUrl = URL_VECTOR_2000
mainName = LAYER_NAME_VECTOR
mainIs2000 = true
}
LayerType.TIANDITU_VECTOR_MERCATOR -> {
mainUrl = URL_VECTOR_MERCATOR
mainName = LAYER_NAME_VECTOR
}
LayerType.TIANDITU_IMAGE_2000 -> {
mainUrl = URL_IMAGE_2000
mainName = LAYER_NAME_IMAGE
mainIs2000 = true
}
LayerType.TIANDITU_IMAGE_ANNOTATION_CHINESE_2000 -> {
mainUrl = URL_IMAGE_ANNOTATION_CHINESE_2000
mainName = LAYER_NAME_IMAGE_ANNOTATION_CHINESE
mainIs2000 = true
}
LayerType.TIANDITU_IMAGE_ANNOTATION_ENGLISH_2000 -> {
mainUrl = URL_IMAGE_ANNOTATION_ENGLISH_2000
mainName = LAYER_NAME_IMAGE_ANNOTATION_ENGLISH
mainIs2000 = true
}
LayerType.TIANDITU_IMAGE_ANNOTATION_CHINESE_MERCATOR -> {
mainUrl = URL_IMAGE_ANNOTATION_CHINESE_MERCATOR;
mainName = LAYER_NAME_IMAGE_ANNOTATION_CHINESE;
}
LayerType.TIANDITU_IMAGE_ANNOTATION_ENGLISH_MERCATOR -> {
mainUrl = URL_IMAGE_ANNOTATION_ENGLISH_MERCATOR
mainName = LAYER_NAME_IMAGE_ANNOTATION_ENGLISH
}
LayerType.TIANDITU_IMAGE_MERCATOR -> {
mainUrl = URL_IMAGE_MERCATOR
mainName = LAYER_NAME_IMAGE
}
LayerType.TIANDITU_VECTOR_ANNOTATION_CHINESE_2000 -> {
mainUrl = URL_VECTOR_ANNOTATION_CHINESE_2000
mainName = LAYER_NAME_VECTOR_ANNOTATION_CHINESE
mainIs2000 = true
}
LayerType.TIANDITU_VECTOR_ANNOTATION_ENGLISH_2000 -> {
mainUrl = URL_VECTOR_ANNOTATION_ENGLISH_2000
mainName = LAYER_NAME_VECTOR_ANNOTATION_ENGLISH
mainIs2000 = true} LayerType.TIANDITU_VECTOR_ANNOTATION_CHINESE_MERCATOR -> { mainUrl = URL_VECTOR_ANNOTATION_CHINESE_MERCATOR mainName = LAYER_NAME_VECTOR_ANNOTATION_CHINESE } LayerType.TIANDITU_VECTOR_ANNOTATION_ENGLISH_MERCATOR -> { mainUrl = URL_VECTOR_ANNOTATION_ENGLISH_MERCATOR mainName = LAYER_NAME_VECTOR_ANNOTATION_ENGLISH } LayerType.TIANDITU_TERRAIN_2000 -> { mainUrl = URL_TERRAIN_2000 mainName = LAYER_NAME_TERRAIN mainIs2000 =true
}
LayerType.TIANDITU_TERRAIN_ANNOTATION_CHINESE_2000 -> {
mainUrl = URL_TERRAIN_ANNOTATION_CHINESE_2000
mainName = LAYER_NAME_TERRAIN_ANNOTATION_CHINESE
mainIs2000 = true} LayerType.TIANDITU_TERRAIN_MERCATOR -> { mainUrl = URL_TERRAIN_MERCATOR mainName = LAYER_NAME_TERRAIN } LayerType.TIANDITU_TERRAIN_ANNOTATION_CHINESE_MERCATOR -> { mainUrl = URL_TERRAIN_ANNOTATION_CHINESE_MERCATOR mainName = LAYER_NAME_TERRAIN_ANNOTATION_CHINESE } } val mainLevelOfDetail = mutableListOf<LevelOfDetail>(); var mainOrigin: Point? = nullif (mainIs2000) {
for (i inminZoomLevel.. maxZoomLevel) { val item = LevelOfDetail(i, RESOLUTIONS_2000[i - 1], SCALES[i - 1]) mainLevelOfDetail.add(item) } mainEnvelope = ENVELOPE_2000 mainOrigin = ORIGIN_2000 }else {
for (i inminZoomLevel.. maxZoomLevel) { val item = LevelOfDetail(i, RESOLUTIONS_MERCATOR[i - 1], SCALES[i - 1]) mainLevelOfDetail.add(item); } mainEnvelope = ENVELOPE_MERCATOR; mainOrigin = ORIGIN_MERCATOR; } mainTileInfo = TileInfo( DPI, TileInfo.ImageFormat.PNG24, mainLevelOfDetail, mainOrigin, mainOrigin.getSpatialReference(), tileHeight, tileWidth ) webTiledLayer = WebTiledLayer( mainUrl, SubDomain.toList(), mainTileInfo, mainEnvelope ); webTiledLayer.setName(mainName) webTiledLayer.loadAsync()return webTiledLayer
}
fun getTianDiTuLayerType(layerType: String): LayerType {
returnWhen (layerType) {// Skymap vector Mercator projection map service"TIANDITU_VECTOR_MERCATOR"-> layertype. TIANDITU_VECTOR_MERCATOR"TIANDITU_VECTOR_ANNOTATION_CHINESE_MERCATOR"-> layertype. TIANDITU_VECTOR_ANNOTATION_CHINESE_MERCATOR"TIANDITU_VECTOR_ANNOTATION_ENGLISH_MERCATOR"-> layertype. TIANDITU_VECTOR_ANNOTATION_ENGLISH_MERCATOR // Skymap Image Mercator map service"TIANDITU_IMAGE_MERCATOR"-> layertype. TIANDITU_IMAGE_MERCATOR //"TIANDITU_IMAGE_ANNOTATION_CHINESE_MERCATOR"-> layertype. TIANDITU_IMAGE_ANNOTATION_CHINESE_MERCATOR"TIANDITU_IMAGE_ANNOTATION_ENGLISH_MERCATOR"-> layertype. TIANDITU_IMAGE_ANNOTATION_ENGLISH_MERCATOR // Map service for terrain Mercator projection"TIANDITU_TERRAIN_MERCATOR"-> layertype. TIANDITU_TERRAIN_MERCATOR //"TIANDITU_TERRAIN_ANNOTATION_CHINESE_MERCATOR"-> layertype. TIANDITU_TERRAIN_ANNOTATION_CHINESE_MERCATOR // Heaven and earth map vector national coordinate system 2000 map service"TIANDITU_VECTOR_2000"-> layertype. TIANDITU_VECTOR_2000 // Map vector country 2000 coordinate System Chinese annotation"TIANDITU_VECTOR_ANNOTATION_CHINESE_2000"-> layertype. TIANDITU_VECTOR_ANNOTATION_CHINESE_2000"TIANDITU_VECTOR_ANNOTATION_ENGLISH_2000"-> layertype. TIANDITU_VECTOR_ANNOTATION_ENGLISH_2000 // Map service of national coordinate system 2000"TIANDITU_IMAGE_2000"-> layertype. TIANDITU_IMAGE_2000 // Map image country 2000 Coordinates Chinese annotation"TIANDITU_IMAGE_ANNOTATION_CHINESE_2000"-> layertype. TIANDITU_IMAGE_ANNOTATION_CHINESE_2000"TIANDITU_IMAGE_ANNOTATION_ENGLISH_2000"-> layertype. TIANDITU_IMAGE_ANNOTATION_ENGLISH_2000 // Map service of national coordinate system 2000"TIANDITU_TERRAIN_2000"-> layertype. TIANDITU_TERRAIN_2000 // Map terrain country 2000 coordinate system Chinese annotation"TIANDITU_TERRAIN_ANNOTATION_CHINESE_2000" -> LayerType.TIANDITU_TERRAIN_ANNOTATION_CHINESE_2000
else-> LayerType.TIANDITU_VECTOR_2000}}} enum class LayerType {/** ** TIANDITU_VECTOR_MERCATOR, /** ** Tianditu_annotation_chinese_mercator, /** ** Tianditu_annotation_english_mercator, /** ** TIANDITU_IMAGE_MERCATOR, /** * TIANDITU_IMAGE_ANNOTATION_CHINESE_MERCATOR, /** ** TIANDITU_IMAGE_ANNOTATION_ENGLISH_MERCATOR, /** ** Mercator, /** ** TIANDITU_TERRAIN_ANNOTATION_CHINESE_MERCATOR, /** ** TIANDITU_VECTOR_2000, /** * Tianditu_annotation_chinese_2000, /** ** Tianditu_annotation_english_2000, /** ** TIANDITU_IMAGE_2000, /** * TIANDITU_IMAGE_ANNOTATION_CHINESE_2000, /** ** TIANDITU_IMAGE_ANNOTATION_ENGLISH_2000, /** ** TIANDITU_TERRAIN_2000, /** * TIANDITU_TERRAIN_ANNOTATION_CHINESE_2000} override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... / / note: Val RequestConfiguration = RequestConfiguration() requestConfiguration.getHeaders().put("referer"."http://www.arcgis.com"); webTiledLayer.setRequestConfiguration(requestConfiguration) webTiledLayer1.setRequestConfiguration(requestConfiguration) webTiledLayer.loadAsync() webTiledLayer1.loadAsync() val basemap = Basemap(webTiledLayer) basemap.getBaseLayers().add(webTiledLayer1) map.basemap = basemap mapView.map = map ... }Copy the code
Six, three-dimensional map
SceneView
The sample
Arcgisruntime uses a GeoView class as the base class of the map directly inherits from the ViewGroup, and then MapView and SceneView inherit from the GeoView as the containers of 2d and 3D maps respectively. Actually, just think of SceneView as a MapView, and think of ArcGISScene as an ArcGISMap.
Code examples:
<? xml version="1.0" encoding="utf-8"? > <androidx.constraintlayout.widget.ConstraintLayout 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"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.SceneViewActivity">
<com.esri.arcgisruntime.mapping.view.SceneView
android:id="@+id/sceneview"
android:layout_width="match_parent"
android:layout_height="match_parent">
</com.esri.arcgisruntime.mapping.view.SceneView>
</androidx.constraintlayout.widget.ConstraintLayout>
class SceneViewActivity : AppCompatActivity() {
private val brest_buildings =
" http://tiles.arcgis.com/tiles/P3ePLMYs2RVChkJx/arcgis/rest/services/Buildings_Brest/SceneServer"
private val elevation_image_service =
"http://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(com.vincent.arcgisdemo.R.layout.activity_scene_view)
val arcGISScene = ArcGISScene()
sceneview.scene = arcGISScene
}
override fun onResume() {
super.onResume()
sceneview.resume()
}
override fun onPause() {
super.onPause()
sceneview.pause()
}
override fun onDestroyDispose () {super.ondestroy () sceneViet.dispose ()}} Override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val arcGISTiledLayer = ArcGISTiledLayer("https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer")
arcGISScene.basemap = Basemap(arcGISTiledLayer)
sceneview.scene = arcGISScene
}
Copy the code
Show a THREE-DIMENSIONAL scene
3d visualizes data information from a perspective close to the real world. The use of 3D scenes is similar to MapView and ArcGISMap, and 2d data can be added into 3D scenes. Different from 2D, 3D scenes have elevation surface.
Elevation free surface (elevation surface
)
private val brest_buildings =
" http://tiles.arcgis.com/tiles/P3ePLMYs2RVChkJx/arcgis/rest/services/Buildings_Brest/SceneServer"override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val arcGISScene = ArcGISScene() arcGISScene.basemap = Basemap.createImagery() sceneview.scene = arcGISScene val sceneLayer = ArcGISSceneLayer(brest_buildings) ArcGISScene. OperationalLayers. Add (sceneLayer) / / set the 3 d scene view lens (camera) / / latitude - the latitude; It could be negative // //longitude; It could be negative // // Alt-altitude; Possibly negative // //heading -- camera horizontal; Can be negative // //pitch -- camera vertical orientation; // //roll- camera = camera (48.378, -4.494, 200.0, 345.0, 65.0, 0.0) sceneview. SetViewpointCamera (camera)}Copy the code
Using an elevation surface (ArcGISTiledElevationSource
,RasterElevationSource
)
ArcGISTiledElevationSource: online services as surface elevation
RasterElevationSource: Use the local DEM file as the elevation surface
private val elevation_image_service =
"http://elevation3d.arcgis.com/arcgis/rest/services/WorldElevation3D/Terrain3D/ImageServer"override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val arcGISScene = ArcGISScene() arcGISScene.basemap = Basemap.createImagery() sceneview.scene = arcGISScene val elevationSource = ArcGISTiledElevationSource(elevation_image_service) ArcGISScene. BaseSurface. ElevationSources. Add (elevationSource) / / set the 3 d scene view camera (camera) / / latitude - the latitude; It could be negative // //longitude; It could be negative // // Alt-altitude; Possibly negative // //heading -- camera horizontal; Can be negative // //pitch -- camera vertical orientation; Is not necessarily a negative / / / / roll - val rotation Angle camera = camera (28.4, 83.9, 10010.0, 10.0, 80.0, 0.0) sceneview. SetViewpointCamera (camera)}Copy the code
The experience of using and not using the high-level surface is similar to whether the height has changed or not. Interested students can try it for themselves.
For the rest, refer directly to the documentation:
- According to the scene
- Create a new scene
- Navigation scene view
- Adds features and graphics to the scene view
- Follow the graphics in the scene view
- Display the scene in augmented reality
7. Thermal image
The sample
API
GeoprocessingJob
Geoprocessing jobs are used to run geoprocessing tasks on servicesGeoprocessingParameters
Geoprocessing parameters contain input parameters that are sent to the target geoprocessing taskGeoprocessingResult
Output parameters returned from the serviceGeoprocessingTask
Used to run asweb
Geographic processing tasks for service publishing
You can view the specific effect through the code:
package com.vincent.arcgisdemo.ui
import android.app.DatePickerDialog
import android.app.Dialog
import android.app.ProgressDialog
import android.content.DialogInterface
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import com.esri.arcgisruntime.concurrent.Job
import com.esri.arcgisruntime.geometry.Point
import com.esri.arcgisruntime.geometry.SpatialReference
import com.esri.arcgisruntime.mapping.ArcGISMap
import com.esri.arcgisruntime.mapping.Basemap
import com.esri.arcgisruntime.mapping.Viewpoint
import com.esri.arcgisruntime.tasks.geoprocessing.GeoprocessingJob
import com.esri.arcgisruntime.tasks.geoprocessing.GeoprocessingString
import com.esri.arcgisruntime.tasks.geoprocessing.GeoprocessingTask
import com.haoge.easyandroid.easy.EasyToast
import com.vincent.arcgisdemo.R
import kotlinx.android.synthetic.main.activity_hot_spots.*
import kotlinx.android.synthetic.main.custom_alert_dialog.view.*
import java.text.ParseException
import java.text.SimpleDateFormat
import java.util.*
class HotSpotsActivity : AppCompatActivity() {
private val TAG = HotSpotsActivity::class.java.simpleName
private lateinit var mGeoprocessingTask: GeoprocessingTask
val hotspot_911_calls =
"https://sampleserver6.arcgisonline.com/arcgis/rest/services/911CallsHotspot/GPServer/911%20Calls%20Hotspot"
private lateinit var mMinDate: Date
private lateinit var mMaxDate: Date
private var canceled: Boolean = false
private lateinit var mGeoprocessingJob: GeoprocessingJob
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_hot_spots) val map = ArcGISMap(Basemap.createTopographic()) val center = Point(13671170.0, 5693633.0, SpatialReference. Create (3857)) Map.initialViewpoint = Viewpoint(Center, 57779.0) mapView. Map = map mGeoprocessingTask = GeoprocessingTask (hotspot_911_calls) mGeoprocessingTask. LoadAsync () CalendarButton. SetOnClickListener {showDateRangeDialog ()} showDateRangeDialog ()} / hot time interval * * * * choice analysis/private funshowDateRangeDialog() {
// create custom dialog
val dialog = Dialog(this)
val dialogView =
LayoutInflater.from(this).inflate(R.layout.custom_alert_dialog, null, false)
dialog.setContentView(dialogView)
dialog.setCancelable(true)
try {
val mSimpleDateFormatter = SimpleDateFormat("yyyy-MM-dd", Locale.US)
// set default date range for the data set
mMinDate = mSimpleDateFormatter.parse("1998-01-01")
mMaxDate = mSimpleDateFormatter.parse("1998-05-31")
} catch (e: ParseException) {
Log.e(TAG, "Error in date format: " + e.message)
}
dialogView.fromDateText.setOnClickListener {
showCalendar(InputCalendar.From, dialogView)
}
dialogView.toDateText.setOnClickListener {
showCalendar(InputCalendar.To, dialogView)
}
// ifbutton is clicked, close the custom dialog dialogView.analyzeButton.setOnClickListener { analyzeHotspots( dialogView.fromDateText.text.toString(), DialogView. ToDateText. Text. The toString ()) dialog. Dismiss ()} dialog. The show ()} / * * * display the date selector dialog, */ Private Fun showCalendar(inputCalendar: inputCalendar, dialogView: View) {// create a dateset listener
val onDateSetListener =
DatePickerDialog.OnDateSetListener { _, year, month, dayOfMonth ->
// build the correct date format for the query
val date = StringBuilder()
.append(year)
.append("-")
.append(month + 1)
.append("-")
.append(dayOfMonth)
// set the date to correct text view
if (inputCalendar === InputCalendar.From) {
dialogView.fromDateText.setText(date)
try {
// limit the min date to after from date
val mSimpleDateFormatter = SimpleDateFormat("yyyy-MM-dd", Locale.US)
mMinDate = mSimpleDateFormatter.parse(date.toString())
} catch (e: ParseException) {
e.printStackTrace()
}
} else if (inputCalendar === InputCalendar.To) {
dialogView.toDateText.setText(date)
try {
// limit the maximum date to before the to date
val mSimpleDateFormatter = SimpleDateFormat("yyyy-MM-dd", Locale.US)
mMaxDate = mSimpleDateFormatter.parse(date.toString())
} catch (e: ParseException) {
e.printStackTrace()
}
}
}
// define the date picker dialog
val calendar = Calendar.getInstance()
val datePickerDialog = DatePickerDialog(
this,
onDateSetListener,
calendar.get(Calendar.YEAR),
calendar.get(Calendar.MONTH),
calendar.get(Calendar.DAY_OF_MONTH)
)
datePickerDialog.datePicker.minDate = mMinDate.time
datePickerDialog.datePicker.maxDate = mMaxDate.time
if(inputCalendar === InputCalendar.From) { // start from calendar from min date datePickerDialog.updateDate(1998, 0, 1)} datepickerdialog.show ()} /** * After the work is completed, load the generated ArcGISMapImageLayer onto the map and reset the viewpoint of the MapView. */ private fun analyzeHotspots(from: String, to: String) {// Cancel mGeoprocessingJob last request mgeoProcessingJob.cancel () // Result generates a map image layer. Remove any of the layer is added to the map before mapView. Map. OperationalLayers. The clear () / /set canceled flag to false
canceled = false
// parameters
val paramsFuture = mGeoprocessingTask.createDefaultParametersAsync()
paramsFuture.addDoneListener {
val geoprocessingParameters = paramsFuture.get()
geoprocessingParameters.processSpatialReference = mapView.spatialReference
geoprocessingParameters.outputSpatialReference = mapView.spatialReference
val queryString = StringBuilder("(\"DATE\" > date '")
.append(from)
.append(" 00:00:00' AND \"DATE\" < date '")
.append(to)
.append("00:00:00)")
geoprocessingParameters.inputs["Query"] = GeoprocessingString(queryString.toString())
// create job
mGeoprocessingJob = mGeoprocessingTask.createJob(geoprocessingParameters)
// start job
mGeoprocessingJob.start()
// create a dialog to show progress of the geoprocessing job
val progressDialog = ProgressDialog(this)
progressDialog.setTitle("Geographic information processing.")
progressDialog.isIndeterminate = false
progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL)
progressDialog.max = 100
progressDialog.setCancelable(false)
progressDialog.setButton(
DialogInterface.BUTTON_NEGATIVE, "Cancel") { dialog, _ -> dialog? .dismiss() //set canceled flag to true
canceled = true
mGeoprocessingJob.cancel()
}
progressDialog.show()
// update progress
mGeoprocessingJob.addProgressChangedListener {
progressDialog.progress = mGeoprocessingJob.progress
}
mGeoprocessingJob.addJobDoneListener {
progressDialog.dismiss()
if (mGeoprocessingJob.status == Job.Status.SUCCEEDED) {
Log.i(TAG, "Job succeeded.") val hotspotMapImageLayer = mGeoprocessingJob.result? .mapImageLayer // add the new layer to the map mapView.map.operationalLayers.add(hotspotMapImageLayer) hotspotMapImageLayer? .addDoneLoadingListener { //set the map viewpoint to the MapImageLayer, once loaded
mapView.setViewpointGeometryAsync(hotspotMapImageLayer.fullExtent)
}
} else if (canceled) {
EasyToast.DEFAULT.show("Job canceled")
Log.i(TAG, "Job cancelled.")}else {
EasyToast.DEFAULT.show("Job did not succeed!")
Log.e(TAG, "Job did not succeed!")
}
}
}
}
override fun onResume() {
mapView.resume()
super.onResume()
}
override fun onPause() {
mapView.pause()
super.onPause()
}
override fun onDestroy() {
mapView.dispose()
super.onDestroy()
}
}
enum class InputCalendar {
From,
To
}
Copy the code
Because this part of the content has not been practiced in the project, it can only be simple to find the official example to view the specific use and relevant source notes, if you have this aspect of the use of experience, welcome to leave a message to exchange!