mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Further refactor of MapFragment.kt and MapViewModel.kt
This commit is contained in:
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)
|
||||
|
||||
@@ -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>
|
||||
Reference in new issue
Block a user