Commit 53f01ecc3407a1e8b0a5bc7ec5947988e380f5e2

Authored by lsagona
1 parent d06a68ec66
Exists in master and in 1 other branch dev

display message on map

Showing 22 changed files with 191 additions and 100 deletions Inline Diff

src/main/kotlin/application/controller/MapPanelController.kt View file @ 53f01ec
File was created 1 package application.controller
2
3 import application.model.MessageListener
4 import application.model.Vessel
5 import application.model.observableMessages
6 import javafx.concurrent.Worker
7 import javafx.fxml.FXML
8 import javafx.fxml.Initializable
9 import javafx.scene.layout.StackPane
10 import map.LeafletMapView
11 import map.MapConfig
12 import map.displayMessageOnMap
13 import java.net.URL
14 import java.util.*
15 import java.util.concurrent.CompletableFuture
16
17 class MapPanelController : Initializable, MessageListener {
18
19 @FXML
20 private lateinit var map: StackPane
21
22 private val mapView = LeafletMapView()
23
24
25 override fun initialize(location: URL?, resources: ResourceBundle?) {
26 val completeFutureMap: CompletableFuture<Worker.State> = mapView.displayMap(MapConfig())
27 observableMessages.listeners.add(this)
28
29 /*completeFutureMap.whenComplete{
30 workerState, _ ->
31 if (workerState == Worker.State.SUCCEEDED) {
32 }
src/main/kotlin/application/controller/MenuBarController.kt View file @ 53f01ec
package application.controller 1 1 package application.controller
2 2
import application.App 3
import application.model.createVesselCollection 4 3 import application.model.createVesselCollection
import application.model.observableMessages 5 4 import application.model.observableMessages
import javafx.event.EventHandler 6 5 import javafx.event.EventHandler
import javafx.fxml.FXML 7 6 import javafx.fxml.FXML
import javafx.fxml.FXMLLoader 8
import javafx.fxml.Initializable 9 7 import javafx.fxml.Initializable
import javafx.scene.Parent 10
import javafx.scene.control.MenuBar 11 8 import javafx.scene.control.MenuBar
import javafx.scene.control.MenuItem 12 9 import javafx.scene.control.MenuItem
import javafx.stage.FileChooser 13 10 import javafx.stage.FileChooser
import java.net.URL 14 11 import java.net.URL
import java.util.* 15 12 import java.util.*
16 13
class MenuBarController : Initializable { 17 14 class MenuBarController : Initializable {
18 15
@FXML 19 16 @FXML
var menuBar: MenuBar = MenuBar() 20 17 var menuBar: MenuBar = MenuBar()
21 18
@FXML 22 19 @FXML
var import: MenuItem = MenuItem() 23 20 var import: MenuItem = MenuItem()
24 21
override fun initialize(location: URL?, resources: ResourceBundle?) { 25 22 override fun initialize(location: URL?, resources: ResourceBundle?) {
26 23
import.onAction = EventHandler { 27 24 import.onAction = EventHandler {
val fileChooser = FileChooser() 28 25 val fileChooser = FileChooser()
fileChooser.title = "Choose a file to import" 29 26 fileChooser.title = "Choose a file to import"
val window = menuBar.scene.window 30 27 val window = menuBar.scene.window
val file = fileChooser.showOpenDialog(window) 31 28 val file = fileChooser.showOpenDialog(window)
src/main/kotlin/application/model/ObservableVessel.kt View file @ 53f01ec
package application.model 1 1 package application.model
2 2
3 import java.util.*
import kotlin.properties.Delegates 3 4 import kotlin.properties.Delegates
4 5
class ObservableVessel { 5 6 class ObservableVessel {
val listeners: MutableList<MessageListener> = mutableListOf() 6 7 val listeners: MutableList<MessageListener> = mutableListOf()
7 8
var vessels: MutableMap<Int?, Vessel> by Delegates.observable( 8 9 var vessels: MutableMap<Int?, Vessel> by Delegates.observable(
initialValue = mutableMapOf(), 9 10 initialValue = mutableMapOf(),
onChange = { 10 11 onChange = {
_, _, new -> 11 12 _, _, new ->
run { 12 13 run {
listeners.forEach { 13 14 listeners.forEach {
it.onValueChanged(new) 14 15 it.onValueChanged(new)
} 15 16 }
} 16 17 }
src/main/kotlin/application/model/Vessel.kt View file @ 53f01ec
package application.model 1 1 package application.model
2 2
import java.time.LocalDateTime 3 3 import java.time.LocalDateTime
4 import java.util.*
4 5
5 6
class Vessel() { 6 7 class Vessel {
val messages: MutableMap<LocalDateTime, Message> = mutableMapOf() 7 8 val messages: SortedMap<LocalDateTime, Message> = sortedMapOf()
src/main/kotlin/application/model/VesselGenerator.kt View file @ 53f01ec
package application.model 1 1 package application.model
2 2
import java.io.File 3 3 import java.io.File
4 import java.util.*
5 import kotlin.collections.ArrayList
4 6
fun createVesselCollection(file: File) : MutableMap<Int?, Vessel> { 5 7 fun createVesselCollection(file: File) : SortedMap<Int, Vessel> {
val messages : ArrayList<Message> = arrayListOf() 6 8 val messages : ArrayList<Message> = arrayListOf()
val vessels: MutableMap<Int?, Vessel> = mutableMapOf() 7 9 val vessels: SortedMap<Int, Vessel> = sortedMapOf()
8 10
file.forEachLine { 9 11 file.forEachLine {
val arrayMessage = it.split(",") 10 12 val arrayMessage = it.split(",")
if (arrayMessage[0].toIntOrNull() !== null) { 11 13 if (arrayMessage[0].toIntOrNull() !== null) {
val message = Message(arrayMessage) 12 14 val message = Message(arrayMessage)
messages.add(message) 13 15 messages.add(message)
if (!vessels.containsKey(message.mmsi)){ 14 16 if (!vessels.containsKey(message.mmsi)){
vessels[message.mmsi] = Vessel() 15 17 vessels[message.mmsi] = Vessel()
} 16 18 }
vessels[message.mmsi]?.messages?.set(message.time, message) 17 19 vessels[message.mmsi]?.messages?.set(message.time, message)
} 18 20 }
19 21
} 20 22 }
src/main/kotlin/map/Circle.kt View file @ 53f01ec
package fdit.leafletmap 1 1 package map
2 2
import javafx.scene.paint.Color 3 3 import javafx.scene.paint.Color
4 4
class Circle private constructor(private var center: LatLong, private var title: String, private var zIndexOffset: Int) { 5 5 class Circle private constructor(private var center: LatLong, private var title: String, private var zIndexOffset: Int) {
private var color = Color(0.0, 0.0, 0.0, 0.0) 6 6 private var color = Color(0.0, 0.0, 0.0, 0.0)
private lateinit var map: LeafletMapView 7 7 private lateinit var map: LeafletMapView
private var isAttached = false 8 8 private var isAttached = false
private var isDisplayed = false 9 9 private var isDisplayed = false
private var radius = 0.0 10 10 private var radius = 0.0
11 11
constructor(position: LatLong, radius: Double, title: String, color: Color, zIndexOffset: Int) : this(position, title, zIndexOffset) { 12 12 constructor(position: LatLong, radius: Double, title: String, color: Color, zIndexOffset: Int) : this(position, title, zIndexOffset) {
this.color = color 13 13 this.color = color
this.title = title.replace("-", "") 14 14 this.title = title.replace("-", "")
this.center = position 15 15 this.center = position
this.radius = nauticalMilesToMeter(radius) 16 16 this.radius = nauticalMilesToMeter(radius)
} 17 17 }
18 18
internal fun addToMap(map: LeafletMapView) { 19 19 internal fun addToMap(map: LeafletMapView) {
this.map = map 20 20 this.map = map
if (map.execScript("typeof circle$title == 'undefined'") as Boolean) { 21 21 if (map.execScript("typeof circle$title == 'undefined'") as Boolean) {
map.execScript("var circle$title;") 22 22 map.execScript("var circle$title;")
} 23 23 }
if (!this.isAttached) { 24 24 if (!this.isAttached) {
val hexColor = "%02x".format((color.red * 255).toInt()) + "%02x".format((color.green * 255).toInt()) + "%02x".format((color.blue * 255).toInt()) 25 25 val hexColor = "%02x".format((color.red * 255).toInt()) + "%02x".format((color.green * 255).toInt()) + "%02x".format((color.blue * 255).toInt())
map.execScript("circle$title = L.circle([${center.latitude}, ${center.longitude}], $radius, {color:'#$hexColor'}).addTo(myMap);") 26 26 map.execScript("circle$title = L.circle([${center.latitude}, ${center.longitude}], $radius, {color:'#$hexColor'}).addTo(myMap);")
this.isAttached = true 27 27 this.isAttached = true
this.isDisplayed = true 28 28 this.isDisplayed = true
} else if (!this.isDisplayed) { 29 29 } else if (!this.isDisplayed) {
map.execScript("circle$title.addTo(myMap)") 30 30 map.execScript("circle$title.addTo(myMap)")
this.isDisplayed = true 31 31 this.isDisplayed = true
} 32 32 }
} 33 33 }
34 34
fun modifyCircle(latLong: LatLong, radius: Double) { 35 35 fun modifyCircle(latLong: LatLong, radius: Double) {
this.center = latLong 36 36 this.center = latLong
this.radius = radius 37 37 this.radius = radius
this.radius = nauticalMilesToMeter(radius) 38 38 this.radius = nauticalMilesToMeter(radius)
} 39 39 }
40 40
fun uppdateMap() { 41 41 fun uppdateMap() {
if (this.isAttached && !this.isDisplayed) { 42 42 if (this.isAttached && !this.isDisplayed) {
map.execScript("myMap.removeLayer(circle$title);" + 43 43 map.execScript("myMap.removeLayer(circle$title);" +
"circle$title = L.circle([${center.latitude}, ${center.longitude}], $radius).addTo(myMap);") 44 44 "circle$title = L.circle([${center.latitude}, ${center.longitude}], $radius).addTo(myMap);")
this.isDisplayed = true 45 45 this.isDisplayed = true
} 46 46 }
} 47 47 }
48 48
internal fun removeCircle(map: LeafletMapView) { 49 49 internal fun removeCircle(map: LeafletMapView) {
if (this.isAttached && this.isDisplayed) { 50 50 if (this.isAttached && this.isDisplayed) {
map.execScript("myMap.removeLayer(circle$title);") 51 51 map.execScript("myMap.removeLayer(circle$title);")
this.isDisplayed = false 52 52 this.isDisplayed = false
src/main/kotlin/map/CircleMarkerGenerator.kt View file @ 53f01ec
File was created 1 package map
2
3 import application.model.observableMessages
4
5 fun clearMap(map: LeafletMapView) {
6 map.clearAllLayer()
7 }
8
9 fun displayMessageOnMap(map: LeafletMapView) {
10 observableMessages.vessels.forEach { (_, value) ->
11 value.messages.forEach { (_, message) ->
12 map.execScript("L.circleMarker([${message.latitude}, ${message.longitude}], {renderer: myRenderer, radius: 0.01}).addTo(myMap)")
src/main/kotlin/map/ColorMarker.kt View file @ 53f01ec
package fdit.leafletmap 1 1 package map
2 2
/** 3 3 /**
* Enumeration for all marker colors of the leaflet-color-markers JavaScript library. 4 4 * Enumeration for all marker colors of the leaflet-color-markers JavaScript library.
* 5 5 *
* @author Stefan Saring 6 6 * @author Stefan Saring
*/ 7 7 */
enum class ColorMarker(val iconName: String) { 8 8 enum class ColorMarker(val iconName: String) {
9 9
BLUE_MARKER("blueIcon"), 10 10 BLUE_MARKER("blueIcon"),
RED_MARKER("redIcon"), 11 11 RED_MARKER("redIcon"),
GREEN_MARKER("greenIcon"), 12 12 GREEN_MARKER("greenIcon"),
ORANGE_MARKER("orangeIcon"), 13 13 ORANGE_MARKER("orangeIcon"),
YELLOW_MARKER("yellowIcon"), 14 14 YELLOW_MARKER("yellowIcon"),
VIOLET_MARKER("violetIcon"), 15 15 VIOLET_MARKER("violetIcon"),
GREY_MARKER("greyIcon"), 16 16 GREY_MARKER("greyIcon"),
BLACK_MARKER("blackIcon") 17 17 BLACK_MARKER("blackIcon")
src/main/kotlin/map/ControlPosition.kt View file @ 53f01ec
package fdit.leafletmap 1 1 package map
2 2
/** 3 3 /**
* Enumeration for all possible map control positions. 4 4 * Enumeration for all possible map control positions.
* 5 5 *
* @author Stefan Saring 6 6 * @author Stefan Saring
*/ 7 7 */
enum class ControlPosition(val positionName: String) { 8 8 enum class ControlPosition(val positionName: String) {
9 9
TOP_LEFT("topleft"), 10 10 TOP_LEFT("topleft"),
TOP_RIGHT("topright"), 11 11 TOP_RIGHT("topright"),
BOTTOM_LEFT("bottomleft"), 12 12 BOTTOM_LEFT("bottomleft"),
BOTTOM_RIGHT("bottomright") 13 13 BOTTOM_RIGHT("bottomright")
src/main/kotlin/map/LatLong.kt View file @ 53f01ec
package fdit.leafletmap 1 1 package map
2 2
/** 3 3 /**
* Immutable value class for defining a geo position. 4 4 * Immutable value class for defining a geo position.
* 5 5 *
* @author Stefan Saring 6 6 * @author Stefan Saring
*/ 7 7 */
data class LatLong(val latitude: Double, val longitude: Double) 8 8 data class LatLong(val latitude: Double, val longitude: Double)
src/main/kotlin/map/LeafletMapView.kt View file @ 53f01ec
package fdit.leafletmap 1 1 package map
2 2
import fdit.leafletmap.events.* 3
import fdit.leafletmap.events.MapClickEventMaker 4
import fdit.leafletmap.events.MapMoveEventMaker 5
import fdit.leafletmap.events.MarkerClickEventMaker 6
import javafx.concurrent.Worker 7 3 import javafx.concurrent.Worker
import javafx.scene.layout.StackPane 8 4 import javafx.scene.layout.StackPane
import javafx.scene.paint.Color 9 5 import javafx.scene.paint.Color
import javafx.scene.shape.Polygon 10 6 import javafx.scene.shape.Polygon
import javafx.scene.web.WebEngine 11 7 import javafx.scene.web.WebEngine
import javafx.scene.web.WebView 12 8 import javafx.scene.web.WebView
import fdit.leafletmap.events.* 13 9 import map.events.*
import netscape.javascript.JSObject 14 10 import netscape.javascript.JSObject
import java.io.ByteArrayOutputStream 15 11 import java.io.ByteArrayOutputStream
import java.io.File 16 12 import java.io.File
import java.io.IOException 17 13 import java.io.IOException
import java.net.URL 18 14 import java.net.URL
import java.util.* 19 15 import java.util.*
import java.util.concurrent.CompletableFuture 20 16 import java.util.concurrent.CompletableFuture
import javax.imageio.ImageIO 21 17 import javax.imageio.ImageIO
22 18
23 19
/** 24 20 /**
* JavaFX component for displaying OpenStreetMap based maps by using the Leaflet.js JavaScript library inside a WebView 25 21 * JavaFX component for displaying OpenStreetMap based maps by using the Leaflet.js JavaScript library inside a WebView
* browser component.<br/> 26 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 27 23 * This component can be embedded most easily by placing it inside a StackPane, the component uses then the size of the
* parent automatically. 28 24 * parent automatically.
* 29 25 *
* @author Stefan Saring 30 26 * @author Stefan Saring
* @author Niklas Kellner 31 27 * @author Niklas Kellner
*/ 32 28 */
class LeafletMapView : StackPane() { 33 29 class LeafletMapView : StackPane() {
34 30
private val webView = WebView() 35 31 private val webView = WebView()
private val webEngine: WebEngine = webView.engine 36 32 private val webEngine: WebEngine = webView.engine
37 33
private var varNameSuffix: Int = 1 38 34 private var varNameSuffix: Int = 1
private val mapClickEvent = MapClickEventMaker() 39 35 private val mapClickEvent = MapClickEventMaker()
private val markerClickEvent = MarkerClickEventMaker() 40 36 private val markerClickEvent = MarkerClickEventMaker()
private val mapMoveEvent = MapMoveEventMaker() 41 37 private val mapMoveEvent = MapMoveEventMaker()
internal val zoomLimitSmallMarker = 8 42 38 internal val zoomLimitSmallMarker = 8
43 39
/** 44 40 /**
* Creates the LeafletMapView component, it does not show any map yet. 45 41 * Creates the LeafletMapView component, it does not show any map yet.
*/ 46 42 */
init { 47 43 init {
this.children.add(webView) 48 44 this.children.add(webView)
} 49 45 }
50 46
/** 51 47 /**
* Displays the initial map in the web view. Needs to be called and complete before adding any markers or tracks. 52 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 53 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). 54 50 * completed with state SUCCEEDED (use CompletableFuture#whenComplete() for waiting to complete).
* 55 51 *
* @param mapConfig configuration of the map layers and controls 56 52 * @param mapConfig configuration of the map layers and controls
* @return the CompletableFuture which will provide the final map load state 57 53 * @return the CompletableFuture which will provide the final map load state
*/ 58 54 */
fun displayMap(mapConfig: MapConfig): CompletableFuture<Worker.State> { 59 55 fun displayMap(mapConfig: MapConfig): CompletableFuture<Worker.State> {
val finalMapLoadState = CompletableFuture<Worker.State>() 60 56 val finalMapLoadState = CompletableFuture<Worker.State>()
61 57
webEngine.loadWorker.stateProperty().addListener { _, _, newValue -> 62 58 webEngine.loadWorker.stateProperty().addListener { _, _, newValue ->
63 59
if (newValue == Worker.State.SUCCEEDED) { 64 60 if (newValue == Worker.State.SUCCEEDED) {
executeMapSetupScripts(mapConfig) 65 61 executeMapSetupScripts(mapConfig)
} 66 62 }
67 63
if (newValue == Worker.State.SUCCEEDED || newValue == Worker.State.FAILED) { 68 64 if (newValue == Worker.State.SUCCEEDED || newValue == Worker.State.FAILED) {
finalMapLoadState.complete(newValue) 69 65 finalMapLoadState.complete(newValue)
} 70 66 }
} 71 67 }
72 68
val localFileUrl: URL = LeafletMapView::class.java.getResource("/leafletmap/leafletmap.html") 73 69 val localFileUrl: URL = LeafletMapView::class.java.getResource("/leafletmap/leafletmap.html")
webEngine.load(localFileUrl.toExternalForm()) 74 70 webEngine.load(localFileUrl.toExternalForm())
return finalMapLoadState 75 71 return finalMapLoadState
} 76 72 }
77 73
private fun executeMapSetupScripts(mapConfig: MapConfig) { 78 74 private fun executeMapSetupScripts(mapConfig: MapConfig) {
79 75
// execute scripts for layer definition 80 76 // execute scripts for layer definition
mapConfig.layers.forEachIndexed { i, layer -> 81 77 mapConfig.layers.forEachIndexed { i, layer ->
execScript("var layer${i + 1} = ${layer.javaScriptCode};") 82 78 execScript("var layer${i + 1} = ${layer.javaScriptCode};")
} 83 79 }
84 80
val jsLayers = mapConfig.layers 85 81 val jsLayers = mapConfig.layers
.mapIndexed { i, layer -> "'${layer.displayName}': layer${i + 1}" } 86 82 .mapIndexed { i, layer -> "'${layer.displayName}': layer${i + 1}" }
.joinToString(", ") 87 83 .joinToString(", ")
execScript("var baseMaps = { $jsLayers };") 88 84 execScript("var baseMaps = { $jsLayers };")
89 85
// execute script for map view creation (Leaflet attribution must not be a clickable link) 90 86 // execute script for map view creation (Leaflet attribution must not be a clickable link)
execScript(""" 91 87 execScript(
88 """
|var myMap = L.map('map', { 92 89 |var myMap = L.map('map', {
| center: new L.LatLng(${mapConfig.initialCenter.latitude}, ${mapConfig.initialCenter.longitude}), 93 90 | center: new L.LatLng(${mapConfig.initialCenter.latitude}, ${mapConfig.initialCenter.longitude}),
| zoom: 5, 94 91 | zoom: 5,
| zoomControl: false, 95 92 | zoomControl: false,
| layers: [layer1] 96 93 | layers: [layer1]
|}); 97 94 |});
| 98 95 |L.control.scale().addTo(mymap);
|var markersGroup = L.featureGroup(); 99 96 |var myRenderer = L.canvas({ padding: 0.5 });""".trimMargin()
|myMap.addLayer(markersGroup); 100 97 )
|var trackGroup = L.featureGroup(); 101
|myMap.addLayer(trackGroup); 102
| 103
|myMap.addEventListener("contextmenu", function(e){}); 104
|var attribution = myMap.attributionControl; 105
|attribution.setPrefix('Leaflet');""".trimMargin()) 106
107 98
eventZoomChangeIcon() 108 99 // eventZoomChangeIcon()
109 100
// execute script for layer control definition if there are multiple layers 110 101 // execute script for layer control definition if there are multiple layers
if (mapConfig.layers.size > 1) { 111 102 if (mapConfig.layers.size > 1) {
execScript(""" 112 103 execScript(
104 """
|var overlayMaps = {}; 113 105 |var overlayMaps = {};
|L.control.layers(baseMaps, overlayMaps).addTo(myMap);""".trimMargin()) 114 106 |L.control.layers(baseMaps, overlayMaps).addTo(myMap);""".trimMargin()
107 )
115 108
} 116 109 }
117 110
// execute script for scale control definition 118 111 // execute script for scale control definition
if (mapConfig.scaleControlConfig.show) { 119 112 if (mapConfig.scaleControlConfig.show) {
execScript("L.control.scale({position: '${mapConfig.scaleControlConfig.position.positionName}', " + 120 113 execScript(
"metric: ${mapConfig.scaleControlConfig.metric}, " + 121 114 "L.control.scale({position: '${mapConfig.scaleControlConfig.position.positionName}', " +
"imperial: ${!mapConfig.scaleControlConfig.metric}})" + 122 115 "metric: ${mapConfig.scaleControlConfig.metric}, " +
".addTo(myMap);") 123 116 "imperial: ${!mapConfig.scaleControlConfig.metric}})" +
117 ".addTo(myMap);"
118 )
} 124 119 }
125 120
// execute script for zoom control definition 126 121 // execute script for zoom control definition
if (mapConfig.zoomControlConfig.show) { 127 122 if (mapConfig.zoomControlConfig.show) {
execScript("L.control.zoom({position: '${mapConfig.zoomControlConfig.position.positionName}'})" + 128 123 execScript(
".addTo(myMap);") 129 124 "L.control.zoom({position: '${mapConfig.zoomControlConfig.position.positionName}'})" +
125 ".addTo(myMap);"
126 )
} 130 127 }
} 131 128 }
132 129
/** 133 130 /**
* Sets the view of the map to the specified geographical center position and zoom level. 134 131 * Sets the view of the map to the specified geographical center position and zoom level.
* 135 132 *
* @param position map center position 136 133 * @param position map center position
* @param zoomLevel zoom level (0 - 19 for OpenStreetMap) 137 134 * @param zoomLevel zoom level (0 - 19 for OpenStreetMap)
*/ 138 135 */
fun setView(position: LatLong, zoomLevel: Int) = 139 136 fun setView(position: LatLong, zoomLevel: Int) =
execScript("myMap.setView([${position.latitude}, ${position.longitude}], $zoomLevel);") 140 137 execScript("myMap.setView([${position.latitude}, ${position.longitude}], $zoomLevel);")
141 138
/** 142 139 /**
* Pans the map to the specified geographical center position. 143 140 * Pans the map to the specified geographical center position.
* 144 141 *
* @param position map center position 145 142 * @param position map center position
*/ 146 143 */
fun panTo(position: LatLong) = 147 144 fun panTo(position: LatLong) =
execScript("myMap.panTo([${position.latitude}, ${position.longitude}]);") 148 145 execScript("myMap.panTo([${position.latitude}, ${position.longitude}]);")
149 146
/** 150 147 /**
* Sets the zoom of the map to the specified level. 151 148 * Sets the zoom of the map to the specified level.
* 152 149 *
* @param zoomLevel zoom level (0 - 19 for OpenStreetMap) 153 150 * @param zoomLevel zoom level (0 - 19 for OpenStreetMap)
*/ 154 151 */
fun setZoom(zoomLevel: Int) = 155 152 fun setZoom(zoomLevel: Int) =
execScript("myMap.setZoom([$zoomLevel]);") 156 153 execScript("myMap.setZoom([$zoomLevel]);")
157 154
/** 158 155 /**
* Adds a Marker Object to a map 159 156 * Adds a Marker Object to a map
* 160 157 *
* @param marker the Marker Object 161 158 * @param marker the Marker Object
*/ 162 159 */
fun addMarker(marker: Marker) { 163 160 fun addMarker(marker: Marker) {
marker.addToMap(getNextMarkerName(), this) 164 161 marker.addToMap(getNextMarkerName(), this)
} 165 162 }
166 163
fun addCircle(circle: Circle) { 167 164 fun addCircle(circle: Circle) {
circle.addToMap(this) 168 165 circle.addToMap(this)
} 169 166 }
170 167
fun addZone(zone: Zone) { 171 168 fun addZone(zone: Zone) {
zone.addToMap(this) 172 169 zone.addToMap(this)
} 173 170 }
174 171
/** 175 172 /**
* Removes an existing marker from the map 176 173 * Removes an existing marker from the map
* 177 174 *
* @param marker the Marker object 178 175 * @param marker the Marker object
*/ 179 176 */
fun removeMarker(marker: Marker) { 180 177 fun removeMarker(marker: Marker) {
execScript("myMap.removeLayer(${marker.getName()});") 181 178 execScript("myMap.removeLayer(${marker.getName()});")
} 182 179 }
183 180
fun removeCircle(circle: Circle) { 184 181 fun removeCircle(circle: Circle) {
circle.removeCircle(this) 185 182 circle.removeCircle(this)
} 186 183 }
187 184
fun removeZone(zone: Zone) { 188 185 fun removeZone(zone: Zone) {
zone.removeZone() 189 186 zone.removeZone()
} 190 187 }
191 188
fun removeZone(id: String) { 192 189 fun removeZone(id: String) {
val idSanitized = id.replace("-", "") 193 190 val idSanitized = id.replace("-", "")
execScript("myMap.removeLayer(polygon$idSanitized);") 194 191 execScript("myMap.removeLayer(polygon$idSanitized);")
} 195 192 }
196 193
197 194
fun uppdateCircle(circle: Circle, latLong: LatLong, radius: Double) { 198 195 fun uppdateCircle(circle: Circle, latLong: LatLong, radius: Double) {
circle.modifyCircle(latLong, radius) 199 196 circle.modifyCircle(latLong, radius)
circle.uppdateMap() 200 197 circle.uppdateMap()
} 201 198 }
202 199
fun setEventMousePosition() { 203 200 fun setEventMousePosition() {
execScript("var lat=0.0, lng=0.0;\n" + 204 201 execScript(
"myMap.addEventListener('mousemove', function(ev) {\n" + 205 202 "var lat=0.0, lng=0.0;\n" +
" lat = ev.latlng.lat;\n" + 206 203 "myMap.addEventListener('mousemove', function(ev) {\n" +
" lng = ev.latlng.lng;\n" + 207 204 " lat = ev.latlng.lat;\n" +
"});" 208 205 " lng = ev.latlng.lng;\n" +
206 "});"
) 209 207 )
} 210 208 }
211 209
fun getMousePosition(): LatLong { 212 210 fun getMousePosition(): LatLong {
val lat = execScript("lat;") as Double 213 211 val lat = execScript("lat;") as Double
val lng = execScript("lng;") as Double 214 212 val lng = execScript("lng;") as Double
return LatLong(lat, lng) 215 213 return LatLong(lat, lng)
} 216 214 }
217 215
/** 218 216 /**
* Adds a custom marker type 219 217 * Adds a custom marker type
* 220 218 *
* @param markerName the name of the marker type 221 219 * @param markerName the name of the marker type
* @param iconUrl the url if the marker icon 222 220 * @param iconUrl the url if the marker icon
*/ 223 221 */
fun addCustomMarker(markerName: String, iconUrl: String): String { 224 222 fun addCustomMarker(markerName: String, iconUrl: String): String {
execScript("var $markerName = L.icon({\n" + 225 223 execScript(
"iconUrl: '${createImage(iconUrl, "png")}',\n" + 226 224 "var $markerName = L.icon({\n" +
"iconSize: [24, 24],\n" + 227 225 "iconUrl: '${createImage(iconUrl, "png")}',\n" +
"iconAnchor: [12, 12],\n" + 228 226 "iconSize: [24, 24],\n" +
"});") 229 227 "iconAnchor: [12, 12],\n" +
228 "});"
229 )
return markerName 230 230 return markerName
} 231 231 }
232 232
private fun createImage(path: String, type: String): String { 233 233 private fun createImage(path: String, type: String): String {
val image = ImageIO.read(File(path)) 234 234 val image = ImageIO.read(File(path))
var imageString: String? = null 235 235 var imageString: String? = null
val bos = ByteArrayOutputStream() 236 236 val bos = ByteArrayOutputStream()
237 237
try { 238 238 try {
ImageIO.write(image, type, bos) 239 239 ImageIO.write(image, type, bos)
val imageBytes = bos.toByteArray() 240 240 val imageBytes = bos.toByteArray()
241 241
val encoder = Base64.getEncoder() 242 242 val encoder = Base64.getEncoder()
imageString = encoder.encodeToString(imageBytes) 243 243 imageString = encoder.encodeToString(imageBytes)
244 244
bos.close() 245 245 bos.close()
} catch (e: IOException) { 246 246 } catch (e: IOException) {
e.printStackTrace() 247 247 e.printStackTrace()
} 248 248 }
return "data:image/$type;base64,$imageString" 249 249 return "data:image/$type;base64,$imageString"
} 250 250 }
251 251
/** 252 252 /**
* Sets the onMarkerClickListener 253 253 * Sets the onMarkerClickListener
* 254 254 *
* @param listener the onMarerClickEventListener 255 255 * @param listener the onMarerClickEventListener
*/ 256 256 */
fun onMarkerClick(listener: MarkerClickEventListener) { 257 257 fun onMarkerClick(listener: MarkerClickEventListener) {
val win = execScript("document") as JSObject 258 258 val win = execScript("document") as JSObject
win.setMember("java", this) 259 259 win.setMember("java", this)
markerClickEvent.addListener(listener) 260 260 markerClickEvent.addListener(listener)
} 261 261 }
262 262
/** 263 263 /**
* Handles the callback from the markerClickEvent 264 264 * Handles the callback from the markerClickEvent
*/ 265 265 */
fun markerClick(title: String) { 266 266 fun markerClick(title: String) {
markerClickEvent.MarkerClickEvent(title) 267 267 markerClickEvent.MarkerClickEvent(title)
} 268 268 }
269 269
/** 270 270 /**
* Sets the onMapMoveListener 271 271 * Sets the onMapMoveListener
* 272 272 *
* @param listener the MapMoveEventListener 273 273 * @param listener the MapMoveEventListener
*/ 274 274 */
fun onMapMove(listener: MapMoveEventListener) { 275 275 fun onMapMove(listener: MapMoveEventListener) {
val win = execScript("document") as JSObject 276 276 val win = execScript("document") as JSObject
win.setMember("java", this) 277 277 win.setMember("java", this)
execScript("myMap.on('moveend', function(e){ document.java.mapMove(myMap.getCenter().lat, myMap.getCenter().lng);});") 278 278 execScript("myMap.on('moveend', function(e){ document.java.mapMove(myMap.getCenter().lat, myMap.getCenter().lng);});")
mapMoveEvent.addListener(listener) 279 279 mapMoveEvent.addListener(listener)
} 280 280 }
281 281
/** 282 282 /**
* Handles the callback from the mapMoveEvent 283 283 * Handles the callback from the mapMoveEvent
*/ 284 284 */
fun mapMove(lat: Double, lng: Double) { 285 285 fun mapMove(lat: Double, lng: Double) {
val latlng = LatLong(lat, lng) 286 286 val latlng = LatLong(lat, lng)
mapMoveEvent.MapMoveEvent(latlng) 287 287 mapMoveEvent.MapMoveEvent(latlng)
} 288 288 }
289 289
/** 290 290 /**
* Sets the onMapClickListener 291 291 * Sets the onMapClickListener
* 292 292 *
* @param listener the onMapClickEventListener 293 293 * @param listener the onMapClickEventListener
*/ 294 294 */
fun onMapClick(listener: MapClickEventListener) { 295 295 fun onMapClick(listener: MapClickEventListener) {
val win = execScript("document") as JSObject 296 296 val win = execScript("document") as JSObject
win.setMember("java", this) 297 297 win.setMember("java", this)
execScript("myMap.on('click', function(e){ document.java.mapClick(e.latlng.lat, e.latlng.lng);});") 298 298 execScript("myMap.on('click', function(e){ document.java.mapClick(e.latlng.lat, e.latlng.lng);});")
mapClickEvent.addListener(listener) 299 299 mapClickEvent.addListener(listener)
} 300 300 }
301 301
/** 302 302 /**
* Handles the callback from the mapClickEvent 303 303 * Handles the callback from the mapClickEvent
*/ 304 304 */
fun mapClick(lat: Double, lng: Double) { 305 305 fun mapClick(lat: Double, lng: Double) {
val latlng = LatLong(lat, lng) 306 306 val latlng = LatLong(lat, lng)
mapClickEvent.MapClickEvent(latlng) 307 307 mapClickEvent.MapClickEvent(latlng)
} 308 308 }
309 309
/** 310 310 /**
* Draws a track path along the specified positions. 311 311 * Draws a track path along the specified positions.
* 312 312 *
* @param positions list of track positions 313 313 * @param positions list of track positions
*/ 314 314 */
fun addTrack(positions: List<LatLong>) { 315 315 fun addTrack(positions: List<LatLong>) {
316 316
val jsPositions = positions 317 317 val jsPositions = positions
.map { " [${it.latitude}, ${it.longitude}]" } 318 318 .map { " [${it.latitude}, ${it.longitude}]" }
.joinToString(", \n") 319 319 .joinToString(", \n")
320 320
execScript(""" 321 321 execScript(
322 """
|var latLngs = [ 322 323 |var latLngs = [
|$jsPositions 323 324 |$jsPositions
|]; 324 325 |];
|var polyline = L.polyline(latLngs, {color: 'red', weight: 2}).addTo(myMap);""".trimMargin()) 325 326 |var polyline = L.polyline(latLngs, {color: 'red', weight: 2}).addTo(myMap);""".trimMargin()
327 )
} 326 328 }
327 329
330 fun clearAllLayer() {
331 execScript("""
332 myMap.eachLayer(function (layer) {
333 map.removeLayer(layer);
334 });
335 """.trimIndent())
336 }
337
fun addTrack(positions: List<LatLong>, id: String, color: Color, tooltip: String) { 328 338 fun addTrack(positions: List<LatLong>, id: String, color: Color, tooltip: String) {
329 339
val jsPositions = positions 330 340 val jsPositions = positions
.map { " [${it.latitude}, ${it.longitude}]" } 331 341 .map { " [${it.latitude}, ${it.longitude}]" }
.joinToString(", \n") 332 342 .joinToString(", \n")
333 343
val cleanTooltip = tooltip.replace("'", "&apos;") 334 344 val cleanTooltip = tooltip.replace("'", "&apos;")
execScript(""" 335 345 execScript(
346 """
|var latLngs = [ 336 347 |var latLngs = [
|$jsPositions 337 348 |$jsPositions
|]; 338 349 |];
|var color = "rgb(${Math.floor(color.getRed() * 255).toInt()} ,${Math.floor(color.getGreen() * 255).toInt()},${Math.floor(color.getBlue() * 255).toInt()})"; 339 350 |var color = "rgb(${Math.floor(color.getRed() * 255).toInt()} ,${Math.floor(color.getGreen() * 255)
|var polyline$id = L.polyline(latLngs, {color: color, weight: 2, zIndexOffset: 200}).bindTooltip('$cleanTooltip', {sticky: true}).addTo(trackGroup)""".trimMargin()) 340 351 .toInt()},${Math.floor(color.getBlue() * 255).toInt()})";
352 |var polyline$id = L.polyline(latLngs, {color: color, weight: 2, zIndexOffset: 200}).bindTooltip('$cleanTooltip', {sticky: true}).addTo(trackGroup)""".trimMargin()
353 )
} 341 354 }
342 355
fun makeVesselTrackTransparent(id: String) { 343 356 fun makeVesselTrackTransparent(id: String) {
execScript("polyline$id.setStyle({opacity: 0.5});") 344 357 execScript("polyline$id.setStyle({opacity: 0.5});")
} 345 358 }
346 359
fun highlightTrack(id: String) { 347 360 fun highlightTrack(id: String) {
execScript("polyline$id.setStyle({weight: 4});") 348 361 execScript("polyline$id.setStyle({weight: 4});")
} 349 362 }
350 363
fun normalizeVesselTrack(id: String) { 351 364 fun normalizeVesselTrack(id: String) {
execScript("polyline$id.setStyle({opacity: 1,weight: 2});") 352 365 execScript("polyline$id.setStyle({opacity: 1,weight: 2});")
} 353 366 }
354 367
fun eventZoomChangeIcon() { 355 368 fun eventZoomChangeIcon() {
execScript(""" 356 369 execScript(
370 """
|myMap.on('zoomend', function() { 357 371 |myMap.on('zoomend', function() {
|var currentZoom = myMap.getZoom(); 358 372 |var currentZoom = myMap.getZoom();
|if (currentZoom < $zoomLimitSmallMarker) { 359 373 |if (currentZoom < $zoomLimitSmallMarker) {
|markersGroup.eachLayer(function(layer) { 360 374 |markersGroup.eachLayer(function(layer) {
return layer.setIcon(aircraftSmallIcon) 361 375 return layer.setIcon(aircraftSmallIcon)
|}); 362 376 |});
|} else { 363 377 |} else {
|markersGroup.eachLayer(function(layer) { 364 378 |markersGroup.eachLayer(function(layer) {
return layer.setIcon(aircraftIcon) 365 379 return layer.setIcon(aircraftIcon)
|}); 366 380 |});
|} 367 381 |}
|}); 368 382 |});
""".trimMargin()) 369 383 """.trimMargin()
384 )
} 370 385 }
371 386
fun removeTrack(id: String) { 372 387 fun removeTrack(id: String) {
execScript("myMap.removeLayer(polyline$id);") 373 388 execScript("myMap.removeLayer(polyline$id);")
} 374 389 }
375 390
fun fitBoundsMarkers() { 376 391 fun fitBoundsMarkers() {
execScript("setTimeout(() => {myMap.fitBounds(markersGroup.getBounds().pad(0.05));}, 500);") 377 392 execScript("setTimeout(() => {myMap.fitBounds(markersGroup.getBounds().pad(0.05));}, 500);")
} 378 393 }
379 394
fun addZone(polygon: Polygon, id: String, color: Color) { 380 395 fun addZone(polygon: Polygon, id: String, color: Color) {
val points = polygon.points 381 396 val points = polygon.points
val latLongs = arrayListOf<LatLong>() 382 397 val latLongs = arrayListOf<LatLong>()
var lat: Double 383 398 var lat: Double
var lon = 0.0 384 399 var lon = 0.0
385 400
for (i in 0 until points.size) { 386 401 for (i in 0 until points.size) {
if (i % 2 == 0) { 387 402 if (i % 2 == 0) {
lon = points[i] 388 403 lon = points[i]
} else { 389 404 } else {
lat = points[i] 390 405 lat = points[i]
latLongs.add(LatLong(lat, lon)) 391 406 latLongs.add(LatLong(lat, lon))
} 392 407 }
} 393 408 }
394 409
val jsPositions = latLongs 395 410 val jsPositions = latLongs
.map { " [${it.latitude}, ${it.longitude}]" } 396 411 .map { " [${it.latitude}, ${it.longitude}]" }
.joinToString(", \n") 397 412 .joinToString(", \n")
src/main/kotlin/map/MapConfig.kt View file @ 53f01ec
package fdit.leafletmap 1 1 package map
2 2
/** 3 3 /**
* Class for defining the layers and controls in the map to be shown. 4 4 * Class for defining the layers and controls in the map to be shown.
* 5 5 *
* @property layers List of layers to be shown in the map, the default layer is OpenStreetMap. If more than one layer is 6 6 * @property layers List of layers to be shown in the map, the default layer is OpenStreetMap. If more than one layer is
* specified, then a layer selection control will be shown in the top right corner. 7 7 * specified, then a layer selection control will be shown in the top right corner.
* @property zoomControlConfig Zoom control definition, by default it's shown in the top left corner. 8 8 * @property zoomControlConfig Zoom control definition, by default it's shown in the top left corner.
* @property scaleControlConfig Scale control definition, by default it's not shown. 9 9 * @property scaleControlConfig Scale control definition, by default it's not shown.
* @property initialCenter Initial center position of the map (default is London city). 10 10 * @property initialCenter Initial center position of the map (default is London city).
* 11 11 *
* @author Stefan Saring 12 12 * @author Stefan Saring
*/ 13 13 */
class MapConfig @JvmOverloads constructor( 14 14 class MapConfig @JvmOverloads constructor(
15 15
val layers: List<MapLayer> = listOf(MapLayer.OPENSTREETMAP), 16 16 val layers: List<MapLayer> = listOf(MapLayer.OPENSTREETMAP),
val zoomControlConfig: ZoomControlConfig = ZoomControlConfig(), 17 17 val zoomControlConfig: ZoomControlConfig = ZoomControlConfig(),
val scaleControlConfig: ScaleControlConfig = ScaleControlConfig(), 18 18 val scaleControlConfig: ScaleControlConfig = ScaleControlConfig(),
val initialCenter: LatLong = LatLong(0.0, 0.0) 19 19 val initialCenter: LatLong = LatLong(0.0, 0.0)
src/main/kotlin/map/MapLayer.kt View file @ 53f01ec
package fdit.leafletmap 1 1 package map
2 2
/** 3 3 /**
* Enumeration for all supported map layers. 4 4 * Enumeration for all supported map layers.
* 5 5 *
* @author Stefan Saring 6 6 * @author Stefan Saring
*/ 7 7 */
enum class MapLayer(val displayName: String, val javaScriptCode: String) { 8 8 enum class MapLayer(val displayName: String, val javaScriptCode: String) {
9 9
/** OpenStreetMap layer. */ 10 10 /** OpenStreetMap layer. */
OPENSTREETMAP("OpenStreetMap", """ 11 11 OPENSTREETMAP("OpenStreetMap", """
L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { 12 12 L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data &copy; OpenStreetMap and contributors', noWrap: true 13 13 attribution: 'Map data &copy; OpenStreetMap and contributors', noWrap: true
})"""), 14 14 })"""),
15 15
/** OpenCycleMap layer. */ 16 16 /** OpenCycleMap layer. */
OPENCYCLEMAP("OpenCycleMap", """ 17 17 OPENCYCLEMAP("OpenCycleMap", """
L.tileLayer('http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png', { 18 18 L.tileLayer('http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png', {
attribution: '&copy; OpenCycleMap, Map data &copy; OpenStreetMap contributors', noWrap: true 19 19 attribution: '&copy; OpenCycleMap, Map data &copy; OpenStreetMap contributors', noWrap: true
})"""), 20 20 })"""),
21 21
/** Hike & bike maps layer (HikeBikeMap.org). */ 22 22 /** Hike & bike maps layer (HikeBikeMap.org). */
HIKE_BIKE_MAP("Hike & Bike Map", """ 23 23 HIKE_BIKE_MAP("Hike & Bike Map", """
L.tileLayer('http://{s}.tiles.wmflabs.org/hikebike/{z}/{x}/{y}.png', { 24 24 L.tileLayer('http://{s}.tiles.wmflabs.org/hikebike/{z}/{x}/{y}.png', {
attribution: '&copy; HikeBikeMap.org, Map data &copy; OpenStreetMap and contributors', noWrap: true 25 25 attribution: '&copy; HikeBikeMap.org, Map data &copy; OpenStreetMap and contributors', noWrap: true
})"""), 26 26 })"""),
27 27
/** MTB map (mtbmap.cz). */ 28 28 /** MTB map (mtbmap.cz). */
MTB_MAP("MTB Map", """ 29 29 MTB_MAP("MTB Map", """
L.tileLayer('http://tile.mtbmap.cz/mtbmap_tiles/{z}/{x}/{y}.png', { 30 30 L.tileLayer('http://tile.mtbmap.cz/mtbmap_tiles/{z}/{x}/{y}.png', {
attribution: '&copy; OpenStreetMap and USGS', noWrap: true 31 31 attribution: '&copy; OpenStreetMap and USGS', noWrap: true
})"""), 32 32 })"""),
33 33
/** MapBox layer in streets mode (consider: a project specific access token is required!). */ 34 34 /** MapBox layer in streets mode (consider: a project specific access token is required!). */
MAPBOX("MapBox", """ 35 35 MAPBOX("MapBox", """
src/main/kotlin/map/Marker.kt View file @ 53f01ec
package fdit.leafletmap 1 1 package map
2 2
import fdit.gui.graphicalScenarioEditor.GraphicalScenarioEditorContext 3 3 import application.model.Vessel
import fdit.gui.utils.tooltip.VesselTooltipUtils.formatVesselSnapshotTooltip 4
import fdit.metamodel.vessel.Vessel 5
6 4
5
/** 7 6 /**
* Creates a marker at the specified geographical position. 8 7 * Creates a marker at the specified geographical position.
* 9 8 *
* @author Niklas Kellner 10 9 * @author Niklas Kellner
* 11 10 *
* @param position marker position 12 11 * @param position marker position
* @param title marker title shown in tooltip (pass empty string when tooltip not needed) 13 12 * @param title marker title shown in tooltip (pass empty string when tooltip not needed)
* @param zIndexOffset zIndexOffset (higher number means on top) 14 13 * @param zIndexOffset zIndexOffset (higher number means on top)
* 15 14 *
*/ 16 15 */
class Marker private constructor(private var position: LatLong, private var zIndexOffset: Int) { 17 16 class Marker private constructor(private var position: LatLong, private var zIndexOffset: Int) {
private var marker = "aircraftIcon" 18 17 private var marker = "aircraftIcon"
private var markerSmall = "aircraftSmallIcon" 19 18 private var markerSmall = "aircraftSmallIcon"
private lateinit var map: LeafletMapView 20 19 private lateinit var map: LeafletMapView
private var attached = false 21 20 private var attached = false
private var clickable = false 22 21 private var clickable = false
private var name = "" 23 22 private var name = ""
private var tooltip = "" 24 23 private var tooltip = ""
private var rotation = 0 25 24 private var rotation = 0
private lateinit var aircraft: Vessel 26 25 private lateinit var aircraft: Vessel
private lateinit var context: GraphicalScenarioEditorContext 27
private var relativeDate: Double = 0.0 28 26 private var relativeDate: Double = 0.0
29 27
30 28
constructor(position: LatLong, aircraft: Vessel, relativeDate: Double, context: GraphicalScenarioEditorContext, aircraftIcon: String, zIndexOffset: Int) : this(position, zIndexOffset){ 31 29 constructor(
30 position: LatLong,
31 aircraft: Vessel,
32 relativeDate: Double,
33 aircraftIcon: String,
34 zIndexOffset: Int
35 ) : this(position, zIndexOffset) {
this.aircraft = aircraft 32 36 this.aircraft = aircraft
this.context = context 33
this.relativeDate = relativeDate 34 37 this.relativeDate = relativeDate
this.marker = aircraftIcon 35 38 this.marker = aircraftIcon
} 36 39 }
37 40
/** 38 41 /**
* Adds the marker to a map, gets called from the mapAddMarker 39 42 * Adds the marker to a map, gets called from the mapAddMarker
* 40 43 *
* @param nextMarkerName the variable name of the marker 41 44 * @param nextMarkerName the variable name of the marker
* @param map the LeafetMapView 42 45 * @param map the LeafetMapView
*/ 43 46 */
internal fun addToMap(nextMarkerName: String, map: LeafletMapView) { 44 47 internal fun addToMap(nextMarkerName: String, map: LeafletMapView) {
this.name = nextMarkerName 45 48 this.name = nextMarkerName
this.map = map 46 49 this.map = map
this.attached = true 47 50 this.attached = true
map.execScript(""" 48 51 map.execScript(
52 """
|var currentZoom = myMap.getZoom(); 49 53 |var currentZoom = myMap.getZoom();
|var $name; 50 54 |var $name;
|if (currentZoom < ${map.zoomLimitSmallMarker}) { 51 55 |if (currentZoom < ${map.zoomLimitSmallMarker}) {
|$name = L.marker([${position.latitude}, ${position.longitude}], {title: '', icon: $markerSmall, zIndexOffset: $zIndexOffset}).addTo(markersGroup); 52 56 |$name = L.marker([${position.latitude}, ${position.longitude}], {title: '', icon: $markerSmall, zIndexOffset: $zIndexOffset}).addTo(markersGroup);
|} else { 53 57 |} else {
|$name = L.marker([${position.latitude}, ${position.longitude}], {title: '', icon: $marker, zIndexOffset: $zIndexOffset}).addTo(markersGroup); 54 58 |$name = L.marker([${position.latitude}, ${position.longitude}], {title: '', icon: $marker, zIndexOffset: $zIndexOffset}).addTo(markersGroup);
|} 55 59 |}
""".trimMargin()) 56 60 """.trimMargin()
setTooltip() 57 61 )
62 // setTooltip()
if (clickable) { 58 63 if (clickable) {
setClickable() 59 64 setClickable()
} 60 65 }
} 61 66 }
62 67
fun setTooltip() { 63 68 fun setTooltip() {
this.tooltip = formatVesselSnapshotTooltip(aircraft, 64 69 this.tooltip = "TODO"
context.getGraphicalScenario().getRecording(), 65
relativeDate) 66
this.tooltip = tooltip.replace("\n", "<br>") 67 70 this.tooltip = tooltip.replace("\n", "<br>")
this.tooltip = tooltip.replace("'", "&apos;") 68 71 this.tooltip = tooltip.replace("'", "&apos;")
map.execScript("$name.bindTooltip('<div id=\"html_c92f9552ec164f36978869550cb44ffe\" style=\"width: 100.0%; height: 100.0%;\">${this.tooltip}</div>');") 69 72 map.execScript("$name.bindTooltip('<div id=\"html_c92f9552ec164f36978869550cb44ffe\" style=\"width: 100.0%; height: 100.0%;\">${this.tooltip}</div>');")
} 70 73 }
71 74
72 75
/** 73 76 /**
* Changes the icon of the marker 74 77 * Changes the icon of the marker
* 75 78 *
* @param newIcon the name of the new icon 76 79 * @param newIcon the name of the new icon
*/ 77 80 */
fun changeIcon(newIcon: String) { 78 81 fun changeIcon(newIcon: String) {
this.marker = newIcon 79 82 this.marker = newIcon
if (attached) { 80 83 if (attached) {
map.execScript("$name.setIcon($marker);") 81 84 map.execScript("$name.setIcon($marker);")
} 82 85 }
} 83 86 }
84 87
/** 85 88 /**
* Changes the icon of the marker 86 89 * Changes the icon of the marker
* 87 90 *
* @param newIcon the new ColorMarker 88 91 * @param newIcon the new ColorMarker
*/ 89 92 */
fun changeIcon(newIcon: ColorMarker) { 90 93 fun changeIcon(newIcon: ColorMarker) {
this.marker = newIcon.iconName 91 94 this.marker = newIcon.iconName
if (attached) { 92 95 if (attached) {
map.execScript("$name.setIcon(${newIcon.iconName});") 93 96 map.execScript("$name.setIcon(${newIcon.iconName});")
} 94 97 }
} 95 98 }
96 99
/** 97 100 /**
* Moves the existing marker specified by the variable name to the new geographical position. 98 101 * Moves the existing marker specified by the variable name to the new geographical position.
* 99 102 *
* @param position new marker position 100 103 * @param position new marker position
*/ 101 104 */
fun move(position: LatLong) { 102 105 fun move(position: LatLong) {
this.position = position 103 106 this.position = position
if (attached) { 104 107 if (attached) {
map.execScript("$name.setLatLng([${this.position.latitude}, ${this.position.longitude}]);") 105 108 map.execScript("$name.setLatLng([${this.position.latitude}, ${this.position.longitude}]);")
setTooltip() 106 109 setTooltip()
} 107 110 }
} 108 111 }
109 112
fun move(position: LatLong, aircraft: Vessel, relativeDate: Double) { 110 113 fun move(position: LatLong, aircraft: Vessel, relativeDate: Double) {
this.aircraft = aircraft 111 114 this.aircraft = aircraft
this.relativeDate = relativeDate 112 115 this.relativeDate = relativeDate
this.position = position 113 116 this.position = position
if (attached) { 114 117 if (attached) {
map.execScript("$name.setLatLng([${this.position.latitude}, ${this.position.longitude}]);") 115 118 map.execScript("$name.setLatLng([${this.position.latitude}, ${this.position.longitude}]);")
} 116 119 }
} 117 120 }
118 121
fun setRotation(rotation: Int) { 119 122 fun setRotation(rotation: Int) {
if (rotation > 360 || rotation < 0) { 120 123 if (rotation > 360 || rotation < 0) {
this.rotation = 0 121 124 this.rotation = 0
} else { 122 125 } else {
this.rotation = rotation 123 126 this.rotation = rotation
} 124 127 }
if (attached) { 125 128 if (attached) {
map.execScript("$name.setRotationAngle(${this.rotation})") 126 129 map.execScript("$name.setRotationAngle(${this.rotation})")
} 127 130 }
} 128 131 }
129 132
130 133
/** 131 134 /**
* Sets the marker clickable 132 135 * Sets the marker clickable
*/ 133 136 */
private fun setClickable() { 134 137 private fun setClickable() {
src/main/kotlin/map/ScaleControlConfig.kt View file @ 53f01ec
package fdit.leafletmap 1 1 package map
2 2
/** 3 3 /**
* Class for defining the scale control of the map. The scale can show either metric or imperial units. 4 4 * Class for defining the scale control of the map. The scale can show either metric or imperial units.
5 5
* @author Stefan Saring 6 6 * @author Stefan Saring
*/ 7 7 */
class ScaleControlConfig @JvmOverloads constructor( 8 8 class ScaleControlConfig @JvmOverloads constructor(
val show: Boolean = false, 9 9 val show: Boolean = false,
val position: ControlPosition = ControlPosition.BOTTOM_LEFT, 10 10 val position: ControlPosition = ControlPosition.BOTTOM_LEFT,
val metric: Boolean = true) 11 11 val metric: Boolean = true
12 )
src/main/kotlin/map/Zone.kt View file @ 53f01ec
package fdit.leafletmap 1 1 package map
2
3 import map.LatLong
4 import map.LeafletMapView
2 5
class Zone constructor(private var title: String) { 3 6 class Zone constructor(private var title: String) {
private lateinit var map: LeafletMapView 4 7 private lateinit var map: LeafletMapView
private var isAttached = false 5 8 private var isAttached = false
private var isDisplayed = false 6 9 private var isDisplayed = false
private var positions = listOf<LatLong>() 7 10 private var positions = listOf<LatLong>()
8 11
9 12
fun addToMap(map: LeafletMapView) { 10 13 fun addToMap(map: LeafletMapView) {
this.map = map 11 14 this.map = map
12 15
if (map.execScript("typeof zone$title == 'undefined';") as Boolean) { 13 16 if (map.execScript("typeof zone$title == 'undefined';") as Boolean) {
map.execScript("var zone$title") 14 17 map.execScript("var zone$title")
} 15 18 }
if (!this.isAttached) { 16 19 if (!this.isAttached) {
17 20
map.execScript("var points$title = [];" + 18 21 map.execScript("var points$title = [];" +
"zone$title = L.polygon(points$title).addTo(myMap);") 19 22 "zone$title = L.polygon(points$title).addTo(myMap);")
this.isAttached = true 20 23 this.isAttached = true
this.isDisplayed = true 21 24 this.isDisplayed = true
} else if (!this.isDisplayed) { 22 25 } else if (!this.isDisplayed) {
map.execScript("zone$title.addTo(myMap);") 23 26 map.execScript("zone$title.addTo(myMap);")
this.isDisplayed = true 24 27 this.isDisplayed = true
} 25 28 }
} 26 29 }
27 30
private fun addPoint(latLong: LatLong) { 28 31 private fun addPoint(latLong: LatLong) {
map.execScript("points$title.push([${latLong.latitude}, ${latLong.longitude}]);") 29 32 map.execScript("points$title.push([${latLong.latitude}, ${latLong.longitude}]);")
} 30 33 }
31 34
fun updatePoints(positions: List<LatLong>) { 32 35 fun updatePoints(positions: List<LatLong>) {
this.positions = positions 33 36 this.positions = positions
if (map.execScript("typeof points$title == 'undefined'") as Boolean) { 34 37 if (map.execScript("typeof points$title == 'undefined'") as Boolean) {
map.execScript("var points$title = [];") 35 38 map.execScript("var points$title = [];")
} else { 36 39 } else {
map.execScript("points$title = [];") 37 40 map.execScript("points$title = [];")
} 38 41 }
for (position in positions) { 39 42 for (position in positions) {
addPoint(position) 40 43 addPoint(position)
} 41 44 }
} 42 45 }
43 46
fun updateMap() { 44 47 fun updateMap() {
if (this.isAttached) { 45 48 if (this.isAttached) {
map.execScript("myMap.removeLayer(zone$title);" + 46 49 map.execScript("myMap.removeLayer(zone$title);" +
"zone$title = L.polygon(points$title).addTo(myMap);") 47 50 "zone$title = L.polygon(points$title).addTo(myMap);")
this.isDisplayed = true 48 51 this.isDisplayed = true
} 49 52 }
} 50 53 }
src/main/kotlin/map/ZoomControlConfig.kt View file @ 53f01ec
package fdit.leafletmap 1 1 package map
2 2
/** 3 3 /**
* Class for defining the zoom control of the map. 4 4 * Class for defining the zoom control of the map.
5 5
* @author Stefan Saring 6 6 * @author Stefan Saring
*/ 7 7 */
class ZoomControlConfig @JvmOverloads constructor( 8 8 class ZoomControlConfig @JvmOverloads constructor(
val show: Boolean = true, 9 9 val show: Boolean = true,
val position: ControlPosition = ControlPosition.TOP_LEFT) 10 10 val position: ControlPosition = ControlPosition.TOP_LEFT
11 )
src/main/kotlin/map/events/MapClickEvent.kt View file @ 53f01ec
package fdit.leafletmap.events 1 1 package map.events
2 2
import fdit.leafletmap.LatLong 3 3 import map.LatLong
import java.util.* 4 4 import java.util.*
5 5
/** 6 6 /**
* Handles the MapClickEvent 7 7 * Handles the MapClickEvent
* @author Niklas Kellner 8 8 * @author Niklas Kellner
*/ 9 9 */
interface MapClickEventListener { 10 10 interface MapClickEventListener {
fun onMapClick(latLong: LatLong) 11 11 fun onMapClick(latLong: LatLong)
} 12 12 }
13 13
internal class MapClickEventMaker { 14 14 internal class MapClickEventMaker {
private val listeners = ArrayList<MapClickEventListener>() 15 15 private val listeners = ArrayList<MapClickEventListener>()
16 16
fun addListener(toAdd: MapClickEventListener) { 17 17 fun addListener(toAdd: MapClickEventListener) {
listeners.add(toAdd) 18 18 listeners.add(toAdd)
} 19 19 }
20 20
fun MapClickEvent(latLong: LatLong) { 21 21 fun MapClickEvent(latLong: LatLong) {
// Notify everybody that may be interested. 22 22 // Notify everybody that may be interested.
for (hl in listeners) 23 23 for (hl in listeners)
src/main/kotlin/map/events/MapMoveEvent.kt View file @ 53f01ec
package fdit.leafletmap.events 1 1 package map.events
2 2
import fdit.leafletmap.LatLong 3 3 import map.LatLong
import java.util.* 4 4 import java.util.*
5 5
/** 6 6 /**
* Handles the MapMoveEvent 7 7 * Handles the MapMoveEvent
* 8 8 *
* @author Niklas Kellner 9 9 * @author Niklas Kellner
*/ 10 10 */
interface MapMoveEventListener { 11 11 interface MapMoveEventListener {
fun onMapMove(center: LatLong) 12 12 fun onMapMove(center: LatLong)
} 13 13 }
14 14
internal class MapMoveEventMaker { 15 15 internal class MapMoveEventMaker {
private val listeners = ArrayList<MapMoveEventListener>() 16 16 private val listeners = ArrayList<MapMoveEventListener>()
17 17
fun addListener(toAdd: MapMoveEventListener) { 18 18 fun addListener(toAdd: MapMoveEventListener) {
listeners.add(toAdd) 19 19 listeners.add(toAdd)
} 20 20 }
21 21
fun MapMoveEvent(latLong: LatLong) { 22 22 fun MapMoveEvent(latLong: LatLong) {
// Notify everybody that may be interested. 23 23 // Notify everybody that may be interested.
for (hl in listeners) 24 24 for (hl in listeners)
src/main/kotlin/map/events/MarkerClickEvent.kt View file @ 53f01ec
package fdit.leafletmap.events 1 1 package map.events
2 2
import java.util.* 3 3 import java.util.*
4 4
/** 5 5 /**
* Handles the MarkerClickEvent 6 6 * Handles the MarkerClickEvent
* 7 7 *
* @author Niklas Kellner 8 8 * @author Niklas Kellner
*/ 9 9 */
interface MarkerClickEventListener { 10 10 interface MarkerClickEventListener {
fun onMarkerClick(title: String) 11 11 fun onMarkerClick(title: String)
} 12 12 }
13 13
internal class MarkerClickEventMaker { 14 14 internal class MarkerClickEventMaker {
private val listeners = ArrayList<MarkerClickEventListener>() 15 15 private val listeners = ArrayList<MarkerClickEventListener>()
private var listenerSet = false 16 16 private var listenerSet = false
17 17
fun addListener(toAdd: MarkerClickEventListener) { 18 18 fun addListener(toAdd: MarkerClickEventListener) {
listeners.add(toAdd) 19 19 listeners.add(toAdd)
listenerSet = true 20 20 listenerSet = true
} 21 21 }
22 22
fun MarkerClickEvent(title: String){ 23 23 fun MarkerClickEvent(title: String){
// Notify everybody that may be interested. 24 24 // Notify everybody that may be interested.
for (hl in listeners) 25 25 for (hl in listeners)
hl.onMarkerClick(title) 26 26 hl.onMarkerClick(title)
} 27 27 }
src/main/resources/gui/mapPanel.fxml View file @ 53f01ec
File was created 1 <?xml version="1.0" encoding="UTF-8"?>
2
3 <?import javafx.scene.layout.StackPane?>
4
5 <StackPane prefHeight="150.0" prefWidth="200.0" xmlns="http://javafx.com/javafx"
6 xmlns:fx="http://javafx.com/fxml"
src/main/resources/gui/windows.fxml View file @ 53f01ec
<?xml version="1.0" encoding="UTF-8"?> 1 1 <?xml version="1.0" encoding="UTF-8"?>
2 2
3
<?import javafx.scene.control.SplitPane?> 4 3 <?import javafx.scene.control.SplitPane?>
<?import javafx.scene.layout.*?> 5 4 <?import javafx.scene.layout.*?>
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="900.0" 6 5 <AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="900.0"
prefWidth="1200.0" xmlns="http://javafx.com/javafx/8.0.241" xmlns:fx="http://javafx.com/fxml/1"> 7 6 prefWidth="1200.0" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
<children> 8 7 <children>
<fx:include source="menuBar.fxml" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" 9 8 <fx:include source="menuBar.fxml" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0"
AnchorPane.topAnchor="0.0"/> 10 9 AnchorPane.topAnchor="0.0"/>
<SplitPane dividerPositions="0.29797979797979796" layoutY="25.0" prefHeight="379.0" prefWidth="594.0" 11 10 <SplitPane dividerPositions="0.29797979797979796" layoutY="25.0" prefHeight="379.0" prefWidth="594.0"
AnchorPane.bottomAnchor="-4.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="6.0" 12 11 AnchorPane.bottomAnchor="-4.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="6.0"
AnchorPane.topAnchor="25.0"> 13 12 AnchorPane.topAnchor="25.0">
<items> 14 13 <items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0"> 15 14 <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children> 16 15 <children>
<fx:include source="controlPanel.fxml" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" 17 16 <fx:include source="controlPanel.fxml" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0"
AnchorPane.topAnchor="0.0"/> 18 17 AnchorPane.topAnchor="0.0"/>
</children> 19 18 </children>
</AnchorPane> 20 19 </AnchorPane>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0"> 21 20 <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children> 22 21 <children>
<SplitPane dividerPositions="0.5" layoutX="127.0" layoutY="74.0" orientation="VERTICAL" 23 22 <SplitPane dividerPositions="0.536" layoutX="127.0" layoutY="74.0" orientation="VERTICAL"
prefHeight="200.0" prefWidth="160.0" AnchorPane.bottomAnchor="0.0" 24 23 prefHeight="200.0" prefWidth="160.0" AnchorPane.bottomAnchor="0.0"
AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0"> 25 24 AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items> 26 25 <items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0"/> 27 26 <fx:include source="mapPanel.fxml" />
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0"/> 28 27 <AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="100.0" prefWidth="160.0"/>
</items> 29 28 </items>
</SplitPane> 30 29 </SplitPane>
</children> 31 30 </children>
</AnchorPane> 32 31 </AnchorPane>
</items> 33 32 </items>
</SplitPane> 34 33 </SplitPane>
</children> 35 34 </children>
</AnchorPane> 36 35 </AnchorPane>
37 36