diff --git a/app/build.gradle b/app/build.gradle
index d9fa7629..f22a408f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -141,6 +141,7 @@ dependencies {
//OSMAND
implementation 'org.osmdroid:osmdroid-android:6.1.14'
+ implementation 'mil.nga:mgrs:2.0.0'
// optional - Kotlin Extensions and Coroutines support for Room
implementation "androidx.room:room-ktx:$room_version"
diff --git a/app/src/main/java/com/geeksville/mesh/NodeInfo.kt b/app/src/main/java/com/geeksville/mesh/NodeInfo.kt
index 6500376e..e533c197 100644
--- a/app/src/main/java/com/geeksville/mesh/NodeInfo.kt
+++ b/app/src/main/java/com/geeksville/mesh/NodeInfo.kt
@@ -120,7 +120,9 @@ data class NodeInfo(
var deviceMetrics: DeviceMetrics? = null
) : Parcelable {
- val batteryPctLevel get() = deviceMetrics?.batteryLevel
+ val batteryLevel get() = deviceMetrics?.batteryLevel
+ val voltage get() = deviceMetrics?.voltage
+ val batteryStr get() = String.format("%d%% %.2fV", batteryLevel, voltage ?: 0)
/**
* true if the device was heard from recently
@@ -150,6 +152,13 @@ data class NodeInfo(
return if (p != null && op != null) p.distance(op).toInt() else null
}
+ /// @return bearing to the other position in degrees
+ fun bearing(o: NodeInfo?): Int? {
+ val p = validPosition
+ val op = o?.validPosition
+ return if (p != null && op != null) p.bearing(op).toInt() else null
+ }
+
/// @return a nice human readable string for the distance, or null for unknown
fun distanceStr(o: NodeInfo?) = distance(o)?.let { dist ->
when {
diff --git a/app/src/main/java/com/geeksville/mesh/model/UIState.kt b/app/src/main/java/com/geeksville/mesh/model/UIState.kt
index 3493df44..741876ea 100644
--- a/app/src/main/java/com/geeksville/mesh/model/UIState.kt
+++ b/app/src/main/java/com/geeksville/mesh/model/UIState.kt
@@ -19,9 +19,9 @@ import com.geeksville.mesh.database.entity.Packet
import com.geeksville.mesh.database.entity.QuickChatAction
import com.geeksville.mesh.repository.datastore.LocalConfigRepository
import com.geeksville.mesh.service.MeshService
+import com.geeksville.mesh.util.GPSFormat
import com.geeksville.mesh.util.positionToMeter
import dagger.hilt.android.lifecycle.HiltViewModel
-import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -215,6 +215,16 @@ class UIViewModel @Inject constructor(
}
}
+ fun gpsString(pos: Position): String {
+ return when (_localConfig.value?.display?.gpsFormat) {
+ ConfigProtos.Config.DisplayConfig.GpsCoordinateFormat.GpsFormatDec -> GPSFormat.dec(pos)
+ ConfigProtos.Config.DisplayConfig.GpsCoordinateFormat.GpsFormatDMS -> GPSFormat.toDMS(pos)
+ ConfigProtos.Config.DisplayConfig.GpsCoordinateFormat.GpsFormatUTM -> GPSFormat.toUTM(pos)
+ ConfigProtos.Config.DisplayConfig.GpsCoordinateFormat.GpsFormatMGRS -> GPSFormat.toMGRS(pos)
+ else -> GPSFormat.dec(pos)
+ }
+ }
+
@Suppress("MemberVisibilityCanBePrivate")
val isRouter: Boolean =
localConfig.value?.device?.role == ConfigProtos.Config.DeviceConfig.Role.Router
diff --git a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt
index fcfc5f9d..95e4855f 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/MapFragment.kt
@@ -70,7 +70,7 @@ class MapFragment : ScreenFragment("Map"), Logging {
map.let {
if (view != null) {
mapController = map.controller
- binding.fabStyleToggle.setOnClickListener {
+ binding.mapStyleButton.setOnClickListener {
chooseMapStyle()
}
model.nodeDB.nodes.value?.let { nodes ->
@@ -121,17 +121,22 @@ class MapFragment : ScreenFragment("Map"), Logging {
val mrkr = nodesWithPosition.map { node ->
val p = node.position!!
debug("Showing on map: $node")
- val f = GeoPoint(p.latitude, p.longitude)
lateinit var marker: MarkerWithLabel
node.user?.let {
val label = it.longName + " " + formatAgo(p.time)
marker = MarkerWithLabel(map, label)
- marker.title = label
- marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
- marker.position = f
+ marker.title = buildString {
+ append("$label ${node.batteryStr}\n${model.gpsString(p)}")
+ model.nodeDB.ourNodeInfo?.let { our ->
+ val dist = our.distanceStr(node)
+ if (dist != null) append(" (${our.bearing(node)}° $dist)")
+ }
+ }
+ marker.setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
+ marker.position = GeoPoint(p.latitude, p.longitude)
marker.icon = ContextCompat.getDrawable(
requireActivity(),
- R.drawable.ic_twotone_person_pin_24
+ R.drawable.ic_twotone_location_on_24
)
}
marker
@@ -238,12 +243,12 @@ class MapFragment : ScreenFragment("Map"), Logging {
val p = mPositionPixels
val textPaint = Paint()
- textPaint.textSize = 50f
+ textPaint.textSize = 40f
textPaint.color = Color.RED
textPaint.isAntiAlias = true
textPaint.textAlign = Paint.Align.CENTER
- c.drawText(mLabel, (p.x - 0).toFloat(), (p.y - 60).toFloat(), textPaint)
+ c.drawText(mLabel, (p.x - 0f), (p.y - 80f), textPaint)
}
}
}
diff --git a/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt b/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt
index c35ba338..8e20da53 100644
--- a/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt
+++ b/app/src/main/java/com/geeksville/mesh/ui/UsersFragment.kt
@@ -114,15 +114,9 @@ class UsersFragment : ScreenFragment("Users"), Logging {
val pos = n.validPosition
if (pos != null) {
- val coords =
- String.format("%.5f %.5f", pos.latitude, pos.longitude).replace(",", ".")
- val html =
- "${coords}"
+ val html = "${model.gpsString(pos)}"
holder.coordsView.text = HtmlCompat.fromHtml(html, HtmlCompat.FROM_HTML_MODE_LEGACY)
holder.coordsView.movementMethod = LinkMovementMethod.getInstance()
holder.coordsView.visibility = View.VISIBLE
@@ -138,7 +132,7 @@ class UsersFragment : ScreenFragment("Users"), Logging {
} else {
holder.distanceView.visibility = View.INVISIBLE
}
- renderBattery(n.batteryPctLevel, n.deviceMetrics?.voltage, holder)
+ renderBattery(n.batteryLevel, n.voltage, holder)
holder.lastTime.text = formatAgo(n.lastHeard)
diff --git a/app/src/main/java/com/geeksville/mesh/util/LocationUtils.kt b/app/src/main/java/com/geeksville/mesh/util/LocationUtils.kt
index bcc5070c..e1f09c7b 100644
--- a/app/src/main/java/com/geeksville/mesh/util/LocationUtils.kt
+++ b/app/src/main/java/com/geeksville/mesh/util/LocationUtils.kt
@@ -1,6 +1,10 @@
package com.geeksville.mesh.util
import com.geeksville.mesh.MeshProtos
+import com.geeksville.mesh.Position
+import mil.nga.grid.features.Point
+import mil.nga.mgrs.MGRS
+import mil.nga.mgrs.utm.UTM
import kotlin.math.abs
import kotlin.math.acos
import kotlin.math.atan2
@@ -15,6 +19,42 @@ import kotlin.math.sin
* text of this license is included in the Gaggle source, see assets/manual/gpl-2.0.txt.
******************************************************************************/
+object GPSFormat {
+ fun dec(p: Position): String {
+ return String.format("%.5f %.5f", p.latitude, p.longitude).replace(",", ".")
+ }
+
+ fun toDMS(p: Position): String {
+ val lat = degreesToDMS(p.latitude, true)
+ val lon = degreesToDMS(p.longitude, false)
+ fun string(a: Array) = String.format("%s°%s'%.5s\"%s", a[0], a[1], a[2], a[3])
+ return string(lat) + " " + string(lon)
+ }
+
+ fun toUTM(p: Position): String {
+ val UTM = UTM.from(Point.point(p.longitude, p.latitude))
+ return String.format(
+ "%s%s %.6s %.7s",
+ UTM.zone,
+ UTM.toMGRS().band,
+ UTM.easting,
+ UTM.northing
+ )
+ }
+
+ fun toMGRS(p: Position): String {
+ val MGRS = MGRS.from(Point.point(p.longitude, p.latitude))
+ return String.format(
+ "%s%s %s%s %05d %05d",
+ MGRS.zone,
+ MGRS.band,
+ MGRS.column,
+ MGRS.row,
+ MGRS.easting,
+ MGRS.northing
+ )
+ }
+}
/**
* Format as degrees, minutes, secs
diff --git a/app/src/main/res/drawable/baseline_layers_white_24dp.xml b/app/src/main/res/drawable/baseline_layers_white_24dp.xml
deleted file mode 100644
index 02fd4892..00000000
--- a/app/src/main/res/drawable/baseline_layers_white_24dp.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
diff --git a/app/src/main/res/drawable/ic_twotone_layers_24.xml b/app/src/main/res/drawable/ic_twotone_layers_24.xml
new file mode 100644
index 00000000..489b8775
--- /dev/null
+++ b/app/src/main/res/drawable/ic_twotone_layers_24.xml
@@ -0,0 +1,8 @@
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_twotone_location_on_24.xml b/app/src/main/res/drawable/ic_twotone_location_on_24.xml
new file mode 100644
index 00000000..0cdba5ab
--- /dev/null
+++ b/app/src/main/res/drawable/ic_twotone_location_on_24.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_twotone_person_pin_24.xml b/app/src/main/res/drawable/ic_twotone_person_pin_24.xml
deleted file mode 100644
index 407fb4f9..00000000
--- a/app/src/main/res/drawable/ic_twotone_person_pin_24.xml
+++ /dev/null
@@ -1,15 +0,0 @@
-
-
-
-
diff --git a/app/src/main/res/layout/map_view.xml b/app/src/main/res/layout/map_view.xml
index 7ed1b0c4..970f2aea 100644
--- a/app/src/main/res/layout/map_view.xml
+++ b/app/src/main/res/layout/map_view.xml
@@ -10,6 +10,20 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
+
+
\ No newline at end of file