vizicities/src/layer/tile/GeoJSONTileLayer.js

150 wiersze
3.9 KiB
JavaScript
Czysty Zwykły widok Historia

import TileLayer from './TileLayer';
import extend from 'lodash.assign';
import GeoJSONTile from './GeoJSONTile';
import throttle from 'lodash.throttle';
import THREE from 'three';
2016-02-29 09:33:27 +00:00
// TODO: Offer on-the-fly slicing of static, non-tile-based GeoJSON files into a
// tile grid using geojson-vt
//
// See: https://github.com/mapbox/geojson-vt
2016-02-28 18:40:51 +00:00
// TODO: Make sure nothing is left behind in the heap after calling destroy()
// TODO: Consider pausing per-frame output during movement so there's little to
// no jank caused by previous tiles still processing
// This tile layer only updates the quadtree after world movement has occurred
//
// Tiles from previous quadtree updates are updated and outputted every frame
// (or at least every frame, throttled to some amount)
//
// This is because the complexity of TopoJSON tiles requires a lot of processing
// and so makes movement janky if updates occur every frame – only updating
// after movement means frame drops are less obvious due to heavy processing
// occurring while the view is generally stationary
//
// The downside is that until new tiles are requested and outputted you will
// see blank spaces as you orbit and move around
//
// An added benefit is that it dramatically reduces the number of tiles being
// requested over a period of time and the time it takes to go from request to
// screen output
//
// It may be possible to perform these updates per-frame once Web Worker
// processing is added
class GeoJSONTileLayer extends TileLayer {
constructor(path, options) {
var defaults = {
maxLOD: 14,
2016-09-07 15:01:02 +00:00
distance: 30000,
workers: false
};
options = extend({}, defaults, options);
super(options);
this.defaults = defaults;
this._path = path;
}
_onAdd(world) {
2016-08-30 15:46:04 +00:00
return new Promise((resolve, reject) => {
super._onAdd(world).then(() => {
// Trigger initial quadtree calculation on the next frame
//
// TODO: This is a hack to ensure the camera is all set up - a better
// solution should be found
setTimeout(() => {
this._calculateLOD();
this._initEvents();
}, 0);
resolve(this);
}).catch(reject);
});
}
_initEvents() {
// Run LOD calculations based on render calls
//
// Throttled to 1 LOD calculation per 100ms
this._throttledWorldUpdate = throttle(this._onWorldUpdate, 100);
this._world.on('preUpdate', this._throttledWorldUpdate, this);
this._world.on('move', this._onWorldMove, this);
this._world.on('controlsMove', this._onControlsMove, this);
}
// Update and output tiles each frame (throttled)
_onWorldUpdate() {
2016-09-07 15:01:02 +00:00
if (this._pauseOutput || this._disableOutput) {
return;
}
this._outputTiles();
}
// Update tiles grid after world move, but don't output them
_onWorldMove(latlon, point) {
2016-09-07 15:01:02 +00:00
if (this._disableOutput) {
return;
}
this._pauseOutput = false;
this._calculateLOD();
}
// Pause updates during control movement for less visual jank
_onControlsMove() {
2016-09-07 15:01:02 +00:00
if (this._disableOutput) {
return;
}
this._pauseOutput = true;
}
_createTile(quadcode, layer) {
2016-10-06 09:11:32 +00:00
var newOptions = extend({}, this.defaults, this._options, {
outputToScene: false
});
2016-09-16 10:39:02 +00:00
delete newOptions.attribution;
return new GeoJSONTile(quadcode, this._path, layer, newOptions);
}
2016-10-25 11:40:13 +00:00
hide() {
this._pauseOutput = true;
super.hide();
}
show() {
this._pauseOutput = false;
super.show();
}
// Destroys the layer and removes it from the scene and memory
destroy() {
this._world.off('preUpdate', this._throttledWorldUpdate);
this._world.off('move', this._onWorldMove);
this._throttledWorldUpdate = null;
// Run common destruction logic from parent
super.destroy();
}
}
export default GeoJSONTileLayer;
var noNew = function(path, options) {
return new GeoJSONTileLayer(path, options);
};
// Initialise without requiring new keyword
export {noNew as geoJSONTileLayer};