mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
feat(metrics): redesign position log with SelectableMetricCard and add CSV export to all metrics screens (#5062)
This commit is contained in:
parent
37e9e2c8f0
commit
a6423d0a0f
16 changed files with 398 additions and 251 deletions
|
|
@ -124,20 +124,21 @@ fun MapView.addPolyline(density: Density, geoPoints: List<GeoPoint>, onClick: ()
|
|||
return polyline
|
||||
}
|
||||
|
||||
fun MapView.addPositionMarkers(positions: List<Position>, onClick: () -> Unit): List<Marker> {
|
||||
fun MapView.addPositionMarkers(positions: List<Position>, onClick: (Int) -> Unit): List<Marker> {
|
||||
val navIcon = ContextCompat.getDrawable(context, R.drawable.ic_map_navigation)
|
||||
val markers = positions.map {
|
||||
Marker(this).apply {
|
||||
icon = navIcon
|
||||
rotation = ((it.ground_track ?: 0) * 1e-5).toFloat()
|
||||
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
|
||||
position = GeoPoint((it.latitude_i ?: 0) * 1e-7, (it.longitude_i ?: 0) * 1e-7)
|
||||
setOnMarkerClickListener { _, _ ->
|
||||
onClick()
|
||||
true
|
||||
val markers =
|
||||
positions.map { pos ->
|
||||
Marker(this).apply {
|
||||
icon = navIcon
|
||||
rotation = ((pos.ground_track ?: 0) * 1e-5).toFloat()
|
||||
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
|
||||
position = GeoPoint((pos.latitude_i ?: 0) * 1e-7, (pos.longitude_i ?: 0) * 1e-7)
|
||||
setOnMarkerClickListener { _, _ ->
|
||||
onClick(pos.time)
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
overlays.addAll(markers)
|
||||
|
||||
return markers
|
||||
|
|
|
|||
|
|
@ -26,9 +26,17 @@ import org.meshtastic.proto.Position
|
|||
* Flavor-unified entry point for the embeddable node-track map. Resolves [destNum] to obtain
|
||||
* [NodeMapViewModel.applicationId] and [NodeMapViewModel.mapStyleId], then delegates to the OSMDroid implementation
|
||||
* ([NodeTrackOsmMap]).
|
||||
*
|
||||
* Supports optional synchronized selection via [selectedPositionTime] and [onPositionSelected].
|
||||
*/
|
||||
@Composable
|
||||
fun NodeTrackMap(destNum: Int, positions: List<Position>, modifier: Modifier = Modifier) {
|
||||
fun NodeTrackMap(
|
||||
destNum: Int,
|
||||
positions: List<Position>,
|
||||
modifier: Modifier = Modifier,
|
||||
selectedPositionTime: Int? = null,
|
||||
onPositionSelected: ((Int) -> Unit)? = null,
|
||||
) {
|
||||
val vm = koinViewModel<NodeMapViewModel>()
|
||||
vm.setDestNum(destNum)
|
||||
NodeTrackOsmMap(
|
||||
|
|
@ -36,5 +44,7 @@ fun NodeTrackMap(destNum: Int, positions: List<Position>, modifier: Modifier = M
|
|||
applicationId = vm.applicationId,
|
||||
mapStyleId = vm.mapStyleId,
|
||||
modifier = modifier,
|
||||
selectedPositionTime = selectedPositionTime,
|
||||
onPositionSelected = onPositionSelected,
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,6 +64,8 @@ import kotlin.math.roundToInt
|
|||
* minimal [MapControlsOverlay][org.meshtastic.app.map.component.MapControlsOverlay] with a track time filter slider so
|
||||
* users can adjust the time range directly from the map.
|
||||
*
|
||||
* Supports optional synchronized selection via [selectedPositionTime] and [onPositionSelected].
|
||||
*
|
||||
* Unlike the main [org.meshtastic.app.map.MapView], this composable does **not** include node clusters, waypoints, or
|
||||
* location tracking. It is designed to be embedded inside the position-log adaptive layout.
|
||||
*/
|
||||
|
|
@ -73,6 +75,8 @@ fun NodeTrackOsmMap(
|
|||
applicationId: String,
|
||||
mapStyleId: Int,
|
||||
modifier: Modifier = Modifier,
|
||||
selectedPositionTime: Int? = null,
|
||||
onPositionSelected: ((Int) -> Unit)? = null,
|
||||
mapViewModel: MapViewModel = koinViewModel(),
|
||||
) {
|
||||
val density = LocalDensity.current
|
||||
|
|
@ -109,7 +113,15 @@ fun NodeTrackOsmMap(
|
|||
map.addCopyright()
|
||||
map.addScaleBarOverlay(density)
|
||||
map.addPolyline(density, geoPoints) {}
|
||||
map.addPositionMarkers(filteredPositions) {}
|
||||
map.addPositionMarkers(filteredPositions) { time -> onPositionSelected?.invoke(time) }
|
||||
// Center on selected position
|
||||
if (selectedPositionTime != null) {
|
||||
val selected = filteredPositions.find { it.time == selectedPositionTime }
|
||||
if (selected != null) {
|
||||
val point = GeoPoint((selected.latitude_i ?: 0) * DEG_D, (selected.longitude_i ?: 0) * DEG_D)
|
||||
map.controller.animateTo(point)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue