Skip to content

Commit c945701

Browse files
committed
Add debug tree map
1 parent b7f7eff commit c945701

File tree

6 files changed

+219
-94
lines changed

6 files changed

+219
-94
lines changed

app/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -211,6 +211,9 @@ dependencies {
211211
// MapLibre
212212
implementation libs.maplibre.android.sdk
213213

214+
// Image Loading
215+
implementation libs.coil.compose
216+
214217
// Kotlin
215218
implementation libs.kotlin.stdlib.jdk7
216219
implementation libs.kotlinx.coroutines.core

app/src/main/java/org/greenstand/android/TreeTracker/map/LibreMap.kt

Lines changed: 93 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,33 +10,54 @@ import androidx.compose.ui.platform.LocalLifecycleOwner
1010
import androidx.compose.ui.viewinterop.AndroidView
1111
import androidx.lifecycle.Lifecycle
1212
import androidx.lifecycle.LifecycleEventObserver
13-
import org.maplibre.android.annotations.MarkerOptions
13+
import com.google.gson.JsonArray
14+
import com.google.gson.JsonObject
1415
import org.maplibre.android.camera.CameraPosition
1516
import org.maplibre.android.camera.CameraUpdateFactory
1617
import org.maplibre.android.geometry.LatLng
1718
import org.maplibre.android.geometry.LatLngBounds
19+
import org.maplibre.android.location.LocationComponentActivationOptions
20+
import org.maplibre.android.location.LocationComponentOptions
1821
import org.maplibre.android.maps.MapView
22+
import org.maplibre.android.style.expressions.Expression
23+
import org.maplibre.android.style.layers.CircleLayer
24+
import org.maplibre.android.style.layers.PropertyFactory
25+
import org.maplibre.android.style.sources.GeoJsonSource
1926

27+
@androidx.annotation.RequiresPermission(allOf = [android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_COARSE_LOCATION])
2028
@Composable
2129
fun LibreMap(
2230
markers: List<MapMarker>,
2331
selectedMarkerId: String?,
2432
modifier: Modifier = Modifier,
25-
styleUrl: String = "https://demotiles.maplibre.org/style.json"
33+
styleUrl: String = "https://demotiles.maplibre.org/style.json",
34+
onMarkerClick: (String) -> Unit = {}
2635
) {
2736
val context = LocalContext.current
2837
val lifecycleOwner = LocalLifecycleOwner.current
2938

3039
val mapView = remember {
3140
MapView(context).apply {
3241
getMapAsync { mapLibreMap ->
33-
mapLibreMap.setStyle(styleUrl) {
42+
mapLibreMap.setStyle(styleUrl) { style ->
3443
// Set initial camera position (centered on equator with moderate zoom)
3544
val initialPosition = CameraPosition.Builder()
3645
.target(LatLng(0.0, 0.0))
3746
.zoom(2.0)
3847
.build()
3948
mapLibreMap.cameraPosition = initialPosition
49+
val locationComponent = mapLibreMap.locationComponent
50+
locationComponent.activateLocationComponent(
51+
LocationComponentActivationOptions
52+
.builder(context, style)
53+
.locationComponentOptions(
54+
LocationComponentOptions.builder(context)
55+
.pulseEnabled(true)
56+
.build()
57+
)
58+
.build()
59+
)
60+
locationComponent.isLocationComponentEnabled = true
4061
}
4162
}
4263
}
@@ -67,18 +88,79 @@ fun LibreMap(
6788
update = { view ->
6889
view.getMapAsync { mapLibreMap ->
6990
mapLibreMap.getStyle { style ->
70-
// Clear existing markers
71-
mapLibreMap.clear()
91+
// Remove existing source and layer if they exist
92+
style.getLayer("tree-markers-layer")?.let { style.removeLayer(it) }
93+
style.getSource("tree-markers-source")?.let { style.removeSource(it) }
7294

73-
// Add markers in bulk
95+
// Add markers in bulk as GeoJSON
7496
if (markers.isNotEmpty()) {
75-
val markerOptions = markers.map { marker ->
76-
MarkerOptions()
77-
.position(LatLng(marker.latitude, marker.longitude))
97+
// Create GeoJSON FeatureCollection
98+
val features = JsonArray()
99+
markers.forEach { marker ->
100+
val feature = JsonObject().apply {
101+
addProperty("type", "Feature")
102+
add("geometry", JsonObject().apply {
103+
addProperty("type", "Point")
104+
add("coordinates", JsonArray().apply {
105+
add(marker.longitude)
106+
add(marker.latitude)
107+
})
108+
})
109+
add("properties", JsonObject().apply {
110+
addProperty("id", marker.id)
111+
})
112+
}
113+
features.add(feature)
114+
}
115+
116+
val featureCollection = JsonObject().apply {
117+
addProperty("type", "FeatureCollection")
118+
add("features", features)
78119
}
79120

80-
// Add all markers at once
81-
mapLibreMap.addMarkers(markerOptions)
121+
// Add GeoJSON source
122+
val source = GeoJsonSource("tree-markers-source", featureCollection.toString())
123+
style.addSource(source)
124+
125+
// Add circle layer with green dots
126+
val radiusExpression = if (selectedMarkerId != null) {
127+
Expression.switchCase(
128+
Expression.eq(Expression.get("id"), Expression.literal(selectedMarkerId)),
129+
Expression.literal(12f), // Selected marker radius
130+
Expression.literal(8f) // Default marker radius
131+
)
132+
} else {
133+
Expression.literal(8f)
134+
}
135+
136+
val circleLayer = CircleLayer("tree-markers-layer", "tree-markers-source").apply {
137+
setProperties(
138+
PropertyFactory.circleRadius(radiusExpression),
139+
PropertyFactory.circleColor("#4CAF50"),
140+
PropertyFactory.circleStrokeWidth(2f),
141+
PropertyFactory.circleStrokeColor("#FFFFFF")
142+
)
143+
}
144+
style.addLayer(circleLayer)
145+
146+
// Add click listener for markers
147+
mapLibreMap.addOnMapClickListener { latLng ->
148+
// Convert map coordinates to screen point
149+
val screenPoint = mapLibreMap.projection.toScreenLocation(latLng)
150+
151+
// Query features at click point from marker layer
152+
val features = mapLibreMap.queryRenderedFeatures(screenPoint, "tree-markers-layer")
153+
154+
// If marker clicked, extract ID and notify
155+
if (features.isNotEmpty()) {
156+
val markerId = features.first().getStringProperty("id")
157+
if (markerId != null) {
158+
onMarkerClick(markerId)
159+
return@addOnMapClickListener true // Consume event
160+
}
161+
}
162+
false // Don't consume - allow map pan/zoom
163+
}
82164

83165
// Handle selected marker zoom
84166
if (selectedMarkerId != null) {

0 commit comments

Comments
 (0)