Compare commits
4 Commits
8745554cee
...
65fac2f406
Author | SHA1 | Date |
---|---|---|
Severin Opel | 65fac2f406 | |
Severin Opel | 60e28f8fe5 | |
Severin Opel | 792892cb82 | |
Severin Opel | 9159073483 |
|
@ -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 |
134
css/doctest.css
134
css/doctest.css
|
@ -1,15 +1,47 @@
|
|||
:root {
|
||||
--white: rgb(250, 250, 250);
|
||||
--light-gray: rgb(219, 219, 219);
|
||||
--gray: rgb(66, 66, 66);
|
||||
--black: rgb(20, 20, 20);
|
||||
--dark-background: rgb(50, 50, 50);
|
||||
|
||||
--border-radius: 5px;
|
||||
}
|
||||
|
||||
html {
|
||||
padding: 0;
|
||||
font-size: 16px;
|
||||
background: white;
|
||||
color: #000;
|
||||
background: var(--white);
|
||||
color: var(--black);
|
||||
font-family: 'Open Sans', Arial, sans-serif;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.dark-mode nav{
|
||||
border-color: var(--white);
|
||||
}
|
||||
|
||||
nav {
|
||||
margin-top: 50px;
|
||||
min-width: 30%;
|
||||
border-radius: var(--border-radius);
|
||||
display: inline-block;
|
||||
padding: 20px 30px;
|
||||
/* background-color: var(--light-gray); */
|
||||
border: 1px solid var(--black);
|
||||
}
|
||||
|
||||
nav > h3 {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
nav > ol a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
html.dark-mode {
|
||||
background: rgb(51, 51, 51);
|
||||
background: var(--dark-background);
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
|
@ -22,16 +54,102 @@ html.dark-mode {
|
|||
color: white;
|
||||
}
|
||||
|
||||
.dark-mode a{
|
||||
color: #569CD6;
|
||||
a {
|
||||
color: #569cd6;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.dark-mode a:hover{
|
||||
.dark-mode a:hover {
|
||||
color: white;
|
||||
}
|
||||
|
||||
a{
|
||||
.dark-mode button {
|
||||
border: 1px solid rgb(211, 211, 211);
|
||||
color: rgb(211, 211, 211);
|
||||
background: linear-gradient(to top, rgb(53, 53, 53), rgb(73, 73, 73));
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: var(--white);
|
||||
border: 1px solid var(--gray);
|
||||
color: var(--gray);
|
||||
padding: 10px 20px;
|
||||
border-radius: 5px;
|
||||
transition: transform 0.2s;
|
||||
}
|
||||
|
||||
label {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
user-select: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input[type='checkbox'] {
|
||||
display: none;
|
||||
}
|
||||
input[type='checkbox'] + .checkbox {
|
||||
position: relative;
|
||||
margin-right: 10px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.controls > * {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
.dark-mode .checkbox {
|
||||
background-color: var(--gray);
|
||||
border: 1px solid var(--white);
|
||||
}
|
||||
|
||||
.checkbox {
|
||||
display: inline-block;
|
||||
border-radius: 3px;
|
||||
background-color: var(--white);
|
||||
border: 1px solid var(--black);
|
||||
}
|
||||
|
||||
.checkbox:after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
input[type='checkbox']:checked + .checkbox {
|
||||
background-color: rgb(174, 238, 78);
|
||||
}
|
||||
|
||||
input[type='checkbox']:checked + .checkbox:after {
|
||||
left: 11px;
|
||||
top: 6px;
|
||||
width: 5px;
|
||||
height: 12px;
|
||||
border: solid var(--white);
|
||||
border-width: 0 4px 4px 0;
|
||||
-webkit-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
button:active {
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
button:hover {
|
||||
filter: brightness(0.95);
|
||||
}
|
||||
|
||||
button:focus {
|
||||
box-shadow: 0 0 3px 1px #569cd6;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
@ -71,7 +189,7 @@ a{
|
|||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.doctest-collapsible-toggle {
|
||||
.doctest-titlebar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: whitesmoke;
|
||||
|
|
|
@ -239,43 +239,49 @@
|
|||
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');
|
||||
|
|
|
@ -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))
|
||||
|
@ -19094,6 +19115,31 @@
|
|||
}
|
||||
}
|
||||
|
||||
class PIXIUtils {
|
||||
/*
|
||||
* Transform a pixi text to it's actual screensize,
|
||||
* ignoring it's local transforms
|
||||
*/
|
||||
static toScreenFontSize(pixiText, fontSize = null) {
|
||||
pixiText._recursivePostUpdateTransform();
|
||||
|
||||
let normalizedScale = {
|
||||
x: pixiText.scale.x / pixiText.transform.worldTransform.a,
|
||||
y: pixiText.scale.x / pixiText.transform.worldTransform.d
|
||||
};
|
||||
|
||||
pixiText.scale = { x: normalizedScale.x, y: normalizedScale.y };
|
||||
if (fontSize) pixiText.style.fontSize = fontSize;
|
||||
}
|
||||
|
||||
static saveFill(graphics) {
|
||||
return {
|
||||
fill: graphics.fill.color,
|
||||
alpha: graphics.fill.alpha
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GeoGraphics are graphical objects, that does not store the graphics information
|
||||
* in screen space, but in geographical coordinates. Therefore GeoGraphics must be
|
||||
|
@ -19532,15 +19578,12 @@
|
|||
*/
|
||||
_drawShape(polygon, hole = []) {
|
||||
// We save the fill specified in the onDraw event handler.
|
||||
//
|
||||
// Consider: Maybe it would be a good idea to add a 'onHoleDraw'
|
||||
// callback to make the hole customizable. Maybe you want
|
||||
// to fill it with a different color or an mediocre alpha value.
|
||||
// then feel free to implement it.
|
||||
let { fill, alpha } = PIXIUtils.saveFill(this.graphics);
|
||||
|
||||
/**
|
||||
* This may seem redundant, but it's required
|
||||
* a) Draw the hole with a polygon.
|
||||
*
|
||||
* This may seem redundant to (c), but it's required (in this order(!))
|
||||
* to make the hole clickable.
|
||||
*
|
||||
* It was a bit confusing, so I made a CodePen
|
||||
|
@ -19551,9 +19594,15 @@
|
|||
this.graphics.drawPolygon(hole);
|
||||
}
|
||||
|
||||
/**
|
||||
* b) Draw the shape.
|
||||
*/
|
||||
this.graphics.beginFill(fill, alpha);
|
||||
this.graphics.drawPolygon(polygon);
|
||||
|
||||
/**
|
||||
* c) Add the hole.
|
||||
*/
|
||||
if (hole.length > 0) {
|
||||
this.graphics.beginHole();
|
||||
this.graphics.drawPolygon(hole);
|
||||
|
@ -19602,31 +19651,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
class PIXIUtils {
|
||||
/*
|
||||
* Transform a pixi text to it's actual screensize,
|
||||
* ignoring it's local transforms
|
||||
*/
|
||||
static toScreenFontSize(pixiText, fontSize = null) {
|
||||
pixiText._recursivePostUpdateTransform();
|
||||
|
||||
let normalizedScale = {
|
||||
x: pixiText.scale.x / pixiText.transform.worldTransform.a,
|
||||
y: pixiText.scale.x / pixiText.transform.worldTransform.d
|
||||
};
|
||||
|
||||
pixiText.scale = { x: normalizedScale.x, y: normalizedScale.y };
|
||||
if (fontSize) pixiText.style.fontSize = fontSize;
|
||||
}
|
||||
|
||||
static saveFill(graphics) {
|
||||
return {
|
||||
fill: graphics.fill.color,
|
||||
alpha: graphics.fill.alpha
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
@ -19638,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) {
|
||||
|
@ -19669,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 = {};
|
||||
|
||||
|
@ -19679,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;
|
||||
|
@ -19686,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);
|
||||
|
@ -19698,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];
|
||||
|
@ -19926,7 +19995,15 @@
|
|||
mapList,
|
||||
scatterContainer,
|
||||
displayObject,
|
||||
{ onTransform = null, onChange = null, focus = null, zoom = null, viewport = null, name = null } = {}
|
||||
{
|
||||
onTransform = null,
|
||||
onChange = null,
|
||||
focus = null,
|
||||
zoom = null,
|
||||
viewport = null,
|
||||
name = null,
|
||||
mapChangeLocked = false
|
||||
} = {}
|
||||
) {
|
||||
super(displayObject, {
|
||||
name
|
||||
|
@ -19954,6 +20031,7 @@
|
|||
// this.maps = maps
|
||||
// if (opts.map) this.placeMap(opts.map)
|
||||
this.dynamicElements = new Map();
|
||||
this._mapChangeLocked = mapChangeLocked;
|
||||
|
||||
// Binds the transformed callback beforehand.
|
||||
this.transformed = this.transformed.bind(this);
|
||||
|
@ -19961,13 +20039,24 @@
|
|||
this.changeMap(mapList.active);
|
||||
}
|
||||
|
||||
get mapChangeLocked() {
|
||||
return this._mapChangeLocked
|
||||
}
|
||||
|
||||
lockMapChange() {
|
||||
this._mapChangeLocked = true;
|
||||
}
|
||||
|
||||
unlockMapChange() {
|
||||
this._mapChangeLocked = false;
|
||||
}
|
||||
|
||||
adapt() {
|
||||
this.layers.forEach(layer => {
|
||||
if (layer.adapt) layer.adapt(this.map);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
focus(coordinates, zoom) {
|
||||
this.mapview.updateFocusPoint(this.map);
|
||||
}
|
||||
|
@ -20017,89 +20106,39 @@
|
|||
/* map ,
|
||||
useScatterAsContainer = true // If set to false, the normal container is used. This is necessary when using submaps and the container need to be a RigidContainer.*/
|
||||
) {
|
||||
console.log('🗺️ Change map to: ', name);
|
||||
let oldMap = this.map;
|
||||
if (!this.mapChangeLocked) {
|
||||
console.log('🗺️ Change map to: ', name);
|
||||
let oldMap = this.map;
|
||||
|
||||
this.mapList.select(name);
|
||||
this.mapList.select(name);
|
||||
|
||||
if (oldMap) {
|
||||
oldMap.unload();
|
||||
oldMap.onTransform.remove(this.transformed);
|
||||
}
|
||||
|
||||
let map = this.map;
|
||||
if (map) {
|
||||
map.load();
|
||||
|
||||
this.scatterContainer.addChild(map.image);
|
||||
|
||||
this.mapview.apply(map);
|
||||
map.image.addChild(this.displayObject);
|
||||
|
||||
// A geolayer's displayObject is on the parent layer.
|
||||
// A maplayer's displayobject is always the child of the map.
|
||||
this.adapt();
|
||||
|
||||
this.changeHandler.call(this, map, oldMap);
|
||||
|
||||
//Call transform one time manually.
|
||||
this.transformed();
|
||||
map.onTransform.add(this.transformed);
|
||||
} else {
|
||||
console.error(`Could not change map to ${name}.`);
|
||||
}
|
||||
|
||||
/*Logging.log(`Map change: ${key}`)
|
||||
|
||||
if (this.active !== key) {
|
||||
if (this.maps.hasOwnProperty(key)) {
|
||||
let old = this.map ? this.map : null
|
||||
this._map = this.maps[key]
|
||||
this._map.name = key
|
||||
this.active = key
|
||||
|
||||
let container = useScatterAsContainer ? this.scatterContainer : this.container
|
||||
|
||||
this.map.load(container)
|
||||
|
||||
// Copies all layers.
|
||||
this.layers.forEach(layer => {
|
||||
if (old) this.map.image.addChild(layer.container)
|
||||
})
|
||||
|
||||
this.placeMap(this.map)
|
||||
|
||||
/**
|
||||
* TODO: Improve
|
||||
*
|
||||
* I'm quite sure if I made a design mistake here.
|
||||
* In an earlier version I did not need to migrate the
|
||||
* layers manually from the map to the next map.
|
||||
*
|
||||
* I believe the old version was a container next to the
|
||||
* map, which got updated on transform.
|
||||
*
|
||||
* -SO
|
||||
*/
|
||||
/*
|
||||
if (old) old.unload()
|
||||
|
||||
|
||||
} else {
|
||||
let keys = Object.keys(this.maps)
|
||||
|
||||
if (keys.length == 0) console.error('There is no map set for the map layer!')
|
||||
else {
|
||||
let fallbackMap = keys[0]
|
||||
console.error(
|
||||
`A map with the key (${key}) does not exists within the mapapp. Fallback to map: ${fallbackMap}.`
|
||||
)
|
||||
this.changeMap(fallbackMap, {
|
||||
useScatterAsContainer
|
||||
})
|
||||
}
|
||||
if (oldMap) {
|
||||
oldMap.unload();
|
||||
oldMap.onTransform.remove(this.transformed);
|
||||
}
|
||||
}*/
|
||||
|
||||
let map = this.map;
|
||||
if (map) {
|
||||
map.load();
|
||||
|
||||
this.scatterContainer.addChild(map.image);
|
||||
|
||||
this.mapview.apply(map);
|
||||
map.image.addChild(this.displayObject);
|
||||
|
||||
// A geolayer's displayObject is on the parent layer.
|
||||
// A maplayer's displayobject is always the child of the map.
|
||||
this.adapt();
|
||||
|
||||
this.changeHandler.call(this, map, oldMap);
|
||||
|
||||
//Call transform one time manually.
|
||||
this.transformed();
|
||||
map.onTransform.add(this.transformed);
|
||||
} else {
|
||||
console.error(`Could not change map to ${name}.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
refocus() {
|
||||
|
@ -20310,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);
|
||||
}
|
||||
|
@ -20345,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();
|
||||
|
@ -20675,8 +20722,7 @@
|
|||
*
|
||||
* @static
|
||||
* @export
|
||||
* @class GeoJsonGraphics
|
||||
* @extends {GeoGraphics}
|
||||
* @class
|
||||
*/
|
||||
class GeoJson {
|
||||
static isLineType(type) {
|
||||
|
@ -20728,7 +20774,6 @@
|
|||
}
|
||||
|
||||
list.push({ type, coordinates });
|
||||
// console.log({type, coordinates})
|
||||
});
|
||||
|
||||
return list
|
||||
|
@ -20792,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) {
|
||||
|
@ -20971,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
|
||||
*/
|
||||
|
@ -21642,4 +21687,7 @@
|
|||
window.Overlay = Overlay;
|
||||
window.MapList = MapList;
|
||||
|
||||
window.GeoJson = GeoJson;
|
||||
window.GeoUtils = GeoUtils;
|
||||
|
||||
}());
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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'
|
||||
|
||||
|
@ -100,7 +100,6 @@ window.CompactScatter = CompactScatter
|
|||
window.CoverScatter = CoverScatter
|
||||
window.MapObjectScatter = MapObjectScatter
|
||||
|
||||
|
||||
import { GeoLayer, MapLayer } from './maps/geolayer.js'
|
||||
window.GeoLayer = GeoLayer
|
||||
window.MapLayer = MapLayer
|
||||
|
@ -118,5 +117,8 @@ import Overlay from './maps/overlay.js'
|
|||
window.Overlay = Overlay
|
||||
|
||||
import { MapList } from './maps/maplist.js'
|
||||
import GeoJson, { GeoUtils } from './maps/geojson.js'
|
||||
window.MapList = MapList
|
||||
|
||||
window.GeoJson = GeoJson
|
||||
window.GeoUtils = GeoUtils
|
||||
|
|
|
@ -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'],
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -2,6 +2,7 @@ import { Points } from '../../utils.js'
|
|||
import { EventHandler } from './utils.js'
|
||||
import { FlagPolygon } from '../graphics/label.js'
|
||||
import { DeepZoomMap } from './map.js'
|
||||
import { PIXIUtils } from '../../../../js/pixi/utils.js'
|
||||
|
||||
/**
|
||||
* GeoGraphics are graphical objects, that does not store the graphics information
|
||||
|
@ -441,15 +442,12 @@ export class GeoShape extends GeoGraphics {
|
|||
*/
|
||||
_drawShape(polygon, hole = []) {
|
||||
// We save the fill specified in the onDraw event handler.
|
||||
//
|
||||
// Consider: Maybe it would be a good idea to add a 'onHoleDraw'
|
||||
// callback to make the hole customizable. Maybe you want
|
||||
// to fill it with a different color or an mediocre alpha value.
|
||||
// then feel free to implement it.
|
||||
let { fill, alpha } = PIXIUtils.saveFill(this.graphics)
|
||||
|
||||
/**
|
||||
* This may seem redundant, but it's required
|
||||
* a) Draw the hole with a polygon.
|
||||
*
|
||||
* This may seem redundant to (c), but it's required (in this order(!))
|
||||
* to make the hole clickable.
|
||||
*
|
||||
* It was a bit confusing, so I made a CodePen
|
||||
|
@ -460,9 +458,15 @@ export class GeoShape extends GeoGraphics {
|
|||
this.graphics.drawPolygon(hole)
|
||||
}
|
||||
|
||||
/**
|
||||
* b) Draw the shape.
|
||||
*/
|
||||
this.graphics.beginFill(fill, alpha)
|
||||
this.graphics.drawPolygon(polygon)
|
||||
|
||||
/**
|
||||
* c) Add the hole.
|
||||
*/
|
||||
if (hole.length > 0) {
|
||||
this.graphics.beginHole()
|
||||
this.graphics.drawPolygon(hole)
|
||||
|
@ -524,6 +528,14 @@ class GeoMultiGraphics extends GeoGraphics {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @export
|
||||
* @class GeoText
|
||||
* @extends {GeoPoint}
|
||||
*/
|
||||
export class GeoText extends GeoPoint {
|
||||
constructor(coordinates, text, opts) {
|
||||
super(coordinates, opts)
|
||||
|
@ -531,14 +543,6 @@ export class GeoText extends GeoPoint {
|
|||
this.textStyle = Object.assign(new PIXI.TextStyle(), opts.textStyle)
|
||||
this._text = new PIXI.Text(text, this.textStyle)
|
||||
|
||||
//TODO: Make this more generic:
|
||||
// We have 8 layers (12-20) for each map so this temporarily works fine.
|
||||
// Outsource it to the map class.
|
||||
//let textScale = Math.pow(2, 7)
|
||||
|
||||
// let textScale = 5
|
||||
// this.text.scale.set(1 / textScale, 1 / textScale)
|
||||
|
||||
switch (this.align) {
|
||||
case 'left':
|
||||
break
|
||||
|
@ -715,27 +719,3 @@ export class GeoMultiShape extends GeoShape {
|
|||
}
|
||||
}
|
||||
|
||||
class PIXIUtils {
|
||||
/*
|
||||
* Transform a pixi text to it's actual screensize,
|
||||
* ignoring it's local transforms
|
||||
*/
|
||||
static toScreenFontSize(pixiText, fontSize = null) {
|
||||
pixiText._recursivePostUpdateTransform()
|
||||
|
||||
let normalizedScale = {
|
||||
x: pixiText.scale.x / pixiText.transform.worldTransform.a,
|
||||
y: pixiText.scale.x / pixiText.transform.worldTransform.d
|
||||
}
|
||||
|
||||
pixiText.scale = { x: normalizedScale.x, y: normalizedScale.y }
|
||||
if (fontSize) pixiText.style.fontSize = fontSize
|
||||
}
|
||||
|
||||
static saveFill(graphics) {
|
||||
return {
|
||||
fill: graphics.fill.color,
|
||||
alpha: graphics.fill.alpha
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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>
|
|
@ -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))
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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>
|
|
@ -6,7 +6,7 @@ import { DeepZoomMap } from './map.js'
|
|||
* 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.
|
Binary file not shown.
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 172 KiB |
Loading…
Reference in New Issue