Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
266a770c8e | ||
|
|
a5f94e0a5f | ||
|
|
fff7a4f685 | ||
|
|
d0ae8893cc | ||
|
|
1b4eee6498 | ||
|
|
ac779af339 | ||
|
|
1316a727f3 | ||
|
|
be950ec8c1 | ||
|
|
ca5a39d661 | ||
|
|
fa0256d782 | ||
|
|
114c217ffa | ||
|
|
28cfdc180c | ||
|
|
116058ba77 | ||
|
|
4211548b8b | ||
|
|
12b17fd7af | ||
|
|
47b178fbc1 | ||
|
|
6ab63eb32c | ||
|
|
6445ab3f57 | ||
|
|
129672eb0f | ||
|
|
70031d3bfa | ||
|
|
9db5723746 | ||
|
|
d4ab5c7e9c | ||
|
|
b068fd176f | ||
|
|
f70968fe01 | ||
|
|
1c70433b86 | ||
|
|
54b685ab42 | ||
|
|
e6f6f6b185 | ||
|
|
7bb3b696f7 | ||
|
|
6cdf5310ba | ||
|
|
1967b791e0 | ||
|
|
ebb4b9154e | ||
|
|
b32132c71f | ||
|
|
f398fe4824 | ||
|
|
d5b87f23cf | ||
|
|
7f2fc93fa3 | ||
|
|
32e913c629 | ||
|
|
87c966062c | ||
|
|
d7f867f1a6 | ||
|
|
e188f3474f | ||
|
|
1e80845aa6 | ||
|
|
a85569e54d |
@@ -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 {
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)})}});
|
||||
@@ -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()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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();">
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 26 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 44 KiB |
@@ -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">
|
||||
|
||||
@@ -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 {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = {
|
||||
@@ -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)
|
||||
|
||||
|
After Width: | Height: | Size: 148 KiB |
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -315,7 +315,6 @@ export class DisplayObjectScatter extends AbstractScatter {
|
||||
if (y != null) this.y = y
|
||||
}
|
||||
|
||||
|
||||
getWorldScatter() {
|
||||
return this
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 101 KiB |
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 61 KiB After Width: | Height: | Size: 90 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 61 KiB |
|
Before Width: | Height: | Size: 31 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 30 KiB After Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 92 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 37 KiB After Width: | Height: | Size: 52 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 53 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 38 KiB |
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 76 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 44 KiB |
|
Before Width: | Height: | Size: 29 KiB After Width: | Height: | Size: 42 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 45 KiB |
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 33 KiB |
@@ -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 })
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 123 KiB After Width: | Height: | Size: 140 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 64 KiB |
|
Before Width: | Height: | Size: 103 KiB After Width: | Height: | Size: 106 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 62 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 74 KiB After Width: | Height: | Size: 85 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 39 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 40 KiB |
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 187 KiB After Width: | Height: | Size: 189 KiB |
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 41 KiB After Width: | Height: | Size: 55 KiB |
@@ -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) {
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||