Continued refactoring of the maps library.

This commit is contained in:
Severin Opel 2019-11-13 12:42:06 +01:00
parent 58cbd44a7b
commit 285e41434a
10 changed files with 193 additions and 118 deletions

View File

@ -5,7 +5,10 @@
<meta charset='UTF-8'> <meta charset='UTF-8'>
<title>GeoGraphics</title> <title>GeoGraphics</title>
<link rel='stylesheet' href='../../3rdparty/highlight/styles/default.css'> <script src="../../3rdparty/highlight/highlight.pack.js"></script>
<link rel="stylesheet" href="../../../fonts/material-icon-font/material-icons.css">
<link rel='stylesheet' href='../../3rdparty/highlight/styles/vs2015.css'>
<link rel='stylesheet' href='../../../css/doctest.css'> <link rel='stylesheet' href='../../../css/doctest.css'>
<script src="../../../dist/iwmlib.3rdparty.js"></script> <script src="../../../dist/iwmlib.3rdparty.js"></script>
@ -67,11 +70,11 @@
wikimedia wikimedia
], (sprites) => { ], (sprites) => {
let osmMap = new ImageMap(sprites.get(osmworld), new MapData(new Projection.Mercator()), { cover: false }) let osmMap = new ImageMap(sprites.get(osmworld), new MapData(new Projection.Mercator()), { cover: true })
let wikimediaMap = new ImageMap(sprites.get(wikimedia), new MapData(new Projection.Robinson(10)), { let wikimediaMap = new ImageMap(sprites.get(wikimedia), new MapData(new Projection.Robinson(10)), {
baseZoomHeight: sprites.get(osmworld).texture.height, baseZoomHeight: sprites.get(osmworld).texture.height,
cover: false cover: true
}) })
app.addMaps({ app.addMaps({
@ -89,7 +92,6 @@
function enableSwitch(button, app) { function enableSwitch(button, app) {
button.addEventListener("click", () => { button.addEventListener("click", () => {
let next = app.mapList.next() let next = app.mapList.next()
console.log(app.mapList)
app.selectMap(next) app.selectMap(next)
}) })
} }

View File

@ -127,6 +127,14 @@ export class GeoGraphics {
return this._position return this._position
} }
get layer() {
return this._layer ? this._layer : null
}
setLayer(layer) {
this._layer = layer
}
// get map() { // get map() {
// if ( // if (
// this.graphics.layer && // this.graphics.layer &&

View File

@ -1,10 +1,8 @@
import { GeoMap } from './map.js' import { GeoMap } from './map.js'
import MapView from './mapview.js' import MapView from './mapview.js'
import { EventHandler } from './utils.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 './geographics.js'
import { MapList } from './maplist.js'
//import { GeoGraphics } from "../pixi/geographics.js" //import { GeoGraphics } from "../pixi/geographics.js"
/** /**
@ -13,7 +11,7 @@ import { GeoGraphics } from './geographics.js'
* of the Adaption. * of the Adaption.
*/ */
export class GeoLayer { export class GeoLayer {
constructor(displayObject) { constructor(displayObject, opts = {}) {
if (displayObject == null || !(displayObject instanceof PIXI.DisplayObject)) { if (displayObject == null || !(displayObject instanceof PIXI.DisplayObject)) {
console.error( console.error(
`You need to provide a displayObject to make a ${this.constructor.name} out of it.`, `You need to provide a displayObject to make a ${this.constructor.name} out of it.`,
@ -22,15 +20,14 @@ export class GeoLayer {
return null return null
} else { } else {
this.geographics = [] this.geographics = []
displayObject.map = this // displayObject.map = this
this.displayObject = displayObject this.displayObject = displayObject
this.layers = []
this.pixiAddChild = displayObject.addChild.bind(displayObject) this.pixiAddChild = displayObject.addChild.bind(displayObject)
displayObject.addChild = (...elements) => { displayObject.addChild = (...elements) => {
elements.forEach(element => { elements.forEach(element => {
if (element instanceof GeoGraphics) { if (element instanceof GeoGraphics) {
element.setLayer(this)
this.geographics.push(element) this.geographics.push(element)
this.pixiAddChild(element.graphics) this.pixiAddChild(element.graphics)
} else { } else {
@ -39,6 +36,62 @@ export class GeoLayer {
}) })
} }
} }
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) { addChild(element) {
@ -60,6 +113,8 @@ export class GeoLayer {
this.layers.forEach(layer => { this.layers.forEach(layer => {
if (layer.adapt) layer.adapt(map) if (layer.adapt) layer.adapt(map)
}) })
this.rescaleChildren()
} else console.error('There was no map specified.', this) } else console.error('There was no map specified.', this)
} }
@ -72,19 +127,54 @@ export class GeoLayer {
// } else super.place(geographic) // } 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) { addLayer(layer) {
if (layer instanceof GeoLayer || layer instanceof MapLayer) { if (layer instanceof GeoLayer || layer instanceof MapLayer) {
layer.removeFromParent()
this.layers.push(layer) this.layers.push(layer)
layer.parent = this
layer.parentChanged()
this.displayObject.addChild(layer.displayObject) this.displayObject.addChild(layer.displayObject)
if (this.map) layer.geographics.forEach(geographics => geographics.adaptTo(this.map)) if (this.map) layer.geographics.forEach(geographics => geographics.adaptTo(this.map))
} else } else
console.error('Could not place layer. Only MapLayer and GeoLayers can be child layers of GeoLayers.', layer) 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, //GeoLayers have to be children of a map layer,
// therefore we can recursively get the map. // therefore we can recursively get the map.
get map() { get map() {
return this._mapLayer.map return this.mapLayer ? this.mapLayer.map : null
} }
get mapLayer() { get mapLayer() {
@ -120,18 +210,12 @@ export class MapLayer extends GeoLayer {
constructor( constructor(
scatterContainer, scatterContainer,
displayObject, displayObject,
{ onTransform = null, onChange = null, focus = null, zoom = null, viewport = null } = {} { onTransform = null, onChange = null, focus = null, zoom = null, viewport = null, mapList = null } = {}
) { ) {
super(displayObject) super(displayObject)
let onTransformListeners = [
() => {
this.labelVisibility()
}
]
if (onTransform) onTransformListeners.push(onTransform)
this.transformHandler = new EventHandler('onTransform', { this.transformHandler = new EventHandler('onTransform', {
listeners: onTransformListeners listeners: onTransform
}) })
this.scatterContainer = scatterContainer this.scatterContainer = scatterContainer
@ -146,6 +230,7 @@ export class MapLayer extends GeoLayer {
viewport viewport
}) })
this.mapList = mapList
this._map = null this._map = null
// //TODO Implement error handling here. // //TODO Implement error handling here.
@ -154,46 +239,6 @@ export class MapLayer extends GeoLayer {
this.dynamicElements = new 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() { adapt() {
this.layers.forEach(layer => { this.layers.forEach(layer => {
if (layer.adapt) layer.adapt(this.map) if (layer.adapt) layer.adapt(this.map)
@ -209,6 +254,7 @@ export class MapLayer extends GeoLayer {
transformed(e) { transformed(e) {
this.mapview.transformed(this.map) this.mapview.transformed(this.map)
this.layers.forEach(layer => layer.parentMapLayerTransformed(this))
this.transformHandler.call(this) this.transformHandler.call(this)
} }
@ -243,8 +289,7 @@ export class MapLayer extends GeoLayer {
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.*/ 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) { if (map instanceof GeoMap) {
console.log('Change map to: ', map)
console.log("Change map to: " , map)
let oldMap = this.map let oldMap = this.map
if (oldMap) oldMap.unload() if (oldMap) oldMap.unload()
@ -252,10 +297,9 @@ export class MapLayer extends GeoLayer {
this.map.load() this.map.load()
this.scatterContainer.addChild(this.map.image) this.scatterContainer.addChild(this.map.image)
console.log(this.map.image.parent)
this.mapview.apply(this.map)
map.image.addChild(this.displayObject) map.image.addChild(this.displayObject)
this.mapview.update(this.map)
// A geolayer's displayObject is on the parent layer. // A geolayer's displayObject is on the parent layer.
// A maplayer's displayobject is always the child of the map. // A maplayer's displayobject is always the child of the map.

View File

@ -110,23 +110,26 @@
}) })
function ready(sprites) { function ready(sprites) {
const cover = true
// When resources are loaded, the ImageMap can be instantiated. // When resources are loaded, the ImageMap can be instantiated.
let imageMap = new ImageMap(sprites.get(europe), europeData, { let imageMap = new ImageMap(sprites.get(europe), europeData, {
coordsLogging: true, coordsLogging: true,
maxScale: 1, maxScale: 1,
cover: false cover
}) })
let testMapData = new DeepZoomMapData(new Projection.Mercator(), testConfig.tiles, { let testMapData = new DeepZoomMapData(new Projection.Mercator(), testConfig.tiles, {
app app
}) })
let testMap = new DeepZoomMap(testMapData, Object.assign({}, testConfig.tiles, { app }), { cover: false }) let testMap = new DeepZoomMap(testMapData, Object.assign({}, testConfig.tiles, { app }), { cover })
app.addMap("test", testMap) app.addMap("test", testMap)
let osmMapData = new DeepZoomMapData(new Projection.Mercator(), osmConfig.tiles, { let osmMapData = new DeepZoomMapData(new Projection.Mercator(), osmConfig.tiles, {
app app
}) })
let deepZoomMap = new DeepZoomMap(osmMapData, Object.assign({}, osmConfig.tiles, { app }), { cover: false }) let deepZoomMap = new DeepZoomMap(osmMapData, Object.assign({}, osmConfig.tiles, { app }), { cover })
app.addMap("osm", deepZoomMap) app.addMap("osm", deepZoomMap)
// Finally apply the map to the MapApp // Finally apply the map to the MapApp

View File

@ -105,7 +105,7 @@ export default class MapApp extends PIXIApp {
super.setup() super.setup()
// TODO get access to fps display // TODO get access to fps display
let fpsDisplay let fpsDisplay = null
this.stage.children.forEach(element => { this.stage.children.forEach(element => {
if (element.refreshFps) fpsDisplay = element if (element.refreshFps) fpsDisplay = element
}) })
@ -114,8 +114,7 @@ export default class MapApp extends PIXIApp {
this.ui = new PIXI.Container() this.ui = new PIXI.Container()
this.scene.addChild(this.ui) this.scene.addChild(this.ui)
if (this.fpsLogging != null && fpsDisplay != null) this.ui.addChild(fpsDisplay)
if (this.fpsLogging && fpsDisplay) this.ui.addChild(fpsDisplay)
if (this.coordsLogging) { if (this.coordsLogging) {
this.coordsDisplay = new CoordinateDisplay(this) this.coordsDisplay = new CoordinateDisplay(this)
@ -216,10 +215,11 @@ export default class MapApp extends PIXIApp {
lastMap.flushHandlers() lastMap.flushHandlers()
} }
this.map.onTransform.add(this.transformed.bind(this))
this.transformed() this.transformed()
this.onMapChanged.call(this, this.map) this.onMapChanged.call(this, this.map)
this.map.onTransform.add(this.transformed.bind(this))
if (this.ui && this.ui.parent) { if (this.ui && this.ui.parent) {
const parent = this.ui.parent const parent = this.ui.parent
@ -249,11 +249,6 @@ export default class MapApp extends PIXIApp {
this.overlayElements.set(layer, obj) this.overlayElements.set(layer, obj)
} }
addMapOverlay(layer) {
this.mapLayer.place(layer)
}
/** /**
* Copies the current coordinates to the clipboard. * Copies the current coordinates to the clipboard.
*/ */

View File

@ -27,7 +27,7 @@ export default class MapView {
return this._zoom return this._zoom
} }
update(map) { apply(map) {
map.moveTo(this._focus, this._zoom) map.moveTo(this._focus, this._zoom)
} }
@ -36,7 +36,7 @@ export default class MapView {
this.updateFocusPoint(map) this.updateFocusPoint(map)
} }
applyCameraPosition(map) { updatePosition(map) {
this.updateFocusPoint(map) this.updateFocusPoint(map)
this.updateZoom(map) this.updateZoom(map)
} }
@ -58,24 +58,39 @@ export default class MapView {
} }
mapPointToWindowPoint(map, point) { mapPointToWindowPoint(map, point) {
let container = map.image.parent let windowPoint = { x: 0, y: 0 }
let _point = new PIXI.Point( if (map['image'] && map.image['parent']) {
map.scatter.position.x + map.scatter.scale * point.x, let container = map.image.parent
map.scatter.position.y + map.scatter.scale * point.y
)
return container.toGlobal(_point) windowPoint = new PIXI.Point(
map.scatter.position.x + map.scatter.scale * point.x,
map.scatter.position.y + map.scatter.scale * point.y
)
windowPoint = container.toGlobal(windowPoint)
} else {
console.error(this._noParentError)
}
return windowPoint
} }
windowPointToMapPoint(map, point) { windowPointToMapPoint(map, point) {
let offset = map.image.parent.toGlobal({ x: 0, y: 0 }) let pointOnMap = { x: 0, y: 0 }
let _point = new PIXI.Point( if (map['image'] && map.image['parent']) {
(point.x - map.scatter.position.x - offset.x) / map.scatter.scale, let offset = map.image.parent.toGlobal({ x: 0, y: 0 })
(point.y - map.scatter.position.y - offset.y) / map.scatter.scale pointOnMap = new PIXI.Point(
) (point.x - map.scatter.position.x - offset.x) / map.scatter.scale,
(point.y - map.scatter.position.y - offset.y) / map.scatter.scale
)
} else console.error(this._noParentError)
return _point return pointOnMap
}
get _noParentError() {
return 'Cannot compute map point when map has no parent.'
} }
/** /**

View File

@ -78,6 +78,7 @@ export default class Overlay {
scale: 1, scale: 1,
rescale: false, rescale: false,
popoverOffset: { x: 0, y: 0 }, popoverOffset: { x: 0, y: 0 },
zoomVisibility: { min: 0, max: Number.MAX_VALUE },
/** /**
* The following Attributes are taken from the TextStyle class * The following Attributes are taken from the TextStyle class
@ -187,6 +188,10 @@ export default class Overlay {
} = {}) { } = {}) {
const name = this.name[0].toUpperCase() + this.name.slice(1).toLowerCase() + ' Overlay' const name = this.name[0].toUpperCase() + this.name.slice(1).toLowerCase() + ' Overlay'
let geoLayer = new GeoLayer(new PIXI.Container(), { name }) let geoLayer = new GeoLayer(new PIXI.Container(), { name })
console.log(this)
geoLayer.visibility = this.zoomVisibility
if (this.rescale) geoLayer.rescale = this.rescale if (this.rescale) geoLayer.rescale = this.rescale
this.items.forEach(item => { this.items.forEach(item => {
if (!excludeItems(item)) { if (!excludeItems(item)) {
@ -201,7 +206,6 @@ export default class Overlay {
}) })
return geoLayer return geoLayer
} }
getItemProperty(item, property) { getItemProperty(item, property) {
let propertyValue = null let propertyValue = null
const propertyExistsOnItem = item[property] !== undefined const propertyExistsOnItem = item[property] !== undefined
@ -508,7 +512,6 @@ export default class Overlay {
maxWidth: 350 maxWidth: 350
}) })
if (geographics.map) { if (geographics.map) {
const scale = 1 / geographics.map.scatter.scale const scale = 1 / geographics.map.scatter.scale
popup.scale.set(scale, scale) popup.scale.set(scale, scale)

View File

@ -243,7 +243,7 @@
let calculatedCoordinates = mercatorProjection.backward(relativePosition) let calculatedCoordinates = mercatorProjection.backward(relativePosition)
// Testcases for the backwards transformation. // Testcases for the backwards transformation.
Doctest.expect(coordinates, calculatedCoordinates) Doctest.expectPointPrecision(coordinates, calculatedCoordinates)
let point = createPointAtPoisition(relativePosition) let point = createPointAtPoisition(relativePosition)
mercatorMap.appendChild(point) mercatorMap.appendChild(point)
@ -317,7 +317,7 @@
let relativePosition = robinsonProjection.forward(position) let relativePosition = robinsonProjection.forward(position)
// Run Test Cases // Run Test Cases
Doctest.expect(robinsonTruth[key], relativePosition) Doctest.expectPointPrecision(robinsonTruth[key], relativePosition,0)
let point = createPointAtPoisition(relativePosition) let point = createPointAtPoisition(relativePosition)
robinsonMap.appendChild(point) robinsonMap.appendChild(point)

View File

@ -89,7 +89,7 @@ export class AdvancedScatterContainer extends ScatterContainer {
let interactionManager = this.renderer.plugins.interaction let interactionManager = this.renderer.plugins.interaction
let displayObject = interactionManager.hitTest(local, this) let displayObject = interactionManager.hitTest(local, this)
console.log(displayObject.dontBlockScatter, displayObject.parent)
if (displayObject.dontBlockScatter && displayObject.parent != null) { if (displayObject.dontBlockScatter && displayObject.parent != null) {
displayObject = interactionManager.hitTest(local, displayObject.parent) displayObject = interactionManager.hitTest(local, displayObject.parent)
} }
@ -97,7 +97,7 @@ export class AdvancedScatterContainer extends ScatterContainer {
if (displayObject != null && displayObject.scatter != null) this.hitScatter = displayObject.scatter if (displayObject != null && displayObject.scatter != null) this.hitScatter = displayObject.scatter
if (this.claimEvents) event.claimedByScatter = this.hitScatter if (this.claimEvents) event.claimedByScatter = this.hitScatter
console.log(displayObject)
return this.hitScatter return this.hitScatter
} }
} }
@ -331,26 +331,22 @@ export class CoverScatter extends AdvancedScatter {
this.debugGraphics.endFill() this.debugGraphics.endFill()
} }
if (this.cover) { // if (this.cover) {
// The reference to the element handler needs to be stored, // // The reference to the element handler needs to be stored,
// that we can remove it later on. // // that we can remove it later on.
const eventHandler = this._applyInitialCover.bind(this, null, true) // this._applyInitialCover = this._applyInitialCover.bind(this)
this._applyInitialCover(eventHandler) // }
}
} }
_applyInitialCover(eventHandler, removeListener = false) { // _applyInitialCover() {
if (this.debug) console.log('ApplyInitialCover: ', parent) // if (this.debug) console.log('ApplyInitialCover: ', parent)
if (removeListener) {
this.displayObject.off('added', eventHandler)
}
if (this.displayObject.parent) // if (this.displayObject.parent)
this.forceCover(this.displayObject.parent.width, this.displayObject.parent.height) // this.forceCover(this.displayObject.parent.width, this.displayObject.parent.height)
else { // else {
this.displayObject.on('added', eventHandler) // this.displayObject.on('added', eventHandler)
} // }
} // }
get boundaries() { get boundaries() {
if (this._boundaries) return this._boundaries if (this._boundaries) return this._boundaries
@ -472,7 +468,11 @@ export class CoverScatter extends AdvancedScatter {
if (this.scalable && this.parent != null) { if (this.scalable && this.parent != null) {
if (this.cover) { if (this.cover) {
let minCoverScale = this.calculateMinCoverScale(this.parent.width, this.parent.height) let minCoverScale = this.calculateMinCoverScale(this.parent.width, this.parent.height)
scale = scale < minCoverScale ? minCoverScale : scale if (scale < minCoverScale) {
console.error('USE MIN COVER SCALE', minCoverScale, scale)
scale = minCoverScale
}
} }
this.scale = scale this.scale = scale
} }
@ -528,7 +528,6 @@ export class MapObjectScatter extends CoverScatter {
) )
super(displayObject, renderer, opts) super(displayObject, renderer, opts)
if (!renderer) { if (!renderer) {
console.error('Renderer was not set!') console.error('Renderer was not set!')
return return

View File

@ -331,8 +331,14 @@ export class EventHandler {
} }
call(context, ...args) { call(context, ...args) {
this.listeners.forEach(listener => listener.call(context, ...args)) if (context == null) {
this.onces.forEach(listener => listener.call(context, ...args)) this.listeners.forEach(listener => listener(...args))
this.onces.forEach(listener => listener(...args))
} else {
this.listeners.forEach(listener => listener.call(context, ...args))
this.onces.forEach(listener => listener.call(context, ...args))
}
this.onces = [] this.onces = []
} }