Add feature state functionality to Android (#4219)
This commit is contained in:
@@ -831,6 +831,18 @@
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".activity.FeatureOverviewActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.feature.FeatureStateActivity"
|
||||
android:description="@string/description_feature_state"
|
||||
android:exported="true"
|
||||
android:label="@string/activity_feature_state">
|
||||
<meta-data
|
||||
android:name="@string/category"
|
||||
android:value="@string/category_features" />
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".activity.FeatureOverviewActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".activity.style.SymbolGeneratorActivity"
|
||||
android:description="@string/description_symbol_generator"
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
package org.maplibre.android.testapp.activity.feature
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.PointF
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.google.gson.JsonObject
|
||||
import org.maplibre.android.camera.CameraUpdateFactory
|
||||
import org.maplibre.android.geometry.LatLng
|
||||
import org.maplibre.android.maps.MapLibreMap
|
||||
import org.maplibre.android.maps.MapView
|
||||
import org.maplibre.android.maps.Style
|
||||
import org.maplibre.android.style.expressions.Expression.*
|
||||
import org.maplibre.android.style.layers.FillLayer
|
||||
import org.maplibre.android.style.layers.LineLayer
|
||||
import org.maplibre.android.style.layers.PropertyFactory.fillColor
|
||||
import org.maplibre.android.style.layers.PropertyFactory.fillOpacity
|
||||
import org.maplibre.android.style.layers.PropertyFactory.lineColor
|
||||
import org.maplibre.android.style.layers.PropertyFactory.lineWidth
|
||||
import org.maplibre.android.style.sources.GeoJsonSource
|
||||
import org.maplibre.android.testapp.R
|
||||
import org.maplibre.android.testapp.styles.TestStyles
|
||||
import java.net.URI
|
||||
|
||||
/**
|
||||
* Test activity showcasing interactive styling with feature state.
|
||||
*
|
||||
* Loads US states from a remote GeoJSON source and uses feature-state
|
||||
* expressions to highlight tapped states.
|
||||
*/
|
||||
class FeatureStateActivity : AppCompatActivity() {
|
||||
companion object {
|
||||
private const val STATES_SOURCE_ID = "states"
|
||||
private const val FILL_LAYER_ID = "state-fills"
|
||||
private const val LINE_LAYER_ID = "state-borders"
|
||||
private const val STATES_URL =
|
||||
"https://maplibre.org/maplibre-gl-js/docs/assets/us_states.geojson"
|
||||
}
|
||||
|
||||
private lateinit var mapView: MapView
|
||||
private lateinit var maplibreMap: MapLibreMap
|
||||
private lateinit var statesSource: GeoJsonSource
|
||||
|
||||
private val mapClickListener = MapLibreMap.OnMapClickListener { point ->
|
||||
handleMapClick(point)
|
||||
}
|
||||
|
||||
// # --8<-- [start:onCreate]
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContentView(R.layout.activity_query_features_point)
|
||||
|
||||
mapView = findViewById<View>(R.id.mapView) as MapView
|
||||
mapView.onCreate(savedInstanceState)
|
||||
mapView.getMapAsync { map ->
|
||||
maplibreMap = map
|
||||
maplibreMap.moveCamera(
|
||||
CameraUpdateFactory.newLatLngZoom(LatLng(42.619626, -103.523181), 3.0)
|
||||
)
|
||||
maplibreMap.setStyle(TestStyles.getPredefinedStyleWithFallback("Streets")) { style ->
|
||||
addStatesLayer(style)
|
||||
maplibreMap.addOnMapClickListener(mapClickListener)
|
||||
}
|
||||
}
|
||||
}
|
||||
// # --8<-- [end:onCreate]
|
||||
|
||||
// # --8<-- [start:addStatesLayer]
|
||||
private fun addStatesLayer(style: Style) {
|
||||
val source = GeoJsonSource(STATES_SOURCE_ID, URI(STATES_URL))
|
||||
style.addSource(source)
|
||||
statesSource = source
|
||||
|
||||
val selectedFill = Color.parseColor("#d94d41")
|
||||
val defaultFill = Color.parseColor("#2f7de1")
|
||||
val selectedBorder = Color.parseColor("#8f241f")
|
||||
val defaultBorder = Color.parseColor("#2f7de1")
|
||||
|
||||
style.addLayer(
|
||||
FillLayer(FILL_LAYER_ID, STATES_SOURCE_ID).withProperties(
|
||||
fillColor(
|
||||
switchCase(
|
||||
toBool(featureState("selected")),
|
||||
color(selectedFill),
|
||||
color(defaultFill)
|
||||
)
|
||||
),
|
||||
fillOpacity(
|
||||
switchCase(
|
||||
toBool(featureState("selected")),
|
||||
literal(0.7f),
|
||||
literal(0.5f)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
style.addLayer(
|
||||
LineLayer(LINE_LAYER_ID, STATES_SOURCE_ID).withProperties(
|
||||
lineColor(
|
||||
switchCase(
|
||||
toBool(featureState("selected")),
|
||||
color(selectedBorder),
|
||||
color(defaultBorder)
|
||||
)
|
||||
),
|
||||
lineWidth(
|
||||
switchCase(
|
||||
toBool(featureState("selected")),
|
||||
literal(2.0f),
|
||||
literal(1.0f)
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
// # --8<-- [end:addStatesLayer]
|
||||
|
||||
// # --8<-- [start:handleMapClick]
|
||||
private fun handleMapClick(point: LatLng): Boolean {
|
||||
val screenPoint: PointF = maplibreMap.projection.toScreenLocation(point)
|
||||
val features = maplibreMap.queryRenderedFeatures(screenPoint, FILL_LAYER_ID)
|
||||
val feature = features.firstOrNull() ?: return false
|
||||
val featureId = feature.id() ?: return false
|
||||
|
||||
val currentState = statesSource.getFeatureState(featureId)
|
||||
val isSelected = currentState
|
||||
?.get("selected")
|
||||
?.takeUnless { it.isJsonNull }
|
||||
?.asBoolean
|
||||
?: false
|
||||
|
||||
val nextState = JsonObject().apply { addProperty("selected", !isSelected) }
|
||||
statesSource.setFeatureState(featureId, nextState)
|
||||
|
||||
val stateName = feature.getStringProperty("STATE_NAME") ?: "Unknown"
|
||||
Toast.makeText(
|
||||
this,
|
||||
"$stateName ${if (!isSelected) "selected" else "deselected"}",
|
||||
Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return true
|
||||
}
|
||||
// # --8<-- [end:handleMapClick]
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
mapView.onStart()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
mapView.onResume()
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
mapView.onPause()
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
super.onStop()
|
||||
mapView.onStop()
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
mapView.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
if (this::maplibreMap.isInitialized) {
|
||||
maplibreMap.removeOnMapClickListener(mapClickListener)
|
||||
}
|
||||
mapView.onDestroy()
|
||||
}
|
||||
|
||||
override fun onLowMemory() {
|
||||
super.onLowMemory()
|
||||
mapView.onLowMemory()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"version": 8,
|
||||
"center": [0, 0],
|
||||
"zoom": 3,
|
||||
"sources": {
|
||||
"style-source": {
|
||||
"type": "geojson",
|
||||
"data": {
|
||||
"type": "FeatureCollection",
|
||||
"features": [
|
||||
{
|
||||
"type": "Feature",
|
||||
"id": "feature-1",
|
||||
"properties": {},
|
||||
"geometry": {
|
||||
"type": "Point",
|
||||
"coordinates": [0, 0]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"layers": [
|
||||
{
|
||||
"id": "background",
|
||||
"type": "background",
|
||||
"paint": {
|
||||
"background-color": "#ffffff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "selected-layer",
|
||||
"type": "circle",
|
||||
"source": "style-source",
|
||||
"filter": ["==", ["feature-state", "selected"], true],
|
||||
"paint": {
|
||||
"circle-radius": 12,
|
||||
"circle-color": "#ff0000"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -50,6 +50,7 @@
|
||||
<string name="description_query_rendered_features_box_symbol_count">Count all rendered symbols in box</string>
|
||||
<string name="description_query_rendered_features_box_highlight">Highlight buildings in box</string>
|
||||
<string name="description_query_source_features">Query source for features</string>
|
||||
<string name="description_feature_state">Toggle feature state and style features interactively</string>
|
||||
<string name="description_simple_map">Shows a simple map</string>
|
||||
<string name="description_map_change">Logs map change events to Logcat</string>
|
||||
<string name="description_visibility_map">Changes visibility of map and view parent</string>
|
||||
|
||||
@@ -49,6 +49,7 @@
|
||||
<string name="activity_query_rendered_features_box_symbol_count">Count symbols in box</string>
|
||||
<string name="activity_query_rendered_features_box_highlight">Highlight features in box</string>
|
||||
<string name="activity_query_source_features">Query source features</string>
|
||||
<string name="activity_feature_state">Feature state</string>
|
||||
<string name="activity_symbol_layer">Symbols</string>
|
||||
<string name="activity_add_sprite">Add Custom Sprite</string>
|
||||
<string name="activity_simple_map">Simple Map</string>
|
||||
|
||||
Reference in New Issue
Block a user