Further fixes to MapFragment.kt and MapViewModel.kt

This commit is contained in:
Arty Bishop committed 2021-04-27 21:06:37 +01:00
1 parent 48758b4f3d
commit 4cfab17413
8 files changed
+243 -226

No files matched your search

+1
View File
@@ -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,