Added the maps-module to the iwmlib.
Migrated a ot of the content from the tuesch to the iwmlib. This is before the decoupeling of the layers.
This commit is contained in:
@@ -0,0 +1,914 @@
|
||||
import { MapObjectScatter } from './scatter.js'
|
||||
import { DeepZoomImage, DeepZoomInfo } from '../deepzoom/image.js'
|
||||
import { MapData, DeepZoomMapData } from './mapdata.js'
|
||||
import { Points } from '../../utils.js'
|
||||
import { EventHandler } from './utils.js'
|
||||
import Mercator from './projections/mercator.js'
|
||||
|
||||
/**
|
||||
* The GeoMap class displays a map, that it gets from MapData object.
|
||||
* It handles the current location on the map, the zoom factor, interactions and
|
||||
* the viewport, the area of the map, the user can see and navigate to.
|
||||
*
|
||||
* You should not instantiate GeoMap on it's own. Use the implemented child classes
|
||||
* or derive a new one from it.
|
||||
*
|
||||
* Note: The name Map is already in use. Therefore GeoMap was used instead.
|
||||
*
|
||||
|
||||
* @abstract
|
||||
* @class
|
||||
* @see {@link maps.html}
|
||||
*/
|
||||
|
||||
export class GeoMap {
|
||||
/**
|
||||
* Creates instance of GeoMap
|
||||
*
|
||||
* @constructor
|
||||
* @param {MapData}[mapdata={}] - The mapdata describes how the map has to be interpreted by the Map class. E.g. what are the boundaries of the map?
|
||||
* @param {object}[opts={}] - With the opts, the created MapObjectScatter can be adjusted.
|
||||
* @param {boolean}[opts.cover=false] - Enables covering behaviour of a map object. Normally maps should cover the whole app.
|
||||
*/
|
||||
constructor(
|
||||
mapdata = {},
|
||||
{
|
||||
debug = true,
|
||||
cover = true,
|
||||
// Scatter Options
|
||||
alpha = 1,
|
||||
startScale = 1,
|
||||
minScale = 0,
|
||||
maxScale = Infinity,
|
||||
translatable = true,
|
||||
scalable = true,
|
||||
rotatable = false, // Many functionalities are not supported when rotating the map. Mainly the cover mechanism.
|
||||
viewport = mapdata.maxViewport,
|
||||
// Events
|
||||
onLoad = null,
|
||||
onTransform = null
|
||||
} = {}
|
||||
) {
|
||||
this._id = GeoMap.counter++
|
||||
this.onLoad = new EventHandler('loaded', { listeners: onLoad })
|
||||
this.onTransform = new EventHandler('transform', { listeners: onTransform })
|
||||
|
||||
this.alpha = alpha
|
||||
this.cover = cover
|
||||
this.debug = debug
|
||||
|
||||
//TODO discuss if this is required here.
|
||||
// Those are just scatter options and the information
|
||||
// is redundant in the map class and the scatter.
|
||||
this.startScale = startScale
|
||||
this.minScale = minScale
|
||||
this.maxScale = maxScale
|
||||
this.rotatable = rotatable
|
||||
this.translatable = translatable
|
||||
this.scalable = scalable
|
||||
this.viewport = viewport
|
||||
|
||||
this.mapdata = mapdata
|
||||
this.overlays = {}
|
||||
|
||||
/**
|
||||
* Adjust the viewport depending on the mapdata clipping.
|
||||
*/
|
||||
if (this.mapdata.clip) {
|
||||
const vp = this.viewport
|
||||
const cp = this.mapdata.clip
|
||||
let bounds = {
|
||||
min: {
|
||||
x: vp.min.x > cp.min.x ? vp.min.x : cp.min.x,
|
||||
y: vp.min.y > cp.min.y ? vp.min.y : cp.min.y
|
||||
},
|
||||
max: {
|
||||
x: vp.max.x < cp.max.x ? vp.max.x : cp.max.x,
|
||||
y: vp.max.y < cp.max.y ? vp.max.y : cp.max.y
|
||||
}
|
||||
}
|
||||
|
||||
this.viewport = bounds
|
||||
}
|
||||
}
|
||||
|
||||
flushHandlers() {
|
||||
// this.onLoaded
|
||||
this.onTransform.empty()
|
||||
}
|
||||
|
||||
lock({ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false } = {}) {
|
||||
if (this.image && this.image.scatter) {
|
||||
this.image.scatter.translatable = rotatable
|
||||
this.image.scatter.movableX = translatable
|
||||
this.image.scatter.movableY = movableX
|
||||
this.image.scatter.rotatable = movableY
|
||||
this.image.scatter.scalable = scalable
|
||||
}
|
||||
|
||||
// Issue #001: This causes the map to not be displayed at the correct position on
|
||||
// map change.
|
||||
// // Rotation does not yet work with the cover mechanism.
|
||||
// //this.rotatable = false
|
||||
// this.translatable = false
|
||||
// this.scalable = false
|
||||
}
|
||||
|
||||
unlock({ rotatable = true, translatable = true, movableX = true, movableY = true, scalable = true } = {}) {
|
||||
if (this.image && this.image.scatter) {
|
||||
this.image.scatter.translatable = translatable
|
||||
this.image.scatter.movableX = movableX
|
||||
this.image.scatter.movableY = movableY
|
||||
this.image.scatter.rotatable = rotatable
|
||||
this.image.scatter.scalable = scalable
|
||||
}
|
||||
// Issue #001
|
||||
// // Rotation does not yet work with the cover mechanism.
|
||||
// //this.rotatable = true
|
||||
// this.translatable = true
|
||||
// this.scalable = true
|
||||
}
|
||||
|
||||
unload() {
|
||||
this.image.parent.removeChild(this.image)
|
||||
this.image.scatter = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Is called when the scatter object is transformed.
|
||||
*
|
||||
* @private
|
||||
* @param {ScatterEvent}[e] - Contains informations on how the element was transformed.
|
||||
*/
|
||||
transformed(e) {
|
||||
this.onTransform.call(this, e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapps the display object around a scatter object.
|
||||
*
|
||||
* @private
|
||||
* @param {DisplayObject} displayObject - Defines the display object that will be wrapped inside the scatter object.
|
||||
* @param {PIXI.Container} container - Defines the frame in which the map will be displayed.
|
||||
*/
|
||||
load(image, renderer, frame = null, scatter = null) {
|
||||
if (this.debug) console.log('Load image: ', image, frame)
|
||||
|
||||
this.image = image
|
||||
if (frame) this.setFrame(frame)
|
||||
|
||||
let min = this.mapdata.toPixel(this.viewport.min)
|
||||
let max = this.mapdata.toPixel(this.viewport.max)
|
||||
let boundaries = {
|
||||
min: { x: min.x, y: max.y },
|
||||
max: { x: max.x, y: min.y }
|
||||
}
|
||||
|
||||
let scatterOpts = Object.assign({
|
||||
cover: this.cover,
|
||||
scaleable: this.scaleable,
|
||||
translatable: this.translatable,
|
||||
rotatable: this.rotatable,
|
||||
debug: this.debug,
|
||||
startScale: this.startScale,
|
||||
minScale: this.minScale,
|
||||
maxScale: this.maxScale,
|
||||
boundaries,
|
||||
onTransform: this.transformed.bind(this)
|
||||
})
|
||||
|
||||
this.scatter = new MapObjectScatter(image, renderer, scatterOpts)
|
||||
this.image.scatter = scatter == null ? this.scatter : scatter
|
||||
|
||||
this.onLoad.call(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an identical copy of the current map.
|
||||
*/
|
||||
clone() {
|
||||
console.error('Method clone() is abstract. Overload it in subclass', this)
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* Moves the 'camera' to another location on the map.
|
||||
*
|
||||
* @abstract
|
||||
* @param {object} coordinates - Target coordinates in form {x: latitude, y: longitude}
|
||||
* @param {number} scale - Target scale.
|
||||
*/
|
||||
moveTo(coordinates, zoom = null, { animate = false } = {}) {
|
||||
console.error(this, 'The method moveTo(coordinates, zoom, opts) has not been defined in child class.')
|
||||
}
|
||||
|
||||
zoomTo(zoomFactor) {
|
||||
console.error('Method zoomTo() is abstract. Overload it in subclass', this, zoomFactor)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reloads the textures of the image, when it was unloaded and is required again.
|
||||
*
|
||||
* @memberof GeoMap
|
||||
*/
|
||||
reloadImage() {
|
||||
console.error(`Call of abstract method reloadImage(). Overwrite in subclass.`, this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Unloads the textures of the image, when not in use.
|
||||
*
|
||||
* @memberof GeoMap
|
||||
*/
|
||||
unloadImage() {
|
||||
console.error(`Call of abstract method unloadImage(). Overwrite in subclass.`, this)
|
||||
}
|
||||
|
||||
/**
|
||||
* Transforms a point at pixel position {x,y}
|
||||
* to a coordinate with latitude and longitude.
|
||||
*
|
||||
*
|
||||
* @param {object} point - Point in form of {x: x_val, y: y_val}.
|
||||
* @returns {object} - Coordinates on the map in form of {x: latitude, y: longitude}.
|
||||
*/
|
||||
coordinatesFromPoint(point) {
|
||||
let coords = this.mapdata.toCoordinates(this.toRelativePosition(point))
|
||||
return coords
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform coordinates in the map into pixel positions on the deep zoom image.
|
||||
*
|
||||
* @param {object} coords - Coordinates of a map position in form {x: latitude, y: longitude}.
|
||||
* @return - Returns a image position in form of {x: x_val, y: y_val}.
|
||||
*/
|
||||
coordinatesToPoint(coordinates) {
|
||||
return this.toAbsolutePixelCoordinates(this.mapdata.toPixel(coordinates))
|
||||
}
|
||||
|
||||
toRelativePosition(point) {
|
||||
let _point = new PIXI.Point()
|
||||
|
||||
_point.x = point.x / this.width
|
||||
_point.y = point.y / this.height
|
||||
|
||||
return _point
|
||||
}
|
||||
|
||||
toAbsolutePixelCoordinates(point) {
|
||||
let _point = new PIXI.Point()
|
||||
_point.x = point.x * this.width
|
||||
_point.y = point.y * this.height
|
||||
|
||||
return _point
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Appends the object to a PIXI container. This is important,
|
||||
// * to notify the map, that it's parent has changed.
|
||||
// *
|
||||
// * If you want to use PIXI's addChild, make sure you call
|
||||
// * appended right afterwards.
|
||||
// *
|
||||
// * @param {PIXI.Container} container
|
||||
// * @returns Returns the map object to allow chaining.
|
||||
// */
|
||||
// appendTo(container) {
|
||||
// container.addChild(this.image)
|
||||
// return this.appended(container)
|
||||
// }
|
||||
|
||||
get width() {
|
||||
return this.image.scatter.width / this.image.scatter.scale
|
||||
}
|
||||
|
||||
get height() {
|
||||
return this.image.scatter.height / this.image.scatter.scale
|
||||
}
|
||||
|
||||
get distance() {
|
||||
console.error('Overload get distance in subclass.')
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a screen point for a coordinate.
|
||||
*/
|
||||
positionInWindow(coordinates) {
|
||||
let pos = this.coordinatesToPoint(coordinates)
|
||||
|
||||
let framePos = {
|
||||
x: this.image.position.x + pos.x * this.image.scale.x,
|
||||
y: this.image.position.y + pos.y * this.image.scale.y
|
||||
}
|
||||
|
||||
return framePos
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the coordinates for a screen point.
|
||||
*/
|
||||
coordinatesInWindow(point) {
|
||||
let mapPos = {
|
||||
x: (point.x - this.image.position.x) / this.image.scale.x,
|
||||
y: (point.y - this.image.position.y) / this.image.scale.y
|
||||
}
|
||||
|
||||
let coords = this.coordinatesFromPoint(mapPos)
|
||||
return coords
|
||||
}
|
||||
|
||||
setFrame(frame) {
|
||||
if (this.debug) console.log('Set Frame: ', frame)
|
||||
this.frame = frame
|
||||
}
|
||||
|
||||
/**
|
||||
* Frame is the display in which the map is shown.
|
||||
* Normally it's the app, but it can be another element,
|
||||
* for example when in a submap.
|
||||
*/
|
||||
getFrame() {
|
||||
let frame = {
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: 0,
|
||||
height: 0
|
||||
}
|
||||
|
||||
if (this.frame) {
|
||||
//The app does not have an x and y attribute.
|
||||
//Therefore we need the additional check.
|
||||
frame = {
|
||||
x: this.frame.x ? this.frame.x : 0,
|
||||
y: this.frame.y ? this.frame.y : 0,
|
||||
width: this.frame.width,
|
||||
height: this.frame.height
|
||||
}
|
||||
} else if (this.image.parent) {
|
||||
let parent = this.image.parent
|
||||
|
||||
for (let key of Object.keys(frame)) {
|
||||
frame[key] = parent[key]
|
||||
}
|
||||
}
|
||||
|
||||
frame.localCenter = {
|
||||
x: frame.width / 2,
|
||||
y: frame.height / 2
|
||||
}
|
||||
|
||||
console.log(frame)
|
||||
frame.center = {
|
||||
x: frame.x + frame.localCenter.x,
|
||||
y: frame.y + frame.localCenter.y
|
||||
}
|
||||
|
||||
return frame
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves all maps from a json object.
|
||||
*
|
||||
* @static
|
||||
* @param {object} json - The parsed map data object.
|
||||
* @returns {object} - Returns an object with the names as keys and the GeoMaps as value.
|
||||
* @memberof GeoMap
|
||||
*/
|
||||
static allFromJson(json) {
|
||||
let error = { message: '' }
|
||||
let maps = {}
|
||||
if (GeoMap._validateJson(json, error)) {
|
||||
for (let [mapname, data] of Object.entries(json)) {
|
||||
maps[mapname] = GeoMap._createMap(data)
|
||||
}
|
||||
} else console.error('Could not validate JSON: ' + error.message)
|
||||
|
||||
return maps
|
||||
}
|
||||
|
||||
/**
|
||||
*Extracts a single map from a JSON map file.
|
||||
*
|
||||
* @static
|
||||
* @param {string} map - Name of the map.
|
||||
* @param {object} json - Parsed json object.
|
||||
* @returns {GeoMap} - Returns a geomap, if the map was in the data and valid. Otherwise false.
|
||||
* @memberof GeoMap
|
||||
*/
|
||||
static mapFromJson(map, json) {
|
||||
if (json[map]) {
|
||||
const data = json[map]
|
||||
if (this._validJsonMap(data)) return GeoMap._createMap(data)
|
||||
else console.error('Map was not in a valid format.')
|
||||
} else console.error('Map was not in data.')
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
* Validates if the map data contains valid data
|
||||
* for creating the maps.
|
||||
*
|
||||
* @static
|
||||
* @param {object} json - The object containing multiple map data sets.
|
||||
* @param {error-object} error - An object that contains an parameter message: {message = ""}. This is faking a call by reference.
|
||||
* @returns {boolean} - True if all sets were valid. False otherwise.
|
||||
* @memberof GeoMap
|
||||
*/
|
||||
static _validateJson(json, error) {
|
||||
let isValid = true
|
||||
|
||||
if (Object.keys(json).length == 0) {
|
||||
isValid = false
|
||||
error.message += 'The provided JSON object did not contain any items.'
|
||||
}
|
||||
for (let [name, data] of Object.entries(json)) {
|
||||
if (!GeoMap._validJsonMap(data)) {
|
||||
error.message += `${name} was not valid. `
|
||||
isValid = false
|
||||
}
|
||||
}
|
||||
|
||||
if (error.message != '') error.message = 'Maps could not be validated: ' + error.message
|
||||
|
||||
return isValid
|
||||
}
|
||||
|
||||
static changePathsInJson(json, path) {
|
||||
for (let key of Object.keys(json)) {
|
||||
json[key].icon = path + json[key].icon
|
||||
json[key].tiles.path = path + json[key].tiles.path
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*Validates of a single data set contains the valid data for creating a map.
|
||||
*
|
||||
* @static
|
||||
* @param {object} json - The object containing a single set of map data.
|
||||
* @returns {boolean} - True if valid, otherwise false.
|
||||
* @memberof GeoMap
|
||||
*/
|
||||
static _validJsonMap(json) {
|
||||
let isJsonValid = true
|
||||
const required = ['projection', 'type', 'icon']
|
||||
for (const requirement of Object.values(required)) {
|
||||
if (!json.hasOwnProperty(requirement)) {
|
||||
isJsonValid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (isJsonValid && json.type.toLowerCase() == 'deepzoom') {
|
||||
if (!json.hasOwnProperty('tiles')) isJsonValid = false
|
||||
}
|
||||
|
||||
return isJsonValid
|
||||
}
|
||||
|
||||
static _createMap(data) {
|
||||
switch (data.type.toLowerCase()) {
|
||||
case 'deepzoom':
|
||||
return GeoMap._createDeepZoomMap(data)
|
||||
default:
|
||||
console.error(`Datatype is invalid or not implemented yet: ${data.type}`)
|
||||
}
|
||||
}
|
||||
|
||||
static _createDeepZoomMap(data) {
|
||||
const projection = GeoMap._getProjectionByName(data.projection)
|
||||
const tilesConfig = data.tiles
|
||||
const options = data.options
|
||||
|
||||
const mapdata = new DeepZoomMapData(projection, tilesConfig)
|
||||
return new DeepZoomMap(mapdata, tilesConfig, options)
|
||||
}
|
||||
|
||||
static _getProjectionByName(projection) {
|
||||
switch (projection.toLowerCase()) {
|
||||
case 'mercator':
|
||||
return new Mercator()
|
||||
default:
|
||||
console.error(`Projection is invalid or not implemented yet ${projection}.`)
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GeoMap.counter = 0
|
||||
|
||||
/**
|
||||
* The DeepZoomMap class extends the GeoMap to create
|
||||
* maps as deepzoom images from maptiles.
|
||||
*
|
||||
* @extends GeoMap
|
||||
* @class
|
||||
* @see {@link maps.html}
|
||||
*/
|
||||
|
||||
export class DeepZoomMap extends GeoMap {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {object} tilesConfig - The tiles config object, that defines at what path and in which format the tiles are.
|
||||
* @param {MapData} mapdata - A MapData object, that contains informations of how the given map has to be interpreted.
|
||||
* @param {object} opts - Additional options to specify the behaviour of the deep zoom image.
|
||||
*/
|
||||
constructor(mapdata, tilesConfig, opts = {}) {
|
||||
if (!tilesConfig.app) console.error('App was not set in the tilesConfig.')
|
||||
opts = Object.assign(
|
||||
{
|
||||
maxScale: Math.min(tilesConfig.width, tilesConfig.height) / tilesConfig.tileSize,
|
||||
minScale: mapdata.getMinScale,
|
||||
highResolution: true,
|
||||
debug: false
|
||||
},
|
||||
opts
|
||||
)
|
||||
super(mapdata, opts)
|
||||
|
||||
this.tilesConfig = tilesConfig
|
||||
this._verifyMapdata()
|
||||
}
|
||||
|
||||
_verifyMapdata() {
|
||||
if (!(this.mapdata instanceof MapData)) {
|
||||
console.error('Use the MapData object for creating maps!')
|
||||
} else {
|
||||
if (this.mapdata instanceof DeepZoomMapData) {
|
||||
if (!this.mapdata.app) console.error('No app was set on the mapdata!')
|
||||
} else {
|
||||
console.error('Use the DeepZoomMapData object.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the DeepZoomImage.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
load(container = null, scatter = null) {
|
||||
this.info = new DeepZoomInfo(this.tilesConfig)
|
||||
console.log('APP:', this.mapdata.app)
|
||||
let image = new DeepZoomImage(this.info, {
|
||||
app: this.mapdata.app,
|
||||
alpha: this.alpha,
|
||||
debug: this.debug,
|
||||
world: scatter == null ? scatter : scatter.getWorldScatter()
|
||||
})
|
||||
|
||||
super.load(image, container, scatter)
|
||||
|
||||
if (this.debug) console.log('Loaded image: ', image, 'With options: ', this.info)
|
||||
}
|
||||
|
||||
unloadImage() {
|
||||
if (this.image) {
|
||||
this.image.deactivate()
|
||||
}
|
||||
}
|
||||
|
||||
reloadImage() {
|
||||
this.image.activate()
|
||||
}
|
||||
|
||||
/**
|
||||
* Event function that is invoked by the deep zoom image, when it has been transformed.
|
||||
*
|
||||
* @private
|
||||
* @param {ScatterEvent} e
|
||||
*/
|
||||
transformed(e) {
|
||||
this.image.transformed(e)
|
||||
super.transformed(e)
|
||||
}
|
||||
|
||||
/**
|
||||
* Jumps to a certain point on the map and adjusts the size of the
|
||||
* map depending on the distance (zoom).
|
||||
*
|
||||
*
|
||||
* @param {*} coordinates
|
||||
* @param {*} [zoom=null]
|
||||
* @param {*} [{ animate = 0 }={}]
|
||||
* @memberof DeepZoomMap
|
||||
*/
|
||||
moveTo(coordinates, zoom = null, { animate = 0 } = {}) {
|
||||
if (this.image.scatter == null) {
|
||||
return
|
||||
}
|
||||
//TODO: This is jaggy on submaps. Make it more smooth. Severin Opel 08-05-2018
|
||||
if (zoom) {
|
||||
/**
|
||||
* Zoom's the view to a specified distance in DZI-Levels.
|
||||
* The input level is clipped to allowed levels.
|
||||
* */
|
||||
let targetScale = this.scaleForFloatingLevel(zoom)
|
||||
|
||||
// Don't scale beyond boundaries.
|
||||
if (this.maxScale) targetScale = Math.min(this.maxScale, targetScale)
|
||||
|
||||
if (this.minScale) targetScale = Math.max(targetScale, this.minScale)
|
||||
|
||||
this.image.scatter.requestScale(targetScale)
|
||||
}
|
||||
|
||||
if (coordinates) {
|
||||
//Get pixel position of lat lang values
|
||||
let point = this.coordinatesToPoint(coordinates)
|
||||
|
||||
let containerCenter
|
||||
if (this.frame) {
|
||||
containerCenter = this.getFrame().localCenter
|
||||
//console.log("HAS FRAME: ", containerCenter)
|
||||
} else {
|
||||
containerCenter = {
|
||||
x: this.image.parent.width / 2,
|
||||
y: this.image.parent.height / 2
|
||||
}
|
||||
|
||||
//console.log("GET CONTAINER CENTER")
|
||||
}
|
||||
|
||||
//Calculate the delta depending on image position and center the
|
||||
// desired position in the frame.
|
||||
let delta = {
|
||||
x: containerCenter.x - point.x * this.image.scatter.scale - this.scatter.position.x,
|
||||
y: containerCenter.y - point.y * this.image.scatter.scale - this.scatter.position.y
|
||||
}
|
||||
// console.log("MOVE TO: " , this.image.scatter.translatable,point, delta)
|
||||
|
||||
if (animate > 0) {
|
||||
let last = 0
|
||||
TweenMax.to({ x: 0, y: 0 }, animate, {
|
||||
ease: Power0.easeNone,
|
||||
x: delta.x,
|
||||
y: delta.y,
|
||||
onUpdateParams: ['{self}'],
|
||||
onUpdate: tween => {
|
||||
let step = tween.ratio - last
|
||||
last = tween.ratio
|
||||
this.image.scatter.transform(Points.multiplyScalar(delta, step), /*ratio*/ 1, 0, { x: 0, y: 0 })
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.image.scatter.transform(delta, /*ratio*/ 1, 0, { x: 0, y: 0 })
|
||||
}
|
||||
}
|
||||
// return this.image.scatter.position
|
||||
}
|
||||
|
||||
scaleForFloatingLevel(level) {
|
||||
level = Math.max(0, level - this.image.info.baseLevel)
|
||||
return Math.pow(2, level) / this.image.resolution
|
||||
}
|
||||
|
||||
floatingLevelForScale(scale) {
|
||||
let level = Math.log2(scale * this.image.resolution)
|
||||
let newLevel = this.image.info.baseLevel + Math.max(level, 0)
|
||||
return Math.min(newLevel, this.image.info.maxLoadableLevel)
|
||||
}
|
||||
|
||||
get distance() {
|
||||
return this.floatingLevelForScale(this.scatter.scale)
|
||||
}
|
||||
|
||||
clone(container, scatter = null) {
|
||||
const map = new DeepZoomMap(this.mapdata, this.tilesConfig, {
|
||||
alpha: this.alpha,
|
||||
cover: this.cover,
|
||||
debug: this.debug,
|
||||
startScale: this.startScale,
|
||||
minScale: this.minScale,
|
||||
maxScale: this.maxScale,
|
||||
rotatable: this.rotatable,
|
||||
translatable: this.translatable,
|
||||
scalable: this.scalable,
|
||||
viewport: this.viewport
|
||||
})
|
||||
|
||||
//TODO: If frame is not set, the map does not work anymore.
|
||||
|
||||
//map.load(container, scatter)
|
||||
|
||||
// this.image.parent.children.forEach(child=>{
|
||||
// if(child instanceof PIXI.DisplayObject){
|
||||
|
||||
// if(child instanceof PIXI.Graphics){
|
||||
// // let clone = child.clone()
|
||||
// // const position = child.position
|
||||
// // clone.position.set(position.x, position.y)
|
||||
// // map.addChild(clone)
|
||||
// // console.log(map.mapLayer)
|
||||
|
||||
// }else console.log("DisplayObject is not yet implemented for cloning.", child)
|
||||
|
||||
// }else console.error("Not implemented object to clone: ", child)
|
||||
// })
|
||||
|
||||
return map
|
||||
}
|
||||
|
||||
tint() {
|
||||
let color = DeepZoomMap.tintcolors[DeepZoomMap.tintcolor++ % DeepZoomMap.tintcolors.length]
|
||||
this.image.children[0].children.forEach(tiles => {
|
||||
tiles.children.forEach(tile => {
|
||||
tile.tint = color
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// /** Returns the tile layer level that corresponds to the given scale.
|
||||
// * @param {number} scale - the scale factor
|
||||
// **/
|
||||
// levelForScale(scale) {
|
||||
// let level = Math.round(Math.log2(scale * this.resolution)) // Math.floor(Math.log2(event.scale))+1
|
||||
// let newLevel = this.info.baseLevel + Math.max(level, 0)
|
||||
// return Math.min(newLevel, this.info.maxLoadableLevel)
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Returns the scale factor that correspond to a given level.
|
||||
// * @param {number} level - the requested level
|
||||
// * @returns {number} - Returns the scale level for the given level.
|
||||
// */
|
||||
// scaleForLevel(level){
|
||||
// level = Math.max(0, level - this.info.baseLevel)
|
||||
// return Math.pow(2, level) / this.resolution
|
||||
// }
|
||||
}
|
||||
|
||||
DeepZoomMap.tintcolors = [0xff0000, 0xff00ff, 0xffff00, 0x00ff00, 0x00ffff, 0x0000ff]
|
||||
DeepZoomMap.tintcolor = 0
|
||||
|
||||
/**
|
||||
*
|
||||
* ImageMap extends GeoMap to display simple images
|
||||
* as maps.
|
||||
*/
|
||||
|
||||
export class ImageMap extends GeoMap {
|
||||
constructor(sprite, mapdata, opts = {}) {
|
||||
super(mapdata, opts)
|
||||
|
||||
this.sprite = sprite
|
||||
|
||||
this.baseZoomHeight = opts.baseZoomHeight ? opts.baseZoomHeight : this.sprite.texture.height
|
||||
}
|
||||
|
||||
load(container = null, scatter = null) {
|
||||
super.load(this.sprite, container, scatter)
|
||||
this.image.alpha = this.alpha
|
||||
this.image.interactive = true
|
||||
}
|
||||
|
||||
clone(container = null, scatter = null) {
|
||||
const map = new ImageMap(new PIXI.Sprite(this.sprite.texture), this.mapdata, {
|
||||
alpha: this.alpha,
|
||||
cover: this.cover,
|
||||
debug: this.debug,
|
||||
startScale: this.startScale,
|
||||
minScale: this.minScale,
|
||||
maxScale: this.maxScale,
|
||||
rotatable: this.rotatable,
|
||||
translatable: this.translatable,
|
||||
scalable: this.scalable,
|
||||
viewport: this.viewport
|
||||
})
|
||||
map.load(container, scatter)
|
||||
|
||||
console.log(map.image)
|
||||
return map
|
||||
}
|
||||
|
||||
// transformed(e) {
|
||||
// for (let key in this.overlays) {
|
||||
// this.overlays[key].forEach((overlay) => {
|
||||
// overlay.sprite.scale.x = overlay.scale / e.scale
|
||||
// overlay.sprite.scale.y = overlay.scale / e.scale
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @param {*} coordinates
|
||||
* @param {*} [zoom=null]
|
||||
* @param {*} [{ animate = 0 }={}]
|
||||
* @memberof ImageMap
|
||||
*/
|
||||
moveTo(coordinates, zoom = null, { animate = 0 } = {}) {
|
||||
// console.log('Coordinates: ', coordinates)
|
||||
|
||||
// if (zoom) {
|
||||
// this.image.scatter.requestScale(zoom)
|
||||
// }
|
||||
|
||||
// if (coordinates) {
|
||||
// //Get pixel position of lat lang values
|
||||
// let offset = Points.multiplyScalar(this.coordinatesToPoint(coordinates), this.scatter.scale)
|
||||
|
||||
// let frame = this.getFrame()
|
||||
// console.log(frame)
|
||||
|
||||
// //Calculate the delta depending on image position and center the
|
||||
// // desired position in the frame.
|
||||
// let delta = {
|
||||
// x: frame.center.x - offset.x - this.scatter.position.x,
|
||||
// y: frame.center.y - offset.y - this.scatter.position.y
|
||||
// }
|
||||
|
||||
// console.log('Move to: ', offset, frame, delta)
|
||||
|
||||
// if (this.image) {
|
||||
// if (animate > 0) {
|
||||
// console.log(animate)
|
||||
// //TODO Replace this with a more robus manipulation method (RequestMovement + animation)
|
||||
// this.image.scatter.move(delta, { animate })
|
||||
// //Requesting the transform dhoul be okay. Maybe also replace with an request mehtod.
|
||||
// } else this.image.scatter.transform(delta, /*ratio*/ 1, 0, { x: 0, y: 0 })
|
||||
// } else console.error('Imge was not loaded yet.')
|
||||
// }
|
||||
|
||||
if (this.image.scatter == null) {
|
||||
return
|
||||
}
|
||||
//TODO: This is jaggy on submaps. Make it more smooth. Severin Opel 08-05-2018
|
||||
if (zoom) {
|
||||
/**
|
||||
* Zoom's the view to a specified distance in DZI-Levels.
|
||||
* The input level is clipped to allowed levels.
|
||||
* */
|
||||
|
||||
let targetScale = zoom * this.zoomFactor
|
||||
|
||||
// Don't scale beyond boundaries.
|
||||
if (this.maxScale) targetScale = Math.min(this.maxScale, targetScale)
|
||||
|
||||
if (this.minScale) targetScale = Math.max(targetScale, this.minScale)
|
||||
|
||||
this.image.scatter.requestScale(targetScale)
|
||||
}
|
||||
|
||||
//coordinates = null
|
||||
if (coordinates) {
|
||||
//Get pixel position of lat lang values
|
||||
let point = this.coordinatesToPoint(coordinates)
|
||||
|
||||
let containerCenter
|
||||
if (this.frame) {
|
||||
containerCenter = this.getFrame().localCenter
|
||||
//console.log("HAS FRAME: ", containerCenter)
|
||||
} else {
|
||||
containerCenter = {
|
||||
x: this.image.parent.width / 2,
|
||||
y: this.image.parent.height / 2
|
||||
}
|
||||
|
||||
//console.log("GET CONTAINER CENTER")
|
||||
}
|
||||
|
||||
//Calculate the delta depending on image position and center the
|
||||
// desired position in the frame.
|
||||
|
||||
let delta = {
|
||||
x: containerCenter.x - point.x * this.image.scatter.scale - this.scatter.position.x,
|
||||
y: containerCenter.y - point.y * this.image.scatter.scale - this.scatter.position.y
|
||||
}
|
||||
// console.log("MOVE TO: " , this.image.scatter.translatable,point, delta)
|
||||
|
||||
if (animate > 0) {
|
||||
let last = 0
|
||||
TweenMax.to({ x: 0, y: 0 }, animate, {
|
||||
ease: Power0.easeNone,
|
||||
x: delta.x,
|
||||
y: delta.y,
|
||||
onUpdateParams: ['{self}'],
|
||||
onUpdate: tween => {
|
||||
let step = tween.ratio - last
|
||||
last = tween.ratio
|
||||
this.image.scatter.transform(Points.multiplyScalar(delta, step), /*ratio*/ 1, 0, { x: 0, y: 0 })
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.image.scatter.transform(delta, /*ratio*/ 1, 0, { x: 0, y: 0 })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
get zoom() {
|
||||
return this.scatter.scale / this.zoomFactor
|
||||
}
|
||||
|
||||
get zoomFactor() {
|
||||
let factor = this.baseZoomHeight / this.sprite.texture.height
|
||||
console.log(factor)
|
||||
return factor
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user