Skip to content

Commit

Permalink
Add doc for annotation (maplibre#1135)
Browse files Browse the repository at this point in the history
Co-authored-by: Hsieh Chin Fan <[email protected]>
Co-authored-by: Bart Louwers <[email protected]>
Co-authored-by: Bart Louwers <[email protected]>
  • Loading branch information
4 people authored Jun 13, 2023
1 parent 1438791 commit ca17d6b
Show file tree
Hide file tree
Showing 7 changed files with 302 additions and 0 deletions.
1 change: 1 addition & 0 deletions docs/mdbook/src/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
89 changes: 89 additions & 0 deletions docs/mdbook/src/android/annotation-guide.md
Original file line number Diff line number Diff line change
@@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
@@ -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>
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down

0 comments on commit ca17d6b

Please sign in to comment.