Commit 3b26be8f9997f5c644d7ffb3a76944f151e66325
1 parent
8108656dd1
Exists in
master
and in
1 other branch
controle the time with slider
Showing 19 changed files with 243 additions and 56 deletions Inline Diff
- src/main/kotlin/application/controller/DataPanelController.kt
- src/main/kotlin/application/controller/MapPanelController.kt
- src/main/kotlin/application/controller/MenuBarController.kt
- src/main/kotlin/application/controller/TimePanel.kt
- src/main/kotlin/application/model/Context.kt
- src/main/kotlin/application/model/CurrentTime.kt
- src/main/kotlin/application/model/MapState.kt
- src/main/kotlin/application/model/ObservableCurrentTime.kt
- src/main/kotlin/application/model/ObservableMapState.kt
- src/main/kotlin/application/model/ObservableReplayState.kt
- src/main/kotlin/application/model/ObservableState.kt
- src/main/kotlin/application/model/ReplayState.kt
- src/main/kotlin/application/model/State.kt
- src/main/kotlin/application/model/StateListener.kt
- src/main/kotlin/application/model/Vessel.kt
- src/main/kotlin/application/model/VesselGenerator.kt
- src/main/kotlin/map/MapDisplayer.kt
- src/main/resources/gui/mapPanel.fxml
- src/main/resources/gui/timePanel.fxml
src/main/kotlin/application/controller/DataPanelController.kt
View file @
3b26be8
| package application.controller | 1 | 1 | package application.controller | |
| 2 | 2 | |||
| import application.model.* | 3 | 3 | import application.model.* | |
| import javafx.collections.FXCollections | 4 | 4 | import javafx.collections.FXCollections | |
| import javafx.collections.ObservableList | 5 | 5 | import javafx.collections.ObservableList | |
| import javafx.fxml.FXML | 6 | 6 | import javafx.fxml.FXML | |
| import javafx.fxml.Initializable | 7 | 7 | import javafx.fxml.Initializable | |
| import javafx.scene.control.ListCell | 8 | 8 | import javafx.scene.control.ListCell | |
| import javafx.scene.control.ListView | 9 | 9 | import javafx.scene.control.ListView | |
| import org.charts.dataviewer.api.config.DataViewerConfiguration | 10 | 10 | import org.charts.dataviewer.api.config.DataViewerConfiguration | |
| import org.charts.dataviewer.api.data.PlotData | 11 | 11 | import org.charts.dataviewer.api.data.PlotData | |
| import org.charts.dataviewer.api.trace.ScatterTrace | 12 | 12 | import org.charts.dataviewer.api.trace.ScatterTrace | |
| import org.charts.dataviewer.javafx.JavaFxDataViewer | 13 | 13 | import org.charts.dataviewer.javafx.JavaFxDataViewer | |
| import org.charts.dataviewer.utils.TraceColour | 14 | 14 | import org.charts.dataviewer.utils.TraceColour | |
| import org.charts.dataviewer.utils.TraceVisibility | 15 | 15 | import org.charts.dataviewer.utils.TraceVisibility | |
| import java.net.URL | 16 | 16 | import java.net.URL | |
| import java.util.* | 17 | 17 | import java.util.* | |
| 18 | 18 | |||
| 19 | 19 | |||
| class DataPanelController : Initializable, SelectedVesselListener { | 20 | 20 | class DataPanelController : Initializable, SelectedVesselListener { | |
| private var dataList: ObservableList<Pair<String, ArrayList<MessageData?>>> = FXCollections.observableArrayList() | 21 | 21 | private var dataList: ObservableList<Pair<String, ArrayList<MessageData?>>> = FXCollections.observableArrayList() | |
| private lateinit var timeData: ArrayList<MessageData?> | 22 | 22 | private lateinit var timeData: ArrayList<MessageData?> | |
| 23 | 23 | |||
| 24 | 24 | |||
| @FXML | 25 | 25 | @FXML | |
| var dataListView = ListView<Pair<String, ArrayList<MessageData?>>>() | 26 | 26 | var dataListView = ListView<Pair<String, ArrayList<MessageData?>>>() | |
| 27 | 27 | |||
| @FXML | 28 | 28 | @FXML | |
| var dataViewer = JavaFxDataViewer() | 29 | 29 | var dataViewer = JavaFxDataViewer() | |
| 30 | 30 | |||
| 31 | 31 | |||
| override fun initialize(location: URL?, resources: ResourceBundle?) { | 32 | 32 | override fun initialize(location: URL?, resources: ResourceBundle?) { | |
| setObservableSelectedVesselListener() | 33 | 33 | setObservableSelectedVesselListener() | |
| dataListView.items = dataList | 34 | 34 | dataListView.items = dataList | |
| 35 | 35 | |||
| 36 | 36 | |||
| val plotData = PlotData() | 37 | 37 | val plotData = PlotData() | |
| val config = DataViewerConfiguration() | 38 | 38 | val config = DataViewerConfiguration() | |
| config.showLegend(true) | 39 | 39 | config.showLegend(true) | |
| config.setLegendInsidePlot(false) | 40 | 40 | config.setLegendInsidePlot(false) | |
| 41 | 41 | |||
| dataListView.setCellFactory { | 42 | 42 | dataListView.setCellFactory { | |
| object : ListCell<Pair<String, ArrayList<MessageData?>>?>() { | 43 | 43 | object : ListCell<Pair<String, ArrayList<MessageData?>>?>() { | |
| override fun updateItem(item: Pair<String, ArrayList<MessageData?>>?, empty: Boolean) { | 44 | 44 | override fun updateItem(item: Pair<String, ArrayList<MessageData?>>?, empty: Boolean) { | |
| super.updateItem(item, empty) | 45 | 45 | super.updateItem(item, empty) | |
| text = if (empty) { | 46 | 46 | text = if (empty) { | |
| null | 47 | 47 | null | |
| } else { | 48 | 48 | } else { | |
| item?.first | 49 | 49 | item?.first | |
| } | 50 | 50 | } | |
| } | 51 | 51 | } | |
| } | 52 | 52 | } | |
| } | 53 | 53 | } | |
| 54 | 54 | |||
| dataListView.selectionModel.selectedItemProperty().addListener { _, _, newValue -> | 55 | 55 | dataListView.selectionModel.selectedItemProperty().addListener { _, _, newValue -> | |
| if (newValue == null) { | 56 | 56 | if (newValue == null) { | |
| plotData.allTraces.clear() | 57 | 57 | plotData.allTraces.clear() | |
| config.setxAxisTitle("") | 58 | 58 | config.setxAxisTitle("") | |
| config.setyAxisTitle("") | 59 | 59 | config.setyAxisTitle("") | |
| dataViewer.updateConfiguration(config) | 60 | 60 | dataViewer.updateConfiguration(config) | |
| 61 | 61 | |||
| dataViewer.resetPlot() | 62 | 62 | dataViewer.resetPlot() | |
| 63 | 63 | |||
| return@addListener | 64 | 64 | return@addListener | |
| } | 65 | 65 | } | |
| 66 | 66 | |||
| val getValueVisitorX = GetValueVisitor() | 67 | 67 | val getValueVisitorX = GetValueVisitor() | |
| val getValueVisitorY = GetValueVisitor() | 68 | 68 | val getValueVisitorY = GetValueVisitor() | |
| 69 | 69 | |||
| val arrayListStringX = arrayListOf<String>() | 70 | 70 | val arrayListStringX = arrayListOf<String>() | |
| val arrayListDoubleX = arrayListOf<Double>() | 71 | 71 | val arrayListDoubleX = arrayListOf<Double>() | |
| val arrayListStringY = arrayListOf<String>() | 72 | 72 | val arrayListStringY = arrayListOf<String>() | |
| val arrayListDoubleY = arrayListOf<Double>() | 73 | 73 | val arrayListDoubleY = arrayListOf<Double>() | |
| 74 | 74 | |||
| for (x in 0 until newValue.second.size) { | 75 | 75 | for (x in 0 until newValue.second.size) { | |
| timeData[x]?.accept(getValueVisitorX) | 76 | 76 | timeData[x]?.accept(getValueVisitorX) | |
| newValue.second[x]?.accept(getValueVisitorY) | 77 | 77 | newValue.second[x]?.accept(getValueVisitorY) | |
| 78 | 78 | |||
| if (getValueVisitorY.value.toDoubleOrNull() == null) { | 79 | 79 | if (getValueVisitorY.value.toDoubleOrNull() == null) { | |
| arrayListStringX.add(getValueVisitorX.value) | 80 | 80 | arrayListStringX.add(getValueVisitorX.value) | |
| arrayListStringY.add(getValueVisitorY.value) | 81 | 81 | arrayListStringY.add(getValueVisitorY.value) | |
| } else { | 82 | 82 | } else { | |
| arrayListStringX.add(getValueVisitorX.value) | 83 | 83 | arrayListStringX.add(getValueVisitorX.value) | |
| arrayListDoubleY.add(getValueVisitorY.value.toDouble()) | 84 | 84 | arrayListDoubleY.add(getValueVisitorY.value.toDouble()) | |
| } | 85 | 85 | } | |
| } | 86 | 86 | } | |
| 87 | 87 | |||
| val scatterTrace = ScatterTrace<Any>() | 88 | 88 | val scatterTrace = ScatterTrace<Any>() | |
| scatterTrace.traceColour = TraceColour.RED | 89 | 89 | scatterTrace.traceColour = TraceColour.RED | |
| scatterTrace.traceVisibility = TraceVisibility.TRUE | 90 | 90 | scatterTrace.traceVisibility = TraceVisibility.TRUE | |
| 91 | 91 | |||
| val serieStringX: Array<String> = arrayListStringX.toTypedArray() | 92 | 92 | val serieStringX: Array<String> = arrayListStringX.toTypedArray() | |
| // val serieDoubleX: Array<Double> = arrayListDoubleX.toTypedArray() | 93 | 93 | // val serieDoubleX: Array<Double> = arrayListDoubleX.toTypedArray() | |
| val serieStringY: Array<String> = arrayListStringY.toTypedArray() | 94 | 94 | val serieStringY: Array<String> = arrayListStringY.toTypedArray() | |
| val serieDoubleY: Array<Double> = arrayListDoubleY.toTypedArray() | 95 | 95 | val serieDoubleY: Array<Double> = arrayListDoubleY.toTypedArray() | |
| 96 | 96 | |||
| if (getValueVisitorY.value.toDoubleOrNull() == null) { | 97 | 97 | if (getValueVisitorY.value.toDoubleOrNull() == null) { | |
| scatterTrace.setxArray(serieStringX) | 98 | 98 | scatterTrace.setxArray(serieStringX) | |
| scatterTrace.setyArray(serieStringY) | 99 | 99 | scatterTrace.setyArray(serieStringY) | |
| } else { | 100 | 100 | } else { | |
| scatterTrace.setxArray(serieStringX) | 101 | 101 | scatterTrace.setxArray(serieStringX) | |
| scatterTrace.setyArray(serieDoubleY) | 102 | 102 | scatterTrace.setyArray(serieDoubleY) | |
| } | 103 | 103 | } | |
| 104 | 104 | |||
| config.plotTitle = "" | 105 | 105 | config.plotTitle = "" | |
| config.setxAxisTitle("Date") | 106 | 106 | config.setxAxisTitle("Time (s)") | |
| config.setyAxisTitle(newValue.first) | 107 | 107 | config.setyAxisTitle(newValue.first) | |
| dataViewer.resetPlot() | 108 | 108 | dataViewer.resetPlot() | |
| plotData.allTraces.clear() | 109 | 109 | plotData.allTraces.clear() | |
| plotData.addTrace(scatterTrace) | 110 | 110 | plotData.addTrace(scatterTrace) | |
| dataViewer.updateConfiguration(config) | 111 | 111 | dataViewer.updateConfiguration(config) | |
| dataViewer.updatePlot(plotData) | 112 | 112 | dataViewer.updatePlot(plotData) | |
| 113 | 113 | |||
| } | 114 | 114 | } | |
| 115 | 115 | |||
| plotData.allTraces.clear() | 116 | 116 | plotData.allTraces.clear() | |
| config.setxAxisTitle("") | 117 | 117 | config.setxAxisTitle("") | |
| config.setyAxisTitle("") | 118 | 118 | config.setyAxisTitle("") | |
| config.plotTitle = "" | 119 | 119 | config.plotTitle = "" | |
| dataViewer.updateConfiguration(config) | 120 | 120 | dataViewer.updateConfiguration(config) | |
| dataViewer.updatePlot(plotData) | 121 | 121 | dataViewer.updatePlot(plotData) | |
| 122 | 122 | |||
| } | 123 | 123 | } | |
| 124 | 124 | |||
| private fun setObservableSelectedVesselListener() { | 125 | 125 | private fun setObservableSelectedVesselListener() { | |
| observableSelectedVessel.listeners.add(this) | 126 | 126 | observableSelectedVessel.listeners.add(this) | |
| } | 127 | 127 | } | |
| 128 | 128 | |||
| private fun populateTime(vessel: Vessel): ArrayList<MessageData?> { | 129 | 129 | private fun populateTime(vessel: Vessel): ArrayList<MessageData?> { | |
| val allTime: ArrayList<MessageData?> = vessel.getAllTime() | 130 | 130 | val allTime: ArrayList<MessageData?> = vessel.getAllTime() | |
| allTime.sortBy { (it as Time).value } | 131 | 131 | allTime.sortBy { (it as Time).value } | |
| 132 | 132 | |||
| return allTime | 133 | 133 | return allTime | |
| } | 134 | 134 | } | |
| 135 | 135 | |||
| private fun populateLatitude(vessel: Vessel): ArrayList<MessageData?> { | 136 | 136 | private fun populateLatitude(vessel: Vessel): ArrayList<MessageData?> { | |
| val allLatitude: ArrayList<MessageData?> = vessel.getAllLatitude() | 137 | 137 | val allLatitude: ArrayList<MessageData?> = vessel.getAllLatitude() | |
| allLatitude.sortBy { (it as Latitude).value } | 138 | 138 | allLatitude.sortBy { (it as Latitude).value } | |
| 139 | 139 | |||
| return allLatitude | 140 | 140 | return allLatitude | |
| } | 141 | 141 | } | |
| 142 | 142 | |||
| private fun populateLongitude(vessel: Vessel): ArrayList<MessageData?> { | 143 | 143 | private fun populateLongitude(vessel: Vessel): ArrayList<MessageData?> { | |
| val allLongitude: ArrayList<MessageData?> = vessel.getAllLongitude() | 144 | 144 | val allLongitude: ArrayList<MessageData?> = vessel.getAllLongitude() | |
| allLongitude.sortBy { (it as Longitude).value } | 145 | 145 | allLongitude.sortBy { (it as Longitude).value } | |
| 146 | 146 | |||
| return allLongitude | 147 | 147 | return allLongitude | |
| } | 148 | 148 | } | |
| 149 | 149 | |||
| private fun populateSpeedOverGround(vessel: Vessel): ArrayList<MessageData?> { | 150 | 150 | private fun populateSpeedOverGround(vessel: Vessel): ArrayList<MessageData?> { | |
| val allSpeedOverGround: ArrayList<MessageData?> = vessel.getAllSpeedOverGround() | 151 | 151 | val allSpeedOverGround: ArrayList<MessageData?> = vessel.getAllSpeedOverGround() | |
| allSpeedOverGround.sortBy { (it as SpeedOverGround).value } | 152 | 152 | allSpeedOverGround.sortBy { (it as SpeedOverGround).value } | |
| 153 | 153 | |||
| return allSpeedOverGround | 154 | 154 | return allSpeedOverGround | |
| } | 155 | 155 | } | |
| 156 | 156 | |||
| private fun populateCourseOverGround(vessel: Vessel): ArrayList<MessageData?> { | 157 | 157 | private fun populateCourseOverGround(vessel: Vessel): ArrayList<MessageData?> { | |
| val allCourseOverGround: ArrayList<MessageData?> = vessel.getAllCourseOverGround() | 158 | 158 | val allCourseOverGround: ArrayList<MessageData?> = vessel.getAllCourseOverGround() | |
| allCourseOverGround.sortBy { (it as CourseOverGround).value } | 159 | 159 | allCourseOverGround.sortBy { (it as CourseOverGround).value } | |
| 160 | 160 | |||
| return allCourseOverGround | 161 | 161 | return allCourseOverGround | |
| } | 162 | 162 | } | |
| 163 | 163 | |||
| private fun populateHeading(vessel: Vessel): ArrayList<MessageData?> { | 164 | 164 | private fun populateHeading(vessel: Vessel): ArrayList<MessageData?> { | |
| val allHeading: ArrayList<MessageData?> = vessel.getAllHeading() | 165 | 165 | val allHeading: ArrayList<MessageData?> = vessel.getAllHeading() | |
| allHeading.sortBy { (it as Heading).value } | 166 | 166 | allHeading.sortBy { (it as Heading).value } | |
| 167 | 167 | |||
| return allHeading | 168 | 168 | return allHeading | |
| } | 169 | 169 | } | |
| 170 | 170 | |||
| private fun populateVesselName(vessel: Vessel): ArrayList<MessageData?> { | 171 | 171 | private fun populateVesselName(vessel: Vessel): ArrayList<MessageData?> { | |
| val allVesselName: ArrayList<MessageData?> = vessel.getAllVesselName() | 172 | 172 | val allVesselName: ArrayList<MessageData?> = vessel.getAllVesselName() | |
| allVesselName.sortBy { (it as VesselName).value } | 173 | 173 | allVesselName.sortBy { (it as VesselName).value } | |
| 174 | 174 | |||
| return allVesselName | 175 | 175 | return allVesselName | |
| } | 176 | 176 | } | |
| 177 | 177 | |||
| private fun populateIMO(vessel: Vessel): ArrayList<MessageData?> { | 178 | 178 | private fun populateIMO(vessel: Vessel): ArrayList<MessageData?> { | |
| val allIMO: ArrayList<MessageData?> = vessel.getAllIMO() | 179 | 179 | val allIMO: ArrayList<MessageData?> = vessel.getAllIMO() | |
| allIMO.sortBy { (it as IMO).value } | 180 | 180 | allIMO.sortBy { (it as IMO).value } | |
| 181 | 181 | |||
| return allIMO | 182 | 182 | return allIMO | |
| } | 183 | 183 | } | |
| 184 | 184 | |||
| private fun populateCallSign(vessel: Vessel): ArrayList<MessageData?> { | 185 | 185 | private fun populateCallSign(vessel: Vessel): ArrayList<MessageData?> { | |
| val allCallSign: ArrayList<MessageData?> = vessel.getAllCallSign() | 186 | 186 | val allCallSign: ArrayList<MessageData?> = vessel.getAllCallSign() | |
| allCallSign.sortBy { (it as CallSign).value } | 187 | 187 | allCallSign.sortBy { (it as CallSign).value } | |
| 188 | 188 | |||
| return allCallSign | 189 | 189 | return allCallSign | |
| } | 190 | 190 | } | |
| 191 | 191 | |||
| private fun populateVesselType(vessel: Vessel): ArrayList<MessageData?> { | 192 | 192 | private fun populateVesselType(vessel: Vessel): ArrayList<MessageData?> { | |
| val allVesselType: ArrayList<MessageData?> = vessel.getAllVesselType() | 193 | 193 | val allVesselType: ArrayList<MessageData?> = vessel.getAllVesselType() | |
| allVesselType.sortBy { (it as VesselType).value } | 194 | 194 | allVesselType.sortBy { (it as VesselType).value } | |
| 195 | 195 | |||
| return allVesselType | 196 | 196 | return allVesselType | |
| } | 197 | 197 | } | |
| 198 | 198 | |||
| private fun populateStatus(vessel: Vessel): ArrayList<MessageData?> { | 199 | 199 | private fun populateStatus(vessel: Vessel): ArrayList<MessageData?> { | |
| val allStatus: ArrayList<MessageData?> = vessel.getAllStatus() | 200 | 200 | val allStatus: ArrayList<MessageData?> = vessel.getAllStatus() | |
| allStatus.sortBy { (it as Status).value } | 201 | 201 | allStatus.sortBy { (it as Status).value } |
src/main/kotlin/application/controller/MapPanelController.kt
View file @
3b26be8
| package application.controller | 1 | 1 | package application.controller | |
| 2 | 2 | |||
| import application.model.* | 3 | 3 | import application.model.* | |
| import application.model.State.* | 4 | 4 | import application.model.MapState.* | |
| import javafx.fxml.FXML | 5 | 5 | import javafx.fxml.FXML | |
| import javafx.fxml.Initializable | 6 | 6 | import javafx.fxml.Initializable | |
| import javafx.scene.layout.StackPane | 7 | 7 | import javafx.scene.layout.StackPane | |
| import map.* | 8 | 8 | import map.* | |
| import java.net.URL | 9 | 9 | import java.net.URL | |
| import java.util.* | 10 | 10 | import java.util.* | |
| 11 | 11 | |||
| class MapPanelController : Initializable { | 12 | 12 | class MapPanelController : Initializable { | |
| 13 | 13 | |||
| @FXML | 14 | 14 | @FXML | |
| private lateinit var map: StackPane | 15 | 15 | private lateinit var map: StackPane | |
| 16 | 16 | |||
| private val mapView = LeafletMapView() | 17 | 17 | private val mapView = LeafletMapView() | |
| 18 | 18 | |||
| 19 | 19 | |||
| override fun initialize(location: URL?, resources: ResourceBundle?) { | 20 | 20 | override fun initialize(location: URL?, resources: ResourceBundle?) { | |
| mapView.displayMap(MapConfig()) | 21 | 21 | mapView.displayMap(MapConfig()) | |
| setObservableVesselListener() | 22 | 22 | setObservableVesselListener() | |
| setObservableSelectedVesselListener() | 23 | 23 | setObservableSelectedVesselListener() | |
| setStateListener() | 24 | 24 | setStateListener() | |
| 25 | observableCurrentTime() | |||
| 26 | ||||
| /*val completeFutureMap: CompletableFuture<Worker.State> = mapView.displayMap(MapConfig()) | 25 | 27 | /*val completeFutureMap: CompletableFuture<Worker.State> = mapView.displayMap(MapConfig()) | |
| completeFutureMap.whenComplete{ | 26 | 28 | completeFutureMap.whenComplete{ | |
| workerState, _ -> | 27 | 29 | workerState, _ -> | |
| if (workerState == Worker.State.SUCCEEDED) { | 28 | 30 | if (workerState == Worker.State.SUCCEEDED) { | |
| } | 29 | 31 | } | |
| }*/ | 30 | 32 | }*/ | |
| map.children.add(mapView) | 31 | 33 | map.children.add(mapView) | |
| map.children | 32 | 34 | map.children | |
| } | 33 | 35 | } | |
| 34 | 36 | |||
| private fun setStateListener() { | 35 | 37 | private fun setStateListener() { | |
| observableState.listeners.add(object : StateListener { | 36 | 38 | observableMapState.listeners.add(object : StateListener { | |
| override fun onValueChanged(newValue: State) { | 37 | 39 | override fun onValueChanged(newValue: MapState) { | |
| if (observableSelectedVessel.vessel.mmsi != null) { | 38 | 40 | if (observableSelectedVessel.vessel.mmsi != null) { | |
| updateMap(observableSelectedVessel.vessel.mmsi!!) | 39 | 41 | updateMap(observableSelectedVessel.vessel.mmsi!!) | |
| } else { | 40 | 42 | } else { | |
| updateMap() | 41 | 43 | updateMap() | |
| } | 42 | 44 | } | |
| } | 43 | 45 | } | |
| }) | 44 | 46 | }) | |
| } | 45 | 47 | } | |
| 46 | 48 | |||
| 49 | private fun observableCurrentTime() { | |||
| 50 | observableCurrentTime.listeners.add(object : CurrentTime{ | |||
| 51 | override fun onValueChanged(newValue: Int) { | |||
| 52 | updateMap() | |||
| 53 | } | |||
| 54 | }) | |||
| 55 | } | |||
| 56 | ||||
| private fun updateMap() { | 47 | 57 | private fun updateMap() { | |
| when (observableState.state) { | 48 | 58 | if (observableIsReplayState.value){ | |
| ALL_MESSAGES -> displayAllMessageOnMap(mapView) | 49 | 59 | displayTargetedVessels(mapView) | |
| CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView) | 50 | 60 | } else { | |
| HEAT_MAP -> displayHeatMapOnMap(mapView) | 51 | 61 | when (observableMapState.state) { | |
| 62 | ALL_MESSAGES -> displayAllMessageOnMap(mapView) | |||
| 63 | CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView) | |||
| 64 | HEAT_MAP -> displayHeatMapOnMap(mapView) | |||
| 65 | } | |||
| } | 52 | 66 | } | |
| } | 53 | 67 | } | |
| 54 | 68 | |||
| private fun updateMap(selectedMMSI: String) { | 55 | 69 | private fun updateMap(selectedMMSI: String) { | |
| when (observableState.state) { | 56 | 70 | when (observableMapState.state) { | |
| ALL_MESSAGES -> displayAllMessageOnMap(mapView, selectedMMSI) | 57 | 71 | ALL_MESSAGES -> displayAllMessageOnMap(mapView, selectedMMSI) | |
| CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView, selectedMMSI) | 58 | 72 | CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView, selectedMMSI) | |
| HEAT_MAP -> displayHeatMapOnMap(mapView, selectedMMSI) | 59 | 73 | HEAT_MAP -> displayHeatMapOnMap(mapView, selectedMMSI) | |
| } | 60 | 74 | } | |
| } | 61 | 75 | } | |
| 62 | 76 | |||
| private fun setObservableVesselListener() { | 63 | 77 | private fun setObservableVesselListener() { | |
| observableVessel.listeners.add(object : MessageListener { | 64 | 78 | observableVessel.listeners.add(object : MessageListener { | |
| override fun onValueChanged(newValue: MutableMap<String?, Vessel>) { | 65 | 79 | override fun onValueChanged(newValue: MutableMap<String?, Vessel>) { | |
| updateMap() | 66 | 80 | updateMap() | |
| } | 67 | 81 | } | |
| }) | 68 | 82 | }) | |
| } | 69 | 83 | } | |
| 70 | 84 | |||
| private fun setObservableSelectedVesselListener() { | 71 | 85 | private fun setObservableSelectedVesselListener() { | |
| observableSelectedVessel.listeners.add(object : SelectedVesselListener { | 72 | 86 | observableSelectedVessel.listeners.add(object : SelectedVesselListener { | |
| override fun onValueChanged(newValue: Vessel) { | 73 | 87 | override fun onValueChanged(newValue: Vessel) { | |
| if (newValue.mmsi != null){ | 74 | 88 | if (newValue.mmsi != null){ | |
| updateMap(newValue.mmsi) | 75 | 89 | updateMap(newValue.mmsi) | |
| }else { | 76 | 90 | }else { | |
| updateMap() | 77 | 91 | updateMap() | |
| } | 78 | 92 | } | |
| } | 79 | 93 | } |
src/main/kotlin/application/controller/MenuBarController.kt
View file @
3b26be8
| package application.controller | 1 | 1 | package application.controller | |
| 2 | 2 | |||
| import application.model.State.* | 3 | 3 | import application.model.MapState.* | |
| import application.model.createVesselCollection | 4 | 4 | import application.model.createVesselCollection | |
| import application.model.observableState | 5 | 5 | import application.model.observableMapState | |
| import application.model.observableVessel | 6 | 6 | import application.model.observableVessel | |
| import javafx.event.EventHandler | 7 | 7 | import javafx.event.EventHandler | |
| import javafx.fxml.FXML | 8 | 8 | import javafx.fxml.FXML | |
| import javafx.fxml.Initializable | 9 | 9 | import javafx.fxml.Initializable | |
| import javafx.scene.control.Alert | 10 | 10 | import javafx.scene.control.Alert | |
| import javafx.scene.control.CheckMenuItem | 11 | 11 | import javafx.scene.control.CheckMenuItem | |
| import javafx.scene.control.MenuBar | 12 | 12 | import javafx.scene.control.MenuBar | |
| import javafx.scene.control.MenuItem | 13 | 13 | import javafx.scene.control.MenuItem | |
| import javafx.stage.FileChooser | 14 | 14 | import javafx.stage.FileChooser | |
| import java.net.URL | 15 | 15 | import java.net.URL | |
| import java.util.* | 16 | 16 | import java.util.* | |
| 17 | 17 | |||
| class MenuBarController : Initializable { | 18 | 18 | class MenuBarController : Initializable { | |
| 19 | 19 | |||
| @FXML | 20 | 20 | @FXML | |
| var menuBar: MenuBar = MenuBar() | 21 | 21 | var menuBar: MenuBar = MenuBar() | |
| 22 | 22 | |||
| @FXML | 23 | 23 | @FXML | |
| var import: MenuItem = MenuItem() | 24 | 24 | var import: MenuItem = MenuItem() | |
| 25 | 25 | |||
| @FXML | 26 | 26 | @FXML | |
| var allMessages: CheckMenuItem = CheckMenuItem() | 27 | 27 | var allMessages: CheckMenuItem = CheckMenuItem() | |
| 28 | 28 | |||
| @FXML | 29 | 29 | @FXML | |
| var clusteredMessage: CheckMenuItem = CheckMenuItem() | 30 | 30 | var clusteredMessage: CheckMenuItem = CheckMenuItem() | |
| 31 | 31 | |||
| @FXML | 32 | 32 | @FXML | |
| var heatMap: CheckMenuItem = CheckMenuItem() | 33 | 33 | var heatMap: CheckMenuItem = CheckMenuItem() | |
| 34 | 34 | |||
| override fun initialize(location: URL?, resources: ResourceBundle?) { | 35 | 35 | override fun initialize(location: URL?, resources: ResourceBundle?) { | |
| 36 | 36 | |||
| setOnActionImportButton() | 37 | 37 | setOnActionImportButton() | |
| 38 | 38 | |||
| setOnActionAllMessageButton() | 39 | 39 | setOnActionAllMessageButton() | |
| setOnActionClusteredMessageButton() | 40 | 40 | setOnActionClusteredMessageButton() | |
| setOnActionHeatMapButton() | 41 | 41 | setOnActionHeatMapButton() | |
| observableState.state = CLUSTERED_MESSAGES | 42 | 42 | observableMapState.state = CLUSTERED_MESSAGES | |
| allMessages.isSelected = false | 43 | 43 | allMessages.isSelected = false | |
| clusteredMessage.isSelected = true | 44 | 44 | clusteredMessage.isSelected = true | |
| heatMap.isSelected = false | 45 | 45 | heatMap.isSelected = false | |
| 46 | 46 | |||
| } | 47 | 47 | } | |
| 48 | 48 | |||
| private fun setOnActionImportButton() { | 49 | 49 | private fun setOnActionImportButton() { | |
| import.onAction = EventHandler { | 50 | 50 | import.onAction = EventHandler { | |
| val fileChooser = FileChooser() | 51 | 51 | val fileChooser = FileChooser() | |
| fileChooser.title = "Choose a file to import" | 52 | 52 | fileChooser.title = "Choose a file to import" | |
| val window = menuBar.scene.window | 53 | 53 | val window = menuBar.scene.window | |
| val file = fileChooser.showOpenDialog(window) | 54 | 54 | val file = fileChooser.showOpenDialog(window) | |
| try { | 55 | 55 | try { | |
| if (file.extension != "csv") { | 56 | 56 | if (file.extension != "csv") { | |
| val alert = Alert(Alert.AlertType.WARNING) | 57 | 57 | val alert = Alert(Alert.AlertType.WARNING) | |
| alert.title = "Warning Alert" | 58 | 58 | alert.title = "Warning Alert" | |
| alert.headerText = "Wrong file format." | 59 | 59 | alert.headerText = "Wrong file format." | |
| alert.contentText = "Please choose ร .csv file." | 60 | 60 | alert.contentText = "Please choose ร .csv file." | |
| alert.showAndWait() | 61 | 61 | alert.showAndWait() | |
| } | 62 | 62 | } | |
| val vessels = createVesselCollection(file) | 63 | 63 | val vessels = createVesselCollection(file) | |
| observableVessel.vessels.clear() | 64 | 64 | observableVessel.vessels.clear() | |
| observableVessel.vessels = vessels | 65 | 65 | observableVessel.vessels = vessels | |
| } catch (ignore: IllegalStateException) { | 66 | 66 | } catch (ignore: IllegalStateException) { | |
| 67 | 67 | |||
| } | 68 | 68 | } | |
| } | 69 | 69 | } | |
| } | 70 | 70 | } | |
| 71 | 71 | |||
| private fun setOnActionAllMessageButton() { | 72 | 72 | private fun setOnActionAllMessageButton() { | |
| allMessages.onAction = EventHandler { | 73 | 73 | allMessages.onAction = EventHandler { | |
| observableState.state = ALL_MESSAGES | 74 | 74 | observableMapState.state = ALL_MESSAGES | |
| allMessages.isSelected = true | 75 | 75 | allMessages.isSelected = true | |
| clusteredMessage.isSelected = false | 76 | 76 | clusteredMessage.isSelected = false | |
| heatMap.isSelected = false | 77 | 77 | heatMap.isSelected = false | |
| } | 78 | 78 | } | |
| } | 79 | 79 | } | |
| 80 | 80 | |||
| private fun setOnActionClusteredMessageButton() { | 81 | 81 | private fun setOnActionClusteredMessageButton() { | |
| clusteredMessage.onAction = EventHandler { | 82 | 82 | clusteredMessage.onAction = EventHandler { | |
| observableState.state = CLUSTERED_MESSAGES | 83 | 83 | observableMapState.state = CLUSTERED_MESSAGES | |
| heatMap.isSelected = false | 84 | 84 | heatMap.isSelected = false | |
| allMessages.isSelected = false | 85 | 85 | allMessages.isSelected = false | |
| clusteredMessage.isSelected = true | 86 | 86 | clusteredMessage.isSelected = true | |
| } | 87 | 87 | } | |
| } | 88 | 88 | } |
src/main/kotlin/application/controller/TimePanel.kt
View file @
3b26be8
| File was created | 1 | package application.controller | ||
| 2 | ||||
| 3 | import application.model.* | |||
| 4 | import javafx.fxml.FXML | |||
| 5 | import javafx.fxml.Initializable | |||
| 6 | import javafx.scene.control.Button | |||
| 7 | import javafx.scene.control.Slider | |||
| 8 | import java.net.URL | |||
| 9 | import java.util.* | |||
| 10 | ||||
| 11 | ||||
| 12 | class TimePanel : Initializable { | |||
| 13 | ||||
| 14 | @FXML | |||
| 15 | var timeSlider = Slider() | |||
| 16 | ||||
| 17 | @FXML | |||
| 18 | var timeStop = Button() | |||
| 19 | ||||
| 20 | @FXML | |||
| 21 | var timePlay = Button() | |||
| 22 | ||||
| 23 | ||||
| 24 | override fun initialize(location: URL?, resources: ResourceBundle?) { | |||
| 25 | setSliderMinMax() | |||
| 26 | setSliderListener() | |||
| 27 | ||||
| 28 | ||||
| 29 | } | |||
| 30 | ||||
| 31 | private fun setSliderMinMax() { | |||
| 32 | observableVessel.listeners.add(object : MessageListener{ | |||
| 33 | override fun onValueChanged(newValue: MutableMap<String?, Vessel>) { |
src/main/kotlin/application/model/Context.kt
View file @
3b26be8
| package application.model | 1 | 1 | package application.model | |
| 2 | 2 | |||
| val observableVessel: ObservableVessel = ObservableVessel() | 3 | 3 | val observableVessel: ObservableVessel = ObservableVessel() | |
| 4 | 4 | |||
| val observableSelectedVessel: ObservableSelectedVessel = ObservableSelectedVessel() | 5 | 5 | val observableSelectedVessel: ObservableSelectedVessel = ObservableSelectedVessel() | |
| 6 | 6 | |||
| val observableState: ObservableState = ObservableState() | 7 | 7 | val observableMapState: ObservableMapState = ObservableMapState() | |
| 8 | ||||
| 9 | val observableIsReplayState: ObservableReplayState = ObservableReplayState() |
src/main/kotlin/application/model/CurrentTime.kt
View file @
3b26be8
| File was created | 1 | package application.model | ||
| 2 | ||||
| 3 | interface CurrentTime { | |||
| 4 | fun onValueChanged(newValue: Int) |
src/main/kotlin/application/model/MapState.kt
View file @
3b26be8
| File was created | 1 | package application.model | ||
| 2 | ||||
| 3 | enum class MapState { | |||
| 4 | ALL_MESSAGES, | |||
| 5 | CLUSTERED_MESSAGES, | |||
| 6 | HEAT_MAP |
src/main/kotlin/application/model/ObservableCurrentTime.kt
View file @
3b26be8
| File was created | 1 | package application.model | ||
| 2 | ||||
| 3 | import kotlin.properties.Delegates | |||
| 4 | ||||
| 5 | class ObservableCurrentTime { | |||
| 6 | ||||
| 7 | val listeners: MutableList<CurrentTime> = mutableListOf() | |||
| 8 | ||||
| 9 | var value: Int by Delegates.observable( | |||
| 10 | initialValue = 0, | |||
| 11 | onChange = { _, _, new -> | |||
| 12 | run { | |||
| 13 | listeners.forEach { | |||
| 14 | it.onValueChanged(new) | |||
| 15 | } |
src/main/kotlin/application/model/ObservableMapState.kt
View file @
3b26be8
| File was created | 1 | package application.model | ||
| 2 | ||||
| 3 | import kotlin.properties.Delegates | |||
| 4 | ||||
| 5 | class ObservableMapState { | |||
| 6 | val listeners: MutableList<StateListener> = mutableListOf() | |||
| 7 | ||||
| 8 | var state: MapState by Delegates.observable( | |||
| 9 | initialValue = MapState.CLUSTERED_MESSAGES, | |||
| 10 | onChange = { _, _, new -> | |||
| 11 | run { | |||
| 12 | listeners.forEach { | |||
| 13 | it.onValueChanged(new) | |||
| 14 | } | |||
| 15 | } |
src/main/kotlin/application/model/ObservableReplayState.kt
View file @
3b26be8
| File was created | 1 | package application.model | ||
| 2 | ||||
| 3 | import kotlin.properties.Delegates | |||
| 4 | ||||
| 5 | class ObservableReplayState { | |||
| 6 | ||||
| 7 | val listeners: MutableList<ReplayState> = mutableListOf() | |||
| 8 | ||||
| 9 | var value: Boolean by Delegates.observable( | |||
| 10 | initialValue = true, | |||
| 11 | onChange = { _, _, new -> | |||
| 12 | run { | |||
| 13 | listeners.forEach { | |||
| 14 | it.onValueChanged(new) | |||
| 15 | } |
src/main/kotlin/application/model/ObservableState.kt
View file @
3b26be8
| package application.model | 1 | File was deleted | ||
| 2 | ||||
| import kotlin.properties.Delegates | 3 | |||
| 4 | ||||
| class ObservableState { | 5 | |||
| val listeners: MutableList<StateListener> = mutableListOf() | 6 | |||
| 7 | ||||
| var state: State by Delegates.observable( | 8 | |||
| initialValue = State.CLUSTERED_MESSAGES, | 9 | |||
| onChange = { _, _, new -> | 10 | |||
| run { | 11 | |||
| listeners.forEach { | 12 | |||
| it.onValueChanged(new) | 13 | |||
| } | 14 | |||
| } | 15 |
src/main/kotlin/application/model/ReplayState.kt
View file @
3b26be8
| File was created | 1 | package application.model | ||
| 2 | ||||
| 3 | interface ReplayState { | |||
| 4 | fun onValueChanged(newValue: Boolean) |
src/main/kotlin/application/model/State.kt
View file @
3b26be8
| package application.model | 1 | File was deleted | ||
| 2 | ||||
| enum class State { | 3 | |||
| ALL_MESSAGES, | 4 | |||
| CLUSTERED_MESSAGES, | 5 | |||
| HEAT_MAP | 6 |
src/main/kotlin/application/model/StateListener.kt
View file @
3b26be8
| package application.model | 1 | 1 | package application.model | |
| 2 | 2 | |||
| interface StateListener { | 3 | 3 | interface StateListener { | |
| fun onValueChanged(newValue: State) | 4 | 4 | fun onValueChanged(newValue: MapState) | |
| } | 5 | 5 | } |
src/main/kotlin/application/model/Vessel.kt
View file @
3b26be8
| package application.model | 1 | 1 | package application.model | |
| 2 | 2 | |||
| import java.time.LocalDateTime | 3 | |||
| import java.time.ZoneOffset | 4 | |||
| import java.util.* | 5 | 3 | import java.util.* | |
| 6 | 4 | |||
| 7 | 5 | |||
| class Vessel(val mmsi: String?) { | 8 | 6 | class Vessel(val mmsi: String?) { | |
| val messages: SortedMap<LocalDateTime, Message> = sortedMapOf() | 9 | 7 | val messages: SortedMap<Long, Message> = sortedMapOf() | |
| 8 | var messageToDisplay: Message? = null | |||
| 9 | get() { | |||
| 10 | // messages.forEach { (key, value) -> | |||
| 11 | // if(observableCurrentTime.value < key) { | |||
| 12 | // field = value | |||
| 13 | // return@forEach | |||
| 14 | // } | |||
| 15 | // } | |||
| 16 | field = messages.asSequence().map{ it }.firstOrNull {observableCurrentTime.value < it.key}.let { it?.value } | |||
| 17 | return field | |||
| 18 | } | |||
| 10 | 19 | |||
| 20 | // val timesNormalized : SortedMap<Long, LocalDateTime> = sortedMapOf() | |||
| 21 | ||||
| 22 | // fun getAllNormalizedDate(): SortedMap<Long, LocalDateTime> { | |||
| 23 | // var offset: Long? = null | |||
| 24 | // if(timesNormalized.size == 0){ | |||
| 25 | // messages.keys.forEach { | |||
| 26 | // val currentTime = it.toEpochSecond(ZoneOffset.UTC) | |||
| 27 | // if(offset == null){ | |||
| 28 | // offset = currentTime | |||
| 29 | // } | |||
| 30 | // timesNormalized[currentTime - offset!!]= it | |||
| 31 | // } | |||
| 32 | // } | |||
| 33 | // return timesNormalized | |||
| 34 | // } | |||
| 35 | ||||
| fun getAllTime(): ArrayList<MessageData?> { | 11 | 36 | fun getAllTime(): ArrayList<MessageData?> { | |
| val timeList = arrayListOf<MessageData?>() | 12 | 37 | val timeList = arrayListOf<MessageData?>() | |
| messages.forEach { | 13 | 38 | messages.forEach { | |
| timeList.add(it.value.time) | 14 | 39 | timeList.add(it.value.time) | |
| } | 15 | 40 | } | |
| 16 | 41 | |||
| return timeList | 17 | 42 | return timeList | |
| } | 18 | 43 | } | |
| 19 | 44 | |||
| fun getAllLatitude(): ArrayList<MessageData?> { | 20 | 45 | fun getAllLatitude(): ArrayList<MessageData?> { | |
| val latitudeList = arrayListOf<MessageData?>() | 21 | 46 | val latitudeList = arrayListOf<MessageData?>() | |
| messages.forEach { | 22 | 47 | messages.forEach { | |
| latitudeList.add(it.value.latitude) | 23 | 48 | latitudeList.add(it.value.latitude) | |
| } | 24 | 49 | } | |
| 25 | 50 | |||
| return latitudeList | 26 | 51 | return latitudeList | |
| } | 27 | 52 | } | |
| 28 | 53 | |||
| fun getAllLongitude(): ArrayList<MessageData?> { | 29 | 54 | fun getAllLongitude(): ArrayList<MessageData?> { | |
| val longitudeList = arrayListOf<MessageData?>() | 30 | 55 | val longitudeList = arrayListOf<MessageData?>() | |
| messages.forEach { | 31 | 56 | messages.forEach { | |
| longitudeList.add(it.value.longitude) | 32 | 57 | longitudeList.add(it.value.longitude) | |
| } | 33 | 58 | } | |
| 34 | 59 | |||
| return longitudeList | 35 | 60 | return longitudeList | |
| } | 36 | 61 | } | |
| 37 | 62 | |||
| fun getAllSpeedOverGround(): ArrayList<MessageData?> { | 38 | 63 | fun getAllSpeedOverGround(): ArrayList<MessageData?> { | |
| val speedOverGroundList = arrayListOf<MessageData?>() | 39 | 64 | val speedOverGroundList = arrayListOf<MessageData?>() | |
| messages.forEach { | 40 | 65 | messages.forEach { | |
| speedOverGroundList.add(it.value.speedOverGround) | 41 | 66 | speedOverGroundList.add(it.value.speedOverGround) | |
| } | 42 | 67 | } | |
| 43 | 68 | |||
| return speedOverGroundList | 44 | 69 | return speedOverGroundList | |
| } | 45 | 70 | } | |
| 46 | 71 | |||
| fun getAllCourseOverGround(): ArrayList<MessageData?> { | 47 | 72 | fun getAllCourseOverGround(): ArrayList<MessageData?> { | |
| val res = arrayListOf<MessageData?>() | 48 | 73 | val res = arrayListOf<MessageData?>() | |
| messages.forEach { | 49 | 74 | messages.forEach { | |
| res.add(it.value.courseOverGround) | 50 | 75 | res.add(it.value.courseOverGround) | |
| } | 51 | 76 | } | |
| 52 | 77 | |||
| return res | 53 | 78 | return res | |
| } | 54 | 79 | } | |
| 55 | 80 | |||
| fun getAllHeading(): ArrayList<MessageData?> { | 56 | 81 | fun getAllHeading(): ArrayList<MessageData?> { | |
| val res = arrayListOf<MessageData?>() | 57 | 82 | val res = arrayListOf<MessageData?>() | |
| messages.forEach { | 58 | 83 | messages.forEach { | |
| res.add(it.value.heading) | 59 | 84 | res.add(it.value.heading) | |
| } | 60 | 85 | } | |
| 61 | 86 | |||
| return res | 62 | 87 | return res | |
| } | 63 | 88 | } | |
| 64 | 89 | |||
| fun getAllVesselName(): ArrayList<MessageData?> { | 65 | 90 | fun getAllVesselName(): ArrayList<MessageData?> { | |
| val res = arrayListOf<MessageData?>() | 66 | 91 | val res = arrayListOf<MessageData?>() | |
| messages.forEach { | 67 | 92 | messages.forEach { | |
| res.add(it.value.vesselName) | 68 | 93 | res.add(it.value.vesselName) | |
| } | 69 | 94 | } | |
| return res | 70 | 95 | return res | |
| } | 71 | 96 | } | |
| 72 | 97 | |||
| fun getAllIMO(): ArrayList<MessageData?> { | 73 | 98 | fun getAllIMO(): ArrayList<MessageData?> { | |
| val res = arrayListOf<MessageData?>() | 74 | 99 | val res = arrayListOf<MessageData?>() | |
| messages.forEach { | 75 | 100 | messages.forEach { | |
| res.add(it.value.imo) | 76 | 101 | res.add(it.value.imo) | |
| } | 77 | 102 | } | |
| return res | 78 | 103 | return res | |
| } | 79 | 104 | } | |
| 80 | 105 | |||
| fun getAllCallSign(): ArrayList<MessageData?> { | 81 | 106 | fun getAllCallSign(): ArrayList<MessageData?> { | |
| val res = arrayListOf<MessageData?>() | 82 | 107 | val res = arrayListOf<MessageData?>() | |
| messages.forEach { | 83 | 108 | messages.forEach { | |
| res.add(it.value.callSign) | 84 | 109 | res.add(it.value.callSign) | |
| } | 85 | 110 | } | |
| return res | 86 | 111 | return res | |
| } | 87 | 112 | } | |
| 88 | 113 | |||
| fun getAllVesselType(): ArrayList<MessageData?> { | 89 | 114 | fun getAllVesselType(): ArrayList<MessageData?> { | |
| val res = arrayListOf<MessageData?>() | 90 | 115 | val res = arrayListOf<MessageData?>() | |
| messages.forEach { | 91 | 116 | messages.forEach { | |
| res.add(it.value.vesselType) | 92 | 117 | res.add(it.value.vesselType) | |
| } | 93 | 118 | } | |
| return res | 94 | 119 | return res | |
| } | 95 | 120 | } | |
| 96 | 121 | |||
| fun getAllStatus(): ArrayList<MessageData?> { | 97 | 122 | fun getAllStatus(): ArrayList<MessageData?> { | |
| val res = arrayListOf<MessageData?>() | 98 | 123 | val res = arrayListOf<MessageData?>() | |
| messages.forEach { | 99 | 124 | messages.forEach { | |
| res.add(it.value.status) | 100 | 125 | res.add(it.value.status) | |
| } | 101 | 126 | } | |
| return res | 102 | 127 | return res | |
| } | 103 | 128 | } | |
| 104 | 129 | |||
| fun getAllLength(): ArrayList<MessageData?> { | 105 | 130 | fun getAllLength(): ArrayList<MessageData?> { | |
| val res = arrayListOf<MessageData?>() | 106 | 131 | val res = arrayListOf<MessageData?>() | |
| messages.forEach { | 107 | 132 | messages.forEach { | |
| res.add(it.value.length) | 108 | 133 | res.add(it.value.length) | |
| } | 109 | 134 | } | |
| return res | 110 | 135 | return res | |
| } | 111 | 136 | } | |
| 112 | 137 | |||
| fun getAllWidth(): ArrayList<MessageData?> { | 113 | 138 | fun getAllWidth(): ArrayList<MessageData?> { | |
| val res = arrayListOf<MessageData?>() | 114 | 139 | val res = arrayListOf<MessageData?>() | |
| messages.forEach { | 115 | 140 | messages.forEach { | |
| res.add(it.value.width) | 116 | 141 | res.add(it.value.width) | |
| } | 117 | 142 | } | |
| return res | 118 | 143 | return res |
src/main/kotlin/application/model/VesselGenerator.kt
View file @
3b26be8
| package application.model | 1 | 1 | package application.model | |
| 2 | 2 | |||
| import java.io.File | 3 | 3 | import java.io.File | |
| 4 | import java.time.ZoneOffset | |||
| import java.util.* | 4 | 5 | import java.util.* | |
| import kotlin.collections.ArrayList | 5 | |||
| 6 | 6 | |||
| fun createVesselCollection(file: File) : SortedMap<String, Vessel> { | 7 | 7 | fun createVesselCollection(file: File): SortedMap<String, Vessel> { | |
| val messages : ArrayList<Message> = arrayListOf() | 8 | 8 | val messages: ArrayList<Message> = arrayListOf() | |
| val vessels: SortedMap<String, Vessel> = sortedMapOf() | 9 | 9 | val vessels: SortedMap<String, Vessel> = sortedMapOf() | |
| 10 | var maxTime: Long = 0 | |||
| 11 | var minTime: Long = Long.MAX_VALUE | |||
| 10 | 12 | |||
| file.forEachLine { | 11 | 13 | file.forEachLine { | |
| val arrayMessage = it.split(",") | 12 | 14 | val arrayMessage = it.split(",") | |
| if (arrayMessage[0].toIntOrNull() !== null) { | 13 | 15 | if (arrayMessage[0].toIntOrNull() !== null) { | |
| val message = Message(arrayMessage) | 14 | 16 | val message = Message(arrayMessage) | |
| messages.add(message) | 15 | 17 | messages.add(message) | |
| if (!vessels.containsKey(message.mmsi.value)){ | 16 | 18 | if (!vessels.containsKey(message.mmsi.value)) { | |
| vessels[message.mmsi.value] = Vessel(message.mmsi.value!!) | 17 | 19 | vessels[message.mmsi.value] = Vessel(message.mmsi.value!!) | |
| } | 18 | 20 | } | |
| vessels[message.mmsi.value]?.messages?.set(message.time.value, message) | 19 | 21 | val time = message.time.value.toEpochSecond(ZoneOffset.UTC) | |
| 22 | vessels[message.mmsi.value]?.messages?.set(time, message) | |||
| 23 | if (time > maxTime) { | |||
| 24 | maxTime = time | |||
| 25 | } | |||
| 26 | if (time < minTime){ | |||
| 27 | minTime = time | |||
| 28 | } | |||
| } | 20 | 29 | } | |
| 21 | ||||
| } | 22 | 30 | } | |
| 31 | ||||
| 32 | Vessel.maxTime = maxTime | |||
| 33 | Vessel.minTime = minTime | |||
| 23 | 34 | |||
| return vessels | 24 | 35 | return vessels |
src/main/kotlin/map/MapDisplayer.kt
View file @
3b26be8
| package map | 1 | 1 | package map | |
| 2 | 2 | |||
| import application.model.observableVessel | 3 | 3 | import application.model.observableVessel | |
| 4 | 4 | |||
| fun clearMap(map: LeafletMapView) { | 5 | 5 | fun clearMap(map: LeafletMapView) { | |
| clearMapCanvas(map) | 6 | 6 | clearMapCanvas(map) | |
| clearMapCluster(map) | 7 | 7 | clearMapCluster(map) | |
| clearHeatMap(map) | 8 | 8 | clearHeatMap(map) | |
| } | 9 | 9 | } | |
| 10 | 10 | |||
| fun clearMapCluster(map: LeafletMapView) { | 11 | 11 | fun clearMapCluster(map: LeafletMapView) { | |
| map.execScript( | 12 | 12 | map.execScript( | |
| """ | 13 | 13 | """ | |
| |myMap.removeLayer(markerClusters); | 14 | 14 | |myMap.removeLayer(markerClusters); | |
| |var markerClusters = L.markerClusterGroup({spiderfyOnMaxZoom: false, disableClusteringAtZoom: 10}); | 15 | 15 | |var markerClusters = L.markerClusterGroup({spiderfyOnMaxZoom: false, disableClusteringAtZoom: 10}); | |
| """.trimMargin() | 16 | 16 | """.trimMargin() | |
| ) | 17 | 17 | ) | |
| } | 18 | 18 | } | |
| 19 | 19 | |||
| fun clearMapCanvas(map: LeafletMapView) { | 20 | 20 | fun clearMapCanvas(map: LeafletMapView) { | |
| map.execScript( | 21 | 21 | map.execScript( | |
| """ | 22 | 22 | """ | |
| |myRenderer.removeFrom(myMap); | 23 | 23 | |myRenderer.removeFrom(myMap); | |
| |var myRenderer = L.canvas({ padding: 0.5 }); | 24 | 24 | |var myRenderer = L.canvas({ padding: 0.5 }); | |
| """.trimMargin() | 25 | 25 | """.trimMargin() | |
| ) | 26 | 26 | ) | |
| } | 27 | 27 | } | |
| 28 | 28 | |||
| fun clearHeatMap(map: LeafletMapView) { | 29 | 29 | fun clearHeatMap(map: LeafletMapView) { | |
| map.execScript( | 30 | 30 | map.execScript( | |
| """ | 31 | 31 | """ | |
| |heatLayer.removeFrom(myMap); | 32 | 32 | |heatLayer.removeFrom(myMap); | |
| |var heatLayer = L.heatLayer([]).addTo(myMap); | 33 | 33 | |var heatLayer = L.heatLayer([]).addTo(myMap); | |
| """.trimMargin() | 34 | 34 | """.trimMargin() | |
| ) | 35 | 35 | ) | |
| } | 36 | 36 | } | |
| 37 | 37 | |||
| fun displayAllMessageOnMap(map: LeafletMapView) { | 38 | 38 | fun displayAllMessageOnMap(map: LeafletMapView) { | |
| clearMap(map) | 39 | 39 | clearMap(map) | |
| observableVessel.vessels.forEach { (_, value) -> | 40 | 40 | observableVessel.vessels.forEach { (_, value) -> | |
| value.messages.forEach { (_, message) -> | 41 | 41 | value.messages.forEach { (_, message) -> | |
| map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap)") | 42 | 42 | map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap)") | |
| } | 43 | 43 | } | |
| } | 44 | 44 | } | |
| } | 45 | 45 | } | |
| 46 | 46 | |||
| fun displayAllMessageOnMap(map: LeafletMapView, selectedMMSI: String) { | 47 | 47 | fun displayAllMessageOnMap(map: LeafletMapView, selectedMMSI: String) { | |
| clearMap(map) | 48 | 48 | clearMap(map) | |
| observableVessel.vessels.forEach { (_, value) -> | 49 | 49 | observableVessel.vessels.forEach { (_, value) -> | |
| value.messages.forEach { (_, message) -> | 50 | 50 | value.messages.forEach { (_, message) -> | |
| if (selectedMMSI == message.mmsi.value) { | 51 | 51 | if (selectedMMSI == message.mmsi.value) { | |
| map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap)") | 52 | 52 | map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap)") | |
| } else { | 53 | 53 | } else { | |
| map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap)") | 54 | 54 | map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap)") | |
| } | 55 | 55 | } | |
| } | 56 | 56 | } | |
| } | 57 | 57 | } | |
| } | 58 | 58 | } | |
| 59 | 59 | |||
| fun displayClusterMessageOnMap(map: LeafletMapView) { | 60 | 60 | fun displayClusterMessageOnMap(map: LeafletMapView) { | |
| clearMap(map) | 61 | 61 | clearMap(map) | |
| observableVessel.vessels.forEach { (_, value) -> | 62 | 62 | observableVessel.vessels.forEach { (_, value) -> | |
| value.messages.forEach { (_, message) -> | 63 | 63 | value.messages.forEach { (_, message) -> | |
| map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}));") | 64 | 64 | map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}));") | |
| } | 65 | 65 | } | |
| } | 66 | 66 | } | |
| map.execScript("myMap.addLayer(markerClusters);") | 67 | 67 | map.execScript("myMap.addLayer(markerClusters);") | |
| } | 68 | 68 | } | |
| 69 | 69 | |||
| 70 | fun displayTargetedVessels(map: LeafletMapView) { | |||
| 71 | clearMap(map) | |||
| 72 | observableVessel.vessels.forEach { (_, value) -> | |||
| 73 | val message = value.messageToDisplay ?: return@forEach | |||
| 74 | map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}));") | |||
| 75 | } | |||
| 76 | map.execScript("myMap.addLayer(markerClusters);") | |||
| 77 | } | |||
| 78 | ||||
| fun displayClusterMessageOnMap(map: LeafletMapView, selectedMMSI: String) { | 70 | 79 | fun displayClusterMessageOnMap(map: LeafletMapView, selectedMMSI: String) { | |
| clearMap(map) | 71 | 80 | clearMap(map) | |
| observableVessel.vessels.forEach { (_, value) -> | 72 | 81 | observableVessel.vessels.forEach { (_, value) -> | |
| value.messages.forEach { (_, message) -> | 73 | 82 | value.messages.forEach { (_, message) -> | |
| if (selectedMMSI == message.mmsi.value) { | 74 | 83 | if (selectedMMSI == message.mmsi.value) { | |
| map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap);") | 75 | 84 | map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap);") | |
| } else { | 76 | 85 | } else { | |
| map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}));") | 77 | 86 | map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}));") | |
| } | 78 | 87 | } | |
| } | 79 | 88 | } | |
| } | 80 | 89 | } | |
| map.execScript("myMap.addLayer(markerClusters);") | 81 | 90 | map.execScript("myMap.addLayer(markerClusters);") | |
| } | 82 | 91 | } | |
| 83 | 92 | |||
| fun displayHeatMapOnMap(map: LeafletMapView) { | 84 | 93 | fun displayHeatMapOnMap(map: LeafletMapView) { | |
| clearMap(map) | 85 | 94 | clearMap(map) | |
| observableVessel.vessels.forEach { (_, value) -> | 86 | 95 | observableVessel.vessels.forEach { (_, value) -> | |
| value.messages.forEach { (_, message) -> | 87 | 96 | value.messages.forEach { (_, message) -> | |
| map.execScript("heatLayer.addLatLng([${message.latitude.value}, ${message.longitude.value}]);") | 88 | 97 | map.execScript("heatLayer.addLatLng([${message.latitude.value}, ${message.longitude.value}]);") | |
| } | 89 | 98 | } | |
| } | 90 | 99 | } | |
| } | 91 | 100 | } | |
| 92 | 101 | |||
| fun displayHeatMapOnMap(map: LeafletMapView, selectedMMSI: String) { | 93 | 102 | fun displayHeatMapOnMap(map: LeafletMapView, selectedMMSI: String) { | |
| clearMap(map) | 94 | 103 | clearMap(map) |
src/main/resources/gui/mapPanel.fxml
View file @
3b26be8
| <?xml version="1.0" encoding="UTF-8"?> | 1 | 1 | <?xml version="1.0" encoding="UTF-8"?> | |
| 2 | 2 | |||
| <?import javafx.scene.layout.StackPane?> | 3 | 3 | <?import javafx.scene.layout.*?> | |
| 4 | 4 | |||
| <StackPane prefHeight="150.0" prefWidth="200.0" xmlns="http://javafx.com/javafx" | 5 | 5 | <VBox prefHeight="150.0" prefWidth="371.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.controller.MapPanelController"> | |
| xmlns:fx="http://javafx.com/fxml" | 6 | 6 | <StackPane fx:id="map" /> | |
| fx:controller="application.controller.MapPanelController" | 7 | 7 | <fx:include source="timePanel.fxml" /> | |
| fx:id="map"/> | 8 | 8 | </VBox> | |
| 9 | ||||
src/main/resources/gui/timePanel.fxml
View file @
3b26be8
| File was created | 1 | <?xml version="1.0" encoding="UTF-8"?> | ||
| 2 | ||||
| 3 | <?import javafx.geometry.*?> | |||
| 4 | <?import javafx.scene.control.*?> | |||
| 5 | <?import javafx.scene.layout.*?> | |||
| 6 | ||||
| 7 | <HBox alignment="CENTER" prefHeight="65.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.controller.TimePanel"> | |||
| 8 | <children> | |||
| 9 | <Button fx:id="timePlay" alignment="CENTER" mnemonicParsing="false" text="Play"> | |||
| 10 | <HBox.margin> | |||
| 11 | <Insets left="5.0" right="5.0" /> | |||
| 12 | </HBox.margin></Button> | |||
| 13 | <Button fx:id="timeStop" mnemonicParsing="false" text="Stop"> | |||
| 14 | <HBox.margin> | |||
| 15 | <Insets left="5.0" right="5.0" /> | |||
| 16 | </HBox.margin></Button> | |||
| 17 | <Slider fx:id="timeSlider"> | |||
| 18 | <HBox.margin> | |||
| 19 | <Insets left="5.0" right="5.0" /> | |||
| 20 | </HBox.margin></Slider> |