checkpoint

This commit is contained in:
voussoir 2023-03-19 11:19:24 -07:00
parent d943b206fa
commit e26bd2bf9a
9 changed files with 106 additions and 49 deletions

View file

@ -53,11 +53,11 @@ class Database(val trackbook: Trackbook)
this.connection.endTransaction() this.connection.endTransaction()
} }
fun insert_trkpt(device_id: String, trkpt: Trkpt) fun insert_trkpt(trkpt: Trkpt)
{ {
Log.i("VOUSSOIR", "Database.insert_trkpt") Log.i("VOUSSOIR", "Database.insert_trkpt")
val values = ContentValues().apply { val values = ContentValues().apply {
put("device_id", device_id) put("device_id", trkpt.device_id)
put("lat", trkpt.latitude) put("lat", trkpt.latitude)
put("lon", trkpt.longitude) put("lon", trkpt.longitude)
put("time", GregorianCalendar.getInstance().time.time) put("time", GregorianCalendar.getInstance().time.time)

View file

@ -98,7 +98,7 @@ object Keys {
const val DEFAULT_ACCURACY: Float = 300f // in meters const val DEFAULT_ACCURACY: Float = 300f // in meters
const val DEFAULT_ALTITUDE: Double = 0.0 const val DEFAULT_ALTITUDE: Double = 0.0
const val DEFAULT_TIME: Long = 0L const val DEFAULT_TIME: Long = 0L
const val COMMIT_INTERVAL: Int = 30 const val COMMIT_INTERVAL: Long = 30 * ONE_SECOND_IN_MILLISECONDS
const val DEFAULT_THRESHOLD_LOCATION_ACCURACY: Int = 30 // 30 meters const val DEFAULT_THRESHOLD_LOCATION_ACCURACY: Int = 30 // 30 meters
const val DEFAULT_THRESHOLD_LOCATION_AGE: Long = 5_000_000_000L // 5s in nanoseconds const val DEFAULT_THRESHOLD_LOCATION_AGE: Long = 5_000_000_000L // 5s in nanoseconds
const val DEFAULT_THRESHOLD_DISTANCE: Float = 15f // 15 meters const val DEFAULT_THRESHOLD_DISTANCE: Float = 15f // 15 meters

View file

@ -40,6 +40,7 @@ import androidx.fragment.app.Fragment
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
import com.google.android.material.floatingactionbutton.FloatingActionButton import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import org.osmdroid.api.IGeoPoint
import org.osmdroid.api.IMapController import org.osmdroid.api.IMapController
import org.osmdroid.events.MapEventsReceiver import org.osmdroid.events.MapEventsReceiver
import org.osmdroid.tileprovider.tilesource.TileSourceFactory import org.osmdroid.tileprovider.tilesource.TileSourceFactory
@ -136,8 +137,7 @@ class MapFragment : Fragment()
mapView.setTileSource(TileSourceFactory.MAPNIK) mapView.setTileSource(TileSourceFactory.MAPNIK)
mapView.setMultiTouchControls(true) mapView.setMultiTouchControls(true)
mapView.zoomController.setVisibility(org.osmdroid.views.CustomZoomButtonsController.Visibility.NEVER) mapView.zoomController.setVisibility(org.osmdroid.views.CustomZoomButtonsController.Visibility.NEVER)
zoomLevel = PreferencesHelper.loadZoomLevel() mapView.controller.setZoom(Keys.DEFAULT_ZOOM_LEVEL)
mapView.controller.setZoom(zoomLevel)
if (AppThemeHelper.isDarkModeOn(requireActivity())) if (AppThemeHelper.isDarkModeOn(requireActivity()))
{ {
@ -214,7 +214,6 @@ class MapFragment : Fragment()
mapView.setOnTouchListener { v, event -> mapView.setOnTouchListener { v, event ->
continuous_auto_center = false continuous_auto_center = false
zoomLevel = mapView.zoomLevelDouble
false false
} }
@ -226,12 +225,10 @@ class MapFragment : Fragment()
centerMap(currentBestLocation, animated=true) centerMap(currentBestLocation, animated=true)
} }
zoom_in_button.setOnClickListener { zoom_in_button.setOnClickListener {
zoomLevel += 0.5 mapView.controller.setZoom(mapView.zoomLevelDouble + 0.5)
mapView.controller.zoomTo(mapView.zoomLevelDouble + 0.5, 0)
} }
zoom_out_button.setOnClickListener { zoom_out_button.setOnClickListener {
zoomLevel -= 0.5 mapView.controller.setZoom(mapView.zoomLevelDouble - 0.5)
mapView.controller.zoomTo(mapView.zoomLevelDouble - 0.5, 0)
} }
requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) requireActivity().window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
@ -533,15 +530,15 @@ class MapFragment : Fragment()
} }
/* Overlay current track on map */ /* Overlay current track on map */
fun create_current_track_overlay(trkpts: Collection<Trkpt>, trackingState: Int) fun create_current_track_overlay(geopoints: MutableList<IGeoPoint>, trackingState: Int)
{ {
if (currentTrackOverlay != null) if (currentTrackOverlay != null)
{ {
mapView.overlays.remove(currentTrackOverlay) mapView.overlays.remove(currentTrackOverlay)
} }
if (trkpts.isNotEmpty()) if (geopoints.isNotEmpty())
{ {
currentTrackOverlay = createTrackOverlay(requireContext(), mapView, trkpts, trackingState) currentTrackOverlay = createTrackOverlay(requireContext(), mapView, geopoints, trackingState)
} }
} }
@ -620,7 +617,7 @@ class MapFragment : Fragment()
trackingState = trackerService.trackingState trackingState = trackerService.trackingState
// update location and track // update location and track
create_current_position_overlays(currentBestLocation, trackingState) create_current_position_overlays(currentBestLocation, trackingState)
create_current_track_overlay(trackerService.recent_trkpts, trackingState) create_current_track_overlay(trackerService.recent_trackpoints_for_mapview, trackingState)
// center map, if it had not been dragged/zoomed before // center map, if it had not been dragged/zoomed before
if (continuous_auto_center) if (continuous_auto_center)
{ {

View file

@ -42,6 +42,9 @@ data class Track (
fun delete() fun delete()
{ {
Log.i("VOUSSOIR", "Track.delete ${device_id} ${start_time} -- ${end_time}.") Log.i("VOUSSOIR", "Track.delete ${device_id} ${start_time} -- ${end_time}.")
database.begin_transaction()
database.connection.delete("trkpt", "device_id = ? AND time > ? AND time < ?", arrayOf(device_id, start_time.time.toString(), end_time.time.toString()))
database.commit()
} }
fun export_gpx(context: Context, fileuri: Uri): Uri? fun export_gpx(context: Context, fileuri: Uri): Uri?
@ -183,6 +186,7 @@ data class Track (
while (cursor.moveToNext()) while (cursor.moveToNext())
{ {
val trkpt = Trkpt( val trkpt = Trkpt(
device_id=device_id,
provider="", provider="",
latitude=cursor.getDouble(COLUMN_LAT), latitude=cursor.getDouble(COLUMN_LAT),
longitude=cursor.getDouble(COLUMN_LON), longitude=cursor.getDouble(COLUMN_LON),

View file

@ -38,7 +38,9 @@ import androidx.constraintlayout.widget.Group
import androidx.core.widget.NestedScrollView import androidx.core.widget.NestedScrollView
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.google.android.material.bottomsheet.BottomSheetBehavior import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.floatingactionbutton.FloatingActionButton
import com.google.android.material.textview.MaterialTextView import com.google.android.material.textview.MaterialTextView
import org.osmdroid.api.IGeoPoint
import org.osmdroid.api.IMapController import org.osmdroid.api.IMapController
import org.osmdroid.events.MapListener import org.osmdroid.events.MapListener
import org.osmdroid.events.ScrollEvent import org.osmdroid.events.ScrollEvent
@ -65,6 +67,8 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
lateinit var rootView: View lateinit var rootView: View
lateinit var save_track_button: ImageButton lateinit var save_track_button: ImageButton
lateinit var deleteButton: ImageButton lateinit var deleteButton: ImageButton
lateinit var zoom_in_button: FloatingActionButton
lateinit var zoom_out_button: FloatingActionButton
lateinit var trackNameView: MaterialTextView lateinit var trackNameView: MaterialTextView
lateinit var track_query_start_date: DatePicker lateinit var track_query_start_date: DatePicker
lateinit var track_query_start_time: TimePicker lateinit var track_query_start_time: TimePicker
@ -113,6 +117,8 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
mapView = rootView.findViewById(R.id.map) mapView = rootView.findViewById(R.id.map)
save_track_button = rootView.findViewById(R.id.save_button) save_track_button = rootView.findViewById(R.id.save_button)
deleteButton = rootView.findViewById(R.id.delete_button) deleteButton = rootView.findViewById(R.id.delete_button)
zoom_in_button = rootView.findViewById(R.id.zoom_in_button)
zoom_out_button = rootView.findViewById(R.id.zoom_out_button)
trackNameView = rootView.findViewById(R.id.statistics_track_name_headline) trackNameView = rootView.findViewById(R.id.statistics_track_name_headline)
controller = mapView.controller controller = mapView.controller
@ -235,6 +241,13 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
) )
} }
zoom_in_button.setOnClickListener {
mapView.controller.zoomTo(mapView.zoomLevelDouble + 0.5, 0)
}
zoom_out_button.setOnClickListener {
mapView.controller.zoomTo(mapView.zoomLevelDouble - 0.5, 0)
}
statisticsSheetBehavior = BottomSheetBehavior.from<View>(statisticsSheet) statisticsSheetBehavior = BottomSheetBehavior.from<View>(statisticsSheet)
statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED statisticsSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
@ -260,9 +273,14 @@ class TrackFragment : Fragment(), MapListener, YesNoDialog.YesNoDialogListener
{ {
mapView.overlays.remove(track_overlay) mapView.overlays.remove(track_overlay)
} }
val geopoints: MutableList<IGeoPoint> = mutableListOf()
for (trkpt in track.trkpts)
{
geopoints.add(trkpt)
}
if (track.trkpts.isNotEmpty()) if (track.trkpts.isNotEmpty())
{ {
track_overlay = createTrackOverlay(requireContext(), mapView, track.trkpts, Keys.STATE_TRACKING_STOPPED) track_overlay = createTrackOverlay(requireContext(), mapView, geopoints, Keys.STATE_TRACKING_STOPPED)
special_points_overlay = create_start_end_markers(requireContext(), mapView, track.trkpts) special_points_overlay = create_start_end_markers(requireContext(), mapView, track.trkpts)
} }
setupStatisticsViews() setupStatisticsViews()

View file

@ -30,6 +30,9 @@ import android.Manifest
import android.os.* import android.os.*
import android.util.Log import android.util.Log
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import org.osmdroid.api.IGeoPoint
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.overlay.simplefastpoint.LabelledGeoPoint
import java.util.* import java.util.*
import org.y20k.trackbook.helpers.* import org.y20k.trackbook.helpers.*
@ -53,6 +56,7 @@ class TrackerService: Service()
private val RECENT_TRKPT_COUNT = 7200 private val RECENT_TRKPT_COUNT = 7200
lateinit var recent_trkpts: Deque<Trkpt> lateinit var recent_trkpts: Deque<Trkpt>
lateinit var recent_displacement_trkpts: Deque<Trkpt> lateinit var recent_displacement_trkpts: Deque<Trkpt>
var recent_trackpoints_for_mapview: MutableList<IGeoPoint> = mutableListOf()
var gpsLocationListenerRegistered: Boolean = false var gpsLocationListenerRegistered: Boolean = false
var networkLocationListenerRegistered: Boolean = false var networkLocationListenerRegistered: Boolean = false
var bound: Boolean = false var bound: Boolean = false
@ -197,14 +201,20 @@ class TrackerService: Service()
return return
} }
val trkpt = Trkpt(location=location) val trkpt = Trkpt(device_id=device_id, location=location)
trackbook.database.insert_trkpt(device_id, trkpt) trackbook.database.insert_trkpt(trkpt)
recent_trkpts.add(trkpt) recent_trkpts.add(trkpt)
while (recent_trkpts.size > RECENT_TRKPT_COUNT) while (recent_trkpts.size > RECENT_TRKPT_COUNT)
{ {
recent_trkpts.removeFirst() recent_trkpts.removeFirst()
} }
recent_trackpoints_for_mapview.add(trkpt)
while (recent_trackpoints_for_mapview.size > RECENT_TRKPT_COUNT)
{
recent_trackpoints_for_mapview.removeFirst()
}
recent_displacement_trkpts.add(trkpt) recent_displacement_trkpts.add(trkpt)
while (recent_displacement_trkpts.size > 5) while (recent_displacement_trkpts.size > 5)
{ {
@ -266,6 +276,7 @@ class TrackerService: Service()
trackbook.load_homepoints() trackbook.load_homepoints()
recent_trkpts = ArrayDeque<Trkpt>(RECENT_TRKPT_COUNT) recent_trkpts = ArrayDeque<Trkpt>(RECENT_TRKPT_COUNT)
recent_displacement_trkpts = ArrayDeque<Trkpt>(5) recent_displacement_trkpts = ArrayDeque<Trkpt>(5)
recent_trackpoints_for_mapview = mutableListOf()
use_gps_location = PreferencesHelper.load_location_gps() use_gps_location = PreferencesHelper.load_location_gps()
use_network_location = PreferencesHelper.load_location_network() use_network_location = PreferencesHelper.load_location_network()
device_id = PreferencesHelper.load_device_id() device_id = PreferencesHelper.load_device_id()

View file

@ -17,19 +17,23 @@
package org.y20k.trackbook package org.y20k.trackbook
import android.location.Location import android.location.Location
import org.osmdroid.api.IGeoPoint
import org.osmdroid.util.GeoPoint
import org.y20k.trackbook.helpers.getNumberOfSatellites import org.y20k.trackbook.helpers.getNumberOfSatellites
data class Trkpt( class Trkpt(
val device_id: String,
val provider: String, val provider: String,
val latitude: Double, latitude: Double,
val longitude: Double, longitude: Double,
val altitude: Double, altitude: Double,
val accuracy: Float, val accuracy: Float,
val time: Long, val time: Long,
val numberSatellites: Int = 0, val numberSatellites: Int = 0,
) ) : GeoPoint(latitude, longitude, altitude)
{ {
constructor(location: Location) : this ( constructor(device_id: String, location: Location) : this(
device_id=device_id,
provider=location.provider.toString(), provider=location.provider.toString(),
latitude=location.latitude, latitude=location.latitude,
longitude=location.longitude, longitude=location.longitude,
@ -48,4 +52,4 @@ data class Trkpt(
location.time = this.time location.time = this.time
return location return location
} }
} }

View file

@ -37,16 +37,10 @@ import java.text.DecimalFormat
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
fun createTrackOverlay(context: Context, map_view: MapView, trkpts: Collection<Trkpt>, trackingState: Int): SimpleFastPointOverlay fun createTrackOverlay(context: Context, map_view: MapView, geopoints: MutableList<IGeoPoint>, trackingState: Int): SimpleFastPointOverlay
{ {
Log.i("VOUSSOIR", "MapOverlayHelper.createTrackOverlay") Log.i("VOUSSOIR", "MapOverlayHelper.createTrackOverlay")
val trackpoints: MutableList<IGeoPoint> = mutableListOf() val pointTheme = SimplePointTheme(geopoints, false)
for (trkpt in trkpts)
{
val label = "${context.getString(R.string.marker_description_time)}: ${SimpleDateFormat.getTimeInstance(SimpleDateFormat.MEDIUM, Locale.getDefault()).format(trkpt.time)} | ${context.getString(R.string.marker_description_accuracy)}: ${DecimalFormat("#0.00").format(trkpt.accuracy)} (${trkpt.provider})"
trackpoints.add(LabelledGeoPoint(trkpt.latitude, trkpt.longitude, trkpt.altitude, label))
}
val pointTheme = SimplePointTheme(trackpoints, false)
val style = Paint() val style = Paint()
style.style = Paint.Style.FILL style.style = Paint.Style.FILL
style.color = if (trackingState == Keys.STATE_TRACKING_ACTIVE) context.getColor(R.color.default_red) else context.getColor(R.color.default_blue) style.color = if (trackingState == Keys.STATE_TRACKING_ACTIVE) context.getColor(R.color.default_red) else context.getColor(R.color.default_blue)
@ -60,23 +54,24 @@ fun createTrackOverlay(context: Context, map_view: MapView, trkpts: Collection<T
.setCellSize(12) // 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. .setCellSize(12) // 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.
var overlay = SimpleFastPointOverlay(pointTheme, overlayOptions) var overlay = SimpleFastPointOverlay(pointTheme, overlayOptions)
// overlay.setOnClickListener(object : SimpleFastPointOverlay.OnClickListener { overlay.setOnClickListener(object : SimpleFastPointOverlay.OnClickListener {
// override fun onClick(points: SimpleFastPointOverlay.PointAdapter?, point: Int?) override fun onClick(points: SimpleFastPointOverlay.PointAdapter?, point: Int?)
// { {
// if (points == null || point == null || point == 0) if (points == null || point == null || point == 0)
// { {
// return return
// } }
// Log.i("VOUSSOIR", "Clicked ${points[point]}") val trkpt = (points[point]) as Trkpt
// trackpoints.remove(points[point]) Log.i("VOUSSOIR", "Clicked ${trkpt.device_id} ${trkpt.time}")
// map_view.overlays.remove(overlay) // trackpoints.remove(points[point])
// overlay = SimpleFastPointOverlay(pointTheme, overlayOptions) // map_view.overlays.remove(overlay)
// overlay.setOnClickListener(this) // overlay = SimpleFastPointOverlay(pointTheme, overlayOptions)
// map_view.overlays.add(overlay) // overlay.setOnClickListener(this)
// map_view.postInvalidate() // map_view.overlays.add(overlay)
// return // map_view.postInvalidate()
// } return
// }) }
})
map_view.overlays.add(overlay) map_view.overlays.add(overlay)
return overlay return overlay

View file

@ -79,7 +79,35 @@
</org.osmdroid.views.MapView> </org.osmdroid.views.MapView>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/zoom_out_button"
style="@style/Widget.MaterialComponents.FloatingActionButton"
android:layout_width="wrap_content"
android:layout_height="56dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="70dp"
android:contentDescription="@string/descr_button_zoom_out"
android:src="@drawable/ic_zoom_out_24dp"
app:backgroundTint="@color/location_button_background"
app:fabSize="mini"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:tint="@color/location_button_icon" />
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/zoom_in_button"
style="@style/Widget.MaterialComponents.FloatingActionButton"
android:layout_width="wrap_content"
android:layout_height="56dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:contentDescription="@string/descr_button_zoom_in"
android:src="@drawable/ic_zoom_in_24dp"
app:backgroundTint="@color/location_button_background"
app:fabSize="mini"
app:layout_constraintBottom_toTopOf="@+id/zoom_out_button"
app:layout_constraintEnd_toEndOf="parent"
app:tint="@color/location_button_icon" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>