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 { displayObject.map = this this.displayObject = displayObject this.layers = [] this.geographics = [] let pixiAddChild = displayObject.addChild.bind(displayObject) displayObject.addChild = (...elements) => { elements.forEach(element => { if (element instanceof GeoGraphics) { this.geographics.push(element) pixiAddChild(element.graphics) } else { pixiAddChild(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 MapList { constructor(active, maps) { this.maps = maps this.active = active if (Object.keys(maps).length > 0) this.select(active) } /** * Selects a map from the map list. * * @param {string} active - Name of the map to select. * @returns * @memberof MapList */ select(active) { let map = null if (active !== this.active) { let keys = Object.keys(this.maps) if (keys.length > 0) { if (this.maps[active] == null) { let altActive = keys[0] console.warn( `The MapList does not contain the provided active key '${active}'. Used '${altActive}' as fallback.` ) active = altActive } if (this.active !== active) { this.active = active map = this.maps[active] } } else { console.error(`Could not provide a fallback map! The map object is empty.`) } } return map } add(key, map) { if (this.maps[key] != null) consol.warn('Key already in mapList. The existing key was overwritten.') this.maps[key] = map } get map() { console.log(this.maps, this.active) return this.maps[this.active] } } 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 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)) } } } 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) // } // } placeMap(map) { if (map instanceof GeoMap) { this.scatterContainer.addChild(map.image) this.map.onTransform.add(this.transformed.bind(this)) this.mapview.update(this.map) this.adapt() } else { console.error("Could not set map, it's not of type GeoMap.", map) } } placeElement(elem) { if (elem instanceof PIXI.DisplayObject) { this.map.image.addChild(elem) this.elements.push(elem) } else { console.error('Element need to be of type PIXI.DisplayObject.', elem) } } 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 } 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. ) { let oldMap = map this._map = map if (oldMap) oldMap.unload() map.load() this.changeHandler.call(this, map, oldMap) this.placeMap(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