2021-01-04 04:06:21 +01:00
/ * *
* The data layer shows all the given geojson elements with the appropriate icon etc
* /
2021-09-21 02:10:42 +02:00
import { UIEventSource } from "../../Logic/UIEventSource" ;
import LayerConfig from "../../Models/ThemeConfig/LayerConfig" ;
import FeatureInfoBox from "../Popup/FeatureInfoBox" ;
import State from "../../State" ;
import { ShowDataLayerOptions } from "./ShowDataLayerOptions" ;
2021-10-13 17:18:14 +02:00
import { FixedUiElement } from "../Base/FixedUiElement" ;
2021-01-04 04:06:21 +01:00
export default class ShowDataLayer {
private readonly _leafletMap : UIEventSource < L.Map > ;
2021-06-23 02:15:28 +02:00
private readonly _enablePopups : boolean ;
2021-08-07 21:19:01 +02:00
private readonly _features : UIEventSource < { feature : any } [ ] >
2021-09-21 01:47:58 +02:00
private readonly _layerToShow : LayerConfig ;
2021-03-22 00:28:21 +01:00
2021-09-21 01:47:58 +02:00
// Used to generate a fresh ID when needed
private _cleanCount = 0 ;
2021-09-21 03:10:15 +02:00
private geoLayer = undefined ;
2021-09-27 14:45:48 +02:00
private isDirty = false ;
2021-09-21 03:10:15 +02:00
/ * *
* If the selected element triggers , this is used to lookup the correct layer and to open the popup
* Used to avoid a lot of callbacks on the selected element
2021-10-01 05:24:10 +02:00
*
2021-10-01 04:49:40 +02:00
* Note : the key of this dictionary is 'feature.properties.id+features.geometry.type' as one feature might have multiple presentations
2021-09-21 03:10:15 +02:00
* @private
* /
private readonly leafletLayersPerId = new Map < string , { feature : any , leafletlayer : any } > ( )
2021-10-13 17:18:14 +02:00
private readonly showDataLayerid : number ;
private static dataLayerIds = 0
2021-09-21 03:10:15 +02:00
constructor ( options : ShowDataLayerOptions & { layerToShow : LayerConfig } ) {
2021-09-21 01:47:58 +02:00
this . _leafletMap = options . leafletMap ;
2021-10-13 17:18:14 +02:00
this . showDataLayerid = ShowDataLayer . dataLayerIds ;
ShowDataLayer . dataLayerIds ++
2021-09-21 01:47:58 +02:00
this . _enablePopups = options . enablePopups ? ? true ;
2021-09-21 03:10:15 +02:00
if ( options . features === undefined ) {
2021-09-21 01:47:58 +02:00
throw "Invalid ShowDataLayer invocation"
}
const features = options . features . features . map ( featFreshes = > featFreshes . map ( ff = > ff . feature ) ) ;
2021-06-24 01:17:29 +02:00
this . _features = features ;
2021-09-21 01:47:58 +02:00
this . _layerToShow = options . layerToShow ;
2021-01-04 04:06:21 +01:00
const self = this ;
2021-02-06 00:05:38 +01:00
2021-09-27 14:45:48 +02:00
options . leafletMap . addCallbackAndRunD ( _ = > {
self . update ( options )
}
) ;
2021-09-22 05:02:09 +02:00
features . addCallback ( _ = > self . update ( options ) ) ;
2021-09-27 14:45:48 +02:00
options . doShowLayer ? . addCallbackAndRun ( doShow = > {
const mp = options . leafletMap . data ;
if ( mp == undefined ) {
return ;
}
if ( doShow ) {
if ( self . isDirty ) {
self . update ( options )
} else {
mp . addLayer ( this . geoLayer )
}
} else {
2021-10-01 05:24:10 +02:00
if ( this . geoLayer !== undefined ) {
2021-09-27 14:45:48 +02:00
mp . removeLayer ( this . geoLayer )
}
}
} )
2021-02-06 00:05:38 +01:00
2021-10-01 05:24:10 +02:00
2021-09-21 03:10:15 +02:00
State . state . selectedElement . addCallbackAndRunD ( selected = > {
if ( self . _leafletMap . data === undefined ) {
2021-01-04 04:06:21 +01:00
return ;
}
2021-10-01 05:24:10 +02:00
const v = self . leafletLayersPerId . get ( selected . properties . id + selected . geometry . type )
2021-09-26 17:36:39 +02:00
if ( v === undefined ) {
return ;
}
2021-09-21 03:10:15 +02:00
const leafletLayer = v . leafletlayer
const feature = v . feature
if ( leafletLayer . getPopup ( ) . isOpen ( ) ) {
return ;
2021-02-14 19:45:02 +01:00
}
2021-09-29 01:12:29 +02:00
if ( selected . properties . id !== feature . properties . id ) {
return ;
}
2021-10-01 05:24:10 +02:00
2021-09-29 01:12:29 +02:00
if ( feature . id !== feature . properties . id ) {
// Probably a feature which has renamed
2021-10-01 05:24:10 +02:00
// the feature might have as id 'node/-1' and as 'feature.properties.id' = 'the newly assigned id'. That is no good too
2021-10-01 04:49:40 +02:00
console . log ( "Not opening the popup for" , feature , "as probably renamed" )
2021-09-29 01:12:29 +02:00
return ;
}
if ( selected . geometry . type === feature . geometry . type // If a feature is rendered both as way and as point, opening one popup might trigger the other to open, which might trigger the one to open again
) {
console . log ( "Opening popup of feature" , feature )
leafletLayer . openPopup ( )
2021-06-24 01:17:29 +02:00
}
2021-09-21 03:10:15 +02:00
} )
2021-09-26 17:36:39 +02:00
2021-09-21 03:10:15 +02:00
}
2021-09-27 14:45:48 +02:00
private update ( options : ShowDataLayerOptions ) {
2021-09-21 03:10:15 +02:00
if ( this . _features . data === undefined ) {
return ;
}
2021-09-27 14:45:48 +02:00
this . isDirty = true ;
if ( options ? . doShowLayer ? . data === false ) {
return ;
}
2021-09-21 03:10:15 +02:00
const mp = options . leafletMap . data ;
if ( mp === undefined ) {
return ;
}
this . _cleanCount ++
// clean all the old stuff away, if any
if ( this . geoLayer !== undefined ) {
mp . removeLayer ( this . geoLayer ) ;
}
2021-09-27 14:45:48 +02:00
const self = this ;
const data = {
type : "FeatureCollection" ,
features : [ ]
}
// @ts-ignore
this . geoLayer = L . geoJSON ( data , {
style : feature = > self . createStyleFor ( feature ) ,
pointToLayer : ( feature , latLng ) = > self . pointToLayer ( feature , latLng ) ,
onEachFeature : ( feature , leafletLayer ) = > self . postProcessFeature ( feature , leafletLayer )
} ) ;
2021-09-21 03:10:15 +02:00
const allFeats = this . _features . data ;
for ( const feat of allFeats ) {
if ( feat === undefined ) {
continue
2021-08-07 21:19:01 +02:00
}
2021-09-26 17:36:39 +02:00
try {
2021-09-22 05:02:09 +02:00
this . geoLayer . addData ( feat ) ;
2021-09-26 17:36:39 +02:00
} catch ( e ) {
2021-09-22 05:02:09 +02:00
console . error ( "Could not add " , feat , "to the geojson layer in leaflet" )
}
2021-01-04 04:06:21 +01:00
}
2021-09-21 03:10:15 +02:00
if ( options . zoomToFeatures ? ? false ) {
try {
mp . fitBounds ( this . geoLayer . getBounds ( ) , { animate : false } )
} catch ( e ) {
console . error ( e )
}
}
2021-09-26 17:36:39 +02:00
if ( options . doShowLayer ? . data ? ? true ) {
mp . addLayer ( this . geoLayer )
}
2021-09-27 14:45:48 +02:00
this . isDirty = false ;
2021-01-04 04:06:21 +01:00
}
private createStyleFor ( feature ) {
2021-03-25 15:19:44 +01:00
const tagsSource = State . state . allElements . addOrGetElement ( feature ) ;
2021-01-04 04:06:21 +01:00
// Every object is tied to exactly one layer
2021-09-21 03:10:15 +02:00
const layer = this . _layerToShow
2021-09-21 01:47:58 +02:00
return layer ? . GenerateLeafletStyle ( tagsSource , true ) ;
2021-01-04 04:06:21 +01:00
}
2021-02-15 19:06:06 +01:00
2021-01-04 04:06:21 +01:00
private pointToLayer ( feature , latLng ) : L . Layer {
// Leaflet cannot handle geojson points natively
// We have to convert them to the appropriate icon
// Click handling is done in the next step
2021-09-21 03:10:15 +02:00
const layer : LayerConfig = this . _layerToShow
2021-02-14 19:45:02 +01:00
if ( layer === undefined ) {
return ;
}
2021-09-29 01:12:29 +02:00
let tagSource = State . state . allElements . getEventSourceById ( feature . properties . id )
2021-10-01 05:24:10 +02:00
if ( tagSource === undefined ) {
tagSource = new UIEventSource < any > ( feature . properties )
2021-09-29 01:12:29 +02:00
}
2021-09-22 05:02:09 +02:00
const clickable = ! ( layer . title === undefined && ( layer . tagRenderings ? ? [ ] ) . length === 0 )
const style = layer . GenerateLeafletStyle ( tagSource , clickable ) ;
2021-06-23 02:41:30 +02:00
const baseElement = style . icon . html ;
2021-06-24 02:34:13 +02:00
if ( ! this . _enablePopups ) {
2021-06-23 02:41:30 +02:00
baseElement . SetStyle ( "cursor: initial !important" )
}
2021-01-04 04:06:21 +01:00
return L . marker ( latLng , {
icon : L.divIcon ( {
2021-06-23 02:41:30 +02:00
html : baseElement.ConstructElement ( ) ,
2021-01-04 04:06:21 +01:00
className : style.icon.className ,
iconAnchor : style.icon.iconAnchor ,
2021-09-22 05:02:09 +02:00
iconUrl : style.icon.iconUrl ? ? "./assets/svg/bug.svg" ,
2021-01-04 04:06:21 +01:00
popupAnchor : style.icon.popupAnchor ,
iconSize : style.icon.iconSize
} )
} ) ;
}
2021-07-18 21:37:14 +02:00
2021-09-21 01:47:58 +02:00
/ * *
* POst processing - basically adding the popup
* @param feature
* @param leafletLayer
* @private
* /
2021-01-04 18:55:10 +01:00
private postProcessFeature ( feature , leafletLayer : L.Layer ) {
2021-09-21 01:47:58 +02:00
const layer : LayerConfig = this . _layerToShow
2021-06-23 02:41:30 +02:00
if ( layer . title === undefined || ! this . _enablePopups ) {
2021-01-04 04:06:21 +01:00
// No popup action defined -> Don't do anything
2021-06-23 02:41:30 +02:00
// or probably a map in the popup - no popups needed!
2021-06-23 02:15:28 +02:00
return ;
}
2021-06-24 02:34:13 +02:00
2021-01-04 04:06:21 +01:00
const popup = L . popup ( {
autoPan : true ,
closeOnEscapeKey : true ,
2021-07-18 21:48:11 +02:00
closeButton : false ,
2021-08-07 21:19:01 +02:00
autoPanPaddingTopLeft : [ 15 , 15 ] ,
2021-01-04 04:06:21 +01:00
} , leafletLayer ) ;
2021-06-15 16:18:58 +02:00
leafletLayer . bindPopup ( popup ) ;
let infobox : FeatureInfoBox = undefined ;
2021-10-13 17:18:14 +02:00
const id = ` popup- ${ feature . properties . id } - ${ feature . geometry . type } - ${ this . showDataLayerid } - ${ this . _cleanCount } `
popup . setContent ( ` <div style='height: 65vh' id=' ${ id } '>Popup for ${ feature . properties . id } ${ feature . geometry . type } ${ id } is loading</div> ` )
2021-02-05 18:58:06 +01:00
leafletLayer . on ( "popupopen" , ( ) = > {
2021-06-15 16:18:58 +02:00
if ( infobox === undefined ) {
2021-06-14 17:28:11 +02:00
const tags = State . state . allElements . getEventSourceById ( feature . properties . id ) ;
infobox = new FeatureInfoBox ( tags , layer ) ;
infobox . isShown . addCallback ( isShown = > {
if ( ! isShown ) {
State . state . selectedElement . setData ( undefined ) ;
2021-06-15 16:18:58 +02:00
leafletLayer . closePopup ( )
2021-06-14 17:28:11 +02:00
}
} ) ;
}
infobox . AttachTo ( id )
2021-07-18 21:37:14 +02:00
infobox . Activate ( ) ;
2021-10-02 22:31:16 +02:00
if ( State . state ? . selectedElement ? . data ? . properties ? . id !== feature . properties . id ) {
State . state . selectedElement . setData ( feature )
}
2021-10-01 05:24:10 +02:00
2021-03-22 00:28:21 +01:00
} ) ;
2021-06-15 16:18:58 +02:00
2021-09-26 17:36:39 +02:00
2021-09-21 03:10:15 +02:00
// Add the feature to the index to open the popup when needed
2021-10-01 05:24:10 +02:00
this . leafletLayersPerId . set ( feature . properties . id + feature . geometry . type , {
feature : feature ,
leafletlayer : leafletLayer
} )
2021-09-26 17:36:39 +02:00
2021-01-04 04:06:21 +01:00
}
}