2015-07-10 17:24:32 +08:00
|
|
|
var assert = require('assert');
|
|
|
|
var step = require('step');
|
|
|
|
|
|
|
|
var cors = require('../middleware/cors');
|
|
|
|
|
2015-07-10 18:31:56 +08:00
|
|
|
var MapStoreMapConfigProvider = require('../models/mapconfig/map_store_provider');
|
2015-07-10 17:24:32 +08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param app
|
|
|
|
* @param {MapStore} mapStore
|
|
|
|
* @param {TileBackend} tileBackend
|
|
|
|
* @param {PreviewBackend} previewBackend
|
|
|
|
* @param {AttributesBackend} attributesBackend
|
2015-07-11 01:10:55 +08:00
|
|
|
* @param {{UserLimitsApi}} userLimitsApi
|
2015-07-10 17:24:32 +08:00
|
|
|
* @constructor
|
|
|
|
*/
|
2015-07-11 01:10:55 +08:00
|
|
|
function LayergroupController(app, mapStore, tileBackend, previewBackend, attributesBackend, userLimitsApi) {
|
2015-07-10 17:24:32 +08:00
|
|
|
this.app = app;
|
|
|
|
this.mapStore = mapStore;
|
|
|
|
this.tileBackend = tileBackend;
|
|
|
|
this.previewBackend = previewBackend;
|
|
|
|
this.attributesBackend = attributesBackend;
|
2015-07-11 01:10:55 +08:00
|
|
|
this.userLimitsApi = userLimitsApi;
|
2015-07-10 17:24:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = LayergroupController;
|
|
|
|
|
|
|
|
|
|
|
|
LayergroupController.prototype.register = function(app) {
|
|
|
|
app.get(app.base_url_mapconfig + '/:token/:z/:x/:y@:scale_factor?x.:format', cors(), this.tile.bind(this));
|
|
|
|
app.get(app.base_url_mapconfig + '/:token/:z/:x/:y.:format', cors(), this.tile.bind(this));
|
|
|
|
app.get(app.base_url_mapconfig + '/:token/:layer/:z/:x/:y.(:format)', cors(), this.layer.bind(this));
|
|
|
|
app.get(app.base_url_mapconfig + '/:token/:layer/attributes/:fid', cors(), this.attributes.bind(this));
|
|
|
|
app.get(app.base_url_mapconfig + '/static/center/:token/:z/:lat/:lng/:width/:height.:format', cors(),
|
|
|
|
this.center.bind(this));
|
|
|
|
app.get(app.base_url_mapconfig + '/static/bbox/:token/:west,:south,:east,:north/:width/:height.:format', cors(),
|
|
|
|
this.bbox.bind(this));
|
|
|
|
};
|
|
|
|
|
|
|
|
LayergroupController.prototype.attributes = function(req, res) {
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
req.profiler.start('windshaft.maplayer_attribute');
|
|
|
|
|
|
|
|
step(
|
|
|
|
function setupParams() {
|
|
|
|
self.app.req2params(req, this);
|
|
|
|
},
|
|
|
|
function retrieveFeatureAttributes(err) {
|
|
|
|
req.profiler.done('req2params');
|
|
|
|
|
|
|
|
assert.ifError(err);
|
|
|
|
|
|
|
|
self.attributesBackend.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]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
// Gets a tile for a given token and set of tile ZXY coords. (OSM style)
|
|
|
|
LayergroupController.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)
|
|
|
|
LayergroupController.prototype.layer = function(req, res, next) {
|
|
|
|
if (req.params.token === 'static') {
|
|
|
|
return next();
|
|
|
|
}
|
|
|
|
req.profiler.start('windshaft.maplayer_tile');
|
|
|
|
this.tileOrLayer(req, res);
|
|
|
|
};
|
|
|
|
|
|
|
|
LayergroupController.prototype.tileOrLayer = function (req, res) {
|
|
|
|
var self = this;
|
|
|
|
|
2015-07-11 01:10:55 +08:00
|
|
|
console.log(req.context.user);
|
|
|
|
|
2015-07-10 17:24:32 +08:00
|
|
|
step(
|
|
|
|
function mapController$prepareParams() {
|
|
|
|
self.app.req2params(req, this);
|
|
|
|
},
|
|
|
|
function mapController$getTileOrGrid(err) {
|
|
|
|
req.profiler.done('req2params');
|
|
|
|
if ( err ) {
|
|
|
|
throw err;
|
|
|
|
}
|
2015-07-11 01:10:55 +08:00
|
|
|
self.tileBackend.getTile(
|
|
|
|
new MapStoreMapConfigProvider(self.mapStore, req.context.user, self.userLimitsApi, req.params),
|
|
|
|
req.params, this
|
|
|
|
);
|
2015-07-10 17:24:32 +08:00
|
|
|
},
|
|
|
|
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
|
|
|
|
LayergroupController.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');
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
LayergroupController.prototype.bbox = function(req, res) {
|
|
|
|
this.staticMap(req, res, +req.params.width, +req.params.height, {
|
|
|
|
west: +req.params.west,
|
|
|
|
north: +req.params.north,
|
|
|
|
east: +req.params.east,
|
|
|
|
south: +req.params.south
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
LayergroupController.prototype.center = function(req, res) {
|
|
|
|
this.staticMap(req, res, +req.params.width, +req.params.height, +req.params.z, {
|
|
|
|
lng: +req.params.lng,
|
|
|
|
lat: +req.params.lat
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
LayergroupController.prototype.staticMap = function(req, res, width, height, zoom /* bounds */, center) {
|
|
|
|
var format = req.params.format === 'jpg' ? 'jpeg' : 'png';
|
|
|
|
req.params.layer = 'all';
|
|
|
|
req.params.format = 'png';
|
|
|
|
|
|
|
|
var self = this;
|
|
|
|
|
|
|
|
step(
|
|
|
|
function() {
|
|
|
|
self.app.req2params(req, this);
|
|
|
|
},
|
|
|
|
function(err) {
|
|
|
|
req.profiler.done('req2params');
|
|
|
|
assert.ifError(err);
|
|
|
|
if (center) {
|
2015-07-11 01:10:55 +08:00
|
|
|
self.previewBackend.getImage(
|
|
|
|
new MapStoreMapConfigProvider(self.mapStore, req.context.user, self.userLimitsApi, req.params),
|
2015-07-10 17:24:32 +08:00
|
|
|
format, width, height, zoom, center, this);
|
|
|
|
} else {
|
2015-07-11 01:10:55 +08:00
|
|
|
self.previewBackend.getImage(
|
|
|
|
new MapStoreMapConfigProvider(self.mapStore, req.context.user, self.userLimitsApi, req.params),
|
2015-07-10 17:24:32 +08:00
|
|
|
format, width, height, zoom /* bounds */, this);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
function handleImage(err, image, headers, stats) {
|
|
|
|
req.profiler.done('render-' + format);
|
|
|
|
req.profiler.add(stats || {});
|
|
|
|
|
|
|
|
if (err) {
|
|
|
|
if (!err.error) {
|
|
|
|
err.error = err.message;
|
|
|
|
}
|
|
|
|
self.app.sendError(res, {errors: ['' + err] }, self.app.findStatusCode(err), 'STATIC_MAP', err);
|
|
|
|
} else {
|
|
|
|
res.setHeader('Content-Type', headers['Content-Type'] || 'image/' + format);
|
|
|
|
self.app.sendResponse(res, [image, 200]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
);
|
|
|
|
};
|