Skip to content

Commit

Permalink
feat: Add position precision indicator to map (meshtastic#1177)
Browse files Browse the repository at this point in the history
jamesarich authored Aug 3, 2024
1 parent 50eac62 commit acbae6d
Showing 2 changed files with 55 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -9,6 +9,7 @@ import com.google.protobuf.ByteString
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
import kotlinx.coroutines.delay
import kotlin.random.Random

private val defaultLoRaConfig = ConfigKt.loRaConfig {
usePreset = true
@@ -156,6 +157,7 @@ class MockInterface @AssistedInject constructor(
private fun sendConfigResponse(configId: Int) {
debug("Sending mock config response")

@Suppress("MagicNumber")
/// Generate a fake node info entry
fun makeNodeInfo(numIn: Int, lat: Double, lon: Double) =
MeshProtos.FromRadio.newBuilder().apply {
@@ -172,6 +174,7 @@ class MockInterface @AssistedInject constructor(
longitudeI = Position.degI(lon)
altitude = 35
time = (System.currentTimeMillis() / 1000).toInt()
precisionBits = Random.nextInt(10, 19)
}.build()
}.build()
}
56 changes: 52 additions & 4 deletions app/src/main/java/com/geeksville/mesh/ui/map/MapFragment.kt
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
@file:Suppress("MagicNumber")

package com.geeksville.mesh.ui.map

import android.content.Context
@@ -30,6 +32,7 @@ import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.ViewCompositionStrategy
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.core.content.ContextCompat
import androidx.fragment.app.activityViewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewmodel.compose.viewModel
@@ -53,8 +56,8 @@ import com.geeksville.mesh.ui.ScreenFragment
import com.geeksville.mesh.ui.components.IconButton
import com.geeksville.mesh.ui.theme.AppTheme
import com.geeksville.mesh.util.SqlTileWriterExt
import com.geeksville.mesh.util.requiredZoomLevel
import com.geeksville.mesh.util.formatAgo
import com.geeksville.mesh.util.requiredZoomLevel
import com.geeksville.mesh.util.zoomIn
import com.geeksville.mesh.waypoint
import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -103,7 +106,32 @@ class MapFragment : ScreenFragment("Map Fragment"), Logging {
}
}
}
}

private enum class PositionPrecision(val value: Int, val precisionMeters: Double) {
TWO(2, 5976446.981252),
THREE(3, 2988223.4850600003),
FOUR(4, 1494111.7369640006),
FIVE(5, 747055.8629159998),
SIX(6, 373527.9258920002),
SEVEN(7, 186763.95738000044),
EIGHT(8, 93381.97312400135),
NINE(9, 46690.98099600022),
TEN(10, 23345.48493200123),
ELEVEN(11, 11672.736900000944),
TWELVE(12, 5836.362884000802),
THIRTEEN(13, 2918.1758760007315),
FOURTEEN(14, 1459.0823719999053),
FIFTEEN(15, 729.5356200010741),
SIXTEEN(16, 364.7622440000765),
SEVENTEEN(17, 182.37555600115968),
EIGHTEEN(18, 91.1822120001193),
NINETEEN(19, 45.58554000039009),
TWENTY(20, 22.787204001316468),
TWENTY_ONE(21, 11.388036000988677),
TWENTY_TWO(22, 5.688452000824781),
TWENTY_THREE(23, 2.8386600007428338),
TWENTY_FOUR(24, 1.413763999910884),
}

@Composable
@@ -120,7 +148,6 @@ private fun MapView.UpdateMarkers(
fun MapView(
model: UIViewModel = viewModel(),
) {

// UI Elements
var cacheEstimate by remember { mutableStateOf("") }

@@ -131,6 +158,7 @@ fun MapView(
var zoomLevelMin = 0.0
var zoomLevelMax = 0.0


// Map Elements
var downloadRegionBoundingBox: BoundingBox? by remember { mutableStateOf(null) }
var myLocationOverlay: MyLocationNewOverlay? by remember { mutableStateOf(null) }
@@ -144,6 +172,7 @@ fun MapView(
val hasGps = context.hasGps()

val map = rememberMapViewWithLifecycle(context)
val primaryColor = ContextCompat.getColor(context, R.color.colorPrimary)

fun MapView.toggleMyLocation() {
if (context.gpsDisabled()) {
@@ -218,6 +247,23 @@ fun MapView(
position = GeoPoint(p.latitude, p.longitude)
icon = markerIcon

PositionPrecision.entries.find { it.value == p.precisionBits }?.let { precision ->
if (precision in PositionPrecision.TEN..PositionPrecision.NINETEEN) {
if ((precision.precisionMeters) > 0) {
val circle = Polygon.pointsAsCircle(
position,
precision.precisionMeters
)
val polygon = Polygon(this@onNodesChanged)
polygon.points = circle
polygon.fillPaint.color = primaryColor
polygon.fillPaint.alpha = 64
polygon.outlinePaint.color = primaryColor
this@onNodesChanged.overlays.add(polygon)
}
}
}

setOnLongClickListener {
performHapticFeedback()
model.focusUserNode(node)
@@ -263,7 +309,8 @@ fun MapView(
}

fun getUsername(id: String?) = if (id == DataPacket.ID_LOCAL) context.getString(R.string.you)
else model.nodeDB.nodes.value[id]?.user?.longName ?: context.getString(R.string.unknown_username)
else model.nodeDB.nodes.value[id]?.user?.longName
?: context.getString(R.string.unknown_username)

fun MapView.onWaypointChanged(waypoints: Collection<Packet>): List<MarkerWithLabel> {
return waypoints.mapNotNull { waypoint ->
@@ -528,7 +575,8 @@ fun MapView(
append("mainFile.sqlite") // TODO: Accept filename input param from user
}
val writer = SqliteArchiveTileWriter(outputName)
val cacheManager = CacheManager(map, writer) // Make sure cacheManager has latest from map
val cacheManager =
CacheManager(map, writer) // Make sure cacheManager has latest from map
//this triggers the download
downloadRegion(
cacheManager,

0 comments on commit acbae6d

Please sign in to comment.