331 lines
10 KiB
JavaScript
331 lines
10 KiB
JavaScript
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
|
|
|
|
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)
|
|
// }
|
|
// }
|
|
|
|
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) {
|
|
let oldMap = this.map
|
|
if (oldMap) oldMap.unload()
|
|
|
|
this._map = map
|
|
|
|
this.map.load()
|
|
this.scatterContainer.addChild(this.map.image)
|
|
|
|
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
|