summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-06-30 18:48:43 +0000
committerPeter Stone <thepeterstone@gmail.com>2026-06-30 18:48:43 +0000
commitaf1232773fd525ecd6f6ad434346673139d0d183 (patch)
tree2c07c33d9e3daa59031053cf9af00b4ffadbfabc
parentddfeae7a5f109fc4e993e6697b961ea652360fc2 (diff)
feat(fishing): dashed bearing line on map for fishing target
Adds setupFishingLayer() and updateFishingBearing() to MapHandler with destinationPoint() haversine helper. Draws dashed orange line from boat toward the computed fishing target bearing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
-rw-r--r--android-app/app/src/main/kotlin/org/terst/nav/ui/MapHandler.kt53
1 files changed, 53 insertions, 0 deletions
diff --git a/android-app/app/src/main/kotlin/org/terst/nav/ui/MapHandler.kt b/android-app/app/src/main/kotlin/org/terst/nav/ui/MapHandler.kt
index 99c8ffc..6d408ed 100644
--- a/android-app/app/src/main/kotlin/org/terst/nav/ui/MapHandler.kt
+++ b/android-app/app/src/main/kotlin/org/terst/nav/ui/MapHandler.kt
@@ -96,6 +96,11 @@ class MapHandler(private val maplibreMap: MapLibreMap) {
private var fadSource: GeoJsonSource? = null
+ private val FISHING_LINE_SOURCE_ID = "fishing-line-source"
+ private val FISHING_LINE_LAYER_ID = "fishing-line-layer"
+ private var fishingLineSource: GeoJsonSource? = null
+ private var fishingLineStyle: Style? = null
+
private var anchorPointSource: GeoJsonSource? = null
private var anchorCircleSource: GeoJsonSource? = null
private var tidalCurrentSource: GeoJsonSource? = null
@@ -489,6 +494,54 @@ class MapHandler(private val maplibreMap: MapLibreMap) {
tackSource?.setGeoJson(FeatureCollection.fromFeatures(emptyList()))
}
+ /**
+ * Initialises the fishing bearing line layer.
+ * A dashed orange line is drawn from the boat position toward the fishing target bearing.
+ * Call once after style loads.
+ */
+ fun setupFishingLayer(style: Style) {
+ fishingLineStyle = style
+ val src = GeoJsonSource(FISHING_LINE_SOURCE_ID, FeatureCollection.fromFeatures(emptyArray()))
+ fishingLineSource = src
+ style.addSource(src)
+ style.addLayer(LineLayer(FISHING_LINE_LAYER_ID, FISHING_LINE_SOURCE_ID).apply {
+ setProperties(
+ PropertyFactory.lineColor("#FF6D00"),
+ PropertyFactory.lineWidth(2f),
+ PropertyFactory.lineDasharray(arrayOf(4f, 2f)),
+ PropertyFactory.visibility("none")
+ )
+ })
+ }
+
+ /**
+ * Updates the fishing bearing line from [lat]/[lon] toward [bearingDeg] for [lengthNm] nautical miles.
+ */
+ fun updateFishingBearing(lat: Double, lon: Double, bearingDeg: Double, lengthNm: Double = 10.0) {
+ val end = destinationPoint(lat, lon, bearingDeg, lengthNm)
+ val line = Feature.fromGeometry(
+ LineString.fromLngLats(listOf(
+ Point.fromLngLat(lon, lat),
+ Point.fromLngLat(end.second, end.first)
+ ))
+ )
+ fishingLineSource?.setGeoJson(FeatureCollection.fromFeature(line))
+ fishingLineStyle?.getLayer(FISHING_LINE_LAYER_ID)?.setProperties(
+ PropertyFactory.visibility("visible")
+ )
+ }
+
+ private fun destinationPoint(lat: Double, lon: Double, bearingDeg: Double, distanceNm: Double): Pair<Double, Double> {
+ val R = 3440.065
+ val d = distanceNm / R
+ val bear = Math.toRadians(bearingDeg)
+ val lat1r = Math.toRadians(lat)
+ val lon1r = Math.toRadians(lon)
+ val lat2r = kotlin.math.asin(kotlin.math.sin(lat1r) * kotlin.math.cos(d) + kotlin.math.cos(lat1r) * kotlin.math.sin(d) * kotlin.math.cos(bear))
+ val lon2r = lon1r + kotlin.math.atan2(kotlin.math.sin(bear) * kotlin.math.sin(d) * kotlin.math.cos(lat1r), kotlin.math.cos(d) - kotlin.math.sin(lat1r) * kotlin.math.sin(lat2r))
+ return Pair(Math.toDegrees(lat2r), Math.toDegrees(lon2r))
+ }
+
private fun createCirclePolygon(lat: Double, lon: Double, radiusMeters: Double): Polygon {
val points = mutableListOf<Point>()
val degreesBetweenPoints = 8