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
.history/
.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
* @param {Projection}[projection] - Specifies the projection of the map (e.g. Mercator Projection).
* @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.)
*/
constructor(projection, opts = {}) {
@ -17482,6 +17482,7 @@
/**
* Transforms a pixel point on the map to a geographical coordinate.
*
* @public
* @param {{x,y} | PIXI.Point} point - A pixel position on the map.
* @returns {{x,y} | PIXI.Point} - A geographical coordinate.
* @memberof MapData
@ -17514,6 +17515,7 @@
/**
* 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}.
* @returns {{x,y} | PIXI.Point} point - A pixel position on the map.
* @memberof MapData
@ -17546,6 +17548,18 @@
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() {
let unclipped = {
min: { x: 0, y: 0 },
@ -17555,30 +17569,30 @@
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}}
* to pixel coordinates.
* Returns the biggest viewport the mapdata allows.
* 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() {
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 {
constructor(projection, tilesConfig, opts = {}) {
if (tilesConfig.clip) {
@ -17773,10 +17787,18 @@
}
flushHandlers() {
// this.onLoaded
this.onLoad.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 } = {}) {
if (this.image && this.image.scatter) {
this.image.scatter.translatable = rotatable;
@ -17785,30 +17807,26 @@
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
}
/**
* 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 } = {}) {
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
this.lock({ rotatable, translatable, movableX, movableY, scalable });
}
/**
* Unloads the image of the map.
*
* @public
* @memberof GeoMap
*/
unload() {
if (this.image) {
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() {
if (this.image) this.image.mask = null;
@ -17855,8 +17879,6 @@
this.image = image;
if (frame) this.setFrame(frame);
let boundaries = this.mapdata.getBoundaries();
let scatterOpts = Object.assign({
cover: this.cover,
scaleable: this.scaleable,
@ -17866,7 +17888,6 @@
startScale: this.startScale,
minScale: this.minScale,
maxScale: this.maxScale,
boundaries,
onTransform: this.transformed.bind(this)
});
@ -17922,7 +17943,7 @@
* 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}.
*/
coordinatesFromPoint(point) {
@ -17934,7 +17955,7 @@
* 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}.
* @return {Point} - Returns a image position in form of {x: x, y: y}.
*/
coordinatesToPoint(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 {
constructor(active = null, maps = {}) {
this.maps = maps;
@ -19641,8 +19669,9 @@
/**
* Selects a map from the map list.
*
* @public
* @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
*/
select(active) {
@ -19672,6 +19701,13 @@
return map
}
/**
* Clones the entire maplist.
*
* @public
* @returns {MapList} - Returns a cloned instance of this map list.
* @memberof MapList
*/
clone() {
let maps = {};
@ -19682,6 +19718,14 @@
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) {
if (this.maps[key] != null) consol.warn('Key already in mapList. The existing key was overwritten.');
if (this.active == null) this.active = key;
@ -19689,10 +19733,25 @@
this.maps[key] = map;
}
/**
* Returns the the active map.
* If none is set, it returns null.
*
*@public
* @readonly
* @memberof MapList
*/
get map() {
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() {
let keys = Object.keys(this.maps);
let idx = keys.indexOf(this.active);
@ -19701,6 +19760,13 @@
return next
}
/**
* Removes all maps from the maplist.
* And cleans up all maps.
*
* @public
* @memberof MapList
*/
cleanup() {
for (let key in this.maps) {
let map = this.maps[key];
@ -20283,9 +20349,9 @@
}
selectMap(key) {
if (this.debug) console.log('Select map', key, result);
let result = this.mapList.select(key);
console.log('Select map', key, result);
if (result && this.mapLayer) {
this.mapLayer.changeMap(this.mapList.map);
}
@ -20318,6 +20384,14 @@
this.onTransform.call(this, event);
}
/**
*
* Called when the mapLayer changed the map.
*
* @private
* @param {*} lastMap
* @memberof MapApp
*/
_mapChanged(lastMap) {
if (lastMap) {
lastMap.flushHandlers();
@ -20648,8 +20722,7 @@
*
* @static
* @export
* @class GeoJsonGraphics
* @extends {GeoGraphics}
* @class
*/
class GeoJson {
static isLineType(type) {
@ -20701,7 +20774,6 @@
}
list.push({ type, coordinates });
// console.log({type, coordinates})
});
return list
@ -20765,7 +20837,7 @@
* considered valid. A complete list is provided in the GeoUtils.
*
* @param {object} point - The point that is tested for validity.
* @returns
* @returns {boolean}
* @memberof GeoJson
*/
static validateAndConvertPoint(point) {
@ -20944,7 +21016,7 @@
* {latitude: lat, longitude: lng}
*
* @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.
* @memberof GeoGraphics
*/

View File

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

View File

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

View File

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

View File

@ -6,8 +6,7 @@ import { GeoGraphics, GeoShape, GeoMultiShape, GeoLine, GeoPoint } from './geogr
*
* @static
* @export
* @class GeoJsonGraphics
* @extends {GeoGraphics}
* @class
*/
export default class GeoJson {
static isLineType(type) {
@ -59,7 +58,6 @@ export default class GeoJson {
}
list.push({ type, coordinates })
// console.log({type, coordinates})
})
return list
@ -123,7 +121,7 @@ export default class GeoJson {
* considered valid. A complete list is provided in the GeoUtils.
*
* @param {object} point - The point that is tested for validity.
* @returns
* @returns {boolean}
* @memberof GeoJson
*/
static validateAndConvertPoint(point) {
@ -302,7 +300,7 @@ export class GeoUtils {
* {latitude: lat, longitude: lng}
*
* @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.
* @memberof GeoGraphics
*/

View File

@ -1,9 +1,9 @@
import { GeoMap } from './map.js'
import MapView from './mapview.js'
import { EventHandler } from './utils.js'
import { GeoGraphics } from './geographics.js'
import { MapList } from './maplist.js'
//import { GeoGraphics } from "../pixi/geographics.js"
import MapViewport from './mapviewport.js'
import { ScatterContainer } from '../scatter.js'
/**
* The GeoLayer is a special PIXILayer, that recognizes other GeoLayers and
@ -93,6 +93,13 @@ export class GeoLayer {
return this._visibility
}
/**
* Alias for geoLayer.displayObject.addChild.
*
* @public
* @param {GeoGraphics | PIXI.DisplayObject} element - Element to add to the displayObject.
* @memberof GeoLayer
*/
addChild(element) {
this.displayObject.addChild(element)
}
@ -117,15 +124,6 @@ export class GeoLayer {
} else console.error('There was no map specified.', this)
}
// place(geographic) {
// if (geographic.constructor.name.startsWith('Geo') && geographic.graphics) {
// // Fix to remove the rollupjs circular dependency
// //if (geographic instanceof GeoGraphics) {
// this.geographics.push(geographic)
// super.place(geographic.graphics)
// } else super.place(geographic)
// }
removeFromParent() {
if (this.parent) {
this.parent.removeLayer(this)
@ -165,8 +163,15 @@ export class GeoLayer {
return this._parent
}
/**
* Adds a GeoLayer as child to the GeoLayer.
*
* @public
* @param {GeoLayer} layer - GeoLayer to add.
* @memberof GeoLayer
*/
addLayer(layer) {
if (layer instanceof GeoLayer || layer instanceof MapLayer) {
if (layer instanceof GeoLayer) {
layer.removeFromParent()
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 {
constructor(
mapList,
@ -247,7 +264,7 @@ export class MapLayer extends GeoLayer {
listeners: onChange
})
this.mapview = new MapView({
this.mapview = new MapViewport({
zoom,
focus,
viewport
@ -279,22 +296,34 @@ export class MapLayer extends GeoLayer {
this._mapChangeLocked = false
}
/**
* Adapts all child layers and their GeoGraphics.
*
* This is called primarily on a map change.
*
* @private
* @memberof MapLayer
*/
adapt() {
this.layers.forEach(layer => {
if (layer.adapt) layer.adapt(this.map)
})
}
focus(coordinates, zoom) {
this.mapview.updateFocusPoint(this.map)
}
transformed(e) {
this.mapview.transformed(this.map)
this.layers.forEach(layer => layer.parentMapLayerTransformed(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) {
let mapList = this.mapList.clone()
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() {
this.mapview.apply(this.map)
}
/**
* @public
* @returns {GeoMap} - Returns the active map.
* @readonly
* @memberof MapLayer
*/
get 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
* the responsible map layer.
*
* This is required for the geo layers.
* MapLayer requests from the geoLayers traverse up to the next MapLayer.
*
* @public
* @returns {MapLayer} - Returns this MapLayer.
* @readonly
* @memberof MapLayer
*/
get mapLayer() {
return this
}
/**
* Cleans up the MapLayer.
*
* @public
* @memberof MapLayer
*/
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() {
// this.onLoaded
this.onLoad.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 } = {}) {
if (this.image && this.image.scatter) {
this.image.scatter.translatable = rotatable
@ -105,30 +113,26 @@ export class GeoMap {
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
}
/**
* 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 } = {}) {
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
this.lock({ rotatable, translatable, movableX, movableY, scalable })
}
/**
* Unloads the image of the map.
*
* @public
* @memberof GeoMap
*/
unload() {
if (this.image) {
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() {
if (this.image) this.image.mask = null
@ -175,8 +185,6 @@ export class GeoMap {
this.image = image
if (frame) this.setFrame(frame)
let boundaries = this.mapdata.getBoundaries()
let scatterOpts = Object.assign({
cover: this.cover,
scaleable: this.scaleable,
@ -186,7 +194,6 @@ export class GeoMap {
startScale: this.startScale,
minScale: this.minScale,
maxScale: this.maxScale,
boundaries,
onTransform: this.transformed.bind(this)
})
@ -242,7 +249,7 @@ export class GeoMap {
* 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}.
*/
coordinatesFromPoint(point) {
@ -254,7 +261,7 @@ export class GeoMap {
* 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}.
* @return {Point} - Returns a image position in form of {x: x, y: y}.
*/
coordinatesToPoint(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
* map applications.
*
* @export
* @class MapApp
* @extends {PIXIApp}
*/
export default class MapApp extends PIXIApp {
constructor(opts = {}) {
@ -191,9 +194,9 @@ export default class MapApp extends PIXIApp {
}
selectMap(key) {
if (this.debug) console.log('Select map', key, result)
let result = this.mapList.select(key)
console.log('Select map', key, result)
if (result && this.mapLayer) {
this.mapLayer.changeMap(this.mapList.map)
}
@ -226,6 +229,14 @@ export default class MapApp extends PIXIApp {
this.onTransform.call(this, event)
}
/**
*
* Called when the mapLayer changed the map.
*
* @private
* @param {*} lastMap
* @memberof MapApp
*/
_mapChanged(lastMap) {
if (lastMap) {
lastMap.flushHandlers()

View File

@ -14,7 +14,7 @@ export class MapData {
* @constructor
* @param {Projection}[projection] - Specifies the projection of the map (e.g. Mercator Projection).
* @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.)
*/
constructor(projection, opts = {}) {
@ -52,6 +52,7 @@ export class MapData {
/**
* Transforms a pixel point on the map to a geographical coordinate.
*
* @public
* @param {{x,y} | PIXI.Point} point - A pixel position on the map.
* @returns {{x,y} | PIXI.Point} - A geographical coordinate.
* @memberof MapData
@ -84,6 +85,7 @@ export class MapData {
/**
* 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}.
* @returns {{x,y} | PIXI.Point} point - A pixel position on the map.
* @memberof MapData
@ -116,6 +118,18 @@ export class MapData {
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() {
let unclipped = {
min: { x: 0, y: 0 },
@ -125,30 +139,30 @@ export class MapData {
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}}
* to pixel coordinates.
* Returns the biggest viewport the mapdata allows.
* 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() {
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 {
constructor(projection, tilesConfig, opts = {}) {
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 {
constructor(active = null, maps = {}) {
this.maps = maps
@ -9,8 +16,9 @@ export class MapList {
/**
* Selects a map from the map list.
*
* @public
* @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
*/
select(active) {
@ -40,6 +48,13 @@ export class MapList {
return map
}
/**
* Clones the entire maplist.
*
* @public
* @returns {MapList} - Returns a cloned instance of this map list.
* @memberof MapList
*/
clone() {
let maps = {}
@ -50,6 +65,14 @@ export class MapList {
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) {
if (this.maps[key] != null) consol.warn('Key already in mapList. The existing key was overwritten.')
if (this.active == null) this.active = key
@ -57,10 +80,25 @@ export class MapList {
this.maps[key] = map
}
/**
* Returns the the active map.
* If none is set, it returns null.
*
*@public
* @readonly
* @memberof MapList
*/
get map() {
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() {
let keys = Object.keys(this.maps)
let idx = keys.indexOf(this.active)
@ -69,6 +107,13 @@ export class MapList {
return next
}
/**
* Removes all maps from the maplist.
* And cleans up all maps.
*
* @public
* @memberof MapList
*/
cleanup() {
for (let key in this.maps) {
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>