diff options
author | Iván Ávalos <avalos@disroot.org> | 2022-01-11 04:12:45 -0600 |
---|---|---|
committer | Iván Ávalos <avalos@disroot.org> | 2022-01-11 04:12:45 -0600 |
commit | 8878254a4a3af9d15262fd2553c7b2db0a7bd8bc (patch) | |
tree | 4918c7ad01bf2c74bf5492f3c443408c733b0ab6 | |
parent | 7629861e4e2b7e52b93f817426cb940c8f075b59 (diff) | |
download | etbsa-trackermap-mobile-8878254a4a3af9d15262fd2553c7b2db0a7bd8bc.tar.gz etbsa-trackermap-mobile-8878254a4a3af9d15262fd2553c7b2db0a7bd8bc.tar.bz2 etbsa-trackermap-mobile-8878254a4a3af9d15262fd2553c7b2db0a7bd8bc.zip |
Implemented polygon geofences
5 files changed, 98 insertions, 17 deletions
diff --git a/androidApp/build.gradle.kts b/androidApp/build.gradle.kts index 5736c84..22d894a 100644 --- a/androidApp/build.gradle.kts +++ b/androidApp/build.gradle.kts @@ -38,5 +38,6 @@ dependencies { implementation("io.ktor:ktor-client-serialization:1.6.6") implementation("com.github.zerobranch:SwipeLayout:1.3.1") implementation("com.github.addisonElliott:SegmentedButton:3.1.9") + implementation("mil.nga.sf:sf-wkt:1.0.1") implementation(group = "", name = "WhirlyGlobeMaply", ext = "aar") }
\ No newline at end of file diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsFragment.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsFragment.kt index b701990..43f879d 100644 --- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsFragment.kt +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/details/reports/UnitReportsFragment.kt @@ -17,7 +17,6 @@ import mx.trackermap.TrackerMap.android.databinding.UnitDetailsReportsBinding import mx.trackermap.TrackerMap.android.details.UnitDetailsAdapter import mx.trackermap.TrackerMap.android.map.MapFragment import mx.trackermap.TrackerMap.android.map.MarkerTransformations -import mx.trackermap.TrackerMap.client.models.Event import mx.trackermap.TrackerMap.client.models.EventInformation import mx.trackermap.TrackerMap.client.models.Position import mx.trackermap.TrackerMap.client.models.Stop @@ -119,8 +118,9 @@ class UnitReportsFragment : Fragment() { ) } - unitReportsViewModel.geofences.observe(this) { geofences -> + unitReportsViewModel.geofences.observe(viewLifecycleOwner) { geofences -> Log.d("UnitReportsFragment", "Success $geofences") + reportsMapFragment.displayGeofences(geofences.values.toTypedArray()) } } diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapFragment.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapFragment.kt index 41cfd50..fa0a56e 100644 --- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapFragment.kt +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/MapFragment.kt @@ -10,8 +10,12 @@ import android.view.ViewGroup import androidx.core.content.res.ResourcesCompat import androidx.core.graphics.drawable.toBitmap import com.mousebird.maply.* +import mil.nga.sf.Polygon +import mil.nga.sf.util.SFException +import mil.nga.sf.wkt.GeometryReader import java.io.File import mx.trackermap.TrackerMap.android.R +import mx.trackermap.TrackerMap.client.models.Geofence import mx.trackermap.TrackerMap.utils.MarkerType typealias MarkerCallback = (Int?) -> Unit @@ -28,6 +32,7 @@ class MapFragment : GlobeMapFragment() { var markerCallback: MarkerCallback? = null private val objects = mutableListOf<ComponentObject>() + private val geofenceObjects = mutableListOf<ComponentObject>() override fun chooseDisplayType(): MapDisplayType { return MapDisplayType.Map @@ -95,12 +100,20 @@ class MapFragment : GlobeMapFragment() { markerCallback?.let { it(null) } } - private fun clear() { - mapControl.removeObjects( - objects, - ThreadMode.ThreadAny - ) - objects.clear() + private fun clear(geofences: Boolean = false) { + if (geofences) { + mapControl.removeObjects( + geofenceObjects, + ThreadMode.ThreadAny + ) + geofenceObjects.clear() + } else { + mapControl.removeObjects( + objects, + ThreadMode.ThreadAny + ) + objects.clear() + } } fun display(markers: Array<Marker>, isReport: Boolean) { @@ -108,6 +121,15 @@ class MapFragment : GlobeMapFragment() { clear() + val points = markers.map { marker -> + Point2d.FromDegrees(marker.longitude, marker.latitude) + }.toTypedArray() + + val vectorInfo = VectorInfo() + vectorInfo.setColor(Color.GREEN) + vectorInfo.setLineWidth(20.0f) + + /* Draw markers for positions */ val screenMarkers = markers.mapIndexed { i, marker -> val screenMarker = ScreenMarker() val markerSize = Point2d(144.0, 144.0) @@ -133,19 +155,11 @@ class MapFragment : GlobeMapFragment() { ThreadMode.ThreadAny )) - val points = markers.map { marker -> - Point2d.FromDegrees(marker.longitude, marker.latitude) - }.toTypedArray() - - // Draw polyline for report + /* Draw polyline for report */ if (isReport && markers.isNotEmpty()) { val vector = VectorObject() vector.addAreal(points) - val vectorInfo = VectorInfo() - vectorInfo.setColor(Color.GREEN) - vectorInfo.setLineWidth(25.0f) - objects.add(mapControl.addVector( vector, vectorInfo, @@ -168,6 +182,54 @@ class MapFragment : GlobeMapFragment() { } } + fun displayGeofences(geofences: Array<Geofence>) { + Log.d("MapFragment", "Displaying geofences") + + clear(true) + + val vectorInfoGeofence = VectorInfo() + vectorInfoGeofence.setColor(Color.BLUE) + vectorInfoGeofence.setLineWidth(10.0f) + + val shapes = mutableListOf<Shape>() + val vectors = mutableListOf<VectorObject>() + + geofences.forEach { geofence -> + geofence.area?.let { area -> + Log.d("MainFragment", "Geofence ${geofence.name} = ${geofence.area}") + try { + val geometry = GeometryReader.readGeometry(area) + if (!geometry.isEmpty) { + when (geometry) { + is Polygon -> { + val vector = VectorObject() + vector.addAreal(geometry.exteriorRing.points.map { + Point2d.FromDegrees(it.y, it.x) + }.toTypedArray()) + + vectors.add(vector) + } + } + } + } catch (e: SFException) { + e.printStackTrace() + } + } + } + + geofenceObjects.add(mapControl.addShapes( + shapes, + ShapeInfo(), + ThreadMode.ThreadAny + )) + + geofenceObjects.add(mapControl.addVectors( + vectors, + vectorInfoGeofence, + ThreadMode.ThreadAny + )) + } + fun focusOn(latitude: Double, longitude: Double, zoom: Double = 0.0000144, animated: Boolean = true) { val lat = latitude * Math.PI / 180 val lon = longitude * Math.PI / 180 diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/UnitMapFragment.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/UnitMapFragment.kt index 3c34abb..4f98a96 100644 --- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/UnitMapFragment.kt +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/map/UnitMapFragment.kt @@ -124,6 +124,10 @@ class UnitMapFragment : Fragment() { unitsMapFragment.focusOn(unit.position!!.latitude!!, unit.position!!.longitude!!) } } + + unitsViewModel.geofences.observe(viewLifecycleOwner) { geofences -> + unitsMapFragment.displayGeofences(geofences.values.toTypedArray()) + } } private fun itemAction(unit: UnitInformation, action: Action) { diff --git a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsViewModel.kt b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsViewModel.kt index ca05736..c0dc7c6 100644 --- a/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsViewModel.kt +++ b/androidApp/src/main/java/mx/trackermap/TrackerMap/android/units/UnitsViewModel.kt @@ -5,7 +5,9 @@ import androidx.lifecycle.* import kotlinx.coroutines.DelicateCoroutinesApi import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch +import mx.trackermap.TrackerMap.client.models.Geofence import mx.trackermap.TrackerMap.client.models.UnitInformation +import mx.trackermap.TrackerMap.controllers.GeofencesController import mx.trackermap.TrackerMap.controllers.UnitsController import org.koin.core.component.KoinComponent import org.koin.core.component.inject @@ -20,22 +22,28 @@ class UnitsViewModel( } private val unitsController: UnitsController by inject() + private val geofenceController: GeofencesController by inject() private var _searchQuery = savedStateHandle.getLiveData("searchQuery", "") private var _unitsDisplayMode = MutableLiveData(UnitsDisplayMode.LIST) private var _units = MutableLiveData<List<UnitInformation>>() private var _selectedUnit = MutableLiveData<UnitInformation?>() + private var _geofences = MutableLiveData<Map<Int, Geofence>>() val searchQuery: LiveData<String> get() = _searchQuery val unitsDisplayMode: LiveData<UnitsDisplayMode> get() = _unitsDisplayMode val units: LiveData<List<UnitInformation>> get() = _units val selectedUnit: LiveData<UnitInformation?> get() = _selectedUnit + val geofences: LiveData<Map<Int, Geofence>> get() = _geofences init { Log.d("UnitsViewModel", "Initializing Units View Model") viewModelScope.launch { setupObservers() } + viewModelScope.launch { + setupGeofenceObserver() + } } private suspend fun setupObservers() { @@ -45,6 +53,12 @@ class UnitsViewModel( } } + private suspend fun setupGeofenceObserver() { + geofenceController.geofencesFlow.collect { + this._geofences.postValue(it) + } + } + fun selectUnit(unit: UnitInformation) { Log.d("UnitsViewModel", "Selecting unit ${unit.device.name}") _selectedUnit.postValue(unit) |