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

Revision 22371, 53.0 KB checked in by mazdermind, 4 years ago (diff)

add hstore-column option, which allows to create specific hstore columns for sub tags. '--hstore-column name:' will for example create a column "name:" with all the values of name:xx tags ind the form of xx=>Value for any name:xx tagged to an element. This changeset also includes changes to the regular hstore code by moving it to a separate function and also extending keyvals.c/h with an inline wrapper-function.

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        /* Discard natural=coastline tags (we render these from a shapefile instead) */
712        if (!strcmp("natural",item->key) && !strcmp("coastline",item->value))
713        {               
714            freeItem( item );
715            item = NULL;
716            add_area_tag = 1; /* Allow named islands to appear as polygons */
717            continue;
718        }   
719
720        for (i=0; i < exportListCount[type]; i++)
721        {
722            if( strcmp( exportList[type][i].name, item->key ) == 0 )
723            {
724                if( exportList[type][i].flags & FLAG_DELETE )
725                {
726                    freeItem( item );
727                    item = NULL;
728                    break;
729                }
730
731                filter = 0;
732                flags |= exportList[type][i].flags;
733
734                pushItem( &temp, item );
735                item = NULL;
736                break;
737            }
738        }
739        if( i == exportListCount[type] )
740        {
741          if (Options->enable_hstore) {
742            pushItem( &temp, item );
743            filter=0;
744          } else {
745            freeItem( item );
746          }
747          item = NULL;
748        }
749    }
750
751    /* Move from temp list back to original list */
752    while( (item = popItem(&temp)) != NULL )
753        pushItem( tags, item );
754
755    *polygon = flags & FLAG_POLYGON;
756
757    /* Special case allowing area= to override anything else */
758    if ((area = getItem(tags, "area"))) {
759        if (!strcmp(area, "yes") || !strcmp(area, "true") ||!strcmp(area, "1"))
760            *polygon = 1;
761        else if (!strcmp(area, "no") || !strcmp(area, "false") || !strcmp(area, "0"))
762            *polygon = 0;
763    } else {
764        /* If we need to force this as a polygon, append an area tag */
765        if (add_area_tag) {
766            addItem(tags, "area", "yes", 0);
767            *polygon = 1;
768        }
769    }
770
771    return filter;
772}
773
774/*
775COPY planet_osm (osm_id, name, place, landuse, leisure, "natural", man_made, waterway, highway, railway, amenity, tourism, learning, bu
776ilding, bridge, layer, way) FROM stdin;
777198497  Bedford Road    \N      \N      \N      \N      \N      \N      residential     \N      \N      \N      \N      \N      \N    \N       0102000020E610000004000000452BF702B342D5BF1C60E63BF8DF49406B9C4D470037D5BF5471E316F3DF4940DFA815A6EF35D5BF9AE95E27F5DF4940B41EB
778E4C1421D5BF24D06053E7DF4940
779212696  Oswald Road     \N      \N      \N      \N      \N      \N      minor   \N      \N      \N      \N      \N      \N      \N    0102000020E610000004000000467D923B6C22D5BFA359D93EE4DF4940B3976DA7AD11D5BF84BBB376DBDF4940997FF44D9A06D5BF4223D8B8FEDF49404D158C4AEA04D
7805BF5BB39597FCDF4940
781*/
782static int pgsql_out_way(int id, struct keyval *tags, struct osmNode *nodes, int count, int exists)
783{
784    int polygon = 0, roads = 0;
785    int i, wkt_size;
786    double split_at;
787
788    /* If the flag says this object may exist already, delete it first */
789    if(exists) {
790        pgsql_delete_way_from_output(id);
791        Options->mid->way_changed(id);
792    }
793
794    if (pgsql_filter_tags(OSMTYPE_WAY, tags, &polygon) || add_z_order(tags, &roads))
795        return 0;
796
797    // Split long ways after around 1 degree or 100km
798    if (Options->projection == PROJ_LATLONG)
799        split_at = 1;
800    else
801        split_at = 100 * 1000;
802
803    wkt_size = get_wkt_split(nodes, count, polygon, split_at);
804
805    for (i=0;i<wkt_size;i++)
806    {
807        char *wkt = get_wkt(i);
808
809        if (wkt && strlen(wkt)) {
810            /* FIXME: there should be a better way to detect polygons */
811            if (!strncmp(wkt, "POLYGON", strlen("POLYGON")) || !strncmp(wkt, "MULTIPOLYGON", strlen("MULTIPOLYGON"))) {
812                expire_tiles_from_nodes_poly(nodes, count, id);
813                double area = get_area(i);
814                if (area > 0.0) {
815                    char tmp[32];
816                    snprintf(tmp, sizeof(tmp), "%f", area);
817                    addItem(tags, "way_area", tmp, 0);
818                }
819                write_wkts(id, tags, wkt, t_poly);
820            } else {
821                expire_tiles_from_nodes_line(nodes, count);
822                write_wkts(id, tags, wkt, t_line);
823                if (roads)
824                    write_wkts(id, tags, wkt, t_roads);
825            }
826        }
827        free(wkt);
828    }
829    clear_wkts();
830       
831    return 0;
832}
833
834static int pgsql_out_relation(int id, struct keyval *rel_tags, struct osmNode **xnodes, struct keyval *xtags, int *xcount, int *xid, const char **xrole)
835{
836    int i, wkt_size;
837    int polygon = 0, roads = 0;
838    int make_polygon = 0;
839    int make_boundary = 0;
840    struct keyval tags, *p, poly_tags;
841    char *type;
842    double split_at;
843
844#if 0
845    fprintf(stderr, "Got relation with counts:");
846    for (i=0; xcount[i]; i++)
847        fprintf(stderr, " %d", xcount[i]);
848    fprintf(stderr, "\n");
849#endif
850    /* Get the type, if there's no type we don't care */
851    type = getItem(rel_tags, "type");
852    if( !type )
853        return 0;
854
855    initList(&tags);
856    initList(&poly_tags);
857
858    /* Clone tags from relation */
859    p = rel_tags->next;
860    while (p != rel_tags) {
861        // For routes, we convert name to route_name
862        if ((strcmp(type, "route") == 0) && (strcmp(p->key, "name") ==0))
863            addItem(&tags, "route_name", p->value, 1);
864        else if (strcmp(p->key, "type")) // drop type=
865            addItem(&tags, p->key, p->value, 1);
866        p = p->next;
867    }
868
869    if( strcmp(type, "route") == 0 )
870    {
871        const char *state = getItem(rel_tags, "state");
872        const char *netw = getItem(rel_tags, "network");
873        int networknr = -1;
874
875        if (state == NULL) {
876            state = "";
877        }
878
879        if (netw != NULL) {
880            if (strcmp(netw, "lcn") == 0) {
881                networknr = 10;
882                if (strcmp(state, "alternate") == 0) {
883                    addItem(&tags, "lcn", "alternate", 1);
884                } else if (strcmp(state, "connection") == 0) {
885                    addItem(&tags, "lcn", "connection", 1);
886                } else {
887                    addItem(&tags, "lcn", "yes", 1);
888                }
889            } else if (strcmp(netw, "rcn") == 0) {
890                networknr = 11;
891                if (strcmp(state, "alternate") == 0) {
892                    addItem(&tags, "rcn", "alternate", 1);
893                } else if (strcmp(state, "connection") == 0) {
894                    addItem(&tags, "rcn", "connection", 1);
895                } else {
896                    addItem(&tags, "rcn", "yes", 1);
897                }
898            } else if (strcmp(netw, "ncn") == 0) {
899                networknr = 12;
900                if (strcmp(state, "alternate") == 0) {
901                    addItem(&tags, "ncn", "alternate", 1);
902                } else if (strcmp(state, "connection") == 0) {
903                    addItem(&tags, "ncn", "connection", 1);
904                } else {
905                    addItem(&tags, "ncn", "yes", 1);
906                }
907
908
909            } else if (strcmp(netw, "lwn") == 0) {
910                networknr = 20;
911                if (strcmp(state, "alternate") == 0) {
912                    addItem(&tags, "lwn", "alternate", 1);
913                } else if (strcmp(state, "connection") == 0) {
914                    addItem(&tags, "lwn", "connection", 1);
915                } else {
916                    addItem(&tags, "lwn", "yes", 1);
917                }
918            } else if (strcmp(netw, "rwn") == 0) {
919                networknr = 21;
920                if (strcmp(state, "alternate") == 0) {
921                    addItem(&tags, "rwn", "alternate", 1);
922                } else if (strcmp(state, "connection") == 0) {
923                    addItem(&tags, "rwn", "connection", 1);
924                } else {
925                    addItem(&tags, "rwn", "yes", 1);
926                }
927            } else if (strcmp(netw, "nwn") == 0) {
928                networknr = 22;
929                if (strcmp(state, "alternate") == 0) {
930                    addItem(&tags, "nwn", "alternate", 1);
931                } else if (strcmp(state, "connection") == 0) {
932                    addItem(&tags, "nwn", "connection", 1);
933                } else {
934                    addItem(&tags, "nwn", "yes", 1);
935                }
936            }
937        }
938
939        if (getItem(rel_tags, "preferred_color") != NULL) {
940            const char *a = getItem(rel_tags, "preferred_color");
941            if (strcmp(a, "0") == 0 || strcmp(a, "1") == 0 || strcmp(a, "2") == 0 || strcmp(a, "3") == 0 || strcmp(a, "4") == 0) {
942                addItem(&tags, "route_pref_color", a, 1);
943            } else {
944                addItem(&tags, "route_pref_color", "0", 1);
945            }
946        } else {
947            addItem(&tags, "route_pref_color", "0", 1);
948        }
949
950        if (getItem(rel_tags, "ref") != NULL) {
951            if (networknr == 10) {
952                addItem(&tags, "lcn_ref", getItem(rel_tags, "ref"), 1);
953            } else if (networknr == 11) {
954                addItem(&tags, "rcn_ref", getItem(rel_tags, "ref"), 1);
955            } else if (networknr == 12) {
956                addItem(&tags, "ncn_ref", getItem(rel_tags, "ref"), 1);
957            } else if (networknr == 20) {
958                addItem(&tags, "lwn_ref", getItem(rel_tags, "ref"), 1);
959            } else if (networknr == 21) {
960                addItem(&tags, "rwn_ref", getItem(rel_tags, "ref"), 1);
961            } else if (networknr == 22) {
962                addItem(&tags, "nwn_ref", getItem(rel_tags, "ref"), 1);
963            }
964        }
965    }
966    else if( strcmp( type, "multipolygon" ) == 0 )
967    {
968        make_polygon = 1;
969
970        /* Copy the tags from the outer way(s) if the relation is untagged */
971        /* or if there is just a name tag, people seem to like naming relations */
972        if (!listHasData(&tags) || ((countList(&tags)==1) && getItem(&tags, "name"))) {
973            for (i=0; xcount[i]; i++) {
974                if (xrole[i] && !strcmp(xrole[i], "inner"))
975                    continue;
976
977                p = xtags[i].next;
978                while (p != &(xtags[i])) {
979                    addItem(&tags, p->key, p->value, 1);
980                    p = p->next;
981                }
982            }
983        }
984
985        // Collect a list of polygon-like tags, these are used later to
986        // identify if an inner rings looks like it should be rendered seperately
987        p = tags.next;
988        while (p != &tags) {
989            if (tag_indicates_polygon(OSMTYPE_WAY, p->key)) {
990                addItem(&poly_tags, p->key, p->value, 1);
991                //fprintf(stderr, "found a polygon tag: %s=%s\n", p->key, p->value);
992            }
993            p = p->next;
994        }
995    }
996    else if( strcmp( type, "boundary" ) == 0 )
997    {
998        // Boundaries will get converted into multiple geometries:
999        // - Linear features will end up in the line and roads tables (useful for admin boundaries)
1000        // - Polygon features also go into the polygon table (useful for national_forests)
1001        // The edges of the polygon also get treated as linear fetaures allowing these to be rendered seperately.
1002        make_boundary = 1;
1003    }
1004    else
1005    {
1006        /* Unknown type, just exit */
1007        resetList(&tags);
1008        resetList(&poly_tags);
1009        return 0;
1010    }
1011
1012    if (pgsql_filter_tags(OSMTYPE_WAY, &tags, &polygon) || add_z_order(&tags, &roads)) {
1013        resetList(&tags);
1014        resetList(&poly_tags);
1015        return 0;
1016    }
1017
1018    // Split long linear ways after around 1 degree or 100km (polygons not effected)
1019    if (Options->projection == PROJ_LATLONG)
1020        split_at = 1;
1021    else
1022        split_at = 100 * 1000;
1023
1024    wkt_size = build_geometry(id, xnodes, xcount, make_polygon, Options->enable_multi, split_at);
1025
1026    if (!wkt_size) {
1027        resetList(&tags);
1028        resetList(&poly_tags);
1029        return 0;
1030    }
1031
1032    for (i=0;i<wkt_size;i++)
1033    {
1034        char *wkt = get_wkt(i);
1035
1036        if (wkt && strlen(wkt)) {
1037            expire_tiles_from_wkt(wkt, -id);
1038            /* FIXME: there should be a better way to detect polygons */
1039            if (!strncmp(wkt, "POLYGON", strlen("POLYGON")) || !strncmp(wkt, "MULTIPOLYGON", strlen("MULTIPOLYGON"))) {
1040                double area = get_area(i);
1041                if (area > 0.0) {
1042                    char tmp[32];
1043                    snprintf(tmp, sizeof(tmp), "%f", area);
1044                    addItem(&tags, "way_area", tmp, 0);
1045                }
1046                write_wkts(-id, &tags, wkt, t_poly);
1047            } else {
1048                write_wkts(-id, &tags, wkt, t_line);
1049                if (roads)
1050                    write_wkts(-id, &tags, wkt, t_roads);
1051            }
1052        }
1053        free(wkt);
1054    }
1055
1056    clear_wkts();
1057
1058    // If we are creating a multipolygon then we
1059    // mark each member so that we can skip them during iterate_ways
1060    // but only if the polygon-tags look the same as the outer ring
1061    if (make_polygon) {
1062        for (i=0; xcount[i]; i++) {
1063            int match = 0;
1064            struct keyval *p = poly_tags.next;
1065            while (p != &poly_tags) {
1066                const char *v = getItem(&xtags[i], p->key);
1067                //fprintf(stderr, "compare polygon tag: %s=%s vs %s\n", p->key, p->value, v ? v : "null");
1068                if (!v || strcmp(v, p->value)) {
1069                    match = 0;
1070                    break;
1071                }
1072                match = 1;
1073                p = p->next;
1074            }
1075            if (match) {
1076                //fprintf(stderr, "match for %d\n", xid[i]);
1077                Options->mid->ways_done(xid[i]);
1078                pgsql_delete_way_from_output(xid[i]);
1079            }
1080        }
1081    }
1082
1083    // If we are making a boundary then also try adding any relations which form complete rings
1084    // The linear variants will have already been processed above
1085    if (make_boundary) {
1086        wkt_size = build_geometry(id, xnodes, xcount, 1, Options->enable_multi, split_at);
1087        for (i=0;i<wkt_size;i++)
1088        {
1089            char *wkt = get_wkt(i);
1090
1091            if (strlen(wkt)) {
1092                expire_tiles_from_wkt(wkt, -id);
1093                /* FIXME: there should be a better way to detect polygons */
1094                if (!strncmp(wkt, "POLYGON", strlen("POLYGON")) || !strncmp(wkt, "MULTIPOLYGON", strlen("MULTIPOLYGON"))) {
1095                    double area = get_area(i);
1096                    if (area > 0.0) {
1097                        char tmp[32];
1098                        snprintf(tmp, sizeof(tmp), "%f", area);
1099                        addItem(&tags, "way_area", tmp, 0);
1100                    }
1101                    write_wkts(-id, &tags, wkt, t_poly);
1102                }
1103            }
1104            free(wkt);
1105        }
1106        clear_wkts();
1107    }
1108
1109    resetList(&tags);
1110    resetList(&poly_tags);
1111    return 0;
1112}
1113
1114static int pgsql_out_start(const struct output_options *options)
1115{
1116    char *sql, tmp[256];
1117    PGresult   *res;
1118    int i,j;
1119    unsigned int sql_len;
1120
1121    Options = options;
1122
1123    read_style_file( options->style );
1124
1125    sql_len = 2048;
1126    sql = malloc(sql_len);
1127    assert(sql);
1128
1129    for (i=0; i<NUM_TABLES; i++) {
1130        PGconn *sql_conn;
1131
1132        /* Substitute prefix into name of table */
1133        {
1134            char *temp = malloc( strlen(options->prefix) + strlen(tables[i].name) + 1 );
1135            sprintf( temp, tables[i].name, options->prefix );
1136            tables[i].name = temp;
1137        }
1138        fprintf(stderr, "Setting up table: %s\n", tables[i].name);
1139        sql_conn = PQconnectdb(options->conninfo);
1140
1141        /* Check to see that the backend connection was successfully made */
1142        if (PQstatus(sql_conn) != CONNECTION_OK) {
1143            fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(sql_conn));
1144            exit_nicely();
1145        }
1146        tables[i].sql_conn = sql_conn;
1147
1148        if (!options->append) {
1149            pgsql_exec(sql_conn, PGRES_COMMAND_OK, "DROP TABLE IF EXISTS %s", tables[i].name);
1150        }
1151        else
1152        {
1153            sprintf(sql, "SELECT srid FROM geometry_columns WHERE f_table_name='%s';", tables[i].name);
1154            res = PQexec(sql_conn, sql);
1155            if (!((PQntuples(res) == 1) && (PQnfields(res) == 1)))
1156            {
1157                fprintf(stderr, "Problem reading geometry information for table %s - does it exist?\n", tables[i].name);
1158                exit_nicely();
1159            }
1160            int their_srid = atoi(PQgetvalue(res, 0, 0));
1161            PQclear(res);
1162            if (their_srid != SRID)
1163            {
1164                fprintf(stderr, "SRID mismatch: cannot append to table %s (SRID %d) using selected SRID %d\n", tables[i].name, their_srid, SRID);
1165                exit_nicely();
1166            }
1167        }
1168
1169        /* These _tmp tables can be left behind if we run out of disk space */
1170        pgsql_exec(sql_conn, PGRES_COMMAND_OK, "DROP TABLE IF EXISTS %s_tmp", tables[i].name);
1171
1172        pgsql_exec(sql_conn, PGRES_COMMAND_OK, "BEGIN");
1173
1174        enum OsmType type = (i == t_point)?OSMTYPE_NODE:OSMTYPE_WAY;
1175        int numTags = exportListCount[type];
1176        struct taginfo *exportTags = exportList[type];
1177        if (!options->append) {
1178            sprintf(sql, "CREATE TABLE %s ( osm_id int4", tables[i].name );
1179            for (j=0; j < numTags; j++) {
1180                if( exportTags[j].flags & FLAG_DELETE )
1181                    continue;
1182                if( (exportTags[j].flags & FLAG_PHSTORE ) == FLAG_PHSTORE)
1183                    continue;
1184                sprintf(tmp, ",\"%s\" %s", exportTags[j].name, exportTags[j].type);
1185                if (strlen(sql) + strlen(tmp) + 1 > sql_len) {
1186                    sql_len *= 2;
1187                    sql = realloc(sql, sql_len);
1188                    assert(sql);
1189                }
1190                strcat(sql, tmp);
1191            }
1192            int i_hstore_column;
1193            for(i_hstore_column = 0; i_hstore_column < Options->n_hstore_columns; i_hstore_column++)
1194            {
1195                strcat(sql, ",\"");
1196                strcat(sql, Options->hstore_columns[i_hstore_column]);
1197                strcat(sql, "\" hstore ");
1198            }
1199            if (Options->enable_hstore) {
1200                strcat(sql, ",tags hstore );\n");
1201            } else {
1202              strcat(sql, " );\n");
1203            }
1204
1205            pgsql_exec(sql_conn, PGRES_COMMAND_OK, "%s", sql);
1206            pgsql_exec(sql_conn, PGRES_TUPLES_OK, "SELECT AddGeometryColumn('%s', 'way', %d, '%s', 2 );\n",
1207                        tables[i].name, SRID, tables[i].type );
1208            pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ALTER TABLE %s ALTER COLUMN way SET NOT NULL;\n", tables[i].name);
1209            /* slim mode needs this to be able to apply diffs */
1210            if( Options->slim )
1211              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);
1212        } else {
1213            /* Add any new columns referenced in the default.style */
1214            PGresult *res;
1215            sprintf(sql, "SELECT * FROM %s LIMIT 0;\n", tables[i].name);
1216            res = PQexec(sql_conn, sql);
1217            if (PQresultStatus(res) != PGRES_TUPLES_OK) {
1218                fprintf(stderr, "Error, failed to query table %s\n%s\n", tables[i].name, sql);
1219                exit_nicely();
1220            }
1221            for (j=0; j < numTags; j++) {
1222                if( exportTags[j].flags & FLAG_DELETE )
1223                    continue;
1224                if( (exportTags[j].flags & FLAG_PHSTORE) == FLAG_PHSTORE)
1225                    continue;
1226                sprintf(tmp, "\"%s\"", exportTags[j].name);
1227                if (PQfnumber(res, tmp) < 0) {
1228#if 0
1229                    fprintf(stderr, "Append failed. Column \"%s\" is missing from \"%s\"\n", exportTags[j].name, tables[i].name);
1230                    exit_nicely();
1231#else
1232                    fprintf(stderr, "Adding new column \"%s\" to \"%s\"\n", exportTags[j].name, tables[i].name);
1233                    pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ALTER TABLE %s ADD COLUMN \"%s\" %s;\n", tables[i].name, exportTags[j].name, exportTags[j].type);
1234#endif
1235                }
1236                /* Note: we do not verify the type or delete unused columns */
1237            }
1238
1239            PQclear(res);
1240
1241            /* change the type of the geometry column if needed - this can only change to a more permisive type */
1242            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'",
1243                        tables[i].type, tables[i].type, tables[i].name);
1244        }
1245        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);
1246       
1247        /* Generate column list for COPY */
1248        strcpy(sql, "osm_id");
1249        for (j=0; j < numTags; j++) {
1250            if( exportTags[j].flags & FLAG_DELETE )
1251                continue;
1252            if( (exportTags[j].flags & FLAG_PHSTORE ) == FLAG_PHSTORE)
1253                    continue;
1254            sprintf(tmp, ",\"%s\"", exportTags[j].name);
1255
1256            if (strlen(sql) + strlen(tmp) + 1 > sql_len) {
1257                sql_len *= 2;
1258                sql = realloc(sql, sql_len);
1259                assert(sql);
1260            }
1261            strcat(sql, tmp);
1262        }
1263
1264        int i_hstore_column;
1265        for(i_hstore_column = 0; i_hstore_column < Options->n_hstore_columns; i_hstore_column++)
1266        {
1267            strcat(sql, ",\"");
1268            strcat(sql, Options->hstore_columns[i_hstore_column]);
1269            strcat(sql, "\" ");
1270        }
1271   
1272        if (Options->enable_hstore) strcat(sql,",tags");
1273
1274        tables[i].columns = strdup(sql);
1275        pgsql_exec(sql_conn, PGRES_COPY_IN, "COPY %s (%s,way) FROM STDIN", tables[i].name, tables[i].columns);
1276
1277        tables[i].copyMode = 1;
1278    }
1279    free(sql);
1280
1281    expire_tiles_init(options);
1282
1283    options->mid->start(options);
1284
1285    return 0;
1286}
1287
1288static void pgsql_pause_copy(struct s_table *table)
1289{
1290    PGresult   *res;
1291    if( !table->copyMode )
1292        return;
1293       
1294    /* Terminate any pending COPY */
1295    int stop = PQputCopyEnd(table->sql_conn, NULL);
1296    if (stop != 1) {
1297       fprintf(stderr, "COPY_END for %s failed: %s\n", table->name, PQerrorMessage(table->sql_conn));
1298       exit_nicely();
1299    }
1300
1301    res = PQgetResult(table->sql_conn);
1302    if (PQresultStatus(res) != PGRES_COMMAND_OK) {
1303       fprintf(stderr, "COPY_END for %s failed: %s\n", table->name, PQerrorMessage(table->sql_conn));
1304       PQclear(res);
1305       exit_nicely();
1306    }
1307    PQclear(res);
1308    table->copyMode = 0;
1309}
1310
1311static void *pgsql_out_stop_one(void *arg)
1312{
1313    struct s_table *table = arg;
1314    PGconn *sql_conn = table->sql_conn;
1315
1316    if( table->buflen != 0 )
1317    {
1318       fprintf( stderr, "Internal error: Buffer for %s has %d bytes after end copy", table->name, table->buflen );
1319       exit_nicely();
1320    }
1321
1322    pgsql_pause_copy(table);
1323    // Commit transaction
1324    fprintf(stderr, "Committing transaction for %s\n", table->name);
1325    pgsql_exec(sql_conn, PGRES_COMMAND_OK, "COMMIT");
1326    if( !Options->append )
1327    {
1328      fprintf(stderr, "Sorting data and creating indexes for %s\n", table->name);
1329      pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ANALYZE %s;\n", table->name);
1330      pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CREATE TABLE %s_tmp AS SELECT * FROM %s ORDER BY way;\n", table->name, table->name);
1331      pgsql_exec(sql_conn, PGRES_COMMAND_OK, "DROP TABLE %s;\n", table->name);
1332      pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ALTER TABLE %s_tmp RENAME TO %s;\n", table->name, table->name);
1333      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);
1334      /* slim mode needs this to be able to apply diffs */
1335      if( Options->slim )
1336        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);
1337      pgsql_exec(sql_conn, PGRES_COMMAND_OK, "GRANT SELECT ON %s TO PUBLIC;\n", table->name);
1338      pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ANALYZE %s;\n", table->name);
1339    }
1340    PQfinish(sql_conn);
1341    table->sql_conn = NULL;
1342    fprintf(stderr, "Completed %s\n", table->name);
1343    free(table->name);
1344    free(table->columns);
1345    return NULL;
1346}
1347static void pgsql_out_stop()
1348{
1349    int i;
1350#ifdef HAVE_PTHREAD
1351    pthread_t threads[NUM_TABLES];
1352#endif
1353
1354    /* Processing any remaing to be processed ways */
1355    Options->mid->iterate_ways( pgsql_out_way );
1356    Options->mid->iterate_relations( pgsql_process_relation );
1357
1358#ifdef HAVE_PTHREAD
1359    for (i=0; i<NUM_TABLES; i++) {
1360        int ret = pthread_create(&threads[i], NULL, pgsql_out_stop_one, &tables[i]);
1361        if (ret) {
1362            fprintf(stderr, "pthread_create() returned an error (%d)", ret);
1363            exit_nicely();
1364        }
1365    }
1366
1367    /* No longer need to access middle layer -- release memory */
1368    Options->mid->stop();
1369
1370    for (i=0; i<NUM_TABLES; i++) {
1371        int ret = pthread_join(threads[i], NULL);
1372        if (ret) {
1373            fprintf(stderr, "pthread_join() returned an error (%d)", ret);
1374            exit_nicely();
1375        }
1376    }
1377#else
1378    /* No longer need to access middle layer -- release memory */
1379    Options->mid->stop();
1380    for (i=0; i<NUM_TABLES; i++)
1381        pgsql_out_stop_one(&tables[i]);
1382#endif
1383
1384    pgsql_out_cleanup();
1385    free_style();
1386
1387    expire_tiles_stop();
1388}
1389
1390static int pgsql_add_node(int id, double lat, double lon, struct keyval *tags)
1391{
1392  int polygon;
1393  int filter = pgsql_filter_tags(OSMTYPE_NODE, tags, &polygon);
1394 
1395  Options->mid->nodes_set(id, lat, lon, tags);
1396  if( !filter )
1397      pgsql_out_node(id, tags, lat, lon);
1398  return 0;
1399}
1400
1401static int pgsql_add_way(int id, int *nds, int nd_count, struct keyval *tags)
1402{
1403  int polygon = 0;
1404
1405  // Check whether the way is: (1) Exportable, (2) Maybe a polygon
1406  int filter = pgsql_filter_tags(OSMTYPE_WAY, tags, &polygon);
1407
1408  // If this isn't a polygon then it can not be part of a multipolygon
1409  // Hence only polygons are "pending"
1410  Options->mid->ways_set(id, nds, nd_count, tags, (!filter && polygon) ? 1 : 0);
1411
1412  if( !polygon && !filter )
1413  {
1414    /* Get actual node data and generate output */
1415    struct osmNode *nodes = malloc( sizeof(struct osmNode) * nd_count );
1416    int count = Options->mid->nodes_get_list( nodes, nds, nd_count );
1417    pgsql_out_way(id, tags, nodes, count, 0);
1418    free(nodes);
1419  }
1420  return 0;
1421}
1422
1423/* This is the workhorse of pgsql_add_relation, split out because it is used as the callback for iterate relations */
1424static int pgsql_process_relation(int id, struct member *members, int member_count, struct keyval *tags, int exists)
1425{
1426  // (int id, struct keyval *rel_tags, struct osmNode **xnodes, struct keyval **xtags, int *xcount)
1427  int i, count;
1428  int *xid = malloc( (member_count+1) * sizeof(int) );
1429  const char **xrole = malloc( (member_count+1) * sizeof(const char *) );
1430  int *xcount = malloc( (member_count+1) * sizeof(int) );
1431  struct keyval *xtags  = malloc( (member_count+1) * sizeof(struct keyval) );
1432  struct osmNode **xnodes = malloc( (member_count+1) * sizeof(struct osmNode*) );
1433
1434  /* If the flag says this object may exist already, delete it first */
1435  if(exists)
1436      pgsql_delete_relation_from_output(id);
1437
1438  count = 0;
1439  for( i=0; i<member_count; i++ )
1440  {
1441    /* Need to handle more than just ways... */
1442    if( members[i].type != OSMTYPE_WAY )
1443        continue;
1444
1445    initList(&(xtags[count]));
1446    if( Options->mid->ways_get( members[i].id, &(xtags[count]), &(xnodes[count]), &(xcount[count]) ) )
1447      continue;
1448    xid[count] = members[i].id;
1449    xrole[count] = members[i].role;
1450    count++;
1451  }
1452  xnodes[count] = NULL;
1453  xcount[count] = 0;
1454  xid[count] = 0;
1455  xrole[count] = NULL;
1456
1457  // At some point we might want to consider storing the retreived data in the members, rather than as seperate arrays
1458  pgsql_out_relation(id, tags, xnodes, xtags, xcount, xid, xrole);
1459
1460  for( i=0; i<count; i++ )
1461  {
1462    resetList( &(xtags[i]) );
1463    free( xnodes[i] );
1464  }
1465
1466  free(xid);
1467  free(xrole);
1468  free(xcount);
1469  free(xtags);
1470  free(xnodes);
1471  return 0;
1472}
1473
1474static int pgsql_add_relation(int id, struct member *members, int member_count, struct keyval *tags)
1475{
1476  const char *type = getItem(tags, "type");
1477
1478  // Must have a type field or we ignore it
1479  if (!type)
1480      return 0;
1481
1482  /* In slim mode we remember these*/
1483  if(Options->mid->relations_set)
1484    Options->mid->relations_set(id, members, member_count, tags);
1485  // (int id, struct keyval *rel_tags, struct osmNode **xnodes, struct keyval **xtags, int *xcount)
1486
1487  return pgsql_process_relation(id, members, member_count, tags, 0);
1488}
1489#define UNUSED  __attribute__ ((unused))
1490
1491/* Delete is easy, just remove all traces of this object. We don't need to
1492 * worry about finding objects that depend on it, since the same diff must
1493 * contain the change for that also. */
1494static int pgsql_delete_node(int osm_id)
1495{
1496    if( !Options->slim )
1497    {
1498        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1499        exit_nicely();
1500    }
1501    pgsql_pause_copy(&tables[t_point]);
1502    expire_tiles_from_db(tables[t_point].sql_conn, osm_id);
1503    pgsql_exec(tables[t_point].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_point].name, osm_id );
1504    Options->mid->nodes_delete(osm_id);
1505    return 0;
1506}
1507
1508/* Seperated out because we use it elsewhere */
1509static int pgsql_delete_way_from_output(int osm_id)
1510{
1511    /* Optimisation: we only need this is slim mode */
1512    if( !Options->slim )
1513        return 0;
1514    pgsql_pause_copy(&tables[t_roads]);
1515    pgsql_pause_copy(&tables[t_line]);
1516    pgsql_pause_copy(&tables[t_poly]);
1517    expire_tiles_from_db(tables[t_roads].sql_conn, osm_id);
1518    expire_tiles_from_db(tables[t_line].sql_conn, osm_id);
1519    expire_tiles_from_db(tables[t_poly].sql_conn, osm_id);
1520    pgsql_exec(tables[t_roads].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_roads].name, osm_id );
1521    pgsql_exec(tables[t_line].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_line].name, osm_id );
1522    pgsql_exec(tables[t_poly].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_poly].name, osm_id );
1523    return 0;
1524}
1525
1526static int pgsql_delete_way(int osm_id)
1527{
1528    if( !Options->slim )
1529    {
1530        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1531        exit_nicely();
1532    }
1533    pgsql_delete_way_from_output(osm_id);
1534    Options->mid->ways_delete(osm_id);
1535    return 0;
1536}
1537
1538/* Relations are identified by using negative IDs */
1539static int pgsql_delete_relation_from_output(int osm_id)
1540{
1541    pgsql_pause_copy(&tables[t_roads]);
1542    pgsql_pause_copy(&tables[t_line]);
1543    pgsql_pause_copy(&tables[t_poly]);
1544    expire_tiles_from_db(tables[t_roads].sql_conn, -osm_id);
1545    expire_tiles_from_db(tables[t_line].sql_conn, -osm_id);
1546    expire_tiles_from_db(tables[t_poly].sql_conn, -osm_id);
1547    pgsql_exec(tables[t_roads].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_roads].name, -osm_id );
1548    pgsql_exec(tables[t_line].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_line].name, -osm_id );
1549    pgsql_exec(tables[t_poly].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_poly].name, -osm_id );
1550    return 0;
1551}
1552
1553static int pgsql_delete_relation(int osm_id)
1554{
1555    if( !Options->slim )
1556    {
1557        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1558        exit_nicely();
1559    }
1560    pgsql_delete_relation_from_output(osm_id);
1561    Options->mid->relations_delete(osm_id);
1562    return 0;
1563}
1564
1565/* Modify is slightly trickier. The basic idea is we simply delete the
1566 * object and create it with the new parameters. Then we need to mark the
1567 * objects that depend on this one */
1568static int pgsql_modify_node(int osm_id, double lat, double lon, struct keyval *tags)
1569{
1570    if( !Options->slim )
1571    {
1572        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1573        exit_nicely();
1574    }
1575    pgsql_delete_node(osm_id);
1576    pgsql_add_node(osm_id, lat, lon, tags);
1577    Options->mid->node_changed(osm_id);
1578    return 0;
1579}
1580
1581static int pgsql_modify_way(int osm_id, int *nodes, int node_count, struct keyval *tags)
1582{
1583    if( !Options->slim )
1584    {
1585        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1586        exit_nicely();
1587    }
1588    pgsql_delete_way(osm_id);
1589    pgsql_add_way(osm_id, nodes, node_count, tags);
1590    Options->mid->way_changed(osm_id);
1591    return 0;
1592}
1593
1594static int pgsql_modify_relation(int osm_id, struct member *members, int member_count, struct keyval *tags)
1595{
1596    if( !Options->slim )
1597    {
1598        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1599        exit_nicely();
1600    }
1601    pgsql_delete_relation(osm_id);
1602    pgsql_add_relation(osm_id, members, member_count, tags);
1603    Options->mid->relation_changed(osm_id);
1604    return 0;
1605}
1606
1607struct output_t out_pgsql = {
1608        start:     pgsql_out_start,
1609        stop:      pgsql_out_stop,
1610        cleanup:   pgsql_out_cleanup,
1611        node_add:      pgsql_add_node,
1612        way_add:       pgsql_add_way,
1613        relation_add:  pgsql_add_relation,
1614       
1615        node_modify: pgsql_modify_node,
1616        way_modify: pgsql_modify_way,
1617        relation_modify: pgsql_modify_relation,
1618
1619        node_delete: pgsql_delete_node,
1620        way_delete: pgsql_delete_way,
1621        relation_delete: pgsql_delete_relation
1622};
Note: See TracBrowser for help on using the repository browser.