Commit 3d3a5f83b85c12a57d28e50b14ae0673bde17205
Exists in
master
Merge branch 'dev'
Showing 26 changed files Inline Diff
- build.gradle
- src/main/kotlin/application/App.kt
- 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/controller/VesselListPanelController.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/ObservableSelectedVessel.kt
- src/main/kotlin/application/model/ObservableState.kt
- src/main/kotlin/application/model/ObservableVessel.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/LeafletMapView.kt
- src/main/kotlin/map/MapDisplayer.kt
- src/main/resources/gui/mapPanel.fxml
- src/main/resources/gui/menuBar.fxml
- src/main/resources/gui/timePanel.fxml
build.gradle
View file @
3d3a5f8
plugins { | 1 | 1 | plugins { | |
id 'java' | 2 | 2 | id 'java' | |
id 'org.jetbrains.kotlin.jvm' version '1.3.72' | 3 | 3 | id 'org.jetbrains.kotlin.jvm' version '1.3.72' | |
} | 4 | 4 | } | |
5 | 5 | |||
group 'marivisu' | 6 | 6 | group 'marivisu' | |
version '1.0-SNAPSHOT' | 7 | 7 | version '1.0-SNAPSHOT' | |
8 | 8 | |||
repositories { | 9 | 9 | repositories { | |
mavenCentral() | 10 | 10 | mavenCentral() | |
maven { | 11 | 11 | maven { | |
url "https://jitpack.io" | 12 | 12 | url "https://jitpack.io" | |
} | 13 | 13 | } | |
} | 14 | 14 | } | |
15 | 15 | |||
dependencies { | 16 | 16 | dependencies { | |
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" | 17 | 17 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" | |
implementation 'org.jfxtras:jmetro:8.6.9' | 18 | 18 | implementation 'org.jfxtras:jmetro:8.6.9' | |
implementation 'org.slf4j:slf4j-api:1.7.30' | 19 | 19 | implementation 'org.slf4j:slf4j-api:1.7.30' | |
20 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9" | |||
testCompile group: 'junit', name: 'junit', version: '4.12' | 20 | 21 | testCompile group: 'junit', name: 'junit', version: '4.12' | |
testCompile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3' | 21 | 22 | testCompile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3' | |
compile group: 'ch.qos.logback', name: 'logback-core', version: '1.2.3' | 22 | 23 | compile group: 'ch.qos.logback', name: 'logback-core', version: '1.2.3' | |
compile 'com.github.jasrodis:javafx-dataviewer-wrapper:-SNAPSHOT' | 23 | 24 | compile 'com.github.jasrodis:javafx-dataviewer-wrapper:-SNAPSHOT' | |
} | 24 | 25 | } | |
25 | 26 | |||
task run(type: JavaExec) { | 26 | 27 | task run(type: JavaExec) { | |
main = 'application.App' | 27 | 28 | main = 'application.App' | |
classpath = sourceSets.main.runtimeClasspath | 28 | 29 | classpath = sourceSets.main.runtimeClasspath | |
} | 29 | 30 | } | |
30 | 31 | |||
compileKotlin { | 31 | 32 | compileKotlin { |
src/main/kotlin/application/App.kt
View file @
3d3a5f8
@file:JvmName("App") | 1 | 1 | @file:JvmName("App") | |
2 | 2 | |||
package application | 3 | 3 | package application | |
4 | 4 | |||
import javafx.application.Application | 5 | 5 | import javafx.application.Application | |
import javafx.application.Platform | 6 | 6 | import javafx.application.Platform | |
import javafx.event.EventHandler | 7 | 7 | import javafx.event.EventHandler | |
import javafx.fxml.FXMLLoader | 8 | 8 | import javafx.fxml.FXMLLoader | |
import javafx.scene.Parent | 9 | 9 | import javafx.scene.Parent | |
import javafx.scene.Scene | 10 | 10 | import javafx.scene.Scene | |
import javafx.stage.Stage | 11 | 11 | import javafx.stage.Stage | |
import jfxtras.styles.jmetro.JMetro | 12 | 12 | import jfxtras.styles.jmetro.JMetro | |
import jfxtras.styles.jmetro.Style | 13 | 13 | import jfxtras.styles.jmetro.Style | |
import kotlin.system.exitProcess | 14 | 14 | import kotlin.system.exitProcess | |
15 | 15 | |||
class App : Application() { | 16 | 16 | class App : Application() { | |
var style: Style = Style.LIGHT | 17 | 17 | var style: Style = Style.LIGHT | |
18 | 18 | |||
override fun start(primaryStage: Stage?) { | 19 | 19 | override fun start(primaryStage: Stage?) { | |
20 | 20 | |||
val fxmlLoader = FXMLLoader(App::class.java.getResource("/gui/windows.fxml")) | 21 | 21 | val fxmlLoader = FXMLLoader(App::class.java.getResource("/gui/windows.fxml")) | |
val parent: Parent = fxmlLoader.load() | 22 | 22 | val parent: Parent = fxmlLoader.load() | |
val scene = Scene(parent) | 23 | 23 | val scene = Scene(parent) | |
JMetro(scene, style) | 24 | 24 | JMetro(scene, style) | |
primaryStage!!.scene = scene | 25 | 25 | primaryStage!!.scene = scene | |
primaryStage.title = "Maritime Visualisation" | 26 | 26 | primaryStage.title = "Maritime Visualisation" | |
primaryStage.onCloseRequest = EventHandler { closeApplication() } | 27 | 27 | primaryStage.onCloseRequest = EventHandler { closeApplication() } | |
primaryStage.show() | 28 | 28 | primaryStage.show() | |
} | 29 | 29 | } | |
30 | 30 | |||
private fun closeApplication() { | 31 | 31 | private fun closeApplication() { | |
32 | ||||
Platform.exit() | 32 | 33 | Platform.exit() | |
exitProcess(0) | 33 | 34 | exitProcess(0) | |
} | 34 | 35 | } | |
35 | 36 |
src/main/kotlin/application/controller/DataPanelController.kt
View file @
3d3a5f8
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 | |
10 | import kotlinx.coroutines.GlobalScope | |||
11 | import kotlinx.coroutines.launch | |||
import org.charts.dataviewer.api.config.DataViewerConfiguration | 10 | 12 | import org.charts.dataviewer.api.config.DataViewerConfiguration | |
import org.charts.dataviewer.api.data.PlotData | 11 | 13 | import org.charts.dataviewer.api.data.PlotData | |
import org.charts.dataviewer.api.trace.ScatterTrace | 12 | 14 | import org.charts.dataviewer.api.trace.ScatterTrace | |
import org.charts.dataviewer.javafx.JavaFxDataViewer | 13 | 15 | import org.charts.dataviewer.javafx.JavaFxDataViewer | |
import org.charts.dataviewer.utils.TraceColour | 14 | 16 | import org.charts.dataviewer.utils.TraceColour | |
import org.charts.dataviewer.utils.TraceVisibility | 15 | 17 | import org.charts.dataviewer.utils.TraceVisibility | |
import java.net.URL | 16 | 18 | import java.net.URL | |
import java.util.* | 17 | 19 | import java.util.* | |
18 | 20 | |||
19 | 21 | |||
class DataPanelController : Initializable, SelectedVesselListener { | 20 | 22 | class DataPanelController : Initializable, SelectedVesselListener { | |
private var dataList: ObservableList<Pair<String, ArrayList<MessageData?>>> = FXCollections.observableArrayList() | 21 | 23 | private var dataList: ObservableList<Pair<String, ArrayList<MessageData?>>> = FXCollections.observableArrayList() | |
private lateinit var timeData: ArrayList<MessageData?> | 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 | ||||
@FXML | 25 | 43 | @FXML | |
var dataListView = ListView<Pair<String, ArrayList<MessageData?>>>() | 26 | 44 | var dataListView = ListView<Pair<String, ArrayList<MessageData?>>>() | |
27 | 45 | |||
@FXML | 28 | 46 | @FXML | |
var dataViewer = JavaFxDataViewer() | 29 | 47 | var dataViewer = JavaFxDataViewer() | |
30 | 48 | |||
49 | private val plotData = PlotData() | |||
50 | private val config = DataViewerConfiguration() | |||
31 | 51 | |||
override fun initialize(location: URL?, resources: ResourceBundle?) { | 32 | 52 | override fun initialize(location: URL?, resources: ResourceBundle?) { | |
setObservableSelectedVesselListener() | 33 | 53 | setObservableSelectedVesselListener() | |
dataListView.items = dataList | 34 | 54 | dataListView.items = dataList | |
35 | 55 | |||
36 | 56 | |||
val plotData = PlotData() | 37 | 57 | ||
val config = DataViewerConfiguration() | 38 | 58 | config.showLegend(false) | |
config.showLegend(true) | 39 | |||
config.setLegendInsidePlot(false) | 40 | 59 | config.setLegendInsidePlot(false) | |
41 | 60 | |||
61 | setObservableCurrentTimeListener() | |||
62 | ||||
dataListView.setCellFactory { | 42 | 63 | dataListView.setCellFactory { | |
object : ListCell<Pair<String, ArrayList<MessageData?>>?>() { | 43 | 64 | object : ListCell<Pair<String, ArrayList<MessageData?>>?>() { | |
override fun updateItem(item: Pair<String, ArrayList<MessageData?>>?, empty: Boolean) { | 44 | 65 | override fun updateItem(item: Pair<String, ArrayList<MessageData?>>?, empty: Boolean) { | |
super.updateItem(item, empty) | 45 | 66 | super.updateItem(item, empty) | |
text = if (empty) { | 46 | 67 | text = if (empty) { | |
null | 47 | 68 | null | |
} else { | 48 | 69 | } else { | |
item?.first | 49 | 70 | item?.first | |
} | 50 | 71 | } | |
} | 51 | 72 | } | |
} | 52 | 73 | } | |
} | 53 | 74 | } | |
54 | 75 | |||
dataListView.selectionModel.selectedItemProperty().addListener { _, _, newValue -> | 55 | 76 | dataListView.selectionModel.selectedItemProperty().addListener { _, _, newValue -> | |
if (newValue == null) { | 56 | 77 | selectedItem = newValue | |
plotData.allTraces.clear() | 57 | 78 | updateDataList(observableSelectedVessel.value) | |
config.setxAxisTitle("") | 58 | 79 | plot(newValue) | |
config.setyAxisTitle("") | 59 | 80 | } | |
dataViewer.updateConfiguration(config) | 60 | |||
61 | 81 | |||
dataViewer.resetPlot() | 62 | 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 | |||
return@addListener | 64 | 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 | |||
val getValueVisitorX = GetValueVisitor() | 67 | 101 | private fun plot(data: Pair<String, ArrayList<MessageData?>>?) { | |
val getValueVisitorY = GetValueVisitor() | 68 | 102 | if (data == null) { | |
103 | plotData.allTraces.clear() | |||
104 | config.setxAxisTitle("") | |||
105 | config.setyAxisTitle("") | |||
106 | dataViewer.updateConfiguration(config) | |||
69 | 107 | |||
val arrayListStringX = arrayListOf<String>() | 70 | 108 | dataViewer.resetPlot() | |
val arrayListDoubleX = arrayListOf<Double>() | 71 | |||
val arrayListStringY = arrayListOf<String>() | 72 | |||
val arrayListDoubleY = arrayListOf<Double>() | 73 | |||
74 | 109 | |||
for (x in 0 until newValue.second.size) { | 75 | 110 | return | |
timeData[x]?.accept(getValueVisitorX) | 76 | 111 | }else if (data.second.size < timeData.size) return | |
newValue.second[x]?.accept(getValueVisitorY) | 77 | |||
78 | 112 | |||
if (getValueVisitorY.value.toDoubleOrNull() == null) { | 79 | 113 | val getValueVisitorX = GetValueVisitor() | |
arrayListStringX.add(getValueVisitorX.value) | 80 | 114 | val getValueVisitorY = GetValueVisitor() | |
arrayListStringY.add(getValueVisitorY.value) | 81 | |||
} else { | 82 | |||
arrayListStringX.add(getValueVisitorX.value) | 83 | |||
arrayListDoubleY.add(getValueVisitorY.value.toDouble()) | 84 | |||
} | 85 | |||
} | 86 | |||
87 | 115 | |||
val scatterTrace = ScatterTrace<Any>() | 88 | 116 | val arrayListStringX = arrayListOf<String>() | |
scatterTrace.traceColour = TraceColour.RED | 89 | 117 | // val arrayListDoubleX = arrayListOf<Double>() | |
scatterTrace.traceVisibility = TraceVisibility.TRUE | 90 | 118 | val arrayListStringY = arrayListOf<String>() | |
119 | val arrayListDoubleY = arrayListOf<Double>() | |||
91 | 120 | |||
val serieStringX: Array<String> = arrayListStringX.toTypedArray() | 92 | 121 | for (x in 0 until timeData.size) { | |
// val serieDoubleX: Array<Double> = arrayListDoubleX.toTypedArray() | 93 | 122 | timeData[x]?.accept(getValueVisitorX) | |
val serieStringY: Array<String> = arrayListStringY.toTypedArray() | 94 | 123 | data.second[x]?.accept(getValueVisitorY) | |
val serieDoubleY: Array<Double> = arrayListDoubleY.toTypedArray() | 95 | |||
96 | 124 | |||
if (getValueVisitorY.value.toDoubleOrNull() == null) { | 97 | 125 | if (getValueVisitorY.value.toDoubleOrNull() == null) { | |
scatterTrace.setxArray(serieStringX) | 98 | 126 | arrayListStringX.add(getValueVisitorX.value) | |
scatterTrace.setyArray(serieStringY) | 99 | 127 | arrayListStringY.add(getValueVisitorY.value) | |
} else { | 100 | 128 | } else { | |
scatterTrace.setxArray(serieStringX) | 101 | 129 | arrayListStringX.add(getValueVisitorX.value) | |
scatterTrace.setyArray(serieDoubleY) | 102 | 130 | arrayListDoubleY.add(getValueVisitorY.value.toDouble()) | |
} | 103 | 131 | } | |
132 | } | |||
104 | 133 | |||
config.setxAxisTitle("Date") | 105 | 134 | val scatterTrace = ScatterTrace<Any>() | |
config.setyAxisTitle(newValue.first) | 106 | 135 | scatterTrace.traceColour = TraceColour.RED | |
dataViewer.resetPlot() | 107 | 136 | scatterTrace.traceVisibility = TraceVisibility.TRUE | |
plotData.allTraces.clear() | 108 | |||
plotData.addTrace(scatterTrace) | 109 | |||
dataViewer.updateConfiguration(config) | 110 | |||
dataViewer.updatePlot(plotData) | 111 | |||
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() | |||
plotData.allTraces.clear() | 115 | 155 | plotData.allTraces.clear() | |
config.setxAxisTitle("") | 116 | 156 | plotData.addTrace(scatterTrace) | |
config.setyAxisTitle("") | 117 | |||
dataViewer.updateConfiguration(config) | 118 | 157 | dataViewer.updateConfiguration(config) | |
dataViewer.updatePlot(plotData) | 119 | 158 | dataViewer.updatePlot(plotData) | |
120 | 159 | |||
} | 121 | 160 | } | |
122 | 161 | |||
162 | ||||
private fun setObservableSelectedVesselListener() { | 123 | 163 | private fun setObservableSelectedVesselListener() { | |
observableSelectedVessel.listeners.add(this) | 124 | 164 | observableSelectedVessel.listeners.add(this) | |
} | 125 | 165 | } | |
126 | 166 | |||
private fun populateTime(vessel: Vessel): ArrayList<MessageData?> { | 127 | 167 | private fun populateTime(vessel: Vessel): ArrayList<MessageData?> { | |
val allTime: ArrayList<MessageData?> = vessel.getAllTime() | 128 | 168 | return if (observableIsReplayState.value) { | |
allTime.sortBy { (it as Time).value } | 129 | 169 | vessel.getAllTimeBeforeSelectedTime() | |
130 | 170 | } else { | ||
return allTime | 131 | 171 | vessel.getAllTime() | |
172 | } | |||
} | 132 | 173 | } | |
133 | 174 | |||
private fun populateLatitude(vessel: Vessel): ArrayList<MessageData?> { | 134 | |||
val allLatitude: ArrayList<MessageData?> = vessel.getAllLatitude() | 135 | |||
allLatitude.sortBy { (it as Latitude).value } | 136 | |||
137 | 175 | |||
return allLatitude | 138 | 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 | |||
private fun populateLongitude(vessel: Vessel): ArrayList<MessageData?> { | 141 | 184 | private fun populateLongitude(vessel: Vessel): ArrayList<MessageData?> { | |
val allLongitude: ArrayList<MessageData?> = vessel.getAllLongitude() | 142 | 185 | return if (observableIsReplayState.value) { | |
allLongitude.sortBy { (it as Longitude).value } | 143 | 186 | vessel.getAllLongitudeBeforeSelectedTime() | |
144 | 187 | } else { | ||
return allLongitude | 145 | 188 | vessel.getAllLongitude() | |
189 | } | |||
} | 146 | 190 | } | |
147 | 191 | |||
private fun populateSpeedOverGround(vessel: Vessel): ArrayList<MessageData?> { | 148 | 192 | private fun populateSpeedOverGround(vessel: Vessel): ArrayList<MessageData?> { | |
val allSpeedOverGround: ArrayList<MessageData?> = vessel.getAllSpeedOverGround() | 149 | 193 | return if (observableIsReplayState.value) { | |
allSpeedOverGround.sortBy { (it as SpeedOverGround).value } | 150 | 194 | vessel.getAllSpeedOverGroundBeforeSelectedTime() | |
151 | 195 | } else { | ||
return allSpeedOverGround | 152 | 196 | vessel.getAllSpeedOverGround() | |
197 | } | |||
} | 153 | 198 | } | |
154 | 199 | |||
private fun populateCourseOverGround(vessel: Vessel): ArrayList<MessageData?> { | 155 | 200 | private fun populateCourseOverGround(vessel: Vessel): ArrayList<MessageData?> { | |
val allCourseOverGround: ArrayList<MessageData?> = vessel.getAllCourseOverGround() | 156 | 201 | return if (observableIsReplayState.value) { | |
allCourseOverGround.sortBy { (it as CourseOverGround).value } | 157 | 202 | vessel.getAllCourseOverGroundBeforeSelectedTime() | |
158 | 203 | } else { | ||
return allCourseOverGround | 159 | 204 | vessel.getAllCourseOverGround() | |
205 | } | |||
} | 160 | 206 | } | |
161 | 207 | |||
private fun populateHeading(vessel: Vessel): ArrayList<MessageData?> { | 162 | 208 | private fun populateHeading(vessel: Vessel): ArrayList<MessageData?> { | |
val allHeading: ArrayList<MessageData?> = vessel.getAllHeading() | 163 | 209 | return if (observableIsReplayState.value) { | |
allHeading.sortBy { (it as Heading).value } | 164 | 210 | vessel.getAllHeadingBeforeSelectedTime() | |
165 | 211 | } else { | ||
return allHeading | 166 | 212 | vessel.getAllHeading() | |
213 | } | |||
} | 167 | 214 | } | |
168 | 215 | |||
private fun populateVesselName(vessel: Vessel): ArrayList<MessageData?> { | 169 | 216 | private fun populateVesselName(vessel: Vessel): ArrayList<MessageData?> { | |
val allVesselName: ArrayList<MessageData?> = vessel.getAllVesselName() | 170 | 217 | return if (observableIsReplayState.value) { | |
allVesselName.sortBy { (it as VesselName).value } | 171 | 218 | vessel.getAllVesselNameBeforeSelectedTime() | |
172 | 219 | } else { | ||
return allVesselName | 173 | 220 | vessel.getAllVesselName() | |
221 | } | |||
} | 174 | 222 | } | |
175 | 223 | |||
private fun populateIMO(vessel: Vessel): ArrayList<MessageData?> { | 176 | 224 | private fun populateIMO(vessel: Vessel): ArrayList<MessageData?> { | |
val allIMO: ArrayList<MessageData?> = vessel.getAllIMO() | 177 | 225 | return if (observableIsReplayState.value) { | |
allIMO.sortBy { (it as IMO).value } | 178 | 226 | vessel.getAllIMOBeforeSelectedTime() | |
179 | 227 | } else { | ||
return allIMO | 180 | 228 | vessel.getAllIMO() | |
229 | } | |||
} | 181 | 230 | } | |
182 | 231 | |||
private fun populateCallSign(vessel: Vessel): ArrayList<MessageData?> { | 183 | 232 | private fun populateCallSign(vessel: Vessel): ArrayList<MessageData?> { | |
val allCallSign: ArrayList<MessageData?> = vessel.getAllCallSign() | 184 | 233 | return if (observableIsReplayState.value) { | |
allCallSign.sortBy { (it as CallSign).value } | 185 | 234 | vessel.getAllCallSignBeforeSelectedTime() | |
186 | 235 | } else { | ||
return allCallSign | 187 | 236 | vessel.getAllCallSign() | |
237 | } | |||
} | 188 | 238 | } | |
189 | 239 | |||
private fun populateVesselType(vessel: Vessel): ArrayList<MessageData?> { | 190 | 240 | private fun populateVesselType(vessel: Vessel): ArrayList<MessageData?> { | |
val allVesselType: ArrayList<MessageData?> = vessel.getAllVesselType() | 191 | 241 | return if (observableIsReplayState.value) { | |
allVesselType.sortBy { (it as VesselType).value } | 192 | 242 | vessel.getAllVesselTypeBeforeSelectedTime() | |
193 | 243 | } else { | ||
return allVesselType | 194 | 244 | vessel.getAllVesselType() | |
245 | } | |||
} | 195 | 246 | } | |
196 | 247 | |||
private fun populateStatus(vessel: Vessel): ArrayList<MessageData?> { | 197 | 248 | private fun populateStatus(vessel: Vessel): ArrayList<MessageData?> { | |
val allStatus: ArrayList<MessageData?> = vessel.getAllStatus() | 198 | 249 | return if (observableIsReplayState.value) { | |
allStatus.sortBy { (it as Status).value } | 199 | 250 | vessel.getAllStatusBeforeSelectedTime() | |
200 | 251 | } else { | ||
return allStatus | 201 | 252 | vessel.getAllStatus() | |
253 | } | |||
} | 202 | 254 | } | |
203 | 255 | |||
private fun populateLength(vessel: Vessel): ArrayList<MessageData?> { | 204 | 256 | private fun populateLength(vessel: Vessel): ArrayList<MessageData?> { | |
val allLength: ArrayList<MessageData?> = vessel.getAllLength() | 205 | 257 | return if (observableIsReplayState.value) { | |
allLength.sortBy { (it as Length).value } | 206 | 258 | vessel.getAllLengthBeforeSelectedTime() | |
207 | 259 | } else { | ||
return allLength | 208 | 260 | vessel.getAllLength() | |
261 | } | |||
} | 209 | 262 | } | |
210 | 263 | |||
private fun populateWidth(vessel: Vessel): ArrayList<MessageData?> { | 211 | 264 | private fun populateWidth(vessel: Vessel): ArrayList<MessageData?> { | |
val allWidth: ArrayList<MessageData?> = vessel.getAllWidth() | 212 | 265 | return if (observableIsReplayState.value) { | |
allWidth.sortBy { (it as Width).value } | 213 | 266 | vessel.getAllWidthBeforeSelectedTime() | |
214 | 267 | } else { | ||
return allWidth | 215 | 268 | vessel.getAllWidth() | |
269 | } | |||
} | 216 | 270 | } | |
217 | 271 | |||
private fun populateDraft(vessel: Vessel): ArrayList<MessageData?> { | 218 | 272 | private fun populateDraft(vessel: Vessel): ArrayList<MessageData?> { | |
val allDraft: ArrayList<MessageData?> = vessel.getAllDraft() | 219 | 273 | return if (observableIsReplayState.value) { | |
allDraft.sortBy { (it as Draft).value } | 220 | 274 | vessel.getAllDraftBeforeSelectedTime() | |
221 | 275 | } else { | ||
return allDraft | 222 | 276 | vessel.getAllDraft() | |
277 | } | |||
} | 223 | 278 | } | |
224 | 279 | |||
private fun populateCargo(vessel: Vessel): ArrayList<MessageData?> { | 225 | 280 | private fun populateCargo(vessel: Vessel): ArrayList<MessageData?> { | |
val allCargo: ArrayList<MessageData?> = vessel.getAllCargo() | 226 | 281 | return if (observableIsReplayState.value) { | |
allCargo.sortBy { (it as Cargo).value } | 227 | 282 | vessel.getAllCargoBeforeSelectedTime() | |
228 | 283 | } else { | ||
return allCargo | 229 | 284 | vessel.getAllCargo() | |
285 | } | |||
} | 230 | 286 | } | |
231 | 287 | |||
private fun populateDataList(vessel: Vessel) { | 232 | 288 | private fun initDataList() { | |
val data = arrayListOf<Pair<String, ArrayList<MessageData?>>>() | 233 | 289 | val data = arrayListOf<Pair<String, ArrayList<MessageData?>>>() | |
234 | 290 | |||
timeData = populateTime(vessel) | 235 | 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 | |||
data.add(Pair("Latitude", populateLatitude(vessel))) | 237 | 306 | dataList.addAll(data) | |
307 | } | |||
238 | 308 | |||
data.add(Pair("Longitude", populateLongitude(vessel))) | 239 | 309 | private fun updateDataList(vessel: Vessel) { | |
310 | timeData = populateTime(vessel) | |||
240 | 311 | |||
data.add(Pair("Speed Over Ground", populateSpeedOverGround(vessel))) | 241 | 312 | if(dataListView.selectionModel.selectedItem == null) return | |
242 | 313 | |||
data.add(Pair("Course Over Ground", populateCourseOverGround(vessel))) | 243 | 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 | |||
data.add(Pair("Heading", populateHeading(vessel))) | 245 | 374 | private fun setObservableCurrentTimeListener() { | |
246 | 375 | observableCurrentTime.listeners.add(object : CurrentTime { | ||
data.add(Pair("Vessel Name", populateVesselName(vessel))) | 247 | 376 | override fun onValueChanged(newValue: Int) { | |
248 | 377 | updateDataList(observableSelectedVessel.value) | ||
data.add(Pair("IMO", populateIMO(vessel))) | 249 | 378 | plot() | |
250 | 379 | } | ||
data.add(Pair("Call Sign", populateCallSign(vessel))) | 251 | 380 | }) | |
252 | ||||
data.add(Pair("Vessel Type", populateVesselType(vessel))) | 253 | |||
254 | ||||
data.add(Pair("Status", populateStatus(vessel))) | 255 | |||
256 | ||||
data.add(Pair("Length", populateLength(vessel))) | 257 | |||
258 | ||||
data.add(Pair("Width", populateWidth(vessel))) | 259 | |||
260 | ||||
data.add(Pair("Draft", populateDraft(vessel))) | 261 | |||
262 | ||||
data.add(Pair("Cargo", populateCargo(vessel))) | 263 | |||
264 | ||||
dataList.addAll(data) | 265 | |||
} | 266 | 381 | } | |
267 | 382 | |||
override fun onValueChanged(newValue: Vessel) { | 268 | 383 | override fun onValueChanged(newValue: Vessel) { | |
dataList.clear() | 269 | 384 | updateDataList(newValue) | |
populateDataList(newValue) | 270 | 385 | plot() | |
271 | ||||
} | 272 | 386 | } | |
273 | 387 | |||
} | 274 | 388 | } | |
275 | 389 | |||
276 | 390 | |||
277 | 391 | |||
src/main/kotlin/application/controller/MapPanelController.kt
View file @
3d3a5f8
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.* | |
5 | import javafx.application.Platform | |||
import javafx.fxml.FXML | 5 | 6 | import javafx.fxml.FXML | |
import javafx.fxml.Initializable | 6 | 7 | import javafx.fxml.Initializable | |
import javafx.scene.layout.StackPane | 7 | 8 | import javafx.scene.layout.StackPane | |
import map.* | 8 | 9 | import map.* | |
import java.net.URL | 9 | 10 | import java.net.URL | |
import java.util.* | 10 | 11 | import java.util.* | |
11 | 12 | |||
class MapPanelController : Initializable { | 12 | 13 | class MapPanelController : Initializable { | |
13 | 14 | |||
@FXML | 14 | 15 | @FXML | |
private lateinit var map: StackPane | 15 | 16 | private lateinit var map: StackPane | |
16 | 17 | |||
private val mapView = LeafletMapView() | 17 | 18 | private val mapView = LeafletMapView() | |
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() | |
/*val completeFutureMap: CompletableFuture<Worker.State> = mapView.displayMap(MapConfig()) | 25 | 25 | setObservableIsReplayState() | |
completeFutureMap.whenComplete{ | 26 | 26 | observableCurrentTime() | |
workerState, _ -> | 27 | 27 | ||
if (workerState == Worker.State.SUCCEEDED) { | 28 | |||
} | 29 | |||
}*/ | 30 | |||
map.children.add(mapView) | 31 | 28 | map.children.add(mapView) | |
map.children | 32 | 29 | map.children | |
} | 33 | 30 | } | |
34 | 31 | |||
private fun setStateListener() { | 35 | 32 | private fun setStateListener() { | |
observableState.listeners.add(object : StateListener { | 36 | 33 | observableMapState.listeners.add(object : StateListener { | |
override fun onValueChanged(newValue: State) { | 37 | 34 | override fun onValueChanged(newValue: MapState) { | |
if (observableSelectedVessel.vessel.mmsi != null) { | 38 | 35 | if (observableSelectedVessel.value.mmsi != null) { | |
updateMap(observableSelectedVessel.vessel.mmsi!!) | 39 | 36 | updateMap(observableSelectedVessel.value.mmsi!!) | |
} else { | 40 | 37 | } else { | |
updateMap() | 41 | 38 | updateMap() | |
} | 42 | 39 | } | |
} | 43 | 40 | } | |
}) | 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 | ||||
private fun updateMap() { | 47 | 56 | private fun updateMap() { | |
when (observableState.state) { | 48 | 57 | Platform.runLater { | |
ALL_MESSAGES -> displayAllMessageOnMap(mapView) | 49 | 58 | if (observableIsReplayState.value) { | |
CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView) | 50 | 59 | when (observableMapState.state) { | |
HEAT_MAP -> displayHeatMapOnMap(mapView) | 51 | 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 | |||
private fun updateMap(selectedMMSI: String) { | 55 | 74 | private fun updateMap(selectedMMSI: String) { | |
when (observableState.state) { | 56 | 75 | Platform.runLater { | |
ALL_MESSAGES -> displayAllMessageOnMap(mapView, selectedMMSI) | 57 | 76 | if (observableIsReplayState.value) { | |
CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView, selectedMMSI) | 58 | 77 | when (observableMapState.state) { | |
HEAT_MAP -> displayHeatMapOnMap(mapView, selectedMMSI) | 59 | 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 | |||
private fun setObservableVesselListener() { | 63 | 92 | private fun setObservableVesselListener() { | |
observableVessel.listeners.add(object : MessageListener { | 64 | 93 | observableVessel.listeners.add(object : MessageListener { | |
override fun onValueChanged(newValue: MutableMap<String?, Vessel>) { | 65 | 94 | override fun onValueChanged(newValue: MutableMap<String?, Vessel>) { | |
updateMap() | 66 | 95 | updateMap() | |
} | 67 | 96 | } | |
}) | 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 | ||||
private fun setObservableSelectedVesselListener() { | 71 | 112 | private fun setObservableSelectedVesselListener() { | |
observableSelectedVessel.listeners.add(object : SelectedVesselListener { | 72 | 113 | observableSelectedVessel.listeners.add(object : SelectedVesselListener { | |
override fun onValueChanged(newValue: Vessel) { | 73 | 114 | override fun onValueChanged(newValue: Vessel) { | |
if (newValue.mmsi != null){ | 74 | 115 | if (newValue.mmsi != null) { | |
updateMap(newValue.mmsi) | 75 | 116 | updateMap(newValue.mmsi) | |
}else { | 76 | 117 | } else { | |
updateMap() | 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
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.observableIsReplayState | |
6 | import application.model.observableMapState | |||
import application.model.observableVessel | 6 | 7 | import application.model.observableVessel | |
import javafx.event.EventHandler | 7 | 8 | import javafx.event.EventHandler | |
import javafx.fxml.FXML | 8 | 9 | import javafx.fxml.FXML | |
import javafx.fxml.Initializable | 9 | 10 | import javafx.fxml.Initializable | |
import javafx.scene.control.Alert | 10 | 11 | import javafx.scene.control.* | |
import javafx.scene.control.CheckMenuItem | 11 | |||
import javafx.scene.control.MenuBar | 12 | |||
import javafx.scene.control.MenuItem | 13 | |||
import javafx.stage.FileChooser | 14 | 12 | import javafx.stage.FileChooser | |
import java.net.URL | 15 | 13 | import java.net.URL | |
import java.util.* | 16 | 14 | import java.util.* | |
17 | 15 | |||
class MenuBarController : Initializable { | 18 | 16 | class MenuBarController : Initializable { | |
19 | 17 | |||
@FXML | 20 | 18 | @FXML | |
var menuBar: MenuBar = MenuBar() | 21 | 19 | var menuBar: MenuBar = MenuBar() | |
22 | 20 | |||
@FXML | 23 | 21 | @FXML | |
var import: MenuItem = MenuItem() | 24 | 22 | var import: MenuItem = MenuItem() | |
25 | 23 | |||
@FXML | 26 | 24 | @FXML | |
var allMessages: CheckMenuItem = CheckMenuItem() | 27 | 25 | var allMessages: CheckMenuItem = CheckMenuItem() | |
28 | 26 | |||
@FXML | 29 | 27 | @FXML | |
var clusteredMessage: CheckMenuItem = CheckMenuItem() | 30 | 28 | var clusteredMessage: CheckMenuItem = CheckMenuItem() | |
31 | 29 | |||
@FXML | 32 | 30 | @FXML | |
var heatMap: CheckMenuItem = CheckMenuItem() | 33 | 31 | var heatMap: CheckMenuItem = CheckMenuItem() | |
34 | 32 | |||
33 | @FXML | |||
34 | var activateReplayButton: RadioMenuItem = RadioMenuItem() | |||
35 | ||||
override fun initialize(location: URL?, resources: ResourceBundle?) { | 35 | 36 | override fun initialize(location: URL?, resources: ResourceBundle?) { | |
36 | 37 | |||
setOnActionImportButton() | 37 | 38 | setOnActionImportButton() | |
38 | 39 | |||
setOnActionAllMessageButton() | 39 | 40 | setOnActionAllMessageButton() | |
setOnActionClusteredMessageButton() | 40 | 41 | setOnActionClusteredMessageButton() | |
setOnActionHeatMapButton() | 41 | 42 | setOnActionHeatMapButton() | |
observableState.state = CLUSTERED_MESSAGES | 42 | 43 | setOnActionActivateReplayButton() | |
44 | observableMapState.state = CLUSTERED_MESSAGES | |||
allMessages.isSelected = false | 43 | 45 | allMessages.isSelected = false | |
clusteredMessage.isSelected = true | 44 | 46 | clusteredMessage.isSelected = true | |
heatMap.isSelected = false | 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 | ||||
private fun setOnActionImportButton() { | 49 | 57 | private fun setOnActionImportButton() { | |
import.onAction = EventHandler { | 50 | 58 | import.onAction = EventHandler { | |
val fileChooser = FileChooser() | 51 | 59 | val fileChooser = FileChooser() | |
fileChooser.title = "Choose a file to import" | 52 | 60 | fileChooser.title = "Choose a file to import" | |
val window = menuBar.scene.window | 53 | 61 | val window = menuBar.scene.window | |
val file = fileChooser.showOpenDialog(window) | 54 | 62 | val file = fileChooser.showOpenDialog(window) | |
try { | 55 | 63 | try { | |
if (file.extension != "csv") { | 56 | 64 | if (file.extension != "csv") { | |
val alert = Alert(Alert.AlertType.WARNING) | 57 | 65 | val alert = Alert(Alert.AlertType.WARNING) | |
alert.title = "Warning Alert" | 58 | 66 | alert.title = "Warning Alert" | |
alert.headerText = "Wrong file format." | 59 | 67 | alert.headerText = "Wrong file format." | |
alert.contentText = "Please choose ร .csv file." | 60 | 68 | alert.contentText = "Please choose ร .csv file." | |
alert.showAndWait() | 61 | 69 | alert.showAndWait() | |
} | 62 | 70 | } | |
val vessels = createVesselCollection(file) | 63 | 71 | val vessels = createVesselCollection(file) | |
observableVessel.vessels.clear() | 64 | 72 | observableVessel.vessels.clear() | |
observableVessel.vessels = vessels | 65 | 73 | observableVessel.vessels = vessels | |
} catch (ignore: IllegalStateException) { | 66 | 74 | } catch (ignore: IllegalStateException) { | |
67 | 75 | |||
} | 68 | 76 | } | |
} | 69 | 77 | } | |
} | 70 | 78 | } | |
71 | 79 | |||
private fun setOnActionAllMessageButton() { | 72 | 80 | private fun setOnActionAllMessageButton() { | |
allMessages.onAction = EventHandler { | 73 | 81 | allMessages.onAction = EventHandler { | |
observableState.state = ALL_MESSAGES | 74 | 82 | observableMapState.state = ALL_MESSAGES | |
allMessages.isSelected = true | 75 | 83 | allMessages.isSelected = true | |
clusteredMessage.isSelected = false | 76 | 84 | clusteredMessage.isSelected = false | |
heatMap.isSelected = false | 77 | 85 | heatMap.isSelected = false | |
} | 78 | 86 | } | |
} | 79 | 87 | } | |
80 | 88 | |||
private fun setOnActionClusteredMessageButton() { | 81 | 89 | private fun setOnActionClusteredMessageButton() { | |
clusteredMessage.onAction = EventHandler { | 82 | 90 | clusteredMessage.onAction = EventHandler { | |
observableState.state = CLUSTERED_MESSAGES | 83 | 91 | observableMapState.state = CLUSTERED_MESSAGES | |
heatMap.isSelected = false | 84 | 92 | heatMap.isSelected = false | |
allMessages.isSelected = false | 85 | 93 | allMessages.isSelected = false | |
clusteredMessage.isSelected = true | 86 | 94 | clusteredMessage.isSelected = true | |
} | 87 | 95 | } | |
} | 88 | 96 | } | |
89 | 97 |
src/main/kotlin/application/controller/TimePanel.kt
View file @
3d3a5f8
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 | 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() { |
src/main/kotlin/application/controller/VesselListPanelController.kt
View file @
3d3a5f8
package application.controller | 1 | 1 | package application.controller | |
2 | 2 | |||
import application.model.MessageListener | 3 | 3 | import application.model.MessageListener | |
import application.model.Vessel | 4 | 4 | import application.model.Vessel | |
import application.model.observableSelectedVessel | 5 | 5 | import application.model.observableSelectedVessel | |
import application.model.observableVessel | 6 | 6 | import application.model.observableVessel | |
import javafx.collections.FXCollections | 7 | 7 | import javafx.collections.FXCollections | |
import javafx.collections.ObservableList | 8 | 8 | import javafx.collections.ObservableList | |
import javafx.event.EventHandler | 9 | |||
import javafx.fxml.FXML | 10 | 9 | import javafx.fxml.FXML | |
import javafx.fxml.Initializable | 11 | 10 | import javafx.fxml.Initializable | |
import javafx.scene.control.* | 12 | 11 | import javafx.scene.control.ListCell | |
12 | import javafx.scene.control.ListView | |||
13 | import javafx.scene.control.MultipleSelectionModel | |||
14 | import javafx.scene.control.SelectionMode | |||
import javafx.scene.input.MouseEvent | 13 | 15 | import javafx.scene.input.MouseEvent | |
import java.net.URL | 14 | 16 | import java.net.URL | |
import java.util.* | 15 | 17 | import java.util.* | |
16 | 18 | |||
17 | 19 | |||
class VesselListPanelController : Initializable, MessageListener { | 18 | 20 | class VesselListPanelController : Initializable, MessageListener { | |
@FXML | 19 | 21 | @FXML | |
var shipListView: ListView<String?> = ListView() | 20 | 22 | var shipListView: ListView<String?> = ListView() | |
21 | 23 | |||
private var shipList: ObservableList<String?> = FXCollections.observableArrayList() | 22 | 24 | private var shipList: ObservableList<String?> = FXCollections.observableArrayList() | |
23 | 25 | |||
override fun initialize(location: URL?, resources: ResourceBundle?) { | 24 | 26 | override fun initialize(location: URL?, resources: ResourceBundle?) { | |
25 | 27 | |||
26 | 28 | |||
shipListView.items = shipList | 27 | 29 | shipListView.items = shipList | |
observableVessel.listeners.add(this) | 28 | 30 | observableVessel.listeners.add(this) | |
shipListView.selectionModel.selectedItemProperty().addListener { _, _, newValue -> | 29 | 31 | shipListView.selectionModel.selectedItemProperty().addListener { _, _, newValue -> | |
if (newValue == null) { | 30 | 32 | if (newValue == null) { | |
observableSelectedVessel.vessel = Vessel(null) | 31 | 33 | observableSelectedVessel.value = Vessel(null) | |
} else { | 32 | 34 | } else { | |
observableSelectedVessel.vessel = observableVessel.vessels[newValue]!! | 33 | 35 | observableSelectedVessel.value = observableVessel.vessels[newValue]!! | |
} | 34 | 36 | } | |
} | 35 | 37 | } | |
setCellFactory() | 36 | 38 | setCellFactory() | |
} | 37 | 39 | } | |
38 | 40 | |||
override fun onValueChanged(newValue: MutableMap<String?, Vessel>) { | 39 | 41 | override fun onValueChanged(newValue: MutableMap<String?, Vessel>) { | |
shipList.clear() | 40 | 42 | shipList.clear() | |
shipList.addAll(newValue.keys) | 41 | 43 | shipList.addAll(newValue.keys) | |
} | 42 | 44 | } | |
43 | 45 | |||
private fun setCellFactory() { | 44 | 46 | private fun setCellFactory() { | |
val selectionModel: MultipleSelectionModel<String?>? = shipListView.selectionModel | 45 | 47 | val selectionModel: MultipleSelectionModel<String?>? = shipListView.selectionModel | |
selectionModel?.selectionMode = SelectionMode.SINGLE | 46 | 48 | selectionModel?.selectionMode = SelectionMode.SINGLE | |
shipListView.setCellFactory { | 47 | 49 | shipListView.setCellFactory { | |
val cell = ListCell<String?>() | 48 | 50 | val cell = ListCell<String?>() | |
cell.textProperty().bind(cell.itemProperty()) | 49 | 51 | cell.textProperty().bind(cell.itemProperty()) | |
cell.addEventFilter(MouseEvent.MOUSE_PRESSED) { event: MouseEvent -> | 50 | 52 | cell.addEventFilter(MouseEvent.MOUSE_PRESSED) { event: MouseEvent -> | |
shipListView.requestFocus() | 51 | 53 | shipListView.requestFocus() | |
if (!cell.isEmpty) { | 52 | 54 | if (!cell.isEmpty) { | |
val index = cell.index | 53 | 55 | val index = cell.index | |
if (selectionModel!!.selectedIndices.contains(index)) { | 54 | 56 | if (selectionModel!!.selectedIndices.contains(index)) { | |
selectionModel.clearSelection() | 55 | 57 | selectionModel.clearSelection() | |
} else { | 56 | 58 | } else { | |
selectionModel.select(index) | 57 | 59 | selectionModel.select(index) | |
} | 58 | 60 | } | |
event.consume() | 59 | 61 | event.consume() |
src/main/kotlin/application/model/Context.kt
View file @
3d3a5f8
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 @
3d3a5f8
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 @
3d3a5f8
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 @
3d3a5f8
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 = Int.MAX_VALUE, | |||
11 | onChange = { _, _, new -> | |||
12 | run { | |||
13 | listeners.forEach { | |||
14 | it.onValueChanged(new) | |||
15 | } |
src/main/kotlin/application/model/ObservableMapState.kt
View file @
3d3a5f8
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 @
3d3a5f8
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 = false, | |||
11 | onChange = { _, _, new -> | |||
12 | run { | |||
13 | listeners.forEach { | |||
14 | it.onValueChanged(new) | |||
15 | } |
src/main/kotlin/application/model/ObservableSelectedVessel.kt
View file @
3d3a5f8
package application.model | 1 | 1 | package application.model | |
2 | 2 | |||
import kotlin.properties.Delegates | 3 | 3 | import kotlin.properties.Delegates | |
4 | 4 | |||
class ObservableSelectedVessel { | 5 | 5 | class ObservableSelectedVessel { | |
val listeners: MutableList<SelectedVesselListener> = mutableListOf() | 6 | 6 | val listeners: MutableList<SelectedVesselListener> = mutableListOf() | |
7 | 7 | |||
var vessel: Vessel by Delegates.observable( | 8 | 8 | var value: Vessel by Delegates.observable( | |
initialValue = Vessel(null), | 9 | 9 | initialValue = Vessel(null), | |
onChange = { _, _, new -> | 10 | 10 | onChange = { _, _, new -> | |
run { | 11 | 11 | run { | |
listeners.forEach { | 12 | 12 | listeners.forEach { | |
it.onValueChanged(new) | 13 | 13 | it.onValueChanged(new) | |
} | 14 | 14 | } | |
} | 15 | 15 | } | |
} | 16 | 16 | } |
src/main/kotlin/application/model/ObservableState.kt
View file @
3d3a5f8
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/ObservableVessel.kt
View file @
3d3a5f8
package application.model | 1 | 1 | package application.model | |
2 | 2 | |||
import kotlin.properties.Delegates | 3 | 3 | import kotlin.properties.Delegates | |
4 | 4 | |||
class ObservableVessel { | 5 | 5 | class ObservableVessel { | |
val listeners: MutableList<MessageListener> = mutableListOf() | 6 | 6 | val listeners: MutableList<MessageListener> = mutableListOf() | |
7 | 7 | |||
var vessels: MutableMap<String?, Vessel> by Delegates.observable( | 8 | 8 | var vessels: MutableMap<String?, Vessel> by Delegates.observable( | |
initialValue = mutableMapOf(), | 9 | 9 | initialValue = mutableMapOf(), | |
onChange = { _, _, new -> | 10 | 10 | onChange = { _, _, new -> | |
run { | 11 | 11 | run { | |
observableSelectedVessel.vessel = Vessel(null) | 12 | 12 | observableSelectedVessel.value = Vessel(null) | |
listeners.forEach { | 13 | 13 | listeners.forEach { | |
it.onValueChanged(new) | 14 | 14 | it.onValueChanged(new) | |
} | 15 | 15 | } | |
} | 16 | 16 | } | |
} | 17 | 17 | } |
src/main/kotlin/application/model/ReplayState.kt
View file @
3d3a5f8
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 @
3d3a5f8
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 @
3d3a5f8
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 @
3d3a5f8
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 | ||||
class Vessel(val mmsi: String?) { | 8 | 5 | class Vessel(val mmsi: String?) { | |
val messages: SortedMap<LocalDateTime, Message> = sortedMapOf() | 9 | 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 | ||||
fun getAllTime(): ArrayList<MessageData?> { | 11 | 145 | fun getAllTime(): ArrayList<MessageData?> { | |
val timeList = arrayListOf<MessageData?>() | 12 | 146 | val timeList = arrayListOf<MessageData?>() | |
messages.forEach { | 13 | 147 | messages.forEach { | |
timeList.add(it.value.time) | 14 | 148 | timeList.add(it.value.time) | |
} | 15 | 149 | } | |
16 | 150 | |||
return timeList | 17 | 151 | return timeList | |
} | 18 | 152 | } | |
19 | 153 | |||
fun getAllLatitude(): ArrayList<MessageData?> { | 20 | 154 | fun getAllLatitude(): ArrayList<MessageData?> { | |
val latitudeList = arrayListOf<MessageData?>() | 21 | 155 | val latitudeList = arrayListOf<MessageData?>() | |
messages.forEach { | 22 | 156 | messages.forEach { | |
latitudeList.add(it.value.latitude) | 23 | 157 | latitudeList.add(it.value.latitude) | |
} | 24 | 158 | } | |
25 | 159 | |||
return latitudeList | 26 | 160 | return latitudeList | |
} | 27 | 161 | } | |
28 | 162 | |||
fun getAllLongitude(): ArrayList<MessageData?> { | 29 | 163 | fun getAllLongitude(): ArrayList<MessageData?> { | |
val longitudeList = arrayListOf<MessageData?>() | 30 | 164 | val longitudeList = arrayListOf<MessageData?>() | |
messages.forEach { | 31 | 165 | messages.forEach { | |
longitudeList.add(it.value.longitude) | 32 | 166 | longitudeList.add(it.value.longitude) | |
} | 33 | 167 | } | |
34 | 168 | |||
return longitudeList | 35 | 169 | return longitudeList | |
} | 36 | 170 | } | |
37 | 171 | |||
fun getAllSpeedOverGround(): ArrayList<MessageData?> { | 38 | 172 | fun getAllSpeedOverGround(): ArrayList<MessageData?> { | |
val speedOverGroundList = arrayListOf<MessageData?>() | 39 | 173 | val speedOverGroundList = arrayListOf<MessageData?>() | |
messages.forEach { | 40 | 174 | messages.forEach { | |
speedOverGroundList.add(it.value.speedOverGround) | 41 | 175 | speedOverGroundList.add(it.value.speedOverGround) | |
} | 42 | 176 | } | |
43 | 177 | |||
return speedOverGroundList | 44 | 178 | return speedOverGroundList | |
} | 45 | 179 | } | |
46 | 180 | |||
fun getAllCourseOverGround(): ArrayList<MessageData?> { | 47 | 181 | fun getAllCourseOverGround(): ArrayList<MessageData?> { | |
val res = arrayListOf<MessageData?>() | 48 | 182 | val res = arrayListOf<MessageData?>() | |
messages.forEach { | 49 | 183 | messages.forEach { | |
res.add(it.value.courseOverGround) | 50 | 184 | res.add(it.value.courseOverGround) | |
} | 51 | 185 | } | |
52 | 186 | |||
return res | 53 | 187 | return res | |
} | 54 | 188 | } | |
55 | 189 | |||
fun getAllHeading(): ArrayList<MessageData?> { | 56 | 190 | fun getAllHeading(): ArrayList<MessageData?> { | |
val res = arrayListOf<MessageData?>() | 57 | 191 | val res = arrayListOf<MessageData?>() | |
messages.forEach { | 58 | 192 | messages.forEach { | |
res.add(it.value.heading) | 59 | 193 | res.add(it.value.heading) | |
} | 60 | 194 | } | |
61 | 195 | |||
return res | 62 | 196 | return res | |
} | 63 | 197 | } | |
64 | 198 | |||
fun getAllVesselName(): ArrayList<MessageData?> { | 65 | 199 | fun getAllVesselName(): ArrayList<MessageData?> { | |
val res = arrayListOf<MessageData?>() | 66 | 200 | val res = arrayListOf<MessageData?>() | |
messages.forEach { | 67 | 201 | messages.forEach { | |
res.add(it.value.vesselName) | 68 | 202 | res.add(it.value.vesselName) | |
} | 69 | 203 | } | |
return res | 70 | 204 | return res | |
} | 71 | 205 | } | |
72 | 206 | |||
fun getAllIMO(): ArrayList<MessageData?> { | 73 | 207 | fun getAllIMO(): ArrayList<MessageData?> { | |
val res = arrayListOf<MessageData?>() | 74 | 208 | val res = arrayListOf<MessageData?>() | |
messages.forEach { | 75 | 209 | messages.forEach { | |
res.add(it.value.imo) | 76 | 210 | res.add(it.value.imo) | |
} | 77 | 211 | } | |
return res | 78 | 212 | return res | |
} | 79 | 213 | } | |
80 | 214 | |||
fun getAllCallSign(): ArrayList<MessageData?> { | 81 | 215 | fun getAllCallSign(): ArrayList<MessageData?> { | |
val res = arrayListOf<MessageData?>() | 82 | 216 | val res = arrayListOf<MessageData?>() | |
messages.forEach { | 83 | 217 | messages.forEach { | |
res.add(it.value.callSign) | 84 | 218 | res.add(it.value.callSign) | |
} | 85 | 219 | } | |
return res | 86 | 220 | return res | |
} | 87 | 221 | } | |
88 | 222 | |||
fun getAllVesselType(): ArrayList<MessageData?> { | 89 | 223 | fun getAllVesselType(): ArrayList<MessageData?> { | |
val res = arrayListOf<MessageData?>() | 90 | 224 | val res = arrayListOf<MessageData?>() | |
messages.forEach { | 91 | 225 | messages.forEach { | |
res.add(it.value.vesselType) | 92 | 226 | res.add(it.value.vesselType) | |
} | 93 | 227 | } | |
return res | 94 | 228 | return res | |
} | 95 | 229 | } | |
96 | 230 | |||
fun getAllStatus(): ArrayList<MessageData?> { | 97 | 231 | fun getAllStatus(): ArrayList<MessageData?> { | |
val res = arrayListOf<MessageData?>() | 98 | 232 | val res = arrayListOf<MessageData?>() | |
messages.forEach { | 99 | 233 | messages.forEach { | |
res.add(it.value.status) | 100 | 234 | res.add(it.value.status) | |
} | 101 | 235 | } | |
return res | 102 | 236 | return res | |
} | 103 | 237 | } | |
104 | 238 | |||
fun getAllLength(): ArrayList<MessageData?> { | 105 | 239 | fun getAllLength(): ArrayList<MessageData?> { | |
val res = arrayListOf<MessageData?>() | 106 | 240 | val res = arrayListOf<MessageData?>() | |
messages.forEach { | 107 | 241 | messages.forEach { | |
res.add(it.value.length) | 108 | 242 | res.add(it.value.length) | |
} | 109 | 243 | } | |
return res | 110 | 244 | return res | |
} | 111 | 245 | } | |
112 | 246 | |||
fun getAllWidth(): ArrayList<MessageData?> { | 113 | 247 | fun getAllWidth(): ArrayList<MessageData?> { | |
val res = arrayListOf<MessageData?>() | 114 | 248 | val res = arrayListOf<MessageData?>() | |
messages.forEach { | 115 | 249 | messages.forEach { | |
res.add(it.value.width) | 116 | 250 | res.add(it.value.width) | |
} | 117 | 251 | } | |
return res | 118 | 252 | return res |
src/main/kotlin/application/model/VesselGenerator.kt
View file @
3d3a5f8
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/LeafletMapView.kt
View file @
3d3a5f8
package map | 1 | 1 | package map | |
2 | 2 | |||
import javafx.concurrent.Worker | 3 | 3 | import javafx.concurrent.Worker | |
import javafx.scene.layout.StackPane | 4 | 4 | import javafx.scene.layout.StackPane | |
import javafx.scene.paint.Color | 5 | 5 | import javafx.scene.paint.Color | |
import javafx.scene.shape.Polygon | 6 | 6 | import javafx.scene.shape.Polygon | |
import javafx.scene.web.WebEngine | 7 | 7 | import javafx.scene.web.WebEngine | |
import javafx.scene.web.WebView | 8 | 8 | import javafx.scene.web.WebView | |
import map.events.* | 9 | 9 | import map.events.* | |
import netscape.javascript.JSObject | 10 | 10 | import netscape.javascript.JSObject | |
import java.io.ByteArrayOutputStream | 11 | 11 | import java.io.ByteArrayOutputStream | |
import java.io.File | 12 | 12 | import java.io.File | |
import java.io.IOException | 13 | 13 | import java.io.IOException | |
import java.net.URL | 14 | 14 | import java.net.URL | |
import java.util.* | 15 | 15 | import java.util.* | |
import java.util.concurrent.CompletableFuture | 16 | 16 | import java.util.concurrent.CompletableFuture | |
import javax.imageio.ImageIO | 17 | 17 | import javax.imageio.ImageIO | |
18 | 18 | |||
19 | 19 | |||
/** | 20 | 20 | /** | |
* JavaFX component for displaying OpenStreetMap based maps by using the Leaflet.js JavaScript library inside a WebView | 21 | 21 | * JavaFX component for displaying OpenStreetMap based maps by using the Leaflet.js JavaScript library inside a WebView | |
* browser component.<br/> | 22 | 22 | * browser component.<br/> | |
* This component can be embedded most easily by placing it inside a StackPane, the component uses then the size of the | 23 | 23 | * This component can be embedded most easily by placing it inside a StackPane, the component uses then the size of the | |
* parent automatically. | 24 | 24 | * parent automatically. | |
* | 25 | 25 | * | |
* @author Stefan Saring | 26 | 26 | * @author Stefan Saring | |
* @author Niklas Kellner | 27 | 27 | * @author Niklas Kellner | |
*/ | 28 | 28 | */ | |
class LeafletMapView : StackPane() { | 29 | 29 | class LeafletMapView : StackPane() { | |
30 | 30 | |||
private val webView = WebView() | 31 | 31 | private val webView = WebView() | |
private val webEngine: WebEngine = webView.engine | 32 | 32 | private val webEngine: WebEngine = webView.engine | |
33 | 33 | |||
private var varNameSuffix: Int = 1 | 34 | 34 | private var varNameSuffix: Int = 1 | |
private val mapClickEvent = MapClickEventMaker() | 35 | 35 | private val mapClickEvent = MapClickEventMaker() | |
private val markerClickEvent = MarkerClickEventMaker() | 36 | 36 | private val markerClickEvent = MarkerClickEventMaker() | |
private val mapMoveEvent = MapMoveEventMaker() | 37 | 37 | private val mapMoveEvent = MapMoveEventMaker() | |
internal val zoomLimitSmallMarker = 8 | 38 | 38 | internal val zoomLimitSmallMarker = 8 | |
39 | 39 | |||
/** | 40 | 40 | /** | |
* Creates the LeafletMapView component, it does not show any map yet. | 41 | 41 | * Creates the LeafletMapView component, it does not show any map yet. | |
*/ | 42 | 42 | */ | |
init { | 43 | 43 | init { | |
this.children.add(webView) | 44 | 44 | this.children.add(webView) | |
} | 45 | 45 | } | |
46 | 46 | |||
/** | 47 | 47 | /** | |
* Displays the initial map in the web view. Needs to be called and complete before adding any markers or tracks. | 48 | 48 | * Displays the initial map in the web view. Needs to be called and complete before adding any markers or tracks. | |
* The returned CompletableFuture will provide the final map load state, the map can be used when the load has | 49 | 49 | * The returned CompletableFuture will provide the final map load state, the map can be used when the load has | |
* completed with state SUCCEEDED (use CompletableFuture#whenComplete() for waiting to complete). | 50 | 50 | * completed with state SUCCEEDED (use CompletableFuture#whenComplete() for waiting to complete). | |
* | 51 | 51 | * | |
* @param mapConfig configuration of the map layers and controls | 52 | 52 | * @param mapConfig configuration of the map layers and controls | |
* @return the CompletableFuture which will provide the final map load state | 53 | 53 | * @return the CompletableFuture which will provide the final map load state | |
*/ | 54 | 54 | */ | |
fun displayMap(mapConfig: MapConfig): CompletableFuture<Worker.State> { | 55 | 55 | fun displayMap(mapConfig: MapConfig): CompletableFuture<Worker.State> { | |
val finalMapLoadState = CompletableFuture<Worker.State>() | 56 | 56 | val finalMapLoadState = CompletableFuture<Worker.State>() | |
57 | 57 | |||
webEngine.loadWorker.stateProperty().addListener { _, _, newValue -> | 58 | 58 | webEngine.loadWorker.stateProperty().addListener { _, _, newValue -> | |
59 | 59 | |||
if (newValue == Worker.State.SUCCEEDED) { | 60 | 60 | if (newValue == Worker.State.SUCCEEDED) { | |
executeMapSetupScripts(mapConfig) | 61 | 61 | executeMapSetupScripts(mapConfig) | |
} | 62 | 62 | } | |
63 | 63 | |||
if (newValue == Worker.State.SUCCEEDED || newValue == Worker.State.FAILED) { | 64 | 64 | if (newValue == Worker.State.SUCCEEDED || newValue == Worker.State.FAILED) { | |
finalMapLoadState.complete(newValue) | 65 | 65 | finalMapLoadState.complete(newValue) | |
} | 66 | 66 | } | |
} | 67 | 67 | } | |
68 | 68 | |||
val localFileUrl: URL = LeafletMapView::class.java.getResource("/leafletmap/leafletmap.html") | 69 | 69 | val localFileUrl: URL = LeafletMapView::class.java.getResource("/leafletmap/leafletmap.html") | |
webEngine.load(localFileUrl.toExternalForm()) | 70 | 70 | webEngine.load(localFileUrl.toExternalForm()) | |
return finalMapLoadState | 71 | 71 | return finalMapLoadState | |
} | 72 | 72 | } | |
73 | 73 | |||
private fun executeMapSetupScripts(mapConfig: MapConfig) { | 74 | 74 | private fun executeMapSetupScripts(mapConfig: MapConfig) { | |
75 | 75 | |||
// execute scripts for layer definition | 76 | 76 | // execute scripts for layer definition | |
mapConfig.layers.forEachIndexed { i, layer -> | 77 | 77 | mapConfig.layers.forEachIndexed { i, layer -> | |
execScript("var layer${i + 1} = ${layer.javaScriptCode};") | 78 | 78 | execScript("var layer${i + 1} = ${layer.javaScriptCode};") | |
} | 79 | 79 | } | |
80 | 80 | |||
val jsLayers = mapConfig.layers | 81 | 81 | val jsLayers = mapConfig.layers | |
.mapIndexed { i, layer -> "'${layer.displayName}': layer${i + 1}" } | 82 | 82 | .mapIndexed { i, layer -> "'${layer.displayName}': layer${i + 1}" } | |
.joinToString(", ") | 83 | 83 | .joinToString(", ") | |
execScript("var baseMaps = { $jsLayers };") | 84 | 84 | execScript("var baseMaps = { $jsLayers };") | |
85 | 85 | |||
// execute script for map view creation (Leaflet attribution must not be a clickable link) | 86 | 86 | // execute script for map view creation (Leaflet attribution must not be a clickable link) | |
execScript( | 87 | 87 | execScript( | |
""" | 88 | 88 | """ | |
|var myMap = L.map('map', { | 89 | 89 | |var myMap = L.map('map', { | |
| center: new L.LatLng(${mapConfig.initialCenter.latitude}, ${mapConfig.initialCenter.longitude}), | 90 | 90 | | center: new L.LatLng(${mapConfig.initialCenter.latitude}, ${mapConfig.initialCenter.longitude}), | |
| zoom: 1, | 91 | 91 | | zoom: 1, | |
| zoomControl: false, | 92 | 92 | | zoomControl: false, | |
| layers: [layer1] | 93 | 93 | | layers: [layer1] | |
|}); | 94 | 94 | |}); | |
|L.control.scale().addTo(myMap); | 95 | 95 | |L.control.scale().addTo(myMap); | |
96 | |var markers = [] | |||
|var myRenderer = L.canvas({ padding: 0.5 }); | 96 | 97 | |var myRenderer = L.canvas({ padding: 0.5 }); | |
|var markerClusters = L.markerClusterGroup({spiderfyOnMaxZoom: false, disableClusteringAtZoom: 10}); | 97 | 98 | |var markerClusters = L.markerClusterGroup({spiderfyOnMaxZoom: false, disableClusteringAtZoom: 10}); | |
|var heatLayer = L.heatLayer([]).addTo(myMap);""".trimMargin() | 98 | 99 | |var heatLayer = L.heatLayer([]).addTo(myMap);""".trimMargin() | |
) | 99 | 100 | ) | |
100 | 101 | |||
// eventZoomChangeIcon() | 101 | 102 | // eventZoomChangeIcon() | |
102 | 103 | |||
// execute script for layer control definition if there are multiple layers | 103 | 104 | // execute script for layer control definition if there are multiple layers | |
if (mapConfig.layers.size > 1) { | 104 | 105 | if (mapConfig.layers.size > 1) { | |
execScript( | 105 | 106 | execScript( | |
""" | 106 | 107 | """ | |
|var overlayMaps = {}; | 107 | 108 | |var overlayMaps = {}; | |
|L.control.layers(baseMaps, overlayMaps).addTo(myMap);""".trimMargin() | 108 | 109 | |L.control.layers(baseMaps, overlayMaps).addTo(myMap);""".trimMargin() | |
) | 109 | 110 | ) | |
110 | 111 | |||
} | 111 | 112 | } | |
112 | 113 | |||
// execute script for scale control definition | 113 | 114 | // execute script for scale control definition | |
if (mapConfig.scaleControlConfig.show) { | 114 | 115 | if (mapConfig.scaleControlConfig.show) { | |
execScript( | 115 | 116 | execScript( | |
"L.control.scale({position: '${mapConfig.scaleControlConfig.position.positionName}', " + | 116 | 117 | "L.control.scale({position: '${mapConfig.scaleControlConfig.position.positionName}', " + | |
"metric: ${mapConfig.scaleControlConfig.metric}, " + | 117 | 118 | "metric: ${mapConfig.scaleControlConfig.metric}, " + | |
"imperial: ${!mapConfig.scaleControlConfig.metric}})" + | 118 | 119 | "imperial: ${!mapConfig.scaleControlConfig.metric}})" + | |
".addTo(myMap);" | 119 | 120 | ".addTo(myMap);" | |
) | 120 | 121 | ) | |
} | 121 | 122 | } | |
122 | 123 | |||
// execute script for zoom control definition | 123 | 124 | // execute script for zoom control definition | |
if (mapConfig.zoomControlConfig.show) { | 124 | 125 | if (mapConfig.zoomControlConfig.show) { | |
execScript( | 125 | 126 | execScript( | |
"L.control.zoom({position: '${mapConfig.zoomControlConfig.position.positionName}'})" + | 126 | 127 | "L.control.zoom({position: '${mapConfig.zoomControlConfig.position.positionName}'})" + | |
".addTo(myMap);" | 127 | 128 | ".addTo(myMap);" | |
) | 128 | 129 | ) | |
} | 129 | 130 | } | |
} | 130 | 131 | } | |
131 | 132 | |||
/** | 132 | 133 | /** | |
* Sets the view of the map to the specified geographical center position and zoom level. | 133 | 134 | * Sets the view of the map to the specified geographical center position and zoom level. | |
* | 134 | 135 | * | |
* @param position map center position | 135 | 136 | * @param position map center position | |
* @param zoomLevel zoom level (0 - 19 for OpenStreetMap) | 136 | 137 | * @param zoomLevel zoom level (0 - 19 for OpenStreetMap) | |
*/ | 137 | 138 | */ | |
fun setView(position: LatLong, zoomLevel: Int) = | 138 | 139 | fun setView(position: LatLong, zoomLevel: Int) = | |
execScript("myMap.setView([${position.latitude}, ${position.longitude}], $zoomLevel);") | 139 | 140 | execScript("myMap.setView([${position.latitude}, ${position.longitude}], $zoomLevel);") | |
140 | 141 | |||
/** | 141 | 142 | /** | |
* Pans the map to the specified geographical center position. | 142 | 143 | * Pans the map to the specified geographical center position. | |
* | 143 | 144 | * | |
* @param position map center position | 144 | 145 | * @param position map center position | |
*/ | 145 | 146 | */ | |
fun panTo(position: LatLong) = | 146 | 147 | fun panTo(position: LatLong) = | |
execScript("myMap.panTo([${position.latitude}, ${position.longitude}]);") | 147 | 148 | execScript("myMap.panTo([${position.latitude}, ${position.longitude}]);") | |
148 | 149 | |||
/** | 149 | 150 | /** | |
* Sets the zoom of the map to the specified level. | 150 | 151 | * Sets the zoom of the map to the specified level. | |
* | 151 | 152 | * | |
* @param zoomLevel zoom level (0 - 19 for OpenStreetMap) | 152 | 153 | * @param zoomLevel zoom level (0 - 19 for OpenStreetMap) | |
*/ | 153 | 154 | */ | |
fun setZoom(zoomLevel: Int) = | 154 | 155 | fun setZoom(zoomLevel: Int) = | |
execScript("myMap.setZoom([$zoomLevel]);") | 155 | 156 | execScript("myMap.setZoom([$zoomLevel]);") | |
156 | 157 | |||
/** | 157 | 158 | /** | |
* Adds a Marker Object to a map | 158 | 159 | * Adds a Marker Object to a map | |
* | 159 | 160 | * | |
* @param marker the Marker Object | 160 | 161 | * @param marker the Marker Object | |
*/ | 161 | 162 | */ | |
fun addMarker(marker: Marker) { | 162 | 163 | fun addMarker(marker: Marker) { | |
marker.addToMap(getNextMarkerName(), this) | 163 | 164 | marker.addToMap(getNextMarkerName(), this) | |
} | 164 | 165 | } | |
165 | 166 | |||
fun addCircle(circle: Circle) { | 166 | 167 | fun addCircle(circle: Circle) { | |
circle.addToMap(this) | 167 | 168 | circle.addToMap(this) | |
} | 168 | 169 | } | |
169 | 170 | |||
fun addZone(zone: Zone) { | 170 | 171 | fun addZone(zone: Zone) { | |
zone.addToMap(this) | 171 | 172 | zone.addToMap(this) | |
} | 172 | 173 | } | |
173 | 174 | |||
/** | 174 | 175 | /** | |
* Removes an existing marker from the map | 175 | 176 | * Removes an existing marker from the map | |
* | 176 | 177 | * | |
* @param marker the Marker object | 177 | 178 | * @param marker the Marker object | |
*/ | 178 | 179 | */ | |
fun removeMarker(marker: Marker) { | 179 | 180 | fun removeMarker(marker: Marker) { | |
execScript("myMap.removeLayer(${marker.getName()});") | 180 | 181 | execScript("myMap.removeLayer(${marker.getName()});") | |
} | 181 | 182 | } | |
182 | 183 | |||
fun removeCircle(circle: Circle) { | 183 | 184 | fun removeCircle(circle: Circle) { | |
circle.removeCircle(this) | 184 | 185 | circle.removeCircle(this) | |
} | 185 | 186 | } | |
186 | 187 | |||
fun removeZone(zone: Zone) { | 187 | 188 | fun removeZone(zone: Zone) { | |
zone.removeZone() | 188 | 189 | zone.removeZone() | |
} | 189 | 190 | } | |
190 | 191 | |||
fun removeZone(id: String) { | 191 | 192 | fun removeZone(id: String) { | |
val idSanitized = id.replace("-", "") | 192 | 193 | val idSanitized = id.replace("-", "") | |
execScript("myMap.removeLayer(polygon$idSanitized);") | 193 | 194 | execScript("myMap.removeLayer(polygon$idSanitized);") | |
} | 194 | 195 | } | |
195 | 196 | |||
196 | 197 | |||
fun uppdateCircle(circle: Circle, latLong: LatLong, radius: Double) { | 197 | 198 | fun uppdateCircle(circle: Circle, latLong: LatLong, radius: Double) { | |
circle.modifyCircle(latLong, radius) | 198 | 199 | circle.modifyCircle(latLong, radius) | |
circle.uppdateMap() | 199 | 200 | circle.uppdateMap() | |
} | 200 | 201 | } | |
201 | 202 | |||
fun setEventMousePosition() { | 202 | 203 | fun setEventMousePosition() { | |
execScript( | 203 | 204 | execScript( | |
"var lat=0.0, lng=0.0;\n" + | 204 | 205 | "var lat=0.0, lng=0.0;\n" + | |
"myMap.addEventListener('mousemove', function(ev) {\n" + | 205 | 206 | "myMap.addEventListener('mousemove', function(ev) {\n" + | |
" lat = ev.latlng.lat;\n" + | 206 | 207 | " lat = ev.latlng.lat;\n" + | |
" lng = ev.latlng.lng;\n" + | 207 | 208 | " lng = ev.latlng.lng;\n" + | |
"});" | 208 | 209 | "});" | |
) | 209 | 210 | ) | |
} | 210 | 211 | } | |
211 | 212 | |||
fun getMousePosition(): LatLong { | 212 | 213 | fun getMousePosition(): LatLong { | |
val lat = execScript("lat;") as Double | 213 | 214 | val lat = execScript("lat;") as Double | |
val lng = execScript("lng;") as Double | 214 | 215 | val lng = execScript("lng;") as Double | |
return LatLong(lat, lng) | 215 | 216 | return LatLong(lat, lng) | |
} | 216 | 217 | } | |
217 | 218 | |||
/** | 218 | 219 | /** | |
* Adds a custom marker type | 219 | 220 | * Adds a custom marker type | |
* | 220 | 221 | * | |
* @param markerName the name of the marker type | 221 | 222 | * @param markerName the name of the marker type | |
* @param iconUrl the url if the marker icon | 222 | 223 | * @param iconUrl the url if the marker icon | |
*/ | 223 | 224 | */ | |
fun addCustomMarker(markerName: String, iconUrl: String): String { | 224 | 225 | fun addCustomMarker(markerName: String, iconUrl: String): String { | |
execScript( | 225 | 226 | execScript( | |
"var $markerName = L.icon({\n" + | 226 | 227 | "var $markerName = L.icon({\n" + | |
"iconUrl: '${createImage(iconUrl, "png")}',\n" + | 227 | 228 | "iconUrl: '${createImage(iconUrl, "png")}',\n" + | |
"iconSize: [24, 24],\n" + | 228 | 229 | "iconSize: [24, 24],\n" + | |
"iconAnchor: [12, 12],\n" + | 229 | 230 | "iconAnchor: [12, 12],\n" + | |
"});" | 230 | 231 | "});" | |
) | 231 | 232 | ) | |
return markerName | 232 | 233 | return markerName | |
} | 233 | 234 | } | |
234 | 235 | |||
private fun createImage(path: String, type: String): String { | 235 | 236 | private fun createImage(path: String, type: String): String { | |
val image = ImageIO.read(File(path)) | 236 | 237 | val image = ImageIO.read(File(path)) | |
var imageString: String? = null | 237 | 238 | var imageString: String? = null | |
val bos = ByteArrayOutputStream() | 238 | 239 | val bos = ByteArrayOutputStream() | |
239 | 240 | |||
try { | 240 | 241 | try { | |
ImageIO.write(image, type, bos) | 241 | 242 | ImageIO.write(image, type, bos) | |
val imageBytes = bos.toByteArray() | 242 | 243 | val imageBytes = bos.toByteArray() | |
243 | 244 | |||
val encoder = Base64.getEncoder() | 244 | 245 | val encoder = Base64.getEncoder() | |
imageString = encoder.encodeToString(imageBytes) | 245 | 246 | imageString = encoder.encodeToString(imageBytes) | |
246 | 247 | |||
bos.close() | 247 | 248 | bos.close() | |
} catch (e: IOException) { | 248 | 249 | } catch (e: IOException) { | |
e.printStackTrace() | 249 | 250 | e.printStackTrace() | |
} | 250 | 251 | } | |
return "data:image/$type;base64,$imageString" | 251 | 252 | return "data:image/$type;base64,$imageString" | |
} | 252 | 253 | } | |
253 | 254 | |||
/** | 254 | 255 | /** | |
* Sets the onMarkerClickListener | 255 | 256 | * Sets the onMarkerClickListener | |
* | 256 | 257 | * | |
* @param listener the onMarerClickEventListener | 257 | 258 | * @param listener the onMarerClickEventListener | |
*/ | 258 | 259 | */ | |
fun onMarkerClick(listener: MarkerClickEventListener) { | 259 | 260 | fun onMarkerClick(listener: MarkerClickEventListener) { | |
val win = execScript("document") as JSObject | 260 | 261 | val win = execScript("document") as JSObject | |
win.setMember("java", this) | 261 | 262 | win.setMember("java", this) | |
markerClickEvent.addListener(listener) | 262 | 263 | markerClickEvent.addListener(listener) | |
} | 263 | 264 | } | |
264 | 265 | |||
/** | 265 | 266 | /** | |
* Handles the callback from the markerClickEvent | 266 | 267 | * Handles the callback from the markerClickEvent | |
*/ | 267 | 268 | */ | |
fun markerClick(title: String) { | 268 | 269 | fun markerClick(title: String) { | |
markerClickEvent.MarkerClickEvent(title) | 269 | 270 | markerClickEvent.MarkerClickEvent(title) | |
} | 270 | 271 | } | |
271 | 272 | |||
/** | 272 | 273 | /** | |
* Sets the onMapMoveListener | 273 | 274 | * Sets the onMapMoveListener | |
* | 274 | 275 | * | |
* @param listener the MapMoveEventListener | 275 | 276 | * @param listener the MapMoveEventListener | |
*/ | 276 | 277 | */ | |
fun onMapMove(listener: MapMoveEventListener) { | 277 | 278 | fun onMapMove(listener: MapMoveEventListener) { | |
val win = execScript("document") as JSObject | 278 | 279 | val win = execScript("document") as JSObject | |
win.setMember("java", this) | 279 | 280 | win.setMember("java", this) | |
execScript("myMap.on('moveend', function(e){ document.java.mapMove(myMap.getCenter().lat, myMap.getCenter().lng);});") | 280 | 281 | execScript("myMap.on('moveend', function(e){ document.java.mapMove(myMap.getCenter().lat, myMap.getCenter().lng);});") | |
mapMoveEvent.addListener(listener) | 281 | 282 | mapMoveEvent.addListener(listener) | |
} | 282 | 283 | } | |
283 | 284 | |||
/** | 284 | 285 | /** | |
* Handles the callback from the mapMoveEvent | 285 | 286 | * Handles the callback from the mapMoveEvent | |
*/ | 286 | 287 | */ | |
fun mapMove(lat: Double, lng: Double) { | 287 | 288 | fun mapMove(lat: Double, lng: Double) { | |
val latlng = LatLong(lat, lng) | 288 | 289 | val latlng = LatLong(lat, lng) | |
mapMoveEvent.MapMoveEvent(latlng) | 289 | 290 | mapMoveEvent.MapMoveEvent(latlng) | |
} | 290 | 291 | } | |
291 | 292 | |||
/** | 292 | 293 | /** | |
* Sets the onMapClickListener | 293 | 294 | * Sets the onMapClickListener | |
* | 294 | 295 | * | |
* @param listener the onMapClickEventListener | 295 | 296 | * @param listener the onMapClickEventListener | |
*/ | 296 | 297 | */ | |
fun onMapClick(listener: MapClickEventListener) { | 297 | 298 | fun onMapClick(listener: MapClickEventListener) { | |
val win = execScript("document") as JSObject | 298 | 299 | val win = execScript("document") as JSObject | |
win.setMember("java", this) | 299 | 300 | win.setMember("java", this) | |
execScript("myMap.on('click', function(e){ document.java.mapClick(e.latlng.lat, e.latlng.lng);});") | 300 | 301 | execScript("myMap.on('click', function(e){ document.java.mapClick(e.latlng.lat, e.latlng.lng);});") | |
mapClickEvent.addListener(listener) | 301 | 302 | mapClickEvent.addListener(listener) | |
} | 302 | 303 | } | |
303 | 304 | |||
/** | 304 | 305 | /** | |
* Handles the callback from the mapClickEvent | 305 | 306 | * Handles the callback from the mapClickEvent | |
*/ | 306 | 307 | */ | |
fun mapClick(lat: Double, lng: Double) { | 307 | 308 | fun mapClick(lat: Double, lng: Double) { | |
val latlng = LatLong(lat, lng) | 308 | 309 | val latlng = LatLong(lat, lng) | |
mapClickEvent.MapClickEvent(latlng) | 309 | 310 | mapClickEvent.MapClickEvent(latlng) | |
} | 310 | 311 | } | |
311 | 312 | |||
/** | 312 | 313 | /** | |
* Draws a track path along the specified positions. | 313 | 314 | * Draws a track path along the specified positions. | |
* | 314 | 315 | * | |
* @param positions list of track positions | 315 | 316 | * @param positions list of track positions | |
*/ | 316 | 317 | */ | |
fun addTrack(positions: List<LatLong>) { | 317 | 318 | fun addTrack(positions: List<LatLong>) { | |
318 | 319 | |||
val jsPositions = positions | 319 | 320 | val jsPositions = positions | |
.map { " [${it.latitude}, ${it.longitude}]" } | 320 | 321 | .map { " [${it.latitude}, ${it.longitude}]" } | |
.joinToString(", \n") | 321 | 322 | .joinToString(", \n") | |
322 | 323 | |||
execScript( | 323 | 324 | execScript( | |
""" | 324 | 325 | """ | |
|var latLngs = [ | 325 | 326 | |var latLngs = [ | |
|$jsPositions | 326 | 327 | |$jsPositions | |
|]; | 327 | 328 | |]; | |
|var polyline = L.polyline(latLngs, {color: 'red', weight: 2}).addTo(myMap);""".trimMargin() | 328 | 329 | |var polyline = L.polyline(latLngs, {color: 'red', weight: 2}).addTo(myMap);""".trimMargin() | |
) | 329 | 330 | ) | |
} | 330 | 331 | } | |
331 | 332 | |||
fun clearAllLayer() { | 332 | 333 | fun clearAllLayer() { | |
execScript(""" | 333 | 334 | execScript(""" | |
myMap.eachLayer(function (layer) { | 334 | 335 | myMap.eachLayer(function (layer) { | |
map.removeLayer(layer); | 335 | 336 | map.removeLayer(layer); | |
}); | 336 | 337 | }); | |
""".trimIndent()) | 337 | 338 | """.trimIndent()) | |
} | 338 | 339 | } | |
339 | 340 | |||
fun addTrack(positions: List<LatLong>, id: String, color: Color, tooltip: String) { | 340 | 341 | fun addTrack(positions: List<LatLong>, id: String, color: Color, tooltip: String) { | |
341 | 342 | |||
val jsPositions = positions | 342 | 343 | val jsPositions = positions | |
.map { " [${it.latitude}, ${it.longitude}]" } | 343 | 344 | .map { " [${it.latitude}, ${it.longitude}]" } | |
.joinToString(", \n") | 344 | 345 | .joinToString(", \n") | |
345 | 346 | |||
val cleanTooltip = tooltip.replace("'", "'") | 346 | 347 | val cleanTooltip = tooltip.replace("'", "'") | |
execScript( | 347 | 348 | execScript( | |
""" | 348 | 349 | """ | |
|var latLngs = [ | 349 | 350 | |var latLngs = [ | |
|$jsPositions | 350 | 351 | |$jsPositions | |
|]; | 351 | 352 | |]; | |
|var color = "rgb(${Math.floor(color.getRed() * 255).toInt()} ,${Math.floor(color.getGreen() * 255) | 352 | 353 | |var color = "rgb(${Math.floor(color.getRed() * 255).toInt()} ,${Math.floor(color.getGreen() * 255) | |
.toInt()},${Math.floor(color.getBlue() * 255).toInt()})"; | 353 | 354 | .toInt()},${Math.floor(color.getBlue() * 255).toInt()})"; | |
|var polyline$id = L.polyline(latLngs, {color: color, weight: 2, zIndexOffset: 200}).bindTooltip('$cleanTooltip', {sticky: true}).addTo(trackGroup)""".trimMargin() | 354 | 355 | |var polyline$id = L.polyline(latLngs, {color: color, weight: 2, zIndexOffset: 200}).bindTooltip('$cleanTooltip', {sticky: true}).addTo(trackGroup)""".trimMargin() | |
) | 355 | 356 | ) | |
} | 356 | 357 | } | |
357 | 358 | |||
fun makeVesselTrackTransparent(id: String) { | 358 | 359 | fun makeVesselTrackTransparent(id: String) { | |
execScript("polyline$id.setStyle({opacity: 0.5});") | 359 | 360 | execScript("polyline$id.setStyle({opacity: 0.5});") | |
} | 360 | 361 | } | |
361 | 362 | |||
fun highlightTrack(id: String) { | 362 | 363 | fun highlightTrack(id: String) { | |
execScript("polyline$id.setStyle({weight: 4});") | 363 | 364 | execScript("polyline$id.setStyle({weight: 4});") | |
} | 364 | 365 | } | |
365 | 366 | |||
fun normalizeVesselTrack(id: String) { | 366 | 367 | fun normalizeVesselTrack(id: String) { | |
execScript("polyline$id.setStyle({opacity: 1,weight: 2});") | 367 | 368 | execScript("polyline$id.setStyle({opacity: 1,weight: 2});") |
src/main/kotlin/map/MapDisplayer.kt
View file @
3d3a5f8
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 | clearMarker(map) | |||
} | 9 | 10 | } | |
10 | 11 | |||
fun clearMapCluster(map: LeafletMapView) { | 11 | 12 | fun clearMapCluster(map: LeafletMapView) { | |
map.execScript( | 12 | 13 | map.execScript( | |
""" | 13 | 14 | """ | |
|myMap.removeLayer(markerClusters); | 14 | 15 | |myMap.removeLayer(markerClusters); | |
|var markerClusters = L.markerClusterGroup({spiderfyOnMaxZoom: false, disableClusteringAtZoom: 10}); | 15 | 16 | |var markerClusters = L.markerClusterGroup({spiderfyOnMaxZoom: false, disableClusteringAtZoom: 10}); | |
""".trimMargin() | 16 | 17 | """.trimMargin() | |
) | 17 | 18 | ) | |
} | 18 | 19 | } | |
19 | 20 | |||
fun clearMapCanvas(map: LeafletMapView) { | 20 | 21 | fun clearMapCanvas(map: LeafletMapView) { | |
map.execScript( | 21 | 22 | map.execScript( | |
""" | 22 | 23 | """ | |
|myRenderer.removeFrom(myMap); | 23 | 24 | |myRenderer.removeFrom(myMap); | |
|var myRenderer = L.canvas({ padding: 0.5 }); | 24 | 25 | |var myRenderer = L.canvas({ padding: 0.5 }); | |
""".trimMargin() | 25 | 26 | """.trimMargin() | |
) | 26 | 27 | ) | |
} | 27 | 28 | } | |
28 | 29 | |||
fun clearHeatMap(map: LeafletMapView) { | 29 | 30 | fun clearHeatMap(map: LeafletMapView) { | |
map.execScript( | 30 | 31 | map.execScript( | |
""" | 31 | 32 | """ | |
|heatLayer.removeFrom(myMap); | 32 | 33 | |heatLayer.removeFrom(myMap); | |
|var heatLayer = L.heatLayer([]).addTo(myMap); | 33 | 34 | |var heatLayer = L.heatLayer([]).addTo(myMap); | |
""".trimMargin() | 34 | 35 | """.trimMargin() | |
) | 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 | ||||
fun displayAllMessageOnMap(map: LeafletMapView) { | 38 | 50 | fun displayAllMessageOnMap(map: LeafletMapView) { | |
clearMap(map) | 39 | 51 | clearMap(map) | |
observableVessel.vessels.forEach { (_, value) -> | 40 | 52 | observableVessel.vessels.forEach { (_, value) -> | |
value.messages.forEach { (_, message) -> | 41 | 53 | 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 | 54 | map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap)") | |
} | 43 | 55 | } | |
} | 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 | ||||
fun displayAllMessageOnMap(map: LeafletMapView, selectedMMSI: String) { | 47 | 67 | fun displayAllMessageOnMap(map: LeafletMapView, selectedMMSI: String) { | |
clearMap(map) | 48 | 68 | clearMap(map) | |
observableVessel.vessels.forEach { (_, value) -> | 49 | 69 | observableVessel.vessels.forEach { (_, value) -> | |
value.messages.forEach { (_, message) -> | 50 | 70 | value.messages.forEach { (_, message) -> | |
if (selectedMMSI == message.mmsi.value) { | 51 | 71 | if (selectedMMSI == message.mmsi.value) { | |
map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap)") | 52 | 72 | map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 2, color: '#ff4040'}).addTo(myMap))") | |
} else { | 53 | 73 | } else { | |
map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap)") | 54 | 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 | ||||
fun displayClusterMessageOnMap(map: LeafletMapView) { | 60 | 92 | fun displayClusterMessageOnMap(map: LeafletMapView) { | |
clearMap(map) | 61 | 93 | clearMap(map) | |
observableVessel.vessels.forEach { (_, value) -> | 62 | 94 | observableVessel.vessels.forEach { (_, value) -> | |
value.messages.forEach { (_, message) -> | 63 | 95 | 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 | 96 | map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}));") | |
} | 65 | 97 | } | |
} | 66 | 98 | } | |
map.execScript("myMap.addLayer(markerClusters);") | 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 | ||||
fun displayClusterMessageOnMap(map: LeafletMapView, selectedMMSI: String) { | 70 | 111 | fun displayClusterMessageOnMap(map: LeafletMapView, selectedMMSI: String) { | |
clearMap(map) | 71 | 112 | clearMap(map) | |
observableVessel.vessels.forEach { (_, value) -> | 72 | 113 | observableVessel.vessels.forEach { (_, value) -> | |
value.messages.forEach { (_, message) -> | 73 | 114 | value.messages.forEach { (_, message) -> | |
if (selectedMMSI == message.mmsi.value) { | 74 | 115 | if (selectedMMSI == message.mmsi.value) { | |
map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap);") | 75 | 116 | map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 2, color: '#ff4040'}).addTo(myMap));") | |
} else { | 76 | 117 | } else { | |
map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}));") | 77 | 118 | map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}));") | |
} | 78 | 119 | } | |
} | 79 | 120 | } | |
} | 80 | 121 | } | |
map.execScript("myMap.addLayer(markerClusters);") | 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 | ||||
fun displayHeatMapOnMap(map: LeafletMapView) { | 84 | 138 | fun displayHeatMapOnMap(map: LeafletMapView) { | |
clearMap(map) | 85 | 139 | clearMap(map) | |
observableVessel.vessels.forEach { (_, value) -> | 86 | 140 | observableVessel.vessels.forEach { (_, value) -> | |
value.messages.forEach { (_, message) -> | 87 | 141 | value.messages.forEach { (_, message) -> | |
map.execScript("heatLayer.addLatLng([${message.latitude.value}, ${message.longitude.value}]);") | 88 | 142 | map.execScript("heatLayer.addLatLng([${message.latitude.value}, ${message.longitude.value}]);") | |
} | 89 | 143 | } | |
} | 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 | ||||
fun displayHeatMapOnMap(map: LeafletMapView, selectedMMSI: String) { | 93 | 155 | fun displayHeatMapOnMap(map: LeafletMapView, selectedMMSI: String) { | |
clearMap(map) | 94 | 156 | clearMap(map) | |
observableVessel.vessels.forEach { (_, value) -> | 95 | 157 | observableVessel.vessels.forEach { (_, value) -> | |
value.messages.forEach { (_, message) -> | 96 | 158 | value.messages.forEach { (_, message) -> | |
if (selectedMMSI == message.mmsi.value) { | 97 | 159 | if (selectedMMSI == message.mmsi.value) { | |
map.execScript("L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap);") | 98 | 160 | map.execScript("markers.push(L.circleMarker([${message.latitude.value}, ${message.longitude.value}], {radius: 2, color: '#ff4040'}).addTo(myMap));") | |
} else { | 99 | 161 | } else { | |
map.execScript("heatLayer.addLatLng([${message.latitude.value}, ${message.longitude.value}]);") | 100 | 162 | map.execScript("heatLayer.addLatLng([${message.latitude.value}, ${message.longitude.value}]);") | |
} | 101 | 163 | } | |
} | 102 | 164 | } | |
} | 103 | 165 | } | |
map.execScript("myMap.addLayer(markerClusters);") | 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 |
src/main/resources/gui/mapPanel.fxml
View file @
3d3a5f8
<?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/menuBar.fxml
View file @
3d3a5f8
<?xml version="1.0" encoding="UTF-8"?> | 1 | 1 | <?xml version="1.0" encoding="UTF-8"?> | |
2 | 2 | |||
<?import javafx.scene.control.*?> | 3 | 3 | <?import javafx.scene.control.*?> | |
4 | 4 | |||
<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 | 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"> | |
<menus> | 6 | 6 | <menus> | |
<Menu mnemonicParsing="false" text="File"> | 7 | 7 | <Menu mnemonicParsing="false" text="File"> | |
<items> | 8 | 8 | <items> | |
<MenuItem fx:id="import" mnemonicParsing="false" text="Import" /> | 9 | 9 | <MenuItem fx:id="import" mnemonicParsing="false" text="Import" /> | |
</items> | 10 | 10 | </items> | |
</Menu> | 11 | 11 | </Menu> | |
<Menu mnemonicParsing="false" text="Settings"> | 12 | 12 | <Menu mnemonicParsing="false" text="Settings"> | |
<items> | 13 | 13 | <items> | |
<Menu mnemonicParsing="false" text="Map selected"> | 14 | 14 | <Menu mnemonicParsing="false" text="Map selected"> | |
<items> | 15 | 15 | <items> | |
<CheckMenuItem mnemonicParsing="false" text="All messages" fx:id="allMessages"/> | 16 | 16 | <CheckMenuItem fx:id="allMessages" mnemonicParsing="false" text="All messages" /> | |
<CheckMenuItem mnemonicParsing="false" text="Clustered messages" fx:id="clusteredMessage"/> | 17 | 17 | <CheckMenuItem fx:id="clusteredMessage" mnemonicParsing="false" text="Clustered messages" /> | |
<CheckMenuItem mnemonicParsing="false" text="Heat map" fx:id="heatMap"/> | 18 | 18 | <CheckMenuItem fx:id="heatMap" mnemonicParsing="false" text="Heat map" /> | |
</items> | 19 | 19 | </items> | |
</Menu> | 20 | 20 | </Menu> | |
21 | <RadioMenuItem fx:id="activateReplayButton" mnemonicParsing="false" text="Unspecified Action" /> | |||
</items> | 21 | 22 | </items> | |
</Menu> | 22 | 23 | </Menu> | |
<Menu mnemonicParsing="false" text="Help"> | 23 | 24 | <Menu mnemonicParsing="false" text="Help"> | |
<items> | 24 | 25 | <items> | |
<MenuItem mnemonicParsing="false" text="About" /> | 25 | 26 | <MenuItem mnemonicParsing="false" text="About" /> | |
</items> | 26 | 27 | </items> | |
</Menu> | 27 | 28 | </Menu> | |
</menus> | 28 | 29 | </menus> | |
</MenuBar> | 29 | 30 | </MenuBar> | |
30 | 31 | |||
src/main/resources/gui/timePanel.fxml
View file @
3d3a5f8
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 | <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> |