Commit 3d3a5f83b85c12a57d28e50b14ae0673bde17205

Authored by lsagona
Exists in master

Merge branch 'dev'

Showing 26 changed files Side-by-side Diff

build.gradle View file @ 3d3a5f8
... ... @@ -17,6 +17,7 @@
17 17 implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
18 18 implementation 'org.jfxtras:jmetro:8.6.9'
19 19 implementation 'org.slf4j:slf4j-api:1.7.30'
  20 + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
20 21 testCompile group: 'junit', name: 'junit', version: '4.12'
21 22 testCompile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
22 23 compile group: 'ch.qos.logback', name: 'logback-core', version: '1.2.3'
src/main/kotlin/application/App.kt View file @ 3d3a5f8
... ... @@ -29,6 +29,7 @@
29 29 }
30 30  
31 31 private fun closeApplication() {
  32 +
32 33 Platform.exit()
33 34 exitProcess(0)
34 35 }
src/main/kotlin/application/controller/DataPanelController.kt View file @ 3d3a5f8
... ... @@ -7,6 +7,8 @@
7 7 import javafx.fxml.Initializable
8 8 import javafx.scene.control.ListCell
9 9 import javafx.scene.control.ListView
  10 +import kotlinx.coroutines.GlobalScope
  11 +import kotlinx.coroutines.launch
10 12 import org.charts.dataviewer.api.config.DataViewerConfiguration
11 13 import org.charts.dataviewer.api.data.PlotData
12 14 import org.charts.dataviewer.api.trace.ScatterTrace
13 15  
14 16  
15 17  
16 18  
... ... @@ -21,24 +23,43 @@
21 23 private var dataList: ObservableList<Pair<String, ArrayList<MessageData?>>> = FXCollections.observableArrayList()
22 24 private lateinit var timeData: ArrayList<MessageData?>
23 25  
  26 + private val latitude: ArrayList<MessageData?> = arrayListOf()
  27 + private val longitude: ArrayList<MessageData?> = arrayListOf()
  28 + private val speedOverGround: ArrayList<MessageData?> = arrayListOf()
  29 + private val courseOverGround: ArrayList<MessageData?> = arrayListOf()
  30 + private val heading: ArrayList<MessageData?> = arrayListOf()
  31 + private val vesselName: ArrayList<MessageData?> = arrayListOf()
  32 + private val imo: ArrayList<MessageData?> = arrayListOf()
  33 + private val callSign: ArrayList<MessageData?> = arrayListOf()
  34 + private val vesselType: ArrayList<MessageData?> = arrayListOf()
  35 + private val status: ArrayList<MessageData?> = arrayListOf()
  36 + private val length: ArrayList<MessageData?> = arrayListOf()
  37 + private val width: ArrayList<MessageData?> = arrayListOf()
  38 + private val draft: ArrayList<MessageData?> = arrayListOf()
  39 + private val cargo: ArrayList<MessageData?> = arrayListOf()
24 40  
  41 + private var selectedItem: Pair<String, ArrayList<MessageData?>>? = null
  42 +
25 43 @FXML
26 44 var dataListView = ListView<Pair<String, ArrayList<MessageData?>>>()
27 45  
28 46 @FXML
29 47 var dataViewer = JavaFxDataViewer()
30 48  
  49 + private val plotData = PlotData()
  50 + private val config = DataViewerConfiguration()
31 51  
32 52 override fun initialize(location: URL?, resources: ResourceBundle?) {
33 53 setObservableSelectedVesselListener()
34 54 dataListView.items = dataList
35 55  
36 56  
37   - val plotData = PlotData()
38   - val config = DataViewerConfiguration()
39   - config.showLegend(true)
  57 +
  58 + config.showLegend(false)
40 59 config.setLegendInsidePlot(false)
41 60  
  61 + setObservableCurrentTimeListener()
  62 +
42 63 dataListView.setCellFactory {
43 64 object : ListCell<Pair<String, ArrayList<MessageData?>>?>() {
44 65 override fun updateItem(item: Pair<String, ArrayList<MessageData?>>?, empty: Boolean) {
45 66  
46 67  
47 68  
48 69  
49 70  
50 71  
51 72  
52 73  
53 74  
54 75  
55 76  
56 77  
57 78  
58 79  
59 80  
60 81  
61 82  
62 83  
63 84  
64 85  
65 86  
66 87  
67 88  
68 89  
69 90  
70 91  
71 92  
72 93  
73 94  
74 95  
75 96  
76 97  
77 98  
78 99  
79 100  
80 101  
81 102  
82 103  
83 104  
84 105  
85 106  
... ... @@ -53,222 +74,315 @@
53 74 }
54 75  
55 76 dataListView.selectionModel.selectedItemProperty().addListener { _, _, newValue ->
56   - if (newValue == null) {
57   - plotData.allTraces.clear()
58   - config.setxAxisTitle("")
59   - config.setyAxisTitle("")
60   - dataViewer.updateConfiguration(config)
  77 + selectedItem = newValue
  78 + updateDataList(observableSelectedVessel.value)
  79 + plot(newValue)
  80 + }
61 81  
62   - dataViewer.resetPlot()
  82 + plotData.allTraces.clear()
  83 + config.setxAxisTitle("")
  84 + config.setyAxisTitle("")
  85 + config.plotTitle = ""
  86 + dataViewer.updateConfiguration(config)
  87 + dataViewer.updatePlot(plotData)
  88 + initDataList()
63 89  
64   - return@addListener
  90 +
  91 + }
  92 +
  93 + private fun plot() {
  94 + if (selectedItem != null || observableSelectedVessel.value == Vessel(null)) {
  95 + GlobalScope.launch {
  96 + plot(selectedItem)
65 97 }
  98 + }
  99 + }
66 100  
67   - val getValueVisitorX = GetValueVisitor()
68   - val getValueVisitorY = GetValueVisitor()
  101 + private fun plot(data: Pair<String, ArrayList<MessageData?>>?) {
  102 + if (data == null) {
  103 + plotData.allTraces.clear()
  104 + config.setxAxisTitle("")
  105 + config.setyAxisTitle("")
  106 + dataViewer.updateConfiguration(config)
69 107  
70   - val arrayListStringX = arrayListOf<String>()
71   - val arrayListDoubleX = arrayListOf<Double>()
72   - val arrayListStringY = arrayListOf<String>()
73   - val arrayListDoubleY = arrayListOf<Double>()
  108 + dataViewer.resetPlot()
74 109  
75   - for (x in 0 until newValue.second.size) {
76   - timeData[x]?.accept(getValueVisitorX)
77   - newValue.second[x]?.accept(getValueVisitorY)
  110 + return
  111 + }else if (data.second.size < timeData.size) return
78 112  
79   - if (getValueVisitorY.value.toDoubleOrNull() == null) {
80   - arrayListStringX.add(getValueVisitorX.value)
81   - arrayListStringY.add(getValueVisitorY.value)
82   - } else {
83   - arrayListStringX.add(getValueVisitorX.value)
84   - arrayListDoubleY.add(getValueVisitorY.value.toDouble())
85   - }
86   - }
  113 + val getValueVisitorX = GetValueVisitor()
  114 + val getValueVisitorY = GetValueVisitor()
87 115  
88   - val scatterTrace = ScatterTrace<Any>()
89   - scatterTrace.traceColour = TraceColour.RED
90   - scatterTrace.traceVisibility = TraceVisibility.TRUE
  116 + val arrayListStringX = arrayListOf<String>()
  117 +// val arrayListDoubleX = arrayListOf<Double>()
  118 + val arrayListStringY = arrayListOf<String>()
  119 + val arrayListDoubleY = arrayListOf<Double>()
91 120  
92   - val serieStringX: Array<String> = arrayListStringX.toTypedArray()
93   -// val serieDoubleX: Array<Double> = arrayListDoubleX.toTypedArray()
94   - val serieStringY: Array<String> = arrayListStringY.toTypedArray()
95   - val serieDoubleY: Array<Double> = arrayListDoubleY.toTypedArray()
  121 + for (x in 0 until timeData.size) {
  122 + timeData[x]?.accept(getValueVisitorX)
  123 + data.second[x]?.accept(getValueVisitorY)
96 124  
97 125 if (getValueVisitorY.value.toDoubleOrNull() == null) {
98   - scatterTrace.setxArray(serieStringX)
99   - scatterTrace.setyArray(serieStringY)
  126 + arrayListStringX.add(getValueVisitorX.value)
  127 + arrayListStringY.add(getValueVisitorY.value)
100 128 } else {
101   - scatterTrace.setxArray(serieStringX)
102   - scatterTrace.setyArray(serieDoubleY)
  129 + arrayListStringX.add(getValueVisitorX.value)
  130 + arrayListDoubleY.add(getValueVisitorY.value.toDouble())
103 131 }
  132 + }
104 133  
105   - config.setxAxisTitle("Date")
106   - config.setyAxisTitle(newValue.first)
107   - dataViewer.resetPlot()
108   - plotData.allTraces.clear()
109   - plotData.addTrace(scatterTrace)
110   - dataViewer.updateConfiguration(config)
111   - dataViewer.updatePlot(plotData)
  134 + val scatterTrace = ScatterTrace<Any>()
  135 + scatterTrace.traceColour = TraceColour.RED
  136 + scatterTrace.traceVisibility = TraceVisibility.TRUE
112 137  
  138 + val serieStringX: Array<String> = arrayListStringX.toTypedArray()
  139 +// val serieDoubleX: Array<Double> = arrayListDoubleX.toTypedArray()
  140 + val serieStringY: Array<String> = arrayListStringY.toTypedArray()
  141 + val serieDoubleY: Array<Double> = arrayListDoubleY.toTypedArray()
  142 +
  143 + if (getValueVisitorY.value.toDoubleOrNull() == null) {
  144 + scatterTrace.setxArray(serieStringX)
  145 + scatterTrace.setyArray(serieStringY)
  146 + } else {
  147 + scatterTrace.setxArray(serieStringX)
  148 + scatterTrace.setyArray(serieDoubleY)
113 149 }
114 150  
  151 + config.plotTitle = ""
  152 + config.setxAxisTitle("Time (s)")
  153 + config.setyAxisTitle(data.first)
  154 + dataViewer.resetPlot()
115 155 plotData.allTraces.clear()
116   - config.setxAxisTitle("")
117   - config.setyAxisTitle("")
  156 + plotData.addTrace(scatterTrace)
118 157 dataViewer.updateConfiguration(config)
119 158 dataViewer.updatePlot(plotData)
120 159  
121 160 }
122 161  
  162 +
123 163 private fun setObservableSelectedVesselListener() {
124 164 observableSelectedVessel.listeners.add(this)
125 165 }
126 166  
127 167 private fun populateTime(vessel: Vessel): ArrayList<MessageData?> {
128   - val allTime: ArrayList<MessageData?> = vessel.getAllTime()
129   - allTime.sortBy { (it as Time).value }
130   -
131   - return allTime
  168 + return if (observableIsReplayState.value) {
  169 + vessel.getAllTimeBeforeSelectedTime()
  170 + } else {
  171 + vessel.getAllTime()
  172 + }
132 173 }
133 174  
134   - private fun populateLatitude(vessel: Vessel): ArrayList<MessageData?> {
135   - val allLatitude: ArrayList<MessageData?> = vessel.getAllLatitude()
136   - allLatitude.sortBy { (it as Latitude).value }
137 175  
138   - return allLatitude
  176 + private fun populateLatitude(vessel: Vessel): ArrayList<MessageData?> {
  177 + return if (observableIsReplayState.value) {
  178 + vessel.getAllLatitudeBeforeSelectedTime()
  179 + } else {
  180 + vessel.getAllLatitude()
  181 + }
139 182 }
140 183  
141 184 private fun populateLongitude(vessel: Vessel): ArrayList<MessageData?> {
142   - val allLongitude: ArrayList<MessageData?> = vessel.getAllLongitude()
143   - allLongitude.sortBy { (it as Longitude).value }
144   -
145   - return allLongitude
  185 + return if (observableIsReplayState.value) {
  186 + vessel.getAllLongitudeBeforeSelectedTime()
  187 + } else {
  188 + vessel.getAllLongitude()
  189 + }
146 190 }
147 191  
148 192 private fun populateSpeedOverGround(vessel: Vessel): ArrayList<MessageData?> {
149   - val allSpeedOverGround: ArrayList<MessageData?> = vessel.getAllSpeedOverGround()
150   - allSpeedOverGround.sortBy { (it as SpeedOverGround).value }
151   -
152   - return allSpeedOverGround
  193 + return if (observableIsReplayState.value) {
  194 + vessel.getAllSpeedOverGroundBeforeSelectedTime()
  195 + } else {
  196 + vessel.getAllSpeedOverGround()
  197 + }
153 198 }
154 199  
155 200 private fun populateCourseOverGround(vessel: Vessel): ArrayList<MessageData?> {
156   - val allCourseOverGround: ArrayList<MessageData?> = vessel.getAllCourseOverGround()
157   - allCourseOverGround.sortBy { (it as CourseOverGround).value }
158   -
159   - return allCourseOverGround
  201 + return if (observableIsReplayState.value) {
  202 + vessel.getAllCourseOverGroundBeforeSelectedTime()
  203 + } else {
  204 + vessel.getAllCourseOverGround()
  205 + }
160 206 }
161 207  
162 208 private fun populateHeading(vessel: Vessel): ArrayList<MessageData?> {
163   - val allHeading: ArrayList<MessageData?> = vessel.getAllHeading()
164   - allHeading.sortBy { (it as Heading).value }
165   -
166   - return allHeading
  209 + return if (observableIsReplayState.value) {
  210 + vessel.getAllHeadingBeforeSelectedTime()
  211 + } else {
  212 + vessel.getAllHeading()
  213 + }
167 214 }
168 215  
169 216 private fun populateVesselName(vessel: Vessel): ArrayList<MessageData?> {
170   - val allVesselName: ArrayList<MessageData?> = vessel.getAllVesselName()
171   - allVesselName.sortBy { (it as VesselName).value }
172   -
173   - return allVesselName
  217 + return if (observableIsReplayState.value) {
  218 + vessel.getAllVesselNameBeforeSelectedTime()
  219 + } else {
  220 + vessel.getAllVesselName()
  221 + }
174 222 }
175 223  
176 224 private fun populateIMO(vessel: Vessel): ArrayList<MessageData?> {
177   - val allIMO: ArrayList<MessageData?> = vessel.getAllIMO()
178   - allIMO.sortBy { (it as IMO).value }
179   -
180   - return allIMO
  225 + return if (observableIsReplayState.value) {
  226 + vessel.getAllIMOBeforeSelectedTime()
  227 + } else {
  228 + vessel.getAllIMO()
  229 + }
181 230 }
182 231  
183 232 private fun populateCallSign(vessel: Vessel): ArrayList<MessageData?> {
184   - val allCallSign: ArrayList<MessageData?> = vessel.getAllCallSign()
185   - allCallSign.sortBy { (it as CallSign).value }
186   -
187   - return allCallSign
  233 + return if (observableIsReplayState.value) {
  234 + vessel.getAllCallSignBeforeSelectedTime()
  235 + } else {
  236 + vessel.getAllCallSign()
  237 + }
188 238 }
189 239  
190 240 private fun populateVesselType(vessel: Vessel): ArrayList<MessageData?> {
191   - val allVesselType: ArrayList<MessageData?> = vessel.getAllVesselType()
192   - allVesselType.sortBy { (it as VesselType).value }
193   -
194   - return allVesselType
  241 + return if (observableIsReplayState.value) {
  242 + vessel.getAllVesselTypeBeforeSelectedTime()
  243 + } else {
  244 + vessel.getAllVesselType()
  245 + }
195 246 }
196 247  
197 248 private fun populateStatus(vessel: Vessel): ArrayList<MessageData?> {
198   - val allStatus: ArrayList<MessageData?> = vessel.getAllStatus()
199   - allStatus.sortBy { (it as Status).value }
200   -
201   - return allStatus
  249 + return if (observableIsReplayState.value) {
  250 + vessel.getAllStatusBeforeSelectedTime()
  251 + } else {
  252 + vessel.getAllStatus()
  253 + }
202 254 }
203 255  
204 256 private fun populateLength(vessel: Vessel): ArrayList<MessageData?> {
205   - val allLength: ArrayList<MessageData?> = vessel.getAllLength()
206   - allLength.sortBy { (it as Length).value }
207   -
208   - return allLength
  257 + return if (observableIsReplayState.value) {
  258 + vessel.getAllLengthBeforeSelectedTime()
  259 + } else {
  260 + vessel.getAllLength()
  261 + }
209 262 }
210 263  
211 264 private fun populateWidth(vessel: Vessel): ArrayList<MessageData?> {
212   - val allWidth: ArrayList<MessageData?> = vessel.getAllWidth()
213   - allWidth.sortBy { (it as Width).value }
214   -
215   - return allWidth
  265 + return if (observableIsReplayState.value) {
  266 + vessel.getAllWidthBeforeSelectedTime()
  267 + } else {
  268 + vessel.getAllWidth()
  269 + }
216 270 }
217 271  
218 272 private fun populateDraft(vessel: Vessel): ArrayList<MessageData?> {
219   - val allDraft: ArrayList<MessageData?> = vessel.getAllDraft()
220   - allDraft.sortBy { (it as Draft).value }
221   -
222   - return allDraft
  273 + return if (observableIsReplayState.value) {
  274 + vessel.getAllDraftBeforeSelectedTime()
  275 + } else {
  276 + vessel.getAllDraft()
  277 + }
223 278 }
224 279  
225 280 private fun populateCargo(vessel: Vessel): ArrayList<MessageData?> {
226   - val allCargo: ArrayList<MessageData?> = vessel.getAllCargo()
227   - allCargo.sortBy { (it as Cargo).value }
228   -
229   - return allCargo
  281 + return if (observableIsReplayState.value) {
  282 + vessel.getAllCargoBeforeSelectedTime()
  283 + } else {
  284 + vessel.getAllCargo()
  285 + }
230 286 }
231 287  
232   - private fun populateDataList(vessel: Vessel) {
  288 + private fun initDataList() {
233 289 val data = arrayListOf<Pair<String, ArrayList<MessageData?>>>()
234 290  
235   - timeData = populateTime(vessel)
  291 + data.add(Pair("Latitude", latitude))
  292 + data.add(Pair("Longitude", longitude))
  293 + data.add(Pair("Speed Over Ground", speedOverGround))
  294 + data.add(Pair("Course Over Ground", courseOverGround))
  295 + data.add(Pair("Heading", heading))
  296 + data.add(Pair("Vessel Name", vesselName))
  297 + data.add(Pair("IMO", imo))
  298 + data.add(Pair("Call Sign", callSign))
  299 + data.add(Pair("Vessel Type", vesselType))
  300 + data.add(Pair("Status", status))
  301 + data.add(Pair("Length", length))
  302 + data.add(Pair("Width", width))
  303 + data.add(Pair("Draft", draft))
  304 + data.add(Pair("Cargo", cargo))
236 305  
237   - data.add(Pair("Latitude", populateLatitude(vessel)))
  306 + dataList.addAll(data)
  307 + }
238 308  
239   - data.add(Pair("Longitude", populateLongitude(vessel)))
  309 + private fun updateDataList(vessel: Vessel) {
  310 + timeData = populateTime(vessel)
240 311  
241   - data.add(Pair("Speed Over Ground", populateSpeedOverGround(vessel)))
  312 + if(dataListView.selectionModel.selectedItem == null) return
242 313  
243   - data.add(Pair("Course Over Ground", populateCourseOverGround(vessel)))
  314 + when (dataListView.selectionModel.selectedItem.first) {
  315 + "Latitude" -> {
  316 + latitude.clear()
  317 + latitude.addAll(populateLatitude(vessel))
  318 + }
  319 + "Longitude" -> {
  320 + longitude.clear()
  321 + longitude.addAll(populateLongitude(vessel))
  322 + }
  323 + "Speed Over Ground" -> {
  324 + speedOverGround.clear()
  325 + speedOverGround.addAll(populateSpeedOverGround(vessel))
  326 + }
  327 + "Course Over Ground" -> {
  328 + courseOverGround.clear()
  329 + courseOverGround.addAll(populateCourseOverGround(vessel))
  330 + }
  331 + "Heading" -> {
  332 + heading.clear()
  333 + heading.addAll(populateHeading(vessel))
  334 + }
  335 + "Vessel Name" -> {
  336 + vesselName.clear()
  337 + vesselName.addAll(populateVesselName(vessel))
  338 + }
  339 + "IMO" -> {
  340 + imo.clear()
  341 + imo.addAll(populateIMO(vessel))
  342 + }
  343 + "Call Sign" -> {
  344 + callSign.clear()
  345 + callSign.addAll(populateCallSign(vessel))
  346 + }
  347 + "Vessel Type" -> {
  348 + vesselType.clear()
  349 + vesselType.addAll(populateVesselType(vessel))
  350 + }
  351 + "Status" -> {
  352 + status.clear()
  353 + status.addAll(populateStatus(vessel))
  354 + }
  355 + "Length" -> {
  356 + length.clear()
  357 + length.addAll(populateLength(vessel))
  358 + }
  359 + "Width" -> {
  360 + width.clear()
  361 + width.addAll(populateWidth(vessel))
  362 + }
  363 + "Draft" -> {
  364 + draft.clear()
  365 + draft.addAll(populateDraft(vessel))
  366 + }
  367 + "Cargo" -> {
  368 + cargo.clear()
  369 + cargo.addAll(populateCargo(vessel))
  370 + }
  371 + }
  372 + }
244 373  
245   - data.add(Pair("Heading", populateHeading(vessel)))
246   -
247   - data.add(Pair("Vessel Name", populateVesselName(vessel)))
248   -
249   - data.add(Pair("IMO", populateIMO(vessel)))
250   -
251   - data.add(Pair("Call Sign", populateCallSign(vessel)))
252   -
253   - data.add(Pair("Vessel Type", populateVesselType(vessel)))
254   -
255   - data.add(Pair("Status", populateStatus(vessel)))
256   -
257   - data.add(Pair("Length", populateLength(vessel)))
258   -
259   - data.add(Pair("Width", populateWidth(vessel)))
260   -
261   - data.add(Pair("Draft", populateDraft(vessel)))
262   -
263   - data.add(Pair("Cargo", populateCargo(vessel)))
264   -
265   - dataList.addAll(data)
  374 + private fun setObservableCurrentTimeListener() {
  375 + observableCurrentTime.listeners.add(object : CurrentTime {
  376 + override fun onValueChanged(newValue: Int) {
  377 + updateDataList(observableSelectedVessel.value)
  378 + plot()
  379 + }
  380 + })
266 381 }
267 382  
268 383 override fun onValueChanged(newValue: Vessel) {
269   - dataList.clear()
270   - populateDataList(newValue)
271   -
  384 + updateDataList(newValue)
  385 + plot()
272 386 }
273 387  
274 388 }
src/main/kotlin/application/controller/MapPanelController.kt View file @ 3d3a5f8
1 1 package application.controller
2 2  
3 3 import application.model.*
4   -import application.model.State.*
  4 +import application.model.MapState.*
  5 +import javafx.application.Platform
5 6 import javafx.fxml.FXML
6 7 import javafx.fxml.Initializable
7 8 import javafx.scene.layout.StackPane
8 9  
9 10  
... ... @@ -16,27 +17,23 @@
16 17  
17 18 private val mapView = LeafletMapView()
18 19  
19   -
20 20 override fun initialize(location: URL?, resources: ResourceBundle?) {
21 21 mapView.displayMap(MapConfig())
22 22 setObservableVesselListener()
23 23 setObservableSelectedVesselListener()
24 24 setStateListener()
25   - /*val completeFutureMap: CompletableFuture<Worker.State> = mapView.displayMap(MapConfig())
26   - completeFutureMap.whenComplete{
27   - workerState, _ ->
28   - if (workerState == Worker.State.SUCCEEDED) {
29   - }
30   - }*/
  25 + setObservableIsReplayState()
  26 + observableCurrentTime()
  27 +
31 28 map.children.add(mapView)
32 29 map.children
33 30 }
34 31  
35 32 private fun setStateListener() {
36   - observableState.listeners.add(object : StateListener {
37   - override fun onValueChanged(newValue: State) {
38   - if (observableSelectedVessel.vessel.mmsi != null) {
39   - updateMap(observableSelectedVessel.vessel.mmsi!!)
  33 + observableMapState.listeners.add(object : StateListener {
  34 + override fun onValueChanged(newValue: MapState) {
  35 + if (observableSelectedVessel.value.mmsi != null) {
  36 + updateMap(observableSelectedVessel.value.mmsi!!)
40 37 } else {
41 38 updateMap()
42 39 }
43 40  
44 41  
... ... @@ -44,19 +41,51 @@
44 41 })
45 42 }
46 43  
  44 + private fun observableCurrentTime() {
  45 + observableCurrentTime.listeners.add(object : CurrentTime {
  46 + override fun onValueChanged(newValue: Int) {
  47 + if (observableSelectedVessel.value.mmsi != null) {
  48 + updateMap(observableSelectedVessel.value.mmsi!!)
  49 + } else {
  50 + updateMap()
  51 + }
  52 + }
  53 + })
  54 + }
  55 +
47 56 private fun updateMap() {
48   - when (observableState.state) {
49   - ALL_MESSAGES -> displayAllMessageOnMap(mapView)
50   - CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView)
51   - HEAT_MAP -> displayHeatMapOnMap(mapView)
  57 + Platform.runLater {
  58 + if (observableIsReplayState.value) {
  59 + when (observableMapState.state) {
  60 + ALL_MESSAGES -> displayTimedAllMessageOnMap(mapView)
  61 + CLUSTERED_MESSAGES -> displayTimedClusterMessageOnMap(mapView)
  62 + HEAT_MAP -> displayTimedHeatMapOnMap(mapView)
  63 + }
  64 + } else {
  65 + when (observableMapState.state) {
  66 + ALL_MESSAGES -> displayAllMessageOnMap(mapView)
  67 + CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView)
  68 + HEAT_MAP -> displayHeatMapOnMap(mapView)
  69 + }
  70 + }
52 71 }
53 72 }
54 73  
55 74 private fun updateMap(selectedMMSI: String) {
56   - when (observableState.state) {
57   - ALL_MESSAGES -> displayAllMessageOnMap(mapView, selectedMMSI)
58   - CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView, selectedMMSI)
59   - HEAT_MAP -> displayHeatMapOnMap(mapView, selectedMMSI)
  75 + Platform.runLater {
  76 + if (observableIsReplayState.value) {
  77 + when (observableMapState.state) {
  78 + ALL_MESSAGES -> displayTimedAllMessageOnMap(mapView, selectedMMSI)
  79 + CLUSTERED_MESSAGES -> displayTimedClusterMessageOnMap(mapView, selectedMMSI)
  80 + HEAT_MAP -> displayTimedHeatMapOnMap(mapView, selectedMMSI)
  81 + }
  82 + } else {
  83 + when (observableMapState.state) {
  84 + ALL_MESSAGES -> displayAllMessageOnMap(mapView, selectedMMSI)
  85 + CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView, selectedMMSI)
  86 + HEAT_MAP -> displayHeatMapOnMap(mapView, selectedMMSI)
  87 + }
  88 + }
60 89 }
61 90 }
62 91  
63 92  
64 93  
65 94  
... ... @@ -68,18 +97,28 @@
68 97 })
69 98 }
70 99  
  100 + private fun setObservableIsReplayState() {
  101 + observableIsReplayState.listeners.add(object : ReplayState {
  102 + override fun onValueChanged(newValue: Boolean) {
  103 + if (observableSelectedVessel.value.mmsi != null) {
  104 + updateMap(observableSelectedVessel.value.mmsi!!)
  105 + } else {
  106 + updateMap()
  107 + }
  108 + }
  109 + })
  110 + }
  111 +
71 112 private fun setObservableSelectedVesselListener() {
72 113 observableSelectedVessel.listeners.add(object : SelectedVesselListener {
73 114 override fun onValueChanged(newValue: Vessel) {
74   - if (newValue.mmsi != null){
  115 + if (newValue.mmsi != null) {
75 116 updateMap(newValue.mmsi)
76   - }else {
  117 + } else {
77 118 updateMap()
78 119 }
79 120 }
80 121 })
81 122 }
82   -
83   -
84 123 }
src/main/kotlin/application/controller/MenuBarController.kt View file @ 3d3a5f8
1 1 package application.controller
2 2  
3   -import application.model.State.*
  3 +import application.model.MapState.*
4 4 import application.model.createVesselCollection
5   -import application.model.observableState
  5 +import application.model.observableIsReplayState
  6 +import application.model.observableMapState
6 7 import application.model.observableVessel
7 8 import javafx.event.EventHandler
8 9 import javafx.fxml.FXML
9 10 import javafx.fxml.Initializable
10   -import javafx.scene.control.Alert
11   -import javafx.scene.control.CheckMenuItem
12   -import javafx.scene.control.MenuBar
13   -import javafx.scene.control.MenuItem
  11 +import javafx.scene.control.*
14 12 import javafx.stage.FileChooser
15 13 import java.net.URL
16 14 import java.util.*
... ... @@ -32,6 +30,9 @@
32 30 @FXML
33 31 var heatMap: CheckMenuItem = CheckMenuItem()
34 32  
  33 + @FXML
  34 + var activateReplayButton: RadioMenuItem = RadioMenuItem()
  35 +
35 36 override fun initialize(location: URL?, resources: ResourceBundle?) {
36 37  
37 38 setOnActionImportButton()
38 39  
... ... @@ -39,13 +40,20 @@
39 40 setOnActionAllMessageButton()
40 41 setOnActionClusteredMessageButton()
41 42 setOnActionHeatMapButton()
42   - observableState.state = CLUSTERED_MESSAGES
  43 + setOnActionActivateReplayButton()
  44 + observableMapState.state = CLUSTERED_MESSAGES
43 45 allMessages.isSelected = false
44 46 clusteredMessage.isSelected = true
45 47 heatMap.isSelected = false
46 48  
47 49 }
48 50  
  51 + private fun setOnActionActivateReplayButton() {
  52 + activateReplayButton.onAction = EventHandler {
  53 + observableIsReplayState.value = activateReplayButton.isSelected
  54 + }
  55 + }
  56 +
49 57 private fun setOnActionImportButton() {
50 58 import.onAction = EventHandler {
51 59 val fileChooser = FileChooser()
... ... @@ -71,7 +79,7 @@
71 79  
72 80 private fun setOnActionAllMessageButton() {
73 81 allMessages.onAction = EventHandler {
74   - observableState.state = ALL_MESSAGES
  82 + observableMapState.state = ALL_MESSAGES
75 83 allMessages.isSelected = true
76 84 clusteredMessage.isSelected = false
77 85 heatMap.isSelected = false
... ... @@ -80,7 +88,7 @@
80 88  
81 89 private fun setOnActionClusteredMessageButton() {
82 90 clusteredMessage.onAction = EventHandler {
83   - observableState.state = CLUSTERED_MESSAGES
  91 + observableMapState.state = CLUSTERED_MESSAGES
84 92 heatMap.isSelected = false
85 93 allMessages.isSelected = false
86 94 clusteredMessage.isSelected = true
... ... @@ -89,7 +97,7 @@
89 97  
90 98 private fun setOnActionHeatMapButton() {
91 99 heatMap.onAction = EventHandler {
92   - observableState.state = HEAT_MAP
  100 + observableMapState.state = HEAT_MAP
93 101 heatMap.isSelected = true
94 102 clusteredMessage.isSelected = false
95 103 allMessages.isSelected = false
src/main/kotlin/application/controller/TimePanel.kt View file @ 3d3a5f8
  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 + disableAllButton()
  28 + setDisable()
  29 + }
  30 +
  31 + private fun setSliderMinMax() {
  32 + timeSlider.min = 0.0
  33 + timeSlider.max = 0.0
  34 + observableVessel.listeners.add(object : MessageListener {
  35 + override fun onValueChanged(newValue: MutableMap<String?, Vessel>) {
  36 + timeSlider.max = Vessel.maxTime.toDouble()
  37 + timeSlider.min = Vessel.minTime.toDouble()
  38 + }
  39 + })
  40 + }
  41 +
  42 + private fun setSliderListener() {
  43 + timeSlider.valueProperty().addListener { _, _, newValue ->
  44 + observableCurrentTime.value = newValue.toInt()
  45 + }
  46 + }
  47 +
  48 + private fun disableAllButton() {
  49 + timeStop.isDisable = true
  50 + timeSlider.isDisable = true
  51 + timePlay.isDisable = true
  52 + }
  53 +
  54 + private fun unableAllButton() {
  55 + timeStop.isDisable = false
  56 + timeSlider.isDisable = false
  57 + timePlay.isDisable = false
  58 + }
  59 +
  60 + private fun setDisable() {
  61 + observableIsReplayState.listeners.add(object : ReplayState {
  62 + override fun onValueChanged(newValue: Boolean) {
  63 + if (observableIsReplayState.value) {
  64 + observableCurrentTime.value = timeSlider.value.toInt()
  65 + unableAllButton()
  66 + } else {
  67 + observableCurrentTime.value = Int.MAX_VALUE
  68 + disableAllButton()
  69 + }
  70 + }
  71 + })
  72 + }
  73 +
  74 +}
src/main/kotlin/application/controller/VesselListPanelController.kt View file @ 3d3a5f8
... ... @@ -6,10 +6,12 @@
6 6 import application.model.observableVessel
7 7 import javafx.collections.FXCollections
8 8 import javafx.collections.ObservableList
9   -import javafx.event.EventHandler
10 9 import javafx.fxml.FXML
11 10 import javafx.fxml.Initializable
12   -import javafx.scene.control.*
  11 +import javafx.scene.control.ListCell
  12 +import javafx.scene.control.ListView
  13 +import javafx.scene.control.MultipleSelectionModel
  14 +import javafx.scene.control.SelectionMode
13 15 import javafx.scene.input.MouseEvent
14 16 import java.net.URL
15 17 import java.util.*
16 18  
... ... @@ -28,9 +30,9 @@
28 30 observableVessel.listeners.add(this)
29 31 shipListView.selectionModel.selectedItemProperty().addListener { _, _, newValue ->
30 32 if (newValue == null) {
31   - observableSelectedVessel.vessel = Vessel(null)
  33 + observableSelectedVessel.value = Vessel(null)
32 34 } else {
33   - observableSelectedVessel.vessel = observableVessel.vessels[newValue]!!
  35 + observableSelectedVessel.value = observableVessel.vessels[newValue]!!
34 36 }
35 37 }
36 38 setCellFactory()
src/main/kotlin/application/model/Context.kt View file @ 3d3a5f8
... ... @@ -4,5 +4,9 @@
4 4  
5 5 val observableSelectedVessel: ObservableSelectedVessel = ObservableSelectedVessel()
6 6  
7   -val observableState: ObservableState = ObservableState()
  7 +val observableMapState: ObservableMapState = ObservableMapState()
  8 +
  9 +val observableIsReplayState: ObservableReplayState = ObservableReplayState()
  10 +
  11 +val observableCurrentTime: ObservableCurrentTime = ObservableCurrentTime()
src/main/kotlin/application/model/CurrentTime.kt View file @ 3d3a5f8
  1 +package application.model
  2 +
  3 +interface CurrentTime {
  4 + fun onValueChanged(newValue: Int)
  5 +}
src/main/kotlin/application/model/MapState.kt View file @ 3d3a5f8
  1 +package application.model
  2 +
  3 +enum class MapState {
  4 + ALL_MESSAGES,
  5 + CLUSTERED_MESSAGES,
  6 + HEAT_MAP
  7 +}
src/main/kotlin/application/model/ObservableCurrentTime.kt View file @ 3d3a5f8
  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 = Int.MAX_VALUE,
  11 + onChange = { _, _, new ->
  12 + run {
  13 + listeners.forEach {
  14 + it.onValueChanged(new)
  15 + }
  16 + }
  17 + }
  18 + )
  19 +
  20 +}
src/main/kotlin/application/model/ObservableMapState.kt View file @ 3d3a5f8
  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 + }
  16 + }
  17 + )
  18 +}
src/main/kotlin/application/model/ObservableReplayState.kt View file @ 3d3a5f8
  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 = false,
  11 + onChange = { _, _, new ->
  12 + run {
  13 + listeners.forEach {
  14 + it.onValueChanged(new)
  15 + }
  16 + }
  17 + }
  18 + )
  19 +
  20 +}
src/main/kotlin/application/model/ObservableSelectedVessel.kt View file @ 3d3a5f8
... ... @@ -5,7 +5,7 @@
5 5 class ObservableSelectedVessel {
6 6 val listeners: MutableList<SelectedVesselListener> = mutableListOf()
7 7  
8   - var vessel: Vessel by Delegates.observable(
  8 + var value: Vessel by Delegates.observable(
9 9 initialValue = Vessel(null),
10 10 onChange = { _, _, new ->
11 11 run {
src/main/kotlin/application/model/ObservableState.kt View file @ 3d3a5f8
1   -package application.model
2   -
3   -import kotlin.properties.Delegates
4   -
5   -class ObservableState {
6   - val listeners: MutableList<StateListener> = mutableListOf()
7   -
8   - var state: State by Delegates.observable(
9   - initialValue = State.CLUSTERED_MESSAGES,
10   - onChange = { _, _, new ->
11   - run {
12   - listeners.forEach {
13   - it.onValueChanged(new)
14   - }
15   - }
16   - }
17   - )
18   -}
src/main/kotlin/application/model/ObservableVessel.kt View file @ 3d3a5f8
... ... @@ -9,7 +9,7 @@
9 9 initialValue = mutableMapOf(),
10 10 onChange = { _, _, new ->
11 11 run {
12   - observableSelectedVessel.vessel = Vessel(null)
  12 + observableSelectedVessel.value = Vessel(null)
13 13 listeners.forEach {
14 14 it.onValueChanged(new)
15 15 }
src/main/kotlin/application/model/ReplayState.kt View file @ 3d3a5f8
  1 +package application.model
  2 +
  3 +interface ReplayState {
  4 + fun onValueChanged(newValue: Boolean)
  5 +
  6 +}
src/main/kotlin/application/model/State.kt View file @ 3d3a5f8
1   -package application.model
2   -
3   -enum class State {
4   - ALL_MESSAGES,
5   - CLUSTERED_MESSAGES,
6   - HEAT_MAP
7   -}
src/main/kotlin/application/model/StateListener.kt View file @ 3d3a5f8
1 1 package application.model
2 2  
3 3 interface StateListener {
4   - fun onValueChanged(newValue: State)
  4 + fun onValueChanged(newValue: MapState)
5 5 }
src/main/kotlin/application/model/Vessel.kt View file @ 3d3a5f8
1 1 package application.model
2 2  
3   -import java.time.LocalDateTime
4   -import java.time.ZoneOffset
5 3 import java.util.*
6 4  
7   -
8 5 class Vessel(val mmsi: String?) {
9   - val messages: SortedMap<LocalDateTime, Message> = sortedMapOf()
  6 + val messages: SortedMap<Long, Message> = sortedMapOf()
  7 + private val messageBeforeSelectedTime: Map<Long, Message>
  8 + get() {
  9 + return messages.filter { observableCurrentTime.value > it.key }
  10 + }
10 11  
  12 + var messageToDisplay: Message? = null
  13 + get() {
  14 + field =
  15 + messages.asSequence().map { it }.firstOrNull { observableCurrentTime.value < it.key }.let { it?.value }
  16 + return field
  17 + }
  18 +
  19 + fun getAllTimeBeforeSelectedTime(): ArrayList<MessageData?> {
  20 + val timeList = arrayListOf<MessageData?>()
  21 + messageBeforeSelectedTime.forEach {
  22 + timeList.add(it.value.time)
  23 + }
  24 +
  25 + return timeList
  26 + }
  27 +
  28 + fun getAllLatitudeBeforeSelectedTime(): ArrayList<MessageData?> {
  29 + val latitudeList = arrayListOf<MessageData?>()
  30 + messageBeforeSelectedTime.forEach {
  31 + latitudeList.add(it.value.latitude)
  32 + }
  33 +
  34 + return latitudeList
  35 + }
  36 +
  37 + fun getAllLongitudeBeforeSelectedTime(): ArrayList<MessageData?> {
  38 + val longitudeList = arrayListOf<MessageData?>()
  39 + messageBeforeSelectedTime.forEach {
  40 + longitudeList.add(it.value.longitude)
  41 + }
  42 +
  43 + return longitudeList
  44 + }
  45 +
  46 + fun getAllSpeedOverGroundBeforeSelectedTime(): ArrayList<MessageData?> {
  47 + val speedOverGroundList = arrayListOf<MessageData?>()
  48 + messageBeforeSelectedTime.forEach {
  49 + speedOverGroundList.add(it.value.speedOverGround)
  50 + }
  51 +
  52 + return speedOverGroundList
  53 + }
  54 +
  55 + fun getAllCourseOverGroundBeforeSelectedTime(): ArrayList<MessageData?> {
  56 + val res = arrayListOf<MessageData?>()
  57 + messageBeforeSelectedTime.forEach {
  58 + res.add(it.value.courseOverGround)
  59 + }
  60 +
  61 + return res
  62 + }
  63 +
  64 + fun getAllHeadingBeforeSelectedTime(): ArrayList<MessageData?> {
  65 + val res = arrayListOf<MessageData?>()
  66 + messageBeforeSelectedTime.forEach {
  67 + res.add(it.value.heading)
  68 + }
  69 +
  70 + return res
  71 + }
  72 +
  73 + fun getAllVesselNameBeforeSelectedTime(): ArrayList<MessageData?> {
  74 + val res = arrayListOf<MessageData?>()
  75 + messageBeforeSelectedTime.forEach {
  76 + res.add(it.value.vesselName)
  77 + }
  78 + return res
  79 + }
  80 +
  81 + fun getAllIMOBeforeSelectedTime(): ArrayList<MessageData?> {
  82 + val res = arrayListOf<MessageData?>()
  83 + messageBeforeSelectedTime.forEach {
  84 + res.add(it.value.imo)
  85 + }
  86 + return res
  87 + }
  88 +
  89 + fun getAllCallSignBeforeSelectedTime(): ArrayList<MessageData?> {
  90 + val res = arrayListOf<MessageData?>()
  91 + messageBeforeSelectedTime.forEach {
  92 + res.add(it.value.callSign)
  93 + }
  94 + return res
  95 + }
  96 +
  97 + fun getAllVesselTypeBeforeSelectedTime(): ArrayList<MessageData?> {
  98 + val res = arrayListOf<MessageData?>()
  99 + messageBeforeSelectedTime.forEach {
  100 + res.add(it.value.vesselType)
  101 + }
  102 + return res
  103 + }
  104 +
  105 + fun getAllStatusBeforeSelectedTime(): ArrayList<MessageData?> {
  106 + val res = arrayListOf<MessageData?>()
  107 + messageBeforeSelectedTime.forEach {
  108 + res.add(it.value.status)
  109 + }
  110 + return res
  111 + }
  112 +
  113 + fun getAllLengthBeforeSelectedTime(): ArrayList<MessageData?> {
  114 + val res = arrayListOf<MessageData?>()
  115 + messageBeforeSelectedTime.forEach {
  116 + res.add(it.value.length)
  117 + }
  118 + return res
  119 + }
  120 +
  121 + fun getAllWidthBeforeSelectedTime(): ArrayList<MessageData?> {
  122 + val res = arrayListOf<MessageData?>()
  123 + messageBeforeSelectedTime.forEach {
  124 + res.add(it.value.width)
  125 + }
  126 + return res
  127 + }
  128 +
  129 + fun getAllDraftBeforeSelectedTime(): ArrayList<MessageData?> {
  130 + val res = arrayListOf<MessageData?>()
  131 + messageBeforeSelectedTime.forEach {
  132 + res.add(it.value.draft)
  133 + }
  134 + return res
  135 + }
  136 +
  137 + fun getAllCargoBeforeSelectedTime(): ArrayList<MessageData?> {
  138 + val res = arrayListOf<MessageData?>()
  139 + messageBeforeSelectedTime.forEach {
  140 + res.add(it.value.cargo)
  141 + }
  142 + return res
  143 + }
  144 +
11 145 fun getAllTime(): ArrayList<MessageData?> {
12 146 val timeList = arrayListOf<MessageData?>()
13 147 messages.forEach {
... ... @@ -132,6 +266,11 @@
132 266 res.add(it.value.cargo)
133 267 }
134 268 return res
  269 + }
  270 +
  271 + companion object {
  272 + var maxTime: Long = 0
  273 + var minTime: Long = 0
135 274 }
136 275  
137 276 }
src/main/kotlin/application/model/VesselGenerator.kt View file @ 3d3a5f8
1 1 package application.model
2 2  
3 3 import java.io.File
  4 +import java.time.ZoneOffset
4 5 import java.util.*
5   -import kotlin.collections.ArrayList
6 6  
7   -fun createVesselCollection(file: File) : SortedMap<String, Vessel> {
8   - val messages : ArrayList<Message> = arrayListOf()
  7 +fun createVesselCollection(file: File): SortedMap<String, Vessel> {
  8 + val messages: ArrayList<Message> = arrayListOf()
9 9 val vessels: SortedMap<String, Vessel> = sortedMapOf()
  10 + var maxTime: Long = 0
  11 + var minTime: Long = Long.MAX_VALUE
10 12  
11 13 file.forEachLine {
12 14 val arrayMessage = it.split(",")
13 15 if (arrayMessage[0].toIntOrNull() !== null) {
14 16 val message = Message(arrayMessage)
15 17 messages.add(message)
16   - if (!vessels.containsKey(message.mmsi.value)){
  18 + if (!vessels.containsKey(message.mmsi.value)) {
17 19 vessels[message.mmsi.value] = Vessel(message.mmsi.value!!)
18 20 }
19   - vessels[message.mmsi.value]?.messages?.set(message.time.value, message)
  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  
24 35 return vessels
25 36 }
src/main/kotlin/map/LeafletMapView.kt View file @ 3d3a5f8
... ... @@ -93,6 +93,7 @@
93 93 | layers: [layer1]
94 94 |});
95 95 |L.control.scale().addTo(myMap);
  96 + |var markers = []
96 97 |var myRenderer = L.canvas({ padding: 0.5 });
97 98 |var markerClusters = L.markerClusterGroup({spiderfyOnMaxZoom: false, disableClusteringAtZoom: 10});
98 99 |var heatLayer = L.heatLayer([]).addTo(myMap);""".trimMargin()
src/main/kotlin/map/MapDisplayer.kt View file @ 3d3a5f8
... ... @@ -6,6 +6,7 @@
6 6 clearMapCanvas(map)
7 7 clearMapCluster(map)
8 8 clearHeatMap(map)
  9 + clearMarker(map)
9 10 }
10 11  
11 12 fun clearMapCluster(map: LeafletMapView) {
... ... @@ -35,6 +36,17 @@
35 36 )
36 37 }
37 38  
  39 +fun clearMarker(map: LeafletMapView) {
  40 + map.execScript(
  41 + """
  42 + |for(var i = 0; i < markers.length; i++){
  43 + |myMap.removeLayer(markers[i]);
  44 + |}
  45 + |markers = []
  46 + """.trimMargin()
  47 + )
  48 +}
  49 +
38 50 fun displayAllMessageOnMap(map: LeafletMapView) {
39 51 clearMap(map)
40 52 observableVessel.vessels.forEach { (_, value) ->
41 53  
42 54  
43 55  
... ... @@ -44,19 +56,39 @@
44 56 }
45 57 }
46 58  
  59 +fun displayTimedAllMessageOnMap(map: LeafletMapView) {
  60 + clearMap(map)
  61 + observableVessel.vessels.forEach { (_, value) ->
  62 + val message = value.messageToDisplay ?: return@forEach
  63 + map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap))")
  64 + }
  65 +}
  66 +
47 67 fun displayAllMessageOnMap(map: LeafletMapView, selectedMMSI: String) {
48 68 clearMap(map)
49 69 observableVessel.vessels.forEach { (_, value) ->
50 70 value.messages.forEach { (_, message) ->
51 71 if (selectedMMSI == message.mmsi.value) {
52   - map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap)")
  72 + map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 2, color: '#ff4040'}).addTo(myMap))")
53 73 } else {
54   - map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap)")
  74 + map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap))")
55 75 }
56 76 }
57 77 }
58 78 }
59 79  
  80 +fun displayTimedAllMessageOnMap(map: LeafletMapView, selectedMMSI: String) {
  81 + clearMap(map)
  82 + observableVessel.vessels.forEach { (_, value) ->
  83 + val message = value.messageToDisplay ?: return@forEach
  84 + if (selectedMMSI == message.mmsi.value) {
  85 + map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap))")
  86 + } else {
  87 + map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap))")
  88 + }
  89 + }
  90 +}
  91 +
60 92 fun displayClusterMessageOnMap(map: LeafletMapView) {
61 93 clearMap(map)
62 94 observableVessel.vessels.forEach { (_, value) ->
63 95  
... ... @@ -67,12 +99,21 @@
67 99 map.execScript("myMap.addLayer(markerClusters);")
68 100 }
69 101  
  102 +fun displayTimedClusterMessageOnMap(map: LeafletMapView) {
  103 + clearMap(map)
  104 + observableVessel.vessels.forEach { (_, value) ->
  105 + val message = value.messageToDisplay ?: return@forEach
  106 + map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 0.01, color: '#${message.getHexColorStroke()}'}));")
  107 + }
  108 + map.execScript("myMap.addLayer(markerClusters);")
  109 +}
  110 +
70 111 fun displayClusterMessageOnMap(map: LeafletMapView, selectedMMSI: String) {
71 112 clearMap(map)
72 113 observableVessel.vessels.forEach { (_, value) ->
73 114 value.messages.forEach { (_, message) ->
74 115 if (selectedMMSI == message.mmsi.value) {
75   - map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap);")
  116 + map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 2, color: '#ff4040'}).addTo(myMap));")
76 117 } else {
77 118 map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}));")
78 119 }
... ... @@ -81,6 +122,19 @@
81 122 map.execScript("myMap.addLayer(markerClusters);")
82 123 }
83 124  
  125 +fun displayTimedClusterMessageOnMap(map: LeafletMapView, selectedMMSI: String) {
  126 + clearMap(map)
  127 + observableVessel.vessels.forEach { (_, value) ->
  128 + val message = value.messageToDisplay ?: return@forEach
  129 + if (selectedMMSI == message.mmsi.value) {
  130 + map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 2, color: '#ff4040'}).addTo(myMap));")
  131 + } else {
  132 + map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 0.01, color: '#${message.getHexColorStroke()}'}));")
  133 + }
  134 + }
  135 + map.execScript("myMap.addLayer(markerClusters);")
  136 +}
  137 +
84 138 fun displayHeatMapOnMap(map: LeafletMapView) {
85 139 clearMap(map)
86 140 observableVessel.vessels.forEach { (_, value) ->
87 141  
88 142  
... ... @@ -90,17 +144,37 @@
90 144 }
91 145 }
92 146  
  147 +fun displayTimedHeatMapOnMap(map: LeafletMapView) {
  148 + clearMap(map)
  149 + observableVessel.vessels.forEach { (_, value) ->
  150 + val message = value.messageToDisplay ?: return@forEach
  151 + map.execScript("heatLayer.addLatLng([${message.latitude.value}, ${message.longitude.value}]);")
  152 + }
  153 +}
  154 +
93 155 fun displayHeatMapOnMap(map: LeafletMapView, selectedMMSI: String) {
94 156 clearMap(map)
95 157 observableVessel.vessels.forEach { (_, value) ->
96 158 value.messages.forEach { (_, message) ->
97 159 if (selectedMMSI == message.mmsi.value) {
98   - map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap);")
  160 + map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 2, color: '#ff4040'}).addTo(myMap));")
99 161 } else {
100 162 map.execScript("heatLayer.addLatLng([${message.latitude.value}, ${message.longitude.value}]);")
101 163 }
102 164 }
103 165 }
104 166 map.execScript("myMap.addLayer(markerClusters);")
  167 +}
  168 +
  169 +fun displayTimedHeatMapOnMap(map: LeafletMapView, selectedMMSI: String) {
  170 + clearMap(map)
  171 + observableVessel.vessels.forEach { (_, value) ->
  172 + val message = value.messageToDisplay ?: return@forEach
  173 + if (selectedMMSI == message.mmsi.value) {
  174 + map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 2, color: '#ff4040'}).addTo(myMap));")
  175 + } else {
  176 + map.execScript("heatLayer.addLatLng([${message.latitude.value}, ${message.longitude.value}]);")
  177 + }
  178 + }
105 179 }
src/main/resources/gui/mapPanel.fxml View file @ 3d3a5f8
1 1 <?xml version="1.0" encoding="UTF-8"?>
2 2  
3   -<?import javafx.scene.layout.StackPane?>
  3 +<?import javafx.scene.layout.*?>
4 4  
5   -<StackPane prefHeight="150.0" prefWidth="200.0" xmlns="http://javafx.com/javafx"
6   - xmlns:fx="http://javafx.com/fxml"
7   - fx:controller="application.controller.MapPanelController"
8   - fx:id="map"/>
  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">
  6 + <StackPane fx:id="map" />
  7 + <fx:include source="timePanel.fxml" />
  8 +</VBox>
src/main/resources/gui/menuBar.fxml View file @ 3d3a5f8
... ... @@ -2,7 +2,7 @@
2 2  
3 3 <?import javafx.scene.control.*?>
4 4  
5   -<MenuBar fx:id="menuBar" prefHeight="25.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.controller.MenuBarController">
  5 +<MenuBar fx:id="menuBar" prefHeight="25.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.controller.MenuBarController">
6 6 <menus>
7 7 <Menu mnemonicParsing="false" text="File">
8 8 <items>
9 9  
... ... @@ -13,11 +13,12 @@
13 13 <items>
14 14 <Menu mnemonicParsing="false" text="Map selected">
15 15 <items>
16   - <CheckMenuItem mnemonicParsing="false" text="All messages" fx:id="allMessages"/>
17   - <CheckMenuItem mnemonicParsing="false" text="Clustered messages" fx:id="clusteredMessage"/>
18   - <CheckMenuItem mnemonicParsing="false" text="Heat map" fx:id="heatMap"/>
  16 + <CheckMenuItem fx:id="allMessages" mnemonicParsing="false" text="All messages" />
  17 + <CheckMenuItem fx:id="clusteredMessage" mnemonicParsing="false" text="Clustered messages" />
  18 + <CheckMenuItem fx:id="heatMap" mnemonicParsing="false" text="Heat map" />
19 19 </items>
20 20 </Menu>
  21 + <RadioMenuItem fx:id="activateReplayButton" mnemonicParsing="false" text="Unspecified Action" />
21 22 </items>
22 23 </Menu>
23 24 <Menu mnemonicParsing="false" text="Help">
src/main/resources/gui/timePanel.fxml View file @ 3d3a5f8
  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 + <Slider fx:id="timeSlider" prefHeight="14.0" prefWidth="538.0">
  10 + <HBox.margin>
  11 + <Insets left="5.0" right="5.0" />
  12 + </HBox.margin></Slider>
  13 + </children>
  14 +</HBox>