var step = require('step'); var assert = require('assert'); var _ = require('underscore'); var NamedMapsCacheEntry = require('../cache/model/named_maps_entry'); var cors = require('../middleware/cors'); var TablesCacheEntry = require('../cache/model/database_tables_entry'); function NamedMapsController(app, namedMapProviderCache, tileBackend, previewBackend, surrogateKeysCache, tablesExtentApi) { this.app = app; this.namedMapProviderCache = namedMapProviderCache; this.tileBackend = tileBackend; this.previewBackend = previewBackend; this.surrogateKeysCache = surrogateKeysCache; this.tablesExtentApi = tablesExtentApi; } module.exports = NamedMapsController; NamedMapsController.prototype.register = function(app) { app.get(app.base_url_templated + '/:template_id/:layer/:z/:x/:y.(:format)', cors(), this.tile.bind(this)); app.get( app.base_url_mapconfig + '/static/named/:template_id/:width/:height.:format', cors(), this.staticMap.bind(this) ); }; NamedMapsController.prototype.sendResponse = function(req, res, resource, headers, namedMapProvider) { this.surrogateKeysCache.tag(res, new NamedMapsCacheEntry(req.context.user, namedMapProvider.getTemplateName())); res.header('Content-Type', headers['Content-Type']); res.header('Cache-Control', 'public,max-age=7200,must-revalidate'); var self = this; var dbName = req.params.dbname; step( function getAffectedTablesAndLastUpdatedTime() { namedMapProvider.getAffectedTablesAndLastUpdatedTime(this); }, function sendResponse(err, result) { req.profiler.done('affectedTables'); if (err) { console.log('ERROR generating cache channel: ' + err); } if (!result || !!result.affectedTables) { // we increase cache control as we can invalidate it res.header('Cache-Control', 'public,max-age=31536000'); var lastModifiedDate; if (Number.isFinite(result.lastUpdatedTime)) { lastModifiedDate = new Date(result.lastUpdatedTime); } else { lastModifiedDate = new Date(); } res.header('Last-Modified', lastModifiedDate.toUTCString()); var tablesCacheEntry = new TablesCacheEntry(dbName, result.affectedTables); res.header('X-Cache-Channel', tablesCacheEntry.getCacheChannel()); if (result.affectedTables.length > 0) { self.surrogateKeysCache.tag(res, tablesCacheEntry); } } self.app.sendResponse(res, [resource, 200]); } ); }; NamedMapsController.prototype.tile = function(req, res) { var self = this; var cdbUser = req.context.user; var namedMapProvider; step( function reqParams() { self.app.req2params(req, this); }, function getTile() { namedMapProvider = self.namedMapProviderCache.get( cdbUser, req.params.template_id, req.query.config, req.query.auth_token, req.params ); self.tileBackend.getTile(namedMapProvider, req.params, this); }, function handleImage(err, tile, headers, stats) { if (req.profiler) { req.profiler.add(stats); } if (err) { if (!err.error) { err.error = err.message; } self.app.sendError(res, err, self.app.findStatusCode(err), 'NAMED_MAP_TILE', err); } else { self.sendResponse(req, res, tile, headers, namedMapProvider); } } ); }; NamedMapsController.prototype.staticMap = function(req, res) { var self = this; var cdbUser = req.context.user; var format = req.params.format === 'jpg' ? 'jpeg' : 'png'; req.params.format = 'png'; req.params.layer = 'all'; var namedMapProvider; step( function reqParams() { self.app.req2params(req, this); }, function getTemplate(err) { assert.ifError(err); namedMapProvider = self.namedMapProviderCache.get( cdbUser, req.params.template_id, req.query.config, req.query.auth_token, req.params ); namedMapProvider.getTemplate(this); }, function prepareStaticImageOptions(err, template) { assert.ifError(err); getStaticImageOptions(template, this); }, function estimateBounds(err, imageOpts) { assert.ifError(err); if (imageOpts) { return imageOpts; } var defaultZoomCenter = { zoom: 1, center: { lng: 0, lat: 0 } }; var cacheChannel = ''; var dbTables = cacheChannel.split(':'); if (dbTables.length <= 1 || dbTables[1].length === 0) { return defaultZoomCenter; } var tableNames = dbTables[1].split(','); if (tableNames.length === 0) { return defaultZoomCenter; } var next = this; self.tablesExtentApi.getBounds(cdbUser, tableNames, function(err, result) { next(null, result || defaultZoomCenter); }); }, function getImage(err, imageOpts) { assert.ifError(err); var width = +req.params.width; var height = +req.params.height; if (!_.isUndefined(imageOpts.zoom) && imageOpts.center) { self.previewBackend.getImage( namedMapProvider, format, width, height, imageOpts.zoom, imageOpts.center, this); } else { self.previewBackend.getImage( namedMapProvider, format, width, height, imageOpts.bounds, this); } }, function handleImage(err, image, headers, stats) { if (req.profiler) { req.profiler.done('render-' + format); req.profiler.add(stats || {}); } if (err) { if (!err.error) { err.error = err.message; } self.app.sendError(res, err, self.app.findStatusCode(err), 'STATIC_VIZ_MAP', err); } else { self.sendResponse(req, res, image, headers, namedMapProvider); } } ); }; function getStaticImageOptions(template, callback) { if (template.view) { var zoomCenter = templateZoomCenter(template.view); if (zoomCenter) { return callback(null, zoomCenter); } var bounds = templateBounds(template.view); if (bounds) { return callback(null, bounds); } } return callback(null, null); } function templateZoomCenter(view) { if (!_.isUndefined(view.zoom) && view.center) { return { zoom: view.zoom, center: view.center }; } return false; } function templateBounds(view) { if (view.bounds) { var hasAllBounds = _.every(['west', 'south', 'east', 'north'], function(prop) { return !!view.bounds[prop]; }); if (hasAllBounds) { return { bounds: { west: view.bounds.west, south: view.bounds.south, east: view.bounds.east, north: view.bounds.north } }; } else { return false; } } return false; }