Renamed 'MapView' to 'MapViewport'. Added documentation to the maps module.

This commit is contained in:
Severin Opel 2019-12-09 18:15:28 +01:00
parent 60e28f8fe5
commit 65fac2f406
16 changed files with 638 additions and 151 deletions

6
.gitignore vendored
View File

@ -80,3 +80,9 @@ typings/
*.code-workspace *.code-workspace
.history/ .history/
.vscode/ .vscode/
# ignore generated contents-
/doc/out/*
**/thumbnails
**/thumbnail.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.0 KiB

170
dist/iwmlib.pixi.js vendored
View File

@ -17444,7 +17444,7 @@
* @constructor * @constructor
* @param {Projection}[projection] - Specifies the projection of the map (e.g. Mercator Projection). * @param {Projection}[projection] - Specifies the projection of the map (e.g. Mercator Projection).
* @param {object}[opts] - Addiditonal options. * @param {object}[opts] - Addiditonal options.
* @param {[[minLat, minLng],[maxLat, maxLng]]}[opts.bounds] - Describes the minimum and maximum coordinates on the map * @param {array}[opts.bounds] - Describes the minimum and maximum coordinates on the map in the form of {[[minLat, minLng],[maxLat, maxLng]]}.
* @param {Point}[opts.translate] - Defines a translation, when clipping is not an option (e.g. when the whole world is shown, but translated.) * @param {Point}[opts.translate] - Defines a translation, when clipping is not an option (e.g. when the whole world is shown, but translated.)
*/ */
constructor(projection, opts = {}) { constructor(projection, opts = {}) {
@ -17482,6 +17482,7 @@
/** /**
* Transforms a pixel point on the map to a geographical coordinate. * Transforms a pixel point on the map to a geographical coordinate.
* *
* @public
* @param {{x,y} | PIXI.Point} point - A pixel position on the map. * @param {{x,y} | PIXI.Point} point - A pixel position on the map.
* @returns {{x,y} | PIXI.Point} - A geographical coordinate. * @returns {{x,y} | PIXI.Point} - A geographical coordinate.
* @memberof MapData * @memberof MapData
@ -17514,6 +17515,7 @@
/** /**
* Transform a geographical coordinate to a pixel point on the map. * Transform a geographical coordinate to a pixel point on the map.
* *
* @public
* @param {{x,y} | PIXI.Point} coordinates - A point in the form of {x:lat,y:lng}. * @param {{x,y} | PIXI.Point} coordinates - A point in the form of {x:lat,y:lng}.
* @returns {{x,y} | PIXI.Point} point - A pixel position on the map. * @returns {{x,y} | PIXI.Point} point - A pixel position on the map.
* @memberof MapData * @memberof MapData
@ -17546,6 +17548,18 @@
return point return point
} }
/**
* Get's the clipping of the map data. Clipping describes the
* piece of the map that is shown. E.g. if we just show a map of
* europe, then we have to set the clipping properly, otherwise
* the preojection would produce the wrong results when transforming
* from a point to coordinates or the other way around.
*
* @readonly
* @memberof MapData
* @returns {object} - Object that contains a min and max value of the clipping in form of: {min: {x,y}, max:{x,y}}. Where x and y are in between 0 and 1.
*/
get clip() { get clip() {
let unclipped = { let unclipped = {
min: { x: 0, y: 0 }, min: { x: 0, y: 0 },
@ -17555,30 +17569,30 @@
return this.opts.clip ? this.opts.clip : unclipped return this.opts.clip ? this.opts.clip : unclipped
} }
/** /**
* Bounds to pixel transforms some bounds in form of {min:{x:minLat, y:minLng},max:{x:maxLat, y:maxLng}} * Returns the biggest viewport the mapdata allows.
* to pixel coordinates. * This is determined by the projecton or the clipping on the mapapp.
* *
* @readonly
* @memberof MapData
*/ */
getBoundaries() {
// let min = this.toPixel(bounds.min)
// let max = this.toPixel(bounds.max)
// Y values needs to be swapped, as PIXI has it's origin
// in the top-left corner and a regular map in the bottom-left corner.
let boundaries = {
min: { x: 0, y: 0 },
max: { x: 1, y: 1 }
};
return boundaries
}
get maxViewport() { get maxViewport() {
return this.opts.clip ? this.opts.clip : this.projection.maxViewport return this.opts.clip ? this.opts.clip : this.projection.maxViewport
} }
} }
/**
* Special mapdata for DeepZoomMap objects.
*
* Note: It just transform the clipping parameter of the tiles config
* to the clipping of the mapapp.
*
* @export
* @class DeepZoomMapData
* @extends {MapData}
*/
class DeepZoomMapData extends MapData { class DeepZoomMapData extends MapData {
constructor(projection, tilesConfig, opts = {}) { constructor(projection, tilesConfig, opts = {}) {
if (tilesConfig.clip) { if (tilesConfig.clip) {
@ -17773,10 +17787,18 @@
} }
flushHandlers() { flushHandlers() {
// this.onLoaded this.onLoad.empty();
this.onTransform.empty(); this.onTransform.empty();
} }
/**
* Locks all transformations on the map.
* Single parameters can be set if necessary. False means the value is locked, true means they can be modified.
*
* @public
* @param {object} [{ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false }={}]
* @memberof GeoMap
*/
lock({ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false } = {}) { lock({ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false } = {}) {
if (this.image && this.image.scatter) { if (this.image && this.image.scatter) {
this.image.scatter.translatable = rotatable; this.image.scatter.translatable = rotatable;
@ -17785,30 +17807,26 @@
this.image.scatter.rotatable = movableY; this.image.scatter.rotatable = movableY;
this.image.scatter.scalable = scalable; 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
} }
/**
* Unlocks all transformations on the map.
* Single parameters can be set if necessary. False means the value is locked, true means they can be modified.
*
* @public
* @param {object} [{ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false }={}]
* @memberof GeoMap
*/
unlock({ rotatable = true, translatable = true, movableX = true, movableY = true, scalable = true } = {}) { unlock({ rotatable = true, translatable = true, movableX = true, movableY = true, scalable = true } = {}) {
if (this.image && this.image.scatter) { this.lock({ rotatable, translatable, movableX, movableY, scalable });
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
} }
/**
* Unloads the image of the map.
*
* @public
* @memberof GeoMap
*/
unload() { unload() {
if (this.image) { if (this.image) {
if (this.image.parent) { if (this.image.parent) {
@ -17822,6 +17840,12 @@
} }
} }
/**
* Removes the map, freeing all memory ba flushing handlers and removing the image.
*
* @public
* @memberof GeoMap
*/
remove() { remove() {
if (this.image) this.image.mask = null; if (this.image) this.image.mask = null;
@ -17855,8 +17879,6 @@
this.image = image; this.image = image;
if (frame) this.setFrame(frame); if (frame) this.setFrame(frame);
let boundaries = this.mapdata.getBoundaries();
let scatterOpts = Object.assign({ let scatterOpts = Object.assign({
cover: this.cover, cover: this.cover,
scaleable: this.scaleable, scaleable: this.scaleable,
@ -17866,7 +17888,6 @@
startScale: this.startScale, startScale: this.startScale,
minScale: this.minScale, minScale: this.minScale,
maxScale: this.maxScale, maxScale: this.maxScale,
boundaries,
onTransform: this.transformed.bind(this) onTransform: this.transformed.bind(this)
}); });
@ -17922,7 +17943,7 @@
* to a coordinate with latitude and longitude. * to a coordinate with latitude and longitude.
* *
* *
* @param {object} point - Point in form of {x: x_val, y: y_val}. * @param {object} point - Point in form of {x, y}.
* @returns {object} - Coordinates on the map in form of {x: latitude, y: longitude}. * @returns {object} - Coordinates on the map in form of {x: latitude, y: longitude}.
*/ */
coordinatesFromPoint(point) { coordinatesFromPoint(point) {
@ -17934,7 +17955,7 @@
* Transform coordinates in the map into pixel positions on the deep zoom image. * 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}. * @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}. * @return {Point} - Returns a image position in form of {x: x, y: y}.
*/ */
coordinatesToPoint(coordinates) { coordinatesToPoint(coordinates) {
return this.toAbsolutePixelCoordinates(this.mapdata.toPixel(coordinates)) return this.toAbsolutePixelCoordinates(this.mapdata.toPixel(coordinates))
@ -19630,6 +19651,13 @@
} }
} }
/**
* MapList is a list of maps with one active index.
* It contains some utility functions to change the map.
*
* @export
* @class MapList
*/
class MapList { class MapList {
constructor(active = null, maps = {}) { constructor(active = null, maps = {}) {
this.maps = maps; this.maps = maps;
@ -19641,8 +19669,9 @@
/** /**
* Selects a map from the map list. * Selects a map from the map list.
* *
* @public
* @param {string} active - Name of the map to select. * @param {string} active - Name of the map to select.
* @returns * @returns {Map} - Returns the active map. Returns null if no map was added to the MapList.
* @memberof MapList * @memberof MapList
*/ */
select(active) { select(active) {
@ -19672,6 +19701,13 @@
return map return map
} }
/**
* Clones the entire maplist.
*
* @public
* @returns {MapList} - Returns a cloned instance of this map list.
* @memberof MapList
*/
clone() { clone() {
let maps = {}; let maps = {};
@ -19682,6 +19718,14 @@
return new MapList(this.active, maps) return new MapList(this.active, maps)
} }
/**
* Adds a new map to the map list.
*
* @public
* @param {string} key - Key to identify the map.
* @param {GeoMap} map - The GeoMap to add.
* @memberof MapList
*/
add(key, map) { add(key, map) {
if (this.maps[key] != null) consol.warn('Key already in mapList. The existing key was overwritten.'); if (this.maps[key] != null) consol.warn('Key already in mapList. The existing key was overwritten.');
if (this.active == null) this.active = key; if (this.active == null) this.active = key;
@ -19689,10 +19733,25 @@
this.maps[key] = map; this.maps[key] = map;
} }
/**
* Returns the the active map.
* If none is set, it returns null.
*
*@public
* @readonly
* @memberof MapList
*/
get map() { get map() {
return this.maps && this.maps[this.active] ? this.maps[this.active] : null return this.maps && this.maps[this.active] ? this.maps[this.active] : null
} }
/**
* Selects the next map in the map array.
*
* @public
* @returns {GeoMap} - Returns the next map in the list.
* @memberof MapList
*/
next() { next() {
let keys = Object.keys(this.maps); let keys = Object.keys(this.maps);
let idx = keys.indexOf(this.active); let idx = keys.indexOf(this.active);
@ -19701,6 +19760,13 @@
return next return next
} }
/**
* Removes all maps from the maplist.
* And cleans up all maps.
*
* @public
* @memberof MapList
*/
cleanup() { cleanup() {
for (let key in this.maps) { for (let key in this.maps) {
let map = this.maps[key]; let map = this.maps[key];
@ -20283,9 +20349,9 @@
} }
selectMap(key) { selectMap(key) {
if (this.debug) console.log('Select map', key, result);
let result = this.mapList.select(key); let result = this.mapList.select(key);
console.log('Select map', key, result);
if (result && this.mapLayer) { if (result && this.mapLayer) {
this.mapLayer.changeMap(this.mapList.map); this.mapLayer.changeMap(this.mapList.map);
} }
@ -20318,6 +20384,14 @@
this.onTransform.call(this, event); this.onTransform.call(this, event);
} }
/**
*
* Called when the mapLayer changed the map.
*
* @private
* @param {*} lastMap
* @memberof MapApp
*/
_mapChanged(lastMap) { _mapChanged(lastMap) {
if (lastMap) { if (lastMap) {
lastMap.flushHandlers(); lastMap.flushHandlers();
@ -20648,8 +20722,7 @@
* *
* @static * @static
* @export * @export
* @class GeoJsonGraphics * @class
* @extends {GeoGraphics}
*/ */
class GeoJson { class GeoJson {
static isLineType(type) { static isLineType(type) {
@ -20701,7 +20774,6 @@
} }
list.push({ type, coordinates }); list.push({ type, coordinates });
// console.log({type, coordinates})
}); });
return list return list
@ -20765,7 +20837,7 @@
* considered valid. A complete list is provided in the GeoUtils. * considered valid. A complete list is provided in the GeoUtils.
* *
* @param {object} point - The point that is tested for validity. * @param {object} point - The point that is tested for validity.
* @returns * @returns {boolean}
* @memberof GeoJson * @memberof GeoJson
*/ */
static validateAndConvertPoint(point) { static validateAndConvertPoint(point) {
@ -20944,7 +21016,7 @@
* {latitude: lat, longitude: lng} * {latitude: lat, longitude: lng}
* *
* @static * @static
* @param {object / array} coordinate - Coordinate to be tested, if it is an valid coordinate. * @param {object|array} coordinate - Coordinate to be tested, if it is an valid coordinate.
* @returns - Returns the coordinate properly transformed. If transformation was not possible, it returns null. * @returns - Returns the coordinate properly transformed. If transformation was not possible, it returns null.
* @memberof GeoGraphics * @memberof GeoGraphics
*/ */

View File

@ -145,43 +145,49 @@ export default class Doctest {
let container = document.createElement('div') let container = document.createElement('div')
container.className = 'doctest-wrapper' container.className = 'doctest-wrapper'
let titleParent = container if (doctest.hasAttribute('data-title') || doctest.hasAttribute('data-collapsible')) {
if (doctest.hasAttribute('data-collapsible')) { let titlebar = document.createElement('div')
let collapsibleToggle = document.createElement('div') titlebar.className = 'doctest-titlebar'
titlebar.style = 'min-height: 10px;'
container.appendChild(titlebar)
let icon = document.createElement('i') if (doctest.hasAttribute('data-title')) {
icon.className = 'material-icons' let title = document.createElement('h6')
collapsibleToggle.appendChild(icon) title.innerText = doctest.getAttribute('data-title')
title.className = 'doctest-section-title'
titlebar.appendChild(title)
}
collapsibleToggle.className = 'doctest-collapsible-toggle' if (doctest.hasAttribute('data-collapsible')) {
collapsibleToggle.style = 'min-height: 10px;' let icon = document.createElement('i')
titleParent = collapsibleToggle icon.className = 'material-icons'
container.appendChild(collapsibleToggle) titlebar.classList.add('doctest-collapsible-toggle')
const collapsedClass = 'collapsed' if (titlebar.childNodes.length > 0) {
titlebar.insertBefore(icon, titlebar.childNodes[0])
function setToggleMode(collapse) {
if (collapse) {
container.classList.add(collapsedClass)
icon.innerText = 'arrow_drop_down'
} else { } else {
container.classList.remove(collapsedClass) titlebar.appendChild(icon)
icon.innerText = 'arrow_drop_up'
} }
}
function determineToggleMode() { const collapsedClass = 'collapsed'
setToggleMode(!container.classList.contains(collapsedClass))
}
setToggleMode(doctest.hasAttribute('data-collapsed')) function setToggleMode(collapse) {
collapsibleToggle.addEventListener('click', determineToggleMode) if (collapse) {
} container.classList.add(collapsedClass)
if (doctest.hasAttribute('data-title')) { icon.innerText = 'arrow_drop_down'
let title = document.createElement('h6') } else {
title.innerText = doctest.getAttribute('data-title') container.classList.remove(collapsedClass)
title.className = 'doctest-section-title' icon.innerText = 'arrow_drop_up'
titleParent.appendChild(title) }
}
function determineToggleMode() {
setToggleMode(!container.classList.contains(collapsedClass))
}
setToggleMode(doctest.hasAttribute('data-collapsed'))
titlebar.addEventListener('click', determineToggleMode)
}
} }
let pre = document.createElement('pre') let pre = document.createElement('pre')

View File

@ -62,7 +62,6 @@ window.FontInfo = FontInfo
window.Text = Text window.Text = Text
//Maps //Maps
import MapView from './maps/mapview.js'
import { GeoMap, ImageMap, DeepZoomMap } from './maps/map.js' import { GeoMap, ImageMap, DeepZoomMap } from './maps/map.js'
import { MapData, DeepZoomMapData } from './maps/mapdata.js' import { MapData, DeepZoomMapData } from './maps/mapdata.js'
@ -80,7 +79,8 @@ window.Projection = {
Robinson Robinson
} }
window.MapView = MapView import MapViewport from './maps/mapviewport.js'
window.MapViewport = MapViewport
import MapApp from './maps/mapapp.js' import MapApp from './maps/mapapp.js'

View File

@ -37,6 +37,7 @@ const index = new Index(itemTemplate, [
['Flippable', 'flippable.html'], ['Flippable', 'flippable.html'],
['LabeledGraphics', 'labeledgraphics.html'], ['LabeledGraphics', 'labeledgraphics.html'],
['List', 'list.html'], ['List', 'list.html'],
['Maps', 'maps/index.html'],
['Message', 'message.html'], ['Message', 'message.html'],
['Modal', 'modal.html'], ['Modal', 'modal.html'],
['Tooltip', 'tooltip.html'], ['Tooltip', 'tooltip.html'],

View File

@ -6,8 +6,7 @@ import { GeoGraphics, GeoShape, GeoMultiShape, GeoLine, GeoPoint } from './geogr
* *
* @static * @static
* @export * @export
* @class GeoJsonGraphics * @class
* @extends {GeoGraphics}
*/ */
export default class GeoJson { export default class GeoJson {
static isLineType(type) { static isLineType(type) {
@ -59,7 +58,6 @@ export default class GeoJson {
} }
list.push({ type, coordinates }) list.push({ type, coordinates })
// console.log({type, coordinates})
}) })
return list return list
@ -123,7 +121,7 @@ export default class GeoJson {
* considered valid. A complete list is provided in the GeoUtils. * considered valid. A complete list is provided in the GeoUtils.
* *
* @param {object} point - The point that is tested for validity. * @param {object} point - The point that is tested for validity.
* @returns * @returns {boolean}
* @memberof GeoJson * @memberof GeoJson
*/ */
static validateAndConvertPoint(point) { static validateAndConvertPoint(point) {
@ -302,7 +300,7 @@ export class GeoUtils {
* {latitude: lat, longitude: lng} * {latitude: lat, longitude: lng}
* *
* @static * @static
* @param {object / array} coordinate - Coordinate to be tested, if it is an valid coordinate. * @param {object|array} coordinate - Coordinate to be tested, if it is an valid coordinate.
* @returns - Returns the coordinate properly transformed. If transformation was not possible, it returns null. * @returns - Returns the coordinate properly transformed. If transformation was not possible, it returns null.
* @memberof GeoGraphics * @memberof GeoGraphics
*/ */

View File

@ -1,9 +1,9 @@
import { GeoMap } from './map.js' import { GeoMap } from './map.js'
import MapView from './mapview.js'
import { EventHandler } from './utils.js' import { EventHandler } from './utils.js'
import { GeoGraphics } from './geographics.js' import { GeoGraphics } from './geographics.js'
import { MapList } from './maplist.js' import { MapList } from './maplist.js'
//import { GeoGraphics } from "../pixi/geographics.js" import MapViewport from './mapviewport.js'
import { ScatterContainer } from '../scatter.js'
/** /**
* The GeoLayer is a special PIXILayer, that recognizes other GeoLayers and * The GeoLayer is a special PIXILayer, that recognizes other GeoLayers and
@ -93,6 +93,13 @@ export class GeoLayer {
return this._visibility return this._visibility
} }
/**
* Alias for geoLayer.displayObject.addChild.
*
* @public
* @param {GeoGraphics | PIXI.DisplayObject} element - Element to add to the displayObject.
* @memberof GeoLayer
*/
addChild(element) { addChild(element) {
this.displayObject.addChild(element) this.displayObject.addChild(element)
} }
@ -117,15 +124,6 @@ export class GeoLayer {
} else console.error('There was no map specified.', this) } 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() { removeFromParent() {
if (this.parent) { if (this.parent) {
this.parent.removeLayer(this) this.parent.removeLayer(this)
@ -165,8 +163,15 @@ export class GeoLayer {
return this._parent return this._parent
} }
/**
* Adds a GeoLayer as child to the GeoLayer.
*
* @public
* @param {GeoLayer} layer - GeoLayer to add.
* @memberof GeoLayer
*/
addLayer(layer) { addLayer(layer) {
if (layer instanceof GeoLayer || layer instanceof MapLayer) { if (layer instanceof GeoLayer) {
layer.removeFromParent() layer.removeFromParent()
this.layers.push(layer) this.layers.push(layer)
@ -218,6 +223,18 @@ export class GeoLayer {
// } // }
} }
/**
* The map layer is responsible for showing certain maps, at a specific position It contains
* a list of available maps and can switch between them seamlessly. GeoGraphics placed on the MapLayer itself
* or child Geolayers will be adapted to maps and adjusted on map change automatically.
*
* The map layer is the 'king' of the geo layers. Every geolayer
* needs a map layer at it's root. Otherwise they won't work-
*
* @export
* @class MapLayer
* @extends {GeoLayer}
*/
export class MapLayer extends GeoLayer { export class MapLayer extends GeoLayer {
constructor( constructor(
mapList, mapList,
@ -247,7 +264,7 @@ export class MapLayer extends GeoLayer {
listeners: onChange listeners: onChange
}) })
this.mapview = new MapView({ this.mapview = new MapViewport({
zoom, zoom,
focus, focus,
viewport viewport
@ -279,22 +296,34 @@ export class MapLayer extends GeoLayer {
this._mapChangeLocked = false this._mapChangeLocked = false
} }
/**
* Adapts all child layers and their GeoGraphics.
*
* This is called primarily on a map change.
*
* @private
* @memberof MapLayer
*/
adapt() { adapt() {
this.layers.forEach(layer => { this.layers.forEach(layer => {
if (layer.adapt) layer.adapt(this.map) if (layer.adapt) layer.adapt(this.map)
}) })
} }
focus(coordinates, zoom) {
this.mapview.updateFocusPoint(this.map)
}
transformed(e) { transformed(e) {
this.mapview.transformed(this.map) this.mapview.transformed(this.map)
this.layers.forEach(layer => layer.parentMapLayerTransformed(this)) this.layers.forEach(layer => layer.parentMapLayerTransformed(this))
this.transformHandler.call(this) this.transformHandler.call(this)
} }
/**
* Clones the map layer-
*
* @param {ScatterContainer} scatterContainer - ScatterContainer of the app.
* @param {PIXI.DisplayObject} [container=null] - Container of the newly created MapLayer. If null, an empty PIXI.Container will be created.
* @returns
* @memberof MapLayer
*/
clone(scatterContainer, container = null) { clone(scatterContainer, container = null) {
let mapList = this.mapList.clone() let mapList = this.mapList.clone()
container = container == null ? new PIXI.Container() : container container = container == null ? new PIXI.Container() : container
@ -369,23 +398,46 @@ export class MapLayer extends GeoLayer {
} }
} }
/**
* Applies the mapviews focus to the map.
* This may be useful, if the container was modified.
*
* @memberof MapLayer
*/
refocus() { refocus() {
this.mapview.apply(this.map) this.mapview.apply(this.map)
} }
/**
* @public
* @returns {GeoMap} - Returns the active map.
* @readonly
* @memberof MapLayer
*/
get map() { get map() {
return this.mapList.map return this.mapList.map
} }
/** /**
* This is required for the consistency of georelated layers. *
* The request traverses up to the mapLayer where it then returns * This is required for the geo layers.
* the responsible map layer. * MapLayer requests from the geoLayers traverse up to the next MapLayer.
*
* @public
* @returns {MapLayer} - Returns this MapLayer.
* @readonly
* @memberof MapLayer
*/ */
get mapLayer() { get mapLayer() {
return this return this
} }
/**
* Cleans up the MapLayer.
*
* @public
* @memberof MapLayer
*/
cleanup() { cleanup() {
this.mapList.cleanup() this.mapList.cleanup()
} }

112
lib/pixi/maps/index.html Normal file
View File

@ -0,0 +1,112 @@
<html>
<head>
<title>PIXI Maps Doctests</title>
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="viewport"
content="width=device-width, height=device-height, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0" />
<link rel="stylesheet" href="../../../css/index.css">
<script src="../../../dist/iwmlib.js"></script>
<template id="itemTemplate">
<a class="wrapper" href="">
<div class="preview">
<div class="thumbnail-container">
<div class="thumbnail">
<img class="icon" src="thumbnails/notfound.png">
<!-- <iframe src="" frameborder="0"></iframe> -->
</div>
</div>
<div class="title"></div>
</div>
</a>
</template>
<style>
body {
font-size: 20px;
display: flex;
flex-direction: column;
}
#logo {
left: 0;
position: absolute;
top: 30px;
}
#logo>img {
width: 80px;
}
header>h1 {
text-transform: uppercase;
letter-spacing: 0.05em;
word-spacing: 0.25em;
margin-top: 0;
}
header>p {
max-width: 720px;
line-height: 1.5em;
color: rgb(207, 207, 207);
}
header {
font-family: "Open Sans", sans-serif;
background-color: #4c4f4f;
color: whitesmoke;
padding: 68px 50px 10px 150px;
box-shadow: 0 0 3px rgba(0, 0, 0, 0.5);
}
.container {
justify-content: center;
flex: 1;
height: auto;
min-height: auto;
width: auto;
min-width: auto;
margin: 0 60px;
}
</style>
</head>
<body>
<header>
<a id="logo" target="_blank" href="http://www.iwm-tuebingen.de">
<img src="../../../assets/logos/iwm_logo_2015_twitter.png">
</a>
<h1>Maps Module</h1>
<p>The maps module provides a handy toolkit to easily integrate maps in an application. Create a full screen map
application by using the mapapp. Utilize the GeoLayer-system to integrate maps in an existing application.
Draw graphics onto the map using geographical positions instead of pixel positions with the GeoGraphics.
Or just use an Overlay to quickly draw icons for each point on a map.
</p>
</header>
<div id="container" class="container">
</div>
<script>
const index = new Index(itemTemplate, [
["GeoGraphics", "geographics.html"],
["GeoJson", "geojson.html"],
["GeoMap", "map.html"],
["MapApp", "mapapp.html"],
["MapData", "mapdata.html"],
["MapViewport", "mapviewport.html"],
["Overlay", "overlay.html"],
["Scatter", "scatter.html"]
],
null)
index.load()
</script>
</body>
</html>

View File

@ -93,10 +93,18 @@ export class GeoMap {
} }
flushHandlers() { flushHandlers() {
// this.onLoaded this.onLoad.empty()
this.onTransform.empty() this.onTransform.empty()
} }
/**
* Locks all transformations on the map.
* Single parameters can be set if necessary. False means the value is locked, true means they can be modified.
*
* @public
* @param {object} [{ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false }={}]
* @memberof GeoMap
*/
lock({ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false } = {}) { lock({ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false } = {}) {
if (this.image && this.image.scatter) { if (this.image && this.image.scatter) {
this.image.scatter.translatable = rotatable this.image.scatter.translatable = rotatable
@ -105,30 +113,26 @@ export class GeoMap {
this.image.scatter.rotatable = movableY this.image.scatter.rotatable = movableY
this.image.scatter.scalable = scalable 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
} }
/**
* Unlocks all transformations on the map.
* Single parameters can be set if necessary. False means the value is locked, true means they can be modified.
*
* @public
* @param {object} [{ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false }={}]
* @memberof GeoMap
*/
unlock({ rotatable = true, translatable = true, movableX = true, movableY = true, scalable = true } = {}) { unlock({ rotatable = true, translatable = true, movableX = true, movableY = true, scalable = true } = {}) {
if (this.image && this.image.scatter) { this.lock({ rotatable, translatable, movableX, movableY, scalable })
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
} }
/**
* Unloads the image of the map.
*
* @public
* @memberof GeoMap
*/
unload() { unload() {
if (this.image) { if (this.image) {
if (this.image.parent) { if (this.image.parent) {
@ -142,6 +146,12 @@ export class GeoMap {
} }
} }
/**
* Removes the map, freeing all memory ba flushing handlers and removing the image.
*
* @public
* @memberof GeoMap
*/
remove() { remove() {
if (this.image) this.image.mask = null if (this.image) this.image.mask = null
@ -175,8 +185,6 @@ export class GeoMap {
this.image = image this.image = image
if (frame) this.setFrame(frame) if (frame) this.setFrame(frame)
let boundaries = this.mapdata.getBoundaries()
let scatterOpts = Object.assign({ let scatterOpts = Object.assign({
cover: this.cover, cover: this.cover,
scaleable: this.scaleable, scaleable: this.scaleable,
@ -186,7 +194,6 @@ export class GeoMap {
startScale: this.startScale, startScale: this.startScale,
minScale: this.minScale, minScale: this.minScale,
maxScale: this.maxScale, maxScale: this.maxScale,
boundaries,
onTransform: this.transformed.bind(this) onTransform: this.transformed.bind(this)
}) })
@ -242,7 +249,7 @@ export class GeoMap {
* to a coordinate with latitude and longitude. * to a coordinate with latitude and longitude.
* *
* *
* @param {object} point - Point in form of {x: x_val, y: y_val}. * @param {object} point - Point in form of {x, y}.
* @returns {object} - Coordinates on the map in form of {x: latitude, y: longitude}. * @returns {object} - Coordinates on the map in form of {x: latitude, y: longitude}.
*/ */
coordinatesFromPoint(point) { coordinatesFromPoint(point) {
@ -254,7 +261,7 @@ export class GeoMap {
* Transform coordinates in the map into pixel positions on the deep zoom image. * 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}. * @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}. * @return {Point} - Returns a image position in form of {x: x, y: y}.
*/ */
coordinatesToPoint(coordinates) { coordinatesToPoint(coordinates) {
return this.toAbsolutePixelCoordinates(this.mapdata.toPixel(coordinates)) return this.toAbsolutePixelCoordinates(this.mapdata.toPixel(coordinates))

View File

@ -11,6 +11,9 @@ import { MapList } from './maplist.js'
* MapApp is responsible for showing fullscreen * MapApp is responsible for showing fullscreen
* map applications. * map applications.
* *
* @export
* @class MapApp
* @extends {PIXIApp}
*/ */
export default class MapApp extends PIXIApp { export default class MapApp extends PIXIApp {
constructor(opts = {}) { constructor(opts = {}) {
@ -191,9 +194,9 @@ export default class MapApp extends PIXIApp {
} }
selectMap(key) { selectMap(key) {
if (this.debug) console.log('Select map', key, result)
let result = this.mapList.select(key) let result = this.mapList.select(key)
console.log('Select map', key, result)
if (result && this.mapLayer) { if (result && this.mapLayer) {
this.mapLayer.changeMap(this.mapList.map) this.mapLayer.changeMap(this.mapList.map)
} }
@ -226,6 +229,14 @@ export default class MapApp extends PIXIApp {
this.onTransform.call(this, event) this.onTransform.call(this, event)
} }
/**
*
* Called when the mapLayer changed the map.
*
* @private
* @param {*} lastMap
* @memberof MapApp
*/
_mapChanged(lastMap) { _mapChanged(lastMap) {
if (lastMap) { if (lastMap) {
lastMap.flushHandlers() lastMap.flushHandlers()

View File

@ -14,7 +14,7 @@ export class MapData {
* @constructor * @constructor
* @param {Projection}[projection] - Specifies the projection of the map (e.g. Mercator Projection). * @param {Projection}[projection] - Specifies the projection of the map (e.g. Mercator Projection).
* @param {object}[opts] - Addiditonal options. * @param {object}[opts] - Addiditonal options.
* @param {[[minLat, minLng],[maxLat, maxLng]]}[opts.bounds] - Describes the minimum and maximum coordinates on the map * @param {array}[opts.bounds] - Describes the minimum and maximum coordinates on the map in the form of {[[minLat, minLng],[maxLat, maxLng]]}.
* @param {Point}[opts.translate] - Defines a translation, when clipping is not an option (e.g. when the whole world is shown, but translated.) * @param {Point}[opts.translate] - Defines a translation, when clipping is not an option (e.g. when the whole world is shown, but translated.)
*/ */
constructor(projection, opts = {}) { constructor(projection, opts = {}) {
@ -52,6 +52,7 @@ export class MapData {
/** /**
* Transforms a pixel point on the map to a geographical coordinate. * Transforms a pixel point on the map to a geographical coordinate.
* *
* @public
* @param {{x,y} | PIXI.Point} point - A pixel position on the map. * @param {{x,y} | PIXI.Point} point - A pixel position on the map.
* @returns {{x,y} | PIXI.Point} - A geographical coordinate. * @returns {{x,y} | PIXI.Point} - A geographical coordinate.
* @memberof MapData * @memberof MapData
@ -84,6 +85,7 @@ export class MapData {
/** /**
* Transform a geographical coordinate to a pixel point on the map. * Transform a geographical coordinate to a pixel point on the map.
* *
* @public
* @param {{x,y} | PIXI.Point} coordinates - A point in the form of {x:lat,y:lng}. * @param {{x,y} | PIXI.Point} coordinates - A point in the form of {x:lat,y:lng}.
* @returns {{x,y} | PIXI.Point} point - A pixel position on the map. * @returns {{x,y} | PIXI.Point} point - A pixel position on the map.
* @memberof MapData * @memberof MapData
@ -116,6 +118,18 @@ export class MapData {
return point return point
} }
/**
* Get's the clipping of the map data. Clipping describes the
* piece of the map that is shown. E.g. if we just show a map of
* europe, then we have to set the clipping properly, otherwise
* the preojection would produce the wrong results when transforming
* from a point to coordinates or the other way around.
*
* @readonly
* @memberof MapData
* @returns {object} - Object that contains a min and max value of the clipping in form of: {min: {x,y}, max:{x,y}}. Where x and y are in between 0 and 1.
*/
get clip() { get clip() {
let unclipped = { let unclipped = {
min: { x: 0, y: 0 }, min: { x: 0, y: 0 },
@ -125,30 +139,30 @@ export class MapData {
return this.opts.clip ? this.opts.clip : unclipped return this.opts.clip ? this.opts.clip : unclipped
} }
/** /**
* Bounds to pixel transforms some bounds in form of {min:{x:minLat, y:minLng},max:{x:maxLat, y:maxLng}} * Returns the biggest viewport the mapdata allows.
* to pixel coordinates. * This is determined by the projecton or the clipping on the mapapp.
* *
* @readonly
* @memberof MapData
*/ */
getBoundaries() {
// let min = this.toPixel(bounds.min)
// let max = this.toPixel(bounds.max)
// Y values needs to be swapped, as PIXI has it's origin
// in the top-left corner and a regular map in the bottom-left corner.
let boundaries = {
min: { x: 0, y: 0 },
max: { x: 1, y: 1 }
}
return boundaries
}
get maxViewport() { get maxViewport() {
return this.opts.clip ? this.opts.clip : this.projection.maxViewport return this.opts.clip ? this.opts.clip : this.projection.maxViewport
} }
} }
/**
* Special mapdata for DeepZoomMap objects.
*
* Note: It just transform the clipping parameter of the tiles config
* to the clipping of the mapapp.
*
* @export
* @class DeepZoomMapData
* @extends {MapData}
*/
export class DeepZoomMapData extends MapData { export class DeepZoomMapData extends MapData {
constructor(projection, tilesConfig, opts = {}) { constructor(projection, tilesConfig, opts = {}) {
if (tilesConfig.clip) { if (tilesConfig.clip) {

View File

@ -1,3 +1,10 @@
/**
* MapList is a list of maps with one active index.
* It contains some utility functions to change the map.
*
* @export
* @class MapList
*/
export class MapList { export class MapList {
constructor(active = null, maps = {}) { constructor(active = null, maps = {}) {
this.maps = maps this.maps = maps
@ -9,8 +16,9 @@ export class MapList {
/** /**
* Selects a map from the map list. * Selects a map from the map list.
* *
* @public
* @param {string} active - Name of the map to select. * @param {string} active - Name of the map to select.
* @returns * @returns {Map} - Returns the active map. Returns null if no map was added to the MapList.
* @memberof MapList * @memberof MapList
*/ */
select(active) { select(active) {
@ -40,6 +48,13 @@ export class MapList {
return map return map
} }
/**
* Clones the entire maplist.
*
* @public
* @returns {MapList} - Returns a cloned instance of this map list.
* @memberof MapList
*/
clone() { clone() {
let maps = {} let maps = {}
@ -50,6 +65,14 @@ export class MapList {
return new MapList(this.active, maps) return new MapList(this.active, maps)
} }
/**
* Adds a new map to the map list.
*
* @public
* @param {string} key - Key to identify the map.
* @param {GeoMap} map - The GeoMap to add.
* @memberof MapList
*/
add(key, map) { add(key, map) {
if (this.maps[key] != null) consol.warn('Key already in mapList. The existing key was overwritten.') if (this.maps[key] != null) consol.warn('Key already in mapList. The existing key was overwritten.')
if (this.active == null) this.active = key if (this.active == null) this.active = key
@ -57,10 +80,25 @@ export class MapList {
this.maps[key] = map this.maps[key] = map
} }
/**
* Returns the the active map.
* If none is set, it returns null.
*
*@public
* @readonly
* @memberof MapList
*/
get map() { get map() {
return this.maps && this.maps[this.active] ? this.maps[this.active] : null return this.maps && this.maps[this.active] ? this.maps[this.active] : null
} }
/**
* Selects the next map in the map array.
*
* @public
* @returns {GeoMap} - Returns the next map in the list.
* @memberof MapList
*/
next() { next() {
let keys = Object.keys(this.maps) let keys = Object.keys(this.maps)
let idx = keys.indexOf(this.active) let idx = keys.indexOf(this.active)
@ -69,6 +107,13 @@ export class MapList {
return next return next
} }
/**
* Removes all maps from the maplist.
* And cleans up all maps.
*
* @public
* @memberof MapList
*/
cleanup() { cleanup() {
for (let key in this.maps) { for (let key in this.maps) {
let map = this.maps[key] let map = this.maps[key]

View File

@ -0,0 +1,163 @@
<!DOCTYPE html>
<html lang="en" class="dark-mode">
<head>
<meta charset="UTF-8" />
<title>MapViewport</title>
<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'>
<script src="../../../dist/iwmlib.3rdparty.js"></script>
<script src="../../../dist/iwmlib.js"></script>
<script src="../../../dist/iwmlib.pixi.js"></script>
<style>
.controls {
display: flex;
}
</style>
</head>
<body onload="Doctest.run()">
<h1>MapViewport</h1>
<p>
The MapViewport works under the hood of a map layer to track the informations about the current focus point and
zoom position.
This is important to maintain the same view when maps are changed.
</p>
<canvas id="canvas"></canvas>
<div id="mapControl"></div>
<div id="cityControl" class="controls"></div>
<p><strong>WHAT TO SEE:</strong> The map should focus Paris.</p>
<script>
let osmConfig = {
"projection": "mercator",
"type": "deepzoom",
"tiles": {
"tileSize": 256,
"format": "png",
"overlap": 0,
"type": "map",
"height": 1024,
"width": 1024,
"path": "../assets/maps/osm",
"urlTileTemplate": "{path}/{level}/{row}/{column}.{format}"
}
}
let testConfig = {
"projection": "mercator",
"type": "deepzoom",
"tiles": {
"tileSize": 128,
"format": "jpg",
"overlap": 0,
"type": "map",
"height": 4096,
"width": 4096,
"path": "../assets/maps/test",
"urlTileTemplate": "{path}/{level}/{row}/{column}.{format}"
}
}
</script>
<script class="doctest">
let capitals = {
london: { x: 51.5, y: -0.083333 },
rome: { x: 41.9, y: 12.483333 },
madrid: { x: 40.4, y: -3.683333 },
paris: { x: 48.833986, y: 2.346989 }
}
// You may define a focus point ...
let focus = capitals.paris
// ... and a zoom level.
let zoom = 1
// Name has to be app (like all other PIXIApps).
const app = (window.app = new MapApp({
focus,
zoom,
view: canvas,
coordsLogging: true,
width: 512,
height: 512
}))
// As map an image of europe is used.
let europe = '../assets/maps/pixabay/europe.jpg'
//Preload all required sprites for the image map.
app.loadSprites([europe], sprites => ready(sprites), {
resolutionDependent: false
})
// The mapdata object contains informations,
// how the displayed map has to be interpreted.
// e.g. which projection is used or how the
// image is clipped.
let europeData = new MapData(new Projection.Mercator(), {
clip: {
min: { x: 32.863294, y: -18.58 },
max: { x: 57.467973, y: 44.277158 }
}
})
function ready(sprites) {
const cover = true
// When resources are loaded, the ImageMap can be instantiated.
let imageMap = new ImageMap(sprites.get(europe), europeData, {
coordsLogging: true,
maxScale: 1,
cover
})
let testMapData = new DeepZoomMapData(new Projection.Mercator(), testConfig.tiles, {
app
})
let testMap = new DeepZoomMap(testMapData, Object.assign({}, testConfig.tiles, { app }), { cover })
app.addMap("test", testMap)
let osmMapData = new DeepZoomMapData(new Projection.Mercator(), osmConfig.tiles, {
app
})
let deepZoomMap = new DeepZoomMap(osmMapData, Object.assign({}, osmConfig.tiles, { app }), { cover })
app.addMap("osm", deepZoomMap)
// Finally apply the map to the MapApp
app.setMap('europe', imageMap)
// The app requires a map before beeing able to run.
// So start the app here.
app.setup().run()
for (let [key, val] of Object.entries(app.mapList.maps)) {
let mapBtn = document.createElement("button")
mapBtn.innerText = key
mapBtn.addEventListener("click", () => {
app.mapLayer.changeMap(val)
})
mapControl.appendChild(mapBtn)
}
}
for (let [key, val] of Object.entries(capitals)) {
let cityBtn = document.createElement("button")
cityBtn.innerText = key
cityBtn.addEventListener("click", () => {
app.mapLayer.map.moveTo(val)
})
cityControl.appendChild(cityBtn)
}
</script>
</body>
</html>

View File

@ -6,7 +6,7 @@ import { DeepZoomMap } from './map.js'
* It ensures, that maps can be changed, without the user noticing it. * It ensures, that maps can be changed, without the user noticing it.
* *
*/ */
export default class MapView { export default class MapViewport {
/** /**
* *
* @param {object} [focus = {x:0, y:0}] - Defines the startup focuspoint of the app. * @param {object} [focus = {x:0, y:0}] - Defines the startup focuspoint of the app.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 184 KiB

After

Width:  |  Height:  |  Size: 172 KiB