41 Commits
Author SHA1 Message Date
Sebastian Kupke 266a770c8e Merge branch 'master' into obersalzberg
# Conflicts:
#	dist/iwmlib.js
#	dist/iwmlib.pixi.js
2021-03-15 17:02:08 +01:00
Uwe Oestermeier a5f94e0a5f Reverted changes from commit fa0256d782 and moved addVelocity from _move back to old locations. 2021-03-15 15:39:07 +01:00
Uwe Oestermeier fff7a4f685 Fixed import order. 2021-03-01 08:58:44 +01:00
Sebastian Kupke d0ae8893cc Version 1.1.1. 2021-02-24 16:15:20 +01:00
Sebastian Kupke 1b4eee6498 Merge branch 'master' of gitea.iwm-tuebingen.de:IWMBrowser/iwmlib 2021-02-24 16:14:26 +01:00
Sebastian Kupke ac779af339 Popup close buttons can take a URL. 2021-02-24 16:13:50 +01:00
Severin Opel 1316a727f3 Fixed offset error. 2021-02-01 13:54:40 +01:00
Severin Opel be950ec8c1 Tried fix scatter. 2021-02-01 13:40:57 +01:00
Severin Opel ca5a39d661 Changed wrapper and events to allow other EventListeners. 2021-01-26 08:01:51 +01:00
Severin Opel fa0256d782 Moved addVelocity to _move. 2021-01-05 16:25:55 +01:00
Severin Opel 114c217ffa Improved logging and adjusted card's click handler
+ Logging now also works when ipcRenderer is appended to the window element.
+ Card's callback replacement method was changed from click to pointerdown.
2020-09-15 11:36:59 +02:00
Severin Opel 28cfdc180c Fixt highlight order on creation. 2020-09-01 13:38:22 +02:00
Uwe Oestermeier 116058ba77 Fixed a problem with clicking transformed rects in card wrappers. 2020-08-27 07:38:35 +02:00
Uwe Oestermeier 4211548b8b Removed console logs. 2020-08-04 18:13:24 +02:00
Uwe Oestermeier 12b17fd7af Fixed call of popup menu commands with parameters.s 2020-08-04 18:07:48 +02:00
Uwe Oestermeier 47b178fbc1 Added event parameter to PopupMenu perform. 2020-08-04 17:35:15 +02:00
Severin Opel 6ab63eb32c Now calling 'this' to allow overloading of methods. 2020-04-21 12:43:55 +02:00
Severin Opel 6445ab3f57 CardLoader.load now checks for ready state. 2020-04-19 13:30:35 +02:00
Severin Opel 129672eb0f Added options object to toggle function. 2020-04-14 12:14:40 +02:00
Uwe Oestermeier 70031d3bfa Removed comment since border case appeared in Edge. 2020-02-25 15:19:01 +01:00
Uwe Oestermeier 9db5723746 Updated dist 2020-02-17 16:20:16 +01:00
Uwe Oestermeier d4ab5c7e9c Merge branch 'master' of https://gitea.iwm-tuebingen.de/IWMBrowser/iwmlib
* 'master' of https://gitea.iwm-tuebingen.de/IWMBrowser/iwmlib:
  Changed scale
  Updated to PixiJS 5.2.1.
2020-02-17 16:19:33 +01:00
Uwe Oestermeier b068fd176f Added lineWidth support. 2020-02-17 16:18:55 +01:00
Uwe Oestermeier f70968fe01 Changed scale 2020-02-07 20:08:44 +01:00
Sebastian Kupke 1c70433b86 Updated to PixiJS 5.2.1. 2020-02-04 16:48:36 +01:00
Uwe Oestermeier 54b685ab42 Changed calling order 2020-02-03 10:06:27 +01:00
Uwe Oestermeier e6f6f6b185 Changed calling order 2020-02-03 10:06:01 +01:00
Uwe Oestermeier 7bb3b696f7 Created new build. 2020-01-31 10:26:41 +01:00
Sebastian Kupke 6cdf5310ba Added graphics alpha workaround. 2020-01-30 15:41:31 +01:00
Sebastian Kupke 1967b791e0 Updated dependencies. 2020-01-22 13:50:40 +01:00
Sebastian Kupke ebb4b9154e Updated pixi-compressed-textures. 2020-01-20 11:33:53 +01:00
Sebastian Kupke b32132c71f Removed stopEvents from RigidContainer. 2020-01-10 14:18:28 +01:00
Sebastian Kupke f398fe4824 Fixed reference bug. 2020-01-09 15:14:27 +01:00
Severin Opel d5b87f23cf Updated generated content (Docs & dist). 2019-12-12 19:04:11 +01:00
Severin Opel 7f2fc93fa3 Reworked overlay itself and doctest. 2019-12-12 19:02:58 +01:00
Severin Opel 32e913c629 Fixed wrongly assigned parameters 2019-12-12 17:23:39 +01:00
Severin Opel 87c966062c Map's cover state now updates every time cover is requested. 2019-12-12 17:01:51 +01:00
Severin Opel d7f867f1a6 Added maps graphics. 2019-12-11 17:30:11 +01:00
Severin Opel e188f3474f Reverted index.css to previous version. 2019-12-11 16:52:23 +01:00
Severin Opel 1e80845aa6 Fixed cover bug. Renamed mapdata to mapprojection. 2019-12-11 16:45:26 +01:00
Severin Opel a85569e54d commited for presentation. 2019-12-11 15:29:59 +01:00
159 changed files with 165099 additions and 48970 deletions
+2 -2
View File
@@ -171,8 +171,8 @@ a {
.doctest {
margin: 0;
padding-top: 50px;
padding-bottom: 50px;
padding-top: 20px;
padding-bottom: 20px;
box-shadow: inset 1px 1px 3px rgba(0, 0, 0, 0.5);
}
.collapsed .doctest {
+30622 -33030
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+118 -112
View File
@@ -429,9 +429,12 @@
window.addEventListener(
'error',
event => {
// if (typeof(event.error) == 'undefined') {
// console.info("Catched undefined error", event)
// }
if (typeof event.error == 'undefined') {
// This sometimes happens in Edge. Since we have no error
// position, we cannot do much beside an info log.
console.info('Catched undefined error', event);
return
}
this.appendError(event.error, event.filename);
},
true
@@ -461,7 +464,10 @@
class Events {
static stop(event) {
event.preventDefault();
event.stopPropagation();
// I removed this, as it destroys all the Hammer.js events.
// And also the click event.
// It seems to have no (apparent) negative impact. -SO
// event.stopPropagation()
}
static extractPoint(event) {
@@ -614,19 +620,6 @@
static toLine(event) {
return `${event.type} #${event.target.id} ${event.clientX} ${event.clientY}`
let result = event.type;
let selector = this.selector(event.target);
result += ' selector: ' + selector;
if (event.target != document.querySelector(selector)) console.log('Cannot resolve', selector);
let keys = ['layerX', 'layerY', 'pageX', 'pageY', 'clientX', 'clientY'];
for (let key of keys) {
try {
result += ' ' + key + ':' + event[key];
} catch (e) {
console.log('Invalid key: ' + key);
}
}
return result
}
static compareExtractedWithSimulated() {
@@ -1092,9 +1085,18 @@
// Distance == 0.0 indicates an inside relation.
static distanceToRect(p, r) {
let cx = Math.max(Math.min(p.x, r.x + r.width), r.x);
let cy = Math.max(Math.min(p.y, r.y + r.height), r.y);
return Math.sqrt((p.x - cx) * (p.x - cx) + (p.y - cy) * (p.y - cy))
let dx = 0;
let dy = 0;
if (p.x < r.x) dx = p.x - r.x;
else if (p.x > r.x + r.width) dx = p.x - (r.x + r.width);
if (p.y < r.y) dy = p.y - r.y;
else if (p.y > r.y + r.height) dy = p.y - (r.y + r.height);
return Math.sqrt(dx * dx + dy * dy)
/* let cx = Math.max(Math.min(p.x, r.x + r.width), r.x)
let cy = Math.max(Math.min(p.y, r.y + r.height), r.y)
let result = Math.sqrt((p.x - cx) * (p.x - cx) + (p.y - cy) * (p.y - cy))
console.log("distanceToRect", p, r, result)
return result */
}
static fromPageToNode(element, p) {
@@ -1722,7 +1724,7 @@
};
try {
ipc = require('electron').ipcRenderer;
ipc = window.ipcRenderer || require('electron').ipcRenderer;
logHandlers.log = message => ipc.send('log', message);
logHandlers.warn = message => ipc.send('warn', message);
logHandlers.error = message => ipc.send('error', message);
@@ -2620,8 +2622,8 @@
if (Events.isCaptured(event)) {
return false
}
let captured = this.target.capture(event);
return captured
return this.target.capture ? this.target.capture(event) : false
}
getPosition(event) {
@@ -2646,25 +2648,6 @@
result[id] = this.getPosition(event);
break
}
// case 'TouchEvent':
// // Needs to be observed: Perhaps changedTouches are all we need. If so
// // we can remove the touchEventKey default parameter
// if (touchEventKey == 'all') {
// for(let t of event.targetTouches) {
// result[t.identifier.toString()] = this.getPosition(t)
// }
// for(let t of event.changedTouches) {
// result[t.identifier.toString()] = this.getPosition(t)
// }
// }
// else {
// for(let t of event.changedTouches) {
// result[t.identifier.toString()] = this.getPosition(t)
// }
// }
// break
default:
break
}
return result
}
@@ -3238,6 +3221,9 @@
let t = performance.now();
let dt = t - this.lastframe;
this.lastframe = t;
// When number is not set or is zero. Use one instead.
let number = delta.number ? delta.number : 1;
if (dt > 0) {
// Avoid division by zero errors later on
// and consider the number of involved pointers sind addVelocity will be called by the
@@ -3245,13 +3231,14 @@
let velocity = {
t: t,
dt: dt,
dx: delta.x / delta.number,
dy: delta.y / delta.number
dx: delta.x / number,
dy: delta.y / number
};
this.velocities.push(velocity);
while (this.velocities.length > buffer) {
this.velocities.shift();
}
}
}
@@ -3499,7 +3486,7 @@
let delta = interaction.delta();
if (delta != null) {
this.addVelocity(delta);
this.addVelocity(delta); // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
let rotate = delta.rotate;
let zoom = delta.zoom;
if (this.maxRotation != null) {
@@ -3699,6 +3686,7 @@
}
_move(delta) {
// this.addVelocity(delta) uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
this.x += this.movableX ? delta.x : 0;
this.y += this.movableX ? delta.y : 0;
}
@@ -3736,14 +3724,14 @@
let newOrigin = Points$1.arc(anchor, beta + rotate, distance * thresholdedZoom);
let extra = Points$1.subtract(newOrigin, origin);
let offset = Points$1.subtract(anchor, origin);
this._move(offset);
const anchorOffset = Points$1.subtract(anchor, origin);
// this._move(offset)
this.scale = newScale;
this.rotation += rotate;
offset = Points$1.negate(offset);
let offset = Points$1.negate(anchorOffset);
offset = Points$1.add(offset, extra);
offset = Points$1.add(offset, translate);
this._move(offset);
this._move(Points$1.add(anchorOffset, offset));
delta.x += extra.x;
delta.y += extra.y;
@@ -4513,7 +4501,8 @@
}
this._x = x;
this._y = y;
TweenLite.set(this.element, { x: x, y: y });
// this.addVelocity({ x: delta.x, y: delta.y }) uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
TweenLite.set(this.element, { x, y });
}
resizeAfterTransform(zoom) {
@@ -4720,16 +4709,24 @@
let clickRects = activeNodes.map(link => {
let rect = link.getBoundingClientRect();
// Since the getBoundingClientRect is untransformed we cannot rely on it's size
// We need a transformed bottom right to calculate local width and height
let bottomRight = Points$1.fromPageToNode(element, {
x: rect.x + rect.width,
y: rect.y + rect.height
});
let topLeft = Points$1.fromPageToNode(element, rect);
let width = Math.abs(bottomRight.x - topLeft.x);
let height = Math.abs(bottomRight.y - topLeft.y);
let center = Points$1.fromPageToNode(element, {
x: rect.x + rect.width / 2,
y: rect.y + rect.height / 2
x: rect.x + width / 2,
y: rect.y + height / 2
});
return {
x: topLeft.x,
y: topLeft.y,
width: rect.width,
height: rect.height,
width,
height,
center,
link
}
@@ -4757,14 +4754,19 @@
/* https://stackoverflow.com/questions/49564905/is-it-possible-to-use-click-function-on-svg-tags-i-tried-element-click-on-a
proposes the dispatchEvent solution. But this leads to problems in flippable.html hiding the back page.
Therefore we use the original click event (see constructor). */
if (this.isSVGNode(node)) {
if (this.triggerSVGClicks) {
let click = new Event('click');
node.dispatchEvent(click);
}
return
}
node.click();
// if (this.isSVGNode(node)) {
// if (this.triggerSVGClicks) {
// let click = new Event('click')
// node.dispatchEvent(click)
// }
// return
// }
// node.click()
let click = new Event('click');
click.clientX = event.clientX;
click.clientY = event.clientY;
node.dispatchEvent(click);
}
nodeTapped(node, event) {
@@ -4952,13 +4954,15 @@
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.open('GET', this.src, false);
xhr.onload = e => {
domNode.innerHTML = this.prepare(xhr.response);
this.addedNode = domNode.firstElementChild;
let { width, height } = this.size(this.addedNode);
if (width) this.wantedWidth = width || this.wantedWidth;
if (height) this.wantedHeight = height || this.wantedHeight;
resolve(this);
xhr.onreadystatechange = e => {
if (xhr.readyState == 4) {
domNode.innerHTML = this.prepare(xhr.response);
this.addedNode = domNode.firstElementChild;
let { width, height } = this.size(this.addedNode);
if (width) this.wantedWidth = width || this.wantedWidth;
if (height) this.wantedHeight = height || this.wantedHeight;
resolve(this);
}
};
xhr.onerror = e => {
reject(this);
@@ -5759,8 +5763,6 @@
let bottom = parseFloat(this.element.style.bottom);
this.element.style.bottom = bottom - delta.y + 'px';
break
default:
break
}
//console.log("onResize", this.onResize)
if (this.onResize) {
@@ -6183,8 +6185,6 @@
x = bbRight;
if (!this.useEventPosWithBoundingBox) y = (bbTop + bbBottom) / 2;
break
default:
break
}
}
@@ -6240,8 +6240,6 @@
x += this.notchSize * 2;
x += this.posOffset;
break
default:
break
}
this.placeOrigin(x, y);
}
@@ -6632,10 +6630,10 @@
Elements$1.addClass(item, 'popupMenuItem');
this.items[key] = item;
item.onclick = event => {
this.perform(key);
this.perform(event, key);
};
item.ontap = event => {
this.perform(key);
this.perform(event, key);
};
item.onmouseover = event => {
this.over(event, key);
@@ -6654,16 +6652,22 @@
}
/** Execute a menu command.
* @param {object} event - The trigger event.
* @param {string} key - The selected key.
*/
perform(key) {
perform(event, key) {
let func = this.commands[key];
if (this.autoClose) {
this.close();
}
setTimeout(() => {
func.call();
}, 20);
setTimeout(
(event, key) => {
func(event, key);
},
20,
event,
key
);
}
/** Update the menu item denoted by key.
@@ -7989,10 +7993,13 @@
* @class Highlight
* @extends {Object}
*/
const SCALE = 1.5;
class Highlight extends Object {
static disableAnimations() {
_HighlightEnabled = false;
let expanded = document.querySelectorAll('.' + Highlight.expandedClass);
let expanded = document.querySelectorAll('.' + this.expandedClass);
for (let obj of expanded) {
this.shrink(obj);
}
@@ -8003,11 +8010,11 @@
}
static removeAnimations(svgRoot) {
let expanded = svgRoot.querySelectorAll('.' + Highlight.expandedClass);
let expanded = svgRoot.querySelectorAll('.' + this.expandedClass);
for (let obj of expanded) {
TweenLite.set(obj, { scale: 1 });
obj.classList.remove('zooming');
obj.classList.remove(Highlight.expandedClass);
obj.classList.remove(this.expandedClass);
}
let defs = svgRoot.querySelector('defs');
while (defs.firstChild) {
@@ -8037,12 +8044,12 @@
}
}
static expand(obj, { scale = 2, duration = 3, stroke = 2, onComplete = null } = {}) {
static expand(obj, { scale = SCALE, duration = 3, stroke = 2, onComplete = null } = {}) {
if (obj == null) return
//console.log("expand")
obj.classList.add('zooming');
TweenLite.to(obj, duration, {
scale: scale,
scale,
onUpdate: () => {
let scale = obj._gsTransform.scaleX;
obj.setAttribute('stroke-width', stroke / scale);
@@ -8050,7 +8057,7 @@
onComplete: () => {
console.log('expand complete');
obj.classList.remove('zooming');
obj.classList.add(Highlight.expandedClass);
obj.classList.add(this.expandedClass);
obj.setAttribute('stroke-width', stroke / scale);
if (onComplete) onComplete();
}
@@ -8070,7 +8077,7 @@
onComplete: () => {
//console.log("shrink complete")
obj.classList.remove('zooming');
obj.classList.remove(Highlight.expandedClass);
obj.classList.remove(this.expandedClass);
obj.setAttribute('stroke-width', stroke);
}
});
@@ -8089,9 +8096,9 @@
let id = circle.getAttribute('id');
TweenLite.set(circle, { transformOrigin: '50% 50%' });
/*if (circle.classList.contains('zooming')) {
console.log("already zooming")
return
}*/
console.log("already zooming")
return
}*/
let svgRoot = circle.closest('svg');
let circleGroup = circle.parentNode;
@@ -8109,7 +8116,7 @@
let maskImageId = 'maskImage' + id;
let maskImage = svgRoot.getElementById(maskImageId);
if (circle.classList.contains(Highlight.expandedClass)) {
if (circle.classList.contains(this.expandedClass)) {
if (!circle.classList.contains('zooming')) {
this.shrink(circle, { stroke });
this.shrink(maskImage, { stroke });
@@ -8132,7 +8139,7 @@
this.shrink(m, { stroke });
}
Highlight._createSVGMask(svgRoot, image, id);
this._createSVGMask(svgRoot, image, id);
// TweenLite.set(maskImage, { transformOrigin: `${tx}% ${ty}%` })
@@ -8142,8 +8149,8 @@
return false
}
static openHighlight(target, { animation = 0.5, scale = 2, onExpanded = null } = {}) {
if (Highlight._isExpanded(target)) {
static openHighlight(target, { animation = 0.5, scale = SCALE, onExpanded = null } = {}) {
if (this._isExpanded(target)) {
console.log('Target is already expanded!');
return
} else {
@@ -8167,11 +8174,11 @@
let image = svgRoot.querySelector('image');
// eslint-disable-next-line no-unused-vars
let [mask, maskImage] = Highlight._getSVGMask(target, {
let [mask, maskImage] = this._getSVGMask(target, {
svgRoot,
image
});
let center = Highlight._calculateCenterRelativeTo(target, image);
let center = this._calculateCenterRelativeTo(target, image);
console.log('_calculateCenterRelativeTo', center);
TweenLite.set(maskImage, {
transformOrigin: `${center.x} ${center.y}`
@@ -8183,16 +8190,16 @@
onComplete: onExpanded
});
target.classList.add(Highlight.expandedClass);
target.classList.add(this.expandedClass);
}
}
static toggleHighlight(node) {
console.log('toggleHighlight', Highlight._isExpanded(node));
if (Highlight._isExpanded(node)) {
Highlight.closeHighlight(node);
static toggleHighlight(node, options = {}) {
console.log('toggleHighlight', this._isExpanded(node));
if (this._isExpanded(node)) {
this.closeHighlight(node, options);
} else {
Highlight.openHighlight(node);
this.openHighlight(node, options);
}
}
@@ -8215,7 +8222,7 @@
let maskImage = svgRoot.getElementById(maskImageId);
if (!mask || !maskImage)
[mask, maskImage] = Highlight._createSVGMask(circle, {
[mask, maskImage] = this._createSVGMask(circle, {
svgRoot,
image,
id
@@ -8281,12 +8288,11 @@
maskImage.setAttribute('width', width);
maskImage.setAttribute('height', height);
maskImage.setAttribute('class', 'addedImage');
svgGroup.insertBefore(maskImage, element); // image.nextSibling)
TweenLite.set(maskImage, { scale: 1 });
maskImage.style.mask = 'url(#' + maskId + ')';
}
svgGroup.appendChild(maskImage);
element.insertAdjacentElement('beforebegin', maskImage); // image.nextSibling)
// svgGroup.appendChild(element)
return [mask, maskImage]
@@ -8304,14 +8310,14 @@
}
static _isExpanded(target) {
return target.classList.contains(Highlight.expandedClass)
return target.classList.contains(this.expandedClass)
}
static closeHighlight(target, { animation = 0.5 } = {}) {
target.classList.remove(Highlight.expandedClass);
target.classList.remove(this.expandedClass);
// eslint-disable-next-line no-unused-vars
let [mask, maskImage] = Highlight._getSVGMask(target);
console.log('Close Highlight', maskImage);
let [mask, maskImage] = this._getSVGMask(target);
// console.log('Close Highlight', maskImage)
TweenLite.to([target, maskImage], animation, {
scale: 1
});
@@ -8321,7 +8327,7 @@
if (!_HighlightEnabled) return
event.stopPropagation();
Highlight.animateCircle(event.target);
this.animateCircle(event.target);
return false
}
@@ -8487,7 +8493,7 @@
*/
static _replaceCallback(context, element, attributeVal) {
if (element.tagName == 'A') {
element.addEventListener('click', event => {
element.addEventListener('pointerdown', event => {
event.preventDefault();
});
}
+1205 -578
View File
File diff suppressed because it is too large Load Diff
+20 -3
View File
@@ -36,7 +36,21 @@
"./lib/pixi/switch.js",
"./lib/pixi/theme.js",
"./lib/pixi/tooltip.js",
"./lib/pixi/volatile.js"
"./lib/pixi/volatile.js",
"./lib/pixi/maps/mapapp.js",
"./lib/pixi/maps/mapviewport.js",
"./lib/pixi/maps/geographics.js",
"./lib/pixi/maps/geojson.js",
"./lib/pixi/maps/geolayer.js",
"./lib/pixi/maps/mapprojection.js",
"./lib/pixi/maps/maplist.js",
"./lib/pixi/maps/overlay.js",
"./lib/pixi/maps/scatter.js",
"./lib/pixi/maps/utils.js",
"./lib/pixi/maps/map.js",
"./lib/pixi/maps/projections/projection.js",
"./lib/pixi/maps/projections/mercator.js",
"./lib/pixi/maps/projections/robinson.js"
],
"exclude": [],
"includePattern": ".+\\.js(doc|x)?$",
@@ -45,7 +59,10 @@
"sourceType": "module",
"tags": {
"allowUnknownTags": true,
"dictionaries": ["jsdoc", "closure"]
"dictionaries": [
"jsdoc",
"closure"
]
},
"templates": {
"applicationName": "iwmlib",
@@ -70,4 +87,4 @@
"useLongnameInNav": false
}
}
}
}
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1830 -239
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1825 -234
View File
File diff suppressed because it is too large Load Diff
+1825 -234
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1825 -234
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1825 -244
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1825 -244
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1828 -237
View File
File diff suppressed because it is too large Load Diff
+1830 -246
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1826 -251
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1829 -236
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1825 -234
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+1829 -238
View File
File diff suppressed because it is too large Load Diff
+1826 -235
View File
File diff suppressed because it is too large Load Diff
+2583 -236
View File
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1829 -240
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
+1841 -233
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
+1817 -230
View File
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1 +1 @@
(function(){var e=0;var a;var t=document.getElementById("source-code");if(t){var n=config.linenums;if(n){t=t.getElementsByTagName("ol")[0];a=Array.prototype.slice.apply(t.children);a=a.map(function(a){e++;a.id="line"+e})}else{t=t.getElementsByTagName("code")[0];a=t.innerHTML.split("\n");a=a.map(function(a){e++;return'<span id="line'+e+'"></span>'+a});t.innerHTML=a.join("\n")}}})();$(function(){var e=$(".navigation");var a=e.find(".list");var t=$(".search");$("#search").on("keyup",function(t){var n=this.value.trim();if(n){var s=new RegExp(n,"i");e.addClass("searching").removeClass("not-searching").find("li, .itemMembers").removeClass("match");e.find("li").each(function(e,a){var t=$(a);if(t.data("name")&&s.test(t.data("name"))){t.addClass("match");t.closest(".itemMembers").addClass("match");t.closest(".item").addClass("match")}})}else{e.removeClass("searching").addClass("not-searching").find(".item, .itemMembers").removeClass("match")}a.scrollTop(0)});$("#menuToggle").click(function(){a.toggleClass("show");t.toggleClass("show")});e.addClass("not-searching");var n=$(".page-title").data("filename").replace(/\.[a-z]+$/,"");var s=e.find('.item[data-name*="'+n+'"]:eq(0)');if(s.length){s.remove().prependTo(a).addClass("current")}if(config.disqus){$(window).on("load",function(){var e=config.disqus;var a=document.createElement("script");a.type="text/javascript";a.async=true;a.src="http://"+e+".disqus.com/embed.js";(document.getElementsByTagName("head")[0]||document.getElementsByTagName("body")[0]).appendChild(a);var t=document.createElement("script");t.async=true;t.type="text/javascript";t.src="http://"+e+".disqus.com/count.js";document.getElementsByTagName("BODY")[0].appendChild(t)})}});
(function(){var e=0;var a;var t=document.getElementById("source-code");if(t){var n=config.linenums;if(n){t=t.getElementsByTagName("ol")[0];a=Array.prototype.slice.apply(t.children);a=a.map(function(a){e++;a.id="line"+e})}else{t=t.getElementsByTagName("code")[0];a=t.innerHTML.split("\n");a=a.map(function(a){e++;return'<span id="line'+e+'"></span>'+a});t.innerHTML=a.join("\n")}}})();$(function(){var e=$(".navigation");var a=e.find(".list");var t=$(".search");$("#search").on("keyup",function(t){var n=this.value.trim();if(n){var s=new RegExp(n,"i");e.addClass("searching").removeClass("not-searching").find("li, .itemMembers").removeClass("match");e.find("li").each(function(e,a){var t=$(a);if(t.data("name")&&s.test(t.data("name"))){t.addClass("match");t.closest(".itemMembers").addClass("match");t.closest(".item").addClass("match")}})}else{e.removeClass("searching").addClass("not-searching").find(".item, .itemMembers").removeClass("match")}a.scrollTop(0)});$("#menuToggle").click(function(){a.toggleClass("show");t.toggleClass("show")});e.addClass("not-searching");var n=$(".page-title").data("filename").replace(/\.[a-z]+$/,"");var s=e.find('a[href*="'+n+'"]:eq(0)').closest(".item");if(s.length){if(s.parents(".children").length){s.addClass("current");s.find("li.item").addClass("notCurrent");s=s.parents("ul.list>li.item")}s.remove().prependTo(a).addClass("current")}if(config.disqus){$(window).on("load",function(){var e=config.disqus;var a=document.createElement("script");a.type="text/javascript";a.async=true;a.src="http://"+e+".disqus.com/embed.js";(document.getElementsByTagName("head")[0]||document.getElementsByTagName("body")[0]).appendChild(a);var t=document.createElement("script");t.async=true;t.type="text/javascript";t.src="http://"+e+".disqus.com/count.js";document.getElementsByTagName("BODY")[0].appendChild(t)})}});
File diff suppressed because one or more lines are too long
+1817 -230
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -176,7 +176,7 @@ export default class Card {
*/
static _replaceCallback(context, element, attributeVal) {
if (element.tagName == 'A') {
element.addEventListener('click', event => {
element.addEventListener('pointerdown', event => {
event.preventDefault()
})
}
+32 -30
View File
@@ -27,10 +27,13 @@ function round(value) {
* @class Highlight
* @extends {Object}
*/
const SCALE = 1.5
export default class Highlight extends Object {
static disableAnimations() {
_HighlightEnabled = false
let expanded = document.querySelectorAll('.' + Highlight.expandedClass)
let expanded = document.querySelectorAll('.' + this.expandedClass)
for (let obj of expanded) {
this.shrink(obj)
}
@@ -41,11 +44,11 @@ export default class Highlight extends Object {
}
static removeAnimations(svgRoot) {
let expanded = svgRoot.querySelectorAll('.' + Highlight.expandedClass)
let expanded = svgRoot.querySelectorAll('.' + this.expandedClass)
for (let obj of expanded) {
TweenLite.set(obj, { scale: 1 })
obj.classList.remove('zooming')
obj.classList.remove(Highlight.expandedClass)
obj.classList.remove(this.expandedClass)
}
let defs = svgRoot.querySelector('defs')
while (defs.firstChild) {
@@ -75,12 +78,12 @@ export default class Highlight extends Object {
}
}
static expand(obj, { scale = 2, duration = 3, stroke = 2, onComplete = null } = {}) {
static expand(obj, { scale = SCALE, duration = 3, stroke = 2, onComplete = null } = {}) {
if (obj == null) return
//console.log("expand")
obj.classList.add('zooming')
TweenLite.to(obj, duration, {
scale: scale,
scale,
onUpdate: () => {
let scale = obj._gsTransform.scaleX
obj.setAttribute('stroke-width', stroke / scale)
@@ -88,7 +91,7 @@ export default class Highlight extends Object {
onComplete: () => {
console.log('expand complete')
obj.classList.remove('zooming')
obj.classList.add(Highlight.expandedClass)
obj.classList.add(this.expandedClass)
obj.setAttribute('stroke-width', stroke / scale)
if (onComplete) onComplete()
}
@@ -108,7 +111,7 @@ export default class Highlight extends Object {
onComplete: () => {
//console.log("shrink complete")
obj.classList.remove('zooming')
obj.classList.remove(Highlight.expandedClass)
obj.classList.remove(this.expandedClass)
obj.setAttribute('stroke-width', stroke)
}
})
@@ -127,9 +130,9 @@ export default class Highlight extends Object {
let id = circle.getAttribute('id')
TweenLite.set(circle, { transformOrigin: '50% 50%' })
/*if (circle.classList.contains('zooming')) {
console.log("already zooming")
return
}*/
console.log("already zooming")
return
}*/
let svgRoot = circle.closest('svg')
let circleGroup = circle.parentNode
@@ -147,7 +150,7 @@ export default class Highlight extends Object {
let maskImageId = 'maskImage' + id
let maskImage = svgRoot.getElementById(maskImageId)
if (circle.classList.contains(Highlight.expandedClass)) {
if (circle.classList.contains(this.expandedClass)) {
if (!circle.classList.contains('zooming')) {
this.shrink(circle, { stroke })
this.shrink(maskImage, { stroke })
@@ -170,7 +173,7 @@ export default class Highlight extends Object {
this.shrink(m, { stroke })
}
Highlight._createSVGMask(svgRoot, image, id)
this._createSVGMask(svgRoot, image, id)
// TweenLite.set(maskImage, { transformOrigin: `${tx}% ${ty}%` })
@@ -180,8 +183,8 @@ export default class Highlight extends Object {
return false
}
static openHighlight(target, { animation = 0.5, scale = 2, onExpanded = null } = {}) {
if (Highlight._isExpanded(target)) {
static openHighlight(target, { animation = 0.5, scale = SCALE, onExpanded = null } = {}) {
if (this._isExpanded(target)) {
console.log('Target is already expanded!')
return
} else {
@@ -205,11 +208,11 @@ export default class Highlight extends Object {
let image = svgRoot.querySelector('image')
// eslint-disable-next-line no-unused-vars
let [mask, maskImage] = Highlight._getSVGMask(target, {
let [mask, maskImage] = this._getSVGMask(target, {
svgRoot,
image
})
let center = Highlight._calculateCenterRelativeTo(target, image)
let center = this._calculateCenterRelativeTo(target, image)
console.log('_calculateCenterRelativeTo', center)
TweenLite.set(maskImage, {
transformOrigin: `${center.x} ${center.y}`
@@ -221,16 +224,16 @@ export default class Highlight extends Object {
onComplete: onExpanded
})
target.classList.add(Highlight.expandedClass)
target.classList.add(this.expandedClass)
}
}
static toggleHighlight(node) {
console.log('toggleHighlight', Highlight._isExpanded(node))
if (Highlight._isExpanded(node)) {
Highlight.closeHighlight(node)
static toggleHighlight(node, options = {}) {
console.log('toggleHighlight', this._isExpanded(node))
if (this._isExpanded(node)) {
this.closeHighlight(node, options)
} else {
Highlight.openHighlight(node)
this.openHighlight(node, options)
}
}
@@ -253,7 +256,7 @@ export default class Highlight extends Object {
let maskImage = svgRoot.getElementById(maskImageId)
if (!mask || !maskImage)
[mask, maskImage] = Highlight._createSVGMask(circle, {
[mask, maskImage] = this._createSVGMask(circle, {
svgRoot,
image,
id
@@ -319,12 +322,11 @@ export default class Highlight extends Object {
maskImage.setAttribute('width', width)
maskImage.setAttribute('height', height)
maskImage.setAttribute('class', 'addedImage')
svgGroup.insertBefore(maskImage, element) // image.nextSibling)
TweenLite.set(maskImage, { scale: 1 })
maskImage.style.mask = 'url(#' + maskId + ')'
}
svgGroup.appendChild(maskImage)
element.insertAdjacentElement('beforebegin', maskImage) // image.nextSibling)
// svgGroup.appendChild(element)
return [mask, maskImage]
@@ -342,14 +344,14 @@ export default class Highlight extends Object {
}
static _isExpanded(target) {
return target.classList.contains(Highlight.expandedClass)
return target.classList.contains(this.expandedClass)
}
static closeHighlight(target, { animation = 0.5 } = {}) {
target.classList.remove(Highlight.expandedClass)
target.classList.remove(this.expandedClass)
// eslint-disable-next-line no-unused-vars
let [mask, maskImage] = Highlight._getSVGMask(target)
console.log('Close Highlight', maskImage)
let [mask, maskImage] = this._getSVGMask(target)
// console.log('Close Highlight', maskImage)
TweenLite.to([target, maskImage], animation, {
scale: 1
})
@@ -359,7 +361,7 @@ export default class Highlight extends Object {
if (!_HighlightEnabled) return
event.stopPropagation()
Highlight.animateCircle(event.target)
this.animateCircle(event.target)
return false
}
+1 -1
View File
@@ -9,8 +9,8 @@
<link rel="stylesheet" href="../../css/highlight.css">
<link rel="stylesheet" href="../../css/popup.css">
<script src="../3rdparty/highlight/highlight.pack.js"></script>
<script src="../../dist/iwmlib.js"></script>
<script src="../../dist/iwmlib.3rdparty.js"></script>
<script src="../../dist/iwmlib.js"></script>
</head>
<body onload="Doctest.run();">
+25 -12
View File
@@ -101,16 +101,24 @@ export default class CardWrapper extends Object {
let clickRects = activeNodes.map(link => {
let rect = link.getBoundingClientRect()
// Since the getBoundingClientRect is untransformed we cannot rely on it's size
// We need a transformed bottom right to calculate local width and height
let bottomRight = Points.fromPageToNode(element, {
x: rect.x + rect.width,
y: rect.y + rect.height
})
let topLeft = Points.fromPageToNode(element, rect)
let width = Math.abs(bottomRight.x - topLeft.x)
let height = Math.abs(bottomRight.y - topLeft.y)
let center = Points.fromPageToNode(element, {
x: rect.x + rect.width / 2,
y: rect.y + rect.height / 2
x: rect.x + width / 2,
y: rect.y + height / 2
})
return {
x: topLeft.x,
y: topLeft.y,
width: rect.width,
height: rect.height,
width,
height,
center,
link
}
@@ -138,14 +146,19 @@ export default class CardWrapper extends Object {
/* https://stackoverflow.com/questions/49564905/is-it-possible-to-use-click-function-on-svg-tags-i-tried-element-click-on-a
proposes the dispatchEvent solution. But this leads to problems in flippable.html hiding the back page.
Therefore we use the original click event (see constructor). */
if (this.isSVGNode(node)) {
if (this.triggerSVGClicks) {
let click = new Event('click')
node.dispatchEvent(click)
}
return
}
node.click()
// if (this.isSVGNode(node)) {
// if (this.triggerSVGClicks) {
// let click = new Event('click')
// node.dispatchEvent(click)
// }
// return
// }
// node.click()
let click = new Event('click')
click.clientX = event.clientX
click.clientY = event.clientY
node.dispatchEvent(click)
}
nodeTapped(node, event) {
+6 -3
View File
@@ -117,9 +117,12 @@ export default class Errors {
window.addEventListener(
'error',
event => {
// if (typeof(event.error) == 'undefined') {
// console.info("Catched undefined error", event)
// }
if (typeof event.error == 'undefined') {
// This sometimes happens in Edge. Since we have no error
// position, we cannot do much beside an info log.
console.info('Catched undefined error', event)
return
}
this.appendError(event.error, event.filename)
},
true
+4 -1
View File
@@ -1,7 +1,10 @@
export default class Events {
static stop(event) {
event.preventDefault()
event.stopPropagation()
// I removed this, as it destroys all the Hammer.js events.
// And also the click event.
// It seems to have no (apparent) negative impact. -SO
// event.stopPropagation()
}
static extractPoint(event) {
+9 -7
View File
@@ -141,13 +141,15 @@ export class HTMLLoader extends CardLoader {
return new Promise((resolve, reject) => {
let xhr = new XMLHttpRequest()
xhr.open('GET', this.src, false)
xhr.onload = e => {
domNode.innerHTML = this.prepare(xhr.response)
this.addedNode = domNode.firstElementChild
let { width, height } = this.size(this.addedNode)
if (width) this.wantedWidth = width || this.wantedWidth
if (height) this.wantedHeight = height || this.wantedHeight
resolve(this)
xhr.onreadystatechange = e => {
if (xhr.readyState == 4) {
domNode.innerHTML = this.prepare(xhr.response)
this.addedNode = domNode.firstElementChild
let { width, height } = this.size(this.addedNode)
if (width) this.wantedWidth = width || this.wantedWidth
if (height) this.wantedHeight = height || this.wantedHeight
resolve(this)
}
}
xhr.onerror = e => {
reject(this)
+2 -2
View File
@@ -849,8 +849,8 @@ export class InteractionDelegate {
if (Events.isCaptured(event)) {
return false
}
let captured = this.target.capture(event)
return captured
return this.target.capture ? this.target.capture(event) : false
}
getPosition(event) {
+1 -1
View File
@@ -10,7 +10,7 @@ let logHandlers = {
}
try {
ipc = require('electron').ipcRenderer
ipc = window.ipcRenderer || require('electron').ipcRenderer
logHandlers.log = message => ipc.send('log', message)
logHandlers.warn = message => ipc.send('warn', message)
logHandlers.error = message => ipc.send('error', message)
+3 -3
View File
@@ -63,10 +63,10 @@ window.Text = Text
//Maps
import { GeoMap, ImageMap, DeepZoomMap } from './maps/map.js'
import { MapData, DeepZoomMapData } from './maps/mapdata.js'
import { MapProjection, DeepZoomMapProjection } from './maps/mapprojection'
window.MapData = MapData
window.DeepZoomMapData = DeepZoomMapData
window.MapProjection = MapProjection
window.DeepZoomMapProjection = DeepZoomMapProjection
window.GeoMap = GeoMap
window.ImageMap = ImageMap
+4 -1
View File
@@ -248,7 +248,10 @@ const button20 = new Button({
y: 310,
label: 'Link Button',
type: 'checkbox',
style: 'link'
style: 'link',
action: event => {
console.log('Link button clicked')
}
})
const button21 = new Button({
+12 -10
View File
@@ -113,6 +113,8 @@ export default class Button extends PIXI.Container {
const theme = Theme.fromString(opts.theme)
this.theme = theme
this.HIDDEN_ALPHA = 0.000000001 // WORKAROUND: See https://github.com/pixijs/pixi.js/wiki/v5-Migration-Guide#graphics-interaction
this.opts = Object.assign(
{},
{
@@ -172,10 +174,10 @@ export default class Button extends PIXI.Container {
if (this.opts.style === 'link') {
Object.assign(this.opts, {
strokeAlpha: 0,
strokeActiveAlpha: 0,
fillAlpha: 0,
fillActiveAlpha: 0
strokeAlpha: this.HIDDEN_ALPHA,
strokeActiveAlpha: this.HIDDEN_ALPHA,
fillAlpha: this.HIDDEN_ALPHA,
fillActiveAlpha: this.HIDDEN_ALPHA
})
}
@@ -286,10 +288,6 @@ export default class Button extends PIXI.Container {
this.opts.beforeAction.call(this, e, this)
}
if (this.opts.action) {
this.opts.action.call(this, e, this)
}
TweenLite.to([this.button, this.content], this.theme.fast, {
alpha: 0.83,
overwrite: 'none'
@@ -299,6 +297,10 @@ export default class Button extends PIXI.Container {
this.active = !this.active
}
if (this.opts.action) {
this.opts.action.call(this, e, this)
}
if (this.opts.afterAction) {
this.opts.afterAction.call(this, e, this)
}
@@ -640,8 +642,8 @@ export default class Button extends PIXI.Container {
hide() {
this.opts.strokeAlpha = 0.0
this.opts.strokeActiveAlpha = 0.0
this.opts.fillAlpha = 0.0000000001 // WORKAROUND: See https://github.com/pixijs/pixi.js/wiki/v5-Migration-Guide#graphics-interaction
this.opts.fillActiveAlpha = 0.0000000001
this.opts.fillAlpha = this.HIDDEN_ALPHA
this.opts.fillActiveAlpha = this.HIDDEN_ALPHA
this.layout()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

After

Width:  |  Height:  |  Size: 44 KiB

+7 -4
View File
@@ -131,10 +131,10 @@
const MercatorProjection = new Projection.Mercator()
const RobinsonProjection = new Projection.Robinson(10)
// In the MapDataOptions specific behaviour can be enforced.
// In the MapProjectionOptions specific behaviour can be enforced.
// E.g. Setting cover to false removes the enforcement of the map to cover the
// whole mapLayer.
const mapDataOptions = { cover: true, debug: true }
const mapProjectionOptions = { cover: true, debug: true }
// Specifies a common zoom height for both maps.
@@ -146,8 +146,8 @@
}
// Create the actual map objects.
let osmMap = new ImageMap(sprites.get(osmworld), new MapData(MercatorProjection, mapDataOptions), mapOptions)
let wikimediaMap = new ImageMap(sprites.get(wikimedia), new MapData(RobinsonProjection, mapDataOptions), mapOptions)
let osmMap = new ImageMap(sprites.get(osmworld), new MapProjection(MercatorProjection, mapProjectionOptions), mapOptions)
let wikimediaMap = new ImageMap(sprites.get(wikimedia), new MapProjection(RobinsonProjection, mapProjectionOptions), mapOptions)
// Add the maps to the mapapp.
// An object is used to have a key, that identifies the maps.
@@ -181,6 +181,9 @@
<!--GeoPoint -->
<section id="geopoint">
<h2>GeoPoint</h2>
<a href="../../../doc/out/GeoPoint.html" class="documentation" target="_blank"><i class="material-icons">
book
</i> GeoPoint</a>
<p>GeoPoint is a single coordinate in the map.</p>
<canvas id="geopoint_canvas"></canvas>
<div class=" controls">
+61 -8
View File
@@ -2,10 +2,10 @@ 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'
import { PIXIUtils } from './utils.js'
/**
* 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
* placed on GeoLayers to work properly.
*
@@ -15,11 +15,12 @@ import { PIXIUtils } from '../../../../js/pixi/utils.js'
* The geolayers forward this 'adaptTo' to all children that are GeoGraphics.
* Which adjust their so called 'point' data to the new map.
*
* @abstract
* @export
* @class GeoGraphics
*/
export class GeoGraphics {
constructor(coordinates, { scale = 1, onDraw = null, onDrawEnd = null, debug = false } = {}) {
this.coordinates = coordinates
this._coordinates = coordinates
this.debug = debug
this.graphics = new PIXI.Graphics()
this.scale = scale
@@ -29,6 +30,17 @@ export class GeoGraphics {
this._position = null
}
/**
* The coordinates of the geographics.
*
* @member {array}
* @readonly
* @memberof GeoGraphics
*/
get coordinates() {
return this._coordinates
}
clone() {
console.error(`Call of abstract method clone(). Overwrite in subclass.`, this)
}
@@ -134,6 +146,12 @@ export class GeoGraphics {
this._layer = layer
}
/**
* Map of the containing layer. Null if on no layer.
*
* @readonly
* @memberof GeoGraphics
*/
get map() {
let map = null
if (this.mapLayer) {
@@ -142,6 +160,13 @@ export class GeoGraphics {
return map
}
/**
* MapLayer of the containing layer. Null if on no layer.
*
* @member {MapLayer}
* @readonly
* @memberof GeoGraphics
*/
get mapLayer() {
let mapLayer = null
if (this.layer) {
@@ -200,6 +225,10 @@ export class GeoGraphics {
*
* This GeoGraphics does not provide any visual representation.
* Draw the desired shape in the onDraw callback.
*
* @export
* @class GeoPoint
* @extends {GeoGraphics}
*/
export class GeoPoint extends GeoGraphics {
clone() {
@@ -229,6 +258,13 @@ export class GeoPoint extends GeoGraphics {
_draw() {}
}
/**
* Represensts a line between two locations.
*
* @export
* @class GeoLine
* @extends {GeoGraphics}
*/
export class GeoLine extends GeoGraphics {
/**
* @param {object} opts - Optional values
@@ -331,6 +367,13 @@ export class GeoLine extends GeoGraphics {
}
}
/**
* Represents a shape on a map.
*
* @export
* @class GeoShape
* @extends {GeoGraphics}
*/
export class GeoShape extends GeoGraphics {
clone() {
return new GeoShape(this.coordinates, this._cloneOptions)
@@ -446,7 +489,7 @@ export class GeoShape extends GeoGraphics {
/**
* 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.
*
@@ -479,6 +522,9 @@ export class GeoShape extends GeoGraphics {
* The MultiGraphics makes use of the other GeoGraphics to
* create a single graphics element out of multiple different
* GeoGraphics.
*
* @class GeoMultiGraphics
* @extends {GeoGraphics}
*/
class GeoMultiGraphics extends GeoGraphics {
/**
@@ -528,9 +574,8 @@ class GeoMultiGraphics extends GeoGraphics {
}
}
/**
*
* Text that is attatched to a GeoPoint.
*
* @export
* @class GeoText
@@ -574,6 +619,8 @@ export class GeoText extends GeoPoint {
/**
* A geotext with a notch at a certain Position.
*
* TODO: This is a specialization for the Tüsch. This should be inside the Tüsch project.
*/
export class GeoFlagLabel extends GeoText {
constructor(coordinates, text, opts) {
@@ -679,6 +726,13 @@ export class GeoFlagLabel extends GeoText {
}
}
/**
* The GeoMultiShape displays multiple forms.
*
* @export
* @class GeoMultiShape
* @extends {GeoShape}
*/
export class GeoMultiShape extends GeoShape {
static _manipulatePoints(points, func) {
points.forEach(shape => {
@@ -718,4 +772,3 @@ export class GeoMultiShape extends GeoShape {
})
}
}
+2 -2
View File
@@ -156,7 +156,7 @@
// As map an image of europe is used.
let europe = "../../../../var/examples/maps/europe/europe.jpg"
let europeData = new MapData(MERCATOR, {
let europeMapProjection = new MapProjection(MERCATOR, {
clip: {
min: { x: 32.863294, y: -18.58 },
max: { x: 57.467973, y: 44.277158 }
@@ -182,7 +182,7 @@
window.readyHandler = new EventHandler("onReady")
function ready(textures) {
let europeMap = new ImageMap(new PIXI.Sprite(textures.get(europe)), europeData)
let europeMap = new ImageMap(new PIXI.Sprite(textures.get(europe)), europeMapProjection)
app.setMap("germany", europeMap)
app.setup().run()
+23 -2
View File
@@ -39,6 +39,15 @@ export default class GeoJson {
return ['Point', 'LineString', 'Polygon', 'MultiPoint', 'MultiLineString', 'MultiPolygon']
}
/**
* Takes a JSON object that contains a FeatureCollection and returns an array
* of GeoJson objects.
*
* @static
* @param {array} featureCollection - Array of GeoJSON objects that were contained in the feature collection.
* @returns {array} Returns an array of geo json objects.
* @memberof GeoJson
*/
static unwrapFeatureCollection(featureCollection) {
if (featureCollection.features == null) {
console.error(
@@ -63,12 +72,22 @@ export default class GeoJson {
return list
}
/**
* Validates and converts one set of coordinates of a specific type.
*
* @static
* @param {string} type - Type of the GeoJson.
* @param {array} coordinates - array of points.
* @returns {array}
* @memberof GeoJson
*/
static validateAndConvert(type, coordinates) {
let converted = null
if (!GeoJson.validateType(type)) throw new GeoJson.InvalidTypeError(type)
else {
if (GeoJson.validateCoordinates(type, coordinates)) {
let converted = GeoJson.convert(type, coordinates)
return converted
converted = GeoJson.convert(type, coordinates)
} else {
console.error(
`Coordinates are invalid. They must be in format of type '${type} - ${GeoJson._getFormatStringOfType(
@@ -77,6 +96,8 @@ export default class GeoJson {
)
}
}
return converted
}
static validateType(type) {
+18 -3
View File
@@ -9,6 +9,9 @@ import { ScatterContainer } from '../scatter.js'
* The GeoLayer is a special PIXILayer, that recognizes other GeoLayers and
* GeoGraphics. The layer can be adapted to a map and notifies all Geo-Children
* of the Adaption.
*
* @export
* @class GeoLayer
*/
export class GeoLayer {
constructor(displayObject, opts = {}) {
@@ -264,7 +267,7 @@ export class MapLayer extends GeoLayer {
listeners: onChange
})
this.mapview = new MapViewport({
this._mapview = new MapViewport({
zoom,
focus,
viewport
@@ -284,6 +287,18 @@ export class MapLayer extends GeoLayer {
this.changeMap(mapList.active)
}
//Todo: rename to mapviewport.
/**
* Returns the MapViewport of this map layer.
*
* @readonly
* @member {MapViewport}
* @memberof MapLayer
*/
get mapview() {
return this._mapview
}
get mapChangeLocked() {
return this._mapChangeLocked
}
@@ -311,7 +326,7 @@ export class MapLayer extends GeoLayer {
}
transformed(e) {
this.mapview.transformed(this.map)
this.mapview.update(this.map)
this.layers.forEach(layer => layer.parentMapLayerTransformed(this))
this.transformHandler.call(this)
}
@@ -321,7 +336,7 @@ export class MapLayer extends GeoLayer {
*
* @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
* @returns {MapLayer} - Returns the cloned MapLayer.
* @memberof MapLayer
*/
clone(scatterContainer, container = null) {
+1 -1
View File
@@ -99,7 +99,7 @@
["GeoJson", "geojson.html"],
["GeoMap", "map.html"],
["MapApp", "mapapp.html"],
["MapData", "mapdata.html"],
["MapProjection", "mapprojection.html"],
["MapViewport", "mapviewport.html"],
["Overlay", "overlay.html"],
["Scatter", "scatter.html"]
+4 -4
View File
@@ -71,10 +71,10 @@
const projection = new Projection.Mercator()
// Create a map data object.
const osmDeepZoomMapData = new DeepZoomMapData(projection, tilesConfig, { app })
const osmDeepZoomMapProjection = new DeepZoomMapProjection(projection, tilesConfig, { app })
// Create the map
const osmMap = new DeepZoomMap(osmDeepZoomMapData, tilesConfig)
const osmMap = new DeepZoomMap(osmDeepZoomMapProjection, tilesConfig)
// Add the map to the app.
app.addMap("osm", osmMap)
@@ -122,10 +122,10 @@
const projection = new Projection.Robinson(10)
// Create a map data object.
let mapData = new MapData(projection)
let mapProjection = new MapProjection(projection)
// Create the map
let imageMap = new ImageMap(sprites.get(mapTexture), mapData)
let imageMap = new ImageMap(sprites.get(mapTexture), mapProjection)
// Add the map to the app.
+118 -44
View File
@@ -1,12 +1,12 @@
import { MapObjectScatter } from './scatter.js'
import { DeepZoomImage, DeepZoomInfo } from '../deepzoom/image.js'
import { MapData, DeepZoomMapData } from './mapdata.js'
import { MapProjection, DeepZoomMapProjection } from './mapprojection.js'
import { Points } from '../../utils.js'
import { EventHandler } from './utils.js'
import Mercator from './projections/mercator.js'
/**
* The GeoMap class displays a map, that it gets from MapData object.
* The GeoMap class displays a map, that it gets from MapProjection object.
* It handles the current location on the map, the zoom factor, interactions and
* the viewport, the area of the map, the user can see and navigate to.
*
@@ -26,12 +26,12 @@ export class GeoMap {
* Creates instance of GeoMap
*
* @constructor
* @param {MapData}[mapdata={}] - The mapdata describes how the map has to be interpreted by the Map class. E.g. what are the boundaries of the map?
* @param {MapProjection}[mapProjection={}] - The map projection describes how the map has to be interpreted by the Map class. E.g. what are the boundaries of the map?
* @param {object}[opts={}] - With the opts, the created MapObjectScatter can be adjusted.
* @param {boolean}[opts.cover=false] - Enables covering behaviour of a map object. Normally maps should cover the whole app.
*/
constructor(
mapdata = {},
mapProjection = {},
{
debug = true,
cover = true,
@@ -43,7 +43,7 @@ export class GeoMap {
translatable = true,
scalable = true,
rotatable = false, // Many functionalities are not supported when rotating the map. Mainly the cover mechanism.
viewport = mapdata.maxViewport,
viewport = mapProjection.maxViewport,
// Events
onLoad = null,
onTransform = null
@@ -54,7 +54,7 @@ export class GeoMap {
this.onTransform = new EventHandler('transform', { listeners: onTransform })
this._alpha = alpha
this.cover = cover
this._cover = cover
this.debug = debug
//TODO discuss if this is required here.
@@ -68,15 +68,15 @@ export class GeoMap {
this.scalable = scalable
this.viewport = viewport
this.mapdata = mapdata
this._mapProjection = mapProjection
this.overlays = {}
/**
* Adjust the viewport depending on the mapdata clipping.
* Adjust the viewport depending on the mapProjection clipping.
*/
if (this.mapdata.clip) {
if (this.mapProjection.clip) {
const vp = this.viewport
const cp = this.mapdata.clip
const cp = this.mapProjection.clip
let bounds = {
min: {
x: vp.min.x > cp.min.x ? vp.min.x : cp.min.x,
@@ -92,6 +92,50 @@ export class GeoMap {
}
}
/**
* Determines if the scatter covers the container.
* @member {boolean}
* @readonly
* @memberof GeoMap
*/
get cover() {
// For cloning we want to store the cover inside the map.
// But the scatter is responsible for the cover state.
// So we update the app's cover state every time
// we get the value.
if (this.image && this.image.scatter && this.image.scatter.cover != null) {
this._cover = this.image.scatter.cover
}
return this._cover
}
/**
* Returns the image object used by the GeoMap.
*
* @readonly
* @memberof GeoMap
*/
get image() {
return this._image
}
/**
* The mapProjection of the map.
*
* @member {MapProjection}
* @readonly
* @memberof GeoMap
*/
get mapProjection() {
return this._mapProjection
}
/**
* Clears all EventHandlers.
*
* @memberof GeoMap
*/
flushHandlers() {
this.onLoad.empty()
this.onTransform.empty()
@@ -107,10 +151,10 @@ export class GeoMap {
*/
lock({ rotatable = false, translatable = false, movableX = false, movableY = false, scalable = false } = {}) {
if (this.image && this.image.scatter) {
this.image.scatter.translatable = rotatable
this.image.scatter.movableX = translatable
this.image.scatter.movableY = movableX
this.image.scatter.rotatable = movableY
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
}
}
@@ -174,6 +218,7 @@ export class GeoMap {
/**
* Wrapps the display object around a scatter object.
* creates the image.
*
* @private
* @param {DisplayObject} displayObject - Defines the display object that will be wrapped inside the scatter object.
@@ -182,7 +227,7 @@ export class GeoMap {
load(image, renderer, frame = null, scatter = null) {
if (this.debug) console.log('Load image: ', image, frame)
this.image = image
this._image = image
if (frame) this.setFrame(frame)
let scatterOpts = Object.assign({
@@ -253,7 +298,7 @@ export class GeoMap {
* @returns {object} - Coordinates on the map in form of {x: latitude, y: longitude}.
*/
coordinatesFromPoint(point) {
let coords = this.mapdata.toCoordinates(this.toRelativePosition(point))
let coords = this.mapProjection.toCoordinates(this.toRelativePosition(point))
return coords
}
@@ -264,7 +309,7 @@ export class GeoMap {
* @return {Point} - Returns a image position in form of {x: x, y: y}.
*/
coordinatesToPoint(coordinates) {
return this.toAbsolutePixelCoordinates(this.mapdata.toPixel(coordinates))
return this.toAbsolutePixelCoordinates(this.mapProjection.toPixel(coordinates))
}
toRelativePosition(point) {
@@ -331,15 +376,41 @@ export class GeoMap {
this.frame = null
}
/**
* The complete Triforce, or one or more components of the Triforce.
* @typedef {Object} Frame
* @property {number} x - X position of the frame.
* @property {number} y - Y position of the frame.
* @property {number} width - Width of the frame.
* @property {number} height - Height od the frame.
* @property {Point} localCenter - Local center of the map.
* @property {Point} center - Global center of the map.
*/
/**
* Sets the frame if the map.
*
* Frame is the display in which the map is shown.
* Normally it's the app, but it can be another element,
* for example when in a submap.
*
* @param {Frame} frame
* @memberof GeoMap
*/
setFrame(frame) {
if (this.debug) console.log('Set Frame: ', frame)
this.frame = frame
}
/**
* Gets the frame if the map.
*
* Frame is the display in which the map is shown.
* Normally it's the app, but it can be another element,
* for example when in a submap.
*
* @returns {Frame} - Returns the frame of the map.
* @memberof GeoMap
*/
getFrame() {
let frame = {
@@ -425,6 +496,7 @@ export class GeoMap {
* for creating the maps.
*
* @static
* @private
* @param {object} json - The object containing multiple map data sets.
* @param {error-object} error - An object that contains an parameter message: {message = ""}. This is faking a call by reference.
* @returns {boolean} - True if all sets were valid. False otherwise.
@@ -460,6 +532,7 @@ export class GeoMap {
*Validates of a single data set contains the valid data for creating a map.
*
* @static
* @private
* @param {object} json - The object containing a single set of map data.
* @returns {boolean} - True if valid, otherwise false.
* @memberof GeoMap
@@ -495,8 +568,8 @@ export class GeoMap {
const tilesConfig = data.tiles
const options = data.options
const mapdata = new DeepZoomMapData(projection, tilesConfig)
return new DeepZoomMap(mapdata, tilesConfig, options)
const mapProjection = new DeepZoomMapProjection(projection, tilesConfig)
return new DeepZoomMap(mapProjection, tilesConfig, options)
}
static _getProjectionByName(projection) {
@@ -513,43 +586,42 @@ export class GeoMap {
GeoMap.counter = 0
/**
* The DeepZoomMap class extends the GeoMap to create
* The DeepZoomMap class extends the GeoMap to create
* maps as deepzoom images from maptiles.
*
* @extends GeoMap
* @class
* @see {@link maps.html}
* @export
* @class DeepZoomMap
* @extends {GeoMap}
*/
export class DeepZoomMap extends GeoMap {
/**
* @constructor
* @param {object} tilesConfig - The tiles config object, that defines at what path and in which format the tiles are.
* @param {MapData} mapdata - A MapData object, that contains informations of how the given map has to be interpreted.
* @param {MapProjection} mapProjection - A MapProjection object, that contains informations of how the given map has to be interpreted.
* @param {object} opts - Additional options to specify the behaviour of the deep zoom image.
*/
constructor(mapdata, tilesConfig, opts = {}) {
constructor(mapProjection, tilesConfig, opts = {}) {
opts = Object.assign(
{
maxScale: Math.min(tilesConfig.width, tilesConfig.height) / tilesConfig.tileSize,
minScale: mapdata.getMinScale,
minScale: mapProjection.getMinScale,
highResolution: true,
debug: false
},
opts
)
super(mapdata, opts)
super(mapProjection, opts)
this.tilesConfig = tilesConfig
this._verifyMapdata()
this._verifyMapProjection()
}
_verifyMapdata() {
if (!(this.mapdata instanceof MapData)) {
console.error('Use the MapData object for creating maps!')
_verifyMapProjection() {
if (!(this.mapProjection instanceof MapProjection)) {
console.error('Use the MapProjection object for creating maps!')
} else {
if (!(this.mapdata instanceof DeepZoomMapData)) {
console.error('Use the DeepZoomMapData object.')
if (!(this.mapProjection instanceof DeepZoomMapProjection)) {
console.error('Use the DeepZoomMapProjection object.')
}
}
}
@@ -560,10 +632,10 @@ export class DeepZoomMap extends GeoMap {
* @private
*/
load(container = null, scatter = null) {
if (!this.mapdata.app) console.error('App was not set in the mapdata.')
if (!this.mapProjection.app) console.error('App was not set in the mapProjection.')
this.info = new DeepZoomInfo(this.tilesConfig)
let image = new DeepZoomImage(this.info, {
app: this.mapdata.app,
app: this.mapProjection.app,
alpha: this.alpha,
debug: this.debug,
world: scatter == null ? scatter : scatter.getWorldScatter()
@@ -681,7 +753,7 @@ export class DeepZoomMap extends GeoMap {
}
clone(container, scatter = null) {
const map = new DeepZoomMap(this.mapdata, this.tilesConfig, {
const map = new DeepZoomMap(this.mapProjection, this.tilesConfig, {
alpha: this.alpha,
cover: this.cover,
debug: this.debug,
@@ -741,15 +813,17 @@ DeepZoomMap.tintcolors = [0xff0000, 0xff00ff, 0xffff00, 0x00ff00, 0x00ffff, 0x00
DeepZoomMap.tintcolor = 0
/**
*
* ImageMap extends GeoMap to display simple images
* as maps.
*
* @export
* @class ImageMap
* @extends {GeoMap}
*/
export class ImageMap extends GeoMap {
constructor(sprite, mapdata, opts = {}) {
super(mapdata, opts)
if (this.debug) console.log('Construct Image Map', sprite, mapdata, opts)
constructor(sprite, mapProjection, opts = {}) {
super(mapProjection, opts)
if (this.debug) console.log('Construct Image Map', sprite, mapProjection, opts)
this.sprite = sprite
@@ -763,7 +837,7 @@ export class ImageMap extends GeoMap {
}
clone(container = null, scatter = null) {
const map = new ImageMap(new PIXI.Sprite(this.sprite.texture), this.mapdata, {
const map = new ImageMap(new PIXI.Sprite(this.sprite.texture), this.mapProjection, {
alpha: this.alpha,
cover: this.cover,
debug: this.debug,
+6 -6
View File
@@ -98,11 +98,11 @@
resolutionDependent: false
})
// The mapdata object contains informations,
// The map projection 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(), {
let europeData = new MapProjection(new Projection.Mercator(), {
clip: {
min: { x: 32.863294, y: -18.58 },
max: { x: 57.467973, y: 44.277158 }
@@ -120,16 +120,16 @@
cover
})
let testMapData = new DeepZoomMapData(new Projection.Mercator(), testConfig.tiles, {
let testMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), testConfig.tiles, {
app
})
let testMap = new DeepZoomMap(testMapData, Object.assign({}, testConfig.tiles, { app }), { cover })
let testMap = new DeepZoomMap(testMapProjection, Object.assign({}, testConfig.tiles, { app }), { cover })
app.addMap("test", testMap)
let osmMapData = new DeepZoomMapData(new Projection.Mercator(), osmConfig.tiles, {
let osmMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), osmConfig.tiles, {
app
})
let deepZoomMap = new DeepZoomMap(osmMapData, Object.assign({}, osmConfig.tiles, { app }), { cover })
let deepZoomMap = new DeepZoomMap(osmMapProjection, Object.assign({}, osmConfig.tiles, { app }), { cover })
app.addMap("osm", deepZoomMap)
// Finally apply the map to the MapApp
+151 -50
View File
@@ -7,6 +7,22 @@ import { Points } from '../../utils.js'
import Logging from '../../logging.js'
import { MapList } from './maplist.js'
/**
* A PIXI.Point or object in form {x,y}.
*
* @typedef {Object} Point
* @property {number} x - Indicates whether the Courage component is present.
* @property {number} y - Indicates whether the Power component is present.
*/
/**
* A coordinate point is a PIXI.Point or object in form {x,y} that contains map coordinates
* instead of pixel values, where x represents the latitude and y the longitude.
* @typedef {Object} CoordinatePoint
* @property {number} x - Indicates whether the Courage component is present.
* @property {number} y - Indicates whether the Power component is present.
*/
/**
* MapApp is responsible for showing fullscreen
* map applications.
@@ -16,6 +32,10 @@ import { MapList } from './maplist.js'
* @extends {PIXIApp}
*/
export default class MapApp extends PIXIApp {
/**
*Creates an instance of MapApp.
* @memberof MapApp
*/
constructor(opts = {}) {
super(opts)
@@ -30,7 +50,6 @@ export default class MapApp extends PIXIApp {
coordsLogging: false,
overlays: {},
keycodes: {},
showHotkeys: false,
imageMapZoomHeight: 256, //Defines the zoomvalue 1 for all image maps inside the mapapp.
focus: null,
zoom: 1,
@@ -47,7 +66,6 @@ export default class MapApp extends PIXIApp {
this.overlayElements = new Map()
this.debug = opts.debug
this.fpsLogging = opts.fpsLogging
this.showHotkeys = opts.showHotkeys
this.keycodes = this._extractKeyCodes(opts.keycodes)
this.coordsLogging = opts.coordsLogging
this.overlays = opts.overlays
@@ -109,9 +127,15 @@ export default class MapApp extends PIXIApp {
console.log(JSON.stringify(boundaries))
}
/**
* Creates the MapLayer.
*
* @private
* @memberof MapApp
*/
_setupMapLayer() {
this.mapContainer = new PIXI.Container()
this.mapLayer = new MapLayer(this.mapList, this.scene, this.mapContainer, {
this._mapLayer = new MapLayer(this.mapList, this.scene, this.mapContainer, {
name: 'Root Map Layer',
focus: this.focus,
zoom: this.zoom,
@@ -177,22 +201,45 @@ export default class MapApp extends PIXIApp {
}
}
/**
* Relayouts the app. E.g. called when the window is resized.
*
* @param {number} width - Desired width of the app.
* @param {number} height - Desired height of the app.
* @memberof MapApp
*/
layout(width, height) {
this.scene.resize(width, height)
this.mapLayer.mapview.update()
this.onSizeChanged.call(this)
}
/**
* Overrides the sceneFactory of the PIXIApp to create a RigidScatterContainer instead of
* a regular PIXI.Container()
*
* @private
* @returns {RigidScatterContainer} - Returns the newly created RigidScatterContainer.
* @memberof MapApp
*/
sceneFactory() {
return new RigidScatterContainer(this.width, this.height, this.renderer, {
app: this,
showBounds: true,
showTouches: true,
showPolygon: true,
container: this
showBounds: false,
showTouches: false,
showPolygon: false,
container: this,
stopEvents: false,
claimEvents: false
})
}
/**
* Changes the map to the given key.
*
* @param {string} key - Identifier of the map to change to.
* @memberof MapApp
*/
selectMap(key) {
if (this.debug) console.log('Select map', key, result)
let result = this.mapList.select(key)
@@ -214,14 +261,30 @@ export default class MapApp extends PIXIApp {
this.selectMap(key)
}
/**
* Adds a map to the maplist.
* If no map is set, the added map will be set as default.
*
* @param {string} key - Identifier for the map.
* @param {GeoMap} map - Map object to add.
* @memberof MapApp
*/
addMap(key, map) {
if (this.mapList) this.mapList.add(key, map)
else console.error('Cannot access mapLayer. It was not initialized yet.')
}
/**
* Adds multiple maps at once.
*
* @param {object} mapObject
* @memberof MapApp
*/
addMaps(mapObject) {
for (let [key, val] of Object.entries(mapObject)) {
this.addMap(key, val)
if (val instanceof GeoMap) {
this.addMap(key, val)
} else console.warn('Tried adding maps that are not og Type GeoMap.')
}
}
@@ -254,6 +317,16 @@ export default class MapApp extends PIXIApp {
}
}
/**
* Returns the mapLayer of the map.
*
* @member {MapLayer}
* @memberof MapApp
*/
get mapLayer() {
return this._mapLayer
}
_doesOverlayElementExist(layer, type, name) {
let layerElements = this.overlayElements.get(layer)
return layerElements != undefined && layerElements[type] != null && layerElements[type][name] != null
@@ -276,7 +349,9 @@ export default class MapApp extends PIXIApp {
}
/**
* Copies the current coordinates to the clipboard.
* Copies the current location to the clipboard.
*
* @memberof MapApp
*/
locationToClipboard() {
let hidden = document.createElement('input')
@@ -289,6 +364,15 @@ export default class MapApp extends PIXIApp {
document.body.removeChild(hidden)
}
/**
* Can be used to copy polygons to the clipboard.
*
* Useful for debugging or to roughly trace a shape in the map.
* The generated pointarray can be used as geometry of a geographic
* or inside an overlay to draw that shape onto the map.
*
* @memberof MapApp
*/
pathToClipboard() {
let hidden = document.createElement('input')
document.body.appendChild(hidden)
@@ -312,25 +396,27 @@ export default class MapApp extends PIXIApp {
document.body.removeChild(hidden)
}
/**
* Returns the active map.
*
* @readonly
* @memberof MapApp
*/
get map() {
return this.mapList.map
}
get activeMapKey() {
return this.mapLayer.active
}
getRelativePosition(x, y) {
return {
x: x * app.width,
y: y * app.height
}
}
clearDrawData() {
this.drawData = []
}
/**
* Logs a text field on the map.
* The text element is a DOMElement.
*
* @param {string} msg - Message to log.
* @memberof MapApp
*/
showNotification(msg) {
let notification = document.createElement('div')
notification.classList.add('notification')
@@ -373,8 +459,25 @@ export default class MapApp extends PIXIApp {
})
}
_currentLocationToString() {}
/**
* @typedef KeyCode
* @type {object}
* @property {string} key - an ID.
* @property {boolean} altKey - Defines if KeyCode requires the alt key to be pressed.
* @property {boolean} shiftKey - Defines if KeyCode requires the shift key to be pressed.
* @property {boolean} ctrlKey - Defines if KeyCode requires the ctrl key to be pressed.
*
*/
/**
* Check's if a key event matches a defined KeyCode.
*
* @private
* @param {KeyboardEvent} event - Event that is fired on keydown, -pressed or -up.
* @param {KeyCode} keyCode - KeyCode is an object in the form of : {key:number, altKey:boolean,shiftKey:boolean,ctrlKey:boolean }
* @returns {boolean} - True, when the event matches the keycode, false otherwise.
* @memberof MapApp
*/
_matchKeyCode(event, keyCode) {
// If keycode does not exist or is invalid - return.
if (!keyCode || keyCode.key == null) return false
@@ -396,6 +499,13 @@ export default class MapApp extends PIXIApp {
})
}
/**
* Checks on every key down if it matches a keycode.
*
* @private
* @param {KeyboardEvent} event
* @memberof MapApp
*/
_checkForKeyCode(event) {
if (this._matchKeyCode(event, this.keycodes.copyCoordinate)) {
event.preventDefault()
@@ -454,10 +564,28 @@ export default class MapApp extends PIXIApp {
this.drawMode = this.DRAW_MODES.PIXI_POINT
}
_extractKeyCodes(keycodeText) {
/**
* @typedef KeyCodePairs
* @type {object}
* @property {string} name - Name of the KeyCode.
* @property {KeyCode} keyCode - KeyCode
*
*/
/**
* Extracts keycodes from a string.
*
* KeycodeStrings may look like this 'ctrl+shift+b'
*
* @private
* @param {KeyCodePairs} nameKeyCodePairs
* @returns {array} - Returns an array of KeyCode objects.
* @memberof MapApp
*/
_extractKeyCodes(nameKeyCodePairs) {
let out = {}
for (let [name, combinationString] of Object.entries(keycodeText)) {
for (let [name, combinationString] of Object.entries(nameKeyCodePairs)) {
let keys = combinationString.split('+')
out[name] = {
key: null,
@@ -528,34 +656,7 @@ export default class MapApp extends PIXIApp {
const center = submap.center
const radius = submap.container.width / 2
const distance = Points.distance(center, event.data.global) / submap.scatter.scale
if (distance > radius) {
//submap.resize((distance) * 2, .2)
//submap.centerAt(myevent.center)
}
}
// for (const submap of this.submaps) {
// const center = submap.center
// const radius = submap.container.width / 2
// const distance = Points.distance(center, event.data.global) / submap.scatter.scale
// const inside = distance < radius + 10
// console.log(distance, radius)
// if (inside) {
// // (this.width + 80) / 2 * this.scatter.scale
// //const width = (submap.width + 80) / 2 * submap.scatter.scale
// //console.log(width)
// if (distance > radius) {
// submap.resize((distance) * 2, .2)
// }
// } else {
// if (distance < radius + 20) {
// //submap.resize((distance - 30) * 2, .2)
// }
// }
// }
}
}
+27 -4
View File
@@ -7,8 +7,8 @@
*/
export class MapList {
constructor(active = null, maps = {}) {
this.maps = maps
this.active = active
this._maps = maps
this._active = active
if (Object.keys(maps).length > 0) this.select(active)
}
@@ -37,7 +37,7 @@ export class MapList {
}
if (this.active !== active) {
this.active = active
this._active = active
map = this.maps[active]
}
} else {
@@ -75,7 +75,7 @@ export class 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
if (this.active == null) this._active = key
map.name = key
this.maps[key] = map
}
@@ -86,12 +86,35 @@ export class MapList {
*
*@public
* @readonly
* @member {GeoMap}
* @memberof MapList
*/
get map() {
return this.maps && this.maps[this.active] ? this.maps[this.active] : null
}
/**
* Returns the list of addedd cards.
*
* @member {object}
* @readonly
* @memberof MapList
*/
get maps() {
return this._maps
}
/**
* Returns the active key.
*
* @member {string}
* @readonly
* @memberof MapList
*/
get active() {
return this._active
}
/**
* Selects the next map in the map array.
*
@@ -3,7 +3,7 @@
<head>
<meta charset='UTF-8'>
<title>MapData</title>
<title>MapProjection</title>
<link rel='stylesheet' href='../../3rdparty/highlight/styles/default.css'>
@@ -37,8 +37,8 @@
</head>
<body onload='Doctest.run()'>
<h1>MapData</h1>
<p>Mapdata calculates is responsible for transforming map coordinates to pixel coordinates and backwards.</p>
<h1>MapProjection</h1>
<p>The map projection calculates is responsible for transforming map coordinates to pixel coordinates and backwards.</p>
<h2>Static Squared World Map</h2>
<p>The most simple example is a squared world map, thats projected with mercator transformation. Ranging from
@@ -73,9 +73,9 @@
//Helper function to print the coordinates to a dom element.
function printCoordinates(element, mapdata, out_dom) {
function printCoordinates(element, mapProjection, out_dom) {
element.addEventListener("mousemove", (event) => {
let coords = mapdata.toCoordinates({ x: event.offsetX / event.target.width, y: event.offsetY / event.target.height })
let coords = mapProjection.toCoordinates({ x: event.offsetX / event.target.width, y: event.offsetY / event.target.height })
out_dom.innerHTML = "Lat: " + coords.x.toFixed(4) + " Lng: " + coords.y.toFixed(4)
})
}
@@ -99,8 +99,8 @@
</script>
<script class="doctest">
// Instantiate our mapdata.
let squared_world = new MapData(new Projection.Mercator());
// Instantiate our map projection.
let squared_world = new MapProjection(new Projection.Mercator());
// Define a position, we are interested in in {x: latitude, y: longitude}
let iceland = { x: 64.514979, y: -19.020796 }
@@ -116,7 +116,7 @@
printCoordinates(map_image_0, squared_world, map_coords_0)
</script>
<h2 id="germany">Clipped Map</h2>
<p>Often we don't use the whole map, or our map is a subsection of the world. MapData can clip those cases, using
<p>Often we don't use the whole map, or our map is a subsection of the world. MapProjection can clip those cases, using
the a
bounding box of min and max coordinates.</p>
<p>Coordinates:
@@ -127,8 +127,8 @@
</div>
<script class="doctest">
//Same procedure as above, just add a clipping parameter to the MapData object.
let europe = new MapData(new Projection.Mercator(), {
//Same procedure as above, just add a clipping parameter to the MapProjection object.
let europe = new MapProjection(new Projection.Mercator(), {
clip: {
min: { x: 32.863294, y: -18.58 },
max: { x: 57.467973, y: 44.277158 }
@@ -159,7 +159,7 @@
const size = 256
// Fake offset by using the old mapdata.
// Fake offset by using the old map projection.
let translation = squared_world.toPixel({ x: 90-10, y: -140 })
console.log(translation)
@@ -167,7 +167,7 @@
//This map is clipped at the bottom
// And also translated in hoizontal direction.
// The translate option corrects that.
let translated_world = new MapData(new Projection.Mercator(), {
let translated_world = new MapProjection(new Projection.Mercator(), {
translate: { x: -10, y: 40 },
})
@@ -1,15 +1,15 @@
/**
* MapData contains the informations about how
* MapProjection contains the informations about how
* a Map has to be interpreted. What are the bounds of the
* map and how to translate coordinates into
* image positions.
*
* @class
* @see {@link mapdata.html}
* @see {@link mapprojection.html}
*/
export class MapData {
export class MapProjection {
/**
* Creates instance of MapData
* Creates instance of MapProjection
*
* @constructor
* @param {Projection}[projection] - Specifies the projection of the map (e.g. Mercator Projection).
@@ -25,7 +25,7 @@ export class MapData {
opts
)
this.projection = projection
this._projection = projection
if (this.opts.clip) {
let _cmin = this.projection.forward(this.opts.clip.min)
@@ -49,13 +49,24 @@ export class MapData {
}
}
/**
* The projection used by the map projection.
*
* @member {Projection}
* @readonly
* @memberof MapProjection
*/
get projection() {
return this._projection
}
/**
* 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
* @param {Point} point - A pixel position on the map.
* @returns {CoordinatePoint} A geographical coordinate.
* @memberof MapProjection
*/
toCoordinates(point) {
if (this.opts.clip) {
@@ -86,9 +97,9 @@ 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
* @param {CoordinatePoint} coordinates - A point in the form of {x:lat,y:lng}.
* @returns {Point} A pixel position on the map.
* @memberof MapProjection
*/
toPixel(coordinates) {
let coords = { x: coordinates.x, y: coordinates.y }
@@ -118,17 +129,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
* Clipping describes the
* piece of the map that is shown. The returned object 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.
*
* 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.
* @member {object}
* @memberof MapProjection
*/
get clip() {
let unclipped = {
@@ -139,31 +151,29 @@ export class MapData {
return this.opts.clip ? this.opts.clip : unclipped
}
/**
* Returns the biggest viewport the mapdata allows.
* Returns the biggest viewport the map projection allows.
* This is determined by the projecton or the clipping on the mapapp.
*
* @readonly
* @memberof MapData
* @memberof MapProjection
*/
get maxViewport() {
return this.opts.clip ? this.opts.clip : this.projection.maxViewport
}
}
/**
* Special mapdata for DeepZoomMap objects.
*
* Special map projection 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 DeepZoomMapProjection
* @extends {MapProjection}
*/
export class DeepZoomMapData extends MapData {
export class DeepZoomMapProjection extends MapProjection {
constructor(projection, tilesConfig, opts = {}) {
if (tilesConfig.clip) {
opts.clip = {
+9 -9
View File
@@ -81,11 +81,11 @@
})
let osmMapData = new DeepZoomMapData(new Projection.Mercator(), osmConfig.tiles, {
let osmMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), osmConfig.tiles, {
app: app
})
let deepZoomMap = new DeepZoomMap(osmMapData, Object.assign({}, osmConfig.tiles, { app: app }), { cover: true })
let deepZoomMap = new DeepZoomMap(osmMapProjection, Object.assign({}, osmConfig.tiles, { app: app }), { cover: true })
app.setMap("deepzoom", deepZoomMap)
@@ -127,11 +127,11 @@
const app = new MapApp(opts)
let osmMapData = new DeepZoomMapData(new Projection.Mercator(), osmConfig.tiles, {
let osmMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), osmConfig.tiles, {
app: app
})
let deepZoomMap = new DeepZoomMap(osmMapData, Object.assign({}, osmConfig.tiles, { app: app }), Object.assign({ cover: false }, mapOptipns))
let deepZoomMap = new DeepZoomMap(osmMapProjection, Object.assign({}, osmConfig.tiles, { app: app }), Object.assign({ cover: false }, mapOptipns))
app.setMap("deepzoom", deepZoomMap)
@@ -216,8 +216,8 @@
// Called when all textures are loaded.
let ready = (sprites) => {
// Define the mapdata for the map.
let europeData = new MapData(new Projection.Mercator(), {
// Define the map projection for the map.
let europeData = new MapProjection(new Projection.Mercator(), {
clip: {
min: { x: 32.863294, y: -18.58 },
max: { x: 57.467973, y: 44.277158 }
@@ -225,7 +225,7 @@
})
// Create the map using the texture and the mapdata.
// Create the map using the texture and the map projection.
// Optionally customize the map by supplying secific options.
let imageMap = new ImageMap(sprites.get(europe), europeData, {
cover: false
@@ -270,12 +270,12 @@
})
// Create the deepzoomdata.
let osmMapData = new DeepZoomMapData(new Projection.Mercator(), mapConfig.tiles, {
let osmMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), mapConfig.tiles, {
app
})
// Create the app.
let deepZoomMap = new DeepZoomMap(osmMapData, Object.assign({}, mapConfig.tiles, { app }), { cover: false })
let deepZoomMap = new DeepZoomMap(osmMapProjection, Object.assign({}, mapConfig.tiles, { app }), { cover: false })
// Set the map in the app.
app.setMap("deepzoom", deepZoomMap)
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 148 KiB

+6 -6
View File
@@ -98,11 +98,11 @@
resolutionDependent: false
})
// The mapdata object contains informations,
// The map projection 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(), {
let europeData = new MapProjection(new Projection.Mercator(), {
clip: {
min: { x: 32.863294, y: -18.58 },
max: { x: 57.467973, y: 44.277158 }
@@ -120,16 +120,16 @@
cover
})
let testMapData = new DeepZoomMapData(new Projection.Mercator(), testConfig.tiles, {
let testMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), testConfig.tiles, {
app
})
let testMap = new DeepZoomMap(testMapData, Object.assign({}, testConfig.tiles, { app }), { cover })
let testMap = new DeepZoomMap(testMapProjection, Object.assign({}, testConfig.tiles, { app }), { cover })
app.addMap("test", testMap)
let osmMapData = new DeepZoomMapData(new Projection.Mercator(), osmConfig.tiles, {
let osmMapProjection = new DeepZoomMapProjection(new Projection.Mercator(), osmConfig.tiles, {
app
})
let deepZoomMap = new DeepZoomMap(osmMapData, Object.assign({}, osmConfig.tiles, { app }), { cover })
let deepZoomMap = new DeepZoomMap(osmMapProjection, Object.assign({}, osmConfig.tiles, { app }), { cover })
app.addMap("osm", deepZoomMap)
// Finally apply the map to the MapApp
+68 -9
View File
@@ -1,7 +1,7 @@
import { DeepZoomMap } from './map.js'
/**
* The MapView class is responsible for a consistent map view.
* The MapViewport class is responsible for a consistent map view.
* It is aware of the current viewposition, the scale and viewport.
* It ensures, that maps can be changed, without the user noticing it.
*
@@ -11,7 +11,7 @@ export default class MapViewport {
*
* @param {object} [focus = {x:0, y:0}] - Defines the startup focuspoint of the app.
* @param {number} [zoom = 0] - Defines the startup zoom of the app. Note that this is just a request.
* The MapView will prioritize a full scale app, than displaying the demanded zoom factor
* The MapViewport will prioritize a full scale app, than displaying the demanded zoom factor
*/
constructor({ focus = null, zoom = null, viewport = { min: { x: -85, y: -180 }, max: { x: 85, y: 180 } } } = {}) {
this.viewport = viewport
@@ -20,32 +20,71 @@ export default class MapViewport {
this.referenceHeight = 256
}
/**
* The current focus point as map coordinates.
*
* @member {CoordinatePoint}
* @readonly
* @memberof MapViewport
*/
get focus() {
return this._focus
}
/**
* The current zoom distance.
* On DeepZoomMaps this is equal to the zoom level.
*
* @member {number}
* @readonly
* @memberof MapViewport
*/
get zoom() {
return this._zoom
}
/**
* Applies the current view to a map.
*
*
* @param {GeoMap} map - Map the viewport should be applied to.
* @memberof MapViewport
*/
apply(map) {
map.moveTo(this._focus, this._zoom)
}
transformed(map) {
/**
* Update the focus point and zoom according to the map.
* This is commonly called when the map is transformed.
*
* @param {GeoMap} map - Map to update.
* @memberof MapViewport
*/
update(map) {
this.updateZoom(map)
this.updateFocusPoint(map)
}
updatePosition(map) {
this.updateFocusPoint(map)
this.updateZoom(map)
}
/**
* Updates the focus point.
* This is automatically called when calling update.
*
* @param {GeoMap} map
* @memberof MapViewport
*/
updateFocusPoint(map) {
const frame = map.getFrame()
this._focus = this.coordinatesFromWindowPoint(map, frame.localCenter)
}
/**
* Updates the zoom.
* This is automatically called when calling update.
*
* @param {GeoMap} map
* @memberof MapViewport
*/
updateZoom(map) {
/**
* TODO: This relies on the fact, that all maps have the same tileSize,
@@ -57,6 +96,16 @@ export default class MapViewport {
}
}
/**
* Transforms a position on the map to a position in the window.
*
* Inverse of windowPointToMapPoint.
*
* @param {GeoMap} map - Map to calculate the position on.
* @param {Point} point - Position on the map in pixels.
* @returns {Point} - Pixel position in the window.
* @memberof MapViewport
*/
mapPointToWindowPoint(map, point) {
let windowPoint = { x: 0, y: 0 }
@@ -76,6 +125,16 @@ export default class MapViewport {
return windowPoint
}
/**
* Transforms in the window to a position on the map.
*
* Inverse of mapPointToWindowPoint(map, point) {.
*
* @param {GeoMap} map - Map to calculate the position on.
* @param {Point} point -Pixel position in the window.
* @returns {Point} - Position on the map in pixels.
* @memberof MapViewport
*/
windowPointToMapPoint(map, point) {
let pointOnMap = { x: 0, y: 0 }
if (map['image'] && map.image['parent']) {
@@ -111,7 +170,7 @@ export default class MapViewport {
y: position.y / (map.height * map.scatter.scale)
}
let coordinates = map.mapdata.toCoordinates(normalized)
let coordinates = map.mapProjection.toCoordinates(normalized)
return coordinates
}
+45 -12
View File
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<html lang="en">
<html lang="en" class="dark-mode">
<head>
<meta charset="UTF-8" />
@@ -17,6 +17,8 @@
<script src="../../../dist/iwmlib.js"></script>
<script src="../../../dist/iwmlib.pixi.js"></script>
<link rel="shortcut icon" type="image/x-icon" href="../../../assets/icons/map.png">
<style>
.inline-showcase {
@@ -33,6 +35,7 @@
<body onload="Doctest.run()">
<h1 class="title">Overlay</h1>
<!-- <a href="../../../" class="Documentation"></a> -->
<p class="description">
The overlayclass creates a convenient way to create and design
complex map overlays.
@@ -46,7 +49,7 @@
coordsLogging: true
})
var osmworld = '../assets/maps/osm/0/0/0.png'
let worlOSMData = new MapData(new Projection.Mercator())
let worlOSMData = new MapProjection(new Projection.Mercator())
function setupMap(textures) {
@@ -108,17 +111,22 @@
let exampleOverlayJSON = {
icon: '../../../assets/icons/place.png',
iconColor: '0x000000',
iconColor: "0x35aaea",
iconAnchor: { x: 0.5, y: 1 },
size: 5,
scale: 0.2,
color: '0xFF00FF',
fillAlpha: 0,
disabledColor: 0x000000,
disabledIconColor: 0xCCCCCC,
disabledScale: 0.5,
color: '0x3FA7EE',
items: [
{
name: 'Abidjan',
name: 'Custom Icon',
fontWeight: "bold",
icon: '../../../assets/icons//beenhere.png',
icon: '../../../assets/icons/beenhere.png',
iconColor: 0x00ff00,
iconAlpha: 0.5,
size: 0,
labelVerticalAlignment: "underneath",
label: 'Abidjan',
location: {
@@ -126,17 +134,19 @@
y: -4.006472
},
information:
'... der größte städtische Ballungsraum der Elfenbeinküste.'
'Here a custom icon is used. It overrides the icon setting in the global section.'
},
{
name: 'Berlin',
label: "enabled",
disabledLabel: "disabled",
location: {
x: 52.52543,
y: 13.385291
},
information:
'... ist die Bundeshauptstadt der Bundesrepublik Deutschland.'
'... ist die Bundeshauptstadt der Bundesrepublik Deutschland.',
enabled: false
},
{
name: 'Canberra',
@@ -156,7 +166,7 @@
y: 18.416962
},
information:
'... ist eine Hafenstadt an der Südwestküste Südafrikas auf einer Halbinsel, die vom beeindruckenden Tafelberg dominiert wird.'
`This item adjusts it's size according to the map.`
},
{
name: 'Moskau',
@@ -190,7 +200,8 @@
},
{
name: 'Tokio',
type: "factory",
label: "factory",
location: {
x: 35.696278,
y: 139.731366
@@ -220,6 +231,7 @@
function texturesLoaded(textures) {
/** When all textures are loaded .... */
setupMap(textures)
@@ -231,6 +243,27 @@
let cleaner = null
const vanishingTime = 1000
// Factories must return a geographics object.
Overlay.createFactory("factory", (item) => {
// This is applied to every item in the overlay that has
// the type factory'
let geographics = new GeoPoint(item.location)
geographics.drawHandler.add((graphics) => {
graphics.beginFill(item.color, item.fillAlpha)
graphics.drawRect(0, 0, 10, 10)
})
let text = new PIXI.Text(item.name, {fontSize: 5})
geographics.graphics.addChild(text)
return geographics
})
/**
*
* The actual PIXI elements are created when the overlay.create() is called.
+385 -192
View File
@@ -1,5 +1,5 @@
import { GeoLayer } from './geolayer.js'
import { GeoPoint, GeoMultiShape } from './geographics.js'
import { GeoPoint, GeoMultiShape, GeoGraphics } from './geographics.js'
import GeoJson from './geojson.js'
import Popup from '../popup.js'
@@ -11,118 +11,216 @@ import Popup from '../popup.js'
*
* It's highly configurable and adaptable to a variety of scenarios.
*
* @export
* @class Overlay
*
*
* @param {object} options
* @param {object} options.name Name of the item.
* @param {object} options.borderColor
* @param {object} options.borderThickness
* @param {object} options.enabled Determines if enabled, disabled when false.
* @param {object} options.labelScale
* @param {object} options.location
* @param {object} options.label
* @param {object} options.labelType
* @param {object} options.labelLocation
* @param {object} options.labelAlignment
* @param {object} options.labelVerticalAlignment
* @param {object} options.labelSpacing
* @param {object} options.labelTarget
* @param {object} options.geometry
* @param {object} options.information
* @param {object} options.informationPath
* @param {object} options.informationType
* @param {object} options.icon
* @param {object} options.iconAlpha
* @param {object} options.iconAnchor
* @param {object} options.iconColor
* @param {object} options.iconScale
* @param {object} options.iconOffset
* @param {object} options.items
* @param {object} options.color Color of te point.
* @param {object} options.fillAlpha FillAlpha of the point.
* @param {object} options.size Size of the point.
* @param {object} options.scale Determines the scale of the graphics.
* @param {object} options.rescale Rescales the graphics relative to the map, that they keep their original scale. Only works for the entire overlay atm. Therefore must be set in the global param eters and not inside the children.
* @param {object} options.popoverOffset
* @param {object} options.zoomVisibility
* @param {object} options.labelMultiLineAlign
* @param {object} options.labelBreakWords
* @param {object} options.labelDropShadow
* @param {object} options.labelDropShadowAlpha
* @param {object} options.labelDropShadowAngle
* @param {object} options.labelDropShadowBlur
* @param {object} options.labelDropShadowColor
* @param {object} options.labelDropShadowDistance
* @param {object} options.fontColor
* @param {object} options.fontFamily
* @param {object} options.fontSize
* @param {object} options.fontStyle
* @param {object} options.fontWeight
* @param {object} options.labelStroke
* @param {object} options.labelStrokeThickness
* @param {object} options.wordWrap
* @param {object} options.wordWrapWidth
* @param {object} options.labelFill
* @param {object} options.labelFillGradientType
* @param {object} options.labelFillGradientStops
* @param {object} options.fontVariant
* @param {object} options.labelLeading
* @param {object} options.letterSpacing
* @param {object} options.labelLineHeight
* @param {object} options.labelLineJoin
* @param {object} options.labelMiterLimit
* @param {object} options.labelPadding
* @param {object} options.labelTrim
* @param {object} options.textBaseline
* @param {object} options.labelWhiteSpace
*/
export default class Overlay {
/**
*Creates an instance of Overlay.
* @param {*} [{
* name = "unnamed", {string}
* connector = false,
* connectorType = "line",
* location = null,
* label = false,
* labelType = null,
* labelLocation = null,
* labelAlignment = "center",
* labelVerticalAlign = "underneath",
* labelTarget = null,
* shape = "geometry", {string} - Defines the shape to be used: geometry, circle, rectangle
* geometry = null,
* geometryType = "point",
* geometryFallback = "circle",
* informationPopup = "infocard",
*
* }={}]
* @memberof Overlay
*/
constructor(opts = {}) {
constructor(options = {}) {
let defaultTextStyle = new PIXI.TextStyle()
Object.assign(
this,
{
type: 'auto',
name: 'unnamed',
let defaultOptions = {
type: 'auto',
name: 'unnamed',
borderColor: 0x000000,
borderThickness: 5,
borderColor: 0x000000,
borderThickness: 5,
enabled: true,
labelScale: 1,
location: null,
label: false,
labelType: null,
labelLocation: null,
labelAlignment: 'center',
labelVerticalAlignment: 'underneath',
labelSpacing: 10,
labelTarget: null,
geometry: null,
information: '',
informationPath: null,
informationType: 'popup',
icon: null,
iconAlpha: 1,
iconAnchor: { x: 0.5, y: 0.5 },
iconColor: 0xffffff,
iconScale: 1,
iconOffset: { x: 0, y: 0 },
items: [],
color: 0xff00ff,
fillAlpha: 1,
size: 50,
scale: 1,
rescale: false,
popoverOffset: { x: 0, y: 0 },
zoomVisibility: { min: 0, max: Number.MAX_VALUE },
disabledAlpha: 1,
disabledColor: 0xaa1111,
disabledBorderColor: 0x000000,
enabled: true,
labelScale: 1,
location: null,
label: false,
labelType: null,
labelLocation: null,
labelAlignment: 'center',
labelVerticalAlign: 'underneath',
labelSpacing: 10,
labelTarget: null,
geometry: null,
information: '',
informationPath: null,
informationType: 'popup',
icon: null,
iconAlpha: 1,
iconAnchor: { x: 0.5, y: 0.5 },
iconColor: 0xffffff,
iconScale: 1,
iconOffset: { x: 0, y: 0 },
items: [],
color: 0xff00ff,
fillAlpha: 1,
size: 50,
scale: 1,
rescale: false,
popoverOffset: { x: 0, y: 0 },
zoomVisibility: { min: 0, max: Number.MAX_VALUE },
/**
* The following Attributes are taken from the TextStyle class
* of PIXI. All can be overwritten in the overlay's JSON file.
*/
labelMultiLineAlign: defaultTextStyle.align,
labelBreakWords: defaultTextStyle.breakWords,
labelDropShadow: defaultTextStyle.dropShadow,
labelDropShadowAlpha: defaultTextStyle.dropShadowAlpha,
labelDropShadowAngle: defaultTextStyle.dropShadowAngle,
labelDropShadowBlur: defaultTextStyle.dropShadowBlur,
labelDropShadowColor: defaultTextStyle.dropShadowColor,
labelDropShadowDistance: defaultTextStyle.dropShadowDistance,
fontColor: 0x000000,
fontFamily: defaultTextStyle.fontFamily,
fontSize: defaultTextStyle.fontSize,
fontStyle: defaultTextStyle.fontStyle,
fontWeight: defaultTextStyle.fontWeight,
labelStroke: defaultTextStyle.stroke,
labelStrokeThickness: defaultTextStyle.strokeThickness,
wordWrap: defaultTextStyle.wordWrap,
wordWrapWidth: defaultTextStyle.wordWrapWidth,
labelFill: defaultTextStyle.fill,
labelFillGradientType: defaultTextStyle.fillGradientType,
labelFillGradientStops: defaultTextStyle.fillGradientStops,
fontVariant: defaultTextStyle.fontVariant,
labelLeading: defaultTextStyle.leading,
letterSpacing: defaultTextStyle.letterSpacing,
labelLineHeight: defaultTextStyle.lineHeight,
labelLineJoin: defaultTextStyle.lineJoin,
labelMiterLimit: defaultTextStyle.miterLimit,
labelPadding: defaultTextStyle.padding,
labelTrim: defaultTextStyle.trim,
textBaseline: defaultTextStyle.textBaseline,
labelWhiteSpace: defaultTextStyle.whiteSpace
}
/**
* The following Attributes are taken from the TextStyle class
* of PIXI. All can be overwritten in the overlay's JSON file.
*/
labelMultiLineAlign: defaultTextStyle.align,
labelBreakWords: defaultTextStyle.breakWords,
labelDropShadow: defaultTextStyle.dropShadow,
labelDropShadowAlpha: defaultTextStyle.dropShadowAlpha,
labelDropShadowAngle: defaultTextStyle.dropShadowAngle,
labelDropShadowBlur: defaultTextStyle.dropShadowBlur,
labelDropShadowColor: defaultTextStyle.dropShadowColor,
labelDropShadowDistance: defaultTextStyle.dropShadowDistance,
fontColor: 0x000000,
fontFamily: defaultTextStyle.fontFamily,
fontSize: defaultTextStyle.fontSize,
fontStyle: defaultTextStyle.fontStyle,
fontWeight: defaultTextStyle.fontWeight,
labelStroke: defaultTextStyle.stroke,
labelStrokeThickness: defaultTextStyle.strokeThickness,
wordWrap: defaultTextStyle.wordWrap,
wordWrapWidth: defaultTextStyle.wordWrapWidth,
labelFill: defaultTextStyle.fill,
labelFillGradientType: defaultTextStyle.fillGradientType,
labelFillGradientStops: defaultTextStyle.fillGradientStops,
fontVariant: defaultTextStyle.fontVariant,
labelLeading: defaultTextStyle.leading,
letterSpacing: defaultTextStyle.letterSpacing,
labelLineHeight: defaultTextStyle.lineHeight,
labelLineJoin: defaultTextStyle.lineJoin,
labelMiterLimit: defaultTextStyle.miterLimit,
labelPadding: defaultTextStyle.padding,
labelTrim: defaultTextStyle.trim,
textBaseline: defaultTextStyle.textBaseline,
labelWhiteSpace: defaultTextStyle.whiteSpace
},
opts
)
defaultOptions = this.addDisabled(defaultOptions)
console.log('DISABLED VERTICALA.', defaultOptions.disabledLabelVerticalAlignment)
options = this.addDisabled(options)
options = Object.assign({}, defaultOptions, options)
Object.assign(this, options)
}
/**
* Some parameters shall not have a disabled option.
* These are defined here.
*
* @readonly
* @memberof Overlay
*/
get excludedDisableParameters() {
return ['items', 'rescale', 'name', 'type', 'enabled']
}
/**
* Copies the normal properties to the disabled version of the property,
* while conserving already set disabled properties.
*
* @param {*} options
* @returns {object} - Returns the modified options object.
* @memberof Overlay
*/
addDisabled(options) {
for (let [key, value] of Object.entries(options)) {
if (this.excludedDisableParameters.indexOf(key) == -1) {
let disabledProperty = this.toDisabledPropertyString(key)
if (options[disabledProperty] == undefined) {
options[disabledProperty] = value
}
}
}
return options
}
/**
* Textures need to be loaded by the app. Texture parameters return all parameters that need to be evaluated for textures.
* @readonly
* @member {array}
* @memberof Overlay
*/
get textureParameters() {
return ['icon']
const textureParameters = ['icon']
textureParameters.forEach(textureParameter => {
textureParameters.push(this.toDisabledPropertyString(textureParameter))
})
return textureParameters
}
/**
* The required textures are saved inside the single items.
* Their texture string gets replaced by a texture object
* containing the path and the texture.
*
* @param {*} parameter
* @param {*} key
* @param {*} path
* @memberof Overlay
*/
setTexture(parameter, key, path) {
let obj = (this[parameter] = {})
obj._key = key
@@ -150,6 +248,14 @@ export default class Overlay {
})
}
/**
* Recursively calls a function on child items.
*
* @param {*} parameter
* @param {*} func
* @param {*} [obj=null]
* @memberof Overlay
*/
apply(parameter, func, obj = null) {
if (obj == null) return
if (Array.isArray(obj)) {
@@ -187,13 +293,21 @@ export default class Overlay {
adjustItems = null,
cleanupItems = null
} = {}) {
const name = this.name[0].toUpperCase() + this.name.slice(1).toLowerCase() + ' Overlay'
console.log(this)
const name = this.name
? this.name[0].toUpperCase() + this.name.slice(1).toLowerCase() + ' Overlay'
: 'Unnamed Overlay'
let geoLayer = new GeoLayer(new PIXI.Container(), { name })
geoLayer.visibility = this.zoomVisibility
if (this.rescale) geoLayer.rescale = this.rescale
this.items.forEach(item => {
if (!excludeItems(item)) {
//Copies all values to a disabled state.
item = this.addDisabled(item)
if (adjustItems) {
adjustItems(item)
}
@@ -208,11 +322,60 @@ export default class Overlay {
})
return geoLayer
}
/**
* Used to pick a property or disabled property using an enabled parameter.
*
* @param {*} item
* @param {*} property
* @param {boolean} [enabled=true]
* @returns {any} - Returns the picked item property.
* @memberof Overlay
*/
pickItemProperty(item, property, enabled = true) {
return enabled ? this.getItemProperty(item, property) : this.getDisabledItemProperty(item, property)
}
toDisabledPropertyString(propertyName) {
const prefix = 'disabled'
return prefix + propertyName[0].toUpperCase() + propertyName.slice(1)
}
/**
* Get's the disabled version of the property as long as it is not excluded.
*
* @param {*} item
* @param {*} property
* @returns {any} - Returns the picked item property.
* @memberof Overlay
*/
getDisabledItemProperty(item, property) {
if (this.excludedDisableParameters.indexOf(property) === -1) {
property = this.toDisabledPropertyString(property)
}
const propertyValue = this.getItemProperty(item, property)
return propertyValue
}
/**
* Tries to get the infromation from an overlay item.
* If the item does not contain the property, the overlay definition will be used.
* When the overlay definition is not set, the default value is used.
*
* @param {OverlayItem} item - Item to get the informations from.
* @param {string} property - Name of the property.
* @returns {any} - Returns the picked item property.
* @memberof Overlay
*/
getItemProperty(item, property) {
let propertyValue = null
const propertyExistsOnItem = item[property] !== undefined
const propertyExistsOnOverlay = this[property] !== undefined
if (property == 'disabledLabelVerticalAlignment') {
console.log(this)
console.log(this['disabledLabelVerticalAlignment'], propertyExistsOnItem, propertyExistsOnOverlay)
}
if (propertyExistsOnItem) propertyValue = item[property]
else if (propertyExistsOnOverlay) propertyValue = this[property]
else {
@@ -229,6 +392,15 @@ export default class Overlay {
else return false
}
/**
* An overlay can have a reference to another file.
* That single overlays are more easy to maintain.
*
* @param {*} item
* @param {*} property
* @returns {any} - Returns the reference as object or the property.
* @memberof Overlay
*/
_resolveReference(item, property) {
if (this._isReference(property)) {
let referencedProperty = property['@property']
@@ -281,18 +453,30 @@ export default class Overlay {
})
}
/**
* Creates a geographic for each item.
* By default it uses the regular routine but you may
* specify a factory for the defined type.
*
* @param {*} item
* @param {*} [informationCallback=null]
* @returns {GeoGraphics} - Returns the created geographics.
* @memberof Overlay
*/
createItem(item, informationCallback = null) {
let geographics
let type = this.getItemProperty(item, 'type')
/**
* Use a factory to draw the items, if a type is specified.
*/
if (this.type != 'auto') {
let geographicsFactory = Overlay.requestFactory(this.type)
if (type != 'auto') {
let geographicsFactory = Overlay.requestFactory(type)
if (!geographicsFactory)
console.error(
'Invalid Overlay Mode: ' +
this.type +
type +
'. Fallback to auto mode. Overlaymodes must be registeres beforehand. Valid modes are: ' +
Overlay.listFactories().join(',') +
'.'
@@ -318,84 +502,100 @@ export default class Overlay {
this._drawRoutine(geographics, item, informationCallback)
}
this._drawLabel(item, geographics)
let label = this._createLabel(item, geographics)
if (label != null && geographics != null) {
console.log(geographics)
geographics.graphics.addChild(label)
}
}
return geographics
}
_drawLabel(item, geographics) {
let label = this.getItemProperty(item, 'label')
if (label) {
let textStyle = this._gatherFontStyle(item)
let text = new PIXI.Text(label, textStyle)
/**
*
* Draws a label that is shown next to the item on the map.
*
* @param {*} item
* @returns {PIXI.Text} - Returns the created label or null.
* @memberof Overlay
*/
_createLabel(item) {
let enabled = this.getItemProperty(item, 'enabled')
let labelText = this.pickItemProperty(item, 'label', enabled)
let label = null
let labelScale = this.getItemProperty(item, 'labelScale')
text.scale.set(labelScale, labelScale)
if (labelText) {
let textStyle = this._gatherFontStyle(item)
label = new PIXI.Text(labelText, textStyle)
let labelScale = this.pickItemProperty(item, 'labelScale', enabled)
label.scale.set(labelScale, labelScale)
let position = new PIXI.Point()
let align = this.getItemProperty(item, 'labelAlignment')
let align = this.pickItemProperty(item, 'labelAlignment', enabled)
if (align == 'left');
else if (align == 'center') position.set(text.position.x - text.width / 2, text.position.y)
else if (align == 'right') position.set(text.position.x - text.width, text.position.y)
else if (align == 'center') position.set(label.position.x - label.width / 2, label.position.y)
else if (align == 'right') position.set(label.position.x - label.width, label.position.y)
else this._logPropertyNotImplemented('labelAlignment', align)
let verticalAlign = this.getItemProperty(item, 'labelVerticalAlignment')
let verticalAlign = this.pickItemProperty(item, 'labelVerticalAlignment', enabled)
if (verticalAlign == 'underneath') {
let size = this.getItemProperty(item, 'size')
let scale = this.getItemProperty(item, 'scale')
let size = this.pickItemProperty(item, 'size', enabled)
let scale = this.pickItemProperty(item, 'scale', enabled)
let labelSpacing = this.getItemProperty(item, 'labelSpacing')
let labelSpacing = this.pickItemProperty(item, 'labelSpacing', enabled)
position.y += size * scale + labelSpacing
} else if (verticalAlign == 'above') {
let size = this.getItemProperty(item, 'size')
let scale = this.getItemProperty(item, 'scale')
let size = this.pickItemProperty(item, 'size', enabled)
let scale = this.pickItemProperty(item, 'scale', enabled)
let labelSpacing = this.getItemProperty(item, 'labelSpacing')
let labelSpacing = this.pickItemProperty(item, 'labelSpacing', enabled)
position.y -= size * scale + text.height + labelSpacing
position.y -= size * scale + label.height + labelSpacing
} else this._logPropertyNotImplemented('labelVerticalAlignment', verticalAlign)
text.position.set(position.x, position.y)
geographics.graphics.addChild(text)
label.position.set(position.x, position.y)
}
return label
}
_gatherFontStyle(item) {
const enabled = this.getItemProperty(item, 'enabled')
return {
align: this.getItemProperty(item, 'labelMultiLineAlign'),
breakWords: this.getItemProperty(item, 'labelBreakWords'),
dropShadow: this.getItemProperty(item, 'labelDropShadow'),
dropShadowAlpha: this.getItemProperty(item, 'labelDropShadowAlpha'),
dropShadowAngle: this.getItemProperty(item, 'labelDropShadowAngle'),
dropShadowBlur: this.getItemProperty(item, 'labelDropShadowBlur'),
dropShadowColor: this.getItemProperty(item, 'labelDropShadowColor'),
dropShadowDistance: this.getItemProperty(item, 'labelDropShadowDistance'),
fontFamily: this.getItemProperty(item, 'fontFamily'),
fontSize: this.getItemProperty(item, 'fontSize'),
fontStyle: this.getItemProperty(item, 'fontStyle'),
fontWeight: this.getItemProperty(item, 'fontWeight'),
stroke: this.getItemProperty(item, 'labelStroke'),
strokeThickness: this.getItemProperty(item, 'labelStrokeThickness'),
wordWrap: this.getItemProperty(item, 'wordWrap'),
wordWrapWidth: this.getItemProperty(item, 'wordWrapWidth'),
fill: this.getItemProperty(item, 'labelFill'),
fillGradientType: this.getItemProperty(item, 'labelFillGradientType'),
fillGradientStops: this.getItemProperty(item, 'labelFillGradientStops'),
fontVariant: this.getItemProperty(item, 'fontVariant'),
leading: this.getItemProperty(item, 'labelLeading'),
letterSpacing: this.getItemProperty(item, 'letterSpacing'),
lineHeight: this.getItemProperty(item, 'labelLineHeight'),
lineJoin: this.getItemProperty(item, 'labelLineJoin'),
miterLimit: this.getItemProperty(item, 'labelMiterLimit'),
padding: this.getItemProperty(item, 'labelPadding'),
trim: this.getItemProperty(item, 'labelTrim'),
textBaseline: this.getItemProperty(item, 'textBaseline'),
whiteSpace: this.getItemProperty(item, 'labelWhiteSpace')
align: this.pickItemProperty(item, 'labelMultiLineAlign', enabled),
breakWords: this.pickItemProperty(item, 'labelBreakWords', enabled),
dropShadow: this.pickItemProperty(item, 'labelDropShadow', enabled),
dropShadowAlpha: this.pickItemProperty(item, 'labelDropShadowAlpha', enabled),
dropShadowAngle: this.pickItemProperty(item, 'labelDropShadowAngle', enabled),
dropShadowBlur: this.pickItemProperty(item, 'labelDropShadowBlur', enabled),
dropShadowColor: this.pickItemProperty(item, 'labelDropShadowColor', enabled),
dropShadowDistance: this.pickItemProperty(item, 'labelDropShadowDistance', enabled),
fontFamily: this.pickItemProperty(item, 'fontFamily', enabled),
fontSize: this.pickItemProperty(item, 'fontSize', enabled),
fontStyle: this.pickItemProperty(item, 'fontStyle', enabled),
fontWeight: this.pickItemProperty(item, 'fontWeight', enabled),
stroke: this.pickItemProperty(item, 'labelStroke', enabled),
strokeThickness: this.pickItemProperty(item, 'labelStrokeThickness', enabled),
wordWrap: this.pickItemProperty(item, 'wordWrap', enabled),
wordWrapWidth: this.pickItemProperty(item, 'wordWrapWidth', enabled),
fill: this.pickItemProperty(item, 'labelFill', enabled),
fillGradientType: this.pickItemProperty(item, 'labelFillGradientType', enabled),
fillGradientStops: this.pickItemProperty(item, 'labelFillGradientStops', enabled),
fontVariant: this.pickItemProperty(item, 'fontVariant', enabled),
leading: this.pickItemProperty(item, 'labelLeading', enabled),
letterSpacing: this.pickItemProperty(item, 'letterSpacing', enabled),
lineHeight: this.pickItemProperty(item, 'labelLineHeight', enabled),
lineJoin: this.pickItemProperty(item, 'labelLineJoin', enabled),
miterLimit: this.pickItemProperty(item, 'labelMiterLimit', enabled),
padding: this.pickItemProperty(item, 'labelPadding', enabled),
trim: this.pickItemProperty(item, 'labelTrim', enabled),
textBaseline: this.pickItemProperty(item, 'textBaseline', enabled),
whiteSpace: this.pickItemProperty(item, 'labelWhiteSpace', enabled)
}
}
@@ -440,25 +640,27 @@ export default class Overlay {
}
_createIcon(geographics, item) {
let icon = this.getItemProperty(item, 'icon')
let enabled = this.getItemProperty(item, 'enabled')
let icon = this.pickItemProperty(item, 'icon', enabled)
if (icon) {
if (icon.texture) {
let sprite = new PIXI.Sprite(icon.texture)
const iconAnchor = this.getItemProperty(item, 'iconAnchor')
const iconAnchor = this.pickItemProperty(item, 'iconAnchor', enabled)
sprite.anchor.set(iconAnchor.x, iconAnchor.y)
const iconScale = this.getItemProperty(item, 'iconScale')
const iconScale = this.pickItemProperty(item, 'iconScale', enabled)
if (iconScale) sprite.scale.set(iconScale, iconScale)
const iconOffset = this.getItemProperty(item, 'iconOffset')
const iconOffset = this.pickItemProperty(item, 'iconOffset', enabled)
if (iconOffset && iconOffset.x != null && iconOffset.y != null)
sprite.position.set(iconOffset.x, iconOffset.y)
const iconColor = this.getItemProperty(item, 'iconColor')
const iconColor = this.pickItemProperty(item, 'iconColor', enabled)
if (iconColor) sprite.tint = iconColor
const iconAlpha = this.getItemProperty(item, 'iconAlpha')
const iconAlpha = this.pickItemProperty(item, 'iconAlpha', enabled)
if (iconAlpha) sprite.alpha = iconAlpha
geographics.graphics.addChild(sprite)
@@ -468,28 +670,6 @@ export default class Overlay {
_createInformation(geographics, item, callback = null) {
if (item.information) {
/**
* SO: The overlay class is quite convenient.
* But managing the information creation solely inside this
* class restricts us massively.
*
* Maybe a restructuring would be good, that we can handle
* the creation of information with a callback and can adapt
* to any occuring situation.
*
* e.g.
*
* overlay.informationHandler((geo, type, item)=>{
* switch(type){
* case "popup":
* createPopup(item.information)
* break;
*
* ....
* }
* })
*/
geographics.graphics.interactive = true
const informationType = this.getItemProperty(item, 'informationType')
@@ -538,17 +718,21 @@ export default class Overlay {
_fill(geographics, item) {
const enabled = this.getItemProperty(item, 'enabled')
const color = enabled ? this.getItemProperty(item, 'color') : this.getItemProperty(item, 'disabledColor')
let alpha = enabled ? this.getItemProperty(item, 'fillAlpha') : this.getItemProperty(item, 'disabledAlpha')
const color = this.pickItemProperty(item, 'color', enabled)
const alpha = this.pickItemProperty(item, 'fillAlpha', enabled)
geographics.graphics.beginFill(color, alpha)
}
_drawPoint(item, informationCallback = null) {
const overlay = this
const enabled = this.getItemProperty(item, 'enabled')
let that = this
let geographic = new GeoPoint(item.location, {
onDraw: function() {
overlay._fill.call(overlay, this, item)
const size = overlay.getItemProperty(item, 'size') ? overlay.getItemProperty(item, 'size') : 0
const size = that.pickItemProperty(item, 'size', enabled)
this.graphics.drawCircle(0, 0, size)
overlay._drawRoutine.call(overlay, this, item, informationCallback)
}
@@ -563,10 +747,19 @@ export default class Overlay {
Overlay.Modes = { auto: null }
Overlay.createFactory = function(name, geographicsFactory) {
if (Overlay.Modes.hasOwnProperty(name))
console.warn(`The mode ${name} was already implemented and was overwritten!`)
Overlay.Modes[name] = geographicsFactory
/**
* Creates a factory for a specific type.
*
* @param {string} type - Name of the factory. When overlay items match the type, the factory is applied.
* @param {function} geographicsFactory - A function that is called with every item. Can be used to draw the geographics individually.
*
* @static
* @memberof {Overlay}
*/
Overlay.createFactory = function(type, geographicsFactory) {
if (Overlay.Modes.hasOwnProperty(type))
console.warn(`The mode ${type} was already implemented and was overwritten!`)
Overlay.Modes[type] = geographicsFactory
}
Overlay.requestFactory = function(name) {
+1 -1
View File
@@ -77,7 +77,7 @@
<body onload="Doctest.run()">
<h1>Projections</h1>
<p>
Projections are used on the mapdata to translate coordinates to pixelpositions. There are various
Projections are used on the map projection to translate coordinates to pixelpositions. There are various
projections that can be used. All implemented ones are showcased here.
</p>
+25
View File
@@ -258,6 +258,31 @@ export class Vector {
}
}
export 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
}
}
}
/**
* The EventHandler class is used to take care of a event based design
* pattern. Callbacks can subscribe to an event and these unknown sources
+24 -3
View File
@@ -18,7 +18,7 @@ export class InteractivePopup extends AbstractPopup {
* @constructor
* @param {object} [opts] - An options object to specify to style and behaviour of the popup.
* @param {boolean} [opts.closeOnPopup=false] - Should the popup be closed when the user clicks on the popup?
* @param {boolean} [opts.closeButton=true] - Should a close button be displayed on the upper right corner?
* @param {boolean|string} [opts.closeButton=true] - Should a close button be displayed on the upper right corner? Alternatively, a URL to the image can be specified.
* @param {object} [opts.button] - A Button object to be display on the lower right corner.
* @param {object} [opts.buttonGroup] - A ButtonGroup object to be displayed on the lower right corner.
*/
@@ -73,7 +73,16 @@ export class InteractivePopup extends AbstractPopup {
// closeButton
//-----------------
if (this.opts.closeButton) {
let closeButton = PIXI.Sprite.from('../../assets/icons/close.png')
let url = null
if (typeof this.opts.closeButton === 'string' || this.opts.closeButton instanceof String) {
url = InteractivePopup.iconIsUrl(this.opts.closeButton)
? this.opts.closeButton
: `../../assets/icons/${this.opts.closeButton}.png`
} else {
url = '../../assets/icons/close.png'
}
let closeButton = PIXI.Sprite.from(url)
closeButton.width = this.headerStyle.fontSize
closeButton.height = closeButton.width
closeButton.tint = this.theme.color2
@@ -172,6 +181,18 @@ export class InteractivePopup extends AbstractPopup {
return size
}
/**
* Tests if an icon string is an url.
*
* @private
* @static
* @param {string} url - The url to test.
* @return {boolean} true if the url is an url to an image.
*/
static iconIsUrl(url) {
return /\.(png|svg|gif|jpg|jpeg|tif|tiff)$/i.test(url)
}
}
/**
@@ -197,7 +218,7 @@ export default class Popup extends InteractivePopup {
*
* @constructor
* @param {object} [opts] - An options object to specify to style and behaviour of the popup.
* @param {boolean} [opts.closeButton=false] - Should a close button be displayed on the upper right corner?
* @param {boolean|string} [opts.closeButton=false] - Should a close button be displayed on the upper right corner? Alternatively, a URL to the image can be specified.
* @param {number} [opts.minWidth=0] - The minimum width of the popup.
* @param {number} [opts.minHeight=0] - The minimum height of the popup.
*/
-1
View File
@@ -315,7 +315,6 @@ export class DisplayObjectScatter extends AbstractScatter {
if (y != null) this.y = y
}
getWorldScatter() {
return this
}
+15 -11
View File
@@ -80,7 +80,9 @@ export default class Stylus extends PIXI.Graphics {
backgroundFill = 0xffffff,
colorAlpha = 1,
captureEvents = true,
acceptMouseEvents = true
acceptMouseEvents = true,
lineWidth = 16,
minStrokeLength = 4
} = {}) {
super()
this.activePointers = 0
@@ -99,7 +101,8 @@ export default class Stylus extends PIXI.Graphics {
this.undoCommandStack = []
this.strokes = []
this.stroke = []
this.minStrokeLength = 4
this.lineWidth = lineWidth
this.minStrokeLength = minStrokeLength
if (captureEvents) this.registerEventHandler(acceptMouseEvents)
this.drawBackground()
}
@@ -273,7 +276,8 @@ export default class Stylus extends PIXI.Graphics {
pressure: event.pressure || null,
tiltX: this.tiltX,
tiltY: this.tiltY,
color: this.color
color: this.color,
lineWidth: this.tiltToLineWidth(this.tiltY)
}
return desc
}
@@ -297,7 +301,7 @@ export default class Stylus extends PIXI.Graphics {
}
tiltToLineWidth(value) {
return 16 //Math.round(Math.abs(value / 10) + 1)
return this.lineWidth * Math.round(Math.abs(value / 10) + 1)
}
drawStroke(stroke) {
@@ -305,11 +309,11 @@ export default class Stylus extends PIXI.Graphics {
let start = stroke[0]
this.beginFill(0, 0)
this.moveTo(start.x, start.y)
let lineWidth = this.tiltToLineWidth(start.tiltY)
let lineWidth = start.lineWidth
this.lineStyle(lineWidth, start.color, this.colorAlpha)
for (let i = 1; i < stroke.length; i++) {
let info = stroke[i]
let lw = this.tiltToLineWidth(info.tiltY)
let lw = info.lineWidth
if (lw != lineWidth) {
lineWidth = lw
this.lineStyle(lineWidth, info.color, this.colorAlpha)
@@ -328,7 +332,7 @@ export default class Stylus extends PIXI.Graphics {
drawStrokes() {
this.drawBackground()
this.lineStyle(1.0, 0xff0000, 1)
this.lineStyle(this.lineWidth, 0xff0000, 1)
for (let stroke of this.iterStrokes()) {
this.drawStroke(stroke)
}
@@ -356,17 +360,17 @@ export default class Stylus extends PIXI.Graphics {
}
normalizeInfo(info) {
let { x, y, pressure, tiltX, tiltY, color } = info
let { x, y, pressure, tiltX, tiltY, color, lineWidth } = info
x /= this.wantedWidth
y /= this.wantedHeight
return { x, y, pressure, tiltX, tiltY, color }
return { x, y, pressure, tiltX, tiltY, color, lineWidth }
}
denormalizeInfo(info) {
let { x, y, pressure, tiltX, tiltY, color } = info
let { x, y, pressure, tiltX, tiltY, color, lineWidth } = info
x = x * this.wantedWidth
y = y * this.wantedHeight
return { x, y, pressure, tiltX, tiltY, color }
return { x, y, pressure, tiltX, tiltY, color, lineWidth }
}
// Convert strokes into an object that can be stored in an Indexed DB.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 61 KiB

After

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 38 KiB

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 32 KiB

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

After

Width:  |  Height:  |  Size: 45 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 33 KiB

+2 -2
View File
@@ -34,8 +34,8 @@ oncontextmenu="PopupMenu.open(cmds,
oncontextmenu="PopupMenu.open(cmds, {x: event.offsetX, y: event.offsetY}, { parent: example, keepWithin: example}); return false;"></div>
<script class="doctest">
let cmds = {
"Start": () => alert("Start"),
"Stop": () => alert("Stop")
"Start": (event, key) => {console.log("start", event, key); alert(key) },
"Stop": (event, key) => alert(key)
}
let app = new App()
let popup = new PopupMenu({ parent: example, fontSize: "2em", autoClose: false })
+12 -6
View File
@@ -83,10 +83,10 @@ export default class PopupMenu extends Popup {
Elements.addClass(item, 'popupMenuItem')
this.items[key] = item
item.onclick = event => {
this.perform(key)
this.perform(event, key)
}
item.ontap = event => {
this.perform(key)
this.perform(event, key)
}
item.onmouseover = event => {
this.over(event, key)
@@ -105,16 +105,22 @@ export default class PopupMenu extends Popup {
}
/** Execute a menu command.
* @param {object} event - The trigger event.
* @param {string} key - The selected key.
*/
perform(key) {
perform(event, key) {
let func = this.commands[key]
if (this.autoClose) {
this.close()
}
setTimeout(() => {
func.call()
}, 20)
setTimeout(
(event, key) => {
func(event, key)
},
20,
event,
key
)
}
/** Update the menu item denoted by key.
+14 -8
View File
@@ -117,6 +117,9 @@ class Throwable {
let t = performance.now()
let dt = t - this.lastframe
this.lastframe = t
// When number is not set or is zero. Use one instead.
let number = delta.number ? delta.number : 1
if (dt > 0) {
// Avoid division by zero errors later on
// and consider the number of involved pointers sind addVelocity will be called by the
@@ -124,13 +127,14 @@ class Throwable {
let velocity = {
t: t,
dt: dt,
dx: delta.x / delta.number,
dy: delta.y / delta.number
dx: delta.x / number,
dy: delta.y / number
}
this.velocities.push(velocity)
while (this.velocities.length > buffer) {
this.velocities.shift()
}
}
}
@@ -378,7 +382,7 @@ export class AbstractScatter extends Throwable {
let delta = interaction.delta()
if (delta != null) {
this.addVelocity(delta)
this.addVelocity(delta) // uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
let rotate = delta.rotate
let zoom = delta.zoom
if (this.maxRotation != null) {
@@ -578,6 +582,7 @@ export class AbstractScatter extends Throwable {
}
_move(delta) {
// this.addVelocity(delta) uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
this.x += this.movableX ? delta.x : 0
this.y += this.movableX ? delta.y : 0
}
@@ -615,14 +620,14 @@ export class AbstractScatter extends Throwable {
let newOrigin = Points.arc(anchor, beta + rotate, distance * thresholdedZoom)
let extra = Points.subtract(newOrigin, origin)
let offset = Points.subtract(anchor, origin)
this._move(offset)
const anchorOffset = Points.subtract(anchor, origin)
// this._move(offset)
this.scale = newScale
this.rotation += rotate
offset = Points.negate(offset)
let offset = Points.negate(anchorOffset)
offset = Points.add(offset, extra)
offset = Points.add(offset, translate)
this._move(offset)
this._move(Points.add(anchorOffset, offset))
delta.x += extra.x
delta.y += extra.y
@@ -1392,7 +1397,8 @@ export class DOMScatter extends AbstractScatter {
}
this._x = x
this._y = y
TweenLite.set(this.element, { x: x, y: y })
// this.addVelocity({ x: delta.x, y: delta.y }) uo: reverted commit fa0256d782dd498c6d3e51321260ca375ca9f855
TweenLite.set(this.element, { x, y })
}
resizeAfterTransform(zoom) {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 53 KiB

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 55 KiB

+12 -3
View File
@@ -396,9 +396,18 @@ export class Points {
// Distance == 0.0 indicates an inside relation.
static distanceToRect(p, r) {
let cx = Math.max(Math.min(p.x, r.x + r.width), r.x)
let cy = Math.max(Math.min(p.y, r.y + r.height), r.y)
return Math.sqrt((p.x - cx) * (p.x - cx) + (p.y - cy) * (p.y - cy))
let dx = 0
let dy = 0
if (p.x < r.x) dx = p.x - r.x
else if (p.x > r.x + r.width) dx = p.x - (r.x + r.width)
if (p.y < r.y) dy = p.y - r.y
else if (p.y > r.y + r.height) dy = p.y - (r.y + r.height)
return Math.sqrt(dx * dx + dy * dy)
/* let cx = Math.max(Math.min(p.x, r.x + r.width), r.x)
let cy = Math.max(Math.min(p.y, r.y + r.height), r.y)
let result = Math.sqrt((p.x - cx) * (p.x - cx) + (p.y - cy) * (p.y - cy))
console.log("distanceToRect", p, r, result)
return result */
}
static fromPageToNode(element, p) {
+1291 -1143
View File
File diff suppressed because it is too large Load Diff
+9 -9
View File
@@ -1,6 +1,6 @@
{
"name": "iwmlib",
"version": "1.1.0",
"version": "1.1.1",
"description": "An Open Source library for multi-touch, WebGL powered applications.",
"main": "index.js",
"directories": {
@@ -27,10 +27,10 @@
"author": "",
"license": "LGPL-3.0-or-later",
"devDependencies": {
"@pixi/jsdoc-template": "^2.4.3",
"eslint": "^6.6.0",
"eslint-config-prettier": "^6.5.0",
"eslint-plugin-prettier": "^3.1.1",
"@pixi/jsdoc-template": "^2.5.1",
"eslint": "^6.8.0",
"eslint-config-prettier": "^6.9.0",
"eslint-plugin-prettier": "^3.1.2",
"fs-extra": "^8.0.1",
"gulp": "^4.0.2",
"gulp-concat": "^2.6.1",
@@ -48,13 +48,13 @@
"gsap": "^2.1.3",
"hammerjs": "^2.0.8",
"optimal-select": "^4.0.1",
"pixi-compressed-textures": "^2.0.3",
"pixi-compressed-textures": "^2.0.5",
"pixi-ease": "^2.4.2",
"pixi-filters": "^3.0.3",
"pixi-particles": "^4.2.0",
"pixi-projection": "^0.3.5",
"pixi-viewport": "^4.3.3",
"pixi.js": "^5.2.0",
"pixi-projection": "^0.3.7",
"pixi-viewport": "^4.8.0",
"pixi.js": "^5.2.1",
"propagating-hammerjs": "^1.4.7"
}
}