将适用于安卓 MapLibre 的原生 SDK 与 Amazon Location Service 配合使用 - Amazon Location Service

本文属于机器翻译版本。若本译文内容与英语原文存在差异,则一律以英文原文为准。

将适用于安卓 MapLibre 的原生 SDK 与 Amazon Location Service 配合使用

使用MapLibre原生 SDK 将交互式地图嵌入到您的 Android 应用程序中。

Android MapLibre 原生 SDK 是一个基于 Mapbox Nativ e 的库,与亚马逊定位服务地图 API 提供的样式和图块兼容。您可以集成适用于 Android 的 MapLibre Native SDK,在您的 Android 应用程序中嵌入带有可缩放、可自定义的矢量地图的交互式地图视图。

本教程介绍如何将适用于 Android MapLibre 的原生 SDK 与亚马逊定位集成。本教程的示例应用程序已作为 Amazon Location Service 示例存储库的一部分提供GitHub

构建应用程序:初始化

要初始化应用程序,请执行以下操作:

  1. 使用空活动模板创建一个新的 Android Studio 项目。

  2. 确保为项目语言选择了 Kotlin

  3. 选择 API 14 的最低开发工具包:Android 4.0 (Ice Cream Sandwich) 或更高版本。

  4. 打开项目结构,然后转到文件 > 项目结构... 选择依赖项部分。

  5. <All Modules> 选中后,选择 + 按钮以添加新的库依赖项

  6. 添加 AWS Android 开发工具包版本 2.20.0 或更高版本。例如:com.amazonaws:aws-android-sdk-core:2.20.0

  7. 添加适用于 Android MapLibre 的原生 SDK 版本 9.4.0 或更高版本。例如:org.maplibre.gl:android-sdk:9.4.0

  8. build.gradle 文件的项目级别,添加以下 maven 存储库以访问适用于 Android 的 MapLibre软件包:

    allprojects { repositories { // Retain your existing repositories google() jcenter() // Declare the repositories for MapLibre mavenCentral() } }

构建应用程序:配置

要使用您的资源和 AWS 区域配置应用程序,请执行以下操作:

<?xml version="1.0" encoding="utf-8"?> <resources> <string name="identityPoolId">us-east-1:54f2ba88-9390-498d-aaa5-0d97fb7ca3bd</string> <string name="mapName">ExampleMap</string> <string name="awsRegion">us-east-1</string> </resources>

构建应用程序:活动布局

编辑 app/src/main/res/layout/activity_main.xml

  • 添加 MapView,用于渲染地图。这还将设置地图的初始中心点。

  • 添加 TextView,显示属性。

<?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=".MainActivity"> <com.mapbox.mapboxsdk.maps.MapView android:id="@+id/mapView" android:layout_width="match_parent" android:layout_height="match_parent" app:mapbox_cameraTargetLat="49.2819" app:mapbox_cameraTargetLng="-123.1187" app:mapbox_cameraZoom="12" app:mapbox_uiAttribution="false" app:mapbox_uiLogo="false" /> <TextView android:id="@+id/attributionView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="#80808080" android:padding="5sp" android:textColor="@android:color/black" android:textSize="10sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" tools:ignore="SmallSp" /> </androidx.constraintlayout.widget.ConstraintLayout>
注意

您必须在应用程序或文档中为使用的每个数据提供程序提供文字标记或文本属性。属性字符串包含在样式描述符响应中的 sources.esri.attributionsources.here.attributionsource.grabmaptiles.attribution 键下。在数据提供程序处使用 Amazon Location 资源时,请务必阅读服务条款和条件

构建应用程序:请求转换

创建一个名为 SigV4Interceptor,拦截 AWS 请求的类,并使用 Signature Version 4 对请求进行签名。创建主活动时,将在用于获取地图资源的 HTTP 客户端中进行注册。

package aws.location.demo.okhttp import com.amazonaws.DefaultRequest import com.amazonaws.auth.AWS4Signer import com.amazonaws.auth.AWSCredentialsProvider import com.amazonaws.http.HttpMethodName import com.amazonaws.util.IOUtils import okhttp3.HttpUrl import okhttp3.Interceptor import okhttp3.Request import okhttp3.Response import okio.Buffer import java.io.ByteArrayInputStream import java.net.URI class SigV4Interceptor( private val credentialsProvider: AWSCredentialsProvider, private val serviceName: String ) : Interceptor { override fun intercept(chain: Interceptor.Chain): Response { val originalRequest = chain.request() if (originalRequest.url().host().contains("amazonaws.com")) { val signer = if (originalRequest.url().encodedPath().contains("@")) { // the presence of "@" indicates that it doesn't need to be double URL-encoded AWS4Signer(false) } else { AWS4Signer() } val awsRequest = toAWSRequest(originalRequest, serviceName) signer.setServiceName(serviceName) signer.sign(awsRequest, credentialsProvider.credentials) return chain.proceed(toSignedOkHttpRequest(awsRequest, originalRequest)) } return chain.proceed(originalRequest) } companion object { fun toAWSRequest(request: Request, serviceName: String): DefaultRequest<Any> { // clone the request (AWS-style) so that it can be populated with credentials val dr = DefaultRequest<Any>(serviceName) // copy request info dr.httpMethod = HttpMethodName.valueOf(request.method()) with(request.url()) { dr.resourcePath = uri().path dr.endpoint = URI.create("${scheme()}://${host()}") // copy parameters for (p in queryParameterNames()) { if (p != "") { dr.addParameter(p, queryParameter(p)) } } } // copy headers for (h in request.headers().names()) { dr.addHeader(h, request.header(h)) } // copy the request body val bodyBytes = request.body()?.let { body -> val buffer = Buffer() body.writeTo(buffer) IOUtils.toByteArray(buffer.inputStream()) } dr.content = ByteArrayInputStream(bodyBytes ?: ByteArray(0)) return dr } fun toSignedOkHttpRequest( awsRequest: DefaultRequest<Any>, originalRequest: Request ): Request { // copy signed request back into an OkHttp Request val builder = Request.Builder() // copy headers from the signed request for ((k, v) in awsRequest.headers) { builder.addHeader(k, v) } // start building an HttpUrl val urlBuilder = HttpUrl.Builder() .host(awsRequest.endpoint.host) .scheme(awsRequest.endpoint.scheme) .encodedPath(awsRequest.resourcePath) // copy parameters from the signed request for ((k, v) in awsRequest.parameters) { urlBuilder.addQueryParameter(k, v) } return builder.url(urlBuilder.build()) .method(originalRequest.method(), originalRequest.body()) .build() } } }

构建应用程序:主活动

主活动负责初始化将向用户显示的视图。这涉及:

  • 实例化 Amazon Cognito CredentialsProvider

  • 注册 Signature Version 4 拦截程序。

  • 通过将地图指向地图样式描述符并显示适当的属性来配置地图。

MainActivity 还负责将生命周期事件转发到地图视图,使其能够在两次调用之间保持活动视区。

package aws.location.demo.maplibre import android.os.Bundle import android.widget.TextView import androidx.appcompat.app.AppCompatActivity import aws.location.demo.okhttp.SigV4Interceptor import com.amazonaws.auth.CognitoCachingCredentialsProvider import com.amazonaws.regions.Regions import com.mapbox.mapboxsdk.Mapbox import com.mapbox.mapboxsdk.maps.MapView import com.mapbox.mapboxsdk.maps.Style import com.mapbox.mapboxsdk.module.http.HttpRequestUtil import okhttp3.OkHttpClient private const val SERVICE_NAME = "geo" class MainActivity : AppCompatActivity() { private var mapView: MapView? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // configuration val identityPoolId = getString(R.string.identityPoolId) val region = getString(R.string.awsRegion) val mapName = getString(R.string.mapName) // Credential initialization val credentialProvider = CognitoCachingCredentialsProvider( applicationContext, identityPoolId, Regions.fromName(identityPoolId.split(":").first()) ) // initialize MapLibre Mapbox.getInstance(this, null) HttpRequestUtil.setOkHttpClient( OkHttpClient.Builder() .addInterceptor(SigV4Interceptor(credentialProvider, SERVICE_NAME)) .build() ) // initialize the view setContentView(R.layout.activity_main) // initialize the map view mapView = findViewById(R.id.mapView) mapView?.onCreate(savedInstanceState) mapView?.getMapAsync { map -> map.setStyle( Style.Builder() .fromUri("https://maps.geo.${region}.amazonaws.com/maps/v0/maps/${mapName}/style-descriptor") ) { style -> findViewById<TextView>(R.id.attributionView).text = style.sources.first()?.attribution } } } override fun onStart() { super.onStart() mapView?.onStart() } override fun onResume() { super.onResume() mapView?.onResume() } override fun onPause() { super.onPause() mapView?.onPause() } override fun onStop() { super.onStop() mapView?.onStop() } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) mapView?.onSaveInstanceState(outState) } override fun onLowMemory() { super.onLowMemory() mapView?.onLowMemory() } override fun onDestroy() { super.onDestroy() mapView?.onDestroy() } }

运行此应用程序会以您选择的样式显示全屏地图。此示例作为 Amazon Location Service 示例存储库的一部分提供GitHub