source: subversion/applications/utils/export/osm2pgsql/output-pgsql.c @ 24605

Revision 24605, 53.5 KB checked in by jonb, 3 years ago (diff)

osm2pgsql: Add command line switch to include natural=coastline ways in the database. Based on patch from Dane Springmeyer. Closes #3350

Line 
1/* Implements the mid-layer processing for osm2pgsql
2 * using several PostgreSQL tables
3 *
4 * This layer stores data read in from the planet.osm file
5 * and is then read by the backend processing code to
6 * emit the final geometry-enabled output formats
7*/
8
9#include <stdio.h>
10#include <unistd.h>
11#include <stdlib.h>
12#include <string.h>
13#include <assert.h>
14#include <errno.h>
15
16#ifdef HAVE_PTHREAD
17#include <pthread.h>
18#endif
19
20#include <libpq-fe.h>
21
22#include "osmtypes.h"
23#include "output.h"
24#include "reprojection.h"
25#include "output-pgsql.h"
26#include "build_geometry.h"
27#include "middle.h"
28#include "pgsql.h"
29#include "expire-tiles.h"
30
31#define SRID (project_getprojinfo()->srs)
32
33/* FIXME: Shouldn't malloc this all to begin with but call realloc()
34   as required. The program will most likely segfault if it reads a
35   style file with more styles than this */
36#define MAX_STYLES 1000
37
38enum table_id {
39    t_point, t_line, t_poly, t_roads
40};
41
42static const struct output_options *Options;
43
44/* Tables to output */
45static struct s_table {
46    //enum table_id table;
47    char *name;
48    const char *type;
49    PGconn *sql_conn;
50    char buffer[1024];
51    unsigned int buflen;
52    int copyMode;
53    char *columns;
54} tables [] = {
55    { .name = "%s_point",   .type = "POINT"     },
56    { .name = "%s_line",    .type = "LINESTRING"},
57    { .name = "%s_polygon", .type = "GEOMETRY"  }, // Actually POLGYON & MULTIPOLYGON but no way to limit to just these two
58    { .name = "%s_roads",   .type = "LINESTRING"}
59};
60#define NUM_TABLES ((signed)(sizeof(tables) / sizeof(tables[0])))
61
62#define FLAG_POLYGON 1    /* For polygon table */
63#define FLAG_LINEAR  2    /* For lines table */
64#define FLAG_NOCACHE 4    /* Optimisation: don't bother remembering this one */
65#define FLAG_DELETE  8    /* These tags should be simply deleted on sight */
66#define FLAG_PHSTORE 17   /* polygons without own column but listed in hstore this implies FLAG_POLYGON */
67static struct flagsname {
68    char *name;
69    int flag;
70} tagflags[] = {
71    { .name = "polygon",    .flag = FLAG_POLYGON },
72    { .name = "linear",     .flag = FLAG_LINEAR },
73    { .name = "nocache",    .flag = FLAG_NOCACHE },
74    { .name = "delete",     .flag = FLAG_DELETE },
75    { .name = "phstore",    .flag = FLAG_PHSTORE }
76};
77#define NUM_FLAGS ((signed)(sizeof(tagflags) / sizeof(tagflags[0])))
78
79/* Table columns, representing key= tags */
80struct taginfo {
81    char *name;
82    char *type;
83    int flags;
84    int count;
85};
86
87static struct taginfo *exportList[4]; /* Indexed by enum table_id */
88static int exportListCount[4];
89
90/* Data to generate z-order column and road table
91 * The name of the roads table is misleading, this table
92 * is used for any feature to be shown at low zoom.
93 * This includes railways and administrative boundaries too
94 */
95static struct {
96    int offset;
97    const char *highway;
98    int roads;
99} layers[] = {
100    { 3, "minor",         0 },
101    { 3, "road",          0 },
102    { 3, "unclassified",  0 },
103    { 3, "residential",   0 },
104    { 4, "tertiary_link", 0 },
105    { 4, "tertiary",      0 },
106   // 5 = railway
107    { 6, "secondary_link",1 },
108    { 6, "secondary",     1 },
109    { 7, "primary_link",  1 },
110    { 7, "primary",       1 },
111    { 8, "trunk_link",    1 },
112    { 8, "trunk",         1 },
113    { 9, "motorway_link", 1 },
114    { 9, "motorway",      1 }
115};
116static const unsigned int nLayers = (sizeof(layers)/sizeof(*layers));
117
118static int pgsql_delete_way_from_output(int osm_id);
119static int pgsql_delete_relation_from_output(int osm_id);
120static int pgsql_process_relation(int id, struct member *members, int member_count, struct keyval *tags, int exists);
121
122void read_style_file( const char *filename )
123{
124  FILE *in;
125  int lineno = 0;
126  int num_read = 0;
127
128  exportList[OSMTYPE_NODE] = malloc( sizeof(struct taginfo) * MAX_STYLES );
129  exportList[OSMTYPE_WAY]  = malloc( sizeof(struct taginfo) * MAX_STYLES );
130
131  in = fopen( filename, "rt" );
132  if( !in )
133  {
134    fprintf( stderr, "Couldn't open style file '%s': %s\n", filename, strerror(errno) );
135    exit_nicely();
136  }
137 
138  char buffer[1024];
139  while( fgets( buffer, sizeof(buffer), in) != NULL )
140  {
141    lineno++;
142   
143    char osmtype[24];
144    char tag[64];
145    char datatype[24];
146    char flags[128];
147    int i;
148    char *str;
149
150    str = strchr( buffer, '#' );
151    if( str )
152      *str = '\0';
153     
154    int fields = sscanf( buffer, "%23s %63s %23s %127s", osmtype, tag, datatype, flags );
155    if( fields <= 0 )  /* Blank line */
156      continue;
157    if( fields < 3 )
158    {
159      fprintf( stderr, "Error reading style file line %d (fields=%d)\n", lineno, fields );
160      exit_nicely();
161    }
162    struct taginfo temp;
163    temp.name = strdup(tag);
164    temp.type = strdup(datatype);
165   
166    temp.flags = 0;
167    for( str = strtok( flags, ",\r\n" ); str; str = strtok(NULL, ",\r\n") )
168    {
169      for( i=0; i<NUM_FLAGS; i++ )
170      {
171        if( strcmp( tagflags[i].name, str ) == 0 )
172        {
173          temp.flags |= tagflags[i].flag;
174          break;
175        }
176      }
177      if( i == NUM_FLAGS )
178        fprintf( stderr, "Unknown flag '%s' line %d, ignored\n", str, lineno );
179    }
180    if (temp.flags==FLAG_PHSTORE) {
181      if (0==(Options->enable_hstore)) {
182        fprintf( stderr, "Error reading style file line %d (fields=%d)\n", lineno, fields );
183        fprintf( stderr, "flag 'phstore' is invalid in non-hstore mode\n");
184        exit_nicely();
185      }
186    }
187    temp.count = 0;
188//    printf("%s %s %d %d\n", temp.name, temp.type, temp.polygon, offset );
189   
190    int flag = 0;
191    if( strstr( osmtype, "node" ) )
192    {
193      memcpy( &exportList[ OSMTYPE_NODE ][ exportListCount[ OSMTYPE_NODE ] ], &temp, sizeof(temp) );
194      exportListCount[ OSMTYPE_NODE ]++;
195      flag = 1;
196    }
197    if( strstr( osmtype, "way" ) )
198    {
199      memcpy( &exportList[ OSMTYPE_WAY ][ exportListCount[ OSMTYPE_WAY ] ], &temp, sizeof(temp) );
200      exportListCount[ OSMTYPE_WAY ]++;
201      flag = 1;
202    }
203    if( !flag )
204    {
205      fprintf( stderr, "Weird style line %d\n", lineno );
206      exit_nicely();
207    }
208    num_read++;
209  }
210  if (ferror(in)) {
211      perror(filename);
212      exit_nicely();
213  }
214  if (num_read == 0) {
215      fprintf(stderr, "Unable to parse any valid columns from the style file. Aborting.\n");
216      exit_nicely();
217  }
218  fclose(in);
219}
220
221static void free_style_refs(const char *name, const char *type)
222{
223    // Find and remove any other references to these pointers
224    // This would be way easier if we kept a single list of styles
225    // Currently this scales with n^2 number of styles
226    int i,j;
227
228    for (i=0; i<NUM_TABLES; i++) {
229        for(j=0; j<exportListCount[i]; j++) {
230            if (exportList[i][j].name == name)
231                exportList[i][j].name = NULL;
232            if (exportList[i][j].type == type)
233                exportList[i][j].type = NULL;
234        }
235    }
236}
237
238static void free_style(void)
239{
240    int i, j;
241    for (i=0; i<NUM_TABLES; i++) {
242        for(j=0; j<exportListCount[i]; j++) {
243            free(exportList[i][j].name);
244            free(exportList[i][j].type);
245            free_style_refs(exportList[i][j].name, exportList[i][j].type);
246        }
247    }
248    for (i=0; i<NUM_TABLES; i++)
249        free(exportList[i]);
250}
251
252/* Handles copying out, but coalesces the data into large chunks for
253 * efficiency. PostgreSQL doesn't actually need this, but each time you send
254 * a block of data you get 5 bytes of overhead. Since we go column by column
255 * with most empty and one byte delimiters, without this optimisation we
256 * transfer three times the amount of data necessary.
257 */
258void copy_to_table(enum table_id table, const char *sql)
259{
260    PGconn *sql_conn = tables[table].sql_conn;
261    unsigned int len = strlen(sql);
262    unsigned int buflen = tables[table].buflen;
263    char *buffer = tables[table].buffer;
264
265    /* Return to copy mode if we dropped out */
266    if( !tables[table].copyMode )
267    {
268        pgsql_exec(sql_conn, PGRES_COPY_IN, "COPY %s (%s,way) FROM STDIN", tables[table].name, tables[table].columns);
269        tables[table].copyMode = 1;
270    }
271    /* If the combination of old and new data is too big, flush old data */
272    if( (unsigned)(buflen + len) > sizeof( tables[table].buffer )-10 )
273    {
274      pgsql_CopyData(tables[table].name, sql_conn, buffer);
275      buflen = 0;
276
277      /* If new data by itself is also too big, output it immediately */
278      if( (unsigned)len > sizeof( tables[table].buffer )-10 )
279      {
280        pgsql_CopyData(tables[table].name, sql_conn, sql);
281        len = 0;
282      }
283    }
284    /* Normal case, just append to buffer */
285    if( len > 0 )
286    {
287      strcpy( buffer+buflen, sql );
288      buflen += len;
289      len = 0;
290    }
291
292    /* If we have completed a line, output it */
293    if( buflen > 0 && buffer[buflen-1] == '\n' )
294    {
295      pgsql_CopyData(tables[table].name, sql_conn, buffer);
296      buflen = 0;
297    }
298
299    tables[table].buflen = buflen;
300}
301
302static int add_z_order(struct keyval *tags, int *roads)
303{
304    const char *layer   = getItem(tags, "layer");
305    const char *highway = getItem(tags, "highway");
306    const char *bridge  = getItem(tags, "bridge");
307    const char *tunnel  = getItem(tags, "tunnel");
308    const char *railway = getItem(tags, "railway");
309    const char *boundary= getItem(tags, "boundary");
310
311    int z_order = 0;
312    int l;
313    unsigned int i;
314    char z[13];
315
316    l = layer ? strtol(layer, NULL, 10) : 0;
317    z_order = 10 * l;
318    *roads = 0;
319
320    if (highway) {
321        for (i=0; i<nLayers; i++) {
322            if (!strcmp(layers[i].highway, highway)) {
323                z_order += layers[i].offset;
324                *roads   = layers[i].roads;
325                break;
326            }
327        }
328    }
329
330    if (railway && strlen(railway)) {
331        z_order += 5;
332        *roads = 1;
333    }
334    // Administrative boundaries are rendered at low zooms so we prefer to use the roads table
335    if (boundary && !strcmp(boundary, "administrative"))
336        *roads = 1;
337
338    if (bridge && (!strcmp(bridge, "true") || !strcmp(bridge, "yes") || !strcmp(bridge, "1")))
339        z_order += 10;
340
341    if (tunnel && (!strcmp(tunnel, "true") || !strcmp(tunnel, "yes") || !strcmp(tunnel, "1")))
342        z_order -= 10;
343
344    snprintf(z, sizeof(z), "%d", z_order);
345    addItem(tags, "z_order", z, 0);
346
347    return 0;
348}
349
350#if 0
351static void fix_motorway_shields(struct keyval *tags)
352{
353    const char *highway = getItem(tags, "highway");
354    const char *name    = getItem(tags, "name");
355    const char *ref     = getItem(tags, "ref");
356
357    /* The current mapnik style uses ref= for motorway shields but some roads just have name= */
358    if (!highway || strcmp(highway, "motorway"))
359        return;
360
361    if (name && !ref)
362        addItem(tags, "ref", name, 0);
363}
364#endif
365
366/* Append all alternate name:xx on to the name tag with space sepatators.
367 * name= always comes first, the alternates are in no particular order
368 * Note: A new line may be better but this does not work with Mapnik
369 *
370 *    <tag k="name" v="Ben Nevis" />
371 *    <tag k="name:gd" v="Ben Nibheis" />
372 * becomes:
373 *    <tag k="name" v="Ben Nevis Ben Nibheis" />
374 */
375void compress_tag_name(struct keyval *tags)
376{
377    const char *name = getItem(tags, "name");
378    struct keyval *name_ext = getMatches(tags, "name:");
379    struct keyval *p;
380    char out[2048];
381
382    if (!name_ext)
383        return;
384
385    out[0] = '\0';
386    if (name) {
387        strncat(out, name, sizeof(out)-1);
388        strncat(out, " ", sizeof(out)-1);
389    }
390    while((p = popItem(name_ext)) != NULL) {
391        /* Exclude name:source = "dicataphone" and duplicates */
392        if (strcmp(p->key, "name:source") && !strstr(out, p->value)) {
393            strncat(out, p->value, sizeof(out)-1);
394            strncat(out, " ", sizeof(out)-1);
395        }
396        freeItem(p);
397    }
398    free(name_ext);
399
400    // Remove trailing space
401    out[strlen(out)-1] = '\0';
402    //fprintf(stderr, "*** New name: %s\n", out);
403    updateItem(tags, "name", out);
404}
405
406
407
408static void pgsql_out_cleanup(void)
409{
410    int i;
411
412    for (i=0; i<NUM_TABLES; i++) {
413        if (tables[i].sql_conn) {
414            PQfinish(tables[i].sql_conn);
415            tables[i].sql_conn = NULL;
416        }
417    }
418}
419
420/* Escape data appropriate to the type */
421static void escape_type(char *sql, int len, const char *value, const char *type) {
422    int items, from, to;
423
424    if ( !strcmp(type, "int4") ) {
425        /* For integers we take the first number, or the average if it's a-b */
426        items = sscanf(value, "%d-%d", &from, &to);
427        if ( items == 1 ) {
428            sprintf(sql, "%d", from);
429        } else if ( items == 2 ) {
430            sprintf(sql, "%d", (from + to) / 2);
431        } else {
432            sprintf(sql, "\\N");
433        }
434    } else {
435        escape(sql, len, value);
436    }
437}
438
439static void write_hstore(enum table_id table, struct keyval *tags)
440{
441    static char *sql;
442    static size_t sqllen=0;
443   
444    // sql buffer
445    if (sqllen==0) {
446      sqllen=2048;
447      sql=malloc(sqllen);
448    }
449   
450    // a clone of the tags pointer
451    struct keyval *xtags = tags;
452   
453    // while this tags has a follow-up..
454    while (xtags->next->key != NULL)
455    {
456        /*
457          hstore ASCII representation looks like
458          "<key>"=>"<value>"
459         
460          we need at least strlen(key)+strlen(value)+6+'\0' bytes
461          in theory any single character could also be escaped
462          thus we need an additional factor of 2.
463          The maximum lenght of a single hstore element is thus
464          calcuated as follows:
465        */
466        size_t hlen=2 * (strlen(xtags->next->key) + strlen(xtags->next->value)) + 7;
467       
468        // if the sql buffer is too small
469        if (hlen > sqllen) {
470            sqllen = hlen;
471            sql = realloc(sql, sqllen);
472        }
473       
474        // pack the tag with its value into the hstore
475        keyval2hstore(sql, xtags->next);
476        copy_to_table(table, sql);
477       
478        // update the tag-pointer to point to the next tag
479        xtags = xtags->next;
480       
481        // if the tag has a follow up, add a comma to the end
482        if (xtags->next->key != NULL)
483            copy_to_table(table, ",");
484    }
485   
486    // finish the hstore column by placing a TAB into the data stream
487    copy_to_table(table, "\t");
488   
489    // the main hstore-column has now been written
490}
491
492// write an hstore column to the database
493static void write_hstore_columns(enum table_id table, struct keyval *tags)
494{
495    static char *sql;
496    static size_t sqllen=0;
497   
498    // sql buffer
499    if (sqllen==0) {
500      sqllen=2048;
501      sql=malloc(sqllen);
502    }
503   
504    // the index of the current hstore column
505    int i_hstore_column;
506   
507    // iterate over all configured hstore colums in the options
508    for(i_hstore_column = 0; i_hstore_column < Options->n_hstore_columns; i_hstore_column++)
509    {
510        // did this node have a tag that matched the current hstore column
511        int found = 0;
512       
513        // a clone of the tags pointer
514        struct keyval *xtags = tags;
515       
516        // while this tags has a follow-up..
517        while (xtags->next->key != NULL) {
518           
519            // check if the tags' key starts with the name of the hstore column
520            char *pos = strstr(xtags->next->key, Options->hstore_columns[i_hstore_column]);
521           
522            // and if it does..
523            if(pos == xtags->next->key)
524            {
525                // remember we found one
526                found=1;
527               
528                // generate the short key name
529                char *shortkey = xtags->next->key + strlen(Options->hstore_columns[i_hstore_column]);
530               
531                // calculate the size needed for this hstore entry
532                size_t hlen=2*(strlen(shortkey)+strlen(xtags->next->value))+7;
533               
534                // if the sql buffer is too small
535                if (hlen > sqllen) {
536                    // resize it
537                    sqllen=hlen;
538                    sql=realloc(sql,sqllen);
539                }
540               
541                // and pack the shortkey with its value into the hstore
542                keyval2hstore_manual(sql, shortkey, xtags->next->value);
543                copy_to_table(table, sql);
544               
545                // update the tag-pointer to point to the next tag
546                xtags=xtags->next;
547               
548                // if the tag has a follow up, add a comma to the end
549                if (xtags->next->key != NULL)
550                    copy_to_table(table, ",");
551            }
552            else
553            {
554                // update the tag-pointer to point to the next tag
555                xtags=xtags->next;
556            }
557        }
558       
559        // if no matching tag has been found, write a NULL
560        if(!found)
561            copy_to_table(table, "\\N");
562       
563        // finish the hstore column by placing a TAB into the data stream
564        copy_to_table(table, "\t");
565    }
566   
567    // all hstore-columns have now been written
568}
569
570
571/* example from: pg_dump -F p -t planet_osm gis
572COPY planet_osm (osm_id, name, place, landuse, leisure, "natural", man_made, waterway, highway, railway, amenity, tourism, learning, building, bridge, layer, way) FROM stdin;
57317959841        \N      \N      \N      \N      \N      \N      \N      bus_stop        \N      \N      \N      \N      \N      \N    -\N      0101000020E610000030CCA462B6C3D4BF92998C9B38E04940
57417401934        The Horn        \N      \N      \N      \N      \N      \N      \N      \N      pub     \N      \N      \N      \N    -\N      0101000020E6100000C12FC937140FD5BFB4D2F4FB0CE04940
575...
576
577mine - 01 01000000 48424298424242424242424256427364
578psql - 01 01000020 E6100000 30CCA462B6C3D4BF92998C9B38E04940
579       01 01000020 E6100000 48424298424242424242424256427364
5800x2000_0000 = hasSRID, following 4 bytes = srid, not supported by geos WKBWriter
581Workaround - output SRID=4326;<WKB>
582*/
583
584static int pgsql_out_node(int id, struct keyval *tags, double node_lat, double node_lon)
585{
586
587    static char *sql;
588    static size_t sqllen=0;
589    char *v;
590    int i;
591
592    if (sqllen==0) {
593      sqllen=2048;
594      sql=malloc(sqllen);
595    }
596
597    expire_tiles_from_bbox(node_lon, node_lat, node_lon, node_lat);
598    sprintf(sql, "%d\t", id);
599    copy_to_table(t_point, sql);
600
601    for (i=0; i < exportListCount[OSMTYPE_NODE]; i++) {
602        if( exportList[OSMTYPE_NODE][i].flags & FLAG_DELETE )
603            continue;
604        if( (exportList[OSMTYPE_NODE][i].flags & FLAG_PHSTORE) == FLAG_PHSTORE)
605            continue;
606        if ((v = getItem(tags, exportList[OSMTYPE_NODE][i].name)))
607        {
608            escape_type(sql, sqllen, v, exportList[OSMTYPE_NODE][i].type);
609            exportList[OSMTYPE_NODE][i].count++;
610        }
611        else
612            sprintf(sql, "\\N");
613
614        copy_to_table(t_point, sql);
615        copy_to_table(t_point, "\t");
616    }
617   
618    // hstore columns
619    write_hstore_columns(t_point, tags);
620   
621    // check if a regular hstore is requested
622    if (Options->enable_hstore)
623        write_hstore(t_point, tags);
624   
625    sprintf(sql, "SRID=%d;POINT(%.15g %.15g)", SRID, node_lon, node_lat);
626    copy_to_table(t_point, sql);
627    copy_to_table(t_point, "\n");
628
629    return 0;
630}
631
632
633
634static void write_wkts(int id, struct keyval *tags, const char *wkt, enum table_id table)
635{
636 
637    static char *sql;
638    static size_t sqllen=0;
639    char *v;
640    int j;
641
642    if (sqllen==0) {
643      sqllen=2048;
644      sql=malloc(sqllen);
645    }
646   
647    sprintf(sql, "%d\t", id);
648    copy_to_table(table, sql);
649
650    for (j=0; j < exportListCount[OSMTYPE_WAY]; j++) {
651            if( exportList[OSMTYPE_WAY][j].flags & FLAG_DELETE )
652                continue;
653            if( (exportList[OSMTYPE_WAY][j].flags & FLAG_PHSTORE) == FLAG_PHSTORE)
654                continue;
655            if ((v = getItem(tags, exportList[OSMTYPE_WAY][j].name)))
656            {
657                exportList[OSMTYPE_WAY][j].count++;
658                escape_type(sql, sqllen, v, exportList[OSMTYPE_WAY][j].type);
659            }
660            else
661                sprintf(sql, "\\N");
662
663            copy_to_table(table, sql);
664            copy_to_table(table, "\t");
665    }
666   
667    // hstore columns
668    write_hstore_columns(table, tags);
669   
670    // check if a regular hstore is requested
671    if (Options->enable_hstore)
672        write_hstore(table, tags);
673   
674    sprintf(sql, "SRID=%d;", SRID);
675    copy_to_table(table, sql);
676    copy_to_table(table, wkt);
677    copy_to_table(table, "\n");
678}
679
680static int tag_indicates_polygon(enum OsmType type, const char *key)
681{
682    int i;
683
684    if (!strcmp(key, "area"))
685        return 1;
686
687    for (i=0; i < exportListCount[type]; i++) {
688        if( strcmp( exportList[type][i].name, key ) == 0 )
689            return exportList[type][i].flags & FLAG_POLYGON;
690    }
691
692    return 0;
693}
694
695/* Go through the given tags and determine the union of flags. Also remove
696 * any tags from the list that we don't know about */
697unsigned int pgsql_filter_tags(enum OsmType type, struct keyval *tags, int *polygon)
698{
699    int i, filter = 1;
700    int flags = 0;
701    int add_area_tag = 0;
702
703    const char *area;
704    struct keyval *item;
705    struct keyval temp;
706    initList(&temp);
707
708    /* We used to only go far enough to determine if it's a polygon or not, but now we go through and filter stuff we don't need */
709    while( (item = popItem(tags)) != NULL )
710    {
711        /* Allow named islands to appear as polygons */
712        if (!strcmp("natural",item->key) && !strcmp("coastline",item->value))
713        {               
714            add_area_tag = 1; 
715        }
716
717        /* Discard natural=coastline tags (we render these from a shapefile instead) */
718        if (!Options->keep_coastlines && !strcmp("natural",item->key) && !strcmp("coastline",item->value))
719        {               
720            freeItem( item );
721            item = NULL;
722            continue;
723        }
724
725        for (i=0; i < exportListCount[type]; i++)
726        {
727            if( strcmp( exportList[type][i].name, item->key ) == 0 )
728            {
729                if( exportList[type][i].flags & FLAG_DELETE )
730                {
731                    freeItem( item );
732                    item = NULL;
733                    break;
734                }
735
736                filter = 0;
737                flags |= exportList[type][i].flags;
738
739                pushItem( &temp, item );
740                item = NULL;
741                break;
742            }
743        }
744        if( i == exportListCount[type] )
745        {
746          if (Options->enable_hstore) {
747            pushItem( &temp, item );
748            filter=0;
749          } else {
750            freeItem( item );
751          }
752          item = NULL;
753        }
754    }
755
756    /* Move from temp list back to original list */
757    while( (item = popItem(&temp)) != NULL )
758        pushItem( tags, item );
759
760    *polygon = flags & FLAG_POLYGON;
761
762    /* Special case allowing area= to override anything else */
763    if ((area = getItem(tags, "area"))) {
764        if (!strcmp(area, "yes") || !strcmp(area, "true") ||!strcmp(area, "1"))
765            *polygon = 1;
766        else if (!strcmp(area, "no") || !strcmp(area, "false") || !strcmp(area, "0"))
767            *polygon = 0;
768    } else {
769        /* If we need to force this as a polygon, append an area tag */
770        if (add_area_tag) {
771            addItem(tags, "area", "yes", 0);
772            *polygon = 1;
773        }
774    }
775
776    return filter;
777}
778
779/*
780COPY planet_osm (osm_id, name, place, landuse, leisure, "natural", man_made, waterway, highway, railway, amenity, tourism, learning, bu
781ilding, bridge, layer, way) FROM stdin;
782198497  Bedford Road    \N      \N      \N      \N      \N      \N      residential     \N      \N      \N      \N      \N      \N    \N       0102000020E610000004000000452BF702B342D5BF1C60E63BF8DF49406B9C4D470037D5BF5471E316F3DF4940DFA815A6EF35D5BF9AE95E27F5DF4940B41EB
783E4C1421D5BF24D06053E7DF4940
784212696  Oswald Road     \N      \N      \N      \N      \N      \N      minor   \N      \N      \N      \N      \N      \N      \N    0102000020E610000004000000467D923B6C22D5BFA359D93EE4DF4940B3976DA7AD11D5BF84BBB376DBDF4940997FF44D9A06D5BF4223D8B8FEDF49404D158C4AEA04D
7855BF5BB39597FCDF4940
786*/
787static int pgsql_out_way(int id, struct keyval *tags, struct osmNode *nodes, int count, int exists)
788{
789    int polygon = 0, roads = 0;
790    int i, wkt_size;
791    double split_at;
792
793    /* If the flag says this object may exist already, delete it first */
794    if(exists) {
795        pgsql_delete_way_from_output(id);
796        Options->mid->way_changed(id);
797    }
798
799    if (pgsql_filter_tags(OSMTYPE_WAY, tags, &polygon) || add_z_order(tags, &roads))
800        return 0;
801
802    // Split long ways after around 1 degree or 100km
803    if (Options->projection == PROJ_LATLONG)
804        split_at = 1;
805    else
806        split_at = 100 * 1000;
807
808    wkt_size = get_wkt_split(nodes, count, polygon, split_at);
809
810    for (i=0;i<wkt_size;i++)
811    {
812        char *wkt = get_wkt(i);
813
814        if (wkt && strlen(wkt)) {
815            /* FIXME: there should be a better way to detect polygons */
816            if (!strncmp(wkt, "POLYGON", strlen("POLYGON")) || !strncmp(wkt, "MULTIPOLYGON", strlen("MULTIPOLYGON"))) {
817                expire_tiles_from_nodes_poly(nodes, count, id);
818                double area = get_area(i);
819                if (area > 0.0) {
820                    char tmp[32];
821                    snprintf(tmp, sizeof(tmp), "%f", area);
822                    addItem(tags, "way_area", tmp, 0);
823                }
824                write_wkts(id, tags, wkt, t_poly);
825            } else {
826                expire_tiles_from_nodes_line(nodes, count);
827                write_wkts(id, tags, wkt, t_line);
828                if (roads)
829                    write_wkts(id, tags, wkt, t_roads);
830            }
831        }
832        free(wkt);
833    }
834    clear_wkts();
835       
836    return 0;
837}
838
839static int pgsql_out_relation(int id, struct keyval *rel_tags, struct osmNode **xnodes, struct keyval *xtags, int *xcount, int *xid, const char **xrole)
840{
841    int i, wkt_size;
842    int polygon = 0, roads = 0;
843    int make_polygon = 0;
844    int make_boundary = 0;
845    struct keyval tags, *p, poly_tags;
846    char *type;
847    double split_at;
848
849#if 0
850    fprintf(stderr, "Got relation with counts:");
851    for (i=0; xcount[i]; i++)
852        fprintf(stderr, " %d", xcount[i]);
853    fprintf(stderr, "\n");
854#endif
855    /* Get the type, if there's no type we don't care */
856    type = getItem(rel_tags, "type");
857    if( !type )
858        return 0;
859
860    initList(&tags);
861    initList(&poly_tags);
862
863    /* Clone tags from relation */
864    p = rel_tags->next;
865    while (p != rel_tags) {
866        // For routes, we convert name to route_name
867        if ((strcmp(type, "route") == 0) && (strcmp(p->key, "name") ==0))
868            addItem(&tags, "route_name", p->value, 1);
869        else if (strcmp(p->key, "type")) // drop type=
870            addItem(&tags, p->key, p->value, 1);
871        p = p->next;
872    }
873
874    if( strcmp(type, "route") == 0 )
875    {
876        const char *state = getItem(rel_tags, "state");
877        const char *netw = getItem(rel_tags, "network");
878        int networknr = -1;
879
880        if (state == NULL) {
881            state = "";
882        }
883
884        if (netw != NULL) {
885            if (strcmp(netw, "lcn") == 0) {
886                networknr = 10;
887                if (strcmp(state, "alternate") == 0) {
888                    addItem(&tags, "lcn", "alternate", 1);
889                } else if (strcmp(state, "connection") == 0) {
890                    addItem(&tags, "lcn", "connection", 1);
891                } else {
892                    addItem(&tags, "lcn", "yes", 1);
893                }
894            } else if (strcmp(netw, "rcn") == 0) {
895                networknr = 11;
896                if (strcmp(state, "alternate") == 0) {
897                    addItem(&tags, "rcn", "alternate", 1);
898                } else if (strcmp(state, "connection") == 0) {
899                    addItem(&tags, "rcn", "connection", 1);
900                } else {
901                    addItem(&tags, "rcn", "yes", 1);
902                }
903            } else if (strcmp(netw, "ncn") == 0) {
904                networknr = 12;
905                if (strcmp(state, "alternate") == 0) {
906                    addItem(&tags, "ncn", "alternate", 1);
907                } else if (strcmp(state, "connection") == 0) {
908                    addItem(&tags, "ncn", "connection", 1);
909                } else {
910                    addItem(&tags, "ncn", "yes", 1);
911                }
912
913
914            } else if (strcmp(netw, "lwn") == 0) {
915                networknr = 20;
916                if (strcmp(state, "alternate") == 0) {
917                    addItem(&tags, "lwn", "alternate", 1);
918                } else if (strcmp(state, "connection") == 0) {
919                    addItem(&tags, "lwn", "connection", 1);
920                } else {
921                    addItem(&tags, "lwn", "yes", 1);
922                }
923            } else if (strcmp(netw, "rwn") == 0) {
924                networknr = 21;
925                if (strcmp(state, "alternate") == 0) {
926                    addItem(&tags, "rwn", "alternate", 1);
927                } else if (strcmp(state, "connection") == 0) {
928                    addItem(&tags, "rwn", "connection", 1);
929                } else {
930                    addItem(&tags, "rwn", "yes", 1);
931                }
932            } else if (strcmp(netw, "nwn") == 0) {
933                networknr = 22;
934                if (strcmp(state, "alternate") == 0) {
935                    addItem(&tags, "nwn", "alternate", 1);
936                } else if (strcmp(state, "connection") == 0) {
937                    addItem(&tags, "nwn", "connection", 1);
938                } else {
939                    addItem(&tags, "nwn", "yes", 1);
940                }
941            }
942        }
943
944        if (getItem(rel_tags, "preferred_color") != NULL) {
945            const char *a = getItem(rel_tags, "preferred_color");
946            if (strcmp(a, "0") == 0 || strcmp(a, "1") == 0 || strcmp(a, "2") == 0 || strcmp(a, "3") == 0 || strcmp(a, "4") == 0) {
947                addItem(&tags, "route_pref_color", a, 1);
948            } else {
949                addItem(&tags, "route_pref_color", "0", 1);
950            }
951        } else {
952            addItem(&tags, "route_pref_color", "0", 1);
953        }
954
955        if (getItem(rel_tags, "ref") != NULL) {
956            if (networknr == 10) {
957                addItem(&tags, "lcn_ref", getItem(rel_tags, "ref"), 1);
958            } else if (networknr == 11) {
959                addItem(&tags, "rcn_ref", getItem(rel_tags, "ref"), 1);
960            } else if (networknr == 12) {
961                addItem(&tags, "ncn_ref", getItem(rel_tags, "ref"), 1);
962            } else if (networknr == 20) {
963                addItem(&tags, "lwn_ref", getItem(rel_tags, "ref"), 1);
964            } else if (networknr == 21) {
965                addItem(&tags, "rwn_ref", getItem(rel_tags, "ref"), 1);
966            } else if (networknr == 22) {
967                addItem(&tags, "nwn_ref", getItem(rel_tags, "ref"), 1);
968            }
969        }
970    }
971    else if( strcmp( type, "boundary" ) == 0 )
972    {
973        // Boundaries will get converted into multiple geometries:
974        // - Linear features will end up in the line and roads tables (useful for admin boundaries)
975        // - Polygon features also go into the polygon table (useful for national_forests)
976        // The edges of the polygon also get treated as linear fetaures allowing these to be rendered seperately.
977        make_boundary = 1;
978    }
979    else if( strcmp( type, "multipolygon" ) == 0 && getItem(&tags, "boundary") )
980    {
981        // Treat type=multipolygon exactly like type=boundary if it has a boundary tag.
982        make_boundary = 1;
983    }
984    else if( strcmp( type, "multipolygon" ) == 0 )
985    {
986        make_polygon = 1;
987
988        /* Copy the tags from the outer way(s) if the relation is untagged */
989        /* or if there is just a name tag, people seem to like naming relations */
990        if (!listHasData(&tags) || ((countList(&tags)==1) && getItem(&tags, "name"))) {
991            for (i=0; xcount[i]; i++) {
992                if (xrole[i] && !strcmp(xrole[i], "inner"))
993                    continue;
994
995                p = xtags[i].next;
996                while (p != &(xtags[i])) {
997                    addItem(&tags, p->key, p->value, 1);
998                    p = p->next;
999                }
1000            }
1001        }
1002
1003        // Collect a list of polygon-like tags, these are used later to
1004        // identify if an inner rings looks like it should be rendered seperately
1005        p = tags.next;
1006        while (p != &tags) {
1007            if (tag_indicates_polygon(OSMTYPE_WAY, p->key)) {
1008                addItem(&poly_tags, p->key, p->value, 1);
1009                //fprintf(stderr, "found a polygon tag: %s=%s\n", p->key, p->value);
1010            }
1011            p = p->next;
1012        }
1013    }
1014    else
1015    {
1016        /* Unknown type, just exit */
1017        resetList(&tags);
1018        resetList(&poly_tags);
1019        return 0;
1020    }
1021
1022    if (pgsql_filter_tags(OSMTYPE_WAY, &tags, &polygon) || add_z_order(&tags, &roads)) {
1023        resetList(&tags);
1024        resetList(&poly_tags);
1025        return 0;
1026    }
1027
1028    // Split long linear ways after around 1 degree or 100km (polygons not effected)
1029    if (Options->projection == PROJ_LATLONG)
1030        split_at = 1;
1031    else
1032        split_at = 100 * 1000;
1033
1034    wkt_size = build_geometry(id, xnodes, xcount, make_polygon, Options->enable_multi, split_at);
1035
1036    if (!wkt_size) {
1037        resetList(&tags);
1038        resetList(&poly_tags);
1039        return 0;
1040    }
1041
1042    for (i=0;i<wkt_size;i++)
1043    {
1044        char *wkt = get_wkt(i);
1045
1046        if (wkt && strlen(wkt)) {
1047            expire_tiles_from_wkt(wkt, -id);
1048            /* FIXME: there should be a better way to detect polygons */
1049            if (!strncmp(wkt, "POLYGON", strlen("POLYGON")) || !strncmp(wkt, "MULTIPOLYGON", strlen("MULTIPOLYGON"))) {
1050                double area = get_area(i);
1051                if (area > 0.0) {
1052                    char tmp[32];
1053                    snprintf(tmp, sizeof(tmp), "%f", area);
1054                    addItem(&tags, "way_area", tmp, 0);
1055                }
1056                write_wkts(-id, &tags, wkt, t_poly);
1057            } else {
1058                write_wkts(-id, &tags, wkt, t_line);
1059                if (roads)
1060                    write_wkts(-id, &tags, wkt, t_roads);
1061            }
1062        }
1063        free(wkt);
1064    }
1065
1066    clear_wkts();
1067
1068    // If we are creating a multipolygon then we
1069    // mark each member so that we can skip them during iterate_ways
1070    // but only if the polygon-tags look the same as the outer ring
1071    if (make_polygon) {
1072        for (i=0; xcount[i]; i++) {
1073            int match = 0;
1074            struct keyval *p = poly_tags.next;
1075            while (p != &poly_tags) {
1076                const char *v = getItem(&xtags[i], p->key);
1077                //fprintf(stderr, "compare polygon tag: %s=%s vs %s\n", p->key, p->value, v ? v : "null");
1078                if (!v || strcmp(v, p->value)) {
1079                    match = 0;
1080                    break;
1081                }
1082                match = 1;
1083                p = p->next;
1084            }
1085            if (match) {
1086                //fprintf(stderr, "match for %d\n", xid[i]);
1087                Options->mid->ways_done(xid[i]);
1088                pgsql_delete_way_from_output(xid[i]);
1089            }
1090        }
1091    }
1092
1093    // If we are making a boundary then also try adding any relations which form complete rings
1094    // The linear variants will have already been processed above
1095    if (make_boundary) {
1096        wkt_size = build_geometry(id, xnodes, xcount, 1, Options->enable_multi, split_at);
1097        for (i=0;i<wkt_size;i++)
1098        {
1099            char *wkt = get_wkt(i);
1100
1101            if (strlen(wkt)) {
1102                expire_tiles_from_wkt(wkt, -id);
1103                /* FIXME: there should be a better way to detect polygons */
1104                if (!strncmp(wkt, "POLYGON", strlen("POLYGON")) || !strncmp(wkt, "MULTIPOLYGON", strlen("MULTIPOLYGON"))) {
1105                    double area = get_area(i);
1106                    if (area > 0.0) {
1107                        char tmp[32];
1108                        snprintf(tmp, sizeof(tmp), "%f", area);
1109                        addItem(&tags, "way_area", tmp, 0);
1110                    }
1111                    write_wkts(-id, &tags, wkt, t_poly);
1112                }
1113            }
1114            free(wkt);
1115        }
1116        clear_wkts();
1117    }
1118
1119    resetList(&tags);
1120    resetList(&poly_tags);
1121    return 0;
1122}
1123
1124static int pgsql_out_start(const struct output_options *options)
1125{
1126    char *sql, tmp[256];
1127    PGresult   *res;
1128    int i,j;
1129    unsigned int sql_len;
1130
1131    Options = options;
1132
1133    read_style_file( options->style );
1134
1135    sql_len = 2048;
1136    sql = malloc(sql_len);
1137    assert(sql);
1138
1139    for (i=0; i<NUM_TABLES; i++) {
1140        PGconn *sql_conn;
1141
1142        /* Substitute prefix into name of table */
1143        {
1144            char *temp = malloc( strlen(options->prefix) + strlen(tables[i].name) + 1 );
1145            sprintf( temp, tables[i].name, options->prefix );
1146            tables[i].name = temp;
1147        }
1148        fprintf(stderr, "Setting up table: %s\n", tables[i].name);
1149        sql_conn = PQconnectdb(options->conninfo);
1150
1151        /* Check to see that the backend connection was successfully made */
1152        if (PQstatus(sql_conn) != CONNECTION_OK) {
1153            fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(sql_conn));
1154            exit_nicely();
1155        }
1156        tables[i].sql_conn = sql_conn;
1157
1158        if (!options->append) {
1159            pgsql_exec(sql_conn, PGRES_COMMAND_OK, "DROP TABLE IF EXISTS %s", tables[i].name);
1160        }
1161        else
1162        {
1163            sprintf(sql, "SELECT srid FROM geometry_columns WHERE f_table_name='%s';", tables[i].name);
1164            res = PQexec(sql_conn, sql);
1165            if (!((PQntuples(res) == 1) && (PQnfields(res) == 1)))
1166            {
1167                fprintf(stderr, "Problem reading geometry information for table %s - does it exist?\n", tables[i].name);
1168                exit_nicely();
1169            }
1170            int their_srid = atoi(PQgetvalue(res, 0, 0));
1171            PQclear(res);
1172            if (their_srid != SRID)
1173            {
1174                fprintf(stderr, "SRID mismatch: cannot append to table %s (SRID %d) using selected SRID %d\n", tables[i].name, their_srid, SRID);
1175                exit_nicely();
1176            }
1177        }
1178
1179        /* These _tmp tables can be left behind if we run out of disk space */
1180        pgsql_exec(sql_conn, PGRES_COMMAND_OK, "DROP TABLE IF EXISTS %s_tmp", tables[i].name);
1181
1182        pgsql_exec(sql_conn, PGRES_COMMAND_OK, "BEGIN");
1183
1184        enum OsmType type = (i == t_point)?OSMTYPE_NODE:OSMTYPE_WAY;
1185        int numTags = exportListCount[type];
1186        struct taginfo *exportTags = exportList[type];
1187        if (!options->append) {
1188            sprintf(sql, "CREATE TABLE %s ( osm_id int4", tables[i].name );
1189            for (j=0; j < numTags; j++) {
1190                if( exportTags[j].flags & FLAG_DELETE )
1191                    continue;
1192                if( (exportTags[j].flags & FLAG_PHSTORE ) == FLAG_PHSTORE)
1193                    continue;
1194                sprintf(tmp, ",\"%s\" %s", exportTags[j].name, exportTags[j].type);
1195                if (strlen(sql) + strlen(tmp) + 1 > sql_len) {
1196                    sql_len *= 2;
1197                    sql = realloc(sql, sql_len);
1198                    assert(sql);
1199                }
1200                strcat(sql, tmp);
1201            }
1202            int i_hstore_column;
1203            for(i_hstore_column = 0; i_hstore_column < Options->n_hstore_columns; i_hstore_column++)
1204            {
1205                strcat(sql, ",\"");
1206                strcat(sql, Options->hstore_columns[i_hstore_column]);
1207                strcat(sql, "\" hstore ");
1208            }
1209            if (Options->enable_hstore) {
1210                strcat(sql, ",tags hstore );\n");
1211            } else {
1212              strcat(sql, " );\n");
1213            }
1214
1215            pgsql_exec(sql_conn, PGRES_COMMAND_OK, "%s", sql);
1216            pgsql_exec(sql_conn, PGRES_TUPLES_OK, "SELECT AddGeometryColumn('%s', 'way', %d, '%s', 2 );\n",
1217                        tables[i].name, SRID, tables[i].type );
1218            pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ALTER TABLE %s ALTER COLUMN way SET NOT NULL;\n", tables[i].name);
1219            /* slim mode needs this to be able to apply diffs */
1220            if( Options->slim )
1221              pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CREATE INDEX %s_pkey ON %s USING BTREE (osm_id) TABLESPACE %s;\n", tables[i].name, tables[i].name, Options->tblsindex);
1222        } else {
1223            /* Add any new columns referenced in the default.style */
1224            PGresult *res;
1225            sprintf(sql, "SELECT * FROM %s LIMIT 0;\n", tables[i].name);
1226            res = PQexec(sql_conn, sql);
1227            if (PQresultStatus(res) != PGRES_TUPLES_OK) {
1228                fprintf(stderr, "Error, failed to query table %s\n%s\n", tables[i].name, sql);
1229                exit_nicely();
1230            }
1231            for (j=0; j < numTags; j++) {
1232                if( exportTags[j].flags & FLAG_DELETE )
1233                    continue;
1234                if( (exportTags[j].flags & FLAG_PHSTORE) == FLAG_PHSTORE)
1235                    continue;
1236                sprintf(tmp, "\"%s\"", exportTags[j].name);
1237                if (PQfnumber(res, tmp) < 0) {
1238#if 0
1239                    fprintf(stderr, "Append failed. Column \"%s\" is missing from \"%s\"\n", exportTags[j].name, tables[i].name);
1240                    exit_nicely();
1241#else
1242                    fprintf(stderr, "Adding new column \"%s\" to \"%s\"\n", exportTags[j].name, tables[i].name);
1243                    pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ALTER TABLE %s ADD COLUMN \"%s\" %s;\n", tables[i].name, exportTags[j].name, exportTags[j].type);
1244#endif
1245                }
1246                /* Note: we do not verify the type or delete unused columns */
1247            }
1248
1249            PQclear(res);
1250
1251            /* change the type of the geometry column if needed - this can only change to a more permisive type */
1252            pgsql_exec(sql_conn, PGRES_COMMAND_OK, "UPDATE geometry_columns SET type = '%s' where type != '%s' and f_table_name = '%s' and f_geometry_column = 'way'",
1253                        tables[i].type, tables[i].type, tables[i].name);
1254        }
1255        pgsql_exec(sql_conn, PGRES_COMMAND_OK, "PREPARE get_way (int4) AS SELECT AsText(way) FROM %s WHERE osm_id = $1;\n", tables[i].name);
1256       
1257        /* Generate column list for COPY */
1258        strcpy(sql, "osm_id");
1259        for (j=0; j < numTags; j++) {
1260            if( exportTags[j].flags & FLAG_DELETE )
1261                continue;
1262            if( (exportTags[j].flags & FLAG_PHSTORE ) == FLAG_PHSTORE)
1263                    continue;
1264            sprintf(tmp, ",\"%s\"", exportTags[j].name);
1265
1266            if (strlen(sql) + strlen(tmp) + 1 > sql_len) {
1267                sql_len *= 2;
1268                sql = realloc(sql, sql_len);
1269                assert(sql);
1270            }
1271            strcat(sql, tmp);
1272        }
1273
1274        int i_hstore_column;
1275        for(i_hstore_column = 0; i_hstore_column < Options->n_hstore_columns; i_hstore_column++)
1276        {
1277            strcat(sql, ",\"");
1278            strcat(sql, Options->hstore_columns[i_hstore_column]);
1279            strcat(sql, "\" ");
1280        }
1281   
1282        if (Options->enable_hstore) strcat(sql,",tags");
1283
1284        tables[i].columns = strdup(sql);
1285        pgsql_exec(sql_conn, PGRES_COPY_IN, "COPY %s (%s,way) FROM STDIN", tables[i].name, tables[i].columns);
1286
1287        tables[i].copyMode = 1;
1288    }
1289    free(sql);
1290
1291    expire_tiles_init(options);
1292
1293    options->mid->start(options);
1294
1295    return 0;
1296}
1297
1298static void pgsql_pause_copy(struct s_table *table)
1299{
1300    PGresult   *res;
1301    if( !table->copyMode )
1302        return;
1303       
1304    /* Terminate any pending COPY */
1305    int stop = PQputCopyEnd(table->sql_conn, NULL);
1306    if (stop != 1) {
1307       fprintf(stderr, "COPY_END for %s failed: %s\n", table->name, PQerrorMessage(table->sql_conn));
1308       exit_nicely();
1309    }
1310
1311    res = PQgetResult(table->sql_conn);
1312    if (PQresultStatus(res) != PGRES_COMMAND_OK) {
1313       fprintf(stderr, "COPY_END for %s failed: %s\n", table->name, PQerrorMessage(table->sql_conn));
1314       PQclear(res);
1315       exit_nicely();
1316    }
1317    PQclear(res);
1318    table->copyMode = 0;
1319}
1320
1321static void *pgsql_out_stop_one(void *arg)
1322{
1323    struct s_table *table = arg;
1324    PGconn *sql_conn = table->sql_conn;
1325
1326    if( table->buflen != 0 )
1327    {
1328       fprintf( stderr, "Internal error: Buffer for %s has %d bytes after end copy", table->name, table->buflen );
1329       exit_nicely();
1330    }
1331
1332    pgsql_pause_copy(table);
1333    // Commit transaction
1334    fprintf(stderr, "Committing transaction for %s\n", table->name);
1335    pgsql_exec(sql_conn, PGRES_COMMAND_OK, "COMMIT");
1336    if( !Options->append )
1337    {
1338      fprintf(stderr, "Sorting data and creating indexes for %s\n", table->name);
1339      pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ANALYZE %s;\n", table->name);
1340      pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CREATE TABLE %s_tmp AS SELECT * FROM %s ORDER BY way;\n", table->name, table->name);
1341      pgsql_exec(sql_conn, PGRES_COMMAND_OK, "DROP TABLE %s;\n", table->name);
1342      pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ALTER TABLE %s_tmp RENAME TO %s;\n", table->name, table->name);
1343      pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CREATE INDEX %s_index ON %s USING GIST (way GIST_GEOMETRY_OPS) TABLESPACE %s;\n", table->name, table->name, Options->tblsindex);
1344      /* slim mode needs this to be able to apply diffs */
1345      if( Options->slim )
1346        pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CREATE INDEX %s_pkey ON %s USING BTREE (osm_id) TABLESPACE %s;\n", table->name, table->name, Options->tblsindex);
1347      pgsql_exec(sql_conn, PGRES_COMMAND_OK, "GRANT SELECT ON %s TO PUBLIC;\n", table->name);
1348      pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ANALYZE %s;\n", table->name);
1349    }
1350    PQfinish(sql_conn);
1351    table->sql_conn = NULL;
1352    fprintf(stderr, "Completed %s\n", table->name);
1353    free(table->name);
1354    free(table->columns);
1355    return NULL;
1356}
1357static void pgsql_out_stop()
1358{
1359    int i;
1360#ifdef HAVE_PTHREAD
1361    pthread_t threads[NUM_TABLES];
1362#endif
1363
1364    /* Processing any remaing to be processed ways */
1365    Options->mid->iterate_ways( pgsql_out_way );
1366    Options->mid->iterate_relations( pgsql_process_relation );
1367
1368#ifdef HAVE_PTHREAD
1369    for (i=0; i<NUM_TABLES; i++) {
1370        int ret = pthread_create(&threads[i], NULL, pgsql_out_stop_one, &tables[i]);
1371        if (ret) {
1372            fprintf(stderr, "pthread_create() returned an error (%d)", ret);
1373            exit_nicely();
1374        }
1375    }
1376
1377    /* No longer need to access middle layer -- release memory */
1378    Options->mid->stop();
1379
1380    for (i=0; i<NUM_TABLES; i++) {
1381        int ret = pthread_join(threads[i], NULL);
1382        if (ret) {
1383            fprintf(stderr, "pthread_join() returned an error (%d)", ret);
1384            exit_nicely();
1385        }
1386    }
1387#else
1388    /* No longer need to access middle layer -- release memory */
1389    Options->mid->stop();
1390    for (i=0; i<NUM_TABLES; i++)
1391        pgsql_out_stop_one(&tables[i]);
1392#endif
1393
1394    pgsql_out_cleanup();
1395    free_style();
1396
1397    expire_tiles_stop();
1398}
1399
1400static int pgsql_add_node(int id, double lat, double lon, struct keyval *tags)
1401{
1402  int polygon;
1403  int filter = pgsql_filter_tags(OSMTYPE_NODE, tags, &polygon);
1404 
1405  Options->mid->nodes_set(id, lat, lon, tags);
1406  if( !filter )
1407      pgsql_out_node(id, tags, lat, lon);
1408  return 0;
1409}
1410
1411static int pgsql_add_way(int id, int *nds, int nd_count, struct keyval *tags)
1412{
1413  int polygon = 0;
1414
1415  // Check whether the way is: (1) Exportable, (2) Maybe a polygon
1416  int filter = pgsql_filter_tags(OSMTYPE_WAY, tags, &polygon);
1417
1418  // If this isn't a polygon then it can not be part of a multipolygon
1419  // Hence only polygons are "pending"
1420  Options->mid->ways_set(id, nds, nd_count, tags, (!filter && polygon) ? 1 : 0);
1421
1422  if( !polygon && !filter )
1423  {
1424    /* Get actual node data and generate output */
1425    struct osmNode *nodes = malloc( sizeof(struct osmNode) * nd_count );
1426    int count = Options->mid->nodes_get_list( nodes, nds, nd_count );
1427    pgsql_out_way(id, tags, nodes, count, 0);
1428    free(nodes);
1429  }
1430  return 0;
1431}
1432
1433/* This is the workhorse of pgsql_add_relation, split out because it is used as the callback for iterate relations */
1434static int pgsql_process_relation(int id, struct member *members, int member_count, struct keyval *tags, int exists)
1435{
1436  // (int id, struct keyval *rel_tags, struct osmNode **xnodes, struct keyval **xtags, int *xcount)
1437  int i, count;
1438  int *xid = malloc( (member_count+1) * sizeof(int) );
1439  const char **xrole = malloc( (member_count+1) * sizeof(const char *) );
1440  int *xcount = malloc( (member_count+1) * sizeof(int) );
1441  struct keyval *xtags  = malloc( (member_count+1) * sizeof(struct keyval) );
1442  struct osmNode **xnodes = malloc( (member_count+1) * sizeof(struct osmNode*) );
1443
1444  /* If the flag says this object may exist already, delete it first */
1445  if(exists)
1446      pgsql_delete_relation_from_output(id);
1447
1448  count = 0;
1449  for( i=0; i<member_count; i++ )
1450  {
1451    /* Need to handle more than just ways... */
1452    if( members[i].type != OSMTYPE_WAY )
1453        continue;
1454
1455    initList(&(xtags[count]));
1456    if( Options->mid->ways_get( members[i].id, &(xtags[count]), &(xnodes[count]), &(xcount[count]) ) )
1457      continue;
1458    xid[count] = members[i].id;
1459    xrole[count] = members[i].role;
1460    count++;
1461  }
1462  xnodes[count] = NULL;
1463  xcount[count] = 0;
1464  xid[count] = 0;
1465  xrole[count] = NULL;
1466
1467  // At some point we might want to consider storing the retreived data in the members, rather than as seperate arrays
1468  pgsql_out_relation(id, tags, xnodes, xtags, xcount, xid, xrole);
1469
1470  for( i=0; i<count; i++ )
1471  {
1472    resetList( &(xtags[i]) );
1473    free( xnodes[i] );
1474  }
1475
1476  free(xid);
1477  free(xrole);
1478  free(xcount);
1479  free(xtags);
1480  free(xnodes);
1481  return 0;
1482}
1483
1484static int pgsql_add_relation(int id, struct member *members, int member_count, struct keyval *tags)
1485{
1486  const char *type = getItem(tags, "type");
1487
1488  // Must have a type field or we ignore it
1489  if (!type)
1490      return 0;
1491
1492  /* In slim mode we remember these*/
1493  if(Options->mid->relations_set)
1494    Options->mid->relations_set(id, members, member_count, tags);
1495  // (int id, struct keyval *rel_tags, struct osmNode **xnodes, struct keyval **xtags, int *xcount)
1496
1497  return pgsql_process_relation(id, members, member_count, tags, 0);
1498}
1499#define UNUSED  __attribute__ ((unused))
1500
1501/* Delete is easy, just remove all traces of this object. We don't need to
1502 * worry about finding objects that depend on it, since the same diff must
1503 * contain the change for that also. */
1504static int pgsql_delete_node(int osm_id)
1505{
1506    if( !Options->slim )
1507    {
1508        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1509        exit_nicely();
1510    }
1511    pgsql_pause_copy(&tables[t_point]);
1512    expire_tiles_from_db(tables[t_point].sql_conn, osm_id);
1513    pgsql_exec(tables[t_point].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_point].name, osm_id );
1514    Options->mid->nodes_delete(osm_id);
1515    return 0;
1516}
1517
1518/* Seperated out because we use it elsewhere */
1519static int pgsql_delete_way_from_output(int osm_id)
1520{
1521    /* Optimisation: we only need this is slim mode */
1522    if( !Options->slim )
1523        return 0;
1524    pgsql_pause_copy(&tables[t_roads]);
1525    pgsql_pause_copy(&tables[t_line]);
1526    pgsql_pause_copy(&tables[t_poly]);
1527    expire_tiles_from_db(tables[t_roads].sql_conn, osm_id);
1528    expire_tiles_from_db(tables[t_line].sql_conn, osm_id);
1529    expire_tiles_from_db(tables[t_poly].sql_conn, osm_id);
1530    pgsql_exec(tables[t_roads].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_roads].name, osm_id );
1531    pgsql_exec(tables[t_line].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_line].name, osm_id );
1532    pgsql_exec(tables[t_poly].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_poly].name, osm_id );
1533    return 0;
1534}
1535
1536static int pgsql_delete_way(int osm_id)
1537{
1538    if( !Options->slim )
1539    {
1540        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1541        exit_nicely();
1542    }
1543    pgsql_delete_way_from_output(osm_id);
1544    Options->mid->ways_delete(osm_id);
1545    return 0;
1546}
1547
1548/* Relations are identified by using negative IDs */
1549static int pgsql_delete_relation_from_output(int osm_id)
1550{
1551    pgsql_pause_copy(&tables[t_roads]);
1552    pgsql_pause_copy(&tables[t_line]);
1553    pgsql_pause_copy(&tables[t_poly]);
1554    expire_tiles_from_db(tables[t_roads].sql_conn, -osm_id);
1555    expire_tiles_from_db(tables[t_line].sql_conn, -osm_id);
1556    expire_tiles_from_db(tables[t_poly].sql_conn, -osm_id);
1557    pgsql_exec(tables[t_roads].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_roads].name, -osm_id );
1558    pgsql_exec(tables[t_line].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_line].name, -osm_id );
1559    pgsql_exec(tables[t_poly].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_poly].name, -osm_id );
1560    return 0;
1561}
1562
1563static int pgsql_delete_relation(int osm_id)
1564{
1565    if( !Options->slim )
1566    {
1567        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1568        exit_nicely();
1569    }
1570    pgsql_delete_relation_from_output(osm_id);
1571    Options->mid->relations_delete(osm_id);
1572    return 0;
1573}
1574
1575/* Modify is slightly trickier. The basic idea is we simply delete the
1576 * object and create it with the new parameters. Then we need to mark the
1577 * objects that depend on this one */
1578static int pgsql_modify_node(int osm_id, double lat, double lon, struct keyval *tags)
1579{
1580    if( !Options->slim )
1581    {
1582        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1583        exit_nicely();
1584    }
1585    pgsql_delete_node(osm_id);
1586    pgsql_add_node(osm_id, lat, lon, tags);
1587    Options->mid->node_changed(osm_id);
1588    return 0;
1589}
1590
1591static int pgsql_modify_way(int osm_id, int *nodes, int node_count, struct keyval *tags)
1592{
1593    if( !Options->slim )
1594    {
1595        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1596        exit_nicely();
1597    }
1598    pgsql_delete_way(osm_id);
1599    pgsql_add_way(osm_id, nodes, node_count, tags);
1600    Options->mid->way_changed(osm_id);
1601    return 0;
1602}
1603
1604static int pgsql_modify_relation(int osm_id, struct member *members, int member_count, struct keyval *tags)
1605{
1606    if( !Options->slim )
1607    {
1608        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1609        exit_nicely();
1610    }
1611    pgsql_delete_relation(osm_id);
1612    pgsql_add_relation(osm_id, members, member_count, tags);
1613    Options->mid->relation_changed(osm_id);
1614    return 0;
1615}
1616
1617struct output_t out_pgsql = {
1618        .start           = pgsql_out_start,
1619        .stop            = pgsql_out_stop,
1620        .cleanup         = pgsql_out_cleanup,
1621        .node_add        = pgsql_add_node,
1622        .way_add         = pgsql_add_way,
1623        .relation_add    = pgsql_add_relation,
1624       
1625        .node_modify     = pgsql_modify_node,
1626        .way_modify      = pgsql_modify_way,
1627        .relation_modify = pgsql_modify_relation,
1628
1629        .node_delete     = pgsql_delete_node,
1630        .way_delete      = pgsql_delete_way,
1631        .relation_delete = pgsql_delete_relation
1632};
Note: See TracBrowser for help on using the repository browser.