Removed PIXIUtils from geographics.

This commit is contained in:
Severin Opel 2019-12-09 14:27:32 +01:00
parent 9159073483
commit 792892cb82
4 changed files with 285 additions and 258 deletions

66
dist/iwmlib.js vendored
View File

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

202
dist/iwmlib.pixi.js vendored
View File

@ -19094,6 +19094,31 @@
}
}
class PIXIUtils {
/*
* Transform a pixi text to it's actual screensize,
* ignoring it's local transforms
*/
static toScreenFontSize(pixiText, fontSize = null) {
pixiText._recursivePostUpdateTransform();
let normalizedScale = {
x: pixiText.scale.x / pixiText.transform.worldTransform.a,
y: pixiText.scale.x / pixiText.transform.worldTransform.d
};
pixiText.scale = { x: normalizedScale.x, y: normalizedScale.y };
if (fontSize) pixiText.style.fontSize = fontSize;
}
static saveFill(graphics) {
return {
fill: graphics.fill.color,
alpha: graphics.fill.alpha
}
}
}
/**
* GeoGraphics are graphical objects, that does not store the graphics information
* in screen space, but in geographical coordinates. Therefore GeoGraphics must be
@ -19532,15 +19557,12 @@
*/
_drawShape(polygon, hole = []) {
// We save the fill specified in the onDraw event handler.
//
// Consider: Maybe it would be a good idea to add a 'onHoleDraw'
// callback to make the hole customizable. Maybe you want
// to fill it with a different color or an mediocre alpha value.
// then feel free to implement it.
let { fill, alpha } = PIXIUtils.saveFill(this.graphics);
/**
* This may seem redundant, but it's required
* a) Draw the hole with a polygon.
*
* This may seem redundant to (c), but it's required (in this order(!))
* to make the hole clickable.
*
* It was a bit confusing, so I made a CodePen
@ -19551,9 +19573,15 @@
this.graphics.drawPolygon(hole);
}
/**
* b) Draw the shape.
*/
this.graphics.beginFill(fill, alpha);
this.graphics.drawPolygon(polygon);
/**
* c) Add the hole.
*/
if (hole.length > 0) {
this.graphics.beginHole();
this.graphics.drawPolygon(hole);
@ -19602,31 +19630,6 @@
}
}
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
}
}
}
class MapList {
constructor(active = null, maps = {}) {
this.maps = maps;
@ -19926,7 +19929,15 @@
mapList,
scatterContainer,
displayObject,
{ onTransform = null, onChange = null, focus = null, zoom = null, viewport = null, name = null } = {}
{
onTransform = null,
onChange = null,
focus = null,
zoom = null,
viewport = null,
name = null,
mapChangeLocked = false
} = {}
) {
super(displayObject, {
name
@ -19954,6 +19965,7 @@
// this.maps = maps
// if (opts.map) this.placeMap(opts.map)
this.dynamicElements = new Map();
this._mapChangeLocked = mapChangeLocked;
// Binds the transformed callback beforehand.
this.transformed = this.transformed.bind(this);
@ -19961,13 +19973,24 @@
this.changeMap(mapList.active);
}
get mapChangeLocked() {
return this._mapChangeLocked
}
lockMapChange() {
this._mapChangeLocked = true;
}
unlockMapChange() {
this._mapChangeLocked = false;
}
adapt() {
this.layers.forEach(layer => {
if (layer.adapt) layer.adapt(this.map);
});
}
focus(coordinates, zoom) {
this.mapview.updateFocusPoint(this.map);
}
@ -20017,89 +20040,39 @@
/* map ,
useScatterAsContainer = true // If set to false, the normal container is used. This is necessary when using submaps and the container need to be a RigidContainer.*/
) {
console.log('🗺️ Change map to: ', name);
let oldMap = this.map;
if (!this.mapChangeLocked) {
console.log('🗺️ Change map to: ', name);
let oldMap = this.map;
this.mapList.select(name);
this.mapList.select(name);
if (oldMap) {
oldMap.unload();
oldMap.onTransform.remove(this.transformed);
}
let map = this.map;
if (map) {
map.load();
this.scatterContainer.addChild(map.image);
this.mapview.apply(map);
map.image.addChild(this.displayObject);
// A geolayer's displayObject is on the parent layer.
// A maplayer's displayobject is always the child of the map.
this.adapt();
this.changeHandler.call(this, map, oldMap);
//Call transform one time manually.
this.transformed();
map.onTransform.add(this.transformed);
} else {
console.error(`Could not change map to ${name}.`);
}
/*Logging.log(`Map change: ${key}`)
if (this.active !== key) {
if (this.maps.hasOwnProperty(key)) {
let old = this.map ? this.map : null
this._map = this.maps[key]
this._map.name = key
this.active = key
let container = useScatterAsContainer ? this.scatterContainer : this.container
this.map.load(container)
// Copies all layers.
this.layers.forEach(layer => {
if (old) this.map.image.addChild(layer.container)
})
this.placeMap(this.map)
/**
* TODO: Improve
*
* I'm quite sure if I made a design mistake here.
* In an earlier version I did not need to migrate the
* layers manually from the map to the next map.
*
* I believe the old version was a container next to the
* map, which got updated on transform.
*
* -SO
*/
/*
if (old) old.unload()
} else {
let keys = Object.keys(this.maps)
if (keys.length == 0) console.error('There is no map set for the map layer!')
else {
let fallbackMap = keys[0]
console.error(
`A map with the key (${key}) does not exists within the mapapp. Fallback to map: ${fallbackMap}.`
)
this.changeMap(fallbackMap, {
useScatterAsContainer
})
}
if (oldMap) {
oldMap.unload();
oldMap.onTransform.remove(this.transformed);
}
}*/
let map = this.map;
if (map) {
map.load();
this.scatterContainer.addChild(map.image);
this.mapview.apply(map);
map.image.addChild(this.displayObject);
// A geolayer's displayObject is on the parent layer.
// A maplayer's displayobject is always the child of the map.
this.adapt();
this.changeHandler.call(this, map, oldMap);
//Call transform one time manually.
this.transformed();
map.onTransform.add(this.transformed);
} else {
console.error(`Could not change map to ${name}.`);
}
}
}
refocus() {
@ -21642,4 +21615,7 @@
window.Overlay = Overlay;
window.MapList = MapList;
window.GeoJson = GeoJson;
window.GeoUtils = GeoUtils;
}());

View File

@ -1,5 +1,5 @@
<!doctype html>
<html lang='en'>
<html lang='en' class="dark-mode">
<head>
<meta charset='UTF-8'>
@ -29,73 +29,7 @@
</style>
</head>
<script>
let capitals = {
abidjan: { x: 5.349470, y: -4.006472 },
berlin: { x: 52.525430, y: 13.385291 },
canberra: { x: -35.282025, y: 149.128648 },
capetown: { x: -33.925448, y: 18.416962 },
moscow: { x: 55.750892, y: 37.622799 },
washington: { x: 38.895650, y: -77.031407 },
rio: { x: -22.871400, y: -43.280490 },
tokio: { x: 35.696278, y: 139.731366 }
}
window.apps = [
]
function createApp(view) {
let app = window.GeoPointApp = new MapApp({
view,
focus: { x: 0, y: 0 },
zoom: 1,
width: 512,
height: 512,
coordsLogging: true
})
const osmworld = "../assets/maps/osm/0/0/0.png"
const wikimedia = "../assets/maps/wikimedia-world-robinson/2000px-BlankMap-World.png"
return new Promise((resolve, reject) => {
setTimeout(() => {
reject("Creating app timed out.")
}, 3000)
app.loadSprites([
osmworld,
wikimedia
], (sprites) => {
let osmMap = new ImageMap(sprites.get(osmworld), new MapData(new Projection.Mercator()), { cover: true, debug: true })
let wikimediaMap = new ImageMap(sprites.get(wikimedia), new MapData(new Projection.Robinson(10)), {
baseZoomHeight: sprites.get(osmworld).texture.height,
cover: true, debug: true
})
app.addMaps({
"osm": osmMap, "wiki": wikimediaMap
})
app.selectMap("osm")
app.setup().run()
window.apps.push(app)
resolve(app)
}, { resolutionDependent: false })
})
}
function enableSwitch(button, app) {
button.addEventListener("click", () => {
let next = app.mapLayer.next()
})
}
</script>
<body onload='Doctest.run()'>
<h1>GeoGraphics</h1>
@ -111,12 +45,139 @@
</ul>
<nav>
<ul>
<h3>Content</h3>
<ol>
<li><a href="#geopoint">Gep Point</a></li>
<li><a href="#geoline">Geo Line</a></li>
<li><a href="#geoshape">Geo Shape</a></li>
</ul>
</ol>
</nav>
<!-- Hidden Scripts -->
<script>
window.apps = []
</script>
<section>
<h2>Data & Functions</h2>
<!-- Capitals Data. -->
<script class="doctest" data-collapsible data-collapsed data-title="Capitals Data">
// Some random capitals of the world.
// Coordinates taken from Google Maps.
let capitals = {
abidjan: { x: 5.349470, y: -4.006472 },
berlin: { x: 52.525430, y: 13.385291 },
canberra: { x: -35.282025, y: 149.128648 },
capetown: { x: -33.925448, y: 18.416962 },
moscow: { x: 55.750892, y: 37.622799 },
washington: { x: 38.895650, y: -77.031407 },
rio: { x: -22.871400, y: -43.280490 },
tokio: { x: 35.696278, y: 139.731366 }
}
</script>
<script class="doctest" data-collapsible data-collapsed data-title="enableSwitch(button, app)">
// Adds an eventlistener to a button, that it changes the map.
function enableSwitch(button, app) {
button.addEventListener("click", () => {
// Always call the next map.
let next = app.mapLayer.next()
})
}
</script>
<script class="doctest" data-collapsible data-collapsed data-title="createApp(view)">
// Creates a MapApp with two predefined maps on a specified canvas element.
function createApp(canvas, options) {
// Create MapApp.
let app = window.GeoPointApp = new MapApp(Object.assign({
view: canvas,
focus: { x: 0, y: 0 },
zoom: 1,
width: 512,
height: 512,
coordsLogging: true
}, options))
// Images used for the maps.
const osmworld = "../assets/maps/osm/0/0/0.png"
const wikimedia = "../assets/maps/wikimedia-world-robinson/2000px-BlankMap-World.png"
// ImageMaps need to be loaded first. Therefore we return a promise.
return new Promise((resolve, reject) => {
// Add a timeout that cancels the promise when it takes too long.
setTimeout(() => {
reject("Creating app timed out.")
}, 3000)
// The app's loader loads the sprites.
app.loadSprites([
osmworld,
wikimedia
], (sprites) => {
// For demonstration of the geographics capability,
// Two different projections are used.
const MercatorProjection = new Projection.Mercator()
const RobinsonProjection = new Projection.Robinson(10)
// In the MapDataOptions 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 }
// Specifies a common zoom height for both maps.
// Otherwise the zoomFactor would differ and on mapchange it
// would appear as the map zoomes drastically in and out.
const osmHeight = sprites.get(osmworld).height
const mapOptions = {
baseZoomHeight: osmHeight
}
// 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)
// Add the maps to the mapapp.
// An object is used to have a key, that identifies the maps.
app.addMaps({
"osm": osmMap, "wiki": wikimediaMap
})
// Select a specific map.
// The addMaps function sets a 'random' map as startmap.
app.selectMap("osm")
// Run the app after the maps were set.
app.setup().run()
// [DEBUG] Save the app in a global array.
window.apps.push(app)
// Resolve the promise.
resolve(app)
}, {
// Tell the loader to not load the images resolution dependent.
// That results in 'weird' naming of the textures.
resolutionDependent: false
})
})
}
</script>
</section>
<!--GeoPoint -->
<section id="geopoint">
<h2>GeoPoint</h2>
@ -127,7 +188,7 @@
</div>
<script class="doctest">
<script class="doctest" data-title="GeoPoint Example" data-collapsible>
; (function () {
createApp(geopoint_canvas).then(app => {
@ -168,11 +229,12 @@
<canvas id="geoline_canvas"></canvas>
<div class="controls">
<button id="geoline_switch">Change Map</button>
<label>closed
<label>
<input type="checkbox" id="geoline_close_toggle">
</label>
</div>
<script class="doctest">
<div class="checkbox"></div>
<span>Close Line</span>
</label> </div>
<script class="doctest" data-title="GeoLine Example">
; (function () {
createApp(geoline_canvas).then(app => {
@ -242,7 +304,7 @@
</div>
<button id="geoshape_switch">Change Map</button>
<script class='doctest'>
<script class='doctest' data-title="GeoShape Example">
(function () {
@ -297,7 +359,7 @@
<div class="wrapper">
<canvas id="geojson_canvas"></canvas>
</div>
<button id="geojson_switch">changemap</button>
<button id="geojson_switch">Change Map</button>
<script class="doctest" data-title="GeoJson Raw Data" data-collapsed data-collapsible>
@ -310,7 +372,10 @@
; (function () {
createApp(geojson_canvas).then(app => {
createApp(geojson_canvas, {
zoom: 6,
focus: { x: 50.99, y: -119.9 }
}).then(app => {
let geoLayer = new GeoLayer(new PIXI.Container())
let geographics = GeoUtils.transformToGeoGraphics([usaFeatureCollection])

View File

@ -2,6 +2,7 @@ import { Points } from '../../utils.js'
import { EventHandler } from './utils.js'
import { FlagPolygon } from '../graphics/label.js'
import { DeepZoomMap } from './map.js'
import { PIXIUtils } from '../../../../js/pixi/utils.js'
/**
* GeoGraphics are graphical objects, that does not store the graphics information
@ -441,15 +442,12 @@ export class GeoShape extends GeoGraphics {
*/
_drawShape(polygon, hole = []) {
// We save the fill specified in the onDraw event handler.
//
// Consider: Maybe it would be a good idea to add a 'onHoleDraw'
// callback to make the hole customizable. Maybe you want
// to fill it with a different color or an mediocre alpha value.
// then feel free to implement it.
let { fill, alpha } = PIXIUtils.saveFill(this.graphics)
/**
* This may seem redundant, but it's required
* a) Draw the hole with a polygon.
*
* This may seem redundant to (c), but it's required (in this order(!))
* to make the hole clickable.
*
* It was a bit confusing, so I made a CodePen
@ -460,9 +458,15 @@ export class GeoShape extends GeoGraphics {
this.graphics.drawPolygon(hole)
}
/**
* b) Draw the shape.
*/
this.graphics.beginFill(fill, alpha)
this.graphics.drawPolygon(polygon)
/**
* c) Add the hole.
*/
if (hole.length > 0) {
this.graphics.beginHole()
this.graphics.drawPolygon(hole)
@ -524,6 +528,14 @@ class GeoMultiGraphics extends GeoGraphics {
}
}
/**
*
*
* @export
* @class GeoText
* @extends {GeoPoint}
*/
export class GeoText extends GeoPoint {
constructor(coordinates, text, opts) {
super(coordinates, opts)
@ -531,14 +543,6 @@ export class GeoText extends GeoPoint {
this.textStyle = Object.assign(new PIXI.TextStyle(), opts.textStyle)
this._text = new PIXI.Text(text, this.textStyle)
//TODO: Make this more generic:
// We have 8 layers (12-20) for each map so this temporarily works fine.
// Outsource it to the map class.
//let textScale = Math.pow(2, 7)
// let textScale = 5
// this.text.scale.set(1 / textScale, 1 / textScale)
switch (this.align) {
case 'left':
break
@ -715,27 +719,3 @@ export class GeoMultiShape extends GeoShape {
}
}
class PIXIUtils {
/*
* Transform a pixi text to it's actual screensize,
* ignoring it's local transforms
*/
static toScreenFontSize(pixiText, fontSize = null) {
pixiText._recursivePostUpdateTransform()
let normalizedScale = {
x: pixiText.scale.x / pixiText.transform.worldTransform.a,
y: pixiText.scale.x / pixiText.transform.worldTransform.d
}
pixiText.scale = { x: normalizedScale.x, y: normalizedScale.y }
if (fontSize) pixiText.style.fontSize = fontSize
}
static saveFill(graphics) {
return {
fill: graphics.fill.color,
alpha: graphics.fill.alpha
}
}
}