Update osm2pgsql to remove minor memory leak of style data. Free up mid-layer memory before doing final step which only touches the final DB. Move boundary data into roads table. Document use of roads table for low-zoom features. Make final DB step multi-threaded. Update default.style to work with existing mapnik code + osm.xml (otherwise Mapnik fails to handle string/integer comparisons in admin_level).

This commit is contained in:
Jon Burgess
2008-05-26 19:33:14 +00:00
parent 7be42bacf5
commit eba5338432
3 changed files with 207 additions and 167 deletions

View File

@ -10,6 +10,7 @@ CFLAGS += $(shell xml2-config --cflags)
CFLAGS += $(shell geos-config --cflags)
CFLAGS += -I$(shell pg_config --includedir)
CFLAGS += -DVERSION=\"$(VERSION)-$(SVN)\"
CFLAGS += -DHAVE_PTHREAD
CC=gcc
CXX=g++
@ -22,6 +23,7 @@ LDFLAGS += -L$(shell pg_config --libdir) -lpq
LDFLAGS += -lbz2
LDFLAGS += -g -lproj
LDFLAGS += -lstdc++
LDFLAGS += -lpthread
SRCS:=$(wildcard *.c) $(wildcard *.cpp)
OBJS:=$(SRCS:.c=.o)

View File

@ -28,43 +28,43 @@ node,way note text delete # These tags can be long but are u
node,way source text delete # This indicates that we shouldn't store them
node,way access text linear
way,node admin_level int4 linear
node,way admin_level text linear
node,way aeroway text polygon
node,way amenity text nocache,polygon
node,way bicycle text nocache
way,node bridge text linear
way,node boundary text linear
way,node building text polygon
way,node cutting text linear
way,node embankment text linear
way,node foot text linear
way,node highway text linear
way,node horse text linear
way,node junction text linear
way,node landuse text polygon
way,node layer text linear
way,node learning text linear
way,node leisure text polygon
way,node man_made text polygon
way,node military text polygon
way,node motorcar text linear
way,node name text linear
way,node natural text polygon
way,node oneway text linear
way,node power text polygon
node,way bridge text linear
node,way boundary text linear
node,way building text polygon
node,way cutting text linear
node,way embankment text linear
node,way foot text linear
node,way highway text linear
node,way horse text linear
node,way junction text linear
node,way landuse text polygon
node,way layer text linear
node,way learning text linear
node,way leisure text polygon
node,way man_made text polygon
node,way military text polygon
node,way motorcar text linear
node,way name text linear
node,way natural text polygon
node,way oneway text linear
node,way power text polygon
node,way place text linear
node,way railway text linear
node,way ref text linear
node,way religion text nocache
way,node residence text linear
way,node route text linear
way,node sport text polygon
way,node tourism text polygon
way,node tunnel text linear
way,node waterway text linear
way,node width text linear
way,node wood text linear
way,node z_order int4 linear
node,way residence text linear
node,way route text linear
node,way sport text polygon
node,way tourism text polygon
node,way tunnel text linear
node,way waterway text linear
node,way width text linear
node,way wood text linear
node,way z_order int4 linear
way way_area real
# If you're interested in bicycle routes, you may want the following fields

View File

@ -13,6 +13,10 @@
#include <assert.h>
#include <errno.h>
#ifdef HAVE_PTHREAD
#include <pthread.h>
#endif
#include <libpq-fe.h>
#include "osmtypes.h"
@ -34,11 +38,11 @@ enum table_id {
static const struct output_options *Options;
/* Tables to output */
static struct {
static struct s_table {
//enum table_id table;
const char *name;
char *name;
const char *type;
PGconn *sql_conn;
char buffer[1024];
unsigned int buflen;
} tables [] = {
@ -47,7 +51,7 @@ static struct {
{ name: "%s_polygon", type: "POLYGON" },
{ name: "%s_roads", type: "LINESTRING"}
};
static const int num_tables = sizeof(tables)/sizeof(*tables);
#define NUM_TABLES ((signed)(sizeof(tables) / sizeof(tables[0])))
#define FLAG_POLYGON 1 /* For polygon table */
#define FLAG_LINEAR 2 /* For lines table */
@ -75,7 +79,11 @@ struct taginfo {
static struct taginfo *exportList[4]; /* Indexed by enum table_id */
static int exportListCount[4];
/* Data to generate z-order column and road table */
/* Data to generate z-order column and road table
* The name of the roads table is misleading, this table
* is used for any feature to be shown at low zoom.
* This includes railways and administrative boundaries too
*/
static struct {
int offset;
const char *highway;
@ -99,8 +107,6 @@ static struct {
static const unsigned int nLayers = (sizeof(layers)/sizeof(*layers));
static PGconn **sql_conns;
void read_style_file( char *filename )
{
FILE *in;
@ -183,6 +189,37 @@ void read_style_file( char *filename )
fclose(in);
}
static void free_style_refs(const char *name, const char *type)
{
// Find and remove any other references to these pointers
// This would be way easier if we kept a single list of styles
// Currently this scales with n^2 number of styles
int i,j;
for (i=0; i<NUM_TABLES; i++) {
for(j=0; j<exportListCount[i]; j++) {
if (exportList[i][j].name == name)
exportList[i][j].name = NULL;
if (exportList[i][j].type == type)
exportList[i][j].type = NULL;
}
}
}
static void free_style(void)
{
int i, j;
for (i=0; i<NUM_TABLES; i++) {
for(j=0; j<exportListCount[i]; j++) {
free(exportList[i][j].name);
free(exportList[i][j].type);
free_style_refs(exportList[i][j].name, exportList[i][j].type);
}
}
for (i=0; i<NUM_TABLES; i++)
free(exportList[i]);
}
/* Handles copying out, but coalesces the data into large chunks for
* efficiency. PostgreSQL doesn't actually need this, but each time you send
* a block of data you get 5 bytes of overhead. Since we go column by column
@ -191,7 +228,7 @@ void read_style_file( char *filename )
*/
void copy_to_table(enum table_id table, const char *sql)
{
PGconn *sql_conn = sql_conns[table];
PGconn *sql_conn = tables[table].sql_conn;
unsigned int len = strlen(sql);
unsigned int buflen = tables[table].buflen;
char *buffer = tables[table].buffer;
@ -202,7 +239,7 @@ void copy_to_table(enum table_id table, const char *sql)
pgsql_CopyData(tables[table].name, sql_conn, buffer);
buflen = 0;
/* If new data by itself is also too big, output it immediatly */
/* If new data by itself is also too big, output it immediately */
if( (unsigned)len > sizeof( tables[table].buffer )-10 )
{
pgsql_CopyData(tables[table].name, sql_conn, sql);
@ -231,10 +268,7 @@ static int add_z_order_polygon(struct keyval *tags, int *roads)
{
const char *natural = getItem(tags, "natural");
const char *layer = getItem(tags, "layer");
#if 0
const char *landuse = getItem(tags, "landuse");
const char *leisure = getItem(tags, "leisure");
#endif
int z_order, l;
char z[13];
@ -245,13 +279,7 @@ static int add_z_order_polygon(struct keyval *tags, int *roads)
l = layer ? strtol(layer, NULL, 10) : 0;
z_order = 10 * l;
*roads = 0;
#if 0
/* - New scheme uses layer + way area to control render order, not tags */
/* landuse & leisure tend to cover large areas and we want them under other polygons */
if (landuse) z_order -= 2;
if (leisure) z_order -= 1;
#endif
snprintf(z, sizeof(z), "%d", z_order);
addItem(tags, "z_order", z, 0);
@ -266,6 +294,7 @@ static int add_z_order_line(struct keyval *tags, int *roads)
const char *bridge = getItem(tags, "bridge");
const char *tunnel = getItem(tags, "tunnel");
const char *railway = getItem(tags, "railway");
const char *boundary= getItem(tags, "boundary");
int z_order = 0;
int l;
unsigned int i;
@ -289,6 +318,9 @@ static int add_z_order_line(struct keyval *tags, int *roads)
z_order += 5;
*roads = 1;
}
// Administrative boundaries are rendered at low zooms so we prefer to use the roads table
if (boundary && !strcmp(boundary, "administrative"))
*roads = 1;
if (bridge && (!strcmp(bridge, "true") || !strcmp(bridge, "yes") || !strcmp(bridge, "1")))
z_order += 10;
@ -369,17 +401,12 @@ static void pgsql_out_cleanup(void)
{
int i;
if (!sql_conns)
return;
for (i=0; i<num_tables; i++) {
if (sql_conns[i]) {
PQfinish(sql_conns[i]);
sql_conns[i] = NULL;
for (i=0; i<NUM_TABLES; i++) {
if (tables[i].sql_conn) {
PQfinish(tables[i].sql_conn);
tables[i].sql_conn = NULL;
}
}
free(sql_conns);
sql_conns = NULL;
}
/* Escape data appropriate to the type */
@ -803,13 +830,10 @@ static int pgsql_out_start(const struct output_options *options)
int i,j;
Options = options;
/* We use a connection per table to enable the use of COPY_IN */
sql_conns = calloc(num_tables, sizeof(PGconn *));
assert(sql_conns);
read_style_file( "default.style" );
for (i=0; i<num_tables; i++) {
for (i=0; i<NUM_TABLES; i++) {
PGconn *sql_conn;
/* Substitute prefix into name of table */
@ -826,7 +850,7 @@ static int pgsql_out_start(const struct output_options *options)
fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(sql_conn));
exit_nicely();
}
sql_conns[i] = sql_conn;
tables[i].sql_conn = sql_conn;
if (!options->append) {
sprintf( sql, "DROP TABLE %s;", tables[i].name);
@ -865,34 +889,29 @@ static int pgsql_out_start(const struct output_options *options)
return 0;
}
static void pgsql_out_stop()
static void *pgsql_out_stop_one(void *arg)
{
char sql[1024];
PGresult *res;
int i;
struct s_table *table = arg;
PGconn *sql_conn = table->sql_conn;
/* Processing any remaing to be processed ways */
Options->mid->iterate_ways( pgsql_out_way );
for (i=0; i<num_tables; i++) {
PGconn *sql_conn = sql_conns[i];
if( tables[i].buflen != 0 )
if( table->buflen != 0 )
{
fprintf( stderr, "Internal error: Buffer for %s has %d bytes after end copy", tables[i].name, tables[i].buflen );
fprintf( stderr, "Internal error: Buffer for %s has %d bytes after end copy", table->name, table->buflen );
exit_nicely();
}
/* Terminate any pending COPY */
int stop = PQputCopyEnd(sql_conn, NULL);
if (stop != 1) {
fprintf(stderr, "COPY_END for %s failed: %s\n", tables[i].name, PQerrorMessage(sql_conn));
fprintf(stderr, "COPY_END for %s failed: %s\n", table->name, PQerrorMessage(sql_conn));
exit_nicely();
}
res = PQgetResult(sql_conn);
if (PQresultStatus(res) != PGRES_COMMAND_OK) {
fprintf(stderr, "COPY_END for %s failed: %s\n", tables[i].name, PQerrorMessage(sql_conn));
fprintf(stderr, "COPY_END for %s failed: %s\n", table->name, PQerrorMessage(sql_conn));
PQclear(res);
exit_nicely();
}
@ -903,54 +922,73 @@ static void pgsql_out_stop()
sql[0] = '\0';
strcat(sql, "ANALYZE ");
strcat(sql, tables[i].name);
strcat(sql, table->name);
strcat(sql, ";\n");
strcat(sql, "CREATE TABLE tmp AS SELECT * FROM ");
strcat(sql, tables[i].name);
strcat(sql, table->name);
strcat(sql, " ORDER BY way;\n");
strcat(sql, "DROP TABLE ");
strcat(sql, tables[i].name);
strcat(sql, table->name);
strcat(sql, ";\n");
strcat(sql, "ALTER TABLE tmp RENAME TO ");
strcat(sql, tables[i].name);
strcat(sql, table->name);
strcat(sql, ";\n");
strcat(sql, "CREATE INDEX ");
strcat(sql, tables[i].name);
strcat(sql, table->name);
strcat(sql, "_index ON ");
strcat(sql, tables[i].name);
strcat(sql, table->name);
strcat(sql, " USING GIST (way GIST_GEOMETRY_OPS);\n");
strcat(sql, "GRANT SELECT ON ");
strcat(sql, tables[i].name);
strcat(sql, table->name);
strcat(sql, " TO PUBLIC;\n");
strcat(sql, "ANALYZE ");
strcat(sql, tables[i].name);
strcat(sql, table->name);
strcat(sql, ";\n");
pgsql_exec(sql_conn, sql, PGRES_COMMAND_OK);
free( (void*) tables[i].name);
free(table->name);
return NULL;
}
#if 0
for( i=0; i<exportListCount[OSMTYPE_NODE]; i++ )
static void pgsql_out_stop()
{
if( exportList[OSMTYPE_NODE][i].count == 0 )
printf( "Unused: node %s\n", exportList[OSMTYPE_NODE][i].name );
}
for( i=0; i<exportListCount[OSMTYPE_WAY]; i++ )
{
if( exportList[OSMTYPE_WAY][i].count == 0 )
printf( "Unused: way %s\n", exportList[OSMTYPE_WAY][i].name );
}
int i;
#ifdef HAVE_PTHREAD
pthread_t threads[NUM_TABLES];
#endif
pgsql_out_cleanup();
/* Processing any remaing to be processed ways */
Options->mid->iterate_ways( pgsql_out_way );
/* No longer need to access middle layer -- release memory */
Options->mid->stop();
#ifdef HAVE_PTHREAD
for (i=0; i<NUM_TABLES; i++) {
int ret = pthread_create(&threads[i], NULL, pgsql_out_stop_one, &tables[i]);
if (ret) {
fprintf(stderr, "pthread_create() returned an error (%d)", ret);
exit_nicely();
}
}
for (i=0; i<NUM_TABLES; i++) {
int ret = pthread_join(threads[i], NULL);
if (ret) {
fprintf(stderr, "pthread_join() returned an error (%d)", ret);
exit_nicely();
}
}
#else
for (i=0; i<NUM_TABLES; i++)
pgsql_out_stop_one(&tables[i]);
#endif
pgsql_out_cleanup();
free_style();
}
static int pgsql_add_node(int id, double lat, double lon, struct keyval *tags)