Further refactor of MapFragment.kt and MapViewModel.kt

This commit is contained in:
Arty Bishop committed 2020-11-28 13:56:03 +00:00
1 parent 62f8a4a534
commit 4556d2d975
4 files changed
+152 -138

No files matched your search

@@ -0,0 +1,18 @@
package com.rtbishop.look4sat.data
import com.github.amsacode.predict4java.Position
import org.osmdroid.views.overlay.Overlay
data class SelectedSat(
val pass: SatPass,
val catNum: Int,
val name: String,
val range: Double,
val altitude: Double,
val velocity: Double,
val qthLoc: String,
val osmPos: Position,
val coverage: Double,
val footprint: Overlay,
val groundTrack: Overlay
)
@@ -4,7 +4,6 @@ import android.content.SharedPreferences
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
@@ -17,8 +16,8 @@ import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.SharedViewModel
import com.rtbishop.look4sat.data.Result
import com.rtbishop.look4sat.data.SatPass
import com.rtbishop.look4sat.data.SelectedSat
import com.rtbishop.look4sat.databinding.FragmentMapBinding
import com.rtbishop.look4sat.utility.Utilities
import dagger.hilt.android.AndroidEntryPoint
import org.osmdroid.config.Configuration
import org.osmdroid.tileprovider.tilesource.ITileSource
@@ -36,8 +35,7 @@ class MapFragment : Fragment(R.layout.fragment_map) {
@Inject
lateinit var preferences: SharedPreferences
private lateinit var trackPaint: Paint
private lateinit var footprintPaint: Paint
private lateinit var binding: FragmentMapBinding
private lateinit var nameFormat: String
private lateinit var qthFormat: String
private lateinit var altFormat: String
@@ -45,46 +43,18 @@ class MapFragment : Fragment(R.layout.fragment_map) {
private lateinit var velFormat: String
private lateinit var latLonFormat: String
private lateinit var coverageFormat: String
private lateinit var selectedSat: SatPass
private val dateNow = Date()
private val viewModel: MapViewModel by viewModels()
private val sharedViewModel: SharedViewModel by activityViewModels()
private var binding: FragmentMapBinding? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupComponents(view)
setupMapView()
setupObservers()
}
private fun setupComponents(view: View) {
Configuration.getInstance().load(requireContext().applicationContext, preferences)
binding = FragmentMapBinding.bind(view)
trackPaint = Paint().apply {
strokeWidth = 2f
style = Paint.Style.STROKE
color = Color.RED
strokeCap = Paint.Cap.ROUND
strokeJoin = Paint.Join.ROUND
isAntiAlias = true
}
footprintPaint = Paint().apply {
style = Paint.Style.FILL_AND_STROKE
color = ContextCompat.getColor(requireContext(), R.color.satFootprint)
isAntiAlias = true
}
nameFormat = getString(R.string.pat_osm_idName)
qthFormat = getString(R.string.map_qth)
altFormat = getString(R.string.pat_altitude)
distFormat = getString(R.string.pat_distance)
velFormat = getString(R.string.pat_osm_vel)
latLonFormat = getString(R.string.pat_osm_latLon)
coverageFormat = getString(R.string.map_coverage)
}
private fun setupMapView() {
binding?.apply {
binding = FragmentMapBinding.bind(view).apply {
mapView.apply {
setMultiTouchControls(true)
setTileSource(getTileSource())
@@ -100,6 +70,13 @@ class MapFragment : Fragment(R.layout.fragment_map) {
overlays.addAll(Array(4) { FolderOverlay() })
}
}
nameFormat = getString(R.string.pat_osm_idName)
qthFormat = getString(R.string.map_qth)
altFormat = getString(R.string.pat_altitude)
distFormat = getString(R.string.pat_distance)
velFormat = getString(R.string.pat_osm_vel)
latLonFormat = getString(R.string.pat_osm_latLon)
coverageFormat = getString(R.string.map_coverage)
}
private fun setupObservers() {
@@ -107,23 +84,16 @@ class MapFragment : Fragment(R.layout.fragment_map) {
sharedViewModel.getPasses().observe(viewLifecycleOwner, {
if (it is Result.Success && it.data.isNotEmpty()) {
viewModel.setPasses(it.data)
binding?.fabPrev?.setOnClickListener { viewModel.changeSelection(true) }
binding?.fabNext?.setOnClickListener { viewModel.changeSelection(false) }
binding.fabPrev.setOnClickListener { viewModel.changeSelection(true) }
binding.fabNext.setOnClickListener { viewModel.changeSelection(false) }
}
})
viewModel.getSelectedPass().observe(viewLifecycleOwner, { satPass ->
selectedSat = satPass
setSelectedSatDetails(satPass)
scrollToSat(satPass)
})
viewModel.getSatMarkers().observe(viewLifecycleOwner, {
setMarkers(it)
setSelectedSatDetails(selectedSat)
})
viewModel.getSelectedSat().observe(viewLifecycleOwner, { setSelectedSatDetails(it) })
viewModel.getSatMarkers().observe(viewLifecycleOwner, { setMarkers(it) })
}
private fun setupPosOverlay(gsp: GroundStationPosition) {
binding?.apply {
binding.apply {
Marker(mapView).apply {
setInfoWindow(null)
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM)
@@ -135,33 +105,23 @@ class MapFragment : Fragment(R.layout.fragment_map) {
}
}
private fun setSelectedSatDetails(satPass: SatPass) {
val satPos = satPass.predictor.getSatPos(dateNow)
val osmPos = viewModel.getOsmPosition(satPos.latitude, satPos.longitude, true)
val qthLoc = Utilities.locToQTH(osmPos.lat, osmPos.lon)
binding?.apply {
idName.text = String.format(nameFormat, satPass.tle.catnum, satPass.tle.name)
qthLocator.text = String.format(qthFormat, qthLoc)
altitude.text = String.format(altFormat, satPos.altitude)
distance.text = String.format(distFormat, satPos.range)
velocity.text = String.format(velFormat, viewModel.getSatVelocity(satPos.altitude))
latLon.text = String.format(latLonFormat, osmPos.lat, osmPos.lon)
mapCoverage.text = String.format(coverageFormat, satPos.rangeCircleRadiusKm * 2)
mapView.overlays[1] = getSatTrack(satPass)
mapView.overlays[2] = getSatFootprint(satPass)
private fun setSelectedSatDetails(sat: SelectedSat) {
binding.apply {
idName.text = String.format(nameFormat, sat.catNum, sat.name)
qthLocator.text = String.format(qthFormat, sat.qthLoc)
altitude.text = String.format(altFormat, sat.altitude)
distance.text = String.format(distFormat, sat.range)
velocity.text = String.format(velFormat, sat.velocity)
latLon.text = String.format(latLonFormat, sat.osmPos.lat, sat.osmPos.lon)
mapCoverage.text = String.format(coverageFormat, sat.coverage)
mapView.overlays[1] = sat.groundTrack
mapView.overlays[2] = sat.footprint
mapView.invalidate()
}
}
private fun scrollToSat(satPass: SatPass) {
val satPos = satPass.predictor.getSatPos(dateNow)
val osmPos = viewModel.getOsmPosition(satPos.latitude, satPos.longitude, true)
binding?.mapView?.controller?.animateTo(GeoPoint(osmPos.lat, osmPos.lon))
}
private fun setMarkers(map: Map<SatPass, Position>) {
dateNow.time = System.currentTimeMillis()
binding?.apply {
binding.apply {
val markers = FolderOverlay()
map.entries.forEach {
Marker(mapView).apply {
@@ -169,7 +129,10 @@ class MapFragment : Fragment(R.layout.fragment_map) {
setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER)
icon = ContextCompat.getDrawable(requireContext(), R.drawable.ic_map_sat)
position = GeoPoint(it.value.lat, it.value.lon)
setOnMarkerClickListener { _, _ -> viewModel.setSelectedSat(it.key) }
setOnMarkerClickListener { _, _ ->
viewModel.setSelectedSat(it.key)
return@setOnMarkerClickListener true
}
markers.add(this)
}
}
@@ -178,48 +141,6 @@ class MapFragment : Fragment(R.layout.fragment_map) {
}
}
private fun getSatTrack(pass: SatPass): Overlay {
val period = (24 * 60 / pass.tle.meanmo).toInt()
val positions = pass.predictor.getPositions(dateNow, 20, 0, period * 3)
val trackOverlay = FolderOverlay()
val trackPoints = mutableListOf<GeoPoint>()
var oldLon = 0.0
positions.forEach {
val osmPos = viewModel.getOsmPosition(it.latitude, it.longitude, true)
if (oldLon < -170.0 && osmPos.lon > 170.0 || oldLon > 170.0 && osmPos.lon < -170.0) {
val currentPoints = mutableListOf<GeoPoint>()
currentPoints.addAll(trackPoints)
Polyline().apply {
outlinePaint.set(trackPaint)
setPoints(currentPoints)
trackOverlay.add(this)
}
trackPoints.clear()
}
oldLon = osmPos.lon
trackPoints.add(GeoPoint(osmPos.lat, osmPos.lon))
}
Polyline().apply {
outlinePaint.set(trackPaint)
setPoints(trackPoints)
trackOverlay.add(this)
}
return trackOverlay
}
private fun getSatFootprint(pass: SatPass): Overlay {
val rangePoints = mutableListOf<GeoPoint>()
pass.predictor.getSatPos(dateNow).rangeCircle.withIndex().forEach {
val osmPos = viewModel.getOsmPosition(it.value.lat, it.value.lon, false)
rangePoints.add(GeoPoint(osmPos.lat, osmPos.lon))
}
return Polygon().apply {
fillPaint.set(footprintPaint)
outlinePaint.set(footprintPaint)
points = rangePoints
}
}
private fun getColorFilter(): ColorMatrixColorFilter {
val targetColor = Color.RED
val newR = Color.red(targetColor) / 255f
@@ -254,9 +175,4 @@ class MapFragment : Fragment(R.layout.fragment_map) {
)
return XYTileSource("wikimedia", 2, 6, 256, ".png", sources, copyright, policy)
}
override fun onDestroyView() {
binding = null
super.onDestroyView()
}
}
@@ -1,5 +1,7 @@
package com.rtbishop.look4sat.ui.mainScreen
import android.graphics.Color
import android.graphics.Paint
import androidx.hilt.lifecycle.ViewModelInject
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
@@ -8,10 +10,17 @@ import androidx.lifecycle.viewModelScope
import com.github.amsacode.predict4java.GroundStationPosition
import com.github.amsacode.predict4java.Position
import com.rtbishop.look4sat.data.SatPass
import com.rtbishop.look4sat.data.SelectedSat
import com.rtbishop.look4sat.utility.PrefsManager
import com.rtbishop.look4sat.utility.Utilities
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
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 java.util.*
import kotlin.math.pow
import kotlin.math.sqrt
@@ -19,60 +28,133 @@ import kotlin.math.sqrt
class MapViewModel @ViewModelInject constructor(prefsManager: PrefsManager) : 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 var filteredPasses = listOf<SatPass>()
private val _gsp = MutableLiveData(prefsManager.getStationPosition())
fun getGSP(): LiveData<GroundStationPosition> = _gsp
private val _selectedPass = MutableLiveData<SatPass>()
fun getSelectedPass(): LiveData<SatPass> = _selectedPass
private val _selectedSat = MutableLiveData<SelectedSat>()
fun getSelectedSat(): LiveData<SelectedSat> = _selectedSat
private val _satMarkers = MutableLiveData<Map<SatPass, Position>>()
fun getSatMarkers(): LiveData<Map<SatPass, Position>> = _satMarkers
fun setPasses(passesList: List<SatPass>) {
filteredPasses = passesList.distinctBy { it.tle }
_selectedPass.value = filteredPasses[0]
calculateSatPos(filteredPasses)
setSelectedSat(filteredPasses[0])
calculatePositions(filteredPasses)
}
private fun calculateSatPos(passes: List<SatPass>) {
viewModelScope.launch(Dispatchers.Default) {
fun changeSelection(decrement: Boolean) {
val index = filteredPasses.indexOf(_selectedSat.value?.pass)
if (decrement) {
if (index > 0) setSelectedSat(filteredPasses[index - 1])
else setSelectedSat(filteredPasses[filteredPasses.size - 1])
} else {
if (index < filteredPasses.size - 1) setSelectedSat(filteredPasses[index + 1])
else setSelectedSat(filteredPasses[0])
}
}
fun setSelectedSat(pass: SatPass?) {
pass?.let {
viewModelScope.launch(Dispatchers.Default) {
val satPos = pass.predictor.getSatPos(dateNow)
val osmPos = getOsmPosition(satPos.latitude, satPos.longitude, true)
val qthLoc = Utilities.locToQTH(osmPos.lat, osmPos.lon)
val velocity = getSatVelocity(satPos.altitude)
val coverage = satPos.rangeCircleRadiusKm * 2
val footprint = getSatFootprint(pass)
val track = getSatTrack(pass)
_selectedSat.postValue(
SelectedSat(
pass, pass.tle.catnum, pass.tle.name, satPos.range,
satPos.altitude, velocity, qthLoc, osmPos, coverage, footprint, track
)
)
}
}
}
private fun calculatePositions(passes: List<SatPass>) {
val passesMap = mutableMapOf<SatPass, Position>()
viewModelScope.launch {
while (true) {
val passesMap = mutableMapOf<SatPass, Position>()
dateNow.time = System.currentTimeMillis()
passes.forEach {
val satPos = it.predictor.getSatPos(dateNow)
passesMap[it] = getOsmPosition(satPos.latitude, satPos.longitude, true)
}
setSelectedSat(_selectedSat.value?.pass)
_satMarkers.postValue(passesMap)
delay(2000)
}
}
}
fun setSelectedSat(satPass: SatPass): Boolean {
_selectedPass.value = satPass
return true
}
fun changeSelection(decrement: Boolean) {
val index = filteredPasses.indexOf(_selectedPass.value)
_selectedPass.value = if (decrement) {
if (index > 0) filteredPasses[index - 1] else filteredPasses[filteredPasses.size - 1]
} else {
if (index < filteredPasses.size - 1) filteredPasses[index + 1] else filteredPasses[0]
private fun getSatFootprint(pass: SatPass): Overlay {
val rangePoints = mutableListOf<GeoPoint>()
pass.predictor.getSatPos(dateNow).rangeCircle.withIndex().forEach {
val osmPos = getOsmPosition(it.value.lat, it.value.lon, false)
rangePoints.add(GeoPoint(osmPos.lat, osmPos.lon))
}
return Polygon().apply {
fillPaint.set(footprintPaint)
outlinePaint.set(footprintPaint)
points = rangePoints
}
}
fun getSatVelocity(satAlt: Double): Double {
private fun getSatTrack(pass: SatPass): Overlay {
val period = (24 * 60 / pass.tle.meanmo).toInt()
val positions = pass.predictor.getPositions(dateNow, 20, 0, period * 3)
val trackOverlay = FolderOverlay()
val trackPoints = mutableListOf<GeoPoint>()
var oldLon = 0.0
positions.forEach {
val osmPos = getOsmPosition(it.latitude, it.longitude, true)
if (oldLon < -170.0 && osmPos.lon > 170.0 || oldLon > 170.0 && osmPos.lon < -170.0) {
val currentPoints = mutableListOf<GeoPoint>()
currentPoints.addAll(trackPoints)
Polyline().apply {
outlinePaint.set(trackPaint)
setPoints(currentPoints)
trackOverlay.add(this)
}
trackPoints.clear()
}
oldLon = osmPos.lon
trackPoints.add(GeoPoint(osmPos.lat, osmPos.lon))
}
Polyline().apply {
outlinePaint.set(trackPaint)
setPoints(trackPoints)
trackOverlay.add(this)
}
return trackOverlay
}
private fun getSatVelocity(altitude: Double): Double {
val earthG = 6.674 * 10.0.pow(-11)
val earthM = 5.98 * 10.0.pow(24)
val radius = 6.37 * 10.0.pow(6) + satAlt * 10.0.pow(3)
val radius = 6.37 * 10.0.pow(6) + altitude * 10.0.pow(3)
return sqrt(earthG * earthM / radius) / 1000
}
fun getOsmPosition(lat: Double, lon: Double, inRadians: Boolean): Position {
private fun getOsmPosition(lat: Double, lon: Double, inRadians: Boolean): Position {
return if (inRadians) {
var osmLat = Math.toDegrees(lat)
var osmLon = Math.toDegrees(lon)
-2
View File
@@ -25,7 +25,5 @@
<color name="greyLight">#D0D0D0</color>
<color name="greyDivider">#808080</color>
<color name="greySurface">#1e1e1e</color>
<!-- map colors -->
<color name="satFootprint">#26FFE082</color>
<color name="mapInfoLayout">#66000000</color>
</resources>