import { GeoMap } from './map.js' import MapView from './mapview.js' import { EventHandler } from './utils.js' import { PIXILayer } from '../../../../src/layers/js/layer.js' import Logging from '../../logging.js' import { GeoGraphics } from './geographics.js' //import { GeoGraphics } from "../pixi/geographics.js" /** * The GeoLayer is a special PIXILayer, that recognizes other GeoLayers and * GeoGraphics. The layer can be adapted to a map and notifies all Geo-Children * of the Adaption. */ export class GeoLayer { constructor(displayObject) { if (displayObject == null || !(displayObject instanceof PIXI.DisplayObject)) { console.error( `You need to provide a displayObject to make a ${this.constructor.name} out of it.`, displayObject ) return null } else { this.geographics = [] displayObject.map = this this.displayObject = displayObject this.layers = [] this.pixiAddChild = displayObject.addChild.bind(displayObject) displayObject.addChild = (...elements) => { elements.forEach(element => { if (element instanceof GeoGraphics) { this.geographics.push(element) this.pixiAddChild(element.graphics) } else { this.pixiAddChild(element) } }) } } } addChild(element) { this.displayObject.addChild(element) } /** * Adapts to a map. If the maplayer should adapt to the parent maplayer, * no parameter must be specified. */ adapt(map = null) { if (!map) map = this.map if (map) { this.geographics.forEach(geographic => { geographic.adaptTo(map) }) this.layers.forEach(layer => { if (layer.adapt) layer.adapt(map) }) } else console.error('There was no map specified.', this) } // place(geographic) { // if (geographic.constructor.name.startsWith('Geo') && geographic.graphics) { // // Fix to remove the rollupjs circular dependency // //if (geographic instanceof GeoGraphics) { // this.geographics.push(geographic) // super.place(geographic.graphics) // } else super.place(geographic) // } addLayer(layer) { if (layer instanceof GeoLayer || layer instanceof MapLayer) { this.layers.push(layer) this.displayObject.addChild(layer.displayObject) if (this.map) layer.geographics.forEach(geographics => geographics.adaptTo(this.map)) } else console.error('Could not place layer. Only MapLayer and GeoLayers can be child layers of GeoLayers.', layer) } //GeoLayers have to be children of a map layer, // therefore we can recursively get the map. get map() { return this._mapLayer.map } get mapLayer() { return this._mapLayer ? this._mapLayer : this.parent.mapLayer } // clone(mapLayerClone) { // const opts = { // mapLayer: mapLayerClone, // map: mapLayerClone.map // } // let geoLayerClone = new GeoLayer(opts) // this.layers.forEach(layer => { // let layerClone = layer.clone(opts) // if (layerClone) { // geoLayerClone.placeLayer(layerClone) // } // }) // this.geographics.forEach(geographics => { // let clone = geographics.clone() // if (clone) { // geoLayerClone.place(clone) // } // }) // return geoLayerClone // } } export class MapLayer extends GeoLayer { constructor( scatterContainer, displayObject, { onTransform = null, onChange = null, focus = null, zoom = null, viewport = null } = {} ) { super(displayObject) let onTransformListeners = [ () => { this.labelVisibility() } ] if (onTransform) onTransformListeners.push(onTransform) this.transformHandler = new EventHandler('onTransform', { listeners: onTransformListeners }) this.scatterContainer = scatterContainer this.changeHandler = new EventHandler('onChange', { listeners: onChange }) this.mapview = new MapView({ zoom, focus, viewport }) this._map = null // //TODO Implement error handling here. // this.maps = maps // if (opts.map) this.placeMap(opts.map) this.dynamicElements = new Map() } labelVisibility() { const visibility = this.childrenVisibility if (visibility) { const zoom = this.mapview.zoom const min = visibility.min || 0 const max = visibility.max || Number.MAX_VALUE // TODO // Currently I dont know what elemnts was. // We just log an error and resolve this on a later point. if (this.elements) { if (zoom > min && zoom < max) { this.elements.forEach(it => (it.visible = true)) this.elements.forEach(it => { const scale = 1 / it.parent.scale.x // it.children are poi groups // it.children[0] is the poi group of the tübingen poi // it.children[0].children are the text containers (not PIXI.Text), about 20 pieces if (it.children.length > 0) { it.children[0].children.forEach(poi => { if (poi.children.length === 1) { poi.scale.set(scale, scale) } }) } }) } else { this.elements.forEach(it => (it.visible = false)) } }else{ console.error("DEBUG: Elements was not defined.") } } } adapt() { this.layers.forEach(layer => { if (layer.adapt) layer.adapt(this.map) }) } // placeLayer(layer) { // super.placeLayer(layer) // if (layer instanceof GeoLayer && this.map) { // layer.adapt(this.map) // } // } transformed(e) { this.mapview.transformed(this.map) this.transformHandler.call(this) } clone(container = null) { let clone = {} for (let name of Object.keys(this.maps)) { //console.info(this.maps[name]) clone[name] = this.maps[name].clone(container) } //console.info(this.active) let mapLayerClone = new MapLayer(this.active, clone, container, { name: MapLayer.idx++, viewport: this.mapview.viewport, focus: this.mapview.focus, zoom: this.mapview.zoom }) //mapLayerClone._map = clone['luftbild'] mapLayerClone.childrenVisibility = this.childrenVisibility return mapLayerClone } /** * Changes the map to the specified one, keeping the position and the zoom of the old map. * * @public * @param {GeoMap} map * @memberof MapLayer */ changeMap( map /*, useScatterAsContainer = true // If set to false, the normal container is used. This is necessary when using submaps and the container need to be a RigidContainer.*/ ) { if (map instanceof GeoMap) { console.log("Change map to: " , map) let oldMap = this.map if (oldMap) oldMap.unload() this._map = map this.map.load() this.scatterContainer.addChild(this.map.image) console.log(this.map.image.parent) map.image.addChild(this.displayObject) this.mapview.update(this.map) // A geolayer's displayObject is on the parent layer. // A maplayer's displayobject is always the child of the map. this.adapt() this.changeHandler.call(this, map, oldMap) //Call transform one time manually. this.transformed() this.map.onTransform.add(this.transformed.bind(this)) } else { console.error("Could not set map, it's not of type GeoMap.", map) } /*Logging.log(`Map change: ${key}`) if (this.active !== key) { if (this.maps.hasOwnProperty(key)) { let old = this.map ? this.map : null this._map = this.maps[key] this._map.name = key this.active = key let container = useScatterAsContainer ? this.scatterContainer : this.container this.map.load(container) // Copies all layers. this.layers.forEach(layer => { if (old) this.map.image.addChild(layer.container) }) this.placeMap(this.map) /** * TODO: Improve * * I'm quite sure if I made a design mistake here. * In an earlier version I did not need to migrate the * layers manually from the map to the next map. * * I believe the old version was a container next to the * map, which got updated on transform. * * -SO */ /* if (old) old.unload() } else { let keys = Object.keys(this.maps) if (keys.length == 0) console.error('There is no map set for the map layer!') else { let fallbackMap = keys[0] console.error( `A map with the key (${key}) does not exists within the mapapp. Fallback to map: ${fallbackMap}.` ) this.changeMap(fallbackMap, { useScatterAsContainer }) } } }*/ } get map() { return this._map } /** * This is required for the consistency of georelated layers. * The request traverses up to the mapLayer where it then returns * the responsible map layer. */ get mapLayer() { return this } } MapLayer.idx = 0