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
|
*.code-workspace
|
||||||
.history/
|
.history/
|
||||||
.vscode/
|
.vscode/
|
||||||
|
|
||||||
|
|
||||||
|
# ignore generated contents-
|
||||||
|
/doc/out/*
|
||||||
|
**/thumbnails
|
||||||
|
**/thumbnail.png
|
Binary file not shown.
After Width: | Height: | Size: 8.0 KiB |
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 {
|
html {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
background: white;
|
background: var(--white);
|
||||||
color: #000;
|
color: var(--black);
|
||||||
font-family: 'Open Sans', Arial, sans-serif;
|
font-family: 'Open Sans', Arial, sans-serif;
|
||||||
max-width: 1200px;
|
max-width: 1200px;
|
||||||
margin: 0 auto;
|
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 {
|
html.dark-mode {
|
||||||
background: rgb(51, 51, 51);
|
background: var(--dark-background);
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,16 +54,102 @@ html.dark-mode {
|
||||||
color: white;
|
color: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark-mode a{
|
a {
|
||||||
color: #569CD6;
|
color: #569cd6;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark-mode a:hover{
|
.dark-mode a:hover {
|
||||||
color: white;
|
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;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +189,7 @@ a{
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
.doctest-collapsible-toggle {
|
.doctest-titlebar {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
color: whitesmoke;
|
color: whitesmoke;
|
||||||
|
|
|
@ -239,43 +239,49 @@
|
||||||
let container = document.createElement('div');
|
let container = document.createElement('div');
|
||||||
container.className = 'doctest-wrapper';
|
container.className = 'doctest-wrapper';
|
||||||
|
|
||||||
let titleParent = container;
|
if (doctest.hasAttribute('data-title') || doctest.hasAttribute('data-collapsible')) {
|
||||||
if (doctest.hasAttribute('data-collapsible')) {
|
let titlebar = document.createElement('div');
|
||||||
let collapsibleToggle = document.createElement('div');
|
titlebar.className = 'doctest-titlebar';
|
||||||
|
titlebar.style = 'min-height: 10px;';
|
||||||
|
container.appendChild(titlebar);
|
||||||
|
|
||||||
let icon = document.createElement('i');
|
if (doctest.hasAttribute('data-title')) {
|
||||||
icon.className = 'material-icons';
|
let title = document.createElement('h6');
|
||||||
collapsibleToggle.appendChild(icon);
|
title.innerText = doctest.getAttribute('data-title');
|
||||||
|
title.className = 'doctest-section-title';
|
||||||
|
titlebar.appendChild(title);
|
||||||
|
}
|
||||||
|
|
||||||
collapsibleToggle.className = 'doctest-collapsible-toggle';
|
if (doctest.hasAttribute('data-collapsible')) {
|
||||||
collapsibleToggle.style = 'min-height: 10px;';
|
let icon = document.createElement('i');
|
||||||
titleParent = collapsibleToggle;
|
icon.className = 'material-icons';
|
||||||
container.appendChild(collapsibleToggle);
|
titlebar.classList.add('doctest-collapsible-toggle');
|
||||||
|
|
||||||
const collapsedClass = 'collapsed';
|
if (titlebar.childNodes.length > 0) {
|
||||||
|
titlebar.insertBefore(icon, titlebar.childNodes[0]);
|
||||||
function setToggleMode(collapse) {
|
|
||||||
if (collapse) {
|
|
||||||
container.classList.add(collapsedClass);
|
|
||||||
icon.innerText = 'arrow_drop_down';
|
|
||||||
} else {
|
} else {
|
||||||
container.classList.remove(collapsedClass);
|
titlebar.appendChild(icon);
|
||||||
icon.innerText = 'arrow_drop_up';
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function determineToggleMode() {
|
const collapsedClass = 'collapsed';
|
||||||
setToggleMode(!container.classList.contains(collapsedClass));
|
|
||||||
}
|
|
||||||
|
|
||||||
setToggleMode(doctest.hasAttribute('data-collapsed'));
|
function setToggleMode(collapse) {
|
||||||
collapsibleToggle.addEventListener('click', determineToggleMode);
|
if (collapse) {
|
||||||
}
|
container.classList.add(collapsedClass);
|
||||||
if (doctest.hasAttribute('data-title')) {
|
icon.innerText = 'arrow_drop_down';
|
||||||
let title = document.createElement('h6');
|
} else {
|
||||||
title.innerText = doctest.getAttribute('data-title');
|
container.classList.remove(collapsedClass);
|
||||||
title.className = 'doctest-section-title';
|
icon.innerText = 'arrow_drop_up';
|
||||||
titleParent.appendChild(title);
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function determineToggleMode() {
|
||||||
|
setToggleMode(!container.classList.contains(collapsedClass));
|
||||||
|
}
|
||||||
|
|
||||||
|
setToggleMode(doctest.hasAttribute('data-collapsed'));
|
||||||
|
titlebar.addEventListener('click', determineToggleMode);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let pre = document.createElement('pre');
|
let pre = document.createElement('pre');
|
||||||
|
|
|
@ -17444,7 +17444,7 @@
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {Projection}[projection] - Specifies the projection of the map (e.g. Mercator Projection).
|
* @param {Projection}[projection] - Specifies the projection of the map (e.g. Mercator Projection).
|
||||||
* @param {object}[opts] - Addiditonal options.
|
* @param {object}[opts] - Addiditonal options.
|
||||||
* @param {[[minLat, minLng],[maxLat, maxLng]]}[opts.bounds] - Describes the minimum and maximum coordinates on the map
|
* @param {array}[opts.bounds] - Describes the minimum and maximum coordinates on the map in the form of {[[minLat, minLng],[maxLat, maxLng]]}.
|
||||||
* @param {Point}[opts.translate] - Defines a translation, when clipping is not an option (e.g. when the whole world is shown, but translated.)
|
* @param {Point}[opts.translate] - Defines a translation, when clipping is not an option (e.g. when the whole world is shown, but translated.)
|
||||||
*/
|
*/
|
||||||
constructor(projection, opts = {}) {
|
constructor(projection, opts = {}) {
|
||||||
|
@ -17482,6 +17482,7 @@
|
||||||
/**
|
/**
|
||||||
* Transforms a pixel point on the map to a geographical coordinate.
|
* Transforms a pixel point on the map to a geographical coordinate.
|
||||||
*
|
*
|
||||||
|
* @public
|
||||||
* @param {{x,y} | PIXI.Point} point - A pixel position on the map.
|
* @param {{x,y} | PIXI.Point} point - A pixel position on the map.
|
||||||
* @returns {{x,y} | PIXI.Point} - A geographical coordinate.
|
* @returns {{x,y} | PIXI.Point} - A geographical coordinate.
|
||||||
* @memberof MapData
|
* @memberof MapData
|
||||||
|
@ -17514,6 +17515,7 @@
|
||||||
/**
|
/**
|
||||||
* Transform a geographical coordinate to a pixel point on the map.
|
* Transform a geographical coordinate to a pixel point on the map.
|
||||||
*
|
*
|
||||||
|
* @public
|
||||||
* @param {{x,y} | PIXI.Point} coordinates - A point in the form of {x:lat,y:lng}.
|
* @param {{x,y} | PIXI.Point} coordinates - A point in the form of {x:lat,y:lng}.
|
||||||
* @returns {{x,y} | PIXI.Point} point - A pixel position on the map.
|
* @returns {{x,y} | PIXI.Point} point - A pixel position on the map.
|
||||||
* @memberof MapData
|
* @memberof MapData
|
||||||
|
@ -17546,6 +17548,18 @@
|
||||||
return point
|
return point
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get's the clipping of the map data. Clipping describes the
|
||||||
|
* piece of the map that is shown. E.g. if we just show a map of
|
||||||
|
* europe, then we have to set the clipping properly, otherwise
|
||||||
|
* the preojection would produce the wrong results when transforming
|
||||||
|
* from a point to coordinates or the other way around.
|
||||||
|
*
|
||||||
|
* @readonly
|
||||||
|
* @memberof MapData
|
||||||
|
* @returns {object} - Object that contains a min and max value of the clipping in form of: {min: {x,y}, max:{x,y}}. Where x and y are in between 0 and 1.
|
||||||
|
*/
|
||||||
get clip() {
|
get clip() {
|
||||||
let unclipped = {
|
let unclipped = {
|
||||||
min: { x: 0, y: 0 },
|
min: { x: 0, y: 0 },
|
||||||
|
@ -17555,30 +17569,30 @@
|
||||||
return this.opts.clip ? this.opts.clip : unclipped
|
return this.opts.clip ? this.opts.clip : unclipped
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bounds to pixel transforms some bounds in form of {min:{x:minLat, y:minLng},max:{x:maxLat, y:maxLng}}
|
* Returns the biggest viewport the mapdata allows.
|
||||||
* to pixel coordinates.
|
* This is determined by the projecton or the clipping on the mapapp.
|
||||||
*
|
*
|
||||||
|
* @readonly
|
||||||
|
* @memberof MapData
|
||||||
*/
|
*/
|
||||||
getBoundaries() {
|
|
||||||
// let min = this.toPixel(bounds.min)
|
|
||||||
// let max = this.toPixel(bounds.max)
|
|
||||||
|
|
||||||
// Y values needs to be swapped, as PIXI has it's origin
|
|
||||||
// in the top-left corner and a regular map in the bottom-left corner.
|
|
||||||
let boundaries = {
|
|
||||||
min: { x: 0, y: 0 },
|
|
||||||
max: { x: 1, y: 1 }
|
|
||||||
};
|
|
||||||
|
|
||||||
return boundaries
|
|
||||||
}
|
|
||||||
|
|
||||||
get maxViewport() {
|
get maxViewport() {
|
||||||
return this.opts.clip ? this.opts.clip : this.projection.maxViewport
|
return this.opts.clip ? this.opts.clip : this.projection.maxViewport
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special mapdata for DeepZoomMap objects.
|
||||||
|
*
|
||||||
|
* Note: It just transform the clipping parameter of the tiles config
|
||||||
|
* to the clipping of the mapapp.
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class DeepZoomMapData
|
||||||
|
* @extends {MapData}
|
||||||
|
*/
|
||||||
class DeepZoomMapData extends MapData {
|
class DeepZoomMapData extends MapData {
|
||||||
constructor(projection, tilesConfig, opts = {}) {
|
constructor(projection, tilesConfig, opts = {}) {
|
||||||
if (tilesConfig.clip) {
|
if (tilesConfig.clip) {
|
||||||
|
@ -17773,10 +17787,18 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
flushHandlers() {
|
flushHandlers() {
|
||||||
// this.onLoaded
|
this.onLoad.empty();
|
||||||
this.onTransform.empty();
|
this.onTransform.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locks all transformations on the map.
|
||||||
|
* Single parameters can be set if necessary. False means the value is locked, true means they can be modified.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @param {object} [{ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false }={}]
|
||||||
|
* @memberof GeoMap
|
||||||
|
*/
|
||||||
lock({ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false } = {}) {
|
lock({ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false } = {}) {
|
||||||
if (this.image && this.image.scatter) {
|
if (this.image && this.image.scatter) {
|
||||||
this.image.scatter.translatable = rotatable;
|
this.image.scatter.translatable = rotatable;
|
||||||
|
@ -17785,30 +17807,26 @@
|
||||||
this.image.scatter.rotatable = movableY;
|
this.image.scatter.rotatable = movableY;
|
||||||
this.image.scatter.scalable = scalable;
|
this.image.scatter.scalable = scalable;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue #001: This causes the map to not be displayed at the correct position on
|
|
||||||
// map change.
|
|
||||||
// // Rotation does not yet work with the cover mechanism.
|
|
||||||
// //this.rotatable = false
|
|
||||||
// this.translatable = false
|
|
||||||
// this.scalable = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlocks all transformations on the map.
|
||||||
|
* Single parameters can be set if necessary. False means the value is locked, true means they can be modified.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @param {object} [{ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false }={}]
|
||||||
|
* @memberof GeoMap
|
||||||
|
*/
|
||||||
unlock({ rotatable = true, translatable = true, movableX = true, movableY = true, scalable = true } = {}) {
|
unlock({ rotatable = true, translatable = true, movableX = true, movableY = true, scalable = true } = {}) {
|
||||||
if (this.image && this.image.scatter) {
|
this.lock({ rotatable, translatable, movableX, movableY, scalable });
|
||||||
this.image.scatter.translatable = translatable;
|
|
||||||
this.image.scatter.movableX = movableX;
|
|
||||||
this.image.scatter.movableY = movableY;
|
|
||||||
this.image.scatter.rotatable = rotatable;
|
|
||||||
this.image.scatter.scalable = scalable;
|
|
||||||
}
|
|
||||||
// Issue #001
|
|
||||||
// // Rotation does not yet work with the cover mechanism.
|
|
||||||
// //this.rotatable = true
|
|
||||||
// this.translatable = true
|
|
||||||
// this.scalable = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unloads the image of the map.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @memberof GeoMap
|
||||||
|
*/
|
||||||
unload() {
|
unload() {
|
||||||
if (this.image) {
|
if (this.image) {
|
||||||
if (this.image.parent) {
|
if (this.image.parent) {
|
||||||
|
@ -17822,6 +17840,12 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the map, freeing all memory ba flushing handlers and removing the image.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @memberof GeoMap
|
||||||
|
*/
|
||||||
remove() {
|
remove() {
|
||||||
if (this.image) this.image.mask = null;
|
if (this.image) this.image.mask = null;
|
||||||
|
|
||||||
|
@ -17855,8 +17879,6 @@
|
||||||
this.image = image;
|
this.image = image;
|
||||||
if (frame) this.setFrame(frame);
|
if (frame) this.setFrame(frame);
|
||||||
|
|
||||||
let boundaries = this.mapdata.getBoundaries();
|
|
||||||
|
|
||||||
let scatterOpts = Object.assign({
|
let scatterOpts = Object.assign({
|
||||||
cover: this.cover,
|
cover: this.cover,
|
||||||
scaleable: this.scaleable,
|
scaleable: this.scaleable,
|
||||||
|
@ -17866,7 +17888,6 @@
|
||||||
startScale: this.startScale,
|
startScale: this.startScale,
|
||||||
minScale: this.minScale,
|
minScale: this.minScale,
|
||||||
maxScale: this.maxScale,
|
maxScale: this.maxScale,
|
||||||
boundaries,
|
|
||||||
onTransform: this.transformed.bind(this)
|
onTransform: this.transformed.bind(this)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -17922,7 +17943,7 @@
|
||||||
* to a coordinate with latitude and longitude.
|
* to a coordinate with latitude and longitude.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @param {object} point - Point in form of {x: x_val, y: y_val}.
|
* @param {object} point - Point in form of {x, y}.
|
||||||
* @returns {object} - Coordinates on the map in form of {x: latitude, y: longitude}.
|
* @returns {object} - Coordinates on the map in form of {x: latitude, y: longitude}.
|
||||||
*/
|
*/
|
||||||
coordinatesFromPoint(point) {
|
coordinatesFromPoint(point) {
|
||||||
|
@ -17934,7 +17955,7 @@
|
||||||
* Transform coordinates in the map into pixel positions on the deep zoom image.
|
* Transform coordinates in the map into pixel positions on the deep zoom image.
|
||||||
*
|
*
|
||||||
* @param {object} coords - Coordinates of a map position in form {x: latitude, y: longitude}.
|
* @param {object} coords - Coordinates of a map position in form {x: latitude, y: longitude}.
|
||||||
* @return - Returns a image position in form of {x: x_val, y: y_val}.
|
* @return {Point} - Returns a image position in form of {x: x, y: y}.
|
||||||
*/
|
*/
|
||||||
coordinatesToPoint(coordinates) {
|
coordinatesToPoint(coordinates) {
|
||||||
return this.toAbsolutePixelCoordinates(this.mapdata.toPixel(coordinates))
|
return this.toAbsolutePixelCoordinates(this.mapdata.toPixel(coordinates))
|
||||||
|
@ -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
|
* GeoGraphics are graphical objects, that does not store the graphics information
|
||||||
* in screen space, but in geographical coordinates. Therefore GeoGraphics must be
|
* in screen space, but in geographical coordinates. Therefore GeoGraphics must be
|
||||||
|
@ -19532,15 +19578,12 @@
|
||||||
*/
|
*/
|
||||||
_drawShape(polygon, hole = []) {
|
_drawShape(polygon, hole = []) {
|
||||||
// We save the fill specified in the onDraw event handler.
|
// 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);
|
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.
|
* to make the hole clickable.
|
||||||
*
|
*
|
||||||
* It was a bit confusing, so I made a CodePen
|
* It was a bit confusing, so I made a CodePen
|
||||||
|
@ -19551,9 +19594,15 @@
|
||||||
this.graphics.drawPolygon(hole);
|
this.graphics.drawPolygon(hole);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* b) Draw the shape.
|
||||||
|
*/
|
||||||
this.graphics.beginFill(fill, alpha);
|
this.graphics.beginFill(fill, alpha);
|
||||||
this.graphics.drawPolygon(polygon);
|
this.graphics.drawPolygon(polygon);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* c) Add the hole.
|
||||||
|
*/
|
||||||
if (hole.length > 0) {
|
if (hole.length > 0) {
|
||||||
this.graphics.beginHole();
|
this.graphics.beginHole();
|
||||||
this.graphics.drawPolygon(hole);
|
this.graphics.drawPolygon(hole);
|
||||||
|
@ -19602,31 +19651,13 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class PIXIUtils {
|
/**
|
||||||
/*
|
* MapList is a list of maps with one active index.
|
||||||
* Transform a pixi text to it's actual screensize,
|
* It contains some utility functions to change the map.
|
||||||
* ignoring it's local transforms
|
*
|
||||||
*/
|
* @export
|
||||||
static toScreenFontSize(pixiText, fontSize = null) {
|
* @class MapList
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class MapList {
|
class MapList {
|
||||||
constructor(active = null, maps = {}) {
|
constructor(active = null, maps = {}) {
|
||||||
this.maps = maps;
|
this.maps = maps;
|
||||||
|
@ -19638,8 +19669,9 @@
|
||||||
/**
|
/**
|
||||||
* Selects a map from the map list.
|
* Selects a map from the map list.
|
||||||
*
|
*
|
||||||
|
* @public
|
||||||
* @param {string} active - Name of the map to select.
|
* @param {string} active - Name of the map to select.
|
||||||
* @returns
|
* @returns {Map} - Returns the active map. Returns null if no map was added to the MapList.
|
||||||
* @memberof MapList
|
* @memberof MapList
|
||||||
*/
|
*/
|
||||||
select(active) {
|
select(active) {
|
||||||
|
@ -19669,6 +19701,13 @@
|
||||||
return map
|
return map
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones the entire maplist.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @returns {MapList} - Returns a cloned instance of this map list.
|
||||||
|
* @memberof MapList
|
||||||
|
*/
|
||||||
clone() {
|
clone() {
|
||||||
let maps = {};
|
let maps = {};
|
||||||
|
|
||||||
|
@ -19679,6 +19718,14 @@
|
||||||
return new MapList(this.active, maps)
|
return new MapList(this.active, maps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new map to the map list.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @param {string} key - Key to identify the map.
|
||||||
|
* @param {GeoMap} map - The GeoMap to add.
|
||||||
|
* @memberof MapList
|
||||||
|
*/
|
||||||
add(key, map) {
|
add(key, map) {
|
||||||
if (this.maps[key] != null) consol.warn('Key already in mapList. The existing key was overwritten.');
|
if (this.maps[key] != null) consol.warn('Key already in mapList. The existing key was overwritten.');
|
||||||
if (this.active == null) this.active = key;
|
if (this.active == null) this.active = key;
|
||||||
|
@ -19686,10 +19733,25 @@
|
||||||
this.maps[key] = map;
|
this.maps[key] = map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the the active map.
|
||||||
|
* If none is set, it returns null.
|
||||||
|
*
|
||||||
|
*@public
|
||||||
|
* @readonly
|
||||||
|
* @memberof MapList
|
||||||
|
*/
|
||||||
get map() {
|
get map() {
|
||||||
return this.maps && this.maps[this.active] ? this.maps[this.active] : null
|
return this.maps && this.maps[this.active] ? this.maps[this.active] : null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects the next map in the map array.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @returns {GeoMap} - Returns the next map in the list.
|
||||||
|
* @memberof MapList
|
||||||
|
*/
|
||||||
next() {
|
next() {
|
||||||
let keys = Object.keys(this.maps);
|
let keys = Object.keys(this.maps);
|
||||||
let idx = keys.indexOf(this.active);
|
let idx = keys.indexOf(this.active);
|
||||||
|
@ -19698,6 +19760,13 @@
|
||||||
return next
|
return next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all maps from the maplist.
|
||||||
|
* And cleans up all maps.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @memberof MapList
|
||||||
|
*/
|
||||||
cleanup() {
|
cleanup() {
|
||||||
for (let key in this.maps) {
|
for (let key in this.maps) {
|
||||||
let map = this.maps[key];
|
let map = this.maps[key];
|
||||||
|
@ -19926,7 +19995,15 @@
|
||||||
mapList,
|
mapList,
|
||||||
scatterContainer,
|
scatterContainer,
|
||||||
displayObject,
|
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, {
|
super(displayObject, {
|
||||||
name
|
name
|
||||||
|
@ -19954,6 +20031,7 @@
|
||||||
// this.maps = maps
|
// this.maps = maps
|
||||||
// if (opts.map) this.placeMap(opts.map)
|
// if (opts.map) this.placeMap(opts.map)
|
||||||
this.dynamicElements = new Map();
|
this.dynamicElements = new Map();
|
||||||
|
this._mapChangeLocked = mapChangeLocked;
|
||||||
|
|
||||||
// Binds the transformed callback beforehand.
|
// Binds the transformed callback beforehand.
|
||||||
this.transformed = this.transformed.bind(this);
|
this.transformed = this.transformed.bind(this);
|
||||||
|
@ -19961,13 +20039,24 @@
|
||||||
this.changeMap(mapList.active);
|
this.changeMap(mapList.active);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get mapChangeLocked() {
|
||||||
|
return this._mapChangeLocked
|
||||||
|
}
|
||||||
|
|
||||||
|
lockMapChange() {
|
||||||
|
this._mapChangeLocked = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
unlockMapChange() {
|
||||||
|
this._mapChangeLocked = false;
|
||||||
|
}
|
||||||
|
|
||||||
adapt() {
|
adapt() {
|
||||||
this.layers.forEach(layer => {
|
this.layers.forEach(layer => {
|
||||||
if (layer.adapt) layer.adapt(this.map);
|
if (layer.adapt) layer.adapt(this.map);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
focus(coordinates, zoom) {
|
focus(coordinates, zoom) {
|
||||||
this.mapview.updateFocusPoint(this.map);
|
this.mapview.updateFocusPoint(this.map);
|
||||||
}
|
}
|
||||||
|
@ -20017,89 +20106,39 @@
|
||||||
/* map ,
|
/* 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.*/
|
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);
|
if (!this.mapChangeLocked) {
|
||||||
let oldMap = this.map;
|
console.log('🗺️ Change map to: ', name);
|
||||||
|
let oldMap = this.map;
|
||||||
|
|
||||||
this.mapList.select(name);
|
this.mapList.select(name);
|
||||||
|
|
||||||
if (oldMap) {
|
if (oldMap) {
|
||||||
oldMap.unload();
|
oldMap.unload();
|
||||||
oldMap.onTransform.remove(this.transformed);
|
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
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}*/
|
|
||||||
|
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() {
|
refocus() {
|
||||||
|
@ -20310,9 +20349,9 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
selectMap(key) {
|
selectMap(key) {
|
||||||
|
if (this.debug) console.log('Select map', key, result);
|
||||||
let result = this.mapList.select(key);
|
let result = this.mapList.select(key);
|
||||||
|
|
||||||
console.log('Select map', key, result);
|
|
||||||
if (result && this.mapLayer) {
|
if (result && this.mapLayer) {
|
||||||
this.mapLayer.changeMap(this.mapList.map);
|
this.mapLayer.changeMap(this.mapList.map);
|
||||||
}
|
}
|
||||||
|
@ -20345,6 +20384,14 @@
|
||||||
this.onTransform.call(this, event);
|
this.onTransform.call(this, event);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Called when the mapLayer changed the map.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {*} lastMap
|
||||||
|
* @memberof MapApp
|
||||||
|
*/
|
||||||
_mapChanged(lastMap) {
|
_mapChanged(lastMap) {
|
||||||
if (lastMap) {
|
if (lastMap) {
|
||||||
lastMap.flushHandlers();
|
lastMap.flushHandlers();
|
||||||
|
@ -20675,8 +20722,7 @@
|
||||||
*
|
*
|
||||||
* @static
|
* @static
|
||||||
* @export
|
* @export
|
||||||
* @class GeoJsonGraphics
|
* @class
|
||||||
* @extends {GeoGraphics}
|
|
||||||
*/
|
*/
|
||||||
class GeoJson {
|
class GeoJson {
|
||||||
static isLineType(type) {
|
static isLineType(type) {
|
||||||
|
@ -20728,7 +20774,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
list.push({ type, coordinates });
|
list.push({ type, coordinates });
|
||||||
// console.log({type, coordinates})
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return list
|
return list
|
||||||
|
@ -20792,7 +20837,7 @@
|
||||||
* considered valid. A complete list is provided in the GeoUtils.
|
* considered valid. A complete list is provided in the GeoUtils.
|
||||||
*
|
*
|
||||||
* @param {object} point - The point that is tested for validity.
|
* @param {object} point - The point that is tested for validity.
|
||||||
* @returns
|
* @returns {boolean}
|
||||||
* @memberof GeoJson
|
* @memberof GeoJson
|
||||||
*/
|
*/
|
||||||
static validateAndConvertPoint(point) {
|
static validateAndConvertPoint(point) {
|
||||||
|
@ -20971,7 +21016,7 @@
|
||||||
* {latitude: lat, longitude: lng}
|
* {latitude: lat, longitude: lng}
|
||||||
*
|
*
|
||||||
* @static
|
* @static
|
||||||
* @param {object / array} coordinate - Coordinate to be tested, if it is an valid coordinate.
|
* @param {object|array} coordinate - Coordinate to be tested, if it is an valid coordinate.
|
||||||
* @returns - Returns the coordinate properly transformed. If transformation was not possible, it returns null.
|
* @returns - Returns the coordinate properly transformed. If transformation was not possible, it returns null.
|
||||||
* @memberof GeoGraphics
|
* @memberof GeoGraphics
|
||||||
*/
|
*/
|
||||||
|
@ -21642,4 +21687,7 @@
|
||||||
window.Overlay = Overlay;
|
window.Overlay = Overlay;
|
||||||
window.MapList = MapList;
|
window.MapList = MapList;
|
||||||
|
|
||||||
|
window.GeoJson = GeoJson;
|
||||||
|
window.GeoUtils = GeoUtils;
|
||||||
|
|
||||||
}());
|
}());
|
||||||
|
|
|
@ -145,43 +145,49 @@ export default class Doctest {
|
||||||
let container = document.createElement('div')
|
let container = document.createElement('div')
|
||||||
container.className = 'doctest-wrapper'
|
container.className = 'doctest-wrapper'
|
||||||
|
|
||||||
let titleParent = container
|
if (doctest.hasAttribute('data-title') || doctest.hasAttribute('data-collapsible')) {
|
||||||
if (doctest.hasAttribute('data-collapsible')) {
|
let titlebar = document.createElement('div')
|
||||||
let collapsibleToggle = document.createElement('div')
|
titlebar.className = 'doctest-titlebar'
|
||||||
|
titlebar.style = 'min-height: 10px;'
|
||||||
|
container.appendChild(titlebar)
|
||||||
|
|
||||||
let icon = document.createElement('i')
|
if (doctest.hasAttribute('data-title')) {
|
||||||
icon.className = 'material-icons'
|
let title = document.createElement('h6')
|
||||||
collapsibleToggle.appendChild(icon)
|
title.innerText = doctest.getAttribute('data-title')
|
||||||
|
title.className = 'doctest-section-title'
|
||||||
|
titlebar.appendChild(title)
|
||||||
|
}
|
||||||
|
|
||||||
collapsibleToggle.className = 'doctest-collapsible-toggle'
|
if (doctest.hasAttribute('data-collapsible')) {
|
||||||
collapsibleToggle.style = 'min-height: 10px;'
|
let icon = document.createElement('i')
|
||||||
titleParent = collapsibleToggle
|
icon.className = 'material-icons'
|
||||||
container.appendChild(collapsibleToggle)
|
titlebar.classList.add('doctest-collapsible-toggle')
|
||||||
|
|
||||||
const collapsedClass = 'collapsed'
|
if (titlebar.childNodes.length > 0) {
|
||||||
|
titlebar.insertBefore(icon, titlebar.childNodes[0])
|
||||||
function setToggleMode(collapse) {
|
|
||||||
if (collapse) {
|
|
||||||
container.classList.add(collapsedClass)
|
|
||||||
icon.innerText = 'arrow_drop_down'
|
|
||||||
} else {
|
} else {
|
||||||
container.classList.remove(collapsedClass)
|
titlebar.appendChild(icon)
|
||||||
icon.innerText = 'arrow_drop_up'
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
function determineToggleMode() {
|
const collapsedClass = 'collapsed'
|
||||||
setToggleMode(!container.classList.contains(collapsedClass))
|
|
||||||
}
|
|
||||||
|
|
||||||
setToggleMode(doctest.hasAttribute('data-collapsed'))
|
function setToggleMode(collapse) {
|
||||||
collapsibleToggle.addEventListener('click', determineToggleMode)
|
if (collapse) {
|
||||||
}
|
container.classList.add(collapsedClass)
|
||||||
if (doctest.hasAttribute('data-title')) {
|
icon.innerText = 'arrow_drop_down'
|
||||||
let title = document.createElement('h6')
|
} else {
|
||||||
title.innerText = doctest.getAttribute('data-title')
|
container.classList.remove(collapsedClass)
|
||||||
title.className = 'doctest-section-title'
|
icon.innerText = 'arrow_drop_up'
|
||||||
titleParent.appendChild(title)
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function determineToggleMode() {
|
||||||
|
setToggleMode(!container.classList.contains(collapsedClass))
|
||||||
|
}
|
||||||
|
|
||||||
|
setToggleMode(doctest.hasAttribute('data-collapsed'))
|
||||||
|
titlebar.addEventListener('click', determineToggleMode)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let pre = document.createElement('pre')
|
let pre = document.createElement('pre')
|
||||||
|
|
|
@ -62,7 +62,6 @@ window.FontInfo = FontInfo
|
||||||
window.Text = Text
|
window.Text = Text
|
||||||
|
|
||||||
//Maps
|
//Maps
|
||||||
import MapView from './maps/mapview.js'
|
|
||||||
import { GeoMap, ImageMap, DeepZoomMap } from './maps/map.js'
|
import { GeoMap, ImageMap, DeepZoomMap } from './maps/map.js'
|
||||||
import { MapData, DeepZoomMapData } from './maps/mapdata.js'
|
import { MapData, DeepZoomMapData } from './maps/mapdata.js'
|
||||||
|
|
||||||
|
@ -80,7 +79,8 @@ window.Projection = {
|
||||||
Robinson
|
Robinson
|
||||||
}
|
}
|
||||||
|
|
||||||
window.MapView = MapView
|
import MapViewport from './maps/mapviewport.js'
|
||||||
|
window.MapViewport = MapViewport
|
||||||
|
|
||||||
import MapApp from './maps/mapapp.js'
|
import MapApp from './maps/mapapp.js'
|
||||||
|
|
||||||
|
@ -100,7 +100,6 @@ window.CompactScatter = CompactScatter
|
||||||
window.CoverScatter = CoverScatter
|
window.CoverScatter = CoverScatter
|
||||||
window.MapObjectScatter = MapObjectScatter
|
window.MapObjectScatter = MapObjectScatter
|
||||||
|
|
||||||
|
|
||||||
import { GeoLayer, MapLayer } from './maps/geolayer.js'
|
import { GeoLayer, MapLayer } from './maps/geolayer.js'
|
||||||
window.GeoLayer = GeoLayer
|
window.GeoLayer = GeoLayer
|
||||||
window.MapLayer = MapLayer
|
window.MapLayer = MapLayer
|
||||||
|
@ -118,5 +117,8 @@ import Overlay from './maps/overlay.js'
|
||||||
window.Overlay = Overlay
|
window.Overlay = Overlay
|
||||||
|
|
||||||
import { MapList } from './maps/maplist.js'
|
import { MapList } from './maps/maplist.js'
|
||||||
|
import GeoJson, { GeoUtils } from './maps/geojson.js'
|
||||||
window.MapList = MapList
|
window.MapList = MapList
|
||||||
|
|
||||||
|
window.GeoJson = GeoJson
|
||||||
|
window.GeoUtils = GeoUtils
|
||||||
|
|
|
@ -37,6 +37,7 @@ const index = new Index(itemTemplate, [
|
||||||
['Flippable', 'flippable.html'],
|
['Flippable', 'flippable.html'],
|
||||||
['LabeledGraphics', 'labeledgraphics.html'],
|
['LabeledGraphics', 'labeledgraphics.html'],
|
||||||
['List', 'list.html'],
|
['List', 'list.html'],
|
||||||
|
['Maps', 'maps/index.html'],
|
||||||
['Message', 'message.html'],
|
['Message', 'message.html'],
|
||||||
['Modal', 'modal.html'],
|
['Modal', 'modal.html'],
|
||||||
['Tooltip', 'tooltip.html'],
|
['Tooltip', 'tooltip.html'],
|
||||||
|
|
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 { EventHandler } from './utils.js'
|
||||||
import { FlagPolygon } from '../graphics/label.js'
|
import { FlagPolygon } from '../graphics/label.js'
|
||||||
import { DeepZoomMap } from './map.js'
|
import { DeepZoomMap } from './map.js'
|
||||||
|
import { PIXIUtils } from '../../../../js/pixi/utils.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GeoGraphics are graphical objects, that does not store the graphics information
|
* GeoGraphics are graphical objects, that does not store the graphics information
|
||||||
|
@ -441,15 +442,12 @@ export class GeoShape extends GeoGraphics {
|
||||||
*/
|
*/
|
||||||
_drawShape(polygon, hole = []) {
|
_drawShape(polygon, hole = []) {
|
||||||
// We save the fill specified in the onDraw event handler.
|
// 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)
|
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.
|
* to make the hole clickable.
|
||||||
*
|
*
|
||||||
* It was a bit confusing, so I made a CodePen
|
* It was a bit confusing, so I made a CodePen
|
||||||
|
@ -460,9 +458,15 @@ export class GeoShape extends GeoGraphics {
|
||||||
this.graphics.drawPolygon(hole)
|
this.graphics.drawPolygon(hole)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* b) Draw the shape.
|
||||||
|
*/
|
||||||
this.graphics.beginFill(fill, alpha)
|
this.graphics.beginFill(fill, alpha)
|
||||||
this.graphics.drawPolygon(polygon)
|
this.graphics.drawPolygon(polygon)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* c) Add the hole.
|
||||||
|
*/
|
||||||
if (hole.length > 0) {
|
if (hole.length > 0) {
|
||||||
this.graphics.beginHole()
|
this.graphics.beginHole()
|
||||||
this.graphics.drawPolygon(hole)
|
this.graphics.drawPolygon(hole)
|
||||||
|
@ -524,6 +528,14 @@ class GeoMultiGraphics extends GeoGraphics {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class GeoText
|
||||||
|
* @extends {GeoPoint}
|
||||||
|
*/
|
||||||
export class GeoText extends GeoPoint {
|
export class GeoText extends GeoPoint {
|
||||||
constructor(coordinates, text, opts) {
|
constructor(coordinates, text, opts) {
|
||||||
super(coordinates, opts)
|
super(coordinates, opts)
|
||||||
|
@ -531,14 +543,6 @@ export class GeoText extends GeoPoint {
|
||||||
this.textStyle = Object.assign(new PIXI.TextStyle(), opts.textStyle)
|
this.textStyle = Object.assign(new PIXI.TextStyle(), opts.textStyle)
|
||||||
this._text = new PIXI.Text(text, this.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) {
|
switch (this.align) {
|
||||||
case 'left':
|
case 'left':
|
||||||
break
|
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
|
* @static
|
||||||
* @export
|
* @export
|
||||||
* @class GeoJsonGraphics
|
* @class
|
||||||
* @extends {GeoGraphics}
|
|
||||||
*/
|
*/
|
||||||
export default class GeoJson {
|
export default class GeoJson {
|
||||||
static isLineType(type) {
|
static isLineType(type) {
|
||||||
|
@ -59,7 +58,6 @@ export default class GeoJson {
|
||||||
}
|
}
|
||||||
|
|
||||||
list.push({ type, coordinates })
|
list.push({ type, coordinates })
|
||||||
// console.log({type, coordinates})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return list
|
return list
|
||||||
|
@ -123,7 +121,7 @@ export default class GeoJson {
|
||||||
* considered valid. A complete list is provided in the GeoUtils.
|
* considered valid. A complete list is provided in the GeoUtils.
|
||||||
*
|
*
|
||||||
* @param {object} point - The point that is tested for validity.
|
* @param {object} point - The point that is tested for validity.
|
||||||
* @returns
|
* @returns {boolean}
|
||||||
* @memberof GeoJson
|
* @memberof GeoJson
|
||||||
*/
|
*/
|
||||||
static validateAndConvertPoint(point) {
|
static validateAndConvertPoint(point) {
|
||||||
|
@ -302,7 +300,7 @@ export class GeoUtils {
|
||||||
* {latitude: lat, longitude: lng}
|
* {latitude: lat, longitude: lng}
|
||||||
*
|
*
|
||||||
* @static
|
* @static
|
||||||
* @param {object / array} coordinate - Coordinate to be tested, if it is an valid coordinate.
|
* @param {object|array} coordinate - Coordinate to be tested, if it is an valid coordinate.
|
||||||
* @returns - Returns the coordinate properly transformed. If transformation was not possible, it returns null.
|
* @returns - Returns the coordinate properly transformed. If transformation was not possible, it returns null.
|
||||||
* @memberof GeoGraphics
|
* @memberof GeoGraphics
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { GeoMap } from './map.js'
|
import { GeoMap } from './map.js'
|
||||||
import MapView from './mapview.js'
|
|
||||||
import { EventHandler } from './utils.js'
|
import { EventHandler } from './utils.js'
|
||||||
import { GeoGraphics } from './geographics.js'
|
import { GeoGraphics } from './geographics.js'
|
||||||
import { MapList } from './maplist.js'
|
import { MapList } from './maplist.js'
|
||||||
//import { GeoGraphics } from "../pixi/geographics.js"
|
import MapViewport from './mapviewport.js'
|
||||||
|
import { ScatterContainer } from '../scatter.js'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The GeoLayer is a special PIXILayer, that recognizes other GeoLayers and
|
* The GeoLayer is a special PIXILayer, that recognizes other GeoLayers and
|
||||||
|
@ -93,6 +93,13 @@ export class GeoLayer {
|
||||||
return this._visibility
|
return this._visibility
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Alias for geoLayer.displayObject.addChild.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @param {GeoGraphics | PIXI.DisplayObject} element - Element to add to the displayObject.
|
||||||
|
* @memberof GeoLayer
|
||||||
|
*/
|
||||||
addChild(element) {
|
addChild(element) {
|
||||||
this.displayObject.addChild(element)
|
this.displayObject.addChild(element)
|
||||||
}
|
}
|
||||||
|
@ -117,15 +124,6 @@ export class GeoLayer {
|
||||||
} else console.error('There was no map specified.', this)
|
} else console.error('There was no map specified.', this)
|
||||||
}
|
}
|
||||||
|
|
||||||
// place(geographic) {
|
|
||||||
// if (geographic.constructor.name.startsWith('Geo') && geographic.graphics) {
|
|
||||||
// // Fix to remove the rollupjs circular dependency
|
|
||||||
// //if (geographic instanceof GeoGraphics) {
|
|
||||||
// this.geographics.push(geographic)
|
|
||||||
// super.place(geographic.graphics)
|
|
||||||
// } else super.place(geographic)
|
|
||||||
// }
|
|
||||||
|
|
||||||
removeFromParent() {
|
removeFromParent() {
|
||||||
if (this.parent) {
|
if (this.parent) {
|
||||||
this.parent.removeLayer(this)
|
this.parent.removeLayer(this)
|
||||||
|
@ -165,8 +163,15 @@ export class GeoLayer {
|
||||||
return this._parent
|
return this._parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a GeoLayer as child to the GeoLayer.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @param {GeoLayer} layer - GeoLayer to add.
|
||||||
|
* @memberof GeoLayer
|
||||||
|
*/
|
||||||
addLayer(layer) {
|
addLayer(layer) {
|
||||||
if (layer instanceof GeoLayer || layer instanceof MapLayer) {
|
if (layer instanceof GeoLayer) {
|
||||||
layer.removeFromParent()
|
layer.removeFromParent()
|
||||||
|
|
||||||
this.layers.push(layer)
|
this.layers.push(layer)
|
||||||
|
@ -218,6 +223,18 @@ export class GeoLayer {
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The map layer is responsible for showing certain maps, at a specific position It contains
|
||||||
|
* a list of available maps and can switch between them seamlessly. GeoGraphics placed on the MapLayer itself
|
||||||
|
* or child Geolayers will be adapted to maps and adjusted on map change automatically.
|
||||||
|
*
|
||||||
|
* The map layer is the 'king' of the geo layers. Every geolayer
|
||||||
|
* needs a map layer at it's root. Otherwise they won't work-
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class MapLayer
|
||||||
|
* @extends {GeoLayer}
|
||||||
|
*/
|
||||||
export class MapLayer extends GeoLayer {
|
export class MapLayer extends GeoLayer {
|
||||||
constructor(
|
constructor(
|
||||||
mapList,
|
mapList,
|
||||||
|
@ -247,7 +264,7 @@ export class MapLayer extends GeoLayer {
|
||||||
listeners: onChange
|
listeners: onChange
|
||||||
})
|
})
|
||||||
|
|
||||||
this.mapview = new MapView({
|
this.mapview = new MapViewport({
|
||||||
zoom,
|
zoom,
|
||||||
focus,
|
focus,
|
||||||
viewport
|
viewport
|
||||||
|
@ -279,22 +296,34 @@ export class MapLayer extends GeoLayer {
|
||||||
this._mapChangeLocked = false
|
this._mapChangeLocked = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adapts all child layers and their GeoGraphics.
|
||||||
|
*
|
||||||
|
* This is called primarily on a map change.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @memberof MapLayer
|
||||||
|
*/
|
||||||
adapt() {
|
adapt() {
|
||||||
this.layers.forEach(layer => {
|
this.layers.forEach(layer => {
|
||||||
if (layer.adapt) layer.adapt(this.map)
|
if (layer.adapt) layer.adapt(this.map)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
focus(coordinates, zoom) {
|
|
||||||
this.mapview.updateFocusPoint(this.map)
|
|
||||||
}
|
|
||||||
|
|
||||||
transformed(e) {
|
transformed(e) {
|
||||||
this.mapview.transformed(this.map)
|
this.mapview.transformed(this.map)
|
||||||
this.layers.forEach(layer => layer.parentMapLayerTransformed(this))
|
this.layers.forEach(layer => layer.parentMapLayerTransformed(this))
|
||||||
this.transformHandler.call(this)
|
this.transformHandler.call(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones the map layer-
|
||||||
|
*
|
||||||
|
* @param {ScatterContainer} scatterContainer - ScatterContainer of the app.
|
||||||
|
* @param {PIXI.DisplayObject} [container=null] - Container of the newly created MapLayer. If null, an empty PIXI.Container will be created.
|
||||||
|
* @returns
|
||||||
|
* @memberof MapLayer
|
||||||
|
*/
|
||||||
clone(scatterContainer, container = null) {
|
clone(scatterContainer, container = null) {
|
||||||
let mapList = this.mapList.clone()
|
let mapList = this.mapList.clone()
|
||||||
container = container == null ? new PIXI.Container() : container
|
container = container == null ? new PIXI.Container() : container
|
||||||
|
@ -369,23 +398,46 @@ export class MapLayer extends GeoLayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the mapviews focus to the map.
|
||||||
|
* This may be useful, if the container was modified.
|
||||||
|
*
|
||||||
|
* @memberof MapLayer
|
||||||
|
*/
|
||||||
refocus() {
|
refocus() {
|
||||||
this.mapview.apply(this.map)
|
this.mapview.apply(this.map)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @public
|
||||||
|
* @returns {GeoMap} - Returns the active map.
|
||||||
|
* @readonly
|
||||||
|
* @memberof MapLayer
|
||||||
|
*/
|
||||||
get map() {
|
get map() {
|
||||||
return this.mapList.map
|
return this.mapList.map
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is required for the consistency of georelated layers.
|
*
|
||||||
* The request traverses up to the mapLayer where it then returns
|
* This is required for the geo layers.
|
||||||
* the responsible map layer.
|
* MapLayer requests from the geoLayers traverse up to the next MapLayer.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @returns {MapLayer} - Returns this MapLayer.
|
||||||
|
* @readonly
|
||||||
|
* @memberof MapLayer
|
||||||
*/
|
*/
|
||||||
get mapLayer() {
|
get mapLayer() {
|
||||||
return this
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cleans up the MapLayer.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @memberof MapLayer
|
||||||
|
*/
|
||||||
cleanup() {
|
cleanup() {
|
||||||
this.mapList.cleanup()
|
this.mapList.cleanup()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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() {
|
flushHandlers() {
|
||||||
// this.onLoaded
|
this.onLoad.empty()
|
||||||
this.onTransform.empty()
|
this.onTransform.empty()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Locks all transformations on the map.
|
||||||
|
* Single parameters can be set if necessary. False means the value is locked, true means they can be modified.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @param {object} [{ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false }={}]
|
||||||
|
* @memberof GeoMap
|
||||||
|
*/
|
||||||
lock({ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false } = {}) {
|
lock({ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false } = {}) {
|
||||||
if (this.image && this.image.scatter) {
|
if (this.image && this.image.scatter) {
|
||||||
this.image.scatter.translatable = rotatable
|
this.image.scatter.translatable = rotatable
|
||||||
|
@ -105,30 +113,26 @@ export class GeoMap {
|
||||||
this.image.scatter.rotatable = movableY
|
this.image.scatter.rotatable = movableY
|
||||||
this.image.scatter.scalable = scalable
|
this.image.scatter.scalable = scalable
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issue #001: This causes the map to not be displayed at the correct position on
|
|
||||||
// map change.
|
|
||||||
// // Rotation does not yet work with the cover mechanism.
|
|
||||||
// //this.rotatable = false
|
|
||||||
// this.translatable = false
|
|
||||||
// this.scalable = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unlocks all transformations on the map.
|
||||||
|
* Single parameters can be set if necessary. False means the value is locked, true means they can be modified.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @param {object} [{ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false }={}]
|
||||||
|
* @memberof GeoMap
|
||||||
|
*/
|
||||||
unlock({ rotatable = true, translatable = true, movableX = true, movableY = true, scalable = true } = {}) {
|
unlock({ rotatable = true, translatable = true, movableX = true, movableY = true, scalable = true } = {}) {
|
||||||
if (this.image && this.image.scatter) {
|
this.lock({ rotatable, translatable, movableX, movableY, scalable })
|
||||||
this.image.scatter.translatable = translatable
|
|
||||||
this.image.scatter.movableX = movableX
|
|
||||||
this.image.scatter.movableY = movableY
|
|
||||||
this.image.scatter.rotatable = rotatable
|
|
||||||
this.image.scatter.scalable = scalable
|
|
||||||
}
|
|
||||||
// Issue #001
|
|
||||||
// // Rotation does not yet work with the cover mechanism.
|
|
||||||
// //this.rotatable = true
|
|
||||||
// this.translatable = true
|
|
||||||
// this.scalable = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unloads the image of the map.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @memberof GeoMap
|
||||||
|
*/
|
||||||
unload() {
|
unload() {
|
||||||
if (this.image) {
|
if (this.image) {
|
||||||
if (this.image.parent) {
|
if (this.image.parent) {
|
||||||
|
@ -142,6 +146,12 @@ export class GeoMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the map, freeing all memory ba flushing handlers and removing the image.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @memberof GeoMap
|
||||||
|
*/
|
||||||
remove() {
|
remove() {
|
||||||
if (this.image) this.image.mask = null
|
if (this.image) this.image.mask = null
|
||||||
|
|
||||||
|
@ -175,8 +185,6 @@ export class GeoMap {
|
||||||
this.image = image
|
this.image = image
|
||||||
if (frame) this.setFrame(frame)
|
if (frame) this.setFrame(frame)
|
||||||
|
|
||||||
let boundaries = this.mapdata.getBoundaries()
|
|
||||||
|
|
||||||
let scatterOpts = Object.assign({
|
let scatterOpts = Object.assign({
|
||||||
cover: this.cover,
|
cover: this.cover,
|
||||||
scaleable: this.scaleable,
|
scaleable: this.scaleable,
|
||||||
|
@ -186,7 +194,6 @@ export class GeoMap {
|
||||||
startScale: this.startScale,
|
startScale: this.startScale,
|
||||||
minScale: this.minScale,
|
minScale: this.minScale,
|
||||||
maxScale: this.maxScale,
|
maxScale: this.maxScale,
|
||||||
boundaries,
|
|
||||||
onTransform: this.transformed.bind(this)
|
onTransform: this.transformed.bind(this)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@ -242,7 +249,7 @@ export class GeoMap {
|
||||||
* to a coordinate with latitude and longitude.
|
* to a coordinate with latitude and longitude.
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @param {object} point - Point in form of {x: x_val, y: y_val}.
|
* @param {object} point - Point in form of {x, y}.
|
||||||
* @returns {object} - Coordinates on the map in form of {x: latitude, y: longitude}.
|
* @returns {object} - Coordinates on the map in form of {x: latitude, y: longitude}.
|
||||||
*/
|
*/
|
||||||
coordinatesFromPoint(point) {
|
coordinatesFromPoint(point) {
|
||||||
|
@ -254,7 +261,7 @@ export class GeoMap {
|
||||||
* Transform coordinates in the map into pixel positions on the deep zoom image.
|
* Transform coordinates in the map into pixel positions on the deep zoom image.
|
||||||
*
|
*
|
||||||
* @param {object} coords - Coordinates of a map position in form {x: latitude, y: longitude}.
|
* @param {object} coords - Coordinates of a map position in form {x: latitude, y: longitude}.
|
||||||
* @return - Returns a image position in form of {x: x_val, y: y_val}.
|
* @return {Point} - Returns a image position in form of {x: x, y: y}.
|
||||||
*/
|
*/
|
||||||
coordinatesToPoint(coordinates) {
|
coordinatesToPoint(coordinates) {
|
||||||
return this.toAbsolutePixelCoordinates(this.mapdata.toPixel(coordinates))
|
return this.toAbsolutePixelCoordinates(this.mapdata.toPixel(coordinates))
|
||||||
|
|
|
@ -11,6 +11,9 @@ import { MapList } from './maplist.js'
|
||||||
* MapApp is responsible for showing fullscreen
|
* MapApp is responsible for showing fullscreen
|
||||||
* map applications.
|
* map applications.
|
||||||
*
|
*
|
||||||
|
* @export
|
||||||
|
* @class MapApp
|
||||||
|
* @extends {PIXIApp}
|
||||||
*/
|
*/
|
||||||
export default class MapApp extends PIXIApp {
|
export default class MapApp extends PIXIApp {
|
||||||
constructor(opts = {}) {
|
constructor(opts = {}) {
|
||||||
|
@ -191,9 +194,9 @@ export default class MapApp extends PIXIApp {
|
||||||
}
|
}
|
||||||
|
|
||||||
selectMap(key) {
|
selectMap(key) {
|
||||||
|
if (this.debug) console.log('Select map', key, result)
|
||||||
let result = this.mapList.select(key)
|
let result = this.mapList.select(key)
|
||||||
|
|
||||||
console.log('Select map', key, result)
|
|
||||||
if (result && this.mapLayer) {
|
if (result && this.mapLayer) {
|
||||||
this.mapLayer.changeMap(this.mapList.map)
|
this.mapLayer.changeMap(this.mapList.map)
|
||||||
}
|
}
|
||||||
|
@ -226,6 +229,14 @@ export default class MapApp extends PIXIApp {
|
||||||
this.onTransform.call(this, event)
|
this.onTransform.call(this, event)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Called when the mapLayer changed the map.
|
||||||
|
*
|
||||||
|
* @private
|
||||||
|
* @param {*} lastMap
|
||||||
|
* @memberof MapApp
|
||||||
|
*/
|
||||||
_mapChanged(lastMap) {
|
_mapChanged(lastMap) {
|
||||||
if (lastMap) {
|
if (lastMap) {
|
||||||
lastMap.flushHandlers()
|
lastMap.flushHandlers()
|
||||||
|
|
|
@ -14,7 +14,7 @@ export class MapData {
|
||||||
* @constructor
|
* @constructor
|
||||||
* @param {Projection}[projection] - Specifies the projection of the map (e.g. Mercator Projection).
|
* @param {Projection}[projection] - Specifies the projection of the map (e.g. Mercator Projection).
|
||||||
* @param {object}[opts] - Addiditonal options.
|
* @param {object}[opts] - Addiditonal options.
|
||||||
* @param {[[minLat, minLng],[maxLat, maxLng]]}[opts.bounds] - Describes the minimum and maximum coordinates on the map
|
* @param {array}[opts.bounds] - Describes the minimum and maximum coordinates on the map in the form of {[[minLat, minLng],[maxLat, maxLng]]}.
|
||||||
* @param {Point}[opts.translate] - Defines a translation, when clipping is not an option (e.g. when the whole world is shown, but translated.)
|
* @param {Point}[opts.translate] - Defines a translation, when clipping is not an option (e.g. when the whole world is shown, but translated.)
|
||||||
*/
|
*/
|
||||||
constructor(projection, opts = {}) {
|
constructor(projection, opts = {}) {
|
||||||
|
@ -52,6 +52,7 @@ export class MapData {
|
||||||
/**
|
/**
|
||||||
* Transforms a pixel point on the map to a geographical coordinate.
|
* Transforms a pixel point on the map to a geographical coordinate.
|
||||||
*
|
*
|
||||||
|
* @public
|
||||||
* @param {{x,y} | PIXI.Point} point - A pixel position on the map.
|
* @param {{x,y} | PIXI.Point} point - A pixel position on the map.
|
||||||
* @returns {{x,y} | PIXI.Point} - A geographical coordinate.
|
* @returns {{x,y} | PIXI.Point} - A geographical coordinate.
|
||||||
* @memberof MapData
|
* @memberof MapData
|
||||||
|
@ -84,6 +85,7 @@ export class MapData {
|
||||||
/**
|
/**
|
||||||
* Transform a geographical coordinate to a pixel point on the map.
|
* Transform a geographical coordinate to a pixel point on the map.
|
||||||
*
|
*
|
||||||
|
* @public
|
||||||
* @param {{x,y} | PIXI.Point} coordinates - A point in the form of {x:lat,y:lng}.
|
* @param {{x,y} | PIXI.Point} coordinates - A point in the form of {x:lat,y:lng}.
|
||||||
* @returns {{x,y} | PIXI.Point} point - A pixel position on the map.
|
* @returns {{x,y} | PIXI.Point} point - A pixel position on the map.
|
||||||
* @memberof MapData
|
* @memberof MapData
|
||||||
|
@ -116,6 +118,18 @@ export class MapData {
|
||||||
return point
|
return point
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get's the clipping of the map data. Clipping describes the
|
||||||
|
* piece of the map that is shown. E.g. if we just show a map of
|
||||||
|
* europe, then we have to set the clipping properly, otherwise
|
||||||
|
* the preojection would produce the wrong results when transforming
|
||||||
|
* from a point to coordinates or the other way around.
|
||||||
|
*
|
||||||
|
* @readonly
|
||||||
|
* @memberof MapData
|
||||||
|
* @returns {object} - Object that contains a min and max value of the clipping in form of: {min: {x,y}, max:{x,y}}. Where x and y are in between 0 and 1.
|
||||||
|
*/
|
||||||
get clip() {
|
get clip() {
|
||||||
let unclipped = {
|
let unclipped = {
|
||||||
min: { x: 0, y: 0 },
|
min: { x: 0, y: 0 },
|
||||||
|
@ -125,30 +139,30 @@ export class MapData {
|
||||||
return this.opts.clip ? this.opts.clip : unclipped
|
return this.opts.clip ? this.opts.clip : unclipped
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Bounds to pixel transforms some bounds in form of {min:{x:minLat, y:minLng},max:{x:maxLat, y:maxLng}}
|
* Returns the biggest viewport the mapdata allows.
|
||||||
* to pixel coordinates.
|
* This is determined by the projecton or the clipping on the mapapp.
|
||||||
*
|
*
|
||||||
|
* @readonly
|
||||||
|
* @memberof MapData
|
||||||
*/
|
*/
|
||||||
getBoundaries() {
|
|
||||||
// let min = this.toPixel(bounds.min)
|
|
||||||
// let max = this.toPixel(bounds.max)
|
|
||||||
|
|
||||||
// Y values needs to be swapped, as PIXI has it's origin
|
|
||||||
// in the top-left corner and a regular map in the bottom-left corner.
|
|
||||||
let boundaries = {
|
|
||||||
min: { x: 0, y: 0 },
|
|
||||||
max: { x: 1, y: 1 }
|
|
||||||
}
|
|
||||||
|
|
||||||
return boundaries
|
|
||||||
}
|
|
||||||
|
|
||||||
get maxViewport() {
|
get maxViewport() {
|
||||||
return this.opts.clip ? this.opts.clip : this.projection.maxViewport
|
return this.opts.clip ? this.opts.clip : this.projection.maxViewport
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Special mapdata for DeepZoomMap objects.
|
||||||
|
*
|
||||||
|
* Note: It just transform the clipping parameter of the tiles config
|
||||||
|
* to the clipping of the mapapp.
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class DeepZoomMapData
|
||||||
|
* @extends {MapData}
|
||||||
|
*/
|
||||||
export class DeepZoomMapData extends MapData {
|
export class DeepZoomMapData extends MapData {
|
||||||
constructor(projection, tilesConfig, opts = {}) {
|
constructor(projection, tilesConfig, opts = {}) {
|
||||||
if (tilesConfig.clip) {
|
if (tilesConfig.clip) {
|
||||||
|
|
|
@ -1,3 +1,10 @@
|
||||||
|
/**
|
||||||
|
* MapList is a list of maps with one active index.
|
||||||
|
* It contains some utility functions to change the map.
|
||||||
|
*
|
||||||
|
* @export
|
||||||
|
* @class MapList
|
||||||
|
*/
|
||||||
export class MapList {
|
export class MapList {
|
||||||
constructor(active = null, maps = {}) {
|
constructor(active = null, maps = {}) {
|
||||||
this.maps = maps
|
this.maps = maps
|
||||||
|
@ -9,8 +16,9 @@ export class MapList {
|
||||||
/**
|
/**
|
||||||
* Selects a map from the map list.
|
* Selects a map from the map list.
|
||||||
*
|
*
|
||||||
|
* @public
|
||||||
* @param {string} active - Name of the map to select.
|
* @param {string} active - Name of the map to select.
|
||||||
* @returns
|
* @returns {Map} - Returns the active map. Returns null if no map was added to the MapList.
|
||||||
* @memberof MapList
|
* @memberof MapList
|
||||||
*/
|
*/
|
||||||
select(active) {
|
select(active) {
|
||||||
|
@ -40,6 +48,13 @@ export class MapList {
|
||||||
return map
|
return map
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clones the entire maplist.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @returns {MapList} - Returns a cloned instance of this map list.
|
||||||
|
* @memberof MapList
|
||||||
|
*/
|
||||||
clone() {
|
clone() {
|
||||||
let maps = {}
|
let maps = {}
|
||||||
|
|
||||||
|
@ -50,6 +65,14 @@ export class MapList {
|
||||||
return new MapList(this.active, maps)
|
return new MapList(this.active, maps)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a new map to the map list.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @param {string} key - Key to identify the map.
|
||||||
|
* @param {GeoMap} map - The GeoMap to add.
|
||||||
|
* @memberof MapList
|
||||||
|
*/
|
||||||
add(key, map) {
|
add(key, map) {
|
||||||
if (this.maps[key] != null) consol.warn('Key already in mapList. The existing key was overwritten.')
|
if (this.maps[key] != null) consol.warn('Key already in mapList. The existing key was overwritten.')
|
||||||
if (this.active == null) this.active = key
|
if (this.active == null) this.active = key
|
||||||
|
@ -57,10 +80,25 @@ export class MapList {
|
||||||
this.maps[key] = map
|
this.maps[key] = map
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the the active map.
|
||||||
|
* If none is set, it returns null.
|
||||||
|
*
|
||||||
|
*@public
|
||||||
|
* @readonly
|
||||||
|
* @memberof MapList
|
||||||
|
*/
|
||||||
get map() {
|
get map() {
|
||||||
return this.maps && this.maps[this.active] ? this.maps[this.active] : null
|
return this.maps && this.maps[this.active] ? this.maps[this.active] : null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Selects the next map in the map array.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @returns {GeoMap} - Returns the next map in the list.
|
||||||
|
* @memberof MapList
|
||||||
|
*/
|
||||||
next() {
|
next() {
|
||||||
let keys = Object.keys(this.maps)
|
let keys = Object.keys(this.maps)
|
||||||
let idx = keys.indexOf(this.active)
|
let idx = keys.indexOf(this.active)
|
||||||
|
@ -69,6 +107,13 @@ export class MapList {
|
||||||
return next
|
return next
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes all maps from the maplist.
|
||||||
|
* And cleans up all maps.
|
||||||
|
*
|
||||||
|
* @public
|
||||||
|
* @memberof MapList
|
||||||
|
*/
|
||||||
cleanup() {
|
cleanup() {
|
||||||
for (let key in this.maps) {
|
for (let key in this.maps) {
|
||||||
let map = this.maps[key]
|
let map = this.maps[key]
|
||||||
|
|
|
@ -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.
|
* It ensures, that maps can be changed, without the user noticing it.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
export default class MapView {
|
export default class MapViewport {
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @param {object} [focus = {x:0, y:0}] - Defines the startup focuspoint of the app.
|
* @param {object} [focus = {x:0, y:0}] - Defines the startup focuspoint of the app.
|
Binary file not shown.
Before Width: | Height: | Size: 184 KiB After Width: | Height: | Size: 172 KiB |
Loading…
Reference in New Issue