iwmlib/lib/pixi/maps/geolayer.js

303 lines
9.0 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 "../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 extends PIXILayer {
constructor(opts = {}) {
super(opts)
/**
* When setting the map and mapLayer with the options paramter.
* The GeoLayer becomes a RootLayer, when the root layer should not be a MapLayer.
*/
if (opts.map) this._map = opts.map
if (opts.map) this._mapLayer = opts.mapLayer
this.geographics = []
}
/**
* 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)
}
placeLayer(layer) {
if (layer instanceof GeoLayer || layer instanceof MapLayer) {
super.placeLayer(layer)
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._map ? this._map : this.parent.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
}
}
/**
* TODO: It may be a good idea to inherit maplayer from geo layer.
*/
export class MapLayer extends PIXILayer {
constructor(active, maps, scatterContainer, opts = {}) {
super(
Object.assign(
{
container: new PIXI.Container()
},
opts
)
)
this.opts = opts
this.transformHandler = new EventHandler('onTransform', {
listeners: () => {
this.labelVisibility()
}
})
this.scatterContainer = scatterContainer
if (!maps[active]) console.error('No map was set!')
else opts.map = maps[active]
this.mapview = new MapView(opts)
this.changeHandler = new EventHandler('onChange', {
listeners: opts.onChange
})
//TODO Implement error handling here.
this.maps = maps
this.changeMap(active)
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.setMap(this.map)
this.map.image.addChild(this.container)
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(e)
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(
key,
{
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.
} = {}
) {
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()
this.changeHandler.call(this, old)
} 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