iwmlib/lib/pixi/maps/geolayer.js

385 lines
11 KiB
JavaScript
Raw Normal View History

import { GeoMap } from './map.js'
import MapView from './mapview.js'
import { EventHandler } from './utils.js'
import { GeoGraphics } from './geographics.js'
import { MapList } from './maplist.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, opts = {}) {
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 {
2019-11-05 11:07:36 +01:00
this.geographics = []
// displayObject.map = this
this.displayObject = displayObject
2019-11-05 11:07:36 +01:00
this.pixiAddChild = displayObject.addChild.bind(displayObject)
displayObject.addChild = (...elements) => {
elements.forEach(element => {
if (element instanceof GeoGraphics) {
element.setLayer(this)
this.geographics.push(element)
2019-11-05 11:07:36 +01:00
this.pixiAddChild(element.graphics)
} else {
2019-11-05 11:07:36 +01:00
this.pixiAddChild(element)
}
})
}
}
this.name = opts['name'] != null ? opts.name : 'Unnamed Layer'
this.parent = null
this.parentMapLayerTransformedHandler = new EventHandler('onParentMapLayerTransformed')
this.layers = []
this._visibility = { min: 0, max: Number.MAX_VALUE }
}
parentMapLayerTransformed(mapLayer) {
this.layers.forEach(layer => {
if (!(layer instanceof MapList)) {
layer.parentMapLayerTransformed()
}
})
this.parentMapLayerTransformedHandler.call(null, mapLayer)
this.rescaleChildren()
}
rescaleChildren() {
let map = this.map
if (this.rescale) {
if (map != null) {
let scale = map.image.scatter.scale
this.displayObject.children.forEach(graphics => {
graphics.scale.set(1 / scale, 1 / scale)
})
}
}
let mapLayer = this.mapLayer
if (this.visibility && mapLayer != null) {
const zoom = mapLayer.mapview.zoom
// TODO
// Currently I dont know what elemnts was.
// We just log an error and resolve this on a later point.
if (zoom > this.visibility.min && zoom < this.visibility.max) {
this.displayObject.children.forEach(it => (it.visible = true))
} else {
this.displayObject.children.forEach(it => (it.visible = false))
}
}
}
set visibility(value) {
let { min = 0, max = Infinity } = value
console.log(min)
this._visibility = { min, max }
}
get visibility() {
return this._visibility
}
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)
})
this.rescaleChildren()
} 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)
// }
removeFromParent() {
if (this.parent) {
this.parent.removeLayer(this)
}
}
removeLayer(layer) {
let idx = this.layers.indexOf(layer)
if (idx != -1) {
layer.parent = null
this.layers.splice(idx, 1)
if (layer.displayObject.parent) {
layer.displayObject.parent.removeChild(layer.displayObject)
}
} else console.warn('Tried to remove layer that was not set.', this, layer)
}
set parent(parent) {
this._parent = parent
}
get parent() {
return this._parent
}
addLayer(layer) {
if (layer instanceof GeoLayer || layer instanceof MapLayer) {
layer.removeFromParent()
this.layers.push(layer)
layer.parent = this
layer.parentChanged()
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)
}
parentChanged() {
this.rescaleChildren()
}
//GeoLayers have to be children of a map layer,
// therefore we can recursively get the map.
get map() {
return this.mapLayer ? this.mapLayer.map : null
}
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, mapList = null } = {}
) {
super(displayObject)
this.transformHandler = new EventHandler('onTransform', {
listeners: onTransform
})
this.scatterContainer = scatterContainer
this.changeHandler = new EventHandler('onChange', {
listeners: onChange
})
this.mapview = new MapView({
zoom,
focus,
viewport
})
this.mapList = mapList
this._map = null
// //TODO Implement error handling here.
// this.maps = maps
// if (opts.map) this.placeMap(opts.map)
this.dynamicElements = new Map()
}
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.layers.forEach(layer => layer.parentMapLayerTransformed(this))
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
}
2019-11-05 11:07:36 +01:00
/**
* Changes the map to the specified one, keeping the position and the zoom of the old map.
*
* @public
* @param {GeoMap} map
* @memberof MapLayer
*/
changeMap(
2019-11-05 11:07:36 +01:00
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.*/
) {
2019-11-05 11:07:36 +01:00
if (map instanceof GeoMap) {
console.log('Change map to: ', map)
2019-11-05 11:07:36 +01:00
let oldMap = this.map
if (oldMap) oldMap.unload()
this._map = map
2019-11-05 11:07:36 +01:00
this.map.load()
this.scatterContainer.addChild(this.map.image)
this.mapview.apply(this.map)
2019-11-05 11:07:36 +01:00
map.image.addChild(this.displayObject)
2019-11-05 11:07:36 +01:00
// 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))
2019-11-05 11:07:36 +01:00
} 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