mirror of
https://github.com/atsunatsu/Look4Sat.git
synced 2026-10-02 03:15:37 +00:00
Further fixes to MapFragment.kt and MapViewModel.kt
This commit is contained in:
1 parent
48758b4f3d
commit
4cfab17413
8 files changed
+243
-226
No files matched your search
@@ -42,6 +42,7 @@ android {
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
}
|
||||
kotlinOptions.useIR = true
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
@@ -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
|
||||
)
|
||||
@@ -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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
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
|
||||
)
|
||||
@@ -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<Satellite, Position>, binding: FragmentMapBinding) {
|
||||
private fun renderSatPositions(posMap: Map<Satellite, Position>, 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<List<Position>>, 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<Position>, 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(
|
||||
|
||||
@@ -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<Satellite>()
|
||||
private var dataUpdateJob: Job? = null
|
||||
private var allSatList = listOf<Satellite>()
|
||||
private lateinit var selectedSat: Satellite
|
||||
|
||||
private val _selectedSat = MutableLiveData<SelectedSat>()
|
||||
fun getSelectedSat(): LiveData<SelectedSat> = _selectedSat
|
||||
|
||||
private val _satMarkers = MutableLiveData<Map<Satellite, Position>>()
|
||||
fun getSatMarkers(): LiveData<Map<Satellite, Position>> = _satMarkers
|
||||
|
||||
val stationPosition = liveData {
|
||||
val stationPos = liveData {
|
||||
emit(Position(gsp.latitude.toOsmLat(), gsp.longitude.toOsmLon()))
|
||||
}
|
||||
|
||||
private val _satTrack = MutableLiveData<List<List<Position>>>()
|
||||
val satTrack: LiveData<List<List<Position>>> = _satTrack
|
||||
|
||||
private val _satFootprint = MutableLiveData<List<Position>>()
|
||||
val satFootprint: LiveData<List<Position>> = _satFootprint
|
||||
|
||||
private val _satData = MutableLiveData<SatData>()
|
||||
val satData: LiveData<SatData> = this._satData
|
||||
|
||||
private val _allSatPositions = MutableLiveData<Map<Satellite, Position>>()
|
||||
val allSatPositions: LiveData<Map<Satellite, Position>> = _allSatPositions
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
val satellites = getSelectedSatellites()
|
||||
if (satellites.isNotEmpty()) {
|
||||
filteredSats = satellites
|
||||
selectedSat = satellites.first()
|
||||
while (true) {
|
||||
ensureActive()
|
||||
dateNow.time = System.currentTimeMillis()
|
||||
_selectedSat.value = getDataForSelSatellite(selectedSat)
|
||||
_satMarkers.value = getDataForAllSatellites(filteredSats)
|
||||
delay(2000)
|
||||
getSelectedSatellites().also { selectedSatellites ->
|
||||
if (selectedSatellites.isNotEmpty()) {
|
||||
allSatList = selectedSatellites
|
||||
selectSatellite(selectedSatellites.first())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,98 +85,130 @@ class MapViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
fun scrollSelection(decrement: Boolean) {
|
||||
if (filteredSats.isNotEmpty()) {
|
||||
val index = filteredSats.indexOf(selectedSat)
|
||||
if (allSatList.isNotEmpty()) {
|
||||
val index = allSatList.indexOf(selectedSat)
|
||||
if (decrement) {
|
||||
if (index > 0) selectSatellite(filteredSats[index - 1])
|
||||
else selectSatellite(filteredSats[filteredSats.size - 1])
|
||||
if (index > 0) selectSatellite(allSatList[index - 1])
|
||||
else selectSatellite(allSatList[allSatList.size - 1])
|
||||
} else {
|
||||
if (index < filteredSats.size - 1) selectSatellite(filteredSats[index + 1])
|
||||
else selectSatellite(filteredSats[0])
|
||||
if (index < allSatList.size - 1) selectSatellite(allSatList[index + 1])
|
||||
else selectSatellite(allSatList[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun selectSatellite(satellite: Satellite) {
|
||||
fun selectSatellite(satellite: Satellite, updateFreq: Long = 2000) {
|
||||
selectedSat = satellite
|
||||
viewModelScope.launch { _selectedSat.value = getDataForSelSatellite(selectedSat) }
|
||||
}
|
||||
|
||||
private suspend fun getDataForSelSatellite(satellite: Satellite): SelectedSat =
|
||||
withContext(Dispatchers.Default) {
|
||||
val satPos = satellite.getPredictor(gsp).getSatPos(dateNow)
|
||||
val osmPos = Position(
|
||||
Math.toDegrees(satPos.latitude).toOsmLat(),
|
||||
Math.toDegrees(satPos.longitude).toOsmLon()
|
||||
)
|
||||
val qthLoc = qthConverter.positionToQTH(osmPos.latitude, osmPos.longitude) ?: "-- --"
|
||||
val velocity = satPos.altitude.getOrbitalVelocity()
|
||||
val footprint = getSatFootprint(satPos)
|
||||
val track = getSatTrack(satellite)
|
||||
return@withContext SelectedSat(
|
||||
satellite, satellite.tle.catnum, satellite.tle.name, satPos.range,
|
||||
satPos.altitude, velocity, qthLoc, osmPos, footprint, track
|
||||
)
|
||||
}
|
||||
|
||||
private suspend fun getDataForAllSatellites(satellites: List<Satellite>): Map<Satellite, Position> =
|
||||
withContext(Dispatchers.Default) {
|
||||
val passesMap = mutableMapOf<Satellite, Position>()
|
||||
satellites.forEach { satellite ->
|
||||
val satPos = satellite.getPredictor(gsp).getSatPos(dateNow)
|
||||
passesMap[satellite] = Position(
|
||||
Math.toDegrees(satPos.latitude).toOsmLat(),
|
||||
Math.toDegrees(satPos.longitude).toOsmLon()
|
||||
)
|
||||
}
|
||||
return@withContext passesMap
|
||||
}
|
||||
|
||||
private fun getSatTrack(satellite: Satellite): Overlay {
|
||||
val positions = satellite.getPredictor(gsp).getPositions(dateNow, 15, 0, 3.2)
|
||||
val trackOverlay = FolderOverlay()
|
||||
val trackPoints = mutableListOf<GeoPoint>()
|
||||
var oldLon = 0.0
|
||||
positions.forEach {
|
||||
val osmPos = Position(
|
||||
Math.toDegrees(it.latitude).toOsmLat(),
|
||||
Math.toDegrees(it.longitude).toOsmLon()
|
||||
)
|
||||
if (oldLon < -170.0 && osmPos.longitude > 170.0 || oldLon > 170.0 && osmPos.longitude < -170.0) {
|
||||
val currentPoints = mutableListOf<GeoPoint>()
|
||||
currentPoints.addAll(trackPoints)
|
||||
Polyline().apply {
|
||||
outlinePaint.set(trackPaint)
|
||||
setPoints(currentPoints)
|
||||
trackOverlay.add(this)
|
||||
viewModelScope.launch {
|
||||
dataUpdateJob?.cancelAndJoin()
|
||||
dataUpdateJob = launch {
|
||||
val dateNow = Date()
|
||||
setSelectedSatTrack(selectedSat, gsp, dateNow)
|
||||
while (isActive) {
|
||||
dateNow.time = System.currentTimeMillis()
|
||||
setSatPositions(allSatList, gsp, dateNow)
|
||||
setSelectedSatFootprint(selectedSat, gsp, dateNow)
|
||||
setSelectedSatData(selectedSat, gsp, dateNow)
|
||||
delay(updateFreq)
|
||||
}
|
||||
trackPoints.clear()
|
||||
}
|
||||
oldLon = osmPos.longitude
|
||||
trackPoints.add(GeoPoint(osmPos.latitude, osmPos.longitude))
|
||||
}
|
||||
Polyline().apply {
|
||||
outlinePaint.set(trackPaint)
|
||||
setPoints(trackPoints)
|
||||
trackOverlay.add(this)
|
||||
}
|
||||
return trackOverlay
|
||||
}
|
||||
|
||||
private fun getSatFootprint(satPos: SatPos): Overlay {
|
||||
val points = mutableListOf<GeoPoint>()
|
||||
satPos.getRangeCircle().forEach {
|
||||
val osmPos = Position(it.latitude.toOsmLat(), it.longitude.toOsmLon())
|
||||
points.add(GeoPoint(osmPos.latitude, osmPos.longitude))
|
||||
}
|
||||
return Polygon().apply {
|
||||
fillPaint.set(footprintPaint)
|
||||
outlinePaint.set(footprintPaint)
|
||||
try {
|
||||
this.points = points
|
||||
} catch (e: IllegalArgumentException) {
|
||||
Timber.d("RangeCircle: ${satPos.getRangeCircle()}, RangePoints: $points")
|
||||
private suspend fun setSatPositions(list: List<Satellite>, gsp: StationPosition, date: Date) {
|
||||
withContext(defaultDispatcher) {
|
||||
val satPositions = mutableMapOf<Satellite, Position>()
|
||||
list.forEach { satellite ->
|
||||
val satPos = satellite.getPredictor(gsp).getSatPos(date)
|
||||
val osmLat = Math.toDegrees(satPos.latitude).toOsmLat()
|
||||
val osmLon = Math.toDegrees(satPos.longitude).toOsmLon()
|
||||
satPositions[satellite] = Position(osmLat, osmLon)
|
||||
}
|
||||
_allSatPositions.postValue(satPositions)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun setSelectedSatTrack(sat: Satellite, gsp: StationPosition, date: Date) {
|
||||
withContext(defaultDispatcher) {
|
||||
val satTracks = mutableListOf<List<Position>>()
|
||||
val currentTrack = mutableListOf<Position>()
|
||||
var oldLongitude = 0.0
|
||||
sat.getPredictor(gsp).getPositions(date, 15, 0, 2.4).forEach { pos ->
|
||||
val osmLat = Math.toDegrees(pos.latitude).toOsmLat()
|
||||
val osmLon = Math.toDegrees(pos.longitude).toOsmLon()
|
||||
val currentPosition = Position(osmLat, osmLon)
|
||||
if (oldLongitude < -170.0 && currentPosition.longitude > 170.0) {
|
||||
// adding left terminal position
|
||||
currentTrack.add(Position(osmLat, -180.0))
|
||||
val finishedTrack = mutableListOf<Position>().apply { addAll(currentTrack) }
|
||||
satTracks.add(finishedTrack)
|
||||
currentTrack.clear()
|
||||
} else if (oldLongitude > 170.0 && currentPosition.longitude < -170.0) {
|
||||
// adding right terminal position
|
||||
currentTrack.add(Position(osmLat, 180.0))
|
||||
val finishedTrack = mutableListOf<Position>().apply { addAll(currentTrack) }
|
||||
satTracks.add(finishedTrack)
|
||||
currentTrack.clear()
|
||||
}
|
||||
oldLongitude = currentPosition.longitude
|
||||
currentTrack.add(currentPosition)
|
||||
}
|
||||
satTracks.add(currentTrack)
|
||||
_satTrack.postValue(satTracks)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun setSelectedSatFootprint(sat: Satellite, gsp: StationPosition, date: Date) {
|
||||
withContext(defaultDispatcher) {
|
||||
val satFootprint = sat.getPosition(gsp, date).getRangeCircle().map { rangePos ->
|
||||
Position(rangePos.latitude.toOsmLat(), rangePos.longitude.toOsmLon())
|
||||
}
|
||||
_satFootprint.postValue(satFootprint)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun setSelectedSatData(sat: Satellite, gsp: StationPosition, date: Date) {
|
||||
withContext(defaultDispatcher) {
|
||||
val satPos = sat.getPredictor(gsp).getSatPos(date)
|
||||
val osmLat = Math.toDegrees(satPos.latitude).toOsmLat()
|
||||
val osmLon = Math.toDegrees(satPos.longitude).toOsmLon()
|
||||
val osmPos = Position(osmLat, osmLon)
|
||||
val qthLoc = qthConverter.positionToQTH(osmPos.latitude, osmPos.longitude) ?: "-- --"
|
||||
val velocity = getOrbitalVelocity(satPos.altitude)
|
||||
val satData = SatData(
|
||||
sat, sat.tle.catnum, sat.tle.name, satPos.range,
|
||||
satPos.altitude, velocity, qthLoc, osmPos
|
||||
)
|
||||
this@MapViewModel._satData.postValue(satData)
|
||||
}
|
||||
}
|
||||
|
||||
private fun Double.toOsmLat(): Double {
|
||||
return min(max(this, -85.0), 85.0)
|
||||
}
|
||||
|
||||
private fun getOsmLat(latitude: Double): Double {
|
||||
return min(max(latitude, -85.0), 85.0)
|
||||
}
|
||||
|
||||
private fun Double.toOsmLon(): Double {
|
||||
val newLon = if (this < -180.0) this + 360.0 else if (this > 180.0) this - 360.0 else this
|
||||
return min(max(newLon, -180.0), 180.0)
|
||||
}
|
||||
|
||||
private fun getOsmLon(longitude: Double): Double {
|
||||
val newLongitude = when {
|
||||
longitude < -180.0 -> longitude + 360.0
|
||||
longitude > 180.0 -> longitude - 360.0
|
||||
else -> longitude
|
||||
}
|
||||
return min(max(newLongitude, -180.0), 180.0)
|
||||
}
|
||||
|
||||
private fun getOrbitalVelocity(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) + altitude * 10.0.pow(3)
|
||||
return sqrt(earthG * earthM / radius) / 1000
|
||||
}
|
||||
}
|
||||
@@ -30,7 +30,7 @@ import com.rtbishop.look4sat.framework.model.Result
|
||||
import com.rtbishop.look4sat.databinding.FragmentPassesBinding
|
||||
import com.rtbishop.look4sat.domain.predict4kotlin.SatPass
|
||||
import com.rtbishop.look4sat.utility.RecyclerDivider
|
||||
import com.rtbishop.look4sat.utility.formatForTimer
|
||||
import com.rtbishop.look4sat.utility.toTimerString
|
||||
import com.rtbishop.look4sat.utility.navigateSafe
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.util.*
|
||||
@@ -80,7 +80,7 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
|
||||
}
|
||||
is Result.InProgress -> {
|
||||
binding.apply {
|
||||
passesTimer.text = 0L.formatForTimer()
|
||||
passesTimer.text = 0L.toTimerString()
|
||||
passesError.visibility = View.INVISIBLE
|
||||
passesRecycler.visibility = View.INVISIBLE
|
||||
passesProgress.visibility = View.VISIBLE
|
||||
@@ -88,7 +88,7 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
|
||||
}
|
||||
is Result.Error -> {
|
||||
binding.apply {
|
||||
passesTimer.text = 0L.formatForTimer()
|
||||
passesTimer.text = 0L.toTimerString()
|
||||
passesProgress.visibility = View.INVISIBLE
|
||||
passesRecycler.visibility = View.INVISIBLE
|
||||
passesError.visibility = View.VISIBLE
|
||||
@@ -103,14 +103,14 @@ class PassesFragment : Fragment(R.layout.fragment_passes), PassesAdapter.PassesC
|
||||
try {
|
||||
val nextPass = passes.first { it.aosDate.time.minus(timeNow) > 0 }
|
||||
val millisBeforeStart = nextPass.aosDate.time.minus(timeNow)
|
||||
binding.passesTimer.text = millisBeforeStart.formatForTimer()
|
||||
binding.passesTimer.text = millisBeforeStart.toTimerString()
|
||||
} catch (e: NoSuchElementException) {
|
||||
val lastPass = passes.last()
|
||||
val millisBeforeEnd = lastPass.losDate.time.minus(timeNow)
|
||||
binding.passesTimer.text = millisBeforeEnd.formatForTimer()
|
||||
binding.passesTimer.text = millisBeforeEnd.toTimerString()
|
||||
}
|
||||
} else {
|
||||
binding.passesTimer.text = 0L.formatForTimer()
|
||||
binding.passesTimer.text = 0L.toTimerString()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ import com.rtbishop.look4sat.R
|
||||
import com.rtbishop.look4sat.databinding.FragmentPolarBinding
|
||||
import com.rtbishop.look4sat.domain.predict4kotlin.SatPass
|
||||
import com.rtbishop.look4sat.utility.RecyclerDivider
|
||||
import com.rtbishop.look4sat.utility.formatForTimer
|
||||
import com.rtbishop.look4sat.utility.toTimerString
|
||||
import com.rtbishop.look4sat.utility.navigateSafe
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.util.*
|
||||
@@ -113,17 +113,17 @@ class PolarFragment : Fragment(R.layout.fragment_polar) {
|
||||
if (!satPass.isDeepSpace) {
|
||||
if (dateNow.before(satPass.aosDate)) {
|
||||
val millisBeforeStart = satPass.aosDate.time.minus(timeNow)
|
||||
binding.polarTimer.text = millisBeforeStart.formatForTimer()
|
||||
binding.polarTimer.text = millisBeforeStart.toTimerString()
|
||||
} else {
|
||||
val millisBeforeEnd = satPass.losDate.time.minus(timeNow)
|
||||
binding.polarTimer.text = millisBeforeEnd.formatForTimer()
|
||||
binding.polarTimer.text = millisBeforeEnd.toTimerString()
|
||||
if (dateNow.after(satPass.losDate)) {
|
||||
binding.polarTimer.text = 0L.formatForTimer()
|
||||
binding.polarTimer.text = 0L.toTimerString()
|
||||
findNavController().navigateSafe(R.id.action_polar_to_passes)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
binding.polarTimer.text = 0L.formatForTimer()
|
||||
binding.polarTimer.text = 0L.toTimerString()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,12 +27,8 @@ import androidx.navigation.NavOptions
|
||||
import androidx.navigation.Navigator
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import java.util.concurrent.TimeUnit
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.sqrt
|
||||
|
||||
fun Long.formatForTimer(): String {
|
||||
fun Long.toTimerString(): String {
|
||||
val format = "%02d:%02d:%02d"
|
||||
val hours = TimeUnit.MILLISECONDS.toHours(this)
|
||||
val minutes = TimeUnit.MILLISECONDS.toMinutes(this) % 60
|
||||
@@ -46,22 +42,6 @@ fun Double.round(decimals: Int): Double {
|
||||
return kotlin.math.round(this * multiplier) / multiplier
|
||||
}
|
||||
|
||||
fun Double.toOsmLat(): Double {
|
||||
return min(max(this, -85.0), 85.0)
|
||||
}
|
||||
|
||||
fun Double.toOsmLon(): Double {
|
||||
val newLon = if (this < -180.0) this + 360.0 else if (this > 180.0) this - 360.0 else this
|
||||
return min(max(newLon, -180.0), 180.0)
|
||||
}
|
||||
|
||||
fun Double.getOrbitalVelocity(): 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) + this * 10.0.pow(3)
|
||||
return sqrt(earthG * earthM / radius) / 1000
|
||||
}
|
||||
|
||||
fun NavController.navigateSafe(
|
||||
@IdRes resId: Int,
|
||||
args: Bundle? = null,
|
||||
|
||||
Reference in new issue
Block a user