CartoDB-SQL-API/lib/models/formats/pg/svg.js

164 lines
5.2 KiB
JavaScript
Raw Normal View History

2018-10-24 21:42:33 +08:00
'use strict';
2019-12-24 01:19:08 +08:00
var Pg = require('./../pg');
2013-05-16 17:24:52 +08:00
2019-12-27 00:46:27 +08:00
var svgWidth = 1024.0;
var svgHeight = 768.0;
var svgRatio = svgWidth / svgHeight;
2013-05-16 17:24:52 +08:00
2019-12-27 00:46:27 +08:00
var radius = 5; // in pixels (based on svgWidth and svgHeight)
2014-10-23 22:49:04 +08:00
2019-12-27 00:46:27 +08:00
var strokeWidth = 1; // in pixels (based on svgWidth and svgHeight)
var strokeColor = 'black';
2014-10-23 22:49:04 +08:00
// fill settings affect polygons and points (circles)
2019-12-27 00:46:27 +08:00
var fillOpacity = 0.5; // 0.0 is fully transparent, 1.0 is fully opaque
// unused if fillColor='none'
var fillColor = 'none'; // affects polygons and circles
2014-10-23 22:49:04 +08:00
2019-12-24 01:19:08 +08:00
function SvgFormat () {
2014-10-23 22:49:04 +08:00
this.totalRows = 0;
this.bbox = null; // will be computed during the results scan
this.buffer = '';
2014-10-23 22:49:04 +08:00
this._streamingStarted = false;
2014-10-23 22:49:04 +08:00
}
2019-01-16 22:58:11 +08:00
SvgFormat.prototype = new Pg('svg');
2019-12-24 01:19:08 +08:00
SvgFormat.prototype._contentType = 'image/svg+xml; charset=utf-8';
2019-12-24 01:19:08 +08:00
SvgFormat.prototype.getQuery = function (sql, options) {
var gn = options.gn;
var dp = options.dp;
return 'WITH source AS ( ' + sql + '), extent AS ( ' +
' SELECT ST_Extent(' + gn + ') AS e FROM source ' +
'), extent_info AS ( SELECT e, ' +
'st_xmin(e) as ex0, st_ymax(e) as ey0, ' +
'st_xmax(e)-st_xmin(e) as ew, ' +
'st_ymax(e)-st_ymin(e) as eh FROM extent )' +
', trans AS ( SELECT CASE WHEN ' +
2019-12-27 00:46:27 +08:00
'eh = 0 THEN ' + svgWidth +
'/ COALESCE(NULLIF(ew,0),' + svgWidth + ') WHEN ' +
svgRatio + ' <= (ew / eh) THEN (' +
svgWidth + '/ew ) ELSE (' +
svgHeight + '/eh ) END as s ' +
', ex0 as x0, ey0 as y0 FROM extent_info ) ' +
'SELECT st_TransScale(e, -x0, -y0, s, s)::box2d as ' +
gn + '_box, ST_Dimension(' + gn + ') as ' + gn +
'_dimension, ST_AsSVG(ST_TransScale(' + gn + ', ' +
'-x0, -y0, s, s), 0, ' + dp + ') as ' + gn +
2019-12-24 01:19:08 +08:00
// + ', ex0, ey0, ew, eh, s ' // DEBUG ONLY +
' FROM trans, extent_info, source' +
' ORDER BY the_geom_dimension ASC';
};
2019-12-24 01:19:08 +08:00
SvgFormat.prototype.startStreaming = function () {
if (this.opts.beforeSink) {
this.opts.beforeSink();
}
var header = [
'<?xml version="1.0" standalone="no"?>',
'<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">'
];
var rootTag = '<svg ';
2019-12-24 01:19:08 +08:00
if (this.bbox) {
// expand box by "radius" + "stroke-width"
// TODO: use a Box2d class for these ops
2019-12-27 00:46:27 +08:00
var growby = radius + strokeWidth;
this.bbox.xmin -= growby;
this.bbox.ymin -= growby;
this.bbox.xmax += growby;
this.bbox.ymax += growby;
this.bbox.width = this.bbox.xmax - this.bbox.xmin;
this.bbox.height = this.bbox.ymax - this.bbox.ymin;
rootTag += 'viewBox="' + this.bbox.xmin + ' ' + (-this.bbox.ymax) + ' ' +
this.bbox.width + ' ' + this.bbox.height + '" ';
}
2019-12-27 00:46:27 +08:00
rootTag += 'style="fill-opacity:' + fillOpacity + '; stroke:' + strokeColor + '; ' +
'stroke-width:' + strokeWidth + '; fill:' + fillColor + '" ';
rootTag += 'xmlns="http://www.w3.org/2000/svg" version="1.1">\n';
header.push(rootTag);
this.opts.sink.write(header.join('\n'));
this._streamingStarted = true;
};
2019-12-24 01:19:08 +08:00
SvgFormat.prototype.handleQueryRow = function (row) {
2014-10-23 22:49:04 +08:00
this.totalRows++;
2013-05-16 17:24:52 +08:00
2019-12-27 00:46:27 +08:00
if (!Object.prototype.hasOwnProperty.call(row, this.opts.gn)) {
2014-10-23 22:49:04 +08:00
this.error = new Error('column "' + this.opts.gn + '" does not exist');
}
2013-05-16 17:24:52 +08:00
2014-10-23 22:49:04 +08:00
var g = row[this.opts.gn];
2019-12-24 01:19:08 +08:00
if (!g) {
return;
} // null or empty
2014-10-23 22:49:04 +08:00
var gdims = row[this.opts.gn + '_dimension'];
// TODO: add an identifier, if any of "cartodb_id", "oid", "id", "gid" are found
// TODO: add "class" attribute to help with styling ?
2019-12-27 00:46:27 +08:00
if (gdims === 0) {
this.buffer += '<circle r="' + radius + '" ' + g + ' />\n';
2019-12-27 00:46:27 +08:00
} else if (gdims === 1) {
2014-10-23 22:49:04 +08:00
// Avoid filling closed linestrings
2019-12-27 00:46:27 +08:00
this.buffer += '<path ' + (fillColor !== 'none' ? 'fill="none" ' : '') + 'd="' + g + '" />\n';
} else if (gdims === 2) {
this.buffer += '<path d="' + g + '" />\n';
2014-10-23 22:49:04 +08:00
}
2013-05-16 17:24:52 +08:00
2019-12-24 01:19:08 +08:00
if (!this.bbox) {
2014-10-23 22:49:04 +08:00
// Parse layer extent: "BOX(x y, X Y)"
// NOTE: the name of the extent field is
// determined by the same code adding the
// ST_AsSVG call (in queryResult)
//
var bbox = row[this.opts.gn + '_box'];
bbox = bbox.match(/BOX\(([^ ]*) ([^ ,]*),([^ ]*) ([^)]*)\)/);
this.bbox = {
2013-05-16 17:24:52 +08:00
xmin: parseFloat(bbox[1]),
ymin: parseFloat(bbox[2]),
xmax: parseFloat(bbox[3]),
ymax: parseFloat(bbox[4])
2014-10-23 22:49:04 +08:00
};
2013-05-16 17:24:52 +08:00
}
if (!this._streamingStarted && this.bbox) {
this.startStreaming();
}
if (this._streamingStarted && (this.totalRows % (this.opts.bufferedRows || 1000))) {
this.opts.sink.write(this.buffer);
this.buffer = '';
}
2014-10-23 22:49:04 +08:00
};
2013-05-16 17:24:52 +08:00
2019-12-24 01:19:08 +08:00
SvgFormat.prototype.handleQueryEnd = function () {
if (this.error && !this._streamingStarted) {
2014-10-23 22:49:04 +08:00
this.callback(this.error);
return;
2013-05-16 17:24:52 +08:00
}
2019-12-24 01:19:08 +08:00
if (this.opts.profiler) {
2014-10-23 22:49:04 +08:00
this.opts.profiler.done('gotRows');
}
2013-05-16 17:24:52 +08:00
if (!this._streamingStarted) {
this.startStreaming();
2014-10-23 22:49:04 +08:00
}
// rootTag close
this.buffer += '</svg>\n';
this.opts.sink.write(this.buffer);
2014-10-23 22:49:04 +08:00
this.opts.sink.end();
this.callback();
};
2013-05-16 17:24:52 +08:00
2014-08-03 02:27:05 +08:00
module.exports = SvgFormat;