mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-03 13:43:20 -04:00
Merge pull request #473 from meshtastic/osm-features
add osm map features
This commit is contained in:
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 =
|
||||
"<a href='geo:${pos.latitude},${pos.longitude}?z=17&label=${
|
||||
URLEncoder.encode(
|
||||
name,
|
||||
"utf-8"
|
||||
)
|
||||
}'>${coords}</a>"
|
||||
val html = "<a href='geo:${pos.latitude},${pos.longitude}?z=17&label=${
|
||||
URLEncoder.encode(name, "utf-8")
|
||||
}'>${model.gpsString(pos)}</a>"
|
||||
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)
|
||||
|
||||
|
||||
@@ -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>) = 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
|
||||
|
||||
Reference in New Issue
Block a user