diff --git a/app/build.gradle b/app/build.gradle
index 3f57f618..d1e32d2e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -42,6 +42,7 @@ android {
buildFeatures {
viewBinding = true
}
+ kotlinOptions.useIR = true
}
dependencies {
diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/model/SatData.kt b/app/src/main/java/com/rtbishop/look4sat/framework/model/SatData.kt
new file mode 100644
index 00000000..b558504c
--- /dev/null
+++ b/app/src/main/java/com/rtbishop/look4sat/framework/model/SatData.kt
@@ -0,0 +1,15 @@
+package com.rtbishop.look4sat.framework.model
+
+import com.rtbishop.look4sat.domain.predict4kotlin.Position
+import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
+
+data class SatData(
+ val pass: Satellite,
+ val catNum: Int,
+ val name: String,
+ val range: Double,
+ val altitude: Double,
+ val velocity: Double,
+ val qthLoc: String,
+ val osmPos: Position
+)
\ No newline at end of file
diff --git a/app/src/main/java/com/rtbishop/look4sat/framework/model/SelectedSat.kt b/app/src/main/java/com/rtbishop/look4sat/framework/model/SelectedSat.kt
deleted file mode 100644
index df94382b..00000000
--- a/app/src/main/java/com/rtbishop/look4sat/framework/model/SelectedSat.kt
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
- * Look4Sat. Amateur radio satellite tracker and pass predictor.
- * Copyright (C) 2019-2021 Arty Bishop (bishop.arty@gmail.com)
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.rtbishop.look4sat.framework.model
-
-import com.rtbishop.look4sat.domain.predict4kotlin.Position
-import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
-import org.osmdroid.views.overlay.Overlay
-
-data class SelectedSat(
- val pass: Satellite,
- val catNum: Int,
- val name: String,
- val range: Double,
- val altitude: Double,
- val velocity: Double,
- val qthLoc: String,
- val osmPos: Position,
- val footprint: Overlay,
- val groundTrack: Overlay
-)
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapFragment.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapFragment.kt
index 7682fae1..027c5ea9 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapFragment.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapFragment.kt
@@ -20,6 +20,7 @@ package com.rtbishop.look4sat.presentation.mapScreen
import android.graphics.Color
import android.graphics.ColorMatrix
import android.graphics.ColorMatrixColorFilter
+import android.graphics.Paint
import android.os.Bundle
import android.view.View
import androidx.core.content.ContextCompat
@@ -29,7 +30,7 @@ import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.databinding.FragmentMapBinding
import com.rtbishop.look4sat.domain.predict4kotlin.Position
import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
-import com.rtbishop.look4sat.framework.model.SelectedSat
+import com.rtbishop.look4sat.framework.model.SatData
import dagger.hilt.android.AndroidEntryPoint
import org.osmdroid.config.Configuration
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
@@ -43,13 +44,26 @@ import java.util.*
@AndroidEntryPoint
class MapFragment : Fragment(R.layout.fragment_map) {
- private val mapViewModel: MapViewModel by viewModels()
+ private val viewModel: MapViewModel by viewModels()
private val minLat = MapView.getTileSystem().minLatitude
private val maxLat = MapView.getTileSystem().maxLatitude
+ private val trackPaint = Paint().apply {
+ strokeWidth = 2f
+ style = Paint.Style.STROKE
+ color = Color.RED
+ strokeCap = Paint.Cap.ROUND
+ strokeJoin = Paint.Join.ROUND
+ isAntiAlias = true
+ }
+ private val footprintPaint = Paint().apply {
+ style = Paint.Style.FILL_AND_STROKE
+ color = Color.parseColor("#26FFE082")
+ isAntiAlias = true
+ }
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- Configuration.getInstance().load(requireContext(), mapViewModel.getPreferences())
+ Configuration.getInstance().load(requireContext(), viewModel.getPreferences())
val binding = FragmentMapBinding.bind(view).apply {
mapView.apply {
setMultiTouchControls(true)
@@ -67,50 +81,37 @@ class MapFragment : Fragment(R.layout.fragment_map) {
overlays.addAll(Array(4) { FolderOverlay() })
}
}
- binding.fabPrev.setOnClickListener { mapViewModel.scrollSelection(true) }
- binding.fabNext.setOnClickListener { mapViewModel.scrollSelection(false) }
+ binding.fabPrev.setOnClickListener { viewModel.scrollSelection(true) }
+ binding.fabNext.setOnClickListener { viewModel.scrollSelection(false) }
setupObservers(binding)
}
private fun setupObservers(binding: FragmentMapBinding) {
- mapViewModel.stationPosition.observe(viewLifecycleOwner, { setupPosOverlay(it, binding) })
- mapViewModel.getSelectedSat().observe(viewLifecycleOwner, { setSatDetails(it, binding) })
- mapViewModel.getSatMarkers().observe(viewLifecycleOwner, { setMarkers(it, binding) })
+ viewModel.stationPos.observe(viewLifecycleOwner, { renderStationPos(it, binding) })
+ viewModel.allSatPositions.observe(viewLifecycleOwner, { renderSatPositions(it, binding) })
+ viewModel.satTrack.observe(viewLifecycleOwner, { renderSatTrack(it, binding) })
+ viewModel.satFootprint.observe(viewLifecycleOwner, { renderSatFootprint(it, binding) })
+ viewModel.satData.observe(viewLifecycleOwner, { renderSatData(it, binding) })
}
- private fun setupPosOverlay(osmPos: Position, binding: FragmentMapBinding) {
+ private fun renderStationPos(stationPos: Position, binding: FragmentMapBinding) {
binding.apply {
Marker(mapView).apply {
setInfoWindow(null)
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_map_pos)
- position = GeoPoint(osmPos.latitude, osmPos.longitude)
+ position = GeoPoint(stationPos.latitude, stationPos.longitude)
mapView.overlays[0] = this
mapView.invalidate()
}
}
}
- private fun setSatDetails(sat: SelectedSat, binding: FragmentMapBinding) {
- binding.apply {
- idName.text = String.format(getString(R.string.pat_osm_idName), sat.catNum, sat.name)
- qthLocator.text = String.format(getString(R.string.map_qth), sat.qthLoc)
- altitude.text = String.format(getString(R.string.pat_altitude), sat.altitude)
- distance.text = String.format(getString(R.string.pat_distance), sat.range)
- velocity.text = String.format(getString(R.string.pat_osm_vel), sat.velocity)
- mapLat.text = String.format(getString(R.string.pat_osm_lat), sat.osmPos.latitude)
- mapLon.text = String.format(getString(R.string.pat_osm_lon), sat.osmPos.longitude)
- mapView.overlays[1] = sat.groundTrack
- mapView.overlays[2] = sat.footprint
- mapView.invalidate()
- }
- }
-
- private fun setMarkers(map: Map, binding: FragmentMapBinding) {
+ private fun renderSatPositions(posMap: Map, binding: FragmentMapBinding) {
binding.apply {
val markers = FolderOverlay()
- map.entries.forEach {
- if (mapViewModel.shouldUseTextLabels()) {
+ posMap.entries.forEach {
+ if (viewModel.shouldUseTextLabels()) {
Marker(mapView).apply {
setInfoWindow(null)
textLabelFontSize = 24
@@ -121,11 +122,11 @@ class MapFragment : Fragment(R.layout.fragment_map) {
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
try {
position = GeoPoint(it.value.latitude, it.value.longitude)
- } catch (e: IllegalArgumentException) {
- Timber.d("Position: $position")
+ } catch (exception: IllegalArgumentException) {
+ Timber.d(exception)
}
setOnMarkerClickListener { _, _ ->
- mapViewModel.selectSatellite(it.key)
+ viewModel.selectSatellite(it.key)
return@setOnMarkerClickListener true
}
markers.add(this)
@@ -137,11 +138,11 @@ class MapFragment : Fragment(R.layout.fragment_map) {
icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_map_sat)
try {
position = GeoPoint(it.value.latitude, it.value.longitude)
- } catch (e: IllegalArgumentException) {
- Timber.d("Position: $position")
+ } catch (exception: IllegalArgumentException) {
+ Timber.d(exception)
}
setOnMarkerClickListener { _, _ ->
- mapViewModel.selectSatellite(it.key)
+ viewModel.selectSatellite(it.key)
return@setOnMarkerClickListener true
}
markers.add(this)
@@ -153,6 +154,46 @@ class MapFragment : Fragment(R.layout.fragment_map) {
}
}
+ private fun renderSatTrack(satTrack: List>, binding: FragmentMapBinding) {
+ val trackOverlay = FolderOverlay()
+ satTrack.forEach { track ->
+ val trackPoints = track.map { GeoPoint(it.latitude, it.longitude) }
+ Polyline().apply {
+ outlinePaint.set(trackPaint)
+ setPoints(trackPoints)
+ trackOverlay.add(this)
+ }
+ }
+ binding.mapView.overlays[1] = trackOverlay
+ }
+
+ private fun renderSatFootprint(satFootprint: List, binding: FragmentMapBinding) {
+ val footprintPoints = satFootprint.map { GeoPoint(it.latitude, it.longitude) }
+ val footprintOverlay = Polygon().apply {
+ fillPaint.set(footprintPaint)
+ outlinePaint.set(footprintPaint)
+ try {
+ this.points = footprintPoints
+ } catch (exception: IllegalArgumentException) {
+ Timber.d(exception)
+ }
+ }
+ binding.mapView.overlays[2] = footprintOverlay
+ }
+
+ private fun renderSatData(satData: SatData, binding: FragmentMapBinding) {
+ binding.apply {
+ idName.text = String.format(getString(R.string.pat_osm_idName), satData.catNum, satData.name)
+ qthLocator.text = String.format(getString(R.string.map_qth), satData.qthLoc)
+ altitude.text = String.format(getString(R.string.pat_altitude), satData.altitude)
+ distance.text = String.format(getString(R.string.pat_distance), satData.range)
+ velocity.text = String.format(getString(R.string.pat_osm_vel), satData.velocity)
+ mapLat.text = String.format(getString(R.string.pat_osm_lat), satData.osmPos.latitude)
+ mapLon.text = String.format(getString(R.string.pat_osm_lon), satData.osmPos.longitude)
+ }
+ binding.mapView.invalidate()
+ }
+
private fun getColorFilter(): ColorMatrixColorFilter {
val grayScaleMatrix = ColorMatrix().apply { setSaturation(0f) }
val negativeMatrix = ColorMatrix(
diff --git a/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapViewModel.kt b/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapViewModel.kt
index d399c400..5ce41ff3 100644
--- a/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapViewModel.kt
+++ b/app/src/main/java/com/rtbishop/look4sat/presentation/mapScreen/MapViewModel.kt
@@ -18,76 +18,59 @@
package com.rtbishop.look4sat.presentation.mapScreen
import android.content.SharedPreferences
-import android.graphics.Color
-import android.graphics.Paint
import androidx.lifecycle.*
+import com.rtbishop.look4sat.di.DefaultDispatcher
import com.rtbishop.look4sat.domain.predict4kotlin.Position
import com.rtbishop.look4sat.domain.predict4kotlin.QthConverter
-import com.rtbishop.look4sat.domain.predict4kotlin.SatPos
import com.rtbishop.look4sat.domain.predict4kotlin.Satellite
-import com.rtbishop.look4sat.framework.model.SelectedSat
+import com.rtbishop.look4sat.domain.predict4kotlin.StationPosition
+import com.rtbishop.look4sat.framework.model.SatData
import com.rtbishop.look4sat.interactors.GetSelectedSatellites
-import com.rtbishop.look4sat.utility.*
+import com.rtbishop.look4sat.utility.PrefsManager
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.*
-import org.osmdroid.util.GeoPoint
-import org.osmdroid.views.overlay.FolderOverlay
-import org.osmdroid.views.overlay.Overlay
-import org.osmdroid.views.overlay.Polygon
-import org.osmdroid.views.overlay.Polyline
-import timber.log.Timber
import java.util.*
import javax.inject.Inject
+import kotlin.math.max
+import kotlin.math.min
+import kotlin.math.pow
+import kotlin.math.sqrt
@HiltViewModel
class MapViewModel @Inject constructor(
private val getSelectedSatellites: GetSelectedSatellites,
private val prefsManager: PrefsManager,
- private val qthConverter: QthConverter
+ private val qthConverter: QthConverter,
+ @DefaultDispatcher private val defaultDispatcher: CoroutineDispatcher
) : ViewModel() {
- private val dateNow = Date()
- private val trackPaint = Paint().apply {
- strokeWidth = 2f
- style = Paint.Style.STROKE
- color = Color.RED
- strokeCap = Paint.Cap.ROUND
- strokeJoin = Paint.Join.ROUND
- isAntiAlias = true
- }
- private val footprintPaint = Paint().apply {
- style = Paint.Style.FILL_AND_STROKE
- color = Color.parseColor("#26FFE082")
- isAntiAlias = true
- }
-
private val gsp = prefsManager.getStationPosition()
-
- private var filteredSats = listOf()
+ private var dataUpdateJob: Job? = null
+ private var allSatList = listOf()
private lateinit var selectedSat: Satellite
- private val _selectedSat = MutableLiveData()
- fun getSelectedSat(): LiveData = _selectedSat
-
- private val _satMarkers = MutableLiveData