import { DeepZoomMap } from './map.js' /** * The MapViewport class is responsible for a consistent map view. * It is aware of the current viewposition, the scale and viewport. * It ensures, that maps can be changed, without the user noticing it. * */ export default class MapViewport { /** * * @param {object} [focus = {x:0, y:0}] - Defines the startup focuspoint of the app. * @param {number} [zoom = 0] - Defines the startup zoom of the app. Note that this is just a request. * The MapViewport will prioritize a full scale app, than displaying the demanded zoom factor */ constructor({ focus = null, zoom = null, viewport = { min: { x: -85, y: -180 }, max: { x: 85, y: 180 } } } = {}) { this.viewport = viewport this._focus = focus this._zoom = zoom this.referenceHeight = 256 } /** * The current focus point as map coordinates. * * @member {CoordinatePoint} * @readonly * @memberof MapViewport */ get focus() { return this._focus } /** * The current zoom distance. * On DeepZoomMaps this is equal to the zoom level. * * @member {number} * @readonly * @memberof MapViewport */ get zoom() { return this._zoom } /** * Applies the current view to a map. * * * @param {GeoMap} map - Map the viewport should be applied to. * @memberof MapViewport */ apply(map) { map.moveTo(this._focus, this._zoom) } /** * Update the focus point and zoom according to the map. * This is commonly called when the map is transformed. * * @param {GeoMap} map - Map to update. * @memberof MapViewport */ update(map) { this.updateZoom(map) this.updateFocusPoint(map) } /** * Updates the focus point. * This is automatically called when calling update. * * @param {GeoMap} map * @memberof MapViewport */ updateFocusPoint(map) { const frame = map.getFrame() this._focus = this.coordinatesFromWindowPoint(map, frame.localCenter) } /** * Updates the zoom. * This is automatically called when calling update. * * @param {GeoMap} map * @memberof MapViewport */ updateZoom(map) { /** * TODO: This relies on the fact, that all maps have the same tileSize, * if a set would have a smaller tileSize. Improve that. */ if (map instanceof DeepZoomMap) this._zoom = map.floatingLevelForScale(map.image.scatter.scale) else { this._zoom = map.zoom } } /** * Transforms a position on the map to a position in the window. * * Inverse of windowPointToMapPoint. * * @param {GeoMap} map - Map to calculate the position on. * @param {Point} point - Position on the map in pixels. * @returns {Point} - Pixel position in the window. * @memberof MapViewport */ mapPointToWindowPoint(map, point) { let windowPoint = { x: 0, y: 0 } if (map['image'] && map.image['parent']) { let container = map.image.parent 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 } /** * Transforms in the window to a position on the map. * * Inverse of mapPointToWindowPoint(map, point) {. * * @param {GeoMap} map - Map to calculate the position on. * @param {Point} point -Pixel position in the window. * @returns {Point} - Position on the map in pixels. * @memberof MapViewport */ windowPointToMapPoint(map, point) { let pointOnMap = { x: 0, y: 0 } if (map['image'] && map.image['parent']) { let offset = map.image.parent.toGlobal({ x: 0, y: 0 }) 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 pointOnMap } get _noParentError() { return 'Cannot compute map point when map has no parent.' } /** * Gets the coordinates of a specific point in the viewport. * * @param {PIXI.Point | {x,y}} point - Pixel position in the viewport. * @returns {{x,y}} Coordinates on the map of the provided position. * @memberof MapView */ coordinatesFromWindowPoint(map, point) { let position = { x: point.x - map.scatter.position.x, y: point.y - map.scatter.position.y } let normalized = { x: position.x / (map.width * map.scatter.scale), y: position.y / (map.height * map.scatter.scale) } let coordinates = map.mapProjection.toCoordinates(normalized) return coordinates } }