1
/**********************************************************************
2
* $Id: generator.c 3967 2009-05-04 16:48:11Z kneufeld $
4
* PostGIS - Spatial Types for PostgreSQL
5
* http://postgis.refractions.net
6
* Copyright 2008 Kevin Neufeld
8
* This is free software; you can redistribute and/or modify it under
9
* the terms of the GNU General Public Licence. See the COPYING file.
11
* This program will generate a .png image for every .wkt file specified
12
* in this directory's Makefile. Every .wkt file may contain several
13
* entries of geometries represented as WKT strings. Every line in
14
* a wkt file is stylized using a predetermined style (line thinkness,
15
* fill color, etc) currently hard coded in this programs main function.
17
* In order to generate a png file, ImageMagicK must be installed in the
18
* user's path as system calls are invoked to "convert". In this manner,
19
* WKT files are converted into SVG syntax and rasterized as png. (PostGIS's
20
* internal SVG methods could not be used dues to syntax issues with ImageMagick)
22
* The goal of this application is to dynamically generate all the spatial
23
* pictures used in PostGIS's documentation pages.
25
* Note: the coordinates of the supplied geometries should be within the x-y range
26
* of 200, otherwise they will appear outside of the generated image.
28
**********************************************************************/
35
#include "lwalgorithm.h"
38
#define SHOW_DIGS_DOUBLE 15
39
#define MAX_DOUBLE_PRECISION 15
40
#define MAX_DIGS_DOUBLE (SHOW_DIGS_DOUBLE + 2) /* +2 for dot and sign */
42
// Some global styling variables
43
char *imageSize = "200x200";
46
int getStyleName(char **styleName, char* line);
49
* Set up liblwgeom to run in stand-alone mode using the
50
* usual system memory handling functions.
52
void lwgeom_init_allocators(void)
54
/* liblwgeom callback - install default handlers */
55
lwgeom_install_default_allocators();
59
* Writes the coordinates of a POINTARRAY to a char* where ordinates are
60
* separated by a comma and coordinates by a space so that the coordinate
61
* pairs can be interpreted by ImageMagick's SVG draw command.
63
* @param output a reference to write the POINTARRAY to
64
* @param pa a reference to a POINTARRAY
65
* @return the numbers of character written to *output
68
pointarrayToString(char *output, POINTARRAY *pa)
70
char x[MAX_DIGS_DOUBLE+MAX_DOUBLE_PRECISION+1];
71
char y[MAX_DIGS_DOUBLE+MAX_DOUBLE_PRECISION+1];
75
for ( i=0; i < pa->npoints; i++ )
78
getPoint2d_p(pa, i, &pt);
79
sprintf(x, "%f", pt.x);
80
trim_trailing_zeros(x);
81
sprintf(y, "%f", pt.y);
82
trim_trailing_zeros(y);
83
if ( i ) ptr += sprintf(ptr, " ");
84
ptr += sprintf(ptr, "%s,%s", x, y);
87
return (ptr - output);
91
* Serializes a LWPOINT to a char*. This is a helper function that partially
92
* writes the appropriate draw and fill commands used to generate an SVG image
93
* using ImageMagick's "convert" command.
95
* @param output a char reference to write the LWPOINT to
96
* @param lwp a reference to a LWPOINT
97
* @return the numbers of character written to *output
100
drawPoint(char *output, LWPOINT *lwp, LAYERSTYLE *styles)
102
char x[MAX_DIGS_DOUBLE+MAX_DOUBLE_PRECISION+1];
103
char y1[MAX_DIGS_DOUBLE+MAX_DOUBLE_PRECISION+1];
104
char y2[MAX_DIGS_DOUBLE+MAX_DOUBLE_PRECISION+1];
106
POINTARRAY *pa = lwp->point;
108
getPoint2d_p(pa, 0, &p);
110
sprintf(x, "%f", p.x);
111
trim_trailing_zeros(x);
112
sprintf(y1, "%f", p.y);
113
trim_trailing_zeros(y1);
114
sprintf(y2, "%f", p.y + styles->pointSize);
115
trim_trailing_zeros(y2);
117
ptr += sprintf(ptr, "-fill %s -strokewidth 0 ", styles->pointColor);
118
ptr += sprintf(ptr, "-draw \"circle %s,%s %s,%s", x, y1, x, y2);
119
ptr += sprintf(ptr, "'\" ");
121
return (ptr - output);
125
* Serializes a LWLINE to a char*. This is a helper function that partially
126
* writes the appropriate draw and stroke commands used to generate an SVG image
127
* using ImageMagick's "convert" command.
129
* @param output a char reference to write the LWLINE to
130
* @param lwl a reference to a LWLINE
131
* @return the numbers of character written to *output
134
drawLineString(char *output, LWLINE *lwl, LAYERSTYLE *style)
138
ptr += sprintf(ptr, "-fill none -stroke %s -strokewidth %d ", style->lineColor, style->lineWidth);
139
ptr += sprintf(ptr, "-draw \"stroke-linecap round stroke-linejoin round path 'M ");
140
ptr += pointarrayToString(ptr, lwl->points );
141
ptr += sprintf(ptr, "'\" ");
143
return (ptr - output);
147
* Serializes a LWPOLY to a char*. This is a helper function that partially
148
* writes the appropriate draw and fill commands used to generate an SVG image
149
* using ImageMagick's "convert" command.
151
* @param output a char reference to write the LWPOLY to
152
* @param lwp a reference to a LWPOLY
153
* @return the numbers of character written to *output
156
drawPolygon(char *output, LWPOLY *lwp, LAYERSTYLE *style)
161
ptr += sprintf(ptr, "-fill %s -stroke %s -strokewidth %d ", style->polygonFillColor, style->polygonStrokeColor, style->polygonStrokeWidth );
162
ptr += sprintf(ptr, "-draw \"path '");
163
for (i=0; i<lwp->nrings; i++)
165
ptr += sprintf(ptr, "M ");
166
ptr += pointarrayToString(ptr, lwp->rings[i] );
167
ptr += sprintf(ptr, " ");
169
ptr += sprintf(ptr, "'\" ");
171
return (ptr - output);
175
* Serializes a LWGEOM to a char*. This is a helper function that partially
176
* writes the appropriate draw, stroke, and fill commands used to generate an
177
* SVG image using ImageMagick's "convert" command.
179
* @param output a char reference to write the LWGEOM to
180
* @param lwgeom a reference to a LWGEOM
181
* @return the numbers of character written to *output
184
drawGeometry(char *output, LWGEOM *lwgeom, LAYERSTYLE *styles )
188
int type = lwgeom_getType(lwgeom->type);
193
ptr += drawPoint(ptr, (LWPOINT*)lwgeom, styles );
196
ptr += drawLineString(ptr, (LWLINE*)lwgeom, styles );
199
ptr += drawPolygon(ptr, (LWPOLY*)lwgeom, styles );
203
case MULTIPOLYGONTYPE:
205
for (i=0; i<((LWCOLLECTION*)lwgeom)->ngeoms; i++)
207
ptr += drawGeometry( ptr, lwcollection_getsubgeom ((LWCOLLECTION*)lwgeom, i), styles );
212
return (ptr - output);
216
* Invokes a system call to ImageMagick's "convert" command that adds a drop
217
* shadow to the current layer image.
219
* @param layerNumber the current working layer number.
222
addDropShadow(int layerNumber)
224
// TODO: change to properly sized string
228
"convert tmp%d.png -gravity center \\( +clone -background navy -shadow 100x3+4+4 \\) +swap -background none -flatten tmp%d.png",
229
layerNumber, layerNumber);
231
LWDEBUGF(4, "%s", str);
235
* Invokes a system call to ImageMagick's "convert" command that adds a
236
* highlight to the current layer image.
238
* @param layerNumber the current working layer number.
241
addHighlight(int layerNumber)
243
// TODO: change to properly sized string
247
"convert tmp%d.png \\( +clone -channel A -separate +channel -negate -background black -virtual-pixel background -blur 0x3 -shade 120x55 -contrast-stretch 0%% +sigmoidal-contrast 7x50%% -fill grey50 -colorize 10%% +clone +swap -compose overlay -composite \\) -compose In -composite tmp%d.png",
248
layerNumber, layerNumber);
250
LWDEBUGF(4, "%s", str);
254
* Invokes a system call to ImageMagick's "convert" command that reduces
255
* the overall filesize
257
* @param filename the current working image.
260
optimizeImage(char* filename)
263
str = malloc( (18 + (2*strlen(filename)) + 1) * sizeof(char) );
264
sprintf(str, "convert %s -depth 8 %s", filename, filename);
266
LWDEBUGF(4, "%s", str);
271
* Flattens all the temporary processing png files into a single image
274
flattenLayers(char* filename)
277
str = malloc( (48 + strlen(filename) + 1) * sizeof(char) );
278
sprintf(str, "convert tmp[0-9].png -background none -flatten %s", filename);
280
LWDEBUGF(4, "%s", str);
282
// TODO: only remove the tmp files if they exist.
295
getStyleName(char **styleName, char* line)
297
char *ptr = strrchr(line, ';');
300
*styleName = malloc( 8 );
301
strncpy(*styleName, "Default", 7);
302
(*styleName)[7] = '\0';
307
*styleName = malloc( ptr - line + 1);
308
strncpy(*styleName, line, ptr - line);
309
(*styleName)[ptr - line] = '\0';
310
LWDEBUGF( 4, "%s", *styleName );
317
* Main Application. Currently, drawing styles are hardcoded in this method.
318
* Future work may entail reading the styles from a .properties file.
320
int main( int argc, const char* argv[] )
334
lwerror("You must specifiy a wkt filename to convert.\n");
338
if ( (pfile = fopen(argv[1], "r")) == NULL)
344
filename = malloc( strlen(argv[1])+11 );
345
strncpy( filename, "../images/", 10 );
346
strncat( filename, argv[1], strlen(argv[1])-3 );
347
strncat( filename, "png", 3 );
349
printf( "generating %s\n", filename );
352
while ( fgets ( line, sizeof line, pfile ) != NULL && !isspace(*line) )
360
ptr += sprintf( ptr, "convert -size %s xc:none ", imageSize );
362
useDefaultStyle = getStyleName(&styleName, line);
363
LWDEBUGF( 4, "%s", styleName );
367
printf(" Warning: using Default style for layer %d\n", layerCount);
368
lwgeom = lwgeom_from_ewkt( line, PARSER_CHECK_NONE );
371
lwgeom = lwgeom_from_ewkt( line+strlen(styleName)+1, PARSER_CHECK_NONE );
372
LWDEBUGF( 4, "geom = %s", lwgeom_to_ewkt((LWGEOM*)lwgeom,0) );
374
styleNumber = layerCount % length(styles);
375
ptr += drawGeometry( ptr, lwgeom, getStyle(styles, styleName) );
377
ptr += sprintf( ptr, "-flip tmp%d.png", layerCount );
381
LWDEBUGF( 4, "%s", output );
384
addHighlight( layerCount );
385
addDropShadow( layerCount );
390
flattenLayers(filename);
391
optimizeImage(filename);