N_GIS 核心知识 N_ArcGis全家桶 N_Android
ArcGIS Runtime for Android
官方文档 ArcGIS Maps SDK for Kotlin api-reference 官方-示例 GitHub库
Key features
关键对象
MapView
MapView 是一种用户界面控件,用于在应用程序中显示单个地图。它包含内置功能,允许用户通过放大和缩小、重新定位地图显示或获取有关地图中元素的其他信息来浏览地图。它还管理一个或多个图形覆盖集合中的图形。由地图视图管理的图形始终显示在地图中显示的所有图层上方。
MapView 的创建有两种方法,一种是在 Layout 文件中直接写控件。一种是实例化
MapView mapView = new MapView(Context context);
<com.esri.android.map.MapView
android:id="@+id/map_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />get started
项目配置
增加 arcgis https://esri.jfrog.io/artifactory/arcgis仓库源
settings.gradle.kts
pluginManagement {
repositories {
// arcgis sdk 源
maven {
setUrl("https://esri.jfrog.io/artifactory/arcgis")
}
...
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
maven {
setUrl("https://esri.jfrog.io/artifactory/arcgis")
}
...
}
}
rootProject.name = "Application"
include(":app")引入依赖
build.gradle.kts
dependencies {
implementation("androidx.core:core-ktx:1.10.1")
....
implementation("com.esri.arcgisruntime:arcgis-android:100.15.0")
}配置 资源忽略, 不然就是:
Execution failed for task ':reference:mergeDebugJavaResource'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.MergeJavaResWorkActionbuild.gradle.kts
android {
....
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
//增加
excludes += "/META-INF/DEPENDENCIES"
}
}
}离线地图 (mmpk)
display-a-map-from-a-mobile-map-package open-mobile-map-package/ MobileMapPackage API
package cn.simae.spxxf.aar.activity
import android.os.Bundle
import android.util.Log
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import cn.simae.spxxf.R
import com.arcgismaps.mapping.MobileMapPackage
import com.arcgismaps.mapping.view.MapView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import java.io.File
class MapActivity : AppCompatActivity() {
val TAG = "MapActivity"
private lateinit var mapView: MapView
private lateinit var mapPackage: MobileMapPackage
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map)
// create and add the MapView to the lifecycle
mapView = findViewById<MapView>(R.id.mapView)
lifecycle.addObserver(mapView)//注意, 这行必须
// get the file path of the (.mmpk) file
val outputDirectory = getExternalFilesDir(null)
val mmpkfile = File(outputDirectory, "simple_anno_mmpk.mmpk")
Log.i(TAG, "onCreate, 加载文件: "+ mmpkfile)
loadMobileMapPackage(mmpkfile.absolutePath)
}
// load your mobile map package here. You call this method from onCreate()
private fun loadMobileMapPackage(mmpkFilePath: String) {
// create the mobile map package
mapPackage = MobileMapPackage(mmpkFilePath)
lifecycleScope.launch(Dispatchers.Main) {
mapPackage.load()
.onSuccess {
mapView.map = mapPackage.maps.first()
Log.i(TAG,"MapActivity 成功, 图层数量: ${mapView.map!!.operationalLayers.size}")
}
.onFailure {
Log.i(TAG,"MapActivity 失败")
showError(it.message.toString(), mapView)
}
}
}
override fun onDestroy() {
super.onDestroy()
}
private fun showError(message: String, view: View) {
Log.e(localClassName, message)
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
}
添加覆盖物
构造 GraphicsOverlay Add graphics to a map view
添加图形
// create a graphics overlay
val graphicsOverlay = GraphicsOverlay()
//坐标
val point = Point(114.10727402, 22.54862306, SpatialReference.wgs84())
//圆形
val redCircleSymbol = SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, Color.red, 10.0f)
val pierGraphic = Graphic(point, redCircleSymbol)
pierGraphic.attributes["type"] = "pier"
graphicsOverlay.graphics.add(pierGraphic)
// add graphics overlay to the geo view
mapView.graphicsOverlays.add(graphicsOverlay)添加图形+文本
val graphicsOverlay = GraphicsOverlay()
//140.85064697265625,\"lng\":114.107398
val point = Point(114.10727402, 22.54862306, SpatialReference.wgs84())
val redCircleSymbol = SimpleMarkerSymbol(SimpleMarkerSymbolStyle.Circle, Color.red, 20.0f)
val redCircle = Graphic(point, redCircleSymbol)
redCircle.attributes["ydbh"] = it.ydbh//Graphic 可以存自定义属性
graphicsOverlay.graphics.add(redCircle)
val textSymbol = TextSymbol()
textSymbol.text = "文本文本"
val text = Graphic(point, textSymbol)
text.attributes["ydbh"] = it.ydbh//Graphic 可以存自定义属性
graphicsOverlay.graphics.add(text)
Log.i(TAG, "loadingPoints: "+graphicsOverlay)
mapView.graphicsOverlays.add(graphicsOverlay)自定义覆盖物
关键是 MarkerSymbol 及其子类 : PictureMarkerSymbol 它可以绘制 BitmapDrawable, 所以可以将安卓View 转为BitmapDrawable 再给它 SimpleMarkerSymbol TextSymbol
fun createBitmapFromView(context: Context, view: View): Bitmap {
view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED)
view.layout(0, 0, view.measuredWidth, view.measuredHeight)
val bitmap = Bitmap.createBitmap(view.measuredWidth, view.measuredHeight, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
view.draw(canvas)
return bitmap
}
fun getSampleDetailBitmap(context: Context): BitmapDrawable {
val inflater = LayoutInflater.from(context)
val view = inflater.inflate(R.layout.map_sample_detail, null)
// 将 View 转为 Bitmap
val bitmap = createBitmapFromView(context, view)
// 将 Bitmap 转为 BitmapDrawable
val bd = BitmapDrawable(resources, bitmap)
return bd;
}
////////////////
var sampleDetail = getSampleDetailBitmap(this);
// 给定 BitmapDrawable创建 PictureMarkerSymbol
val symbol = PictureMarkerSymbol.createWithImage( sampleDetail)
//添加到 graphicsOverlay 中
val point = Point(lng.toDouble(), lat.toDouble(), SpatialReference.wgs84())
val graphics = Graphic(point, symbol)
XLog.i("$TAG, 添加 sampleDetail = "+graphics)
graphicsOverlay.graphics.add(graphics)事件监听
https://developers.arcgis.com/kotlin/api-reference/arcgis-maps-kotlin/com.arcgismaps.mapping.view/-geo-view/identify-graphics-overlays.html
覆盖物本身不能添加事件监听, 只有mapView全局的事件
//1. 监听全局点击 事件
mapView.onSingleTapConfirmed.collect{tapEvent ->
run {
// 2. 通过点击事件的位置, 再去匹配 graphicsOverlay 中 的元素
//参数说明:
//graphicsOverlay: 你希望识别图形的 GraphicsOverlay 对象。
//screenPoint: 用户点击或触摸的屏幕点。
//tolerance: 像素容差,用于确定识别附近图形的范围。
//returnPopupsOnly: 是否只返回包含弹出窗口的图形。
//maximumResults: 最大返回的图形数量。
val identifyResult: Result<com.arcgismaps.mapping.view.IdentifyGraphicsOverlayResult> = mapView.identifyGraphicsOverlay(
graphicsOverlay,
tapEvent.screenCoordinate,
1.0 ,
false, 1
)
identifyResult.onSuccess {
XLog.i(TAG + ", 点击目标 graphics " +it.graphics[0].attributes["ydbh"])
}
XLog.i(TAG + ", 点击事件 " + tapEvent)
}
}查询 Query
https://developers.arcgis.com/kotlin/query/
Relevant classes and members in the API ref
The following API is used to identify features in layers. The identify layers methods are defined in GeoView.
identifyLayer(): Identifies features in a specific layer.identifyLayer(): Identifies the topmost feature in a specific layer.identifyLayers(): Identifies features in all layers in the map or scene view.identifyLayers(): Identifies the topmost feature in each layer of the map or scene view.IdentifyLayerResult: Contains the results of an identify for a layer. Methods that identify on all layers have anIdentifyLayerResultfor each layer.
The following API is used to identify graphics in graphic overlays. The identify graphics overlay methods are defined in GeoView.
identifyGraphicsOverlay(): Identifies graphics in a specific graphics overlay.identifyGraphicsOverlay(): Identifies the topmost graphic in a specific graphics overlay.identifyGraphicsOverlays(): Identifies graphics in all graphics overlays in the map or scene view.identifyGraphicsOverlays(): Identifies the topmost graphic in each graphics overlay of the map or scene view.IdentifyGraphicsOverlayResult: Contains the results of an identify for a graphics overlay. Methods that identify on all graphics overlays have anIdentifyGraphicsOverlayResultfor each graphics overlay.
实例
单个图层查询
private suspend fun showLayerDetailDialog(tapEvent: SingleTapConfirmedEvent){
val layers = mapView.map!!.operationalLayers
layers.forEach {
val identifyLayersFuture :Result<IdentifyLayerResult> = mapView.identifyLayer(it, tapEvent.screenCoordinate, 1.0, false)
identifyLayersFuture.onSuccess { res ->
run {
if (res.geoElements.isNotEmpty()) {
///////////////
val sb = StringBuffer();
sb.append(("$TAG 图层: ${it.name}, 结果: " + res.geoElements.get(0).attributes))
.append("\n")
.append(" mapPoint x = ${tapEvent.mapPoint?.x} , = ${tapEvent.mapPoint?.y}")
val dialogBuilder = AlertDialog.Builder(this)
val textView = TextView(this)
textView.setText(sb.toString())
val alertDialog = dialogBuilder
.setView(textView)
.create()
alertDialog.show()
}
}
}
}
}多个图层查询
val identifyLayersFuture:Result<List<IdentifyLayerResult>> = mapView.identifyLayers( tapEvent.screenCoordinate, 1.0, false )
identifyLayersFuture.onSuccess { res ->
run {
if (res.isNotEmpty()) {
for (ilr in res){
val sb = StringBuffer();
sb.append(("$TAG 图层: ${ilr.layerContent.name}, 结果: " + ilr.geoElements.get(0).attributes))
.append("\n")
.append(" mapPoint x = ${tapEvent.mapPoint?.x} , = ${tapEvent.mapPoint?.y}")
val dialogBuilder = AlertDialog.Builder(this)
val textView = TextView(this)
textView.setText(sb.toString())
val alertDialog = dialogBuilder
.setView(textView)
.create()
alertDialog.show()
}
}
}
}三普
/**
* 查询图层显示
*/
@SuppressLint("SetTextI18n")
private suspend fun showLayerDetail(tapEvent: SingleTapConfirmedEvent){
val identifyLayersFuture:Result<List<IdentifyLayerResult>> = mapView.identifyLayers( tapEvent.screenCoordinate, 1.0, false )
identifyLayersFuture.onSuccess { res ->
run {
if (res.isNotEmpty()) {
val builder = AlertDialog.Builder(this)
val layout = LinearLayout(this)// ui 弹窗布局
layout.orientation = LinearLayout.VERTICAL
layout.setPadding(20, 20, 20, 30) // 设置内边距
builder.setTitle("信息")
builder.setView(layout)
//
val mappKeys = mapOf<String,String>("poxiang" to "坡向", "poxing" to "坡型", "ZLDWMC" to "坐落单位"
, "XZQMC" to "乡镇名称", "DLMC" to "土地利用类型", "my" to "母岩")
XLog.i("$TAG ================查询=============")
for (ilr in res){
val layerText = TextView(this)
layerText.text = "图层: ${ilr.layerContent.name}"
val typeface = Typeface.defaultFromStyle(Typeface.BOLD)
layerText.typeface = typeface
layerText.setPadding(0, 30, 0, 0)
layout.addView(layerText)//ui 图层名称
var hasContext = false
if (ilr.geoElements.isNotEmpty()){
val sb = StringBuffer();
sb.append(("图层: ${ilr.layerContent.name}, length: ${ilr.geoElements.size}, 0结果: " + ilr.geoElements[0].attributes))
XLog.w("$TAG $sb")
//
val attributes = ilr.geoElements[0].attributes
for (attribute in attributes){
val mnName = mappKeys[attribute.key] ?: continue
hasContext = true
val rowLayout = LinearLayout(this)
rowLayout.orientation = LinearLayout.HORIZONTAL
rowLayout.weightSum = 2f // 分配权重,使按钮平均分布
val attrKey = TextView(this)
attrKey.text = "$mnName: "
rowLayout.addView(attrKey)//ui 属性中文
val attrVal = TextView(this)
attrVal.text = attribute.value.toString()//ui 属性值
rowLayout.addView(attrVal)
layout.addView(rowLayout)
}
}
if (!hasContext){//没有
layout.removeView(layerText )
}
}
val dialogBuilder = AlertDialog.Builder(this)
val alertDialog = dialogBuilder
.setView(layout)
.create()
alertDialog.show()
}
}
}
}Reduce your APK size
https://developers.arcgis.com/android/license-and-deployment/reduce-your-apk-size/