From 6bf218d633327d8dad97d3e91ea48a5a572bef40 Mon Sep 17 00:00:00 2001 From: Arty Bishop Date: Mon, 2 Dec 2019 23:07:21 +0000 Subject: [PATCH] Now drawing the ground track and the footprint of the test satellite (Meteor M2-2) on the WorldMap --- .../lookingsat/ui/WorldMapFragment.kt | 174 ++++++++++++++---- 1 file changed, 142 insertions(+), 32 deletions(-) diff --git a/app/src/main/java/com/rtbishop/lookingsat/ui/WorldMapFragment.kt b/app/src/main/java/com/rtbishop/lookingsat/ui/WorldMapFragment.kt index 36609779..1a23a57e 100644 --- a/app/src/main/java/com/rtbishop/lookingsat/ui/WorldMapFragment.kt +++ b/app/src/main/java/com/rtbishop/lookingsat/ui/WorldMapFragment.kt @@ -1,23 +1,28 @@ package com.rtbishop.lookingsat.ui import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Path import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.FrameLayout -import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.lifecycle.ViewModelProvider -import androidx.lifecycle.lifecycleScope import com.github.amsacode.predict4java.PassPredictor +import com.github.amsacode.predict4java.Position +import com.github.amsacode.predict4java.SatPos import com.github.amsacode.predict4java.TLE -import com.google.android.material.floatingactionbutton.FloatingActionButton import com.rtbishop.lookingsat.MainViewModel import com.rtbishop.lookingsat.R -import kotlinx.coroutines.launch +import java.util.* import java.util.concurrent.Executors import java.util.concurrent.TimeUnit +import kotlin.math.abs +import kotlin.math.roundToInt class WorldMapFragment : Fragment() { @@ -27,7 +32,7 @@ class WorldMapFragment : Fragment() { private lateinit var viewModel: MainViewModel private lateinit var trackView: TrackView private lateinit var mapFrame: FrameLayout - private lateinit var fab: FloatingActionButton + // private lateinit var fab: FloatingActionButton private lateinit var selectedSat: TLE private lateinit var predictor: PassPredictor @@ -47,13 +52,22 @@ class WorldMapFragment : Fragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) mapFrame = view.findViewById(R.id.worldmap_frame) - fab = view.findViewById(R.id.worldmap_fab) - fab.setOnClickListener { showSelectSatDialog() } +// fab = view.findViewById(R.id.worldmap_fab) +// fab.setOnClickListener { showSelectSatDialog() } - selectedSat = viewModel.selectedSingleSat - lifecycleScope.launch { - predictor = PassPredictor(selectedSat, viewModel.gsp.value) - } + val tleString = arrayOf( + "METEOR-M2 2", + "1 44387U 19038A 19336.45319619 -.00000025 00000-0 80323-5 0 9996", + "2 44387 98.5920 295.7459 0002200 37.8724 322.2609 14.23335951 21369" + ) + + selectedSat = TLE(tleString) + predictor = PassPredictor(selectedSat, viewModel.gsp.value) + +// selectedSat = viewModel.selectedSingleSat +// lifecycleScope.launch { +// predictor = PassPredictor(selectedSat, viewModel.gsp.value) +// } trackView = TrackView(activity as MainActivity) mapFrame.addView(trackView) @@ -61,29 +75,125 @@ class WorldMapFragment : Fragment() { service.scheduleAtFixedRate({ trackView.invalidate() }, delay, delay, TimeUnit.MILLISECONDS) } - private fun showSelectSatDialog() { - val tleMainList = viewModel.tleMainList - val tleNameArray = arrayOfNulls(tleMainList.size) - var selection = viewModel.selectedSingleSat +// private fun showSelectSatDialog() { +// val tleMainList = viewModel.tleMainList +// val tleNameArray = arrayOfNulls(tleMainList.size) +// var selection = viewModel.selectedSingleSat +// +// tleMainList.withIndex().forEach { (position, tle) -> +// tleNameArray[position] = tle.name +// } +// +// val builder = AlertDialog.Builder(activity as MainActivity) +// builder.setTitle("Select Sat to track") +// .setSingleChoiceItems(tleNameArray, -1) { _, which -> +// selection = tleMainList[which] +// } +// .setPositiveButton("Ok") { _, _ -> +// viewModel.updateSelectedSingleSat(selection) +// } +// .setNegativeButton("Cancel") { dialog, _ -> +// dialog.cancel() +// } +// .create() +// .show() +// } - tleMainList.withIndex().forEach { (position, tle) -> - tleNameArray[position] = tle.name + inner class TrackView(context: Context) : View(context) { + private val groundTrackPaint = Paint().apply { + isAntiAlias = true + color = Color.RED + style = Paint.Style.STROKE + strokeWidth = scale + } + private val footprintPaint = Paint().apply { + isAntiAlias = true + color = Color.YELLOW + style = Paint.Style.STROKE + strokeWidth = scale + } + private val scale = resources.displayMetrics.density + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + val frameWidth = mapFrame.measuredWidth + val frameHeight = mapFrame.measuredHeight + val currentTime = getDateFor(System.currentTimeMillis()) + val orbitalPeriod = (24 * 60 / selectedSat.meanmo).toInt() + val orbitEndTime = getDateFor(currentTime.time + orbitalPeriod * 60 * 1000) + val satPos = predictor.getSatPos(currentTime) + val footprintPositions = satPos.rangeCircle + + var positions = predictor.getPositions(currentTime, 60, orbitalPeriod, orbitalPeriod) + drawGroundTrack(canvas, frameWidth, frameHeight, positions) + positions = predictor.getPositions(orbitEndTime, 60, 1, orbitalPeriod) + drawGroundTrack(canvas, frameWidth, frameHeight, positions) + drawFootprint(canvas, frameWidth, frameHeight, footprintPositions) + var longitude = rad2Deg(satPos.longitude) + + if (longitude > 180.0) { + longitude -= 180.0 + } else { + longitude += 180.0 + } } - val builder = AlertDialog.Builder(activity as MainActivity) - builder.setTitle("Select Sat to track") - .setSingleChoiceItems(tleNameArray, -1) { _, which -> - selection = tleMainList[which] - } - .setPositiveButton("Ok") { _, _ -> - viewModel.updateSelectedSingleSat(selection) - } - .setNegativeButton("Cancel") { dialog, _ -> - dialog.cancel() - } - .create() - .show() - } + private fun drawGroundTrack(canvas: Canvas, width: Int, height: Int, list: List) { + val trackPath = Path() + var trackX: Float + var trackY: Float + var prevX = 181f - inner class TrackView(context: Context) : View(context) + list.withIndex().forEach { (index, satPos) -> + trackX = rad2Deg(satPos.longitude).toFloat() + + if (trackX <= 180.0) trackX += 180 + else trackX -= 180 + + trackX *= width / 360f + trackY = ((90.0 - rad2Deg(satPos.latitude)) * (height / 180.0)) + .roundToInt() + .toFloat() + + if (index == 0 || abs(trackX - prevX) > 180) trackPath.moveTo(trackX, trackY) + else trackPath.lineTo(trackX, trackY) + + prevX = trackX + } + canvas.drawPath(trackPath, groundTrackPaint) + } + + private fun drawFootprint(canvas: Canvas, width: Int, height: Int, list: List) { + val printPath = Path() + var printX: Float + var printY: Float + var prevX = 181f + + list.withIndex().forEach { (index, position) -> + printX = position.lon.toFloat() + + if (printX <= 180f) printX += 180f + else printX -= 180f + + printX *= width / 360f + printY = ((90.0 - position.lat) * (height / 180.0)) + .roundToInt() + .toFloat() + + if (index == 0 || abs(printX - prevX) > 180) printPath.moveTo(printX, printY) + else printPath.lineTo(printX, printY) + + prevX = printX + } + canvas.drawPath(printPath, footprintPaint) + } + + private fun getDateFor(value: Long): Date { + return Date(value) + } + + private fun rad2Deg(value: Double): Double { + return value * 180 / Math.PI + } + } } \ No newline at end of file