Split out Android examples to separate documentation site (#2978)
This commit is contained in:
@@ -92,3 +92,16 @@ To run the benchmarks (for Android) include the following line on a PR comment:
|
||||
## Profiling
|
||||
|
||||
[maplibre-native/docs/mdbook](https://maplibre.org/maplibre-native/docs/book/) describes how Tracy can be used for profiling.
|
||||
|
||||
|
||||
## Examples Documentation
|
||||
|
||||
To build the Examples Documentation you need to have Docker installed.
|
||||
|
||||
From `platform/android`, run:
|
||||
|
||||
```
|
||||
make mkdocs
|
||||
```
|
||||
|
||||
Next, visit http://localhost:8000/maplibre-native/android/examples/
|
||||
@@ -311,3 +311,13 @@ clean:
|
||||
./MapLibreAndroidTestApp/src/androidTest/java/org/maplibre/android/testapp/activity/gen \
|
||||
./MapLibreAndroid/src/main/assets \
|
||||
./MapLibreAndroidTestApp/src/main/assets/integration
|
||||
|
||||
### MkDocs Documentation ########################################################
|
||||
|
||||
.PHONY: mkdocs
|
||||
mkdocs:
|
||||
docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material
|
||||
|
||||
.PHONY: mkdocs-build
|
||||
mkdocs-build:
|
||||
docker run --rm -v ${PWD}:/docs squidfunk/mkdocs-material build --strict
|
||||
@@ -65,6 +65,9 @@ public class AttributionParser {
|
||||
*/
|
||||
@NonNull
|
||||
public String createAttributionString(boolean shortenedOutput) {
|
||||
if (attributions.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder stringBuilder = new StringBuilder(withCopyrightSign ? "" : "© ");
|
||||
int counter = 0;
|
||||
for (Attribution attribution : attributions) {
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.maplibre.android.snapshotter
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.*
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.text.Html
|
||||
@@ -515,15 +516,7 @@ open class MapSnapshotter(context: Context, options: Options) {
|
||||
drawAttribution(canvas, measure, anchorPoint)
|
||||
} else {
|
||||
val snapshot = mapSnapshot.bitmap
|
||||
Logger.e(
|
||||
TAG,
|
||||
String.format(
|
||||
"Could not generate attribution for snapshot size: %s x %s." + " You are required to provide your own attribution for the used sources: %s",
|
||||
snapshot.width,
|
||||
snapshot.height,
|
||||
mapSnapshot.attributions
|
||||
)
|
||||
)
|
||||
Logger.e(TAG, "Could not generate attribution for snapshot size: ${snapshot.width} x ${snapshot.height}. You are required to provide your own attribution for the used sources: ${mapSnapshot.attributions.joinToString()}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -544,7 +537,17 @@ open class MapSnapshotter(context: Context, options: Options) {
|
||||
textView.textSize = 10 * scale
|
||||
textView.setTextColor(textColor)
|
||||
textView.setBackgroundResource(R.drawable.maplibre_rounded_corner)
|
||||
textView.text = Html.fromHtml(createAttributionString(mapSnapshot, shortText))
|
||||
val attributionString = createAttributionString(mapSnapshot, shortText)
|
||||
if (attributionString == "") {
|
||||
Logger.w(
|
||||
TAG,
|
||||
String.format(
|
||||
"Attribution string is empty. Make sure you provide your own attribution for the used sources if needed.",
|
||||
)
|
||||
)
|
||||
return TextView(context)
|
||||
}
|
||||
textView.text = fromHTML(attributionString)
|
||||
textView.measure(widthMeasureSpec, heightMeasureSpec)
|
||||
textView.layout(0, 0, textView.measuredWidth, textView.measuredHeight)
|
||||
return textView
|
||||
@@ -770,3 +773,9 @@ open class MapSnapshotter(context: Context, options: Options) {
|
||||
private const val LOGO_MARGIN_DP = 4
|
||||
}
|
||||
}
|
||||
|
||||
fun fromHTML(source: String) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
Html.fromHtml(source, Html.FROM_HTML_MODE_LEGACY)
|
||||
} else {
|
||||
Html.fromHtml(source)
|
||||
}
|
||||
|
||||
@@ -362,6 +362,21 @@ class AttributionParseTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Throws(Exception::class)
|
||||
fun testOutputNoAttribution() {
|
||||
val attributionParser = AttributionParser.Options(RuntimeEnvironment.application)
|
||||
.withAttributionData("")
|
||||
.withCopyrightSign(false)
|
||||
.withImproveMap(false)
|
||||
.build()
|
||||
Assert.assertEquals(
|
||||
"Attribution string should match",
|
||||
"",
|
||||
attributionParser.createAttributionString()
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val STREETS_ATTRIBUTION =
|
||||
"<a href=\"https://www.mapbox.com/about/maps/\" target=\"_blank\">© MapLibre</a> <a href=\"http://www.openstreetmap.org/about/\" target=\"_blank\">© OpenStreetMap</a> \n"
|
||||
|
||||
@@ -27,7 +27,7 @@ import java.io.IOException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.Locale
|
||||
|
||||
/* ANCHOR: top */
|
||||
// # --8<-- [start:top]
|
||||
class JsonApiActivity : AppCompatActivity() {
|
||||
|
||||
// Declare a variable for MapView
|
||||
@@ -35,7 +35,7 @@ class JsonApiActivity : AppCompatActivity() {
|
||||
|
||||
// Declare a variable for MapLibreMap
|
||||
private lateinit var maplibreMap: MapLibreMap
|
||||
/* ANCHOR_END: top */
|
||||
// # --8<-- [end:top]
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
@@ -48,7 +48,7 @@ class JsonApiActivity : AppCompatActivity() {
|
||||
// Init the MapView
|
||||
mapView = findViewById(R.id.mapView)
|
||||
|
||||
/* ANCHOR: mapAsync */
|
||||
// # --8<-- [start:mapAsync]
|
||||
mapView.getMapAsync { map ->
|
||||
maplibreMap = map
|
||||
|
||||
@@ -57,10 +57,10 @@ class JsonApiActivity : AppCompatActivity() {
|
||||
// Fetch data from USGS
|
||||
getEarthQuakeDataFromUSGS()
|
||||
}
|
||||
/* ANCHOR_END: mapAsync */
|
||||
// # --8<-- [end:mapAsync]
|
||||
}
|
||||
|
||||
/* ANCHOR: getEarthquakes */
|
||||
// # --8<-- [start:getEarthquakes]
|
||||
// Get Earthquake data from usgs.gov, read API doc at:
|
||||
// https://earthquake.usgs.gov/fdsnws/event/1/
|
||||
private fun getEarthQuakeDataFromUSGS() {
|
||||
@@ -91,9 +91,9 @@ class JsonApiActivity : AppCompatActivity() {
|
||||
}
|
||||
})
|
||||
}
|
||||
/* ANCHOR_END: getEarthquakes */
|
||||
// # --8<-- [end:getEarthquakes]
|
||||
|
||||
/* ANCHOR: addMarkers */
|
||||
// # --8<-- [start:addMarkers]
|
||||
private fun addMarkersToMap(data: FeatureCollection) {
|
||||
val bounds = mutableListOf<LatLng>()
|
||||
|
||||
@@ -146,7 +146,7 @@ class JsonApiActivity : AppCompatActivity() {
|
||||
maplibreMap.cameraPosition = newCameraPosition
|
||||
}
|
||||
}
|
||||
/* ANCHOR_END: addMarkers */
|
||||
// # --8<-- [end:addMarkers]
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
@@ -110,6 +110,7 @@ class GestureDetectorActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
fun attachListeners() {
|
||||
// # --8<-- [start:addOnMoveListener]
|
||||
maplibreMap.addOnMoveListener(
|
||||
object : OnMoveListener {
|
||||
override fun onMoveBegin(detector: MoveGestureDetector) {
|
||||
@@ -132,6 +133,7 @@ class GestureDetectorActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
)
|
||||
// # --8<-- [end:addOnMoveListener]
|
||||
maplibreMap.addOnRotateListener(
|
||||
object : OnRotateListener {
|
||||
override fun onRotateBegin(detector: RotateGestureDetector) {
|
||||
|
||||
@@ -52,6 +52,7 @@ class LatLngBoundsActivity : AppCompatActivity() {
|
||||
binding.mapView.getMapAsync { map ->
|
||||
maplibreMap = map
|
||||
|
||||
// # --8<-- [start:featureCollection]
|
||||
val featureCollection: FeatureCollection =
|
||||
fromJson(GeoParseUtil.loadStringFromAssets(this, "points-sf.geojson"))
|
||||
bounds = createBounds(featureCollection)
|
||||
@@ -59,6 +60,7 @@ class LatLngBoundsActivity : AppCompatActivity() {
|
||||
map.getCameraForLatLngBounds(bounds, createPadding(peekHeight))?.let {
|
||||
map.cameraPosition = it
|
||||
}
|
||||
// # --8<-- [end:featureCollection]
|
||||
|
||||
try {
|
||||
loadStyle(featureCollection)
|
||||
@@ -71,7 +73,7 @@ class LatLngBoundsActivity : AppCompatActivity() {
|
||||
private fun loadStyle(featureCollection: FeatureCollection) {
|
||||
maplibreMap.setStyle(
|
||||
Style.Builder()
|
||||
.fromUri(TestStyles.getPredefinedStyleWithFallback("Streets"))
|
||||
.fromUri(TestStyles.VERSATILES)
|
||||
.withLayer(
|
||||
SymbolLayer("symbol", "symbol")
|
||||
.withProperties(
|
||||
@@ -129,6 +131,7 @@ class LatLngBoundsActivity : AppCompatActivity() {
|
||||
return intArrayOf(additionalPadding, additionalPadding, additionalPadding, bottomPadding)
|
||||
}
|
||||
|
||||
// # --8<-- [start:createBounds]
|
||||
private fun createBounds(featureCollection: FeatureCollection): LatLngBounds {
|
||||
val boundsBuilder = LatLngBounds.Builder()
|
||||
featureCollection.features()?.let {
|
||||
@@ -139,6 +142,7 @@ class LatLngBoundsActivity : AppCompatActivity() {
|
||||
}
|
||||
return boundsBuilder.build()
|
||||
}
|
||||
// # --8<-- [end:createBounds]
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
@@ -21,7 +21,7 @@ import org.maplibre.android.maps.Style
|
||||
import org.maplibre.android.testapp.R
|
||||
import org.maplibre.android.testapp.styles.TestStyles
|
||||
|
||||
/* ANCHOR: top */
|
||||
// # --8<-- [start:top]
|
||||
/**
|
||||
* This activity shows a basic usage of the LocationComponent's pulsing circle. There's no
|
||||
* customization of the pulsing circle's color, radius, speed, etc.
|
||||
@@ -43,9 +43,9 @@ class BasicLocationPulsingCircleActivity : AppCompatActivity(), OnMapReadyCallba
|
||||
mapView.onCreate(savedInstanceState)
|
||||
checkPermissions()
|
||||
}
|
||||
/* ANCHOR_END: top */
|
||||
// # --8<-- [end:top]
|
||||
|
||||
/* ANCHOR: onMapReady */
|
||||
// # --8<-- [start:onMapReady]
|
||||
@SuppressLint("MissingPermission")
|
||||
override fun onMapReady(maplibreMap: MapLibreMap) {
|
||||
this.maplibreMap = maplibreMap
|
||||
@@ -63,9 +63,9 @@ class BasicLocationPulsingCircleActivity : AppCompatActivity(), OnMapReadyCallba
|
||||
locationComponent!!.forceLocationUpdate(lastLocation)
|
||||
}
|
||||
}
|
||||
/* ANCHOR_END: onMapReady */
|
||||
// # --8<-- [end:onMapReady]
|
||||
|
||||
/* ANCHOR: LocationComponentActivationOptions */
|
||||
// # --8<-- [start:LocationComponentActivationOptions]
|
||||
private fun buildLocationComponentActivationOptions(
|
||||
style: Style,
|
||||
locationComponentOptions: LocationComponentOptions
|
||||
@@ -82,7 +82,7 @@ class BasicLocationPulsingCircleActivity : AppCompatActivity(), OnMapReadyCallba
|
||||
)
|
||||
.build()
|
||||
}
|
||||
/* ANCHOR_END: LocationComponentActivationOptions */
|
||||
// # --8<-- [end:LocationComponentActivationOptions]
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
menuInflater.inflate(R.menu.menu_pulsing_location_mode, menu)
|
||||
@@ -136,7 +136,7 @@ class BasicLocationPulsingCircleActivity : AppCompatActivity(), OnMapReadyCallba
|
||||
maplibreMap.setStyle(Style.Builder().fromUri(Utils.nextStyle()))
|
||||
}
|
||||
|
||||
/* ANCHOR: permission */
|
||||
// # --8<-- [start:permission]
|
||||
private fun checkPermissions() {
|
||||
if (PermissionsManager.areLocationPermissionsGranted(this)) {
|
||||
mapView.getMapAsync(this)
|
||||
@@ -170,7 +170,7 @@ class BasicLocationPulsingCircleActivity : AppCompatActivity(), OnMapReadyCallba
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
permissionsManager!!.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
}
|
||||
/* ANCHOR_END: permission */
|
||||
// # --8<-- [end:permission]
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
|
||||
@@ -22,10 +22,8 @@ import org.maplibre.android.snapshotter.MapSnapshotter
|
||||
import org.maplibre.android.style.expressions.Expression
|
||||
import org.maplibre.android.style.layers.Property
|
||||
import org.maplibre.android.style.layers.PropertyFactory
|
||||
import org.maplibre.android.style.layers.RasterLayer
|
||||
import org.maplibre.android.style.layers.SymbolLayer
|
||||
import org.maplibre.android.style.sources.GeoJsonSource
|
||||
import org.maplibre.android.style.sources.RasterSource
|
||||
import org.maplibre.android.style.sources.Source
|
||||
import org.maplibre.android.testapp.R
|
||||
import org.maplibre.android.testapp.styles.TestStyles
|
||||
@@ -35,11 +33,13 @@ import java.util.Objects
|
||||
import java.util.Random
|
||||
|
||||
/**
|
||||
* Test activity showing how to use a the [com.mapbox.mapboxsdk.snapshotter.MapSnapshotter]
|
||||
* Test activity showing how to use the [MapSnapshotter]
|
||||
*/
|
||||
|
||||
class MapSnapshotterActivity : AppCompatActivity() {
|
||||
lateinit var grid: GridLayout
|
||||
private val snapshotters: MutableList<MapSnapshotter> = ArrayList()
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_map_snapshotter)
|
||||
@@ -47,81 +47,81 @@ class MapSnapshotterActivity : AppCompatActivity() {
|
||||
// Find the grid view and start snapshotting as soon
|
||||
// as the view is measured
|
||||
grid = findViewById(R.id.snapshot_grid)
|
||||
grid.getViewTreeObserver()
|
||||
.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
grid.getViewTreeObserver().removeOnGlobalLayoutListener(this)
|
||||
addSnapshots()
|
||||
}
|
||||
})
|
||||
grid.viewTreeObserver.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
grid.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
addSnapshots()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun addSnapshots() {
|
||||
Timber.i("Creating snapshotters")
|
||||
for (row in 0 until grid.rowCount) {
|
||||
for (column in 0 until grid.columnCount) {
|
||||
startSnapShot(row, column)
|
||||
startSnapshot(row, column)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun startSnapShot(row: Int, column: Int) {
|
||||
// Optionally the style
|
||||
val builder = Style.Builder()
|
||||
.fromUri(
|
||||
TestStyles.getPredefinedStyleWithFallback(
|
||||
if ((column + row) % 2 == 0) {
|
||||
"Streets"
|
||||
} else {
|
||||
"Pastel"
|
||||
}
|
||||
)
|
||||
)
|
||||
// # --8<-- [start:startSnapshot]
|
||||
private fun startSnapshot(row: Int, column: Int) {
|
||||
// # --8<-- [start:styleBuilder]
|
||||
val styles = arrayOf(
|
||||
TestStyles.DEMOTILES,
|
||||
TestStyles.AMERICANA,
|
||||
TestStyles.OPENFREEMAP_LIBERY,
|
||||
TestStyles.AWS_OPEN_DATA_STANDARD_LIGHT,
|
||||
TestStyles.PROTOMAPS_LIGHT,
|
||||
TestStyles.PROTOMAPS_DARK,
|
||||
TestStyles.PROTOMAPS_WHITE,
|
||||
TestStyles.PROTOMAPS_GRAYSCALE,
|
||||
TestStyles.VERSATILES
|
||||
)
|
||||
val builder = Style.Builder().fromUri(
|
||||
styles[(row * grid.rowCount + column) % styles.size]
|
||||
)
|
||||
// # --8<-- [end:styleBuilder]
|
||||
|
||||
// Define the dimensions
|
||||
// # --8<-- [start:mapSnapShotterOptions]
|
||||
val options = MapSnapshotter.Options(
|
||||
grid.measuredWidth / grid.columnCount,
|
||||
grid.measuredHeight / grid.rowCount
|
||||
) // Optionally the pixel ratio
|
||||
)
|
||||
.withPixelRatio(1f)
|
||||
.withLocalIdeographFontFamily(MapLibreConstants.DEFAULT_FONT)
|
||||
// # --8<-- [end:mapSnapShotterOptions]
|
||||
|
||||
// Optionally the visible region
|
||||
// # --8<-- [start:setRegion]
|
||||
if (row % 2 == 0) {
|
||||
options.withRegion(
|
||||
LatLngBounds.Builder()
|
||||
.include(
|
||||
LatLng(
|
||||
randomInRange(-80f, 80f).toDouble(),
|
||||
randomInRange(-160f, 160f)
|
||||
.toDouble()
|
||||
randomInRange(-160f, 160f).toDouble()
|
||||
)
|
||||
)
|
||||
.include(
|
||||
LatLng(
|
||||
randomInRange(-80f, 80f).toDouble(),
|
||||
randomInRange(-160f, 160f)
|
||||
.toDouble()
|
||||
randomInRange(-160f, 160f).toDouble()
|
||||
)
|
||||
)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
// # --8<-- [end:setRegion]
|
||||
|
||||
// Optionally the camera options
|
||||
// # --8<-- [start:setCameraPosition]
|
||||
if (column % 2 == 0) {
|
||||
options.withCameraPosition(
|
||||
CameraPosition.Builder()
|
||||
.target(
|
||||
if (options.region != null) {
|
||||
options.region!!.center
|
||||
} else {
|
||||
LatLng(
|
||||
randomInRange(-80f, 80f).toDouble(),
|
||||
randomInRange(-160f, 160f)
|
||||
.toDouble()
|
||||
)
|
||||
}
|
||||
options.region?.center ?: LatLng(
|
||||
randomInRange(-80f, 80f).toDouble(),
|
||||
randomInRange(-160f, 160f).toDouble()
|
||||
)
|
||||
)
|
||||
.bearing(randomInRange(0f, 360f).toDouble())
|
||||
.tilt(randomInRange(0f, 60f).toDouble())
|
||||
@@ -130,21 +130,15 @@ class MapSnapshotterActivity : AppCompatActivity() {
|
||||
.build()
|
||||
)
|
||||
}
|
||||
if (row == 0 && column == 0) {
|
||||
// Add a source
|
||||
val source: Source =
|
||||
RasterSource("my-raster-source", "maptiler://sources/satellite", 512)
|
||||
builder.withLayerAbove(
|
||||
RasterLayer("satellite-layer", "my-raster-source"),
|
||||
"country_1"
|
||||
)
|
||||
builder.withSource(source)
|
||||
} else if (row == 0 && column == 2) {
|
||||
// # --8<-- [end:setCameraPosition]
|
||||
|
||||
// # --8<-- [start:addMarkerLayer]
|
||||
if (row == 0 && column == 2) {
|
||||
val carBitmap = BitmapUtils.getBitmapFromDrawable(
|
||||
ResourcesCompat.getDrawable(resources, R.drawable.ic_directions_car_black, theme)
|
||||
)
|
||||
|
||||
// marker source
|
||||
// Marker source
|
||||
val markerCollection = FeatureCollection.fromFeatures(
|
||||
arrayOf(
|
||||
Feature.fromGeometry(
|
||||
@@ -159,7 +153,7 @@ class MapSnapshotterActivity : AppCompatActivity() {
|
||||
)
|
||||
val markerSource: Source = GeoJsonSource(MARKER_SOURCE, markerCollection)
|
||||
|
||||
// marker layer
|
||||
// Marker layer
|
||||
val markerSymbolLayer = SymbolLayer(MARKER_LAYER, MARKER_SOURCE)
|
||||
.withProperties(
|
||||
PropertyFactory.iconImage(Expression.get(TITLE_FEATURE_PROPERTY)),
|
||||
@@ -167,11 +161,7 @@ class MapSnapshotterActivity : AppCompatActivity() {
|
||||
PropertyFactory.iconAllowOverlap(true),
|
||||
PropertyFactory.iconSize(
|
||||
Expression.switchCase(
|
||||
Expression.toBool(
|
||||
Expression.get(
|
||||
SELECTED_FEATURE_PROPERTY
|
||||
)
|
||||
),
|
||||
Expression.toBool(Expression.get(SELECTED_FEATURE_PROPERTY)),
|
||||
Expression.literal(1.5f),
|
||||
Expression.literal(1.0f)
|
||||
)
|
||||
@@ -187,16 +177,16 @@ class MapSnapshotterActivity : AppCompatActivity() {
|
||||
.withCameraPosition(
|
||||
CameraPosition.Builder()
|
||||
.target(
|
||||
LatLng(
|
||||
5.537109374999999,
|
||||
52.07950600379697
|
||||
)
|
||||
LatLng(5.537109374999999, 52.07950600379697)
|
||||
)
|
||||
.zoom(1.0)
|
||||
.padding(1.0, 1.0, 1.0, 1.0)
|
||||
.build()
|
||||
)
|
||||
}
|
||||
// # --8<-- [end:addMarkerLayer]
|
||||
|
||||
// # --8<-- [start:startSnapshotter]
|
||||
options.withStyleBuilder(builder)
|
||||
val snapshotter = MapSnapshotter(this@MapSnapshotterActivity, options)
|
||||
|
||||
@@ -206,8 +196,9 @@ class MapSnapshotterActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
override fun onStyleImageMissing(imageName: String) {
|
||||
val androidIcon =
|
||||
BitmapUtils.getBitmapFromDrawable(ResourcesCompat.getDrawable(resources, R.drawable.ic_android_2, theme))
|
||||
val androidIcon = BitmapUtils.getBitmapFromDrawable(
|
||||
ResourcesCompat.getDrawable(resources, R.drawable.ic_android_2, theme)
|
||||
)
|
||||
snapshotter.addImage(imageName, androidIcon!!, false)
|
||||
}
|
||||
})
|
||||
@@ -225,11 +216,10 @@ class MapSnapshotterActivity : AppCompatActivity() {
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
snapshotters.add(snapshotter)
|
||||
}
|
||||
|
||||
public override fun onPause() {
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
|
||||
// Make sure to stop the snapshotters on pause
|
||||
@@ -240,11 +230,11 @@ class MapSnapshotterActivity : AppCompatActivity() {
|
||||
}
|
||||
|
||||
private fun featureProperties(id: String, title: String): JsonObject {
|
||||
val `object` = JsonObject()
|
||||
`object`.add(ID_FEATURE_PROPERTY, JsonPrimitive(id))
|
||||
`object`.add(TITLE_FEATURE_PROPERTY, JsonPrimitive(title))
|
||||
`object`.add(SELECTED_FEATURE_PROPERTY, JsonPrimitive(false))
|
||||
return `object`
|
||||
val jsonObject = JsonObject()
|
||||
jsonObject.add(ID_FEATURE_PROPERTY, JsonPrimitive(id))
|
||||
jsonObject.add(TITLE_FEATURE_PROPERTY, JsonPrimitive(title))
|
||||
jsonObject.add(SELECTED_FEATURE_PROPERTY, JsonPrimitive(false))
|
||||
return jsonObject
|
||||
}
|
||||
|
||||
companion object {
|
||||
@@ -252,7 +242,7 @@ class MapSnapshotterActivity : AppCompatActivity() {
|
||||
private const val SELECTED_FEATURE_PROPERTY = "selected"
|
||||
private const val TITLE_FEATURE_PROPERTY = "title"
|
||||
|
||||
// layer & source constants
|
||||
// Layer & source constants
|
||||
private const val MARKER_SOURCE = "marker-source"
|
||||
private const val MARKER_LAYER = "marker-layer"
|
||||
private val random = Random()
|
||||
@@ -260,4 +250,4 @@ class MapSnapshotterActivity : AppCompatActivity() {
|
||||
return random.nextFloat() * (max - min) + min
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ class MapSnapshotterBitMapOverlayActivity :
|
||||
Math.min(container.measuredHeight, 1024)
|
||||
)
|
||||
.withStyleBuilder(
|
||||
Style.Builder().fromUri(TestStyles.getPredefinedStyleWithFallback("Outdoor"))
|
||||
Style.Builder().fromUri(TestStyles.AMERICANA)
|
||||
)
|
||||
.withCameraPosition(
|
||||
CameraPosition.Builder().target(LatLng(52.090737, 5.121420))
|
||||
|
||||
@@ -23,10 +23,15 @@ import java.net.URI
|
||||
import java.net.URISyntaxException
|
||||
|
||||
/**
|
||||
* Test activity showing how to use a the [MapSnapshotter] and heatmap layer on it.
|
||||
* Test activity showing how to use the [MapSnapshotter] and heatmap layer on it.
|
||||
*/
|
||||
|
||||
// # --8<-- [start:class_declaration]
|
||||
class MapSnapshotterHeatMapActivity : AppCompatActivity(), MapSnapshotter.SnapshotReadyCallback {
|
||||
// # --8<-- [end:class_declaration]
|
||||
private var mapSnapshotter: MapSnapshotter? = null
|
||||
|
||||
// # --8<-- [start:onCreate]
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_map_snapshotter_marker)
|
||||
@@ -36,9 +41,9 @@ class MapSnapshotterHeatMapActivity : AppCompatActivity(), MapSnapshotter.Snapsh
|
||||
override fun onGlobalLayout() {
|
||||
container.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
Timber.i("Starting snapshot")
|
||||
val builder = Style.Builder().fromUri(TestStyles.getPredefinedStyleWithFallback("Outdoor"))
|
||||
val builder = Style.Builder().fromUri(TestStyles.AMERICANA)
|
||||
.withSource(earthquakeSource!!)
|
||||
.withLayerAbove(heatmapLayer, "water_intermittent")
|
||||
.withLayerAbove(heatmapLayer, "water")
|
||||
mapSnapshotter = MapSnapshotter(
|
||||
applicationContext,
|
||||
MapSnapshotter.Options(container.measuredWidth, container.measuredHeight)
|
||||
@@ -55,23 +60,15 @@ class MapSnapshotterHeatMapActivity : AppCompatActivity(), MapSnapshotter.Snapsh
|
||||
}
|
||||
})
|
||||
}
|
||||
// # --8<-- [end:onCreate]
|
||||
|
||||
// Color ramp for heatmap. Domain is 0 (low) to 1 (high).
|
||||
// Begin color ramp at 0-stop with a 0-transparency color
|
||||
// to create a blur-like effect.
|
||||
// Increase the heatmap weight based on frequency and property magnitude
|
||||
// Increase the heatmap color weight weight by zoom level
|
||||
// heatmap-intensity is a multiplier on top of heatmap-weight
|
||||
// Adjust the heatmap radius by zoom level
|
||||
// Transition from heatmap to circle layer by zoom level
|
||||
// # --8<-- [start:heatmapLayer]
|
||||
private val heatmapLayer: HeatmapLayer
|
||||
private get() {
|
||||
get() {
|
||||
val layer = HeatmapLayer(HEATMAP_LAYER_ID, EARTHQUAKE_SOURCE_ID)
|
||||
layer.maxZoom = 9f
|
||||
layer.sourceLayer = HEATMAP_LAYER_SOURCE
|
||||
layer.setProperties( // Color ramp for heatmap. Domain is 0 (low) to 1 (high).
|
||||
// Begin color ramp at 0-stop with a 0-transparency color
|
||||
// to create a blur-like effect.
|
||||
layer.setProperties(
|
||||
PropertyFactory.heatmapColor(
|
||||
Expression.interpolate(
|
||||
Expression.linear(), Expression.heatmapDensity(),
|
||||
@@ -82,7 +79,7 @@ class MapSnapshotterHeatMapActivity : AppCompatActivity(), MapSnapshotter.Snapsh
|
||||
Expression.literal(0.8), Expression.rgb(239, 138, 98),
|
||||
Expression.literal(1), Expression.rgb(178, 24, 43)
|
||||
)
|
||||
), // Increase the heatmap weight based on frequency and property magnitude
|
||||
),
|
||||
PropertyFactory.heatmapWeight(
|
||||
Expression.interpolate(
|
||||
Expression.linear(),
|
||||
@@ -90,8 +87,7 @@ class MapSnapshotterHeatMapActivity : AppCompatActivity(), MapSnapshotter.Snapsh
|
||||
Expression.stop(0, 0),
|
||||
Expression.stop(6, 1)
|
||||
)
|
||||
), // Increase the heatmap color weight weight by zoom level
|
||||
// heatmap-intensity is a multiplier on top of heatmap-weight
|
||||
),
|
||||
PropertyFactory.heatmapIntensity(
|
||||
Expression.interpolate(
|
||||
Expression.linear(),
|
||||
@@ -99,7 +95,7 @@ class MapSnapshotterHeatMapActivity : AppCompatActivity(), MapSnapshotter.Snapsh
|
||||
Expression.stop(0, 1),
|
||||
Expression.stop(9, 3)
|
||||
)
|
||||
), // Adjust the heatmap radius by zoom level
|
||||
),
|
||||
PropertyFactory.heatmapRadius(
|
||||
Expression.interpolate(
|
||||
Expression.linear(),
|
||||
@@ -107,7 +103,7 @@ class MapSnapshotterHeatMapActivity : AppCompatActivity(), MapSnapshotter.Snapsh
|
||||
Expression.stop(0, 2),
|
||||
Expression.stop(9, 20)
|
||||
)
|
||||
), // Transition from heatmap to circle layer by zoom level
|
||||
),
|
||||
PropertyFactory.heatmapOpacity(
|
||||
Expression.interpolate(
|
||||
Expression.linear(),
|
||||
@@ -119,33 +115,40 @@ class MapSnapshotterHeatMapActivity : AppCompatActivity(), MapSnapshotter.Snapsh
|
||||
)
|
||||
return layer
|
||||
}
|
||||
// # --8<-- [end:heatmapLayer]
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
mapSnapshotter!!.cancel()
|
||||
}
|
||||
|
||||
// # --8<-- [start:onSnapshotReady]
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onSnapshotReady(snapshot: MapSnapshot) {
|
||||
Timber.i("Snapshot ready")
|
||||
val imageView = findViewById<ImageView>(R.id.snapshot_image)
|
||||
imageView.setImageBitmap(snapshot.bitmap)
|
||||
}
|
||||
// # --8<-- [end:onSnapshotReady]
|
||||
|
||||
// # --8<-- [start:onStop]
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
mapSnapshotter?.cancel()
|
||||
}
|
||||
// # --8<-- [end:onStop]
|
||||
|
||||
// # --8<-- [start:earthquakeSource]
|
||||
private val earthquakeSource: Source?
|
||||
private get() {
|
||||
get() {
|
||||
var source: Source? = null
|
||||
try {
|
||||
source = GeoJsonSource(EARTHQUAKE_SOURCE_ID, URI(EARTHQUAKE_SOURCE_URL))
|
||||
} catch (uriSyntaxException: URISyntaxException) {
|
||||
Timber.e(uriSyntaxException, "That's not an url... ")
|
||||
Timber.e(uriSyntaxException, "That's not a valid URL.")
|
||||
}
|
||||
return source
|
||||
}
|
||||
// # --8<-- [end:earthquakeSource]
|
||||
|
||||
companion object {
|
||||
private const val EARTHQUAKE_SOURCE_URL =
|
||||
"https://www.mapbox.com/mapbox-gl-js/assets/earthquakes.geojson"
|
||||
"https://maplibre.org/maplibre-gl-js/docs/assets/earthquakes.geojson"
|
||||
private const val EARTHQUAKE_SOURCE_ID = "earthquakes"
|
||||
private const val HEATMAP_LAYER_ID = "earthquakes-heat"
|
||||
private const val HEATMAP_LAYER_SOURCE = "earthquakes"
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.view.View
|
||||
import android.view.ViewTreeObserver.OnGlobalLayoutListener
|
||||
import android.widget.ImageView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import okio.ByteString.Companion.readByteString
|
||||
import org.maplibre.android.camera.CameraPosition
|
||||
import org.maplibre.android.geometry.LatLng
|
||||
import org.maplibre.android.maps.Style
|
||||
@@ -19,8 +20,15 @@ import java.lang.RuntimeException
|
||||
/**
|
||||
* Test activity showing how to use a the MapSnapshotter with a local style
|
||||
*/
|
||||
class MapSnapshotterLocalStyleActivity : AppCompatActivity(), MapSnapshotter.SnapshotReadyCallback {
|
||||
private var mapSnapshotter: MapSnapshotter? = null
|
||||
class MapSnapshotterLocalStyleActivity : AppCompatActivity() {
|
||||
private lateinit var mapSnapshotter: MapSnapshotter
|
||||
|
||||
companion object {
|
||||
private const val LATITUDE = 52.090737
|
||||
private const val LONGITUDE = 5.121420
|
||||
private const val ZOOM = 2.0
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_map_snapshotter_marker)
|
||||
@@ -29,48 +37,39 @@ class MapSnapshotterLocalStyleActivity : AppCompatActivity(), MapSnapshotter.Sna
|
||||
.addOnGlobalLayoutListener(object : OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
container.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
val styleJson: String
|
||||
styleJson = try {
|
||||
ResourceUtils.readRawResource(
|
||||
this@MapSnapshotterLocalStyleActivity,
|
||||
R.raw.sat_style
|
||||
)
|
||||
} catch (exception: IOException) {
|
||||
throw RuntimeException(exception)
|
||||
}
|
||||
// # --8<-- [start:readStyleJson]
|
||||
val styleJson = resources.openRawResource(R.raw.demotiles).reader().readText()
|
||||
// # --8<-- [end:readStyleJson]
|
||||
Timber.i("Starting snapshot")
|
||||
// # --8<-- [start:createMapSnapshotter]
|
||||
mapSnapshotter = MapSnapshotter(
|
||||
applicationContext,
|
||||
MapSnapshotter.Options(
|
||||
Math.min(container.measuredWidth, 1024),
|
||||
Math.min(container.measuredHeight, 1024)
|
||||
container.measuredWidth.coerceAtMost(1024),
|
||||
container.measuredHeight.coerceAtMost(1024)
|
||||
)
|
||||
.withStyleBuilder(Style.Builder().fromJson(styleJson))
|
||||
.withCameraPosition(
|
||||
CameraPosition.Builder().target(LatLng(52.090737, 5.121420))
|
||||
.zoom(18.0).build()
|
||||
CameraPosition.Builder().target(LatLng(LATITUDE, LONGITUDE))
|
||||
.zoom(ZOOM).build()
|
||||
)
|
||||
)
|
||||
mapSnapshotter!!.start(
|
||||
this@MapSnapshotterLocalStyleActivity,
|
||||
object : MapSnapshotter.ErrorHandler {
|
||||
override fun onError(error: String) {
|
||||
Timber.e(error)
|
||||
}
|
||||
}
|
||||
)
|
||||
// # --8<-- [end:createMapSnapshotter]
|
||||
// # --8<-- [start:createSnapshot]
|
||||
mapSnapshotter.start({ snapshot ->
|
||||
Timber.i("Snapshot ready")
|
||||
val imageView = findViewById<View>(R.id.snapshot_image) as ImageView
|
||||
imageView.setImageBitmap(snapshot.bitmap)
|
||||
}) { error -> Timber.e(error )}
|
||||
// # --8<-- [end:createSnapshot]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
mapSnapshotter!!.cancel()
|
||||
}
|
||||
|
||||
override fun onSnapshotReady(snapshot: MapSnapshot) {
|
||||
Timber.i("Snapshot ready")
|
||||
val imageView = findViewById<View>(R.id.snapshot_image) as ImageView
|
||||
imageView.setImageBitmap(snapshot.bitmap)
|
||||
if (this::mapSnapshotter.isInitialized) {
|
||||
mapSnapshotter.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -202,8 +202,8 @@ class RuntimeStyleActivity : AppCompatActivity() {
|
||||
true
|
||||
}
|
||||
|
||||
R.id.action_add_satellite_layer -> {
|
||||
addSatelliteLayer()
|
||||
R.id.action_add_raster_source_layer -> {
|
||||
addRasterSourceLayer()
|
||||
true
|
||||
}
|
||||
|
||||
@@ -501,13 +501,14 @@ class RuntimeStyleActivity : AppCompatActivity() {
|
||||
).show()
|
||||
}
|
||||
|
||||
private fun addSatelliteLayer() {
|
||||
// Add a source
|
||||
val source: Source = RasterSource("my-raster-source", "maptiler://sources/satellite", 512)
|
||||
maplibreMap.style!!.addSource(source)
|
||||
private fun addRasterSourceLayer() {
|
||||
// Take note of Tile Usage Policy https://operations.osmfoundation.org/policies/tiles/
|
||||
val osmTileSet = TileSet("osm", "https://tile.openstreetmap.org/{z}/{x}/{y}.png")
|
||||
osmTileSet.attribution = "OSM Contributors"
|
||||
val source = RasterSource("osm", osmTileSet, 128)
|
||||
val layer = RasterLayer("osm-layer", "osm")
|
||||
|
||||
// Add a layer
|
||||
maplibreMap.style!!.addLayer(RasterLayer("satellite-layer", "my-raster-source"))
|
||||
maplibreMap.setStyle(Style.Builder().withSource(source).withLayer(layer))
|
||||
}
|
||||
|
||||
private fun updateWaterColorOnZoom() {
|
||||
|
||||
@@ -75,7 +75,7 @@ class StyleFileActivity : AppCompatActivity() {
|
||||
*/
|
||||
private fun loadStyleFileTask(context: Context): String =
|
||||
try {
|
||||
ResourceUtils.readRawResource(context, R.raw.sat_style)
|
||||
ResourceUtils.readRawResource(context, R.raw.demotiles)
|
||||
} catch (exception: Exception) {
|
||||
Timber.e(exception, "Can't load local file style")
|
||||
""
|
||||
|
||||
@@ -3,10 +3,27 @@ package org.maplibre.android.testapp.styles
|
||||
import org.maplibre.android.maps.Style
|
||||
|
||||
object TestStyles {
|
||||
val VERSATILES = "https://tiles.versatiles.org/assets/styles/colorful.json"
|
||||
const val DEMOTILES = "https://demotiles.maplibre.org/style.json"
|
||||
|
||||
val AMERICANA = "https://americanamap.org/style.json"
|
||||
const val VERSATILES = "https://tiles.versatiles.org/assets/styles/colorful.json"
|
||||
|
||||
const val AMERICANA = "https://americanamap.org/style.json"
|
||||
|
||||
const val OPENFREEMAP_LIBERY = "https://tiles.openfreemap.org/styles/liberty"
|
||||
|
||||
const val AWS_OPEN_DATA_STANDARD_LIGHT = "https://maps.geo.us-east-2.amazonaws.com/maps/v0/maps/OpenDataStyle/style-descriptor?key=v1.public.eyJqdGkiOiI1NjY5ZTU4My0yNWQwLTQ5MjctODhkMS03OGUxOTY4Y2RhMzgifR_7GLT66TNRXhZJ4KyJ-GK1TPYD9DaWuc5o6YyVmlikVwMaLvEs_iqkCIydspe_vjmgUVsIQstkGoInXV_nd5CcmqRMMa-_wb66SxDdbeRDvmmkpy2Ow_LX9GJDgL2bbiCws0wupJPFDwWCWFLwpK9ICmzGvNcrPbX5uczOQL0N8V9iUvziA52a1WWkZucIf6MUViFRf3XoFkyAT15Ll0NDypAzY63Bnj8_zS8bOaCvJaQqcXM9lrbTusy8Ftq8cEbbK5aMFapXRjug7qcrzUiQ5sr0g23qdMvnKJQFfo7JuQn8vwAksxrQm6A0ByceEXSfyaBoVpFcTzEclxUomhY.NjAyMWJkZWUtMGMyOS00NmRkLThjZTMtODEyOTkzZTUyMTBi"
|
||||
|
||||
private fun protomaps(style: String): String {
|
||||
return "https://api.protomaps.com/styles/v2/${style}.json?key=e761cc7daedf832a"
|
||||
}
|
||||
val PROTOMAPS_LIGHT = protomaps("light")
|
||||
|
||||
val PROTOMAPS_DARK = protomaps("dark")
|
||||
|
||||
val PROTOMAPS_GRAYSCALE = protomaps("grayscale")
|
||||
|
||||
val PROTOMAPS_WHITE = protomaps("white")
|
||||
|
||||
fun getPredefinedStyleWithFallback(name: String): String {
|
||||
try {
|
||||
val style = Style.getPredefinedStyle(name)
|
||||
|
||||
@@ -43,8 +43,8 @@
|
||||
android:title="@string/add_a_terrain_layer"
|
||||
maplibre:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/action_add_satellite_layer"
|
||||
android:title="@string/add_a_satellite_layer"
|
||||
android:id="@+id/action_add_raster_source_layer"
|
||||
android:title="@string/set_raster_style"
|
||||
maplibre:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/action_update_water_color_on_zoom"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,766 +0,0 @@
|
||||
{
|
||||
"id": "hybrid",
|
||||
"name": "Satellite Hybrid",
|
||||
"zoom": 1,
|
||||
"pitch": 0,
|
||||
"center": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"glyphs": "maptiler://fonts/{fontstack}/{range}.pbf",
|
||||
"layers": [
|
||||
{
|
||||
"id": "satellite",
|
||||
"type": "raster",
|
||||
"paint": {
|
||||
"raster-opacity": 1
|
||||
},
|
||||
"filter": [
|
||||
"all"
|
||||
],
|
||||
"layout": {
|
||||
"visibility": "visible"
|
||||
},
|
||||
"source": "satellite",
|
||||
"minzoom": 0
|
||||
},
|
||||
{
|
||||
"id": "tunnel",
|
||||
"type": "line",
|
||||
"paint": {
|
||||
"line-color": "rgba(255, 255, 255, 0.2)",
|
||||
"line-width": [
|
||||
"interpolate",
|
||||
[
|
||||
"exponential",
|
||||
1.5
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
6,
|
||||
0.5,
|
||||
20,
|
||||
30
|
||||
],
|
||||
"line-dasharray": [
|
||||
0.28,
|
||||
0.14
|
||||
]
|
||||
},
|
||||
"filter": [
|
||||
"all",
|
||||
[
|
||||
"==",
|
||||
"$type",
|
||||
"LineString"
|
||||
],
|
||||
[
|
||||
"==",
|
||||
"brunnel",
|
||||
"tunnel"
|
||||
],
|
||||
[
|
||||
"in",
|
||||
"class",
|
||||
"motorway",
|
||||
"primary",
|
||||
"secondary",
|
||||
"tertiary",
|
||||
"trunk"
|
||||
]
|
||||
],
|
||||
"layout": {
|
||||
"line-cap": "butt",
|
||||
"line-join": "miter"
|
||||
},
|
||||
"source": "openmaptiles",
|
||||
"source-layer": "transportation"
|
||||
},
|
||||
{
|
||||
"id": "path",
|
||||
"type": "line",
|
||||
"paint": {
|
||||
"line-color": "rgba(247, 247, 247, 0.33)",
|
||||
"line-width": [
|
||||
"interpolate",
|
||||
[
|
||||
"exponential",
|
||||
1.5
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
14,
|
||||
0.5,
|
||||
20,
|
||||
4
|
||||
],
|
||||
"line-dasharray": [
|
||||
1,
|
||||
1
|
||||
]
|
||||
},
|
||||
"filter": [
|
||||
"all",
|
||||
[
|
||||
"==",
|
||||
"$type",
|
||||
"LineString"
|
||||
],
|
||||
[
|
||||
"in",
|
||||
"class",
|
||||
"path",
|
||||
"track"
|
||||
]
|
||||
],
|
||||
"layout": {
|
||||
"line-cap": "square",
|
||||
"line-join": "bevel"
|
||||
},
|
||||
"source": "openmaptiles",
|
||||
"source-layer": "transportation"
|
||||
},
|
||||
{
|
||||
"id": "road",
|
||||
"type": "line",
|
||||
"paint": {
|
||||
"line-color": [
|
||||
"interpolate",
|
||||
[
|
||||
"linear"
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
8,
|
||||
"rgba(255, 255, 255, 0.2)",
|
||||
14,
|
||||
"rgba(255, 255, 255, 0.3)"
|
||||
],
|
||||
"line-width": [
|
||||
"interpolate",
|
||||
[
|
||||
"linear"
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
5,
|
||||
[
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"class"
|
||||
],
|
||||
[
|
||||
"motorway",
|
||||
"motorway_link"
|
||||
],
|
||||
1,
|
||||
0
|
||||
],
|
||||
7,
|
||||
[
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"class"
|
||||
],
|
||||
[
|
||||
"motorway",
|
||||
"motorway_link"
|
||||
],
|
||||
1.4,
|
||||
0
|
||||
],
|
||||
8,
|
||||
[
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"class"
|
||||
],
|
||||
[
|
||||
"motorway",
|
||||
"motorway_link",
|
||||
"primary",
|
||||
"trunk"
|
||||
],
|
||||
0.75,
|
||||
0
|
||||
],
|
||||
9,
|
||||
[
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"class"
|
||||
],
|
||||
[
|
||||
"secondary",
|
||||
"tertiary"
|
||||
],
|
||||
0.7,
|
||||
1
|
||||
],
|
||||
10,
|
||||
[
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"class"
|
||||
],
|
||||
[
|
||||
"motorway",
|
||||
"motorway_link"
|
||||
],
|
||||
1.3,
|
||||
1.3
|
||||
],
|
||||
14,
|
||||
[
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"class"
|
||||
],
|
||||
[
|
||||
"minor",
|
||||
"service"
|
||||
],
|
||||
0.5,
|
||||
2.4
|
||||
]
|
||||
]
|
||||
},
|
||||
"filter": [
|
||||
"all",
|
||||
[
|
||||
"==",
|
||||
"$type",
|
||||
"LineString"
|
||||
],
|
||||
[
|
||||
"!in",
|
||||
"class",
|
||||
"rail",
|
||||
"ferry",
|
||||
"path",
|
||||
"track"
|
||||
],
|
||||
[
|
||||
"!=",
|
||||
"brunnel",
|
||||
"tunnel"
|
||||
]
|
||||
],
|
||||
"layout": {
|
||||
"line-cap": "butt",
|
||||
"line-join": "round"
|
||||
},
|
||||
"source": "openmaptiles",
|
||||
"minzoom": 6,
|
||||
"source-layer": "transportation"
|
||||
},
|
||||
{
|
||||
"id": "railway",
|
||||
"type": "line",
|
||||
"paint": {
|
||||
"line-color": "rgba(179, 170, 158, 0.2)",
|
||||
"line-opacity": [
|
||||
"interpolate",
|
||||
[
|
||||
"linear"
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
11,
|
||||
0.5,
|
||||
16,
|
||||
1.3
|
||||
]
|
||||
},
|
||||
"filter": [
|
||||
"==",
|
||||
"class",
|
||||
"rail"
|
||||
],
|
||||
"layout": {
|
||||
"visibility": "visible"
|
||||
},
|
||||
"source": "openmaptiles",
|
||||
"minzoom": 11,
|
||||
"source-layer": "transportation"
|
||||
},
|
||||
{
|
||||
"id": "admin_sub",
|
||||
"type": "line",
|
||||
"paint": {
|
||||
"line-color": "rgba(194, 194, 194, 0.5)",
|
||||
"line-dasharray": [
|
||||
2,
|
||||
1
|
||||
]
|
||||
},
|
||||
"filter": [
|
||||
"in",
|
||||
"admin_level",
|
||||
4,
|
||||
6,
|
||||
8
|
||||
],
|
||||
"layout": {
|
||||
"visibility": "visible"
|
||||
},
|
||||
"source": "openmaptiles",
|
||||
"minzoom": 3,
|
||||
"source-layer": "boundary"
|
||||
},
|
||||
{
|
||||
"id": "admin_country-dark",
|
||||
"type": "line",
|
||||
"paint": {
|
||||
"line-color": "rgba(0, 0, 0, 0.51)",
|
||||
"line-width": [
|
||||
"interpolate",
|
||||
[
|
||||
"exponential",
|
||||
1.5
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
3,
|
||||
0.5,
|
||||
21,
|
||||
32
|
||||
],
|
||||
"line-offset": 1
|
||||
},
|
||||
"filter": [
|
||||
"all",
|
||||
[
|
||||
"<=",
|
||||
"admin_level",
|
||||
2
|
||||
],
|
||||
[
|
||||
"==",
|
||||
"$type",
|
||||
"LineString"
|
||||
]
|
||||
],
|
||||
"layout": {
|
||||
"line-cap": "butt",
|
||||
"line-join": "round",
|
||||
"visibility": "visible"
|
||||
},
|
||||
"source": "openmaptiles",
|
||||
"source-layer": "boundary"
|
||||
},
|
||||
{
|
||||
"id": "admin_country",
|
||||
"type": "line",
|
||||
"paint": {
|
||||
"line-color": "rgba(226, 226, 226, 1)",
|
||||
"line-width": [
|
||||
"interpolate",
|
||||
[
|
||||
"exponential",
|
||||
1.5
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
3,
|
||||
0.5,
|
||||
21,
|
||||
32
|
||||
]
|
||||
},
|
||||
"filter": [
|
||||
"all",
|
||||
[
|
||||
"<=",
|
||||
"admin_level",
|
||||
2
|
||||
],
|
||||
[
|
||||
"==",
|
||||
"$type",
|
||||
"LineString"
|
||||
]
|
||||
],
|
||||
"layout": {
|
||||
"line-cap": "round",
|
||||
"line-join": "round",
|
||||
"visibility": "visible"
|
||||
},
|
||||
"source": "openmaptiles",
|
||||
"source-layer": "boundary"
|
||||
},
|
||||
{
|
||||
"id": "road_label",
|
||||
"type": "symbol",
|
||||
"paint": {
|
||||
"text-color": "rgba(255, 255, 255, 1)",
|
||||
"text-halo-color": "rgba(43, 43, 43, 1)",
|
||||
"text-halo-width": 1
|
||||
},
|
||||
"filter": [
|
||||
"==",
|
||||
"$type",
|
||||
"LineString"
|
||||
],
|
||||
"layout": {
|
||||
"text-font": [
|
||||
"Noto Sans Regular"
|
||||
],
|
||||
"text-size": [
|
||||
"interpolate",
|
||||
[
|
||||
"cubic-bezier",
|
||||
0.75,
|
||||
1,
|
||||
0.75,
|
||||
1
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
10,
|
||||
8,
|
||||
15,
|
||||
9
|
||||
],
|
||||
"text-field": "{name:latin} {name:nonlatin}",
|
||||
"text-transform": "none",
|
||||
"symbol-placement": "line",
|
||||
"text-letter-spacing": 0.1,
|
||||
"text-rotation-alignment": "map"
|
||||
},
|
||||
"source": "openmaptiles",
|
||||
"source-layer": "transportation_name"
|
||||
},
|
||||
{
|
||||
"id": "place_label",
|
||||
"type": "symbol",
|
||||
"paint": {
|
||||
"text-color": "rgba(255, 255, 255, 1)",
|
||||
"text-halo-blur": 0.5,
|
||||
"text-halo-color": [
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"class"
|
||||
],
|
||||
[
|
||||
"city"
|
||||
],
|
||||
"rgba(0, 0, 0, 0.75)",
|
||||
"rgba(43, 43, 43, 1)"
|
||||
],
|
||||
"text-halo-width": 1
|
||||
},
|
||||
"filter": [
|
||||
"all",
|
||||
[
|
||||
"==",
|
||||
"$type",
|
||||
"Point"
|
||||
],
|
||||
[
|
||||
"!in",
|
||||
"class",
|
||||
"country",
|
||||
"state"
|
||||
]
|
||||
],
|
||||
"layout": {
|
||||
"text-font": [
|
||||
"Noto Sans Regular"
|
||||
],
|
||||
"text-size": [
|
||||
"interpolate",
|
||||
[
|
||||
"cubic-bezier",
|
||||
0.5,
|
||||
1,
|
||||
0.5,
|
||||
1
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
3,
|
||||
[
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"class"
|
||||
],
|
||||
"city",
|
||||
11,
|
||||
10
|
||||
],
|
||||
6,
|
||||
[
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"class"
|
||||
],
|
||||
"city",
|
||||
14.5,
|
||||
11
|
||||
],
|
||||
8,
|
||||
[
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"class"
|
||||
],
|
||||
"city",
|
||||
16,
|
||||
12
|
||||
],
|
||||
10,
|
||||
[
|
||||
"match",
|
||||
[
|
||||
"get",
|
||||
"class"
|
||||
],
|
||||
"city",
|
||||
20,
|
||||
13
|
||||
]
|
||||
],
|
||||
"text-field": "{name:latin}\n{name:nonlatin}",
|
||||
"text-max-width": 10
|
||||
},
|
||||
"source": "openmaptiles",
|
||||
"maxzoom": 16,
|
||||
"minzoom": 3,
|
||||
"source-layer": "place"
|
||||
},
|
||||
{
|
||||
"id": "state_label",
|
||||
"type": "symbol",
|
||||
"paint": {
|
||||
"text-color": "rgba(255, 255, 255, 1)",
|
||||
"text-halo-blur": 1,
|
||||
"text-halo-color": "rgba(0, 0, 0, 1)",
|
||||
"text-halo-width": 1
|
||||
},
|
||||
"filter": [
|
||||
"all",
|
||||
[
|
||||
"==",
|
||||
"$type",
|
||||
"Point"
|
||||
],
|
||||
[
|
||||
"in",
|
||||
"class",
|
||||
"state"
|
||||
]
|
||||
],
|
||||
"layout": {
|
||||
"text-font": [
|
||||
"Noto Sans Italic"
|
||||
],
|
||||
"text-size": [
|
||||
"interpolate",
|
||||
[
|
||||
"cubic-bezier",
|
||||
0.75,
|
||||
1,
|
||||
0.75,
|
||||
1
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
1,
|
||||
[
|
||||
"step",
|
||||
[
|
||||
"get",
|
||||
"rank"
|
||||
],
|
||||
13,
|
||||
1,
|
||||
12,
|
||||
2,
|
||||
12
|
||||
],
|
||||
4,
|
||||
[
|
||||
"step",
|
||||
[
|
||||
"get",
|
||||
"rank"
|
||||
],
|
||||
15,
|
||||
1,
|
||||
14,
|
||||
2,
|
||||
14
|
||||
],
|
||||
6,
|
||||
[
|
||||
"step",
|
||||
[
|
||||
"get",
|
||||
"rank"
|
||||
],
|
||||
23,
|
||||
1,
|
||||
18,
|
||||
2,
|
||||
18
|
||||
],
|
||||
9,
|
||||
[
|
||||
"step",
|
||||
[
|
||||
"get",
|
||||
"rank"
|
||||
],
|
||||
27,
|
||||
1,
|
||||
22,
|
||||
2,
|
||||
22
|
||||
]
|
||||
],
|
||||
"text-field": "{name:latin}",
|
||||
"text-max-width": 10
|
||||
},
|
||||
"source": "openmaptiles",
|
||||
"maxzoom": 12,
|
||||
"minzoom": 3,
|
||||
"source-layer": "place"
|
||||
},
|
||||
{
|
||||
"id": "country_label",
|
||||
"type": "symbol",
|
||||
"paint": {
|
||||
"text-color": "rgba(255, 255, 255, 1)",
|
||||
"text-halo-blur": 1,
|
||||
"text-halo-color": "rgba(0, 0, 0, 1)",
|
||||
"text-halo-width": 1
|
||||
},
|
||||
"filter": [
|
||||
"all",
|
||||
[
|
||||
"==",
|
||||
"$type",
|
||||
"Point"
|
||||
],
|
||||
[
|
||||
"in",
|
||||
"class",
|
||||
"country"
|
||||
]
|
||||
],
|
||||
"layout": {
|
||||
"text-font": [
|
||||
"Noto Sans Bold"
|
||||
],
|
||||
"text-size": [
|
||||
"interpolate",
|
||||
[
|
||||
"cubic-bezier",
|
||||
0.75,
|
||||
1,
|
||||
0.75,
|
||||
1
|
||||
],
|
||||
[
|
||||
"zoom"
|
||||
],
|
||||
1,
|
||||
[
|
||||
"step",
|
||||
[
|
||||
"get",
|
||||
"rank"
|
||||
],
|
||||
13,
|
||||
1,
|
||||
12,
|
||||
2,
|
||||
12
|
||||
],
|
||||
4,
|
||||
[
|
||||
"step",
|
||||
[
|
||||
"get",
|
||||
"rank"
|
||||
],
|
||||
15,
|
||||
1,
|
||||
14,
|
||||
2,
|
||||
14
|
||||
],
|
||||
6,
|
||||
[
|
||||
"step",
|
||||
[
|
||||
"get",
|
||||
"rank"
|
||||
],
|
||||
23,
|
||||
1,
|
||||
18,
|
||||
2,
|
||||
18
|
||||
],
|
||||
9,
|
||||
[
|
||||
"step",
|
||||
[
|
||||
"get",
|
||||
"rank"
|
||||
],
|
||||
27,
|
||||
1,
|
||||
22,
|
||||
2,
|
||||
22
|
||||
]
|
||||
],
|
||||
"text-field": "{name:latin}",
|
||||
"text-max-width": 10
|
||||
},
|
||||
"source": "openmaptiles",
|
||||
"maxzoom": 12,
|
||||
"source-layer": "place"
|
||||
}
|
||||
],
|
||||
"bearing": 0,
|
||||
"sources": {
|
||||
"satellite": {
|
||||
"url": "maptiler://sources/satellite",
|
||||
"type": "raster",
|
||||
"tileSize": 256
|
||||
},
|
||||
"openmaptiles": {
|
||||
"url": "maptiler://sources/v3",
|
||||
"type": "vector"
|
||||
}
|
||||
},
|
||||
"version": 8,
|
||||
"metadata": {
|
||||
"maputnik:renderer": "mbgljs",
|
||||
"openmaptiles:version": "3.x"
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@
|
||||
<string name="add_a_dynamic_geojson_source">Add a dynamic GeoJSON source</string>
|
||||
<string name="remove_buildings_layer">Remove buildings layer</string>
|
||||
<string name="add_a_terrain_layer">Add a terrain layer</string>
|
||||
<string name="add_a_satellite_layer">Add a satellite layer</string>
|
||||
<string name="set_raster_style">Set style with raster layer</string>
|
||||
<string name="change_the_water_color_on_zoom">Change the water color on zoom</string>
|
||||
<string name="custom_tiles">Custom tiles</string>
|
||||
<string name="apply_filtered_fill">Apply filtered fill</string>
|
||||
|
||||
48
platform/android/docs/README.md
Normal file
48
platform/android/docs/README.md
Normal file
@@ -0,0 +1,48 @@
|
||||
# MapLibre Android Examples
|
||||
|
||||
Welcome to the examples documentation of MapLibre Android.
|
||||
|
||||
<div class="grid cards" markdown>
|
||||
|
||||
- :material-clock-fast:{ .lg .middle } __Quickstart__
|
||||
|
||||
---
|
||||
|
||||
Learn how to include MapLibre Android in your project
|
||||
|
||||
[:octicons-arrow-right-24: Getting started](getting-started.md)
|
||||
|
||||
- :fontawesome-brands-slack:{ .lg .middle } __Find us on Slack__
|
||||
|
||||
---
|
||||
|
||||
Discuss the project and ask questions in the `#maplibre-android` channel
|
||||
|
||||
[:octicons-arrow-right-24: Slack](https://osmus.slack.com/archives/C01G3D28DAB)
|
||||
|
||||
- :fontawesome-solid-code:{ .lg .middle } __Contribute to these Docs__
|
||||
|
||||
---
|
||||
|
||||
Share your own examples with the community!
|
||||
|
||||
[:octicons-arrow-right-24: Documentation on GitHub](https://github.com/maplibre/maplibre-native/tree/main/platform/android/docs)
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
## Open-Source Apps Using MapLibre Android
|
||||
|
||||
You can learn how to use the API from MapLibre Android by stuying the source code of existing apps that intergrate MapLibre Android. Here are some open-source apps that use MapLibre Android:
|
||||
|
||||
- [Streetcomplete](https://github.com/streetcomplete/StreetComplete) ([source code](https://github.com/search?q=repo%3Astreetcomplete%2FStreetComplete%20maplibre&type=code))
|
||||
- [The official Wikipedia app for Android](https://github.com/wikimedia/apps-android-wikipedia) ([source code](https://github.com/search?q=repo%3Awikimedia%2Fapps-android-wikipedia%20maplibre&type=code)).
|
||||
- [MapLibreAndroidTestApp](https://github.com/maplibre/maplibre-native/tree/main/platform/android/MapLibreAndroidTestApp). This app is part of the MapLibre Native repository and is used for (automated) testing. Many of the examples in this documentation site come directly from this app.
|
||||
|
||||
## See Also
|
||||
|
||||
- [MapLibre Android API Documentation](https://maplibre.org/maplibre-native/android/api/)
|
||||
- [Source code on GitHub](https://github.com/maplibre/maplibre-native/tree/main/platform/android)
|
||||
- [Latest releases](https://github.com/maplibre/maplibre-native/releases?q=android-v11&expanded=true)
|
||||
- [GitHub Discussions](https://github.com/maplibre/maplibre-native/discussions/categories/q-a?discussions_q=is%3Aopen+category%3AQ%26A+label%3Aandroid)
|
||||
- [MapLibre on Slack](https://slack.openstreetmap.us). Join the `#maplibre-native` and `#maplibre-android` channels.
|
||||
13
platform/android/docs/annotations/add-markers.md
Normal file
13
platform/android/docs/annotations/add-markers.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Add Markers in Bulk
|
||||
|
||||
This example demonstrates how you can add markers in bulk.
|
||||
|
||||
<div style="display: flex; width: 100%; justify-content: center; gap: 1em;">
|
||||
<img src="https://github.com/user-attachments/assets/69f3f7fd-1778-4e2a-afa9-df3b01ab80c8" alt="100 images on map" width="300px"/>
|
||||
|
||||
<img src="https://github.com/user-attachments/assets/ea75a2ee-bb86-4ec0-ba54-7e328699d806" alt="1000 images on map" width="300px">
|
||||
</div>
|
||||
|
||||
```kotlin title="BulkMarkerActivity.kt"
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/BulkMarkerActivity.kt"
|
||||
```
|
||||
83
platform/android/docs/annotations/marker-annotations.md
Normal file
83
platform/android/docs/annotations/marker-annotations.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# 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
|
||||
`org.maplibre.android.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 `MapLibreMap`.
|
||||
It is used to add annotations to the map instance.
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/JsonApiActivity.kt:top"
|
||||
```
|
||||
|
||||
4. Call `mapview.getMapSync()` in order to get a `MapLibreMap` object.
|
||||
After `maplibreMap` is assigned, call the `getEarthQuakeDataFromUSGS()` method
|
||||
to make a HTTP request and transform data into the map annotations.
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/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
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/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
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/JsonApiActivity.kt:addMarkers"
|
||||
```
|
||||
|
||||
7. Here is the final result. For the full contents of `JsonApiActivity`, please visit source code of our [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%20-android/org.maplibre.android.annotations/-marker/index.html
|
||||
[provided image]: https://github.com/maplibre/maplibre-native/blob/main/platform/android/MapLibreAndroid/src/main/res/drawable-xxxhdpi/maplibre_marker_icon_default.png
|
||||
[Polyline]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.annotations/-polyline/index.html
|
||||
[Polygon]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.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%20-android/org.maplibre.android.annotations/-icon-factory/index.html
|
||||
[Icon]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.annotations/-icon/index.html
|
||||
[Quickstart]: ../getting-started.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%20-android/org.maplibre.android.annotations/-marker-options/index.html
|
||||
[Test App]: https://github.com/maplibre/maplibre-native/tree/main/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/annotation/JsonApiActivity.kt
|
||||
18
platform/android/docs/assets/extra.css
Normal file
18
platform/android/docs/assets/extra.css
Normal file
@@ -0,0 +1,18 @@
|
||||
|
||||
[data-md-color-scheme="default"] {
|
||||
--md-primary-fg-color: #295DAA;
|
||||
--md-accent-fg-color: #568ad6;
|
||||
}
|
||||
|
||||
[data-md-color-scheme="slate"] {
|
||||
--md-primary-fg-color: #295DAA;
|
||||
--md-accent-fg-color: #568ad6;
|
||||
}
|
||||
|
||||
.md-nav__title {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.md-grid {
|
||||
max-width: 1800px;
|
||||
}
|
||||
62
platform/android/docs/assets/logo.svg
Normal file
62
platform/android/docs/assets/logo.svg
Normal file
@@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="110.14971mm"
|
||||
height="110.14971mm"
|
||||
viewBox="0 0 110.14971 110.14971"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
xml:space="preserve"
|
||||
inkscape:export-filename="maplibreappicon.png"
|
||||
inkscape:export-xdpi="3.6895239"
|
||||
inkscape:export-ydpi="3.6895239"
|
||||
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
|
||||
sodipodi:docname="maplibreappicon.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
||||
id="namedview1"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:zoom="0.65575523"
|
||||
inkscape:cx="16.774552"
|
||||
inkscape:cy="359.1279"
|
||||
inkscape:window-width="1392"
|
||||
inkscape:window-height="1212"
|
||||
inkscape:window-x="169"
|
||||
inkscape:window-y="348"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false" /><defs
|
||||
id="defs1" /><g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-49.925144,-93.425148)"><rect
|
||||
style="fill:#295daa;fill-opacity:1;stroke-width:0.264583"
|
||||
id="rect2"
|
||||
width="110.14971"
|
||||
height="110.14971"
|
||||
x="49.925144"
|
||||
y="93.425148"
|
||||
ry="23.5" /><path
|
||||
fill="#82b4fe"
|
||||
fill-rule="evenodd"
|
||||
d="m 105.19551,109.31159 c -13.050838,-0.0847 -23.732067,9.79276 -23.811706,22.0173 -0.07038,10.82464 5.282141,16.10916 11.078368,21.83157 4.040982,3.98939 8.297338,8.1915 11.082068,14.63199 0.18786,0.15266 0.31354,0.25453 0.36751,0.30268 a 1.3663083,1.3663083 0 0 0 0.89906,0.34025 1.368425,1.368425 0 0 0 0.90381,-0.32834 c 0.0145,-0.013 0.0413,-0.0244 0.0741,-0.0384 0.09,-0.0386 0.22675,-0.0974 0.29739,-0.26008 2.77548,-6.37911 7.09242,-10.5246 11.21516,-14.4833 5.87084,-5.63747 11.34745,-10.8966 11.41757,-21.68842 0.0796,-12.22481 -10.47221,-22.24008 -23.52331,-22.32528 z m -0.22754,35.01734 c 6.46218,0.0421 11.73586,-5.24139 11.77845,-11.80094 0.0426,-6.55955 -5.16175,-11.91128 -11.6242,-11.95335 -6.462448,-0.0421 -11.735858,5.2414 -11.778721,11.80095 -0.0426,6.55955 5.161757,11.91127 11.624201,11.95334 z"
|
||||
clip-rule="evenodd"
|
||||
id="path3"
|
||||
style="stroke-width:0.264583" /><path
|
||||
fill="#ffffff"
|
||||
fill-rule="evenodd"
|
||||
d="m 96.509239,172.21893 c -1.078177,-0.007 -1.960033,1.18533 -1.969558,2.66303 l -0.04868,7.4795 c -0.0098,1.4777 0.856457,2.68129 1.934898,2.68843 l 16.506031,0.10743 c 1.07818,0.007 1.96003,-1.18534 1.96956,-2.66304 l 0.0487,-7.4795 c 0.01,-1.47796 -0.85646,-2.68155 -1.9349,-2.68843 z"
|
||||
clip-rule="evenodd"
|
||||
id="path4"
|
||||
style="stroke-width:0.264583" /></g></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
13
platform/android/docs/camera/animation-types.md
Normal file
13
platform/android/docs/camera/animation-types.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Animation Types
|
||||
|
||||
This example showcases the different animation types.
|
||||
|
||||
- **Move**: available via the `MapLibreMap.moveCamera` method.
|
||||
- **Ease**: available via the `MapLibreMap.easeCamera` method.
|
||||
- **Animate**: available via the `MapLibreMap.animateCamera` method.
|
||||
|
||||
It also shows how to pass callbacks that are called when an animation is cancelled.
|
||||
|
||||
```kotlin title="CameraAnimationTypeActivity.kt"
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/camera/CameraAnimationTypeActivity.kt"
|
||||
```
|
||||
7
platform/android/docs/camera/animator-animation.md
Normal file
7
platform/android/docs/camera/animator-animation.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# Animator Animation
|
||||
|
||||
This example showcases how to use the Animator API to schedule a sequence of map animations.
|
||||
|
||||
```kotlin title="CameraAnimatorActivity.kt"
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/camera/CameraAnimatorActivity.kt"
|
||||
```
|
||||
7
platform/android/docs/camera/cameraposition.md
Normal file
7
platform/android/docs/camera/cameraposition.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# CameraPosition Capabilities
|
||||
|
||||
This example showcases how to listen to camera change events.
|
||||
|
||||
```kotlin title="CameraPositionActivity.kt"
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/camera/CameraPositionActivity.kt"
|
||||
```
|
||||
28
platform/android/docs/camera/gesture-detector.md
Normal file
28
platform/android/docs/camera/gesture-detector.md
Normal file
@@ -0,0 +1,28 @@
|
||||
# Gesture Detector
|
||||
|
||||
The gesture detector of MapLibre Android is encapsulated in the [`maplibre-gestures-android`](https://github.com/maplibre/maplibre-gestures-android) package.
|
||||
|
||||
#### Gesture Listeners
|
||||
|
||||
You can add listeners for move, rotate, scale and shove gestures. For example, adding a move gesture listener with `MapLibreMap.addOnRotateListener`:
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/camera/GestureDetectorActivity.kt:addOnMoveListener"
|
||||
```
|
||||
|
||||
Refer to the full example below for examples of listeners for the other gesture types.
|
||||
|
||||
#### Settings
|
||||
|
||||
You can access an `UISettings` object via `MapLibreMap.uiSettings`. Available settings include:
|
||||
|
||||
- **Toggle Quick Zoom**. You can double tap on the map to use quick zoom. You can toggle this behavior on and off (`UiSettings.isQuickZoomGesturesEnabled`).
|
||||
- **Toggle Velocity Animations**. By default flicking causes the map to continue panning (while decelerating). You can turn this off with `UiSettings.isScaleVelocityAnimationEnabled`.
|
||||
- **Toggle Rotate Enabled**. Use `uiSettings.isRotateGesturesEnabled`.
|
||||
- **Toggle Zoom Enabled**. Use `uiSettings.isZoomGesturesEnabled`.
|
||||
|
||||
## Full Example Activity
|
||||
|
||||
```kotlin title="GestureDetectorActivity.kt"
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/camera/GestureDetectorActivity.kt"
|
||||
```
|
||||
26
platform/android/docs/camera/lat-lng-bounds.md
Normal file
26
platform/android/docs/camera/lat-lng-bounds.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# LatLngBounds API
|
||||
|
||||
|
||||
!!! note
|
||||
|
||||
You can find the full source code of this example in [`LatLngBoundsActivity.kt`](https://github.com/maplibre/maplibre-native/blob/main/platform/android/testapp/activity/camera/LatLngBoundsActivity.kt) of the MapLibreAndroidTestApp.
|
||||
|
||||
This example demonstrates setting the camera to some bounds defined by some features. It sets these bounds when the map is initialized and when the [bottom sheet](https://m2.material.io/components/sheets-bottom) is opened or closed.
|
||||
|
||||
<figure markdown="span">
|
||||
<video controls width="250">
|
||||
<source src="https://github.com/user-attachments/assets/46a89dfb-3abc-448a-be53-8abe39b2919b" />
|
||||
</video>
|
||||
</figure>
|
||||
|
||||
Here you can see how the feature collection is loaded and how `MapLibreMap.getCameraForLatLngBounds` is used to set the bounds during map initialization:
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/camera/LatLngBoundsActivity.kt:featureCollection"
|
||||
```
|
||||
|
||||
The `createBounds` function uses the `LatLngBounds` API to include all points within the bounds:
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/camera/LatLngBoundsActivity.kt:createBounds"
|
||||
```
|
||||
86
platform/android/docs/configuration.md
Normal file
86
platform/android/docs/configuration.md
Normal file
@@ -0,0 +1,86 @@
|
||||
# Configuration
|
||||
|
||||
This guide will explain various ways to create a map.
|
||||
|
||||
When working with maps, you likely want to configure the `MapView`.
|
||||
|
||||
There are several ways to build a `MapView`:
|
||||
|
||||
1. Using existing XML namespace tags for`MapView` in the layout.
|
||||
2. Creating `MapLibreMapOptions` and passing builder function values into the `MapView`.
|
||||
3. Creating a `SupportMapFragment` with the help of `MapLibreMapOptions`.
|
||||
|
||||
Before diving into `MapView` configurations, let's understand the capabilities of both XML namespaces and `MapLibreMapOptions`.
|
||||
|
||||
Here are some common configurations you can set:
|
||||
|
||||
- Map base URI
|
||||
- Camera settings
|
||||
- Zoom level
|
||||
- Pitch
|
||||
- Gestures
|
||||
- Compass
|
||||
- Logo
|
||||
- Attribution
|
||||
- Placement of the above elements on the map and more
|
||||
|
||||
We will explore how to achieve these configurations in XML layout and programmatically in Activity code, step by step.
|
||||
|
||||
### `MapView` Configuration with an XML layout
|
||||
|
||||
To configure `MapView` within an XML layout, you need to use the right namespace and provide the necessary data in the layout file.
|
||||
|
||||
```xml
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/res/layout/activity_map_options_xml.xml"
|
||||
```
|
||||
|
||||
This can be found in [`activity_map_options_xml.xml`](https://github.com/maplibre/maplibre-native/blob/main/platform/android/MapLibreAndroidTestApp/src/main/res/layout/activity_map_fragment.xml).
|
||||
|
||||
You can assign any other existing values to the `maplibre...` tags. Then, you only need to create `MapView` and `MapLibreMap` objects with a simple setup in the Activity.
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/options/MapOptionsXmlActivity.kt"
|
||||
```
|
||||
|
||||
This can be found in [`MapOptionsXmlActivity.kt`](https://github.com/maplibre/maplibre-native/blob/main/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/options/MapOptionsXmlActivity.kt).
|
||||
|
||||
### `MapView` configuration with `MapLibreMapOptions`
|
||||
|
||||
Here we don't have to create MapView from XML since we want to create it programmatically.
|
||||
```xml
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/res/layout/activity_map_options_runtime.xml"
|
||||
```
|
||||
|
||||
This can be found in [`activity_map_options_runtime.xml`](https://github.com/maplibre/maplibre-native/blob/main/platform/android/MapLibreAndroidTestApp/src/main/res/layout/activity_map_options_runtime.xml).
|
||||
|
||||
A `MapLibreMapOptions` object must be created and passed to the MapView constructor. All setup is done in the Activity code:
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/options/MapOptionsRuntimeActivity.ktl"
|
||||
```
|
||||
|
||||
This can be found in [`MapOptionsRuntimeActivity.kt`](https://github.com/maplibre/maplibre-native/blob/main/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/options/MapOptionsRuntimeActivity.kt).
|
||||
|
||||
Finally you will see a result similar to this:
|
||||
|
||||
<div style="text-align: center">
|
||||
<img src="https://github.com/user-attachments/assets/dd85f496-3e6f-4788-933e-4ec3d5999935" alt="Screenshot with the MapLibreMapOptions">
|
||||
</div>
|
||||
|
||||
For the full contents of `MapOptionsRuntimeActivity` and `MapOptionsXmlActivity`, please take a look at the source code of [MapLibreAndroidTestApp](https://github.com/maplibre/maplibre-native/tree/main/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/options).
|
||||
|
||||
You can read more about `MapLibreMapOptions` in the [Android API documentation](https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.maps/-map-libre-map-options/index.html?query=open%20class%20MapLibreMapOptions%20:%20Parcelable).
|
||||
|
||||
### `SupportMapFragment` with the help of `MapLibreMapOptions`.
|
||||
|
||||
If you are using MapFragment in your project, it is also easy to provide initial values to the `newInstance()` static method of `SupportMapFragment`, which requires a `MapLibreMapOptions` parameter.
|
||||
|
||||
Let's see how this can be done in a sample activity:
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/fragment/SupportMapFragmentActivity.kt"
|
||||
```
|
||||
|
||||
You can also find the full contents of `SupportMapFragmentActivity` in the [MapLibreAndroidTestApp](https://github.com/maplibre/maplibre-native/tree/main/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/fragment/SupportMapFragmentActivity.kt).
|
||||
|
||||
To learn more about `SupportMapFragment`, please visit the [Android API documentation](https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.maps/-support-map-fragment/index.html?query=open%20class%20SupportMapFragment%20:%20Fragment,%20OnMapReadyCallback).
|
||||
115
platform/android/docs/getting-started.md
Normal file
115
platform/android/docs/getting-started.md
Normal file
@@ -0,0 +1,115 @@
|
||||
# Quickstart
|
||||
|
||||
1. Add bintray Maven repositories to your project-level Gradle file (usually `<project>/<app-module>/build.gradle`).
|
||||
|
||||
```gradle
|
||||
allprojects {
|
||||
repositories {
|
||||
...
|
||||
mavenCentral()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
2. Add the library as a dependency into your module Gradle file (usually `<project>/<app-module>/build.gradle`). Replace `<version>` with the [latest MapLibre Android version](https://github.com/maplibre/maplibre-native/releases?q=android-v11&expanded=true) (e.g.: `org.maplibre.gl:android-sdk:11.5.2`):
|
||||
|
||||
```gradle
|
||||
dependencies {
|
||||
...
|
||||
implementation 'org.maplibre.gl:android-sdk:<version>'
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
3. Sync your Android project with Gradle files.
|
||||
|
||||
4. Add a `MapView` to your layout XML file (usually `<project>/<app-module>/src/main/res/layout/activity_main.xml`).
|
||||
|
||||
```xml
|
||||
...
|
||||
<org.maplibre.android.maps.MapView
|
||||
android:id="@+id/mapView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
/>
|
||||
...
|
||||
```
|
||||
|
||||
5. Initialize the `MapView` in your `MainActivity` file by following the example below:
|
||||
|
||||
```kotlin
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import org.maplibre.android.Maplibre
|
||||
import org.maplibre.android.camera.CameraPosition
|
||||
import org.maplibre.android.geometry.LatLng
|
||||
import org.maplibre.android.maps.MapView
|
||||
import org.maplibre.android.testapp.R
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
|
||||
// Declare a variable for MapView
|
||||
private lateinit var mapView: MapView
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// Init MapLibre
|
||||
MapLibre.getInstance(this)
|
||||
|
||||
// Init layout view
|
||||
val inflater = LayoutInflater.from(this)
|
||||
val rootView = inflater.inflate(R.layout.activity_main, null)
|
||||
setContentView(rootView)
|
||||
|
||||
// Init the MapView
|
||||
mapView = rootView.findViewById(R.id.mapView)
|
||||
mapView.getMapAsync { map ->
|
||||
map.setStyle("https://demotiles.maplibre.org/style.json")
|
||||
map.cameraPosition = CameraPosition.Builder().target(LatLng(0.0,0.0)).zoom(1.0).build()
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
6. Build and run the app. If you run the app successfully, a map will be displayed as seen in the screenshot below.
|
||||
|
||||
<div style="text-align: center;">
|
||||
<img src="https://user-images.githubusercontent.com/32692818/228113379-475e86f5-e3fa-4a36-8b4b-1fcba0f1eb3b.png" alt="Screenshot with the map in demotile style" width="50%" height="50%">
|
||||
</div>
|
||||
95
platform/android/docs/location-component.md
Normal file
95
platform/android/docs/location-component.md
Normal file
@@ -0,0 +1,95 @@
|
||||
# LocationComponent
|
||||
|
||||
This guide will demonstrate how to utilize the [LocationComponent] to represent the user's current location.
|
||||
|
||||
|
||||
When implementing the [LocationComponent], the application should request location permissions. Declare the need for foreground location in the `AndroidManifest.xml` file. For more information, please refer to the [Android Developer Documentation].
|
||||
|
||||
```xml
|
||||
<manifest ... >
|
||||
<!-- Always include this permission -->
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||
|
||||
<!-- Include only if your app benefits from precise location access. -->
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||
</manifest>
|
||||
```
|
||||
|
||||
Create a new activity named `BasicLocationPulsingCircleActivity`:
|
||||
|
||||
- This Activity should implement the `OnMapReadyCallback` interface. The `onMapReady()` method is triggered when the map is ready to be used.
|
||||
- Add a variable `permissionsManager` to manage permissions.
|
||||
- Add a variable `locationComponent` to manage user location.
|
||||
- At the end of the `onCreate()` method, call `checkPermissions()` to ensure that the application can access the user's location.
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/location/BasicLocationPulsingCircleActivity.kt:top"
|
||||
```
|
||||
|
||||
In the `checkPermissions()` method, the [PermissionManager] is used to request location permissions at runtime and handle the callbacks for permission granting or rejection.Additionally, you should pass the results of `Activity.onRequestPermissionResult()` to it. If the permissions are granted, call `mapView.getMapAsync(this)` to register the activity as a listener for onMapReady event.
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/location/BasicLocationPulsingCircleActivity.kt:permission"
|
||||
|
||||
```
|
||||
|
||||
In the `onMapReady()` method, first set the style and then handle the user's location using the [LocationComponent].
|
||||
|
||||
To configure the [LocationComponent], developers should use [LocationComponentOptions].
|
||||
|
||||
In this demonstration, we create an instance of this class.
|
||||
|
||||
In this method:
|
||||
|
||||
- Use the annotation `@SuppressLint("MissingPermission")` to suppress warnings related to missing location access permissions.
|
||||
- In `setStyle(),` you can utilize other public and token-free styles like [demotiles] instead of the [predefined styles].
|
||||
- For the builder of [LocationComponentOptions], use `pulseEnabled(true)` to enable the pulse animation, which enhances awareness of the user's location.
|
||||
- Use method `buildLocationComponentActivationOptions()` to set [LocationComponentActivationOptions], then activate `locatinoComponent` with it.
|
||||
- To apply options, make sure you call `activateLocationComponent()` of `locationComponent`. You can also set `locationComponent`'s various properties like `isLocationComponentEnabled` , `cameraMode` , etc...
|
||||
- `CameraMode.TRACKING`[^1] means that when the user's location is updated, the camera will reposition accordingly.
|
||||
- `locationComponent!!.forceLocationUpdate(lastLocation)` updates the the user's last known location.
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/location/BasicLocationPulsingCircleActivity.kt:onMapReady"
|
||||
```
|
||||
|
||||
[LocationComponentActivationOptions] is used to hold the style, [LocationComponentOptions] and other locating behaviors.
|
||||
|
||||
- It can also be used to configure how to obtain the current location, such as [LocationEngine] and intervals.
|
||||
- In this demonstration, it sets 750ms as the fastest interval for location updates, providing high accuracy location results (but with higher power consumption).
|
||||
- For more information, please visit the [documentation page][LocationComponentActivationOptions].
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/location/BasicLocationPulsingCircleActivity.kt:LocationComponentActivationOptions"
|
||||
```
|
||||
|
||||
For further customization, you can also utilize the `foregroundTintColor()` and `pulseColor()` methods on the [LocationComponentOptions] builder:
|
||||
|
||||
```kotlin
|
||||
val locationComponentOptions =
|
||||
LocationComponentOptions.builder(this@BasicLocationPulsingCircleActivity)
|
||||
.pulseEnabled(true)
|
||||
.pulseColor(Color.RED) // Set color of pulse
|
||||
.foregroundTintColor(Color.BLACK) // Set color of user location
|
||||
.build()
|
||||
```
|
||||
|
||||
Here is the final results with different color configurations. For the complete content of this demo, please refer to the source code of the [Test App].
|
||||
|
||||

|
||||
|
||||
|
||||
[^1]: A variety of [camera modes] determine how the camera will track the user location.
|
||||
They provide the right context to your users at the correct time.
|
||||
|
||||
[LocationComponent]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.location/-location-component/index.html
|
||||
[Android Developer Documentation]: https://developer.android.com/training/location/permissions
|
||||
[onMapReadyCallback]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.maps/-on-map-ready-callback/index.html
|
||||
[PermissionManager]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.location.permissions/-permissions-manager/index.html
|
||||
[LocationComponentOptions]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.location/-location-component-options/index.html
|
||||
[demotiles]: https://demotiles.maplibre.org/style.json
|
||||
[predefined styles]: https://github.com/maplibre/maplibre-native/tree/main/src/mbgl/util/tile_server_options.cpp
|
||||
[LocationComponentActivationOptions]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.location/-location-component-activation-options/index.html
|
||||
[LocationEngine]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.location.engine/-location-engine/index.html
|
||||
[Test APP]: https://github.com/maplibre/maplibre-native/tree/main/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/location/BasicLocationPulsingCircleActivity.kt
|
||||
[camera modes]: https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.location.modes/-camera-mode/index.html
|
||||
163
platform/android/docs/snapshotter.md
Normal file
163
platform/android/docs/snapshotter.md
Normal file
@@ -0,0 +1,163 @@
|
||||
# Using the Snapshotter
|
||||
|
||||
This guide will help you walk through how to use [MapSnapshotter](https://maplibre.org/maplibre-native/android/api/-map-libre%20-native%20-android/org.maplibre.android.snapshotter/-map-snapshotter/index.html).
|
||||
|
||||
## Map Snapshot with Local Style
|
||||
|
||||
!!! note
|
||||
|
||||
You can find the full source code of this example in [`MapSnapshotterLocalStyleActivity.kt`](https://github.com/maplibre/maplibre-native/blob/main/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterLocalStyleActivity.kt) of the MapLibreAndroidTestApp.
|
||||
|
||||
To get started we will show how to use the map snapshotter with a local style.
|
||||
|
||||
<figure markdown="span">
|
||||
{ width="300" }
|
||||
</figure>
|
||||
|
||||
Add the [source code of the Demotiles style](https://github.com/maplibre/demotiles/blob/gh-pages/style.json) as `demotiles.json` to the `res/raw` directory of our app[^1]. First we will read this style:
|
||||
|
||||
[^1]: See [App resources overview](https://developer.android.com/guide/topics/resources/providing-resources) for this and other ways you can provide resources to your app.
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterLocalStyleActivity.kt:readStyleJson"
|
||||
```
|
||||
|
||||
Next, we configure the MapSnapshotter, passing height and width, the style we just read and the camera position:
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterLocalStyleActivity.kt:createMapSnapshotter"
|
||||
```
|
||||
|
||||
Lastly we use the `.start()` method to create the snapshot, and pass callbacks for when the snapshot is ready or for when an error occurs.
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterLocalStyleActivity.kt:createSnapshot"
|
||||
```
|
||||
|
||||
## Show a Grid of Snapshots
|
||||
|
||||
!!! note
|
||||
|
||||
You can find the full source code of this example in [`MapSnapshotterActivity.kt`](https://github.com/maplibre/maplibre-native/blob/main/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterActivity.kt) of the MapLibreAndroidTestApp.
|
||||
|
||||
In this example, we demonstrate how to use the `MapSnapshotter` to create multiple map snapshots with different styles and camera positions, displaying them in a grid layout.
|
||||
|
||||
<figure markdown="span">
|
||||
{ width="300" }
|
||||
</figure>
|
||||
|
||||
First we create a [`GridLayout`](https://developer.android.com/reference/kotlin/android/widget/GridLayout) and a list of `MapSnapshotter` instances. We create a `Style.Builder` with a different style for each cell in the grid.
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterActivity.kt:styleBuilder"
|
||||
```
|
||||
|
||||
Next we create a `MapSnapshotter.Options` object to customize the settings of each snapshot(ter).
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterActivity.kt:mapSnapShotterOptions"
|
||||
```
|
||||
|
||||
For some rows we randomize the visible region of the snapshot:
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterActivity.kt:setRegion"
|
||||
```
|
||||
|
||||
For some columns we randomize the camera position:
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterActivity.kt:setCameraPosition"
|
||||
```
|
||||
|
||||
In the last column of the first row we add two bitmaps. See the next example for more details.
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterActivity.kt:addMarkerLayer"
|
||||
```
|
||||
|
||||
## Map Snapshot with Bitmap Overlay
|
||||
|
||||
!!! note
|
||||
|
||||
You can also find this code in [`MapSnapshotterBitMapOverlayActivity.kt`](https://github.com/maplibre/maplibre-native/blob/main/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterBitMapOverlayActivity.kt) of the MapLibreAndroidTestApp.
|
||||
|
||||
This example adds a bitmap on top of the snapshot. It also demonstrates how you can add a click listener to a snapshot.
|
||||
|
||||
<figure markdown="span">
|
||||
{ width="300" }
|
||||
</figure>
|
||||
|
||||
|
||||
```kotlin title="MapSnapshotterBitMapOverlayActivity.kt"
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterBitMapOverlayActivity.kt"
|
||||
```
|
||||
|
||||
## Map Snapshotter with Heatmap Layer
|
||||
|
||||
!!! note
|
||||
|
||||
You can find the full source code of this example in [`MapSnapshotterHeatMapActivity.kt`](https://github.com/maplibre/maplibre-native/blob/main/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterHeatMapActivity.kt) of the MapLibreAndroidTestApp.
|
||||
|
||||
In this example, we demonstrate how to use the `MapSnapshotter` to create a snapshot of a map that includes a heatmap layer. The heatmap represents earthquake data loaded from a GeoJSON source.
|
||||
|
||||
<figure markdown="span">
|
||||
{ width="300" }
|
||||
</figure>
|
||||
|
||||
First, we create the `MapSnapshotterHeatMapActivity` class, which extends `AppCompatActivity` and implements `MapSnapshotter.SnapshotReadyCallback` to receive the snapshot once it's ready.
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterHeatMapActivity.kt:class_declaration"
|
||||
```
|
||||
|
||||
In the `onCreate` method, we set up the layout and initialize the `MapSnapshotter` once the layout is ready.
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterHeatMapActivity.kt:onCreate"
|
||||
```
|
||||
|
||||
Here, we wait for the layout to be laid out using an `OnGlobalLayoutListener` before initializing the `MapSnapshotter`. We create a `Style.Builder` with a base style (`TestStyles.AMERICANA`), add the earthquake data source, and add the heatmap layer above the "water" layer.
|
||||
|
||||
The `heatmapLayer` property defines the `HeatmapLayer` used to visualize the earthquake data.
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterHeatMapActivity.kt:heatmapLayer"
|
||||
```
|
||||
|
||||
This code sets up the heatmap layer's properties, such as color ramp, weight, intensity, radius, and opacity, using expressions that interpolate based on data properties and zoom level.
|
||||
|
||||
We also define the `earthquakeSource`, which loads data from a GeoJSON file containing earthquake information.
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterHeatMapActivity.kt:earthquakeSource"
|
||||
```
|
||||
|
||||
When the snapshot is ready, the `onSnapshotReady` callback is invoked, where we set the snapshot bitmap to an `ImageView` to display it.
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterHeatMapActivity.kt:onSnapshotReady"
|
||||
```
|
||||
|
||||
Finally, we ensure to cancel the snapshotter in the `onStop` method to free up resources.
|
||||
|
||||
```kotlin
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/snapshot/MapSnapshotterHeatMapActivity.kt:onStop"
|
||||
```
|
||||
|
||||
|
||||
## Map Snapshotter with Expression
|
||||
|
||||
!!! note
|
||||
|
||||
You can find the full source code of this example in [`MapSnapshotterWithinExpression.kt`](https://github.com/maplibre/maplibre-native/blob/main/platform/android/MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/turf/MapSnapshotterWithinExpression.kt) of the MapLibreAndroidTestApp.
|
||||
|
||||
In this example the map on top is a live while the map on the bottom is a snapshot that is updated as you pan the map. We style of the snapshot is modified: using a [within](https://maplibre.org/maplibre-style-spec/expressions/#within) expression only POIs within a certain distance to a line is shown. A highlight for this area is added to the map as are various points.
|
||||
|
||||
<figure markdown="span">
|
||||
{ width="300" }
|
||||
</figure>
|
||||
|
||||
```kotlin title="MapSnapshotterWithinExpression.kt"
|
||||
--8<-- "MapLibreAndroidTestApp/src/main/java/org/maplibre/android/testapp/activity/turf/MapSnapshotterWithinExpression.kt"
|
||||
```
|
||||
74
platform/android/mkdocs.yaml
Normal file
74
platform/android/mkdocs.yaml
Normal file
@@ -0,0 +1,74 @@
|
||||
site_name: MapLibre Android Examples
|
||||
site_url: https://www.maplibre.org/maplibre-native/android/examples
|
||||
repo_url: https://github.com/louwers/maplibre-native
|
||||
site_description: MapLibre Android Examples
|
||||
edit_uri: edit/main/platform/android/docs
|
||||
extra_css:
|
||||
- assets/extra.css
|
||||
theme:
|
||||
name: 'material'
|
||||
favicon: https://maplibre.org/favicon.ico
|
||||
logo: assets/logo.svg
|
||||
features:
|
||||
- content.code.copy
|
||||
- search.suggest
|
||||
- navigation.instant
|
||||
- navigation.sections
|
||||
- content.action.edit
|
||||
palette:
|
||||
- media: "(prefers-color-scheme)"
|
||||
toggle:
|
||||
icon: material/brightness-auto
|
||||
name: Switch to light mode
|
||||
- media: "(prefers-color-scheme: dark)"
|
||||
scheme: slate
|
||||
toggle:
|
||||
icon: material/brightness-4
|
||||
name: Switch to system preference
|
||||
- media: "(prefers-color-scheme: light)"
|
||||
scheme: default
|
||||
toggle:
|
||||
icon: material/brightness-7
|
||||
name: Switch to dark mode
|
||||
markdown_extensions:
|
||||
- pymdownx.highlight:
|
||||
anchor_linenums: true
|
||||
line_spans: __span
|
||||
pygments_lang_class: true
|
||||
- pymdownx.inlinehilite
|
||||
- pymdownx.snippets:
|
||||
dedent_subsections: true
|
||||
- pymdownx.superfences
|
||||
- pymdownx.escapeall:
|
||||
hardbreak: True
|
||||
nbsp: True
|
||||
- admonition
|
||||
- pymdownx.details
|
||||
- footnotes
|
||||
- attr_list
|
||||
- md_in_html
|
||||
- pymdownx.emoji:
|
||||
emoji_index: !!python/name:material.extensions.emoji.twemoji
|
||||
emoji_generator: !!python/name:material.extensions.emoji.to_svg
|
||||
extra:
|
||||
social:
|
||||
- icon: fontawesome/brands/mastodon
|
||||
link: https://mastodon.social/@maplibre
|
||||
- icon: fontawesome/brands/x-twitter
|
||||
link: https://twitter.com/maplibre
|
||||
- icon: fontawesome/brands/linkedin
|
||||
link: https://www.linkedin.com/company/maplibre
|
||||
- icon: fontawesome/brands/slack
|
||||
link: https://osmus.slack.com/archives/C01G3D28DAB
|
||||
- icon: fontawesome/brands/github
|
||||
link: https://github.com/maplibre
|
||||
plugins:
|
||||
- search
|
||||
- social:
|
||||
cards_layout_options:
|
||||
background_color: '#295DAA'
|
||||
validation:
|
||||
omitted_files: warn
|
||||
absolute_links: warn
|
||||
unrecognized_links: warn
|
||||
anchors: warn
|
||||
Reference in New Issue
Block a user