From 3b26be8f9997f5c644d7ffb3a76944f151e66325 Mon Sep 17 00:00:00 2001 From: lsagona Date: Tue, 25 Aug 2020 14:46:50 +0200 Subject: [PATCH] controle the time with slider --- .../application/controller/DataPanelController.kt | 2 +- .../application/controller/MapPanelController.kt | 30 +++++++++---- .../application/controller/MenuBarController.kt | 12 ++--- .../kotlin/application/controller/TimePanel.kt | 46 ++++++++++++++++++++ src/main/kotlin/application/model/Context.kt | 6 ++- src/main/kotlin/application/model/CurrentTime.kt | 5 +++ src/main/kotlin/application/model/MapState.kt | 7 +++ .../application/model/ObservableCurrentTime.kt | 20 +++++++++ .../kotlin/application/model/ObservableMapState.kt | 18 ++++++++ .../application/model/ObservableReplayState.kt | 20 +++++++++ .../kotlin/application/model/ObservableState.kt | 18 -------- src/main/kotlin/application/model/ReplayState.kt | 6 +++ src/main/kotlin/application/model/State.kt | 7 --- src/main/kotlin/application/model/StateListener.kt | 2 +- src/main/kotlin/application/model/Vessel.kt | 36 +++++++++++++-- .../kotlin/application/model/VesselGenerator.kt | 23 +++++++--- src/main/kotlin/map/MapDisplayer.kt | 9 ++++ src/main/resources/gui/mapPanel.fxml | 10 ++--- src/main/resources/gui/timePanel.fxml | 22 ++++++++++ 19 files changed, 243 insertions(+), 56 deletions(-) create mode 100644 src/main/kotlin/application/controller/TimePanel.kt create mode 100644 src/main/kotlin/application/model/CurrentTime.kt create mode 100644 src/main/kotlin/application/model/MapState.kt create mode 100644 src/main/kotlin/application/model/ObservableCurrentTime.kt create mode 100644 src/main/kotlin/application/model/ObservableMapState.kt create mode 100644 src/main/kotlin/application/model/ObservableReplayState.kt delete mode 100644 src/main/kotlin/application/model/ObservableState.kt create mode 100644 src/main/kotlin/application/model/ReplayState.kt delete mode 100644 src/main/kotlin/application/model/State.kt create mode 100644 src/main/resources/gui/timePanel.fxml diff --git a/src/main/kotlin/application/controller/DataPanelController.kt b/src/main/kotlin/application/controller/DataPanelController.kt index 3942558..ada250d 100644 --- a/src/main/kotlin/application/controller/DataPanelController.kt +++ b/src/main/kotlin/application/controller/DataPanelController.kt @@ -103,7 +103,7 @@ class DataPanelController : Initializable, SelectedVesselListener { } config.plotTitle = "" - config.setxAxisTitle("Date") + config.setxAxisTitle("Time (s)") config.setyAxisTitle(newValue.first) dataViewer.resetPlot() plotData.allTraces.clear() diff --git a/src/main/kotlin/application/controller/MapPanelController.kt b/src/main/kotlin/application/controller/MapPanelController.kt index 54fcd50..92c838b 100644 --- a/src/main/kotlin/application/controller/MapPanelController.kt +++ b/src/main/kotlin/application/controller/MapPanelController.kt @@ -1,7 +1,7 @@ package application.controller import application.model.* -import application.model.State.* +import application.model.MapState.* import javafx.fxml.FXML import javafx.fxml.Initializable import javafx.scene.layout.StackPane @@ -22,6 +22,8 @@ class MapPanelController : Initializable { setObservableVesselListener() setObservableSelectedVesselListener() setStateListener() + observableCurrentTime() + /*val completeFutureMap: CompletableFuture = mapView.displayMap(MapConfig()) completeFutureMap.whenComplete{ workerState, _ -> @@ -33,8 +35,8 @@ class MapPanelController : Initializable { } private fun setStateListener() { - observableState.listeners.add(object : StateListener { - override fun onValueChanged(newValue: State) { + observableMapState.listeners.add(object : StateListener { + override fun onValueChanged(newValue: MapState) { if (observableSelectedVessel.vessel.mmsi != null) { updateMap(observableSelectedVessel.vessel.mmsi!!) } else { @@ -44,16 +46,28 @@ class MapPanelController : Initializable { }) } + private fun observableCurrentTime() { + observableCurrentTime.listeners.add(object : CurrentTime{ + override fun onValueChanged(newValue: Int) { + updateMap() + } + }) + } + private fun updateMap() { - when (observableState.state) { - ALL_MESSAGES -> displayAllMessageOnMap(mapView) - CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView) - HEAT_MAP -> displayHeatMapOnMap(mapView) + if (observableIsReplayState.value){ + displayTargetedVessels(mapView) + } else { + when (observableMapState.state) { + ALL_MESSAGES -> displayAllMessageOnMap(mapView) + CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView) + HEAT_MAP -> displayHeatMapOnMap(mapView) + } } } private fun updateMap(selectedMMSI: String) { - when (observableState.state) { + when (observableMapState.state) { ALL_MESSAGES -> displayAllMessageOnMap(mapView, selectedMMSI) CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView, selectedMMSI) HEAT_MAP -> displayHeatMapOnMap(mapView, selectedMMSI) diff --git a/src/main/kotlin/application/controller/MenuBarController.kt b/src/main/kotlin/application/controller/MenuBarController.kt index 6d94861..2303835 100644 --- a/src/main/kotlin/application/controller/MenuBarController.kt +++ b/src/main/kotlin/application/controller/MenuBarController.kt @@ -1,8 +1,8 @@ package application.controller -import application.model.State.* +import application.model.MapState.* import application.model.createVesselCollection -import application.model.observableState +import application.model.observableMapState import application.model.observableVessel import javafx.event.EventHandler import javafx.fxml.FXML @@ -39,7 +39,7 @@ class MenuBarController : Initializable { setOnActionAllMessageButton() setOnActionClusteredMessageButton() setOnActionHeatMapButton() - observableState.state = CLUSTERED_MESSAGES + observableMapState.state = CLUSTERED_MESSAGES allMessages.isSelected = false clusteredMessage.isSelected = true heatMap.isSelected = false @@ -71,7 +71,7 @@ class MenuBarController : Initializable { private fun setOnActionAllMessageButton() { allMessages.onAction = EventHandler { - observableState.state = ALL_MESSAGES + observableMapState.state = ALL_MESSAGES allMessages.isSelected = true clusteredMessage.isSelected = false heatMap.isSelected = false @@ -80,7 +80,7 @@ class MenuBarController : Initializable { private fun setOnActionClusteredMessageButton() { clusteredMessage.onAction = EventHandler { - observableState.state = CLUSTERED_MESSAGES + observableMapState.state = CLUSTERED_MESSAGES heatMap.isSelected = false allMessages.isSelected = false clusteredMessage.isSelected = true @@ -89,7 +89,7 @@ class MenuBarController : Initializable { private fun setOnActionHeatMapButton() { heatMap.onAction = EventHandler { - observableState.state = HEAT_MAP + observableMapState.state = HEAT_MAP heatMap.isSelected = true clusteredMessage.isSelected = false allMessages.isSelected = false diff --git a/src/main/kotlin/application/controller/TimePanel.kt b/src/main/kotlin/application/controller/TimePanel.kt new file mode 100644 index 0000000..4e3a819 --- /dev/null +++ b/src/main/kotlin/application/controller/TimePanel.kt @@ -0,0 +1,46 @@ +package application.controller + +import application.model.* +import javafx.fxml.FXML +import javafx.fxml.Initializable +import javafx.scene.control.Button +import javafx.scene.control.Slider +import java.net.URL +import java.util.* + + +class TimePanel : Initializable { + + @FXML + var timeSlider = Slider() + + @FXML + var timeStop = Button() + + @FXML + var timePlay = Button() + + + override fun initialize(location: URL?, resources: ResourceBundle?) { + setSliderMinMax() + setSliderListener() + + + } + + private fun setSliderMinMax() { + observableVessel.listeners.add(object : MessageListener{ + override fun onValueChanged(newValue: MutableMap) { + timeSlider.max = Vessel.maxTime.toDouble() + timeSlider.min = Vessel.minTime.toDouble() + } + }) + } + + private fun setSliderListener() { + timeSlider.valueProperty().addListener { _, _, newValue -> + observableCurrentTime.value = newValue.toInt() + } + } + +} \ No newline at end of file diff --git a/src/main/kotlin/application/model/Context.kt b/src/main/kotlin/application/model/Context.kt index 35572cf..a3c6897 100644 --- a/src/main/kotlin/application/model/Context.kt +++ b/src/main/kotlin/application/model/Context.kt @@ -4,4 +4,8 @@ val observableVessel: ObservableVessel = ObservableVessel() val observableSelectedVessel: ObservableSelectedVessel = ObservableSelectedVessel() -val observableState: ObservableState = ObservableState() \ No newline at end of file +val observableMapState: ObservableMapState = ObservableMapState() + +val observableIsReplayState: ObservableReplayState = ObservableReplayState() + +val observableCurrentTime: ObservableCurrentTime = ObservableCurrentTime() \ No newline at end of file diff --git a/src/main/kotlin/application/model/CurrentTime.kt b/src/main/kotlin/application/model/CurrentTime.kt new file mode 100644 index 0000000..c563519 --- /dev/null +++ b/src/main/kotlin/application/model/CurrentTime.kt @@ -0,0 +1,5 @@ +package application.model + +interface CurrentTime { + fun onValueChanged(newValue: Int) +} diff --git a/src/main/kotlin/application/model/MapState.kt b/src/main/kotlin/application/model/MapState.kt new file mode 100644 index 0000000..6f56f0c --- /dev/null +++ b/src/main/kotlin/application/model/MapState.kt @@ -0,0 +1,7 @@ +package application.model + +enum class MapState { + ALL_MESSAGES, + CLUSTERED_MESSAGES, + HEAT_MAP +} \ No newline at end of file diff --git a/src/main/kotlin/application/model/ObservableCurrentTime.kt b/src/main/kotlin/application/model/ObservableCurrentTime.kt new file mode 100644 index 0000000..2b579ef --- /dev/null +++ b/src/main/kotlin/application/model/ObservableCurrentTime.kt @@ -0,0 +1,20 @@ +package application.model + +import kotlin.properties.Delegates + +class ObservableCurrentTime { + + val listeners: MutableList = mutableListOf() + + var value: Int by Delegates.observable( + initialValue = 0, + onChange = { _, _, new -> + run { + listeners.forEach { + it.onValueChanged(new) + } + } + } + ) + +} diff --git a/src/main/kotlin/application/model/ObservableMapState.kt b/src/main/kotlin/application/model/ObservableMapState.kt new file mode 100644 index 0000000..50e2271 --- /dev/null +++ b/src/main/kotlin/application/model/ObservableMapState.kt @@ -0,0 +1,18 @@ +package application.model + +import kotlin.properties.Delegates + +class ObservableMapState { + val listeners: MutableList = mutableListOf() + + var state: MapState by Delegates.observable( + initialValue = MapState.CLUSTERED_MESSAGES, + onChange = { _, _, new -> + run { + listeners.forEach { + it.onValueChanged(new) + } + } + } + ) +} diff --git a/src/main/kotlin/application/model/ObservableReplayState.kt b/src/main/kotlin/application/model/ObservableReplayState.kt new file mode 100644 index 0000000..fe1c97c --- /dev/null +++ b/src/main/kotlin/application/model/ObservableReplayState.kt @@ -0,0 +1,20 @@ +package application.model + +import kotlin.properties.Delegates + +class ObservableReplayState { + + val listeners: MutableList = mutableListOf() + + var value: Boolean by Delegates.observable( + initialValue = true, + onChange = { _, _, new -> + run { + listeners.forEach { + it.onValueChanged(new) + } + } + } + ) + +} diff --git a/src/main/kotlin/application/model/ObservableState.kt b/src/main/kotlin/application/model/ObservableState.kt deleted file mode 100644 index 51f6b37..0000000 --- a/src/main/kotlin/application/model/ObservableState.kt +++ /dev/null @@ -1,18 +0,0 @@ -package application.model - -import kotlin.properties.Delegates - -class ObservableState { - val listeners: MutableList = mutableListOf() - - var state: State by Delegates.observable( - initialValue = State.CLUSTERED_MESSAGES, - onChange = { _, _, new -> - run { - listeners.forEach { - it.onValueChanged(new) - } - } - } - ) -} diff --git a/src/main/kotlin/application/model/ReplayState.kt b/src/main/kotlin/application/model/ReplayState.kt new file mode 100644 index 0000000..987ee7d --- /dev/null +++ b/src/main/kotlin/application/model/ReplayState.kt @@ -0,0 +1,6 @@ +package application.model + +interface ReplayState { + fun onValueChanged(newValue: Boolean) + +} \ No newline at end of file diff --git a/src/main/kotlin/application/model/State.kt b/src/main/kotlin/application/model/State.kt deleted file mode 100644 index d8b22d5..0000000 --- a/src/main/kotlin/application/model/State.kt +++ /dev/null @@ -1,7 +0,0 @@ -package application.model - -enum class State { - ALL_MESSAGES, - CLUSTERED_MESSAGES, - HEAT_MAP -} \ No newline at end of file diff --git a/src/main/kotlin/application/model/StateListener.kt b/src/main/kotlin/application/model/StateListener.kt index bad9628..d0c39a1 100644 --- a/src/main/kotlin/application/model/StateListener.kt +++ b/src/main/kotlin/application/model/StateListener.kt @@ -1,5 +1,5 @@ package application.model interface StateListener { - fun onValueChanged(newValue: State) + fun onValueChanged(newValue: MapState) } diff --git a/src/main/kotlin/application/model/Vessel.kt b/src/main/kotlin/application/model/Vessel.kt index 0d3945b..bda8ad1 100644 --- a/src/main/kotlin/application/model/Vessel.kt +++ b/src/main/kotlin/application/model/Vessel.kt @@ -1,12 +1,37 @@ package application.model -import java.time.LocalDateTime -import java.time.ZoneOffset import java.util.* class Vessel(val mmsi: String?) { - val messages: SortedMap = sortedMapOf() + val messages: SortedMap = sortedMapOf() + var messageToDisplay: Message? = null + get() { + // messages.forEach { (key, value) -> +// if(observableCurrentTime.value < key) { +// field = value +// return@forEach +// } +// } + field = messages.asSequence().map{ it }.firstOrNull {observableCurrentTime.value < it.key}.let { it?.value } + return field + } + + // val timesNormalized : SortedMap = sortedMapOf() + +// fun getAllNormalizedDate(): SortedMap { +// var offset: Long? = null +// if(timesNormalized.size == 0){ +// messages.keys.forEach { +// val currentTime = it.toEpochSecond(ZoneOffset.UTC) +// if(offset == null){ +// offset = currentTime +// } +// timesNormalized[currentTime - offset!!]= it +// } +// } +// return timesNormalized +// } fun getAllTime(): ArrayList { val timeList = arrayListOf() @@ -134,4 +159,9 @@ class Vessel(val mmsi: String?) { return res } + companion object{ + var maxTime: Long = 0 + var minTime: Long = 0 + } + } \ No newline at end of file diff --git a/src/main/kotlin/application/model/VesselGenerator.kt b/src/main/kotlin/application/model/VesselGenerator.kt index cfd09f6..522fa3a 100644 --- a/src/main/kotlin/application/model/VesselGenerator.kt +++ b/src/main/kotlin/application/model/VesselGenerator.kt @@ -1,25 +1,36 @@ package application.model import java.io.File +import java.time.ZoneOffset import java.util.* -import kotlin.collections.ArrayList -fun createVesselCollection(file: File) : SortedMap { - val messages : ArrayList = arrayListOf() +fun createVesselCollection(file: File): SortedMap { + val messages: ArrayList = arrayListOf() val vessels: SortedMap = sortedMapOf() + var maxTime: Long = 0 + var minTime: Long = Long.MAX_VALUE file.forEachLine { val arrayMessage = it.split(",") if (arrayMessage[0].toIntOrNull() !== null) { val message = Message(arrayMessage) messages.add(message) - if (!vessels.containsKey(message.mmsi.value)){ + if (!vessels.containsKey(message.mmsi.value)) { vessels[message.mmsi.value] = Vessel(message.mmsi.value!!) } - vessels[message.mmsi.value]?.messages?.set(message.time.value, message) + val time = message.time.value.toEpochSecond(ZoneOffset.UTC) + vessels[message.mmsi.value]?.messages?.set(time, message) + if (time > maxTime) { + maxTime = time + } + if (time < minTime){ + minTime = time + } } - } + Vessel.maxTime = maxTime + Vessel.minTime = minTime + return vessels } diff --git a/src/main/kotlin/map/MapDisplayer.kt b/src/main/kotlin/map/MapDisplayer.kt index bcb5429..cd9dd6c 100644 --- a/src/main/kotlin/map/MapDisplayer.kt +++ b/src/main/kotlin/map/MapDisplayer.kt @@ -67,6 +67,15 @@ fun displayClusterMessageOnMap(map: LeafletMapView) { map.execScript("myMap.addLayer(markerClusters);") } +fun displayTargetedVessels(map: LeafletMapView) { + clearMap(map) + observableVessel.vessels.forEach { (_, value) -> + val message = value.messageToDisplay ?: return@forEach + map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}));") + } + map.execScript("myMap.addLayer(markerClusters);") +} + fun displayClusterMessageOnMap(map: LeafletMapView, selectedMMSI: String) { clearMap(map) observableVessel.vessels.forEach { (_, value) -> diff --git a/src/main/resources/gui/mapPanel.fxml b/src/main/resources/gui/mapPanel.fxml index 1414910..98b7f56 100644 --- a/src/main/resources/gui/mapPanel.fxml +++ b/src/main/resources/gui/mapPanel.fxml @@ -1,8 +1,8 @@ - + - \ No newline at end of file + + + + diff --git a/src/main/resources/gui/timePanel.fxml b/src/main/resources/gui/timePanel.fxml new file mode 100644 index 0000000..f3f39bb --- /dev/null +++ b/src/main/resources/gui/timePanel.fxml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + -- 1.7.10.4