mapbox-gl
Version:
A WebGL interactive maps library
135 lines (108 loc) • 4.28 kB
JavaScript
'use strict';
const Evented = require('../util/evented');
const util = require('../util/util');
const loadTileJSON = require('./load_tilejson');
const normalizeURL = require('../util/mapbox').normalizeTileURL;
const TileBounds = require('./tile_bounds');
class VectorTileSource extends Evented {
constructor(id, options, dispatcher, eventedParent) {
super();
this.id = id;
this.dispatcher = dispatcher;
this.type = 'vector';
this.minzoom = 0;
this.maxzoom = 22;
this.scheme = 'xyz';
this.tileSize = 512;
this.reparseOverscaled = true;
this.isTileClipped = true;
util.extend(this, util.pick(options, ['url', 'scheme', 'tileSize']));
this._options = util.extend({ type: 'vector' }, options);
if (this.tileSize !== 512) {
throw new Error('vector tile sources must have a tileSize of 512');
}
this.setEventedParent(eventedParent);
}
load() {
this.fire('dataloading', {dataType: 'source'});
loadTileJSON(this._options, (err, tileJSON) => {
if (err) {
this.fire('error', err);
return;
}
util.extend(this, tileJSON);
this.setBounds(tileJSON.bounds);
// `content` is included here to prevent a race condition where `Style#_updateSources` is called
// before the TileJSON arrives. this makes sure the tiles needed are loaded once TileJSON arrives
// ref: https://github.com/mapbox/mapbox-gl-js/pull/4347#discussion_r104418088
this.fire('data', {dataType: 'source', sourceDataType: 'metadata'});
this.fire('data', {dataType: 'source', sourceDataType: 'content'});
});
}
setBounds(bounds) {
this.bounds = bounds;
if (bounds) {
this.tileBounds = new TileBounds(bounds, this.minzoom, this.maxzoom);
}
}
hasTile(coord) {
return !this.tileBounds || this.tileBounds.contains(coord, this.maxzoom);
}
onAdd(map) {
this.load();
this.map = map;
}
serialize() {
return util.extend({}, this._options);
}
loadTile(tile, callback) {
const overscaling = tile.coord.z > this.maxzoom ? Math.pow(2, tile.coord.z - this.maxzoom) : 1;
const params = {
url: normalizeURL(tile.coord.url(this.tiles, this.maxzoom, this.scheme), this.url),
uid: tile.uid,
coord: tile.coord,
zoom: tile.coord.z,
tileSize: this.tileSize * overscaling,
type: this.type,
source: this.id,
overscaling: overscaling,
angle: this.map.transform.angle,
pitch: this.map.transform.pitch,
showCollisionBoxes: this.map.showCollisionBoxes
};
if (!tile.workerID || tile.state === 'expired') {
tile.workerID = this.dispatcher.send('loadTile', params, done.bind(this));
} else if (tile.state === 'loading') {
// schedule tile reloading after it has been loaded
tile.reloadCallback = callback;
} else {
this.dispatcher.send('reloadTile', params, done.bind(this), tile.workerID);
}
function done(err, data) {
if (tile.aborted)
return;
if (err) {
return callback(err);
}
if (this.map._refreshExpiredTiles) tile.setExpiryData(data);
tile.loadVectorData(data, this.map.painter);
if (tile.redoWhenDone) {
tile.redoWhenDone = false;
tile.redoPlacement(this);
}
callback(null);
if (tile.reloadCallback) {
this.loadTile(tile, tile.reloadCallback);
tile.reloadCallback = null;
}
}
}
abortTile(tile) {
this.dispatcher.send('abortTile', { uid: tile.uid, type: this.type, source: this.id }, null, tile.workerID);
}
unloadTile(tile) {
tile.unloadVectorData();
this.dispatcher.send('removeTile', { uid: tile.uid, type: this.type, source: this.id }, null, tile.workerID);
}
}
module.exports = VectorTileSource;