Skip to content

Instantly share code, notes, and snippets.

@hampelm
Last active December 13, 2015 17:48
Show Gist options
  • Save hampelm/4950496 to your computer and use it in GitHub Desktop.
Save hampelm/4950496 to your computer and use it in GitHub Desktop.
Remote GeoJSON datasource
var fs = require("fs");
var projector = require(__dirname + "/../lib/projector");
var request = require("request");
var FILTER_BY_EXTENTS = true;
var RemoteGeoJsonSource = function(options) {
this._projection = projector.util.cleanProjString(options.projection || "EPSG:4326");
this._path = options.path; // required
this._encoding = options.encoding || "utf8";
this.name = options.name || options.path.slice(options.path.lastIndexOf("/") + 1);
if (this.name.indexOf(".") !== -1) {
this.name = this.name.slice(0, this.name.indexOf("."));
}
this.sourceName = this.name;
// loading synchronization
this._loadCallbacks = [];
this._loading = false;
// stored state
this._loadError = null;
this._data = null;
this._projectedData = {};
};
RemoteGeoJsonSource.prototype = {
constructor: RemoteGeoJsonSource,
getShapes: function(minX, minY, maxX, maxY, mapProjection, callback) {
if (this._projectedData[mapProjection]){
var data = this._filterByExtent(this._projectedData[mapProjection], minX, minY, maxX, maxY);
callback(null, data);
}
else {
this.load(function(error, data) {
if (error){
this._loadError = error;
}
else if (!this._projectedData[mapProjection]) {
this._project(mapProjection);
}
var data = this._filterByExtent(this._projectedData[mapProjection], minX, minY, maxX, maxY);
callback(this._loadError, data);
}.bind(this));
}
},
load: function(callback) {
if (this._data || this._loadError) {
callback(this._loadError, this._data);
return;
}
callback && this._loadCallbacks.push(callback);
if (!this._loading) {
this._loading = true;
var start = Date.now();
console.log("Loading data from " + this._path + "...");
var request = require('request');
request(this._path, function (error, response, body) {
if (error || response.statusCode !== 200) {
this._loadError = error;
}
else {
try {
this._data = JSON.parse(body);
console.log("Loaded in " + (Date.now() - start) + "ms");
}
catch (ex) {
this._loadError = ex;
console.log("Failed to load in " + (Date.now() - start) + "ms");
}
}
this._loading = false;
var callbacks = this._loadCallbacks;
this._loadCallbacks = [];
callbacks.forEach(function(callback) {
callback(this._loadError, this._data);
}.bind(this));
}.bind(this));
}
},
project: function(destinationProjection) {
this._project(destinationProjection);
},
_project: function(mapProjection) {
var doBounds = !this._projectedData[mapProjection];
if (this._projection !== mapProjection) {
console.log("Projecting features...");
start = Date.now();
this._projectedData[mapProjection] = projector.project.FeatureCollection(this._projection, mapProjection, this._data);
console.log("Projected in " + (Date.now() - start) + "ms");
} else {
console.log("Projection not necessary");
this._projectedData[mapProjection] = this._data;
}
// HACK
if (FILTER_BY_EXTENTS && doBounds) {
this._calculateBounds(this._projectedData[mapProjection]);
}
},
_calculateBounds: function(dataset) {
return this._shapes(dataset).forEach(function(shape) {
shape.bounds = this._shapeBounds(shape);
}, this);
},
_filterByExtent: function(dataset, minX, minY, maxX, maxY) {
if (!FILTER_BY_EXTENTS) {
return dataset;
}
var extent = {
minX: minX,
minY: minY,
maxX: maxX,
maxY: maxY
};
return this._shapes(dataset).filter(function(shape) {
// return intersects(this._shapeBounds(shape), extent);
return intersects(shape.bounds, extent);
}.bind(this));
},
_shapeBounds: function(shape) {
shape = shape.geometry || shape;
var coordinates = shape.coordinates;
if (shape.type === "Point") {
return {
minX: coordinates[0],
maxX: coordinates[0],
minY: coordinates[1],
maxY: coordinates[1]
};
}
var bounds = {
minX: Infinity,
minY: Infinity,
maxX: -Infinity,
maxY: -Infinity
};
if (shape.type === "Polygon" || shape.type === "MultiLineString") {
for (var i = coordinates.length - 1; i >= 0; i--) {
var coordinateSet = coordinates[i];
for (var j = coordinateSet.length - 1; j >= 0; j--) {
bounds.minX = Math.min(bounds.minX, coordinateSet[j][0]);
bounds.maxX = Math.max(bounds.maxX, coordinateSet[j][0]);
bounds.minY = Math.min(bounds.minY, coordinateSet[j][1]);
bounds.maxY = Math.max(bounds.maxY, coordinateSet[j][1]);
}
}
}
else if (shape.type === "MultiPolygon") {
for (var i = coordinates.length - 1; i >= 0; i--) {
var coordinateSet = coordinates[i];
for (var j = coordinateSet.length - 1; j >= 0; j--) {
var coordinateSetSet = coordinateSet[j];
for (var k = coordinateSetSet.length - 1; k >= 0; k--) {
bounds.minX = Math.min(bounds.minX, coordinateSetSet[k][0]);
bounds.maxX = Math.max(bounds.maxX, coordinateSetSet[k][0]);
bounds.minY = Math.min(bounds.minY, coordinateSetSet[k][1]);
bounds.maxY = Math.max(bounds.maxY, coordinateSetSet[k][1]);
}
}
}
}
else {
for (var i = coordinates.length - 1; i >= 0; i--) {
bounds.minX = Math.min(bounds.minX, coordinates[i][0]);
bounds.maxX = Math.max(bounds.maxX, coordinates[i][0]);
bounds.minY = Math.min(bounds.minY, coordinates[i][1]);
bounds.maxY = Math.max(bounds.maxY, coordinates[i][1]);
}
}
return bounds;
},
_shapes: function(feature) {
var shapes = [];
if (feature.type === "FeatureCollection") {
for (var i = feature.features.length - 1; i >= 0; i--) {
shapes = shapes.concat(this._shapes(feature.features[i]));
}
}
else if (feature.type === "Feature") {
if (feature.geometry.type === "GeometryCollection") {
shapes = shapes.concat(this._shapes(feature.geometry));
}
else {
shapes.push(feature);
}
}
else if (feature.type === "GeometryCollection") {
for (var i = feature.geometries.length - 1; i >= 0; i--) {
shapes = shapes.concat(this._shapes(feature.geometries[i]));
}
}
else {
shapes.push(feature);
}
return shapes;
}
};
module.exports = RemoteGeoJsonSource;
var intersects = function(a, b) {
var xIntersects = (a.minX < b.maxX && a.minX > b.minX) ||
(a.maxX < b.maxX && a.maxX > b.minX) ||
(a.minX < b.minX && a.maxX > b.maxX);
var yIntersects = (a.minY < b.maxY && a.minY > b.minY) ||
(a.maxY < b.maxY && a.maxY > b.minY) ||
(a.minY < b.minY && a.maxY > b.maxY);
return xIntersects && yIntersects;
};
@Mr0grog
Copy link

Mr0grog commented Feb 14, 2013

I think you could simplify by just doing (totally untested):

var request = require("request");
var GeoJsonSource = require("./GeoJson");

var RemoteGeoJsonSource = function(options) {
  GeoJsonSource.call(this, options);
};

RemoteGeoJsonSource = Object.create(GeoJsonSource.prototype);

RemoteGeoJsonSource.prototype.load = function(callback) {
  if (this._data || this._loadError) {
    callback(this._loadError, this._data);
    return;
  }

  callback && this._loadCallbacks.push(callback);

  if (!this._loading) {
    this._loading = true;

    var start = Date.now();
    console.log("Loading data from " + this._path + "...");

    var request = require('request');
    request(this._path, function (error, response, body) {
      if (error || response.statusCode !== 200) {
        this._loadError = error;
      }
      else {
        try {
          this._data = JSON.parse(body);
          console.log("Loaded in " + (Date.now() - start) + "ms");
        }
        catch (ex) {
          this._loadError = ex;
          console.log("Failed to load in " + (Date.now() - start) + "ms");
        }
      }

      this._loading = false;

      var callbacks = this._loadCallbacks;
      this._loadCallbacks = [];
      callbacks.forEach(function(callback) {
        callback(this._loadError, this._data);
      }.bind(this));
    }.bind(this));
  }
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment