Commit 79b001037b8d42da03b5857adf1243176060e734

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

heat map

Showing 16 changed files with 605 additions and 24 deletions Inline Diff

build.gradle View file @ 79b0010
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()
} 11 11 }
12 12
dependencies { 13 13 dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" 14 14 implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation 'org.jfxtras:jmetro:8.6.9' 15 15 implementation 'org.jfxtras:jmetro:8.6.9'
16 implementation 'org.slf4j:slf4j-api:1.7.30'
testCompile group: 'junit', name: 'junit', version: '4.12' 16 17 testCompile group: 'junit', name: 'junit', version: '4.12'
18 testCompile group: 'ch.qos.logback', name: 'logback-classic', version: '1.2.3'
19 compile group: 'ch.qos.logback', name: 'logback-core', version: '1.2.3'
} 17 20 }
18 21
compileKotlin { 19 22 compileKotlin {
kotlinOptions.jvmTarget = "1.8" 20 23 kotlinOptions.jvmTarget = "1.8"
src/main/kotlin/application/Logger.kt View file @ 79b0010
File was created 1 package application
2
3 import org.slf4j.Logger
4 import org.slf4j.LoggerFactory
src/main/kotlin/application/controller/MapPanelController.kt View file @ 79b0010
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.State.*
import javafx.concurrent.Worker 5
import javafx.fxml.FXML 6 5 import javafx.fxml.FXML
import javafx.fxml.Initializable 7 6 import javafx.fxml.Initializable
import javafx.scene.layout.StackPane 8 7 import javafx.scene.layout.StackPane
import map.* 9 8 import map.*
import java.net.URL 10 9 import java.net.URL
import java.util.* 11 10 import java.util.*
import java.util.concurrent.CompletableFuture 12
13 11
class MapPanelController : Initializable { 14 12 class MapPanelController : Initializable {
15 13
@FXML 16 14 @FXML
private lateinit var map: StackPane 17 15 private lateinit var map: StackPane
18 16
private val mapView = LeafletMapView() 19 17 private val mapView = LeafletMapView()
20 18
21 19
override fun initialize(location: URL?, resources: ResourceBundle?) { 22 20 override fun initialize(location: URL?, resources: ResourceBundle?) {
val completeFutureMap: CompletableFuture<Worker.State> = mapView.displayMap(MapConfig()) 23 21 mapView.displayMap(MapConfig())
24
setObservableVesselListener() 25 22 setObservableVesselListener()
setObservableSelectedVesselListener() 26 23 setObservableSelectedVesselListener()
setStateListener() 27 24 setStateListener()
/*completeFutureMap.whenComplete{ 28 25 /*val completeFutureMap: CompletableFuture<Worker.State> = mapView.displayMap(MapConfig())
26 completeFutureMap.whenComplete{
workerState, _ -> 29 27 workerState, _ ->
if (workerState == Worker.State.SUCCEEDED) { 30 28 if (workerState == Worker.State.SUCCEEDED) {
} 31 29 }
}*/ 32 30 }*/
map.children.add(mapView) 33 31 map.children.add(mapView)
map.children 34 32 map.children
} 35 33 }
36 34
private fun setStateListener() { 37 35 private fun setStateListener() {
observableState.listeners.add(object : StateListener { 38 36 observableState.listeners.add(object : StateListener {
override fun onValueChanged(newValue: State) { 39 37 override fun onValueChanged(newValue: State) {
updateMap() 40 38 if (observableSelectedVessel.vessel.mmsi != null) {
39 updateMap(observableSelectedVessel.vessel.mmsi!!)
40 } else {
41 updateMap()
42 }
} 41 43 }
}) 42 44 })
} 43 45 }
44 46
private fun updateMap() { 45 47 private fun updateMap() {
when(observableState.state){ 46 48 when (observableState.state) {
ALL_MESSAGES -> displayAllMessageOnMap(mapView) 47 49 ALL_MESSAGES -> displayAllMessageOnMap(mapView)
CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView) 48 50 CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView)
HEAT_MAP -> displayHeatMapOnMap(mapView) 49 51 HEAT_MAP -> displayHeatMapOnMap(mapView)
} 50 52 }
} 51 53 }
52 54
private fun updateMap(selectedMMSI: Int) { 53 55 private fun updateMap(selectedMMSI: Int) {
when(observableState.state){ 54 56 when (observableState.state) {
ALL_MESSAGES -> displayAllMessageOnMap(mapView, selectedMMSI) 55 57 ALL_MESSAGES -> displayAllMessageOnMap(mapView, selectedMMSI)
CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView, selectedMMSI) 56 58 CLUSTERED_MESSAGES -> displayClusterMessageOnMap(mapView, selectedMMSI)
HEAT_MAP -> displayHeatMapOnMap(mapView, selectedMMSI) 57 59 HEAT_MAP -> displayHeatMapOnMap(mapView, selectedMMSI)
} 58 60 }
} 59 61 }
60 62
private fun setObservableVesselListener() { 61 63 private fun setObservableVesselListener() {
observableVessel.listeners.add(object : MessageListener { 62 64 observableVessel.listeners.add(object : MessageListener {
override fun onValueChanged(newValue: MutableMap<Int?, Vessel>) { 63 65 override fun onValueChanged(newValue: MutableMap<Int?, Vessel>) {
updateMap() 64 66 updateMap()
} 65 67 }
}) 66 68 })
} 67 69 }
68 70
private fun setObservableSelectedVesselListener() { 69 71 private fun setObservableSelectedVesselListener() {
observableSelectedVessel.listeners.add(object : SelectedVesselListener { 70 72 observableSelectedVessel.listeners.add(object : SelectedVesselListener {
override fun onValueChanged(newValue: Vessel) { 71 73 override fun onValueChanged(newValue: Vessel) {
updateMap(newValue.mmsi!!) 72 74 if (newValue.mmsi != null){
75 updateMap(newValue.mmsi)
76 }
src/main/kotlin/application/controller/MenuBarController.kt View file @ 79b0010
package application.controller 1 1 package application.controller
2 2
import application.model.State 3
import application.model.State.* 4 3 import application.model.State.*
import application.model.createVesselCollection 5 4 import application.model.createVesselCollection
import application.model.observableState 6 5 import application.model.observableState
import application.model.observableVessel 7 6 import application.model.observableVessel
import javafx.event.EventHandler 8 7 import javafx.event.EventHandler
import javafx.fxml.FXML 9 8 import javafx.fxml.FXML
import javafx.fxml.Initializable 10 9 import javafx.fxml.Initializable
import javafx.scene.control.CheckMenuItem 11 10 import javafx.scene.control.CheckMenuItem
import javafx.scene.control.MenuBar 12 11 import javafx.scene.control.MenuBar
import javafx.scene.control.MenuItem 13 12 import javafx.scene.control.MenuItem
import javafx.stage.FileChooser 14 13 import javafx.stage.FileChooser
import java.net.URL 15 14 import java.net.URL
import java.util.* 16 15 import java.util.*
17 16
class MenuBarController : Initializable { 18 17 class MenuBarController : Initializable {
19 18
@FXML 20 19 @FXML
var menuBar: MenuBar = MenuBar() 21 20 var menuBar: MenuBar = MenuBar()
22 21
@FXML 23 22 @FXML
var import: MenuItem = MenuItem() 24 23 var import: MenuItem = MenuItem()
25 24
@FXML 26 25 @FXML
var allMessages: CheckMenuItem = CheckMenuItem() 27 26 var allMessages: CheckMenuItem = CheckMenuItem()
28 27
@FXML 29 28 @FXML
var clusteredMessage: CheckMenuItem = CheckMenuItem() 30 29 var clusteredMessage: CheckMenuItem = CheckMenuItem()
31 30
@FXML 32 31 @FXML
var heatMap: CheckMenuItem = CheckMenuItem() 33 32 var heatMap: CheckMenuItem = CheckMenuItem()
34 33
override fun initialize(location: URL?, resources: ResourceBundle?) { 35 34 override fun initialize(location: URL?, resources: ResourceBundle?) {
36 35
setOnActionImportButton() 37 36 setOnActionImportButton()
38 37
setOnActionAllMessageButton() 39 38 setOnActionAllMessageButton()
setOnActionClusteredMessageButton() 40 39 setOnActionClusteredMessageButton()
setOnActionHeatMapButton() 41 40 setOnActionHeatMapButton()
41 observableState.state = CLUSTERED_MESSAGES
42 allMessages.isSelected = false
clusteredMessage.isSelected = true 42 43 clusteredMessage.isSelected = true
44 heatMap.isSelected = false
43 45
} 44 46 }
45 47
private fun setOnActionImportButton() { 46 48 private fun setOnActionImportButton() {
import.onAction = EventHandler { 47 49 import.onAction = EventHandler {
val fileChooser = FileChooser() 48 50 val fileChooser = FileChooser()
fileChooser.title = "Choose a file to import" 49 51 fileChooser.title = "Choose a file to import"
val window = menuBar.scene.window 50 52 val window = menuBar.scene.window
val file = fileChooser.showOpenDialog(window) 51 53 val file = fileChooser.showOpenDialog(window)
val vessels = createVesselCollection(file) 52 54 try {
observableVessel.vessels.clear() 53 55 val vessels = createVesselCollection(file)
observableVessel.vessels = vessels 54 56 observableVessel.vessels.clear()
57 observableVessel.vessels = vessels
58 } catch (ignore: IllegalStateException){
59
60 }
} 55 61 }
} 56 62 }
57 63
private fun setOnActionAllMessageButton() { 58 64 private fun setOnActionAllMessageButton() {
allMessages.onAction = EventHandler { 59 65 allMessages.onAction = EventHandler {
observableState.state = ALL_MESSAGES 60 66 observableState.state = ALL_MESSAGES
allMessages.isSelected = true 61 67 allMessages.isSelected = true
clusteredMessage.isSelected = false 62 68 clusteredMessage.isSelected = false
heatMap.isSelected = false 63 69 heatMap.isSelected = false
} 64 70 }
} 65 71 }
66 72
private fun setOnActionClusteredMessageButton() { 67 73 private fun setOnActionClusteredMessageButton() {
clusteredMessage.onAction = EventHandler { 68 74 clusteredMessage.onAction = EventHandler {
observableState.state = CLUSTERED_MESSAGES 69 75 observableState.state = CLUSTERED_MESSAGES
heatMap.isSelected = false 70 76 heatMap.isSelected = false
allMessages.isSelected = false 71 77 allMessages.isSelected = false
clusteredMessage.isSelected = true 72 78 clusteredMessage.isSelected = true
src/main/kotlin/application/model/Message.kt View file @ 79b0010
package application.model 1 1 package application.model
2 2
import java.time.LocalDateTime 3 3 import java.time.LocalDateTime
4 4
class Message(split: List<String>) { 5 5 class Message(split: List<String>) {
val mmsi: Int? = split[0].toIntOrNull() 6 6 val mmsi: Int? = split[0].toIntOrNull()
val time: LocalDateTime = LocalDateTime.parse(split[1]) 7 7 val time: LocalDateTime = LocalDateTime.parse(split[1])
val latitude: Double? = split[2].toDoubleOrNull() 8 8 val latitude: Double? = split[2].toDoubleOrNull()
val longitude: Double? = split[3].toDoubleOrNull() 9 9 val longitude: Double? = split[3].toDoubleOrNull()
val speedOverGround: Double? = split[4].toDoubleOrNull() 10 10 val speedOverGround: Double? = split[4].toDoubleOrNull()
val courseOverGround: Double? = split[5].toDoubleOrNull() 11 11 val courseOverGround: Double? = split[5].toDoubleOrNull()
val heading: Int? = split[6].toIntOrNull() 12 12 val heading: Int? = split[6].toIntOrNull()
val vesselName: String? = split[7] 13 13 val vesselName: String? = split[7]
val imo: String? = split[8] 14 14 val imo: String? = split[8]
val callSign: String? = split[9] 15 15 val callSign: String? = split[9]
val vesselType: Int? = split[10].toIntOrNull() 16 16 val vesselType: Int? = split[10].toIntOrNull()
val status: String? = split[11] 17 17 val status: String? = split[11]
val length: Double? = split[12].toDoubleOrNull() 18 18 val length: Double? = split[12].toDoubleOrNull()
val width: Double? = split[13].toDoubleOrNull() 19 19 val width: Double? = split[13].toDoubleOrNull()
val draft: Double? = split[14].toDoubleOrNull() 20 20 val draft: Double? = split[14].toDoubleOrNull()
val cargo: Int? = split[15].toIntOrNull() 21 21 val cargo: Int? = split[15].toIntOrNull()
22 22
fun getHexColor(): String{ 23 23 fun getHexColorStroke(): String{
var hex = Integer.toHexString(this.mmsi!!) 24 24 var hex = Integer.toHexString(this.mmsi!!)
25 if (hex.length > 6){
26 hex = hex.substring(hex.length - 6)
27 }
28 return hex
29 }
30
31 fun getHexColorFill(): String{
32 var hex = Integer.toHexString(this.mmsi!! - 50)
if (hex.length > 6){ 25 33 if (hex.length > 6){
hex = hex.substring(hex.length - 6) 26 34 hex = hex.substring(hex.length - 6)
} 27 35 }
return hex 28 36 return hex
src/main/kotlin/application/model/ObservableVessel.kt View file @ 79b0010
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<Int?, Vessel> by Delegates.observable( 8 8 var vessels: MutableMap<Int?, Vessel> by Delegates.observable(
initialValue = mutableMapOf(), 9 9 initialValue = mutableMapOf(),
onChange = { _, _, new -> 10 10 onChange = { _, _, new ->
run { 11 11 run {
12 observableSelectedVessel.vessel = Vessel(null)
listeners.forEach { 12 13 listeners.forEach {
it.onValueChanged(new) 13 14 it.onValueChanged(new)
} 14 15 }
} 15 16 }
src/main/kotlin/map/LeafletMapView.kt View file @ 79b0010
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: 5, 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);
|var myRenderer = L.canvas({ padding: 0.5 }); 96 96 |var myRenderer = L.canvas({ padding: 0.5 });
|var markerClusters = L.markerClusterGroup({spiderfyOnMaxZoom: false, disableClusteringAtZoom: 10});""".trimMargin() 97 97 |var markerClusters = L.markerClusterGroup({spiderfyOnMaxZoom: false, disableClusteringAtZoom: 10});
98 |var heatLayer = L.heatLayer([]).addTo(myMap);""".trimMargin()
) 98 99 )
99 100
// eventZoomChangeIcon() 100 101 // eventZoomChangeIcon()
101 102
// execute script for layer control definition if there are multiple layers 102 103 // execute script for layer control definition if there are multiple layers
if (mapConfig.layers.size > 1) { 103 104 if (mapConfig.layers.size > 1) {
execScript( 104 105 execScript(
""" 105 106 """
|var overlayMaps = {}; 106 107 |var overlayMaps = {};
|L.control.layers(baseMaps, overlayMaps).addTo(myMap);""".trimMargin() 107 108 |L.control.layers(baseMaps, overlayMaps).addTo(myMap);""".trimMargin()
) 108 109 )
109 110
} 110 111 }
111 112
// execute script for scale control definition 112 113 // execute script for scale control definition
if (mapConfig.scaleControlConfig.show) { 113 114 if (mapConfig.scaleControlConfig.show) {
execScript( 114 115 execScript(
"L.control.scale({position: '${mapConfig.scaleControlConfig.position.positionName}', " + 115 116 "L.control.scale({position: '${mapConfig.scaleControlConfig.position.positionName}', " +
"metric: ${mapConfig.scaleControlConfig.metric}, " + 116 117 "metric: ${mapConfig.scaleControlConfig.metric}, " +
"imperial: ${!mapConfig.scaleControlConfig.metric}})" + 117 118 "imperial: ${!mapConfig.scaleControlConfig.metric}})" +
".addTo(myMap);" 118 119 ".addTo(myMap);"
) 119 120 )
} 120 121 }
121 122
// execute script for zoom control definition 122 123 // execute script for zoom control definition
if (mapConfig.zoomControlConfig.show) { 123 124 if (mapConfig.zoomControlConfig.show) {
execScript( 124 125 execScript(
"L.control.zoom({position: '${mapConfig.zoomControlConfig.position.positionName}'})" + 125 126 "L.control.zoom({position: '${mapConfig.zoomControlConfig.position.positionName}'})" +
".addTo(myMap);" 126 127 ".addTo(myMap);"
) 127 128 )
} 128 129 }
} 129 130 }
130 131
/** 131 132 /**
* Sets the view of the map to the specified geographical center position and zoom level. 132 133 * Sets the view of the map to the specified geographical center position and zoom level.
* 133 134 *
* @param position map center position 134 135 * @param position map center position
* @param zoomLevel zoom level (0 - 19 for OpenStreetMap) 135 136 * @param zoomLevel zoom level (0 - 19 for OpenStreetMap)
*/ 136 137 */
fun setView(position: LatLong, zoomLevel: Int) = 137 138 fun setView(position: LatLong, zoomLevel: Int) =
execScript("myMap.setView([${position.latitude}, ${position.longitude}], $zoomLevel);") 138 139 execScript("myMap.setView([${position.latitude}, ${position.longitude}], $zoomLevel);")
139 140
/** 140 141 /**
* Pans the map to the specified geographical center position. 141 142 * Pans the map to the specified geographical center position.
* 142 143 *
* @param position map center position 143 144 * @param position map center position
*/ 144 145 */
fun panTo(position: LatLong) = 145 146 fun panTo(position: LatLong) =
execScript("myMap.panTo([${position.latitude}, ${position.longitude}]);") 146 147 execScript("myMap.panTo([${position.latitude}, ${position.longitude}]);")
147 148
/** 148 149 /**
* Sets the zoom of the map to the specified level. 149 150 * Sets the zoom of the map to the specified level.
* 150 151 *
* @param zoomLevel zoom level (0 - 19 for OpenStreetMap) 151 152 * @param zoomLevel zoom level (0 - 19 for OpenStreetMap)
*/ 152 153 */
fun setZoom(zoomLevel: Int) = 153 154 fun setZoom(zoomLevel: Int) =
execScript("myMap.setZoom([$zoomLevel]);") 154 155 execScript("myMap.setZoom([$zoomLevel]);")
155 156
/** 156 157 /**
* Adds a Marker Object to a map 157 158 * Adds a Marker Object to a map
* 158 159 *
* @param marker the Marker Object 159 160 * @param marker the Marker Object
*/ 160 161 */
fun addMarker(marker: Marker) { 161 162 fun addMarker(marker: Marker) {
marker.addToMap(getNextMarkerName(), this) 162 163 marker.addToMap(getNextMarkerName(), this)
} 163 164 }
164 165
fun addCircle(circle: Circle) { 165 166 fun addCircle(circle: Circle) {
circle.addToMap(this) 166 167 circle.addToMap(this)
} 167 168 }
168 169
fun addZone(zone: Zone) { 169 170 fun addZone(zone: Zone) {
zone.addToMap(this) 170 171 zone.addToMap(this)
} 171 172 }
172 173
/** 173 174 /**
* Removes an existing marker from the map 174 175 * Removes an existing marker from the map
* 175 176 *
* @param marker the Marker object 176 177 * @param marker the Marker object
*/ 177 178 */
fun removeMarker(marker: Marker) { 178 179 fun removeMarker(marker: Marker) {
execScript("myMap.removeLayer(${marker.getName()});") 179 180 execScript("myMap.removeLayer(${marker.getName()});")
} 180 181 }
181 182
fun removeCircle(circle: Circle) { 182 183 fun removeCircle(circle: Circle) {
circle.removeCircle(this) 183 184 circle.removeCircle(this)
} 184 185 }
185 186
fun removeZone(zone: Zone) { 186 187 fun removeZone(zone: Zone) {
zone.removeZone() 187 188 zone.removeZone()
} 188 189 }
189 190
fun removeZone(id: String) { 190 191 fun removeZone(id: String) {
val idSanitized = id.replace("-", "") 191 192 val idSanitized = id.replace("-", "")
execScript("myMap.removeLayer(polygon$idSanitized);") 192 193 execScript("myMap.removeLayer(polygon$idSanitized);")
} 193 194 }
194 195
195 196
fun uppdateCircle(circle: Circle, latLong: LatLong, radius: Double) { 196 197 fun uppdateCircle(circle: Circle, latLong: LatLong, radius: Double) {
circle.modifyCircle(latLong, radius) 197 198 circle.modifyCircle(latLong, radius)
circle.uppdateMap() 198 199 circle.uppdateMap()
} 199 200 }
200 201
fun setEventMousePosition() { 201 202 fun setEventMousePosition() {
execScript( 202 203 execScript(
"var lat=0.0, lng=0.0;\n" + 203 204 "var lat=0.0, lng=0.0;\n" +
"myMap.addEventListener('mousemove', function(ev) {\n" + 204 205 "myMap.addEventListener('mousemove', function(ev) {\n" +
" lat = ev.latlng.lat;\n" + 205 206 " lat = ev.latlng.lat;\n" +
" lng = ev.latlng.lng;\n" + 206 207 " lng = ev.latlng.lng;\n" +
"});" 207 208 "});"
) 208 209 )
} 209 210 }
210 211
fun getMousePosition(): LatLong { 211 212 fun getMousePosition(): LatLong {
val lat = execScript("lat;") as Double 212 213 val lat = execScript("lat;") as Double
val lng = execScript("lng;") as Double 213 214 val lng = execScript("lng;") as Double
return LatLong(lat, lng) 214 215 return LatLong(lat, lng)
} 215 216 }
216 217
/** 217 218 /**
* Adds a custom marker type 218 219 * Adds a custom marker type
* 219 220 *
* @param markerName the name of the marker type 220 221 * @param markerName the name of the marker type
* @param iconUrl the url if the marker icon 221 222 * @param iconUrl the url if the marker icon
*/ 222 223 */
fun addCustomMarker(markerName: String, iconUrl: String): String { 223 224 fun addCustomMarker(markerName: String, iconUrl: String): String {
execScript( 224 225 execScript(
"var $markerName = L.icon({\n" + 225 226 "var $markerName = L.icon({\n" +
"iconUrl: '${createImage(iconUrl, "png")}',\n" + 226 227 "iconUrl: '${createImage(iconUrl, "png")}',\n" +
"iconSize: [24, 24],\n" + 227 228 "iconSize: [24, 24],\n" +
"iconAnchor: [12, 12],\n" + 228 229 "iconAnchor: [12, 12],\n" +
"});" 229 230 "});"
) 230 231 )
return markerName 231 232 return markerName
} 232 233 }
233 234
private fun createImage(path: String, type: String): String { 234 235 private fun createImage(path: String, type: String): String {
val image = ImageIO.read(File(path)) 235 236 val image = ImageIO.read(File(path))
var imageString: String? = null 236 237 var imageString: String? = null
val bos = ByteArrayOutputStream() 237 238 val bos = ByteArrayOutputStream()
238 239
try { 239 240 try {
ImageIO.write(image, type, bos) 240 241 ImageIO.write(image, type, bos)
val imageBytes = bos.toByteArray() 241 242 val imageBytes = bos.toByteArray()
242 243
val encoder = Base64.getEncoder() 243 244 val encoder = Base64.getEncoder()
imageString = encoder.encodeToString(imageBytes) 244 245 imageString = encoder.encodeToString(imageBytes)
245 246
bos.close() 246 247 bos.close()
} catch (e: IOException) { 247 248 } catch (e: IOException) {
e.printStackTrace() 248 249 e.printStackTrace()
} 249 250 }
return "data:image/$type;base64,$imageString" 250 251 return "data:image/$type;base64,$imageString"
} 251 252 }
252 253
/** 253 254 /**
* Sets the onMarkerClickListener 254 255 * Sets the onMarkerClickListener
* 255 256 *
* @param listener the onMarerClickEventListener 256 257 * @param listener the onMarerClickEventListener
*/ 257 258 */
fun onMarkerClick(listener: MarkerClickEventListener) { 258 259 fun onMarkerClick(listener: MarkerClickEventListener) {
val win = execScript("document") as JSObject 259 260 val win = execScript("document") as JSObject
win.setMember("java", this) 260 261 win.setMember("java", this)
markerClickEvent.addListener(listener) 261 262 markerClickEvent.addListener(listener)
} 262 263 }
263 264
/** 264 265 /**
* Handles the callback from the markerClickEvent 265 266 * Handles the callback from the markerClickEvent
*/ 266 267 */
fun markerClick(title: String) { 267 268 fun markerClick(title: String) {
markerClickEvent.MarkerClickEvent(title) 268 269 markerClickEvent.MarkerClickEvent(title)
} 269 270 }
270 271
/** 271 272 /**
* Sets the onMapMoveListener 272 273 * Sets the onMapMoveListener
* 273 274 *
* @param listener the MapMoveEventListener 274 275 * @param listener the MapMoveEventListener
*/ 275 276 */
fun onMapMove(listener: MapMoveEventListener) { 276 277 fun onMapMove(listener: MapMoveEventListener) {
val win = execScript("document") as JSObject 277 278 val win = execScript("document") as JSObject
win.setMember("java", this) 278 279 win.setMember("java", this)
execScript("myMap.on('moveend', function(e){ document.java.mapMove(myMap.getCenter().lat, myMap.getCenter().lng);});") 279 280 execScript("myMap.on('moveend', function(e){ document.java.mapMove(myMap.getCenter().lat, myMap.getCenter().lng);});")
mapMoveEvent.addListener(listener) 280 281 mapMoveEvent.addListener(listener)
} 281 282 }
282 283
/** 283 284 /**
* Handles the callback from the mapMoveEvent 284 285 * Handles the callback from the mapMoveEvent
*/ 285 286 */
fun mapMove(lat: Double, lng: Double) { 286 287 fun mapMove(lat: Double, lng: Double) {
val latlng = LatLong(lat, lng) 287 288 val latlng = LatLong(lat, lng)
mapMoveEvent.MapMoveEvent(latlng) 288 289 mapMoveEvent.MapMoveEvent(latlng)
} 289 290 }
290 291
/** 291 292 /**
* Sets the onMapClickListener 292 293 * Sets the onMapClickListener
* 293 294 *
* @param listener the onMapClickEventListener 294 295 * @param listener the onMapClickEventListener
*/ 295 296 */
fun onMapClick(listener: MapClickEventListener) { 296 297 fun onMapClick(listener: MapClickEventListener) {
val win = execScript("document") as JSObject 297 298 val win = execScript("document") as JSObject
win.setMember("java", this) 298 299 win.setMember("java", this)
execScript("myMap.on('click', function(e){ document.java.mapClick(e.latlng.lat, e.latlng.lng);});") 299 300 execScript("myMap.on('click', function(e){ document.java.mapClick(e.latlng.lat, e.latlng.lng);});")
mapClickEvent.addListener(listener) 300 301 mapClickEvent.addListener(listener)
} 301 302 }
302 303
/** 303 304 /**
* Handles the callback from the mapClickEvent 304 305 * Handles the callback from the mapClickEvent
*/ 305 306 */
fun mapClick(lat: Double, lng: Double) { 306 307 fun mapClick(lat: Double, lng: Double) {
val latlng = LatLong(lat, lng) 307 308 val latlng = LatLong(lat, lng)
mapClickEvent.MapClickEvent(latlng) 308 309 mapClickEvent.MapClickEvent(latlng)
} 309 310 }
310 311
/** 311 312 /**
* Draws a track path along the specified positions. 312 313 * Draws a track path along the specified positions.
* 313 314 *
* @param positions list of track positions 314 315 * @param positions list of track positions
*/ 315 316 */
fun addTrack(positions: List<LatLong>) { 316 317 fun addTrack(positions: List<LatLong>) {
317 318
val jsPositions = positions 318 319 val jsPositions = positions
.map { " [${it.latitude}, ${it.longitude}]" } 319 320 .map { " [${it.latitude}, ${it.longitude}]" }
.joinToString(", \n") 320 321 .joinToString(", \n")
321 322
execScript( 322 323 execScript(
""" 323 324 """
|var latLngs = [ 324 325 |var latLngs = [
|$jsPositions 325 326 |$jsPositions
|]; 326 327 |];
|var polyline = L.polyline(latLngs, {color: 'red', weight: 2}).addTo(myMap);""".trimMargin() 327 328 |var polyline = L.polyline(latLngs, {color: 'red', weight: 2}).addTo(myMap);""".trimMargin()
) 328 329 )
} 329 330 }
330 331
fun clearAllLayer() { 331 332 fun clearAllLayer() {
execScript(""" 332 333 execScript("""
myMap.eachLayer(function (layer) { 333 334 myMap.eachLayer(function (layer) {
map.removeLayer(layer); 334 335 map.removeLayer(layer);
}); 335 336 });
""".trimIndent()) 336 337 """.trimIndent())
} 337 338 }
338 339
fun addTrack(positions: List<LatLong>, id: String, color: Color, tooltip: String) { 339 340 fun addTrack(positions: List<LatLong>, id: String, color: Color, tooltip: String) {
340 341
val jsPositions = positions 341 342 val jsPositions = positions
.map { " [${it.latitude}, ${it.longitude}]" } 342 343 .map { " [${it.latitude}, ${it.longitude}]" }
.joinToString(", \n") 343 344 .joinToString(", \n")
344 345
val cleanTooltip = tooltip.replace("'", "&apos;") 345 346 val cleanTooltip = tooltip.replace("'", "&apos;")
execScript( 346 347 execScript(
""" 347 348 """
|var latLngs = [ 348 349 |var latLngs = [
|$jsPositions 349 350 |$jsPositions
|]; 350 351 |];
|var color = "rgb(${Math.floor(color.getRed() * 255).toInt()} ,${Math.floor(color.getGreen() * 255) 351 352 |var color = "rgb(${Math.floor(color.getRed() * 255).toInt()} ,${Math.floor(color.getGreen() * 255)
.toInt()},${Math.floor(color.getBlue() * 255).toInt()})"; 352 353 .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() 353 354 |var polyline$id = L.polyline(latLngs, {color: color, weight: 2, zIndexOffset: 200}).bindTooltip('$cleanTooltip', {sticky: true}).addTo(trackGroup)""".trimMargin()
) 354 355 )
} 355 356 }
356 357
fun makeVesselTrackTransparent(id: String) { 357 358 fun makeVesselTrackTransparent(id: String) {
execScript("polyline$id.setStyle({opacity: 0.5});") 358 359 execScript("polyline$id.setStyle({opacity: 0.5});")
} 359 360 }
360 361
fun highlightTrack(id: String) { 361 362 fun highlightTrack(id: String) {
execScript("polyline$id.setStyle({weight: 4});") 362 363 execScript("polyline$id.setStyle({weight: 4});")
} 363 364 }
364 365
fun normalizeVesselTrack(id: String) { 365 366 fun normalizeVesselTrack(id: String) {
execScript("polyline$id.setStyle({opacity: 1,weight: 2});") 366 367 execScript("polyline$id.setStyle({opacity: 1,weight: 2});")
} 367 368 }
368 369
src/main/kotlin/map/MapDisplayer.kt View file @ 79b0010
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)
8 clearHeatMap(map)
} 8 9 }
9 10
fun clearMapCluster(map: LeafletMapView) { 10 11 fun clearMapCluster(map: LeafletMapView) {
map.execScript( 11 12 map.execScript(
""" 12 13 """
|myMap.removeLayer(markerClusters) 13 14 |myMap.removeLayer(markerClusters);
|var markerClusters = L.markerClusterGroup({spiderfyOnMaxZoom: false, disableClusteringAtZoom: 9}); 14 15 |var markerClusters = L.markerClusterGroup({spiderfyOnMaxZoom: false, disableClusteringAtZoom: 10});
""".trimMargin() 15 16 """.trimMargin()
) 16 17 )
} 17 18 }
18 19
fun clearMapCanvas(map: LeafletMapView) { 19 20 fun clearMapCanvas(map: LeafletMapView) {
map.execScript( 20 21 map.execScript(
""" 21 22 """
|myRenderer.removeFrom(myMap) 22 23 |myRenderer.removeFrom(myMap);
|var myRenderer = L.canvas({ padding: 0.5 }); 23 24 |var myRenderer = L.canvas({ padding: 0.5 });
""".trimMargin() 24 25 """.trimMargin()
) 25 26 )
} 26 27 }
27 28
29 fun clearHeatMap(map: LeafletMapView) {
30 map.execScript(
31 """
32 |heatLayer.removeFrom(myMap);
33 |var heatLayer = L.heatLayer([]).addTo(myMap);
34 """.trimMargin()
35 )
36 }
37
fun displayAllMessageOnMap(map: LeafletMapView) { 28 38 fun displayAllMessageOnMap(map: LeafletMapView) {
clearMap(map) 29 39 clearMap(map)
observableVessel.vessels.forEach { (_, value) -> 30 40 observableVessel.vessels.forEach { (_, value) ->
value.messages.forEach { (_, message) -> 31 41 value.messages.forEach { (_, message) ->
map.execScript("L.circleMarker([${message.latitude}, ${message.longitude}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColor()}'}).addTo(myMap)") 32 42 map.execScript("L.circleMarker([${message.latitude}, ${message.longitude}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap)")
} 33 43 }
} 34 44 }
} 35 45 }
36 46
fun displayAllMessageOnMap(map: LeafletMapView, selectedMMSI: Int) { 37 47 fun displayAllMessageOnMap(map: LeafletMapView, selectedMMSI: Int) {
clearMap(map) 38 48 clearMap(map)
observableVessel.vessels.forEach { (_, value) -> 39 49 observableVessel.vessels.forEach { (_, value) ->
value.messages.forEach { (_, message) -> 40 50 value.messages.forEach { (_, message) ->
if (selectedMMSI == message.mmsi) { 41 51 if (selectedMMSI == message.mmsi) {
map.execScript("L.circleMarker([${message.latitude}, ${message.longitude}], {renderer: myRenderer, radius: 2, color: '#ff001e'}).addTo(myMap)") 42 52 map.execScript("L.circleMarker([${message.latitude}, ${message.longitude}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap)")
} else { 43 53 } else {
map.execScript("L.circleMarker([${message.latitude}, ${message.longitude}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColor()}'}).addTo(myMap)") 44 54 map.execScript("L.circleMarker([${message.latitude}, ${message.longitude}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}).addTo(myMap)")
} 45 55 }
} 46 56 }
} 47 57 }
58 }
59
60 fun displayClusterMessageOnMap(map: LeafletMapView) {
61 clearMap(map)
62 observableVessel.vessels.forEach { (_, value) ->
63 value.messages.forEach { (_, message) ->
64 map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude}, ${message.longitude}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}));")
65 }
66 }
67 map.execScript("myMap.addLayer(markerClusters);")
68 }
69
70 fun displayClusterMessageOnMap(map: LeafletMapView, selectedMMSI: Int) {
71 clearMap(map)
72 observableVessel.vessels.forEach { (_, value) ->
73 value.messages.forEach { (_, message) ->
74 if (selectedMMSI == message.mmsi) {
75 map.execScript("L.circleMarker([${message.latitude}, ${message.longitude}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap);")
76 } else {
77 map.execScript("markerClusters.addLayer(L.circleMarker([${message.latitude}, ${message.longitude}], {renderer: myRenderer, radius: 0.01, color: '#${message.getHexColorStroke()}'}));")
78 }
79 }
80 }
81 map.execScript("myMap.addLayer(markerClusters);")
82 }
83
84 fun displayHeatMapOnMap(map: LeafletMapView) {
85 clearMap(map)
86 observableVessel.vessels.forEach { (_, value) ->
87 value.messages.forEach { (_, message) ->
88 map.execScript("heatLayer.addLatLng([${message.latitude}, ${message.longitude}]);")
89 }
90 }
91
92 }
93
94 fun displayHeatMapOnMap(map: LeafletMapView, selectedMMSI: Int) {
95 clearMap(map)
96 observableVessel.vessels.forEach { (_, value) ->
97 value.messages.forEach { (_, message) ->
98 if (selectedMMSI == message.mmsi) {
99 map.execScript("L.circleMarker([${message.latitude}, ${message.longitude}], {renderer: myRenderer, radius: 2, color: '#ff4040'}).addTo(myMap);")
100 } else {
101 map.execScript("heatLayer.addLatLng([${message.latitude}, ${message.longitude}]);")
102 }
103 }
104 }
105 map.execScript("myMap.addLayer(markerClusters);")
} 48 106 }
src/main/resources/leafletmap/Leaflet.heat/LICENSE View file @ 79b0010
File was created 1 Copyright (c) 2014, Vladimir Agafonkin
2 All rights reserved.
3
4 Redistribution and use in source and binary forms, with or without modification, are
5 permitted provided that the following conditions are met:
6
7 1. Redistributions of source code must retain the above copyright notice, this list of
8 conditions and the following disclaimer.
9
10 2. Redistributions in binary form must reproduce the above copyright notice, this list
11 of conditions and the following disclaimer in the documentation and/or other materials
12 provided with the distribution.
13
14 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
15 EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
17 COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
src/main/resources/leafletmap/Leaflet.heat/README.md View file @ 79b0010
File was created 1 Leaflet.heat
2 ==========
3
4 A tiny, simple and fast [Leaflet](http://leafletjs.com) heatmap plugin.
5 Uses [simpleheat](https://github.com/mourner/simpleheat) under the hood,
6 additionally clustering points into a grid for performance.
7
8
9 ## Demos
10
11 - [10,000 points &rarr;](http://leaflet.github.io/Leaflet.heat/demo)
12 - [Adding points dynamically &rarr;](http://leaflet.github.io/Leaflet.heat/demo/draw.html)
13
14
15 ## Basic Usage
16
17 ```js
18 var heat = L.heatLayer([
19 [50.5, 30.5, 0.2], // lat, lng, intensity
20 [50.6, 30.4, 0.5],
21 ...
22 ], {radius: 25}).addTo(map);
23 ```
24
25 To include the plugin, just use `leaflet-heat.js` from the `dist` folder:
26
27 ```html
28 <script src="leaflet-heat.js"></script>
29 ```
30
31 ## Building
32 To build the dist files run:
33 ```npm install && npm run prepublish```
34
35
36 ## Reference
37
38 #### L.heatLayer(latlngs, options)
39
40 Constructs a heatmap layer given an array of points and an object with the following options:
41 - **minOpacity** - the minimum opacity the heat will start at
42 - **maxZoom** - zoom level where the points reach maximum intensity (as intensity scales with zoom),
43 equals `maxZoom` of the map by default
44 - **max** - maximum point intensity, `1.0` by default
45 - **radius** - radius of each "point" of the heatmap, `25` by default
46 - **blur** - amount of blur, `15` by default
47 - **gradient** - color gradient config, e.g. `{0.4: 'blue', 0.65: 'lime', 1: 'red'}`
48
49 Each point in the input array can be either an array like `[50.5, 30.5, 0.5]`,
50 or a [Leaflet LatLng object](http://leafletjs.com/reference.html#latlng).
51
52 Optional third argument in each `LatLng` point (`altitude`) represents point intensity.
53 Unless `max` option is specified, intensity should range between `0.0` and `1.0`.
54
55
56 #### Methods
57
58 - **setOptions(options)**: Sets new heatmap options and redraws it.
59 - **addLatLng(latlng)**: Adds a new point to the heatmap and redraws it.
60 - **setLatLngs(latlngs)**: Resets heatmap data and redraws it.
61 - **redraw()**: Redraws the heatmap.
src/main/resources/leafletmap/Leaflet.heat/demo/draw.html View file @ 79b0010
File was created 1 <!DOCTYPE html>
2 <html>
3 <head>
4 <title>Leaflet.heat demo</title>
5 <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.css" />
6 <script src="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.js"></script>
7 <style>
8 #map { width: 800px; height: 600px; }
9 body { font: 16px/1.4 "Helvetica Neue", Arial, sans-serif; }
10 .ghbtns { position: relative; top: 4px; margin-left: 5px; }
11 a { color: #0077ff; }
12 </style>
13 </head>
14 <body>
15
16 <p>
17 A dynamic demo of <a href="https://github.com/Leaflet/Leaflet.heat">Leaflet.heat</a>, a tiny and fast Leaflet heatmap plugin.
18 <iframe class="ghbtns" src="http://ghbtns.com/github-btn.html?user=Leaflet&amp;repo=Leaflet.heat&amp;type=watch&amp;count=true"
19 allowtransparency="true" frameborder="0" scrolling="0" width="90" height="20"></iframe>
20 </p>
21
22 <div id="map"></div>
23
24 <!-- <script src="../node_modules/simpleheat/simpleheat.js"></script>
25 <script src="../src/HeatLayer.js"></script>
26 -->
27 <script src="../dist/leaflet-heat.js"></script>
28
29 <script src="http://leaflet.github.io/Leaflet.markercluster/example/realworld.388.js"></script>
30 <script>
31
32 var map = L.map('map').setView([-37.82109, 175.2193], 16);
33
34 var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
35 attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors',
36 }).addTo(map);
37
38 addressPoints = addressPoints.map(function (p) { return [p[0], p[1]]; });
39
40 var heat = L.heatLayer(addressPoints).addTo(map),
41 draw = true;
42
43 map.on({
44 movestart: function () { draw = false; },
45 moveend: function () { draw = true; },
src/main/resources/leafletmap/Leaflet.heat/demo/index.html View file @ 79b0010
File was created 1 <!DOCTYPE html>
2 <html>
3 <head>
4 <title>Leaflet.heat demo</title>
5 <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.css" />
6 <script src="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.js"></script>
7 <style>
8 #map { width: 800px; height: 600px; }
9 body { font: 16px/1.4 "Helvetica Neue", Arial, sans-serif; }
10 .ghbtns { position: relative; top: 4px; margin-left: 5px; }
11 a { color: #0077ff; }
12 </style>
13 </head>
14 <body>
15
16 <p>
17 A 10,000-point demo of <a href="https://github.com/Leaflet/Leaflet.heat">Leaflet.heat</a>, a tiny and fast Leaflet heatmap plugin.
18 <iframe class="ghbtns" src="http://ghbtns.com/github-btn.html?user=Leaflet&amp;repo=Leaflet.heat&amp;type=watch&amp;count=true"
19 allowtransparency="true" frameborder="0" scrolling="0" width="90" height="20"></iframe>
20 </p>
21
22 <div id="map"></div>
23
24 <!-- <script src="../node_modules/simpleheat/simpleheat.js"></script>
25 <script src="../src/HeatLayer.js"></script> -->
26
27 <script src="../dist/leaflet-heat.js"></script>
28
29 <script src="http://leaflet.github.io/Leaflet.markercluster/example/realworld.10000.js"></script>
30 <script>
31
32 var map = L.map('map').setView([-37.87, 175.475], 12);
33
34 var tiles = L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', {
src/main/resources/leafletmap/Leaflet.heat/dist/leaflet-heat.js View file @ 79b0010
File was created 1 /*
2 (c) 2014, Vladimir Agafonkin
3 simpleheat, a tiny JavaScript library for drawing heatmaps with Canvas
4 https://github.com/mourner/simpleheat
5 */
6 !function(){"use strict";function t(i){return this instanceof t?(this._canvas=i="string"==typeof i?document.getElementById(i):i,this._ctx=i.getContext("2d"),this._width=i.width,this._height=i.height,this._max=1,void this.clear()):new t(i)}t.prototype={defaultRadius:25,defaultGradient:{.4:"blue",.6:"cyan",.7:"lime",.8:"yellow",1:"red"},data:function(t,i){return this._data=t,this},max:function(t){return this._max=t,this},add:function(t){return this._data.push(t),this},clear:function(){return this._data=[],this},radius:function(t,i){i=i||15;var a=this._circle=document.createElement("canvas"),s=a.getContext("2d"),e=this._r=t+i;return a.width=a.height=2*e,s.shadowOffsetX=s.shadowOffsetY=200,s.shadowBlur=i,s.shadowColor="black",s.beginPath(),s.arc(e-200,e-200,t,0,2*Math.PI,!0),s.closePath(),s.fill(),this},gradient:function(t){var i=document.createElement("canvas"),a=i.getContext("2d"),s=a.createLinearGradient(0,0,0,256);i.width=1,i.height=256;for(var e in t)s.addColorStop(e,t[e]);return a.fillStyle=s,a.fillRect(0,0,1,256),this._grad=a.getImageData(0,0,1,256).data,this},draw:function(t){this._circle||this.radius(this.defaultRadius),this._grad||this.gradient(this.defaultGradient);var i=this._ctx;i.clearRect(0,0,this._width,this._height);for(var a,s=0,e=this._data.length;e>s;s++)a=this._data[s],i.globalAlpha=Math.max(a[2]/this._max,t||.05),i.drawImage(this._circle,a[0]-this._r,a[1]-this._r);var n=i.getImageData(0,0,this._width,this._height);return this._colorize(n.data,this._grad),i.putImageData(n,0,0),this},_colorize:function(t,i){for(var a,s=3,e=t.length;e>s;s+=4)a=4*t[s],a&&(t[s-3]=i[a],t[s-2]=i[a+1],t[s-1]=i[a+2])}},window.simpleheat=t}(),/*
7 (c) 2014, Vladimir Agafonkin
8 Leaflet.heat, a tiny and fast heatmap plugin for Leaflet.
9 https://github.com/Leaflet/Leaflet.heat
10 */
11 L.HeatLayer=(L.Layer?L.Layer:L.Class).extend({initialize:function(t,i){this._latlngs=t,L.setOptions(this,i)},setLatLngs:function(t){return this._latlngs=t,this.redraw()},addLatLng:function(t){return this._latlngs.push(t),this.redraw()},setOptions:function(t){return L.setOptions(this,t),this._heat&&this._updateOptions(),this.redraw()},redraw:function(){return!this._heat||this._frame||this._map._animating||(this._frame=L.Util.requestAnimFrame(this._redraw,this)),this},onAdd:function(t){this._map=t,this._canvas||this._initCanvas(),t._panes.overlayPane.appendChild(this._canvas),t.on("moveend",this._reset,this),t.options.zoomAnimation&&L.Browser.any3d&&t.on("zoomanim",this._animateZoom,this),this._reset()},onRemove:function(t){t.getPanes().overlayPane.removeChild(this._canvas),t.off("moveend",this._reset,this),t.options.zoomAnimation&&t.off("zoomanim",this._animateZoom,this)},addTo:function(t){return t.addLayer(this),this},_initCanvas:function(){var t=this._canvas=L.DomUtil.create("canvas","leaflet-heatmap-layer leaflet-layer"),i=L.DomUtil.testProp(["transformOrigin","WebkitTransformOrigin","msTransformOrigin"]);t.style[i]="50% 50%";var a=this._map.getSize();t.width=a.x,t.height=a.y;var s=this._map.options.zoomAnimation&&L.Browser.any3d;L.DomUtil.addClass(t,"leaflet-zoom-"+(s?"animated":"hide")),this._heat=simpleheat(t),this._updateOptions()},_updateOptions:function(){this._heat.radius(this.options.radius||this._heat.defaultRadius,this.options.blur),this.options.gradient&&this._heat.gradient(this.options.gradient),this.options.max&&this._heat.max(this.options.max)},_reset:function(){var t=this._map.containerPointToLayerPoint([0,0]);L.DomUtil.setPosition(this._canvas,t);var i=this._map.getSize();this._heat._width!==i.x&&(this._canvas.width=this._heat._width=i.x),this._heat._height!==i.y&&(this._canvas.height=this._heat._height=i.y),this._redraw()},_redraw:function(){var t,i,a,s,e,n,h,o,r,d=[],_=this._heat._r,l=this._map.getSize(),m=new L.Bounds(L.point([-_,-_]),l.add([_,_])),c=void 0===this.options.max?1:this.options.max,u=void 0===this.options.maxZoom?this._map.getMaxZoom():this.options.maxZoom,f=1/Math.pow(2,Math.max(0,Math.min(u-this._map.getZoom(),12))),g=_/2,p=[],v=this._map._getMapPanePos(),w=v.x%g,y=v.y%g;for(t=0,i=this._latlngs.length;i>t;t++)if(a=this._map.latLngToContainerPoint(this._latlngs[t]),m.contains(a)){e=Math.floor((a.x-w)/g)+2,n=Math.floor((a.y-y)/g)+2;var x=void 0!==this._latlngs[t].alt?this._latlngs[t].alt:void 0!==this._latlngs[t][2]?+this._latlngs[t][2]:1;r=x*f,p[n]=p[n]||[],s=p[n][e],s?(s[0]=(s[0]*s[2]+a.x*r)/(s[2]+r),s[1]=(s[1]*s[2]+a.y*r)/(s[2]+r),s[2]+=r):p[n][e]=[a.x,a.y,r]}for(t=0,i=p.length;i>t;t++)if(p[t])for(h=0,o=p[t].length;o>h;h++)s=p[t][h],s&&d.push([Math.round(s[0]),Math.round(s[1]),Math.min(s[2],c)]);this._heat.data(d).draw(this.options.minOpacity),this._frame=null},_animateZoom:function(t){var i=this._map.getZoomScale(t.zoom),a=this._map._getCenterOffset(t.center)._multiplyBy(-i).subtract(this._map._getMapPanePos());L.DomUtil.setTransform?L.DomUtil.setTransform(this._canvas,a,i):this._canvas.style[L.DomUtil.TRANSFORM]=L.DomUtil.getTranslateString(a)+" scale("+i+")"}}),L.heatLayer=function(t,i){return new L.HeatLayer(t,i)};
src/main/resources/leafletmap/Leaflet.heat/package.json View file @ 79b0010
File was created 1 {
2 "name": "leaflet.heat",
3 "version": "0.2.0",
4 "description": "A tiny and fast Leaflet heatmap plugin.",
5 "homepage": "https://github.com/Leaflet/Leaflet.heat",
6 "keywords": [
7 "heatmap",
8 "canvas",
9 "visualization",
10 "gis",
11 "leaflet",
12 "plugin"
13 ],
14 "author": "Vladimir Agafonkin",
15 "repository": {
16 "type": "git",
17 "url": "git://github.com/Leaflet/Leaflet.heat.git"
18 },
19 "main": "dist/leaflet-heat.js",
20 "devDependencies": {
21 "eslint": "^1.7.3",
22 "eslint-config-mourner": "^1.0.1",
23 "simpleheat": "~0.2.0",
24 "uglify-js": "^2.5.0"
25 },
26 "eslintConfig": {
27 "extends": "mourner",
28 "globals": {
29 "L": false,
30 "simpleheat": false
31 }
32 },
33 "scripts": {
34 "test": "eslint src",
35 "prepublish": "uglifyjs node_modules/simpleheat/simpleheat.js src/HeatLayer.js -c -m -o dist/leaflet-heat.js"
36 },
37 "license": "BSD-2-Clause",
38 "jshintConfig": {
39 "quotmark": "single",
40 "globals": {
41 "L": true,
42 "simpleheat": true
43 },
44 "trailing": true,
45 "camelcase": true,
46 "curly": true,
47 "eqeqeq": true,
48 "noempty": true,
49 "nonbsp": true,
50 "undef": true,
51 "unused": true,
52 "browser": true
53 }
54 }
src/main/resources/leafletmap/Leaflet.heat/src/HeatLayer.js View file @ 79b0010
File was created 1 'use strict';
2
3 L.HeatLayer = (L.Layer ? L.Layer : L.Class).extend({
4
5 // options: {
6 // minOpacity: 0.05,
7 // maxZoom: 18,
8 // radius: 25,
9 // blur: 15,
10 // max: 1.0
11 // },
12
13 initialize: function (latlngs, options) {
14 this._latlngs = latlngs;
15 L.setOptions(this, options);
16 },
17
18 setLatLngs: function (latlngs) {
19 this._latlngs = latlngs;
20 return this.redraw();
21 },
22
23 addLatLng: function (latlng) {
24 this._latlngs.push(latlng);
25 return this.redraw();
26 },
27
28 setOptions: function (options) {
29 L.setOptions(this, options);
30 if (this._heat) {
31 this._updateOptions();
32 }
33 return this.redraw();
34 },
35
36 redraw: function () {
37 if (this._heat && !this._frame && this._map && !this._map._animating) {
38 this._frame = L.Util.requestAnimFrame(this._redraw, this);
39 }
40 return this;
41 },
42
43 onAdd: function (map) {
44 this._map = map;
45
46 if (!this._canvas) {
47 this._initCanvas();
48 }
49
50 if (this.options.pane) {
51 this.getPane().appendChild(this._canvas);
52 }else{
53 map._panes.overlayPane.appendChild(this._canvas);
54 }
55
56 map.on('moveend', this._reset, this);
57
58 if (map.options.zoomAnimation && L.Browser.any3d) {
59 map.on('zoomanim', this._animateZoom, this);
60 }
61
62 this._reset();
63 },
64
65 onRemove: function (map) {
66 if (this.options.pane) {
67 this.getPane().removeChild(this._canvas);
68 }else{
69 map.getPanes().overlayPane.removeChild(this._canvas);
70 }
71
72 map.off('moveend', this._reset, this);
73
74 if (map.options.zoomAnimation) {
75 map.off('zoomanim', this._animateZoom, this);
76 }
77 },
78
79 addTo: function (map) {
80 map.addLayer(this);
81 return this;
82 },
83
84 _initCanvas: function () {
85 var canvas = this._canvas = L.DomUtil.create('canvas', 'leaflet-heatmap-layer leaflet-layer');
86
87 var originProp = L.DomUtil.testProp(['transformOrigin', 'WebkitTransformOrigin', 'msTransformOrigin']);
88 canvas.style[originProp] = '50% 50%';
89
90 var size = this._map.getSize();
91 canvas.width = size.x;
92 canvas.height = size.y;
93
94 var animated = this._map.options.zoomAnimation && L.Browser.any3d;
95 L.DomUtil.addClass(canvas, 'leaflet-zoom-' + (animated ? 'animated' : 'hide'));
96
97 this._heat = simpleheat(canvas);
98 this._updateOptions();
99 },
100
101 _updateOptions: function () {
102 this._heat.radius(this.options.radius || this._heat.defaultRadius, this.options.blur);
103
104 if (this.options.gradient) {
105 this._heat.gradient(this.options.gradient);
106 }
107 if (this.options.max) {
108 this._heat.max(this.options.max);
109 }
110 },
111
112 _reset: function () {
113 var topLeft = this._map.containerPointToLayerPoint([0, 0]);
114 L.DomUtil.setPosition(this._canvas, topLeft);
115
116 var size = this._map.getSize();
117
118 if (this._heat._width !== size.x) {
119 this._canvas.width = this._heat._width = size.x;
120 }
121 if (this._heat._height !== size.y) {
122 this._canvas.height = this._heat._height = size.y;
123 }
124
125 this._redraw();
126 },
127
128 _redraw: function () {
129 if (!this._map) {
130 return;
131 }
132 var data = [],
133 r = this._heat._r,
134 size = this._map.getSize(),
135 bounds = new L.Bounds(
136 L.point([-r, -r]),
137 size.add([r, r])),
138
139 max = this.options.max === undefined ? 1 : this.options.max,
140 maxZoom = this.options.maxZoom === undefined ? this._map.getMaxZoom() : this.options.maxZoom,
141 v = 1 / Math.pow(2, Math.max(0, Math.min(maxZoom - this._map.getZoom(), 12))),
142 cellSize = r / 2,
143 grid = [],
144 panePos = this._map._getMapPanePos(),
145 offsetX = panePos.x % cellSize,
146 offsetY = panePos.y % cellSize,
147 i, len, p, cell, x, y, j, len2, k;
148
149 // console.time('process');
150 for (i = 0, len = this._latlngs.length; i < len; i++) {
151 p = this._map.latLngToContainerPoint(this._latlngs[i]);
152 if (bounds.contains(p)) {
153 x = Math.floor((p.x - offsetX) / cellSize) + 2;
154 y = Math.floor((p.y - offsetY) / cellSize) + 2;
155
156 var alt =
157 this._latlngs[i].alt !== undefined ? this._latlngs[i].alt :
158 this._latlngs[i][2] !== undefined ? +this._latlngs[i][2] : 1;
159 k = alt * v;
160
161 grid[y] = grid[y] || [];
162 cell = grid[y][x];
163
164 if (!cell) {
165 grid[y][x] = [p.x, p.y, k];
166
167 } else {
168 cell[0] = (cell[0] * cell[2] + p.x * k) / (cell[2] + k); // x
169 cell[1] = (cell[1] * cell[2] + p.y * k) / (cell[2] + k); // y
170 cell[2] += k; // cumulated intensity value
171 }
src/main/resources/leafletmap/leafletmap.html View file @ 79b0010
<!DOCTYPE html> 1 1 <!DOCTYPE html>
<html> 2 2 <html>
<head> 3 3 <head>
<title>Leaflet Map</title> 4 4 <title>Leaflet Map</title>
5 5
<meta charset="utf-8" /> 6 6 <meta charset="utf-8" />
<!-- only needed for mobile browsers: disable unwanted scaling and use full width --> 7 7 <!-- only needed for mobile browsers: disable unwanted scaling and use full width -->
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" /> 8 8 <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
9 9
<!-- force the map to use the full available space --> 10 10 <!-- force the map to use the full available space -->
<style> 11 11 <style>
html, body, #map { 12 12 html, body, #map {
height: 100%; 13 13 height: 100%;
width: 100%; 14 14 width: 100%;
padding: 0px; 15 15 padding: 0px;
margin: 0px; 16 16 margin: 0px;
} 17 17 }
</style> 18 18 </style>
19 19
<!-- for remote access of the Leaflet library 20 20 <!-- for remote access of the Leaflet library
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" /> 21 21 <link rel="stylesheet" href="https://unpkg.com/leaflet@1.0.3/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet.js"></script> --> 22 22 <script src="https://unpkg.com/leaflet@1.0.3/dist/leaflet.js"></script> -->
23 23
<link rel="stylesheet" href="leaflet/leaflet.css" /> 24 24 <link rel="stylesheet" href="leaflet/leaflet.css" />
<link rel="stylesheet" href="Leaflet.markercluster-1.4.1/dist/MarkerCluster.css" /> 25 25 <link rel="stylesheet" href="Leaflet.markercluster-1.4.1/dist/MarkerCluster.css" />
<link rel="stylesheet" href="Leaflet.markercluster-1.4.1/dist/MarkerCluster.Default.css" /> 26 26 <link rel="stylesheet" href="Leaflet.markercluster-1.4.1/dist/MarkerCluster.Default.css" />
27 27
<script src="leaflet/leaflet.js"></script> 28 28 <script src="leaflet/leaflet.js"></script>
<script src="leaflet/leaflet.rotatedMarker.js"></script> 29 29 <script src="leaflet/leaflet.rotatedMarker.js"></script>
<script src="Leaflet.markercluster-1.4.1/dist/leaflet.markercluster-src.js"></script> 30 30 <script src="Leaflet.markercluster-1.4.1/dist/leaflet.markercluster-src.js"></script>
31 <script src="Leaflet.heat/dist/leaflet-heat.js"></script>
31 32