kopia lustrzana https://github.com/robhawkes/vizicities
150 wiersze
3.9 KiB
JavaScript
Executable File
150 wiersze
3.9 KiB
JavaScript
Executable File
import TileLayer from './TileLayer';
|
||
import extend from 'lodash.assign';
|
||
import GeoJSONTile from './GeoJSONTile';
|
||
import throttle from 'lodash.throttle';
|
||
import THREE from 'three';
|
||
|
||
// 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
|
||
|
||
// 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,
|
||
distance: 30000,
|
||
workers: false
|
||
};
|
||
|
||
options = extend({}, defaults, options);
|
||
|
||
super(options);
|
||
|
||
this.defaults = defaults;
|
||
|
||
this._path = path;
|
||
}
|
||
|
||
_onAdd(world) {
|
||
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() {
|
||
if (this._pauseOutput || this._disableOutput) {
|
||
return;
|
||
}
|
||
|
||
this._outputTiles();
|
||
}
|
||
|
||
// Update tiles grid after world move, but don't output them
|
||
_onWorldMove(latlon, point) {
|
||
if (this._disableOutput) {
|
||
return;
|
||
}
|
||
|
||
this._pauseOutput = false;
|
||
this._calculateLOD();
|
||
}
|
||
|
||
// Pause updates during control movement for less visual jank
|
||
_onControlsMove() {
|
||
if (this._disableOutput) {
|
||
return;
|
||
}
|
||
|
||
this._pauseOutput = true;
|
||
}
|
||
|
||
_createTile(quadcode, layer) {
|
||
var newOptions = extend({}, this.defaults, this._options, {
|
||
outputToScene: false
|
||
});
|
||
|
||
delete newOptions.attribution;
|
||
|
||
return new GeoJSONTile(quadcode, this._path, layer, newOptions);
|
||
}
|
||
|
||
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};
|