Compare View

switch
from
...
to
 
Commits (7)

Diff

Showing 26 changed files Inline Diff

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()
40
41 private var selectedItem: Pair<String, ArrayList<MessageData?>>? = null
24 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 81
82 plotData.allTraces.clear()
83 config.setxAxisTitle("")
84 config.setyAxisTitle("")
85 config.plotTitle = ""
86 dataViewer.updateConfiguration(config)
87 dataViewer.updatePlot(plotData)
88 initDataList()
61 89
dataViewer.resetPlot() 62
63 90
return@addListener 64 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))
236 292 data.add(Pair("Longitude", longitude))
data.add(Pair("Latitude", populateLatitude(vessel))) 237 293 data.add(Pair("Speed Over Ground", speedOverGround))
238 294 data.add(Pair("Course Over Ground", courseOverGround))
data.add(Pair("Longitude", populateLongitude(vessel))) 239 295 data.add(Pair("Heading", heading))
240 296 data.add(Pair("Vessel Name", vesselName))
data.add(Pair("Speed Over Ground", populateSpeedOverGround(vessel))) 241 297 data.add(Pair("IMO", imo))
242 298 data.add(Pair("Call Sign", callSign))
data.add(Pair("Course Over Ground", populateCourseOverGround(vessel))) 243 299 data.add(Pair("Vessel Type", vesselType))
244 300 data.add(Pair("Status", status))
data.add(Pair("Heading", populateHeading(vessel))) 245 301 data.add(Pair("Length", length))
246 302 data.add(Pair("Width", width))
data.add(Pair("Vessel Name", populateVesselName(vessel))) 247 303 data.add(Pair("Draft", draft))
248 304 data.add(Pair("Cargo", cargo))
data.add(Pair("IMO", populateIMO(vessel))) 249
250
data.add(Pair("Call Sign", populateCallSign(vessel))) 251
252
data.add(Pair("Vessel Type", populateVesselType(vessel))) 253
254
data.add(Pair("Status", populateStatus(vessel))) 255
256 305
data.add(Pair("Length", populateLength(vessel))) 257 306 dataList.addAll(data)
307 }
258 308
data.add(Pair("Width", populateWidth(vessel))) 259 309 private fun updateDataList(vessel: Vessel) {
310 timeData = populateTime(vessel)
260 311
data.add(Pair("Draft", populateDraft(vessel))) 261 312 if(dataListView.selectionModel.selectedItem == null) return
262 313
data.add(Pair("Cargo", populateCargo(vessel))) 263 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 }
264 373
dataList.addAll(data) 265 374 private fun setObservableCurrentTimeListener() {
375 observableCurrentTime.listeners.add(object : CurrentTime {
376 override fun onValueChanged(newValue: Int) {
377 updateDataList(observableSelectedVessel.value)
378 plot()
379 }
380 })
} 266 381 }
267 382
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!!)
37 } else {
38 updateMap()
39 }
40 }
41 })
42 }
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!!)
} else { 40 49 } else {
updateMap() 41 50 updateMap()
} 42 51 }
} 43 52 }
}) 44 53 })
} 45 54 }
46 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
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 }
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 }
10 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 }
23 31
32 Vessel.maxTime = maxTime
33 Vessel.minTime = minTime
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("'", "&apos;") 346 347 val cleanTooltip = tooltip.replace("'", "&apos;")
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
xmlns:fx="http://javafx.com/fxml" 6
fx:controller="application.controller.MapPanelController" 7
fx:id="map"/> 8
5 <VBox prefHeight="150.0" prefWidth="371.0" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.controller.MapPanelController">
6 <StackPane fx:id="map" />
7 <fx:include source="timePanel.fxml" />
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>