diff --git a/app/src/main/java/com/rtbishop/look4sat/data/SatItem.kt b/app/src/main/java/com/rtbishop/look4sat/data/SatItem.kt deleted file mode 100644 index 1d98c407..00000000 --- a/app/src/main/java/com/rtbishop/look4sat/data/SatItem.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.rtbishop.look4sat.data - -import org.osmdroid.api.IGeoPoint -import org.osmdroid.views.overlay.OverlayItem - -data class SatItem( - val name: String, - val description: String, - val position: IGeoPoint, - val pass: SatPass -) : OverlayItem(name, description, position) \ No newline at end of file diff --git a/app/src/main/java/com/rtbishop/look4sat/ui/fragments/MapFragment.kt b/app/src/main/java/com/rtbishop/look4sat/ui/fragments/MapFragment.kt index 5b2f1280..1ff6e068 100644 --- a/app/src/main/java/com/rtbishop/look4sat/ui/fragments/MapFragment.kt +++ b/app/src/main/java/com/rtbishop/look4sat/ui/fragments/MapFragment.kt @@ -4,20 +4,17 @@ import android.graphics.Color import android.graphics.ColorMatrix import android.graphics.ColorMatrixColorFilter import android.graphics.Paint +import android.graphics.drawable.Drawable import android.os.Bundle import android.view.View import androidx.core.content.ContextCompat -import androidx.core.content.res.ResourcesCompat import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentActivity import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope -import androidx.preference.PreferenceManager -import com.github.amsacode.predict4java.GroundStationPosition +import com.github.amsacode.predict4java.Position import com.rtbishop.look4sat.R import com.rtbishop.look4sat.SharedViewModel import com.rtbishop.look4sat.data.Result -import com.rtbishop.look4sat.data.SatItem import com.rtbishop.look4sat.data.SatPass import com.rtbishop.look4sat.databinding.FragmentMapBinding import com.rtbishop.look4sat.utility.PrefsManager @@ -27,7 +24,7 @@ import kotlinx.coroutines.delay import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.osmdroid.config.Configuration -import org.osmdroid.tileprovider.tilesource.OnlineTileSourceBase +import org.osmdroid.tileprovider.tilesource.ITileSource import org.osmdroid.tileprovider.tilesource.TileSourcePolicy import org.osmdroid.tileprovider.tilesource.XYTileSource import org.osmdroid.util.BoundingBox @@ -45,43 +42,27 @@ class MapFragment : Fragment(R.layout.fragment_map) { @Inject lateinit var prefsManager: PrefsManager - private lateinit var mainActivity: FragmentActivity private lateinit var binding: FragmentMapBinding private lateinit var trackPaint: Paint private lateinit var footprintPaint: Paint private lateinit var selectedPass: SatPass - private val dateNow = Date(System.currentTimeMillis()) + private val dateNow = Date() private val viewModel: SharedViewModel by activityViewModels() + private var iconPos: Drawable? = null + private var iconSat: Drawable? = null override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) binding = FragmentMapBinding.bind(view) - mainActivity = requireActivity() - - val prefs = PreferenceManager.getDefaultSharedPreferences(mainActivity.applicationContext) - Configuration.getInstance().load(mainActivity.applicationContext, prefs) - - trackPaint = Paint().apply { - strokeWidth = 2f - style = Paint.Style.STROKE - color = ContextCompat.getColor(mainActivity, R.color.satTrack) - strokeCap = Paint.Cap.ROUND - strokeJoin = Paint.Join.ROUND - isAntiAlias = true - } - - footprintPaint = Paint().apply { - style = Paint.Style.FILL_AND_STROKE - color = ContextCompat.getColor(mainActivity, R.color.satFootprint) - isAntiAlias = true - } - setupMapView() - setupPosOverlay(prefsManager.getStationPosition()) + setupPosOverlay() setupObservers() } private fun setupMapView() { + Configuration.getInstance() + .load(requireContext().applicationContext, prefsManager.preferences) + val filter = getColorFilter(ContextCompat.getColor(requireContext(), R.color.satTrack)) binding.mapView.apply { setMultiTouchControls(true) setTileSource(getWikimediaTileSource()) @@ -89,36 +70,52 @@ class MapFragment : Fragment(R.layout.fragment_map) { maxZoomLevel = 6.0 controller.setZoom(3.0) zoomController.setVisibility(CustomZoomButtonsController.Visibility.NEVER) - isHorizontalMapRepetitionEnabled = false - isVerticalMapRepetitionEnabled = false overlayManager.tilesOverlay.loadingBackgroundColor = Color.TRANSPARENT overlayManager.tilesOverlay.loadingLineColor = Color.TRANSPARENT + overlayManager.tilesOverlay.setColorFilter(filter) setScrollableAreaLimitDouble(BoundingBox(85.05, 180.0, -85.05, -180.0)) - // apply filter - val tilesFilter = getColorFilter(Color.parseColor("#D50000")) - overlayManager.tilesOverlay.setColorFilter(tilesFilter) - // add overlays: 0 - GSP, 1 - SatTrack, 2 - SatFootprint, 3 - SatIcons val layers = listOf(FolderOverlay(), FolderOverlay(), FolderOverlay(), FolderOverlay()) overlays.addAll(layers) } + trackPaint = Paint().apply { + strokeWidth = 2f + style = Paint.Style.STROKE + color = ContextCompat.getColor(requireContext(), R.color.satTrack) + 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 + } + iconPos = ContextCompat.getDrawable(requireContext(), R.drawable.ic_map_pos) + iconSat = ContextCompat.getDrawable(requireContext(), R.drawable.ic_map_sat) } - private fun getWikimediaTileSource(): OnlineTileSourceBase { - return XYTileSource( - "wikimedia", - 2, - 6, - 256, - ".png", - arrayOf("https://maps.wikimedia.org/osm-intl/"), - resources.getString(R.string.map_copyright), - TileSourcePolicy( - 1, TileSourcePolicy.FLAG_NO_BULK and TileSourcePolicy.FLAG_NO_PREVENTIVE and - TileSourcePolicy.FLAG_USER_AGENT_MEANINGFUL and TileSourcePolicy.FLAG_USER_AGENT_NORMALIZED - ) + private fun getWikimediaTileSource(): ITileSource { + val copyright = resources.getString(R.string.map_copyright) + val sources = arrayOf("https://maps.wikimedia.org/osm-intl/") + val policy = TileSourcePolicy( + 1, TileSourcePolicy.FLAG_NO_BULK and TileSourcePolicy.FLAG_NO_PREVENTIVE and + TileSourcePolicy.FLAG_USER_AGENT_MEANINGFUL and TileSourcePolicy.FLAG_USER_AGENT_NORMALIZED ) + return XYTileSource("wikimedia", 2, 6, 256, ".png", sources, copyright, policy) + } + + private fun setupPosOverlay() { + val gsp = prefsManager.getStationPosition() + Marker(binding.mapView).apply { + setInfoWindow(null) + setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM) + icon = iconPos + position = GeoPoint(gsp.latitude, gsp.longitude) + binding.mapView.overlays[0] = this + binding.mapView.invalidate() + } } private fun setupObservers() { @@ -133,63 +130,39 @@ class MapFragment : Fragment(R.layout.fragment_map) { }) } - private fun setupPosOverlay(gsp: GroundStationPosition) { - Marker(binding.mapView).apply { - setInfoWindow(null) - setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM) - icon = ResourcesCompat.getDrawable(resources, R.drawable.ic_map_pos, mainActivity.theme) - position = GeoPoint(gsp.latitude, gsp.longitude) - binding.mapView.overlays[0] = this - binding.mapView.invalidate() - } - } - private fun setupSatOverlay(passList: List) { lifecycleScope.launch { while (true) { dateNow.time = System.currentTimeMillis() - binding.mapView.overlays[3] = getSatIcons(passList) + binding.mapView.overlays[3] = getSatMarkers(passList) binding.mapView.overlays[2] = getSatFootprint(selectedPass) setSatInfo(selectedPass) binding.mapView.invalidate() - delay(3000) + delay(2000) } } } - private suspend fun getSatIcons(passList: List): Overlay = + private suspend fun getSatMarkers(passList: List): Overlay = withContext(Dispatchers.Default) { - val icon = - ResourcesCompat.getDrawable(resources, R.drawable.ic_map_sat, mainActivity.theme) - val items = mutableListOf() - - passList.forEach { - val satPos = it.predictor.getSatPos(dateNow) - var lat = Math.toDegrees(satPos.latitude) - if (lat > 85.05) lat = 85.05 - else if (lat < -85.05) lat = -85.05 - var lon = Math.toDegrees(satPos.longitude) - if (lon > 180.0) lon -= 360.0 - - SatItem(it.tle.name, it.tle.name, GeoPoint(lat, lon), it).apply { - markerHotspot = OverlayItem.HotspotPlace.CENTER + val items = FolderOverlay() + passList.forEach { satPass -> + val satPos = satPass.predictor.getSatPos(dateNow) + val osmPos = getOsmPosition(satPos.latitude, satPos.longitude, true) + Marker(binding.mapView).apply { + setInfoWindow(null) + setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_CENTER) + icon = iconSat + position = GeoPoint(osmPos.lat, osmPos.lon) + setOnMarkerClickListener { _, _ -> + selectedPass = satPass + setSatDetails(satPass) + true + } items.add(this) } } - - val listener = object : ItemizedIconOverlay.OnItemGestureListener { - override fun onItemLongPress(index: Int, item: SatItem): Boolean { - return true - } - - override fun onItemSingleTapUp(index: Int, item: SatItem): Boolean { - selectedPass = item.pass - setSatDetails(item.pass) - return true - } - } - - return@withContext ItemizedIconOverlay(items, icon, listener, mainActivity) + return@withContext items } private fun setSatDetails(satPass: SatPass) { @@ -201,31 +174,17 @@ class MapFragment : Fragment(R.layout.fragment_map) { private fun setSatInfo(satPass: SatPass) { val satPos = satPass.predictor.getSatPos(dateNow) - - var satLat = Math.toDegrees(satPos.latitude) - if (satLat > 85.05) satLat = 85.05 - else if (satLat < -85.05) satLat = -85.05 - - var satLon = Math.toDegrees(satPos.longitude) - if (satLon > 180f) satLon -= 360f - - binding.idName.text = - String.format( - mainActivity.getString(R.string.pat_osm_idName), - satPass.tle.catnum, - satPass.tle.name - ) - binding.altitude.text = - String.format(mainActivity.getString(R.string.pat_altitude), satPos.altitude) - binding.distance.text = - String.format(mainActivity.getString(R.string.pat_distance), satPos.range) - binding.velocity.text = - String.format( - mainActivity.getString(R.string.pat_osm_vel), - getSatVelocity(satPos.altitude) - ) - binding.latLon.text = - String.format(mainActivity.getString(R.string.pat_osm_latLon), satLat, satLon) + val osmPos = getOsmPosition(satPos.latitude, satPos.longitude, true) + val catNum = satPass.tle.catnum + val name = satPass.tle.name + val satVelocity = getSatVelocity(satPos.altitude) + binding.apply { + idName.text = String.format(getString(R.string.pat_osm_idName), catNum, name) + altitude.text = String.format(getString(R.string.pat_altitude), satPos.altitude) + distance.text = String.format(getString(R.string.pat_distance), satPos.range) + velocity.text = String.format(getString(R.string.pat_osm_vel), satVelocity) + latLon.text = String.format(getString(R.string.pat_osm_latLon), osmPos.lat, osmPos.lon) + } } private fun getSatVelocity(satAlt: Double): Double { @@ -240,17 +199,11 @@ class MapFragment : Fragment(R.layout.fragment_map) { val positions = pass.predictor.getPositions(dateNow, 20, 0, period * 3) val trackOverlay = FolderOverlay() val trackPoints = mutableListOf() - var oldLon = 0.0 + positions.forEach { - var newLat = Math.toDegrees(it.latitude) - if (newLat > 85.05) newLat = 85.05 - else if (newLat < -85.05) newLat = -85.05 - - var newLon = Math.toDegrees(it.longitude) - if (newLon > 180.0) newLon -= 360.0 - - if (oldLon < -170.0 && newLon > 170.0 || oldLon > 170.0 && newLon < -170.0) { + 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() currentPoints.addAll(trackPoints) Polyline().apply { @@ -260,8 +213,8 @@ class MapFragment : Fragment(R.layout.fragment_map) { } trackPoints.clear() } - oldLon = newLon - trackPoints.add(GeoPoint(newLat, newLon)) + oldLon = osmPos.lon + trackPoints.add(GeoPoint(osmPos.lat, osmPos.lon)) } Polyline().apply { @@ -269,7 +222,6 @@ class MapFragment : Fragment(R.layout.fragment_map) { setPoints(trackPoints) trackOverlay.add(this) } - return trackOverlay } @@ -279,18 +231,12 @@ class MapFragment : Fragment(R.layout.fragment_map) { var zeroPoint = GeoPoint(0.0, 0.0) rangeCircle.withIndex().forEach { - var lat = it.value.lat - if (lat > 85.05) lat = 85.05 - else if (lat < -85.05) lat = -85.05 - - var lon = it.value.lon - if (lon > 180.0) lon -= 360.0 - - if (it.index == 0) zeroPoint = GeoPoint(lat, lon) - rangePoints.add(GeoPoint(lat, lon)) + val osmPos = getOsmPosition(it.value.lat, it.value.lon, false) + if (it.index == 0) zeroPoint = GeoPoint(osmPos.lat, osmPos.lon) + rangePoints.add(GeoPoint(osmPos.lat, osmPos.lon)) } - rangePoints.add(zeroPoint) + rangePoints.add(zeroPoint) return Polygon().apply { fillPaint.set(footprintPaint) outlinePaint.set(footprintPaint) @@ -302,7 +248,6 @@ class MapFragment : Fragment(R.layout.fragment_map) { val newR = Color.red(targetColor) / 255f val newG = Color.green(targetColor) / 255f val newB = Color.blue(targetColor) / 255f - val negativeMatrix = ColorMatrix( floatArrayOf( -1f, 0f, 0f, 0f, 255f, @@ -311,7 +256,6 @@ class MapFragment : Fragment(R.layout.fragment_map) { 0f, 0f, 0f, 1f, 0f ) ) - val tintedMatrix = ColorMatrix( floatArrayOf( newR, newG, newB, 0f, 0f, @@ -320,18 +264,21 @@ class MapFragment : Fragment(R.layout.fragment_map) { 0f, 0f, 0f, 0f, 255f ) ) - tintedMatrix.preConcat(negativeMatrix) return ColorMatrixColorFilter(tintedMatrix) } - override fun onPause() { - binding.mapView.onPause() - super.onPause() - } - - override fun onResume() { - super.onResume() - binding.mapView.onResume() + private fun getOsmPosition(lat: Double, lon: Double, inRadians: Boolean): Position { + return if (inRadians) { + var osmLat = Math.toDegrees(lat) + var osmLon = Math.toDegrees(lon) + if (osmLat > 85.05) osmLat = 85.05 else if (osmLat < -85.05) osmLat = -85.05 + if (osmLon > 180f) osmLon -= 360f + Position(osmLat, osmLon) + } else { + val osmLat = if (lat > 85.05) 85.05 else if (lat < -85.05) -85.05 else lat + val osmLon = if (lon > 180.0) lon - 360.0 else lon + Position(osmLat, osmLon) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/rtbishop/look4sat/utility/PrefsManager.kt b/app/src/main/java/com/rtbishop/look4sat/utility/PrefsManager.kt index 1ecb8177..eea7bd13 100644 --- a/app/src/main/java/com/rtbishop/look4sat/utility/PrefsManager.kt +++ b/app/src/main/java/com/rtbishop/look4sat/utility/PrefsManager.kt @@ -26,7 +26,7 @@ import com.github.amsacode.predict4java.GroundStationPosition import com.rtbishop.look4sat.data.PassPrefs import javax.inject.Inject -class PrefsManager @Inject constructor(private val preferences: SharedPreferences) { +class PrefsManager @Inject constructor(val preferences: SharedPreferences) { companion object { const val keyLatitude = "latitude" const val keyLongitude = "longitude" diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 5489eefa..ce357a1f 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -26,7 +26,7 @@ #808080 #1e1e1e - #B00020 + #D50000 #26FFE082 #66000000