212 lines
7.1 KiB
JavaScript
212 lines
7.1 KiB
JavaScript
var assert = require('assert');
|
|
|
|
var step = require('step');
|
|
|
|
/**
|
|
* @param app
|
|
* @param {MapBackend} mapBackend
|
|
* @param layergroupRequestDecorator
|
|
* @constructor
|
|
*/
|
|
function MapController(app, mapBackend, layergroupRequestDecorator) {
|
|
this._app = app;
|
|
this._mapBackend = mapBackend;
|
|
this._layergroupRequestDecorator = layergroupRequestDecorator;
|
|
}
|
|
|
|
module.exports = MapController;
|
|
|
|
|
|
MapController.prototype.register = function(app) {
|
|
app.get(app.base_url_mapconfig + '/:token/:z/:x/:y@:scale_factor?x.:format', this.tile.bind(this));
|
|
app.get(app.base_url_mapconfig + '/:token/:z/:x/:y.:format', this.tile.bind(this));
|
|
app.get(app.base_url_mapconfig + '/:token/:layer/:z/:x/:y.(:format)', this.layer.bind(this));
|
|
app.options(app.base_url_mapconfig, this.cors.bind(this));
|
|
app.get(app.base_url_mapconfig, this.createGet.bind(this));
|
|
app.post(app.base_url_mapconfig, this.createPost.bind(this));
|
|
app.get(app.base_url_mapconfig + '/:token/:layer/attributes/:fid', this.attributes.bind(this));
|
|
};
|
|
|
|
// send CORS headers when client send options.
|
|
MapController.prototype.cors = function(req, res, next) {
|
|
this._app.doCORS(res, "Content-Type");
|
|
return next();
|
|
};
|
|
|
|
// Gets attributes for a given layer feature
|
|
MapController.prototype.attributes = function(req, res) {
|
|
var self = this;
|
|
|
|
req.profiler.start('windshaft.maplayer_attribute');
|
|
|
|
this._app.doCORS(res);
|
|
|
|
step(
|
|
function setupParams() {
|
|
self._app.req2params(req, this);
|
|
},
|
|
function retrieveFeatureAttributes(err) {
|
|
req.profiler.done('req2params');
|
|
|
|
assert.ifError(err);
|
|
|
|
self._mapBackend.getFeatureAttributes(req.params, false, this);
|
|
},
|
|
function finish(err, tile, stats) {
|
|
req.profiler.add(stats || {});
|
|
|
|
if (err) {
|
|
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
|
|
var errMsg = err.message ? ( '' + err.message ) : ( '' + err );
|
|
var statusCode = self._app.findStatusCode(err);
|
|
self._app.sendError(res, { errors: [errMsg] }, statusCode, 'GET ATTRIBUTES', err);
|
|
} else {
|
|
self._app.sendResponse(res, [tile, 200]);
|
|
}
|
|
}
|
|
);
|
|
|
|
};
|
|
|
|
MapController.prototype.create = function(req, res, prepareConfigFn) {
|
|
var self = this;
|
|
|
|
this._app.doCORS(res);
|
|
|
|
var layergroupDecorator = {
|
|
beforeLayergroupCreate: function(requestMapConfig, callback) {
|
|
self._layergroupRequestDecorator.beforeLayergroupCreate(req, requestMapConfig, callback);
|
|
},
|
|
afterLayergroupCreate: function(layergroup, response, callback) {
|
|
self._layergroupRequestDecorator.afterLayergroupCreate(req, layergroup, response, callback);
|
|
}
|
|
};
|
|
|
|
step(
|
|
function setupParams(){
|
|
self._app.req2params(req, this);
|
|
},
|
|
prepareConfigFn,
|
|
function initLayergroup(err, requestMapConfig) {
|
|
assert.ifError(err);
|
|
self._mapBackend.createLayergroup(requestMapConfig, req.params, layergroupDecorator, this);
|
|
},
|
|
function finish(err, response){
|
|
if (err) {
|
|
response = { errors: [ err.message ] };
|
|
var statusCode = self._app.findStatusCode(err);
|
|
self._app.sendError(res, response, statusCode, 'GET LAYERGROUP', err);
|
|
} else {
|
|
self._app.sendResponse(res, [response, 200]);
|
|
}
|
|
}
|
|
);
|
|
};
|
|
|
|
MapController.prototype.createGet = function(req, res){
|
|
req.profiler.start('windshaft.createmap_get');
|
|
|
|
this.create(req, res, function createGet$prepareConfig(err, req) {
|
|
assert.ifError(err);
|
|
if ( ! req.params.config ) {
|
|
throw new Error('layergroup GET needs a "config" parameter');
|
|
}
|
|
return JSON.parse(req.params.config);
|
|
});
|
|
};
|
|
|
|
// TODO rewrite this so it is possible to share code with `MapController::create` method
|
|
MapController.prototype.createPost = function(req, res) {
|
|
req.profiler.start('windshaft.createmap_post');
|
|
|
|
this.create(req, res, function createPost$prepareConfig(err, req) {
|
|
assert.ifError(err);
|
|
if ( ! req.headers['content-type'] || req.headers['content-type'].split(';')[0] !== 'application/json' ) {
|
|
throw new Error('layergroup POST data must be of type application/json');
|
|
}
|
|
return req.body;
|
|
});
|
|
};
|
|
|
|
// Gets a tile for a given token and set of tile ZXY coords. (OSM style)
|
|
MapController.prototype.tile = function(req, res) {
|
|
req.profiler.start('windshaft.map_tile');
|
|
this.tileOrLayer(req, res);
|
|
};
|
|
|
|
// Gets a tile for a given token, layer set of tile ZXY coords. (OSM style)
|
|
MapController.prototype.layer = function(req, res, next) {
|
|
if (req.params.token === 'static') {
|
|
return next();
|
|
}
|
|
req.profiler.start('windshaft.maplayer_tile');
|
|
this.tileOrLayer(req, res);
|
|
};
|
|
|
|
MapController.prototype.tileOrLayer = function (req, res) {
|
|
var self = this;
|
|
|
|
this._app.doCORS(res);
|
|
step(
|
|
function mapController$prepareParams() {
|
|
self._app.req2params(req, this);
|
|
},
|
|
function mapController$getTileOrGrid(err) {
|
|
req.profiler.done('req2params');
|
|
if ( err ) {
|
|
throw err;
|
|
}
|
|
self._mapBackend.getTileOrGrid(req.params, this);
|
|
},
|
|
function mapController$finalize(err, tile, headers, stats) {
|
|
req.profiler.add(stats);
|
|
self.finalizeGetTileOrGrid(err, req, res, tile, headers);
|
|
return null;
|
|
},
|
|
function finish(err) {
|
|
if ( err ) {
|
|
console.error("windshaft.tiles: " + err);
|
|
}
|
|
}
|
|
);
|
|
};
|
|
|
|
// This function is meant for being called as the very last
|
|
// step by all endpoints serving tiles or grids
|
|
MapController.prototype.finalizeGetTileOrGrid = function(err, req, res, tile, headers) {
|
|
var supportedFormats = {
|
|
grid_json: true,
|
|
json_torque: true,
|
|
torque_json: true,
|
|
png: true
|
|
};
|
|
|
|
var formatStat = 'invalid';
|
|
if (req.params.format) {
|
|
var format = req.params.format.replace('.', '_');
|
|
if (supportedFormats[format]) {
|
|
formatStat = format;
|
|
}
|
|
}
|
|
|
|
if (err){
|
|
// See https://github.com/Vizzuality/Windshaft-cartodb/issues/68
|
|
var errMsg = err.message ? ( '' + err.message ) : ( '' + err );
|
|
var statusCode = this._app.findStatusCode(err);
|
|
|
|
// Rewrite mapnik parsing errors to start with layer number
|
|
var matches = errMsg.match("(.*) in style 'layer([0-9]+)'");
|
|
if (matches) {
|
|
errMsg = 'style'+matches[2]+': ' + matches[1];
|
|
}
|
|
|
|
this._app.sendError(res, { errors: ['' + errMsg] }, statusCode, 'TILE RENDER', err);
|
|
global.statsClient.increment('windshaft.tiles.error');
|
|
global.statsClient.increment('windshaft.tiles.' + formatStat + '.error');
|
|
} else {
|
|
this._app.sendWithHeaders(res, tile, 200, headers);
|
|
global.statsClient.increment('windshaft.tiles.success');
|
|
global.statsClient.increment('windshaft.tiles.' + formatStat + '.success');
|
|
}
|
|
};
|