Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions debug/site.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ map.on('style.load', function() {
}, 'country-label-lg');

map.addSource('geojson-random-points', {
"skipWorker": true,
"type": "geojson",
"data": "/debug/random.geojson"
});
Expand Down
153 changes: 137 additions & 16 deletions js/source/geojson_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@ var Evented = require('../util/evented');
var TilePyramid = require('./tile_pyramid');
var Source = require('./source');
var urlResolve = require('resolve-url');
var WorkerTile = require('./worker_tile');
var ajax = require('../util/ajax');
var supercluster = require('supercluster');
var geojsonvt = require('geojson-vt');
var GeoJSONWrapper = require('../source/geojson_wrapper');

module.exports = GeoJSONSource;

Expand Down Expand Up @@ -58,6 +63,8 @@ function GeoJSONSource(options) {
log: false
};

this.skipWorker = options.skipWorker;

this._pyramid = new TilePyramid({
tileSize: 512,
minzoom: this.minzoom,
Expand All @@ -70,6 +77,9 @@ function GeoJSONSource(options) {
remove: this._removeTile.bind(this),
redoPlacement: this._redoTilePlacement.bind(this)
});

this._loadedTiles = {};
this._geoJSONIndexes = {};
}

GeoJSONSource.prototype = util.inherit(Evented, /** @lends GeoJSONSource.prototype */{
Expand Down Expand Up @@ -123,32 +133,118 @@ GeoJSONSource.prototype = util.inherit(Evented, /** @lends GeoJSONSource.prototy
getVisibleCoordinates: Source._getVisibleCoordinates,
getTile: Source._getTile,

featuresAt: Source._vectorFeaturesAt,
featuresIn: Source._vectorFeaturesIn,
featuresAt: function(coord, params, callback) {
if (!this._pyramid)
return callback(null, []);

var result = this._pyramid.tileAt(coord);
if (!result)
return callback(null, []);

this._queryFeatures({
uid: result.tile.uid,
x: result.x,
y: result.y,
tileExtent: result.tile.tileExtent,
scale: result.scale,
source: this.id,
params: params
}, callback, result.tile.workerID);
},

featuresIn: function(bounds, params, callback) {
if (!this._pyramid)
return callback(null, []);

var results = this._pyramid.tilesIn(bounds);
if (!results)
return callback(null, []);

util.asyncAll(results, function queryTile(result, cb) {
this._queryFeatures({
uid: result.tile.uid,
source: this.id,
minX: result.minX,
maxX: result.maxX,
minY: result.minY,
maxY: result.maxY,
params: params
}, cb, result.tile.workerID);
}.bind(this), function done(err, features) {
callback(err, Array.prototype.concat.apply([], features));
});
},

_queryFeatures: function(params, callback, workerID) {
if (this.skipWorker) {
var tile = this._loadedTiles[params.source] && this._loadedTiles[params.source][params.uid];
if (tile) {
tile.featureTree.query(params, callback);
} else {
callback(null, []);
}
} else {
this.dispatcher.send('query features', params, callback, workerID);
}
},

_updateData: function() {
this._dirty = false;
var data = this._data;
if (typeof data === 'string' && typeof window != 'undefined') {
data = urlResolve(window.location.href, data);
}
this.workerID = this.dispatcher.send('parse geojson', {

var params = {
data: data,
tileSize: 512,
source: this.id,
geojsonVtOptions: this.geojsonVtOptions,
cluster: this.cluster,
superclusterOptions: this.superclusterOptions
}, function(err) {
this._loaded = true;
};

var that = this;

if (this.skipWorker) {
var indexData = function(err, data) {
if (err) return callback(err);
if (typeof data != 'object') {
return callback(new Error("Input data is not a valid GeoJSON object."));
}
try {
this._geoJSONIndexes[params.source] = params.cluster ?
supercluster(params.superclusterOptions).load(data.features) :
geojsonvt(data, params.geojsonVtOptions);
} catch (err) {
return callback(err);
}
callback(null);
}.bind(this);

// TODO accept params.url for urls instead

// Not, because of same origin issues, urls must either include an
// explicit origin or absolute path.
// ie: /foo/bar.json or http://example.com/bar.json
// but not ../foo/bar.json
if (typeof params.data === 'string') {
ajax.getJSON(params.data, indexData);
}
else indexData(null, params.data);
} else {
this.workerID = this.dispatcher.send('parse geojson', params, callback);
}

function callback(err) {
that._loaded = true;
if (err) {
this.fire('error', {error: err});
that.fire('error', {error: err});
} else {
this._pyramid.reload();
this.fire('change');
that._pyramid.reload();
that.fire('change');
}

}.bind(this));
}
},

_loadTile: function(tile) {
Expand All @@ -166,28 +262,53 @@ GeoJSONSource.prototype = util.inherit(Evented, /** @lends GeoJSONSource.prototy
collisionDebug: this.map.collisionDebug
};

tile.workerID = this.dispatcher.send('load geojson tile', params, function(err, data) {
var that = this;

if (this.skipWorker) {
var source = params.source,
coord = params.coord;

tile.unloadVectorData(this.map.painter);
if (!this._geoJSONIndexes[source]) return callback(null, null); // we couldn't load the file

var geoJSONTile = this._geoJSONIndexes[source].getTile(coord.z, coord.x, coord.y);

if (!geoJSONTile) return callback(null, null); // nothing in the given tile

var workerTile = new WorkerTile(params);
workerTile.parse(
new GeoJSONWrapper(geoJSONTile.features),
this.style._order.map(function(id) { return this.style._layers[id].json(); }, this), // layers
null, // actor
callback
);

this._loadedTiles[source] = this._loadedTiles[source] || {};
this._loadedTiles[source][params.uid] = workerTile;
} else {
tile.workerID = this.dispatcher.send('load geojson tile', params, callback, this.workerID);
}

function callback(err, data) {
tile.unloadVectorData(that.map.painter);

if (tile.aborted)
return;

if (err) {
this.fire('tile.error', {tile: tile});
that.fire('tile.error', {tile: tile});
return;
}

tile.loadVectorData(data);

if (tile.redoWhenDone) {
tile.redoWhenDone = false;
tile.redoPlacement(this);
tile.redoPlacement(that);
}

this.fire('tile.load', {tile: tile});
that.fire('tile.load', {tile: tile});
}

}.bind(this), this.workerID);
},

_abortTile: function(tile) {
Expand Down
1 change: 1 addition & 0 deletions js/source/tile.js
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ Tile.prototype = {
},

getElementGroups: function(layer, shaderName) {
if (!this.buffers || !Object.keys(this.buffers)) return null;
return this.elementGroups && this.elementGroups[layer.ref || layer.id] && this.elementGroups[layer.ref || layer.id][shaderName];
}
};
Expand Down