use SimpleFastPointOverlay for the track overlay (see #96)

master
y20k 2021-03-15 17:43:31 +01:00
parent c193e8a475
commit 1c70fe1b5e
No known key found for this signature in database
GPG Key ID: 824D4259F41FAFF6
6 changed files with 117 additions and 76 deletions

View File

@ -181,11 +181,11 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlayHelpe
/* Overrides onMarkerTapped from MarkerListener */ /* Overrides onMarkerTapped from MarkerListener */
override fun onMarkerTapped(latitude: Double, longitude: Double, displayStartEndMarker: Boolean) { override fun onMarkerTapped(latitude: Double, longitude: Double) {
super.onMarkerTapped(latitude, longitude, displayStartEndMarker) super.onMarkerTapped(latitude, longitude)
if (bound) { if (bound) {
track = TrackHelper.toggleStarred(activity as Context, track, latitude, longitude) track = TrackHelper.toggleStarred(activity as Context, track, latitude, longitude)
layout.overlayCurrentTrack(track, trackingState, displayStartEndMarker) layout.overlayCurrentTrack(track, trackingState)
trackerService.track = track trackerService.track = track
} }
} }
@ -320,7 +320,7 @@ class MapFragment : Fragment(), YesNoDialog.YesNoDialogListener, MapOverlayHelpe
trackingState = trackerService.trackingState trackingState = trackerService.trackingState
// update location and track // update location and track
layout.markCurrentPosition(currentBestLocation, trackingState) layout.markCurrentPosition(currentBestLocation, trackingState)
layout.overlayCurrentTrack(track, trackingState, displayStartEndMarker = false) layout.overlayCurrentTrack(track, trackingState)
// center map, if it had not been dragged/zoomed before // center map, if it had not been dragged/zoomed before
if (!layout.userInteraction) { layout.centerMap(currentBestLocation, true)} if (!layout.userInteraction) { layout.centerMap(currentBestLocation, true)}
// show error snackbar if necessary // show error snackbar if necessary

View File

@ -162,11 +162,11 @@ class TrackFragment : Fragment(), RenameTrackDialog.RenameTrackListener, YesNoDi
/* Overrides onMarkerTapped from MarkerListener */ /* Overrides onMarkerTapped from MarkerListener */
override fun onMarkerTapped(latitude: Double, longitude: Double, displayStartEndMarker: Boolean) { override fun onMarkerTapped(latitude: Double, longitude: Double) {
super.onMarkerTapped(latitude, longitude, displayStartEndMarker) super.onMarkerTapped(latitude, longitude)
// update track display // update track display
track = TrackHelper.toggleStarred(activity as Context, track, latitude, longitude) track = TrackHelper.toggleStarred(activity as Context, track, latitude, longitude)
layout.updateTrackOverlay(track, displayStartEndMarker) layout.updateTrackOverlay(track)
// save track // save track
GlobalScope.launch { GlobalScope.launch {
FileHelper.saveTrackSuspended(track, true) FileHelper.saveTrackSuspended(track, true)

View File

@ -476,7 +476,6 @@ class TrackerService: Service(), CoroutineScope, SensorEventListener {
if (now.time - lastSave.time > Keys.SAVE_TEMP_TRACK_INTERVAL) { if (now.time - lastSave.time > Keys.SAVE_TEMP_TRACK_INTERVAL) {
lastSave = now lastSave = now
GlobalScope.launch { FileHelper.saveTempTrackSuspended(this@TrackerService, track) } GlobalScope.launch { FileHelper.saveTempTrackSuspended(this@TrackerService, track) }
LogHelper.e(TAG, "TEMP SAVE: $lastSave")
} }
} }
// update notification // update notification

View File

@ -19,14 +19,20 @@ package org.y20k.trackbook.helpers
import android.content.Context import android.content.Context
import android.graphics.Paint
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.location.Location import android.location.Location
import android.os.Vibrator import android.os.Vibrator
import android.widget.Toast import android.widget.Toast
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import org.osmdroid.api.IGeoPoint
import org.osmdroid.util.GeoPoint import org.osmdroid.util.GeoPoint
import org.osmdroid.views.overlay.ItemizedIconOverlay import org.osmdroid.views.overlay.ItemizedIconOverlay
import org.osmdroid.views.overlay.OverlayItem import org.osmdroid.views.overlay.OverlayItem
import org.osmdroid.views.overlay.simplefastpoint.LabelledGeoPoint
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlayOptions
import org.osmdroid.views.overlay.simplefastpoint.SimplePointTheme
import org.y20k.trackbook.Keys import org.y20k.trackbook.Keys
import org.y20k.trackbook.R import org.y20k.trackbook.R
import org.y20k.trackbook.core.Track import org.y20k.trackbook.core.Track
@ -44,7 +50,7 @@ class MapOverlayHelper (private var markerListener: MarkerListener) {
/* Interface used to communicate back to activity/fragment */ /* Interface used to communicate back to activity/fragment */
interface MarkerListener { interface MarkerListener {
fun onMarkerTapped(latitude: Double, longitude: Double, displayStartEndMarker: Boolean) { fun onMarkerTapped(latitude: Double, longitude: Double) {
} }
} }
@ -83,70 +89,86 @@ class MapOverlayHelper (private var markerListener: MarkerListener) {
overlayItems.add(overlayItem) overlayItems.add(overlayItem)
// create and return overlay for current position // create and return overlay for current position
return createOverlay(context, overlayItems, enableStarring = false, displayStartEndMarker = false) return createOverlay(context, overlayItems, enableStarring = false)
} }
/* Creates icon overlay for track */ /* Creates icon overlay for track */
fun createTrackOverlay(context: Context, track: Track, trackingState: Int, displayStartEndMarker: Boolean = false): ItemizedIconOverlay<OverlayItem> { fun createTrackOverlay(context: Context, track: Track, trackingState: Int): SimpleFastPointOverlay {
// get marker color
val overlayItems: ArrayList<OverlayItem> = ArrayList<OverlayItem>() val color = if (trackingState == Keys.STATE_TRACKING_ACTIVE) context.getColor(R.color.trackbook_red)
val wayPoints: MutableList<WayPoint> = track.wayPoints else context.getColor(R.color.trackbook_blue)
val maxIndex: Int = wayPoints.size - 1 // gather points for overlay
val points: MutableList<IGeoPoint> = mutableListOf()
when (trackingState) { track.wayPoints.forEach { wayPoint ->
// CASE: Recording is active val label: String = "${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()).format(wayPoint.time)} | ${context.getString(R.string.marker_description_accuracy)}: ${DecimalFormat("#0.00").format(wayPoint.accuracy)} (${wayPoint.provider})"
Keys.STATE_TRACKING_ACTIVE -> { // only add normal points
wayPoints.forEach { wayPoint: WayPoint -> if (!wayPoint.starred && !wayPoint.isStopOver) {
// get drawable points.add(LabelledGeoPoint(wayPoint.latitude, wayPoint.longitude, wayPoint.altitude, label))
val newMarker: Drawable
if (wayPoint.starred) {
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_star_red_24dp)!!
} else if (wayPoint.isStopOver) {
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_grey_24dp)!!
} else {
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_red_24dp)!!
}
// create overlay item and add to list of overlay items
val overlayItem: OverlayItem = createOverlayItem(context, wayPoint.latitude, wayPoint.longitude, wayPoint.accuracy, wayPoint.provider, wayPoint.time)
overlayItem.setMarker(newMarker)
overlayItems.add(overlayItem)
}
}
// CASE: Recording is paused/stopped
else -> {
wayPoints.forEachIndexed { index: Int, wayPoint: WayPoint ->
// get drawable
val newMarker: Drawable
if (displayStartEndMarker && index == 0) {
if (wayPoint.starred) {
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_start_starred_blue_48dp)!!
} else {
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_start_blue_48dp)!!
}
} else if (displayStartEndMarker && index == maxIndex) {
if (wayPoint.starred) {
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_end_starred_blue_48dp)!!
} else {
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_end_blue_48dp)!!
}
}
else if (wayPoint.starred) {
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_star_blue_24dp)!!
} else if (wayPoint.isStopOver) {
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_grey_24dp)!!
} else {
newMarker = ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_blue_24dp)!!
}
// create overlay item and add to list of overlay items
val overlayItem: OverlayItem = createOverlayItem(context, wayPoint.latitude, wayPoint.longitude, wayPoint.accuracy, wayPoint.provider, wayPoint.time)
overlayItem.setMarker(newMarker)
overlayItems.add(overlayItem)
}
} }
} }
val pointTheme: SimplePointTheme = SimplePointTheme(points, false)
// set styling for overlay
val style: Paint = Paint()
style.style = Paint.Style.FILL
style.color = color
val scalingFactor: Float = UiHelper.getDensityScalingFactor(context)
val overlayOptions: SimpleFastPointOverlayOptions = SimpleFastPointOverlayOptions.getDefaultStyle()
.setAlgorithm(SimpleFastPointOverlayOptions.RenderingAlgorithm.MAXIMUM_OPTIMIZATION)
.setSymbol(SimpleFastPointOverlayOptions.Shape.CIRCLE)
.setPointStyle(style)
.setRadius(6F * scalingFactor) // radius is set in px - scaling factor makes that display density independent (= dp)
.setIsClickable(true)
// .setCellSize(15) // Sets the grid cell size used for indexing, in pixels. Larger cells result in faster rendering speed, but worse fidelity. Default is 10 pixels, for large datasets (>10k points), use 15.
// create and return overlay
val overlay: SimpleFastPointOverlay = SimpleFastPointOverlay(pointTheme, overlayOptions)
overlay.setOnClickListener(object : SimpleFastPointOverlay.OnClickListener {
override fun onClick(points: SimpleFastPointOverlay.PointAdapter?, point: Int?) {
if (points != null && point != null) {
val markerPoint: IGeoPoint = points.get(point)
markerListener.onMarkerTapped(markerPoint.latitude, markerPoint.longitude)
}
}
})
return overlay
}
/* Creates overlay containing start, stop, stopover and starred markers for track */
fun createSpecialMakersTrackOverlay(context: Context, track: Track, trackingState: Int, displayStartEndMarker: Boolean = false): ItemizedIconOverlay<OverlayItem> {
val overlayItems: ArrayList<OverlayItem> = ArrayList<OverlayItem>()
val trackingActive: Boolean = trackingState == Keys.STATE_TRACKING_ACTIVE
val maxIndex: Int = track.wayPoints.size - 1
track.wayPoints.forEachIndexed { index: Int, wayPoint: WayPoint ->
var overlayItem: OverlayItem? = null
if (!trackingActive && index == 0 && displayStartEndMarker && wayPoint.starred) {
overlayItem = createOverlayItem(context, wayPoint.latitude, wayPoint.longitude, wayPoint.accuracy, wayPoint.provider, wayPoint.time)
overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_start_starred_blue_48dp)!!)
} else if (!trackingActive && index == 0 && displayStartEndMarker && !wayPoint.starred) {
overlayItem = createOverlayItem(context, wayPoint.latitude, wayPoint.longitude, wayPoint.accuracy, wayPoint.provider, wayPoint.time)
overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_start_blue_48dp)!!)
} else if (!trackingActive && index == maxIndex && displayStartEndMarker && wayPoint.starred) {
overlayItem = createOverlayItem(context, wayPoint.latitude, wayPoint.longitude, wayPoint.accuracy, wayPoint.provider, wayPoint.time)
overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_end_starred_blue_48dp)!!)
} else if (!trackingActive && index == maxIndex && displayStartEndMarker && !wayPoint.starred) {
overlayItem = createOverlayItem(context, wayPoint.latitude, wayPoint.longitude, wayPoint.accuracy, wayPoint.provider, wayPoint.time)
overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_end_blue_48dp)!!)
} else if (!trackingActive && wayPoint.starred) {
overlayItem = createOverlayItem(context, wayPoint.latitude, wayPoint.longitude, wayPoint.accuracy, wayPoint.provider, wayPoint.time)
overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_star_blue_24dp)!!)
} else if (trackingActive && wayPoint.starred) {
overlayItem = createOverlayItem(context, wayPoint.latitude, wayPoint.longitude, wayPoint.accuracy, wayPoint.provider, wayPoint.time)
overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_star_red_24dp)!!)
} else if (wayPoint.isStopOver) {
overlayItem = createOverlayItem(context, wayPoint.latitude, wayPoint.longitude, wayPoint.accuracy, wayPoint.provider, wayPoint.time)
overlayItem.setMarker(ContextCompat.getDrawable(context, R.drawable.ic_marker_track_location_grey_24dp)!!)
}
// add overlay item, if it was created
if (overlayItem != null) overlayItems.add(overlayItem)
}
// create and return overlay for current position // create and return overlay for current position
return createOverlay(context, overlayItems, enableStarring = true, displayStartEndMarker = displayStartEndMarker) return createOverlay(context, overlayItems, enableStarring = true)
} }
@ -163,12 +185,12 @@ class MapOverlayHelper (private var markerListener: MarkerListener) {
/* Creates an overlay */ /* Creates an overlay */
private fun createOverlay(context: Context, overlayItems: ArrayList<OverlayItem>, enableStarring: Boolean, displayStartEndMarker: Boolean): ItemizedIconOverlay<OverlayItem> { private fun createOverlay(context: Context, overlayItems: ArrayList<OverlayItem>, enableStarring: Boolean): ItemizedIconOverlay<OverlayItem> {
return ItemizedIconOverlay<OverlayItem>(context, overlayItems, return ItemizedIconOverlay<OverlayItem>(context, overlayItems,
object : ItemizedIconOverlay.OnItemGestureListener<OverlayItem> { object : ItemizedIconOverlay.OnItemGestureListener<OverlayItem> {
override fun onItemSingleTapUp(index: Int, item: OverlayItem): Boolean { override fun onItemSingleTapUp(index: Int, item: OverlayItem): Boolean {
if (enableStarring) { if (enableStarring) {
markerListener.onMarkerTapped(item.point.latitude, item.point.longitude, displayStartEndMarker) markerListener.onMarkerTapped(item.point.latitude, item.point.longitude)
return true return true
} else { } else {
return false return false

View File

@ -41,6 +41,7 @@ import org.osmdroid.views.overlay.OverlayItem
import org.osmdroid.views.overlay.TilesOverlay import org.osmdroid.views.overlay.TilesOverlay
import org.osmdroid.views.overlay.compass.CompassOverlay import org.osmdroid.views.overlay.compass.CompassOverlay
import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
import org.y20k.trackbook.Keys import org.y20k.trackbook.Keys
import org.y20k.trackbook.R import org.y20k.trackbook.R
import org.y20k.trackbook.core.Track import org.y20k.trackbook.core.Track
@ -67,7 +68,8 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
val resumeButton: FloatingActionButton val resumeButton: FloatingActionButton
var userInteraction: Boolean = false var userInteraction: Boolean = false
private var currentPositionOverlay: ItemizedIconOverlay<OverlayItem> private var currentPositionOverlay: ItemizedIconOverlay<OverlayItem>
private var currentTrackOverlay: ItemizedIconOverlay<OverlayItem>? private var currentTrackOverlay: SimpleFastPointOverlay?
private var currentTrackSpecialMarkerOverlay: ItemizedIconOverlay<OverlayItem>?
private var locationErrorBar: Snackbar private var locationErrorBar: Snackbar
private var controller: IMapController private var controller: IMapController
private var zoomLevel: Double private var zoomLevel: Double
@ -112,8 +114,9 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
mapView.overlays.add(currentPositionOverlay) mapView.overlays.add(currentPositionOverlay)
centerMap(startLocation) centerMap(startLocation)
// initialize track overlay // initialize track overlays
currentTrackOverlay = null currentTrackOverlay = null
currentTrackSpecialMarkerOverlay = null
// initialize recording button state // initialize recording button state
updateRecordingButton(trackingState) updateRecordingButton(trackingState)
@ -162,13 +165,19 @@ data class MapFragmentLayoutHolder(private var context: Context, private var mar
/* Overlay current track on map */ /* Overlay current track on map */
fun overlayCurrentTrack(track: Track, trackingState: Int, displayStartEndMarker: Boolean) { fun overlayCurrentTrack(track: Track, trackingState: Int) {
if (currentTrackOverlay != null) { if (currentTrackOverlay != null) {
mapView.overlays.remove(currentTrackOverlay) mapView.overlays.remove(currentTrackOverlay)
} }
if (currentTrackSpecialMarkerOverlay != null) {
mapView.overlays.remove(currentTrackSpecialMarkerOverlay)
}
if (track.wayPoints.isNotEmpty()) { if (track.wayPoints.isNotEmpty()) {
currentTrackOverlay = MapOverlayHelper(markerListener).createTrackOverlay(context, track, trackingState, displayStartEndMarker) val mapOverlayHelper: MapOverlayHelper = MapOverlayHelper(markerListener)
currentTrackOverlay = mapOverlayHelper.createTrackOverlay(context, track, trackingState)
currentTrackSpecialMarkerOverlay = mapOverlayHelper.createSpecialMakersTrackOverlay(context, track, trackingState)
mapView.overlays.add(currentTrackOverlay) mapView.overlays.add(currentTrackOverlay)
mapView.overlays.add(currentTrackSpecialMarkerOverlay)
} }
} }

View File

@ -45,6 +45,7 @@ import org.osmdroid.views.overlay.OverlayItem
import org.osmdroid.views.overlay.TilesOverlay import org.osmdroid.views.overlay.TilesOverlay
import org.osmdroid.views.overlay.compass.CompassOverlay import org.osmdroid.views.overlay.compass.CompassOverlay
import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
import org.osmdroid.views.overlay.simplefastpoint.SimpleFastPointOverlay
import org.y20k.trackbook.Keys import org.y20k.trackbook.Keys
import org.y20k.trackbook.R import org.y20k.trackbook.R
import org.y20k.trackbook.core.Track import org.y20k.trackbook.core.Track
@ -68,7 +69,8 @@ data class TrackFragmentLayoutHolder(private var context: Context, private var m
val editButton: ImageButton val editButton: ImageButton
val trackNameView: MaterialTextView val trackNameView: MaterialTextView
private val mapView: MapView private val mapView: MapView
private var trackOverlay: ItemizedIconOverlay<OverlayItem>? private var trackSpecialMarkersOverlay: ItemizedIconOverlay<OverlayItem>?
private var trackOverlay: SimpleFastPointOverlay?
private var controller: IMapController private var controller: IMapController
private var zoomLevel: Double private var zoomLevel: Double
private val statisticsSheetBehavior: BottomSheetBehavior<View> private val statisticsSheetBehavior: BottomSheetBehavior<View>
@ -146,9 +148,12 @@ data class TrackFragmentLayoutHolder(private var context: Context, private var m
mapView.overlays.add(compassOverlay) mapView.overlays.add(compassOverlay)
// create map overlay // create map overlay
trackOverlay = MapOverlayHelper(markerListener).createTrackOverlay(context, track, Keys.STATE_TRACKING_NOT, displayStartEndMarker = true) val mapOverlayHelper: MapOverlayHelper = MapOverlayHelper(markerListener)
trackOverlay = mapOverlayHelper.createTrackOverlay(context, track, Keys.STATE_TRACKING_NOT)
trackSpecialMarkersOverlay = mapOverlayHelper.createSpecialMakersTrackOverlay(context, track, Keys.STATE_TRACKING_NOT, displayStartEndMarker = true)
if (track.wayPoints.isNotEmpty()) { if (track.wayPoints.isNotEmpty()) {
mapView.overlays.add(trackOverlay) mapView.overlays.add(trackOverlay)
mapView.overlays.add(trackSpecialMarkersOverlay)
} }
// set up and show statistics sheet // set up and show statistics sheet
@ -168,14 +173,20 @@ data class TrackFragmentLayoutHolder(private var context: Context, private var m
/* Updates map overlay */ /* Updates map overlay */
fun updateTrackOverlay(newTrack: Track, displayStartEndMarker: Boolean) { fun updateTrackOverlay(newTrack: Track) {
track = newTrack track = newTrack
if (trackOverlay != null) { if (trackOverlay != null) {
mapView.overlays.remove(trackOverlay) mapView.overlays.remove(trackOverlay)
} }
if (trackSpecialMarkersOverlay != null) {
mapView.overlays.remove(trackSpecialMarkersOverlay)
}
if (track.wayPoints.isNotEmpty()) { if (track.wayPoints.isNotEmpty()) {
trackOverlay = MapOverlayHelper(markerListener).createTrackOverlay(context, track, Keys.STATE_TRACKING_NOT, displayStartEndMarker) val mapOverlayHelper: MapOverlayHelper = MapOverlayHelper(markerListener)
trackOverlay = mapOverlayHelper.createTrackOverlay(context, track, Keys.STATE_TRACKING_NOT)
trackSpecialMarkersOverlay = mapOverlayHelper.createSpecialMakersTrackOverlay(context, track, Keys.STATE_TRACKING_NOT, displayStartEndMarker = true)
mapView.overlays.add(trackOverlay) mapView.overlays.add(trackOverlay)
mapView.overlays.add(trackSpecialMarkersOverlay)
} }
} }