Extracted MapView class to a separate file, code refactoring

This commit is contained in:
Arty Bishop committed 2020-05-09 15:44:35 +01:00
1 parent 67f98101aa
commit 32df8c4c04
2 files changed
+192 -186

No files matched your search

@@ -19,19 +19,14 @@
package com.rtbishop.look4sat.ui.fragments
import android.content.Context
import android.graphics.*
import android.os.Bundle
import android.view.View
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import com.github.amsacode.predict4java.GroundStationPosition
import com.github.amsacode.predict4java.Position
import com.github.amsacode.predict4java.SatPos
import com.github.amsacode.predict4java.TLE
import com.rtbishop.look4sat.Look4SatApp
import com.rtbishop.look4sat.R
@@ -41,13 +36,12 @@ import com.rtbishop.look4sat.data.SatPass
import com.rtbishop.look4sat.databinding.FragmentMapViewBinding
import com.rtbishop.look4sat.ui.MainActivity
import com.rtbishop.look4sat.ui.SharedViewModel
import com.rtbishop.look4sat.ui.views.MapView
import com.rtbishop.look4sat.utility.GeneralUtils
import com.rtbishop.look4sat.utility.PassPredictor
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.util.*
import javax.inject.Inject
import kotlin.math.abs
class MapViewFragment : Fragment(R.layout.fragment_map_view) {
@@ -58,13 +52,14 @@ class MapViewFragment : Fragment(R.layout.fragment_map_view) {
private lateinit var mapView: MapView
private lateinit var predictor: PassPredictor
private lateinit var selectedSat: TLE
private lateinit var binding: FragmentMapViewBinding
private var gsp = GroundStationPosition(0.0, 0.0, 0.0)
private var satPassList = emptyList<SatPass>()
private var checkedItem = 0
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val binding = FragmentMapViewBinding.bind(view)
binding = FragmentMapViewBinding.bind(view)
mainActivity = activity as MainActivity
(mainActivity.application as Look4SatApp).appComponent.inject(this)
viewModel = ViewModelProvider(mainActivity, modelFactory).get(SharedViewModel::class.java)
@@ -97,7 +92,10 @@ class MapViewFragment : Fragment(R.layout.fragment_map_view) {
binding.fabMap.setOnClickListener { showSelectSatDialog(satPassList) }
selectedSat = satPassList.first().tle
predictor = satPassList.first().predictor
mapView = MapView(mainActivity, binding)
mapView = MapView(mainActivity)
mapView.setList(satPassList)
mapView.setGSP(gsp)
mapView.setChecked(checkedItem)
binding.frameMap.addView(mapView)
refreshView()
} else {
@@ -115,6 +113,7 @@ class MapViewFragment : Fragment(R.layout.fragment_map_view) {
lifecycleScope.launch {
while (true) {
mapView.invalidate()
setTextViewsToSelectedSatPos()
delay(viewModel.getRefreshRate())
}
}
@@ -133,6 +132,9 @@ class MapViewFragment : Fragment(R.layout.fragment_map_view) {
checkedItem = which
selectedSat = list[which].tle
predictor = list[which].predictor
mapView.setList(satPassList)
mapView.setGSP(gsp)
mapView.setChecked(checkedItem)
mapView.invalidate()
dialog.dismiss()
}
@@ -140,184 +142,18 @@ class MapViewFragment : Fragment(R.layout.fragment_map_view) {
.show()
}
inner class MapView(context: Context, private val binding: FragmentMapViewBinding) :
View(context) {
private val scale = resources.displayMetrics.density
private val groundTrackPaint = Paint().apply {
isAntiAlias = true
color = ContextCompat.getColor(mainActivity, R.color.satTrack)
style = Paint.Style.STROKE
strokeWidth = scale
}
private val satPaintPrim = Paint().apply {
isAntiAlias = true
color = ContextCompat.getColor(mainActivity, R.color.satPrimary)
style = Paint.Style.STROKE
strokeWidth = scale
}
private val satPaintSec = Paint().apply {
isAntiAlias = true
color = ContextCompat.getColor(mainActivity, R.color.satSecondary)
style = Paint.Style.STROKE
strokeWidth = scale
}
private val txtPaint = Paint().apply {
isAntiAlias = true
color = ContextCompat.getColor(mainActivity, R.color.satPrimary)
style = Paint.Style.FILL
strokeWidth = scale
textSize = 16f
setShadowLayer(2f, 2f, 2f, Color.BLACK)
}
private val outlinePaint = Paint().apply {
isAntiAlias = true
color = Color.BLACK
style = Paint.Style.FILL_AND_STROKE
strokeWidth = scale
textSize = 16f
}
private val rect = Rect()
private fun setTextViewsToSelectedSatPos() {
val currentTime = GeneralUtils.getDateFor(System.currentTimeMillis())
val orbitalPeriod = (24 * 60 / selectedSat.meanmo).toInt()
val positions = predictor.getPositions(currentTime, 60, 0, orbitalPeriod * 3)
var lon = GeneralUtils.rad2Deg(positions[0].longitude).toFloat()
val lat = GeneralUtils.rad2Deg(positions[0].latitude).toFloat()
val rng = positions[0].range
override fun onDraw(canvas: Canvas) {
canvas.translate(width / 2f, height / 2f)
val degLon = width / 360f
val degLat = height / 180f
drawHomeLoc(canvas, degLon, degLat)
val currentTime = GeneralUtils.getDateFor(System.currentTimeMillis())
val orbitalPeriod = (24 * 60 / selectedSat.meanmo).toInt()
val positions = predictor.getPositions(currentTime, 60, 0, orbitalPeriod * 3)
setTextViewsToSelectedSatPos(positions[0])
drawGroundTrack(canvas, degLon, degLat, positions)
satPassList.forEach {
drawSat(canvas, degLon, degLat, it.tle, it.predictor, currentTime, satPaintSec)
}
drawSat(
canvas,
degLon,
degLat,
satPassList[checkedItem].tle,
satPassList[checkedItem].predictor,
currentTime,
satPaintPrim
)
}
if (lon > 180f) lon -= 360f
private fun drawHomeLoc(canvas: Canvas, degLon: Float, degLat: Float) {
val lon = gsp.longitude.toFloat()
val lat = gsp.latitude.toFloat() * -1
val cx = lon * degLon
val cy = lat * degLat
val gspName = context.getString(R.string.map_gsp)
txtPaint.getTextBounds(gspName, 0, gspName.length, rect)
canvas.drawCircle(cx, cy, scale * 2, txtPaint)
canvas.drawText(gspName, cx - rect.width() / 2, cy - txtPaint.textSize, outlinePaint)
canvas.drawText(gspName, cx - rect.width() / 2, cy - txtPaint.textSize, txtPaint)
}
private fun setTextViewsToSelectedSatPos(position: SatPos) {
var lon = GeneralUtils.rad2Deg(position.longitude).toFloat()
val lat = GeneralUtils.rad2Deg(position.latitude).toFloat()
val rng = position.range
if (lon > 180f) lon -= 360f
binding.tvMapLat.text = String.format(context.getString(R.string.pat_latitude), lat)
binding.tvMapLon.text = String.format(context.getString(R.string.pat_longitude), lon)
binding.tvMapRng.text = String.format(context.getString(R.string.pat_range), rng)
}
private fun drawGroundTrack(
canvas: Canvas,
degLon: Float,
degLat: Float,
list: List<SatPos>
) {
val path = Path()
var lon: Float
var lat: Float
var lastLon = 181f
list.withIndex().forEach { (index, satPos) ->
lon = GeneralUtils.rad2Deg(satPos.longitude).toFloat()
lat = GeneralUtils.rad2Deg(satPos.latitude).toFloat() * -1
if (lon > 180f) lon -= 360f
lon *= degLon
lat *= degLat
if (index == 0 || abs(lon - lastLon) > 180) path.moveTo(lon, lat)
else path.lineTo(lon, lat)
lastLon = lon
}
canvas.drawPath(path, groundTrackPaint)
}
private fun drawSat(
canvas: Canvas,
degLon: Float,
degLat: Float,
tle: TLE,
predictor: PassPredictor,
date: Date,
paint: Paint
) {
val satPosNow = predictor.getSatPos(date)
val footprintPosList = satPosNow.rangeCircle
drawFootprint(canvas, degLon, degLat, footprintPosList, paint)
drawName(canvas, degLon, degLat, satPosNow, tle.name)
}
private fun drawFootprint(
canvas: Canvas,
degLon: Float,
degLat: Float,
list: List<Position>,
paint: Paint
) {
val path = Path()
var lon: Float
var lat: Float
var lastLon = 181f
list.withIndex().forEach { (index, position) ->
lon = position.lon.toFloat()
lat = position.lat.toFloat() * -1
if (lon > 180f) lon -= 360f
lon *= degLon
lat *= degLat
if (index == 0 || abs(lon - lastLon) > 180) path.moveTo(lon, lat)
else path.lineTo(lon, lat)
lastLon = lon
}
canvas.drawPath(path, paint)
}
private fun drawName(
canvas: Canvas,
degLon: Float,
degLat: Float,
position: SatPos,
name: String
) {
var lon = GeneralUtils.rad2Deg(position.longitude).toFloat()
val lat = GeneralUtils.rad2Deg(position.latitude).toFloat() * -1
if (lon > 180f) lon -= 360f
val cx = lon * degLon
val cy = lat * degLat
canvas.drawCircle(cx, cy, scale * 2, txtPaint)
txtPaint.getTextBounds(name, 0, name.length, rect)
canvas.drawText(name, cx - rect.width() / 2, cy - txtPaint.textSize, outlinePaint)
canvas.drawText(name, cx - rect.width() / 2, cy - txtPaint.textSize, txtPaint)
}
binding.tvMapLat.text = String.format(mainActivity.getString(R.string.pat_latitude), lat)
binding.tvMapLon.text = String.format(mainActivity.getString(R.string.pat_longitude), lon)
binding.tvMapRng.text = String.format(mainActivity.getString(R.string.pat_range), rng)
}
}
@@ -0,0 +1,170 @@
package com.rtbishop.look4sat.ui.views
import android.content.Context
import android.graphics.*
import android.view.View
import androidx.core.content.ContextCompat
import com.github.amsacode.predict4java.GroundStationPosition
import com.github.amsacode.predict4java.Position
import com.github.amsacode.predict4java.SatPos
import com.github.amsacode.predict4java.TLE
import com.rtbishop.look4sat.R
import com.rtbishop.look4sat.data.SatPass
import com.rtbishop.look4sat.utility.GeneralUtils
import java.util.*
import kotlin.math.abs
class MapView(context: Context) : View(context) {
private lateinit var passList: List<SatPass>
private lateinit var gsp: GroundStationPosition
private lateinit var selectedSat: TLE
private val scale = resources.displayMetrics.density
private val groundTrackPaint = Paint().apply {
isAntiAlias = true
color = ContextCompat.getColor(context, R.color.satTrack)
style = Paint.Style.STROKE
strokeWidth = scale
}
private val satPaintPrim = Paint().apply {
isAntiAlias = true
color = ContextCompat.getColor(context, R.color.satPrimary)
style = Paint.Style.STROKE
strokeWidth = scale
}
private val satPaintSec = Paint().apply {
isAntiAlias = true
color = ContextCompat.getColor(context, R.color.satSecondary)
style = Paint.Style.STROKE
strokeWidth = scale
}
private val txtPaint = Paint().apply {
isAntiAlias = true
color = ContextCompat.getColor(context, R.color.satPrimary)
style = Paint.Style.FILL
strokeWidth = scale
textSize = 16f
setShadowLayer(2f, 2f, 2f, Color.BLACK)
}
private val outlinePaint = Paint().apply {
isAntiAlias = true
color = Color.BLACK
style = Paint.Style.FILL_AND_STROKE
strokeWidth = scale
textSize = 16f
}
private val rect = Rect()
private var checked = 0
private var degLon = 0f
private var degLat = 0f
fun setList(satPassList: List<SatPass>) {
passList = satPassList
}
fun setGSP(groundStationPosition: GroundStationPosition) {
gsp = groundStationPosition
}
fun setChecked(item: Int) {
checked = item
selectedSat = passList[checked].tle
}
override fun onDraw(canvas: Canvas) {
degLon = width / 360f
degLat = height / 180f
canvas.translate(width / 2f, height / 2f)
drawHomeLoc(canvas)
val currentTime = GeneralUtils.getDateFor(System.currentTimeMillis())
val orbitalPeriod = (24 * 60 / selectedSat.meanmo).toInt()
val predictor = passList[checked].predictor
val positions = predictor.getPositions(currentTime, 60, 0, orbitalPeriod * 3)
drawGroundTrack(canvas, positions)
passList.forEach { satPass -> drawSat(canvas, satPass, currentTime, satPaintSec) }
drawSat(canvas, passList[checked], currentTime, satPaintPrim)
}
private fun drawHomeLoc(canvas: Canvas) {
val lon = gsp.longitude.toFloat()
val lat = gsp.latitude.toFloat() * -1
val cx = lon * degLon
val cy = lat * degLat
val gspName = context.getString(R.string.map_gsp)
txtPaint.getTextBounds(gspName, 0, gspName.length, rect)
canvas.drawCircle(cx, cy, scale * 2, txtPaint)
canvas.drawText(gspName, cx - rect.width() / 2, cy - txtPaint.textSize, outlinePaint)
canvas.drawText(gspName, cx - rect.width() / 2, cy - txtPaint.textSize, txtPaint)
}
private fun drawGroundTrack(canvas: Canvas, list: List<SatPos>) {
val path = Path()
var lon: Float
var lat: Float
var lastLon = 181f
list.withIndex().forEach { (index, satPos) ->
lon = GeneralUtils.rad2Deg(satPos.longitude).toFloat()
lat = GeneralUtils.rad2Deg(satPos.latitude).toFloat() * -1
if (lon > 180f) lon -= 360f
lon *= degLon
lat *= degLat
if (index == 0 || abs(lon - lastLon) > 180) path.moveTo(lon, lat)
else path.lineTo(lon, lat)
lastLon = lon
}
canvas.drawPath(path, groundTrackPaint)
}
private fun drawSat(canvas: Canvas, satPass: SatPass, date: Date, paint: Paint) {
val tle = satPass.tle
val predictor = satPass.predictor
val satPosNow = predictor.getSatPos(date)
val footprintPosList = satPosNow.rangeCircle
drawFootprint(canvas, footprintPosList, paint)
drawName(canvas, satPosNow, tle.name)
}
private fun drawFootprint(canvas: Canvas, list: List<Position>, paint: Paint) {
val path = Path()
var lon: Float
var lat: Float
var lastLon = 181f
list.withIndex().forEach { (index, position) ->
lon = position.lon.toFloat()
lat = position.lat.toFloat() * -1
if (lon > 180f) lon -= 360f
lon *= degLon
lat *= degLat
if (index == 0 || abs(lon - lastLon) > 180) path.moveTo(lon, lat)
else path.lineTo(lon, lat)
lastLon = lon
}
canvas.drawPath(path, paint)
}
private fun drawName(canvas: Canvas, position: SatPos, name: String) {
var lon = GeneralUtils.rad2Deg(position.longitude).toFloat()
val lat = GeneralUtils.rad2Deg(position.latitude).toFloat() * -1
if (lon > 180f) lon -= 360f
val cx = lon * degLon
val cy = lat * degLat
canvas.drawCircle(cx, cy, scale * 2, txtPaint)
txtPaint.getTextBounds(name, 0, name.length, rect)
canvas.drawText(name, cx - rect.width() / 2, cy - txtPaint.textSize, outlinePaint)
canvas.drawText(name, cx - rect.width() / 2, cy - txtPaint.textSize, txtPaint)
}
}