Add doc for annotation (#1135)

Co-authored-by: Hsieh Chin Fan <pham@topo.tw>
Co-authored-by: Bart Louwers <bart@emeel.net>
Co-authored-by: Bart Louwers <bart.louwers@gmail.com>
This commit is contained in:
Hsieh Chin Fan
2023-06-13 17:42:52 +08:00
committed by GitHub
parent 1438791e3f
commit ca17d6bfe6
7 changed files with 302 additions and 0 deletions

View File

@@ -12,4 +12,5 @@
- [MapLibre Native for Android](./android/README.md)
- [Quickstart](./android/getting-started-guide.md)
- [Annotation: Marker](./android/annotation-guide.md)
- [Location Component](./android/location-component-guide.md)

View File

@@ -0,0 +1,89 @@
# Annotation: Marker
This guide will show you how to add Markers in the map.
`Annotation` is an overlay on top of a Map. In package
`com.mapbox.mapboxsdk.annotations`, it has the following subclasses:
1. [Marker]
2. [Polyline]
3. [Polygon]
A Marker shows an icon image at a geographical location. By default, marker uses
a [provided image] as its icon.
![marker image]
Or, the icon can be customized using [IconFactory] to generate an
[Icon] using a provided image.
For more customization, please read the documentation about [MarkerOptions].
In this showcase, we continue the code from the [Quickstart],
rename Activity into `JsonApiActivity`,
and pull the GeoJSON data from a free and public API.
Then add markers to the map with GeoJSON:
1. In your module Gradle file (usually `<project>/<app-module>/build.gradle`), add
`okhttp` to simplify code for making HTTP requests.
```gradle
dependencies {
...
implementation 'com.squareup.okhttp3:okhttp:4.10.0'
...
}
```
2. Sync your Android project the with Gradle files.
3. In `JsonApiActivity` we add a new variable for `MapboxMap`.
It is used to add annotations to the map instance.
```kotlin
{{#include ../../../../platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/JsonApiActivity.kt:top}}
```
4. Call `mapview.getMapSync()` in order to get a `MapboxMap` object.
After `mapboxMap` is assigned, call the `getEarthQuakeDataFromUSGS()` method
to make a HTTP request and transform data into the map annotations.
```kotlin
{{#include ../../../../platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/JsonApiActivity.kt:mapAsync}}
```
5. Define a function `getEarthQuakeDataFromUSGS()` to fetch GeoJSON data from a public API.
If we successfully get the response, call `addMarkersToMap()` on the UI thread.
```kotlin
{{#include ../../../../platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/JsonApiActivity.kt:getEarthquakes}}
```
6. Now it is time to add markers into the map.
- In the `addMarkersToMap()` method, we define two types of bitmap for the marker icon.
- For each feature in the GeoJSON, add a marker with a snippet about earthquake details.
- If the magnitude of an earthquake is bigger than 6.0, we use the red icon. Otherwise, we use the blue one.
- Finally, move the camera to the bounds of the newly added markers
```kotlin
{{#include ../../../../platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/JsonApiActivity.kt:addMarkers}}
```
7. Here is the final result. For the full contents of `JsonApiActivity`, please visit source code of [Test APP]
<div style="align: center">
<img src="https://github.com/maplibre/maplibre-native/assets/19887090/00446249-9b19-4a48-8a46-00d4c5a2f981" alt="Screenshot with the map in demotile style">
</div>
[Marker]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20for%20-android/com.mapbox.mapboxsdk.annotations/-marker/index.html
[provided image]: https://github.com/maplibre/maplibre-native/blob/main/platform/android/MapboxGLAndroidSDK/src/main/res/drawable-xxxhdpi/maplibre_marker_icon_default.png
[Polyline]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20for%20-android/com.mapbox.mapboxsdk.annotations/-polyline/index.html
[Polygon]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20for%20-android/com.mapbox.mapboxsdk.annotations/-polygon/index.html
[marker image]: https://raw.githubusercontent.com/maplibre/maplibre-native/main/test/fixtures/sprites/default_marker.png
[IconFactory]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20for%20-android/com.mapbox.mapboxsdk.annotations/-icon-factory/index.html
[Icon]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20for%20-android/com.mapbox.mapboxsdk.annotations/-icon/index.html
[Quickstart]: ./getting-started-guide.md
[mvn]: https://mvnrepository.com/artifact/org.maplibre.gl/android-plugin-annotation-v9
[Android Developer Documentation]: https://developer.android.com/topic/libraries/architecture/coroutines
[MarkerOptions]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20for%20-android/com.mapbox.mapboxsdk.annotations/-marker-options/index.html
[Test App]: https://github.com/maplibre/maplibre-native/tree/main/platform/android/MapboxGLAndroidSDKTestApp/src/main/java/com/mapbox/mapboxsdk/testapp/activity/annotation/JsonApiActivity.kt

View File

@@ -111,6 +111,19 @@
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.annotation.JsonApiActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:description="@string/description_add_makers_by_json_api"
android:exported="true"
android:label="@string/activity_json_api">
<meta-data
android:name="@string/category"
android:value="@string/category_annotation" />
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".activity.FeatureOverviewActivity" />
</activity>
<activity
android:name=".activity.camera.CameraAnimationTypeActivity"
android:description="@string/description_camera_animation_types"

View File

@@ -0,0 +1,185 @@
package org.maplibre.android.testapp.activity.annotation
import android.graphics.Color
import android.os.Bundle
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.toBitmap
import com.mapbox.geojson.FeatureCollection
import com.mapbox.geojson.Point
import okhttp3.Call
import okhttp3.Callback
import okhttp3.HttpUrl.Companion.toHttpUrl
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import org.maplibre.android.MapLibre
import org.maplibre.android.annotations.IconFactory
import org.maplibre.android.annotations.MarkerOptions
import org.maplibre.android.camera.CameraPosition
import org.maplibre.android.geometry.LatLng
import org.maplibre.android.geometry.LatLngBounds
import org.maplibre.android.maps.MapLibreMap
import org.maplibre.android.maps.MapView
import org.maplibre.android.testapp.R
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.Locale
/* ANCHOR: top */
class JsonApiActivity : AppCompatActivity() {
// Declare a variable for MapView
private lateinit var mapView: MapView
// Declare a variable for MapboxMap
private lateinit var maplibreMap: MapLibreMap
/* ANCHOR_END: top */
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Init MapLibre
MapLibre.getInstance(this)
// Init layout view
setContentView(R.layout.activity_json_api)
// Init the MapView
mapView = findViewById(R.id.mapView)
/* ANCHOR: mapAsync */
mapView.getMapAsync { map ->
maplibreMap = map
maplibreMap.setStyle("https://demotiles.maplibre.org/style.json")
// Fetch data from USGS
getEarthQuakeDataFromUSGS()
}
/* ANCHOR_END: mapAsync */
}
/* ANCHOR: getEarthquakes */
// Get Earthquake data from usgs.gov, read API doc at:
// https://earthquake.usgs.gov/fdsnws/event/1/
private fun getEarthQuakeDataFromUSGS() {
val url = "https://earthquake.usgs.gov/fdsnws/event/1/query".toHttpUrl().newBuilder()
.addQueryParameter("format", "geojson")
.addQueryParameter("starttime", "2022-01-01")
.addQueryParameter("endtime", "2022-12-31")
.addQueryParameter("minmagnitude", "5.8")
.addQueryParameter("latitude", "24")
.addQueryParameter("longitude", "121")
.addQueryParameter("maxradius", "1.5")
.build()
val request: Request = Request.Builder().url(url).build()
OkHttpClient().newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Toast.makeText(this@JsonApiActivity, "Fail to fetch data", Toast.LENGTH_SHORT)
.show()
}
override fun onResponse(call: Call, response: Response) {
val featureCollection = response.body?.string()
?.let(FeatureCollection::fromJson)
?: return
// If FeatureCollection in response is not null
// Then add markers to map
runOnUiThread { addMarkersToMap(featureCollection) }
}
})
}
/* ANCHOR_END: getEarthquakes */
/* ANCHOR: addMarkers */
private fun addMarkersToMap(data: FeatureCollection) {
val bounds = mutableListOf<LatLng>()
// Get bitmaps for marker icon
val infoIconDrawable = ResourcesCompat.getDrawable(
this.resources,
// Intentionally specify package name
// This makes copy from another project easier
org.maplibre.android.R.drawable.maplibre_info_icon_default,
null
)!!
val bitmapBlue = infoIconDrawable.toBitmap()
val bitmapRed = infoIconDrawable
.mutate()
.apply { setTint(Color.RED) }
.toBitmap()
// Add symbol for each point feature
data.features()?.forEach { feature ->
val geometry = feature.geometry()?.toJson() ?: return@forEach
val point = Point.fromJson(geometry) ?: return@forEach
val latLng = LatLng(point.latitude(), point.longitude())
bounds.add(latLng)
// Contents in InfoWindow of each marker
val title = feature.getStringProperty("title")
val epochTime = feature.getNumberProperty("time")
val dateString = SimpleDateFormat("yyyy/MM/dd HH:mm", Locale.TAIWAN).format(epochTime)
// If magnitude > 6.0, show marker with red icon. If not, show blue icon instead
val mag = feature.getNumberProperty("mag")
val icon = IconFactory.getInstance(this)
.fromBitmap(if (mag.toFloat() > 6.0) bitmapRed else bitmapBlue)
// Use MarkerOptions and addMarker() to add a new marker in map
val markerOptions = MarkerOptions()
.position(latLng)
.title(dateString)
.snippet(title)
.icon(icon)
maplibreMap.addMarker(markerOptions)
}
// Move camera to newly added annotations
maplibreMap.getCameraForLatLngBounds(LatLngBounds.fromLatLngs(bounds))?.let {
val newCameraPosition = CameraPosition.Builder()
.target(it.target)
.zoom(it.zoom - 0.5)
.build()
maplibreMap.cameraPosition = newCameraPosition
}
}
/* ANCHOR_END: addMarkers */
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 onLowMemory() {
super.onLowMemory()
mapView.onLowMemory()
}
override fun onDestroy() {
super.onDestroy()
mapView.onDestroy()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
mapView.onSaveInstanceState(outState)
}
}

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.maplibre.android.maps.MapView
android:id="@id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>

View File

@@ -8,6 +8,7 @@
<string name="description_map_fragment_backstack">Showcase using a Map Fragment with a fragment backstack</string>
<string name="description_multimap">Activity with multiple maps on screen</string>
<string name="description_press_for_marker">Add marker to map on long press</string>
<string name="description_add_makers_by_json_api">Use JSON API to add markers</string>
<string name="description_camera_zoom">Different types of zoom methods</string>
<string name="description_minmax_zoom">Configure a max and min zoomlevel</string>
<string name="description_info_window">Learn how to handle the InfoWindow</string>

View File

@@ -11,6 +11,7 @@
<string name="activity_polygon">Polygon</string>
<string name="activity_press_for_marker">Press Map For Marker</string>
<string name="activity_add_remove_markers">Zoom function with SymbolLayer</string>
<string name="activity_json_api">Add Markers From JSON API</string>
<string name="activity_info_window">Standard InfoWindow</string>
<string name="activity_infowindow_adapter">Custom InfoWindow</string>
<string name="activity_dynamic_infowindow_adapter">Custom Dynamic InfoWindow</string>