diff --git a/build.gradle b/build.gradle index 45554c6..b202bb8 100644 --- a/build.gradle +++ b/build.gradle @@ -17,6 +17,7 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" implementation 'org.jfxtras:jmetro:8.6.9' implementation 'org.slf4j:slf4j-api:1.7.30' + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9" testCompile group: 'junit', name: 'junit', version: '4.12' testCompile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3' compile group: 'ch.qos.logback', name: 'logback-core', version: '1.2.3' diff --git a/src/main/kotlin/application/App.kt b/src/main/kotlin/application/App.kt index 47397f1..c27eacb 100644 --- a/src/main/kotlin/application/App.kt +++ b/src/main/kotlin/application/App.kt @@ -29,6 +29,7 @@ class App : Application() { } private fun closeApplication() { + Platform.exit() exitProcess(0) } diff --git a/src/main/kotlin/application/controller/DataPanelController.kt b/src/main/kotlin/application/controller/DataPanelController.kt index ada250d..69a9f2d 100644 --- a/src/main/kotlin/application/controller/DataPanelController.kt +++ b/src/main/kotlin/application/controller/DataPanelController.kt @@ -7,6 +7,8 @@ import javafx.fxml.FXML import javafx.fxml.Initializable import javafx.scene.control.ListCell import javafx.scene.control.ListView +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch import org.charts.dataviewer.api.config.DataViewerConfiguration import org.charts.dataviewer.api.data.PlotData import org.charts.dataviewer.api.trace.ScatterTrace @@ -21,6 +23,22 @@ class DataPanelController : Initializable, SelectedVesselListener { private var dataList: ObservableList>> = FXCollections.observableArrayList() private lateinit var timeData: ArrayList + private val latitude: ArrayList = arrayListOf() + private val longitude: ArrayList = arrayListOf() + private val speedOverGround: ArrayList = arrayListOf() + private val courseOverGround: ArrayList = arrayListOf() + private val heading: ArrayList = arrayListOf() + private val vesselName: ArrayList = arrayListOf() + private val imo: ArrayList = arrayListOf() + private val callSign: ArrayList = arrayListOf() + private val vesselType: ArrayList = arrayListOf() + private val status: ArrayList = arrayListOf() + private val length: ArrayList = arrayListOf() + private val width: ArrayList = arrayListOf() + private val draft: ArrayList = arrayListOf() + private val cargo: ArrayList = arrayListOf() + + private var selectedItem: Pair>? = null @FXML var dataListView = ListView>>() @@ -28,17 +46,20 @@ class DataPanelController : Initializable, SelectedVesselListener { @FXML var dataViewer = JavaFxDataViewer() + private val plotData = PlotData() + private val config = DataViewerConfiguration() override fun initialize(location: URL?, resources: ResourceBundle?) { setObservableSelectedVesselListener() dataListView.items = dataList - val plotData = PlotData() - val config = DataViewerConfiguration() + config.showLegend(true) config.setLegendInsidePlot(false) + setObservableCurrentTimeListener() + dataListView.setCellFactory { object : ListCell>?>() { override fun updateItem(item: Pair>?, empty: Boolean) { @@ -53,224 +74,296 @@ class DataPanelController : Initializable, SelectedVesselListener { } dataListView.selectionModel.selectedItemProperty().addListener { _, _, newValue -> - if (newValue == null) { - plotData.allTraces.clear() - config.setxAxisTitle("") - config.setyAxisTitle("") - dataViewer.updateConfiguration(config) + selectedItem = newValue + plot(newValue) + } + + plotData.allTraces.clear() + config.setxAxisTitle("") + config.setyAxisTitle("") + config.plotTitle = "" + dataViewer.updateConfiguration(config) + dataViewer.updatePlot(plotData) + initDataList() + - dataViewer.resetPlot() + } - return@addListener + private fun plot() { + if (selectedItem != null) { + GlobalScope.launch { + plot(selectedItem) } + } + } - val getValueVisitorX = GetValueVisitor() - val getValueVisitorY = GetValueVisitor() + private fun plot(data: Pair>?) { + if (data == null) { + plotData.allTraces.clear() + config.setxAxisTitle("") + config.setyAxisTitle("") + dataViewer.updateConfiguration(config) - val arrayListStringX = arrayListOf() - val arrayListDoubleX = arrayListOf() - val arrayListStringY = arrayListOf() - val arrayListDoubleY = arrayListOf() + dataViewer.resetPlot() - for (x in 0 until newValue.second.size) { - timeData[x]?.accept(getValueVisitorX) - newValue.second[x]?.accept(getValueVisitorY) + return + } - if (getValueVisitorY.value.toDoubleOrNull() == null) { - arrayListStringX.add(getValueVisitorX.value) - arrayListStringY.add(getValueVisitorY.value) - } else { - arrayListStringX.add(getValueVisitorX.value) - arrayListDoubleY.add(getValueVisitorY.value.toDouble()) - } - } + val getValueVisitorX = GetValueVisitor() + val getValueVisitorY = GetValueVisitor() - val scatterTrace = ScatterTrace() - scatterTrace.traceColour = TraceColour.RED - scatterTrace.traceVisibility = TraceVisibility.TRUE + val arrayListStringX = arrayListOf() +// val arrayListDoubleX = arrayListOf() + val arrayListStringY = arrayListOf() + val arrayListDoubleY = arrayListOf() - val serieStringX: Array = arrayListStringX.toTypedArray() -// val serieDoubleX: Array = arrayListDoubleX.toTypedArray() - val serieStringY: Array = arrayListStringY.toTypedArray() - val serieDoubleY: Array = arrayListDoubleY.toTypedArray() + for (x in 0 until timeData.size) { + timeData[x]?.accept(getValueVisitorX) + data.second[x]?.accept(getValueVisitorY) if (getValueVisitorY.value.toDoubleOrNull() == null) { - scatterTrace.setxArray(serieStringX) - scatterTrace.setyArray(serieStringY) + arrayListStringX.add(getValueVisitorX.value) + arrayListStringY.add(getValueVisitorY.value) } else { - scatterTrace.setxArray(serieStringX) - scatterTrace.setyArray(serieDoubleY) + arrayListStringX.add(getValueVisitorX.value) + arrayListDoubleY.add(getValueVisitorY.value.toDouble()) } + } - config.plotTitle = "" - config.setxAxisTitle("Time (s)") - config.setyAxisTitle(newValue.first) - dataViewer.resetPlot() - plotData.allTraces.clear() - plotData.addTrace(scatterTrace) - dataViewer.updateConfiguration(config) - dataViewer.updatePlot(plotData) + val scatterTrace = ScatterTrace() + scatterTrace.traceColour = TraceColour.RED + scatterTrace.traceVisibility = TraceVisibility.TRUE + val serieStringX: Array = arrayListStringX.toTypedArray() +// val serieDoubleX: Array = arrayListDoubleX.toTypedArray() + val serieStringY: Array = arrayListStringY.toTypedArray() + val serieDoubleY: Array = arrayListDoubleY.toTypedArray() + + if (getValueVisitorY.value.toDoubleOrNull() == null) { + scatterTrace.setxArray(serieStringX) + scatterTrace.setyArray(serieStringY) + } else { + scatterTrace.setxArray(serieStringX) + scatterTrace.setyArray(serieDoubleY) } - plotData.allTraces.clear() - config.setxAxisTitle("") - config.setyAxisTitle("") config.plotTitle = "" + config.setxAxisTitle("Time (s)") + config.setyAxisTitle(data.first) + dataViewer.resetPlot() + plotData.allTraces.clear() + plotData.addTrace(scatterTrace) dataViewer.updateConfiguration(config) dataViewer.updatePlot(plotData) } + private fun setObservableSelectedVesselListener() { observableSelectedVessel.listeners.add(this) } private fun populateTime(vessel: Vessel): ArrayList { - val allTime: ArrayList = vessel.getAllTime() - allTime.sortBy { (it as Time).value } - - return allTime + return if (observableIsReplayState.value) { + vessel.getAllTimeBeforeSelectedTime() + } else { + vessel.getAllTime() + } } - private fun populateLatitude(vessel: Vessel): ArrayList { - val allLatitude: ArrayList = vessel.getAllLatitude() - allLatitude.sortBy { (it as Latitude).value } - return allLatitude + private fun populateLatitude(vessel: Vessel): ArrayList { + return if (observableIsReplayState.value) { + vessel.getAllLatitudeBeforeSelectedTime() + } else { + vessel.getAllLatitude() + } } private fun populateLongitude(vessel: Vessel): ArrayList { - val allLongitude: ArrayList = vessel.getAllLongitude() - allLongitude.sortBy { (it as Longitude).value } - - return allLongitude + return if (observableIsReplayState.value) { + vessel.getAllLongitudeBeforeSelectedTime() + } else { + vessel.getAllLongitude() + } } private fun populateSpeedOverGround(vessel: Vessel): ArrayList { - val allSpeedOverGround: ArrayList = vessel.getAllSpeedOverGround() - allSpeedOverGround.sortBy { (it as SpeedOverGround).value } - - return allSpeedOverGround + return if (observableIsReplayState.value) { + vessel.getAllSpeedOverGroundBeforeSelectedTime() + } else { + vessel.getAllSpeedOverGround() + } } private fun populateCourseOverGround(vessel: Vessel): ArrayList { - val allCourseOverGround: ArrayList = vessel.getAllCourseOverGround() - allCourseOverGround.sortBy { (it as CourseOverGround).value } - - return allCourseOverGround + return if (observableIsReplayState.value) { + vessel.getAllCourseOverGroundBeforeSelectedTime() + } else { + vessel.getAllCourseOverGround() + } } private fun populateHeading(vessel: Vessel): ArrayList { - val allHeading: ArrayList = vessel.getAllHeading() - allHeading.sortBy { (it as Heading).value } - - return allHeading + return if (observableIsReplayState.value) { + vessel.getAllHeadingBeforeSelectedTime() + } else { + vessel.getAllHeading() + } } private fun populateVesselName(vessel: Vessel): ArrayList { - val allVesselName: ArrayList = vessel.getAllVesselName() - allVesselName.sortBy { (it as VesselName).value } - - return allVesselName + return if (observableIsReplayState.value) { + vessel.getAllVesselNameBeforeSelectedTime() + } else { + vessel.getAllVesselName() + } } private fun populateIMO(vessel: Vessel): ArrayList { - val allIMO: ArrayList = vessel.getAllIMO() - allIMO.sortBy { (it as IMO).value } - - return allIMO + return if (observableIsReplayState.value) { + vessel.getAllIMOBeforeSelectedTime() + } else { + vessel.getAllIMO() + } } private fun populateCallSign(vessel: Vessel): ArrayList { - val allCallSign: ArrayList = vessel.getAllCallSign() - allCallSign.sortBy { (it as CallSign).value } - - return allCallSign + return if (observableIsReplayState.value) { + vessel.getAllCallSignBeforeSelectedTime() + } else { + vessel.getAllCallSign() + } } private fun populateVesselType(vessel: Vessel): ArrayList { - val allVesselType: ArrayList = vessel.getAllVesselType() - allVesselType.sortBy { (it as VesselType).value } - - return allVesselType + return if (observableIsReplayState.value) { + vessel.getAllVesselTypeBeforeSelectedTime() + } else { + vessel.getAllVesselType() + } } private fun populateStatus(vessel: Vessel): ArrayList { - val allStatus: ArrayList = vessel.getAllStatus() - allStatus.sortBy { (it as Status).value } - - return allStatus + return if (observableIsReplayState.value) { + vessel.getAllStatusBeforeSelectedTime() + } else { + vessel.getAllStatus() + } } private fun populateLength(vessel: Vessel): ArrayList { - val allLength: ArrayList = vessel.getAllLength() - allLength.sortBy { (it as Length).value } - - return allLength + return if (observableIsReplayState.value) { + vessel.getAllLengthBeforeSelectedTime() + } else { + vessel.getAllLength() + } } private fun populateWidth(vessel: Vessel): ArrayList { - val allWidth: ArrayList = vessel.getAllWidth() - allWidth.sortBy { (it as Width).value } - - return allWidth + return if (observableIsReplayState.value) { + vessel.getAllWidthBeforeSelectedTime() + } else { + vessel.getAllWidth() + } } private fun populateDraft(vessel: Vessel): ArrayList { - val allDraft: ArrayList = vessel.getAllDraft() - allDraft.sortBy { (it as Draft).value } - - return allDraft + return if (observableIsReplayState.value) { + vessel.getAllDraftBeforeSelectedTime() + } else { + vessel.getAllDraft() + } } private fun populateCargo(vessel: Vessel): ArrayList { - val allCargo: ArrayList = vessel.getAllCargo() - allCargo.sortBy { (it as Cargo).value } - - return allCargo + return if (observableIsReplayState.value) { + vessel.getAllCargoBeforeSelectedTime() + } else { + vessel.getAllCargo() + } } - private fun populateDataList(vessel: Vessel) { + private fun initDataList() { val data = arrayListOf>>() + data.add(Pair("Latitude", latitude)) + data.add(Pair("Longitude", longitude)) + data.add(Pair("Speed Over Ground", speedOverGround)) + data.add(Pair("Course Over Ground", courseOverGround)) + data.add(Pair("Heading", heading)) + data.add(Pair("Vessel Name", vesselName)) + data.add(Pair("IMO", imo)) + data.add(Pair("Call Sign", callSign)) + data.add(Pair("Vessel Type", vesselType)) + data.add(Pair("Status", status)) + data.add(Pair("Length", length)) + data.add(Pair("Width", width)) + data.add(Pair("Draft", draft)) + data.add(Pair("Cargo", cargo)) + + dataList.addAll(data) + } + + private fun updateDataList(vessel: Vessel) { timeData = populateTime(vessel) - data.add(Pair("Latitude", populateLatitude(vessel))) + latitude.clear() + latitude.addAll(populateLatitude(vessel)) - data.add(Pair("Longitude", populateLongitude(vessel))) + longitude.clear() + longitude.addAll(populateLongitude(vessel)) - data.add(Pair("Speed Over Ground", populateSpeedOverGround(vessel))) + speedOverGround.clear() + speedOverGround.addAll(populateSpeedOverGround(vessel)) - data.add(Pair("Course Over Ground", populateCourseOverGround(vessel))) + courseOverGround.clear() + courseOverGround.addAll(populateCourseOverGround(vessel)) - data.add(Pair("Heading", populateHeading(vessel))) + heading.clear() + heading.addAll(populateHeading(vessel)) - data.add(Pair("Vessel Name", populateVesselName(vessel))) + vesselName.clear() + vesselName.addAll(populateVesselName(vessel)) - data.add(Pair("IMO", populateIMO(vessel))) + imo.clear() + imo.addAll(populateIMO(vessel)) - data.add(Pair("Call Sign", populateCallSign(vessel))) + callSign.clear() + callSign.addAll(populateCallSign(vessel)) - data.add(Pair("Vessel Type", populateVesselType(vessel))) + vesselType.clear() + vesselType.addAll(populateVesselType(vessel)) - data.add(Pair("Status", populateStatus(vessel))) + status.clear() + status.addAll(populateStatus(vessel)) - data.add(Pair("Length", populateLength(vessel))) + length.clear() + length.addAll(populateLength(vessel)) - data.add(Pair("Width", populateWidth(vessel))) + width.clear() + width.addAll(populateWidth(vessel)) - data.add(Pair("Draft", populateDraft(vessel))) + draft.clear() + draft.addAll(populateDraft(vessel)) - data.add(Pair("Cargo", populateCargo(vessel))) + cargo.clear() + cargo.addAll(populateCargo(vessel)) - dataList.addAll(data) } - override fun onValueChanged(newValue: Vessel) { - dataList.clear() - populateDataList(newValue) + private fun setObservableCurrentTimeListener() { + observableCurrentTime.listeners.add(object : CurrentTime { + override fun onValueChanged(newValue: Int) { + updateDataList(observableSelectedVessel.value) + plot() + } + }) + } + override fun onValueChanged(newValue: Vessel) { + updateDataList(newValue) + plot() } } diff --git a/src/main/kotlin/application/controller/MapPanelController.kt b/src/main/kotlin/application/controller/MapPanelController.kt index d4478a7..a6f202e 100644 --- a/src/main/kotlin/application/controller/MapPanelController.kt +++ b/src/main/kotlin/application/controller/MapPanelController.kt @@ -2,6 +2,7 @@ package application.controller import application.model.* import application.model.MapState.* +import javafx.application.Platform import javafx.fxml.FXML import javafx.fxml.Initializable import javafx.scene.layout.StackPane @@ -53,33 +54,37 @@ class MapPanelController : Initializable { } private fun updateMap() { - if (observableIsReplayState.value) { - when (observableMapState.state) { - ALL_MESSAGES -> displayTimedAllMessageOnMap(mapView) - CLUSTERED_MESSAGES -> displayTimedClusterMessageOnMap(mapView) - HEAT_MAP -> displayTimedHeatMapOnMap(mapView) - } - } else { - when (observableMapState.state) { - ALL_MESSAGES -> displayAllMessageOnMap(mapView) - CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView) - HEAT_MAP -> displayHeatMapOnMap(mapView) + Platform.runLater { + if (observableIsReplayState.value) { + when (observableMapState.state) { + ALL_MESSAGES -> displayTimedAllMessageOnMap(mapView) + CLUSTERED_MESSAGES -> displayTimedClusterMessageOnMap(mapView) + HEAT_MAP -> displayTimedHeatMapOnMap(mapView) + } + } else { + when (observableMapState.state) { + ALL_MESSAGES -> displayAllMessageOnMap(mapView) + CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView) + HEAT_MAP -> displayHeatMapOnMap(mapView) + } } } } private fun updateMap(selectedMMSI: String) { - if (observableIsReplayState.value) { - when (observableMapState.state) { - ALL_MESSAGES -> displayTimedAllMessageOnMap(mapView, selectedMMSI) - CLUSTERED_MESSAGES -> displayTimedClusterMessageOnMap(mapView, selectedMMSI) - HEAT_MAP -> displayTimedHeatMapOnMap(mapView, selectedMMSI) - } - } else { - when (observableMapState.state) { - ALL_MESSAGES -> displayAllMessageOnMap(mapView, selectedMMSI) - CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView, selectedMMSI) - HEAT_MAP -> displayHeatMapOnMap(mapView, selectedMMSI) + Platform.runLater { + if (observableIsReplayState.value) { + when (observableMapState.state) { + ALL_MESSAGES -> displayTimedAllMessageOnMap(mapView, selectedMMSI) + CLUSTERED_MESSAGES -> displayTimedClusterMessageOnMap(mapView, selectedMMSI) + HEAT_MAP -> displayTimedHeatMapOnMap(mapView, selectedMMSI) + } + } else { + when (observableMapState.state) { + ALL_MESSAGES -> displayAllMessageOnMap(mapView, selectedMMSI) + CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView, selectedMMSI) + HEAT_MAP -> displayHeatMapOnMap(mapView, selectedMMSI) + } } } } @@ -93,7 +98,7 @@ class MapPanelController : Initializable { } private fun setObservableIsReplayState() { - observableIsReplayState.listeners.add(object: ReplayState { + observableIsReplayState.listeners.add(object : ReplayState { override fun onValueChanged(newValue: Boolean) { if (observableSelectedVessel.value.mmsi != null) { updateMap(observableSelectedVessel.value.mmsi!!) diff --git a/src/main/kotlin/application/controller/TimePanel.kt b/src/main/kotlin/application/controller/TimePanel.kt index c197338..133d31c 100644 --- a/src/main/kotlin/application/controller/TimePanel.kt +++ b/src/main/kotlin/application/controller/TimePanel.kt @@ -61,8 +61,10 @@ class TimePanel : Initializable { observableIsReplayState.listeners.add(object : ReplayState { override fun onValueChanged(newValue: Boolean) { if (observableIsReplayState.value) { + observableCurrentTime.value = timeSlider.value.toInt() unableAllButton() } else { + observableCurrentTime.value = Int.MAX_VALUE disableAllButton() } } diff --git a/src/main/kotlin/application/model/ObservableCurrentTime.kt b/src/main/kotlin/application/model/ObservableCurrentTime.kt index 2b579ef..9a8a30c 100644 --- a/src/main/kotlin/application/model/ObservableCurrentTime.kt +++ b/src/main/kotlin/application/model/ObservableCurrentTime.kt @@ -7,7 +7,7 @@ class ObservableCurrentTime { val listeners: MutableList = mutableListOf() var value: Int by Delegates.observable( - initialValue = 0, + initialValue = Int.MAX_VALUE, onChange = { _, _, new -> run { listeners.forEach { diff --git a/src/main/kotlin/application/model/Vessel.kt b/src/main/kotlin/application/model/Vessel.kt index 201bda7..4709919 100644 --- a/src/main/kotlin/application/model/Vessel.kt +++ b/src/main/kotlin/application/model/Vessel.kt @@ -2,9 +2,13 @@ package application.model import java.util.* - class Vessel(val mmsi: String?) { val messages: SortedMap = sortedMapOf() + private val messageBeforeSelectedTime: Map + get() { + return messages.filter { observableCurrentTime.value > it.key } + } + var messageToDisplay: Message? = null get() { field = @@ -12,6 +16,132 @@ class Vessel(val mmsi: String?) { return field } + fun getAllTimeBeforeSelectedTime(): ArrayList { + val timeList = arrayListOf() + messageBeforeSelectedTime.forEach { + timeList.add(it.value.time) + } + + return timeList + } + + fun getAllLatitudeBeforeSelectedTime(): ArrayList { + val latitudeList = arrayListOf() + messageBeforeSelectedTime.forEach { + latitudeList.add(it.value.latitude) + } + + return latitudeList + } + + fun getAllLongitudeBeforeSelectedTime(): ArrayList { + val longitudeList = arrayListOf() + messageBeforeSelectedTime.forEach { + longitudeList.add(it.value.longitude) + } + + return longitudeList + } + + fun getAllSpeedOverGroundBeforeSelectedTime(): ArrayList { + val speedOverGroundList = arrayListOf() + messageBeforeSelectedTime.forEach { + speedOverGroundList.add(it.value.speedOverGround) + } + + return speedOverGroundList + } + + fun getAllCourseOverGroundBeforeSelectedTime(): ArrayList { + val res = arrayListOf() + messageBeforeSelectedTime.forEach { + res.add(it.value.courseOverGround) + } + + return res + } + + fun getAllHeadingBeforeSelectedTime(): ArrayList { + val res = arrayListOf() + messageBeforeSelectedTime.forEach { + res.add(it.value.heading) + } + + return res + } + + fun getAllVesselNameBeforeSelectedTime(): ArrayList { + val res = arrayListOf() + messageBeforeSelectedTime.forEach { + res.add(it.value.vesselName) + } + return res + } + + fun getAllIMOBeforeSelectedTime(): ArrayList { + val res = arrayListOf() + messageBeforeSelectedTime.forEach { + res.add(it.value.imo) + } + return res + } + + fun getAllCallSignBeforeSelectedTime(): ArrayList { + val res = arrayListOf() + messageBeforeSelectedTime.forEach { + res.add(it.value.callSign) + } + return res + } + + fun getAllVesselTypeBeforeSelectedTime(): ArrayList { + val res = arrayListOf() + messageBeforeSelectedTime.forEach { + res.add(it.value.vesselType) + } + return res + } + + fun getAllStatusBeforeSelectedTime(): ArrayList { + val res = arrayListOf() + messageBeforeSelectedTime.forEach { + res.add(it.value.status) + } + return res + } + + fun getAllLengthBeforeSelectedTime(): ArrayList { + val res = arrayListOf() + messageBeforeSelectedTime.forEach { + res.add(it.value.length) + } + return res + } + + fun getAllWidthBeforeSelectedTime(): ArrayList { + val res = arrayListOf() + messageBeforeSelectedTime.forEach { + res.add(it.value.width) + } + return res + } + + fun getAllDraftBeforeSelectedTime(): ArrayList { + val res = arrayListOf() + messageBeforeSelectedTime.forEach { + res.add(it.value.draft) + } + return res + } + + fun getAllCargoBeforeSelectedTime(): ArrayList { + val res = arrayListOf() + messageBeforeSelectedTime.forEach { + res.add(it.value.cargo) + } + return res + } + fun getAllTime(): ArrayList { val timeList = arrayListOf() messages.forEach { diff --git a/src/main/resources/gui/timePanel.fxml b/src/main/resources/gui/timePanel.fxml index 8cf2145..d4eb991 100644 --- a/src/main/resources/gui/timePanel.fxml +++ b/src/main/resources/gui/timePanel.fxml @@ -6,15 +6,7 @@ - - - +