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

Last change on this file since 9267 was 9267, checked in by jonb, 12 years ago

osm2pgsql: Comment out debug lines

File size: 40.1 KB
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
30#define SRID (project_getprojinfo()->srs)
31
32#define MAX_STYLES 100
33
34enum table_id {
35    t_point, t_line, t_poly, t_roads
36};
37
38static const struct output_options *Options;
39
40/* Tables to output */
41static struct s_table {
42    //enum table_id table;
43    char *name;
44    const char *type;
45    PGconn *sql_conn;
46    char buffer[1024];
47    unsigned int buflen;
48    int copyMode;
49} tables [] = {
50    { name: "%s_point",   type: "POINT"     },
51    { name: "%s_line",    type: "LINESTRING"},
52    { name: "%s_polygon", type: "POLYGON"  },
53    { name: "%s_roads",   type: "LINESTRING"}
54};
55#define NUM_TABLES ((signed)(sizeof(tables) / sizeof(tables[0])))
56
57#define FLAG_POLYGON 1    /* For polygon table */
58#define FLAG_LINEAR  2    /* For lines table */
59#define FLAG_NOCACHE 4    /* Optimisation: don't bother remembering this one */
60#define FLAG_DELETE  8    /* These tags should be simply deleted on sight */
61static struct flagsname {
62    char *name;
63    int flag;
64} tagflags[] = {
65    { name: "polygon",    flag: FLAG_POLYGON },
66    { name: "linear",     flag: FLAG_LINEAR },
67    { name: "nocache",    flag: FLAG_NOCACHE },
68    { name: "delete",     flag: FLAG_DELETE }
69};
70#define NUM_FLAGS ((signed)(sizeof(tagflags) / sizeof(tagflags[0])))
71
72/* Table columns, representing key= tags */
73struct taginfo {
74    char *name;
75    char *type;
76    int flags;
77    int count;
78};
79
80static struct taginfo *exportList[4]; /* Indexed by enum table_id */
81static int exportListCount[4];
82
83/* Data to generate z-order column and road table
84 * The name of the roads table is misleading, this table
85 * is used for any feature to be shown at low zoom.
86 * This includes railways and administrative boundaries too
87 */
88static struct {
89    int offset;
90    const char *highway;
91    int roads;
92} layers[] = {
93    { 9, "motorway",      1 },
94    { 9, "motorway_link", 1 },
95    { 8, "trunk",         1 },
96    { 8, "trunk_link",    1 },
97    { 7, "primary",       1 },
98    { 7, "primary_link",  1 },
99    { 6, "secondary",     1 },
100    { 6, "secondary_link",1 },
101   // 5 = railway
102    { 4, "tertiary",      0 },
103    { 4, "tertiary_link", 0 },
104    { 3, "residential",   0 },
105    { 3, "unclassified",  0 },
106    { 3, "road",          0 },
107    { 3, "minor",         0 }
108};
109static const unsigned int nLayers = (sizeof(layers)/sizeof(*layers));
110
111static int pgsql_delete_way_from_output(int osm_id);
112static int pgsql_delete_relation_from_output(int osm_id);
113static int pgsql_process_relation(int id, struct member *members, int member_count, struct keyval *tags, int exists);
114
115void read_style_file( char *filename )
116{
117  FILE *in;
118  int lineno = 0;
119
120  exportList[OSMTYPE_NODE] = malloc( sizeof(struct taginfo) * MAX_STYLES );
121  exportList[OSMTYPE_WAY]  = malloc( sizeof(struct taginfo) * MAX_STYLES );
122
123  in = fopen( filename, "rt" );
124  if( !in )
125  {
126    fprintf( stderr, "Couldn't open style file '%s': %s\n", filename, strerror(errno) );
127    exit_nicely();
128  }
129 
130  char buffer[1024];
131  while( fgets( buffer, sizeof(buffer), in) != NULL )
132  {
133    lineno++;
134   
135    char osmtype[24];
136    char tag[24];
137    char datatype[24];
138    char flags[128];
139    int i;
140    char *str;
141
142    str = strchr( buffer, '#' );
143    if( str )
144      *str = '\0';
145     
146    int fields = sscanf( buffer, "%23s %23s %23s %127s", osmtype, tag, datatype, flags );
147    if( fields <= 0 )  /* Blank line */
148      continue;
149    if( fields < 3 )
150    {
151      fprintf( stderr, "Error reading style file line %d (fields=%d)\n", lineno, fields );
152      exit_nicely();
153    }
154    struct taginfo temp;
155    temp.name = strdup(tag);
156    temp.type = strdup(datatype);
157   
158    temp.flags = 0;
159    for( str = strtok( flags, ",\r\n" ); str; str = strtok(NULL, ",\r\n") )
160    {
161      for( i=0; i<NUM_FLAGS; i++ )
162      {
163        if( strcmp( tagflags[i].name, str ) == 0 )
164        {
165          temp.flags |= tagflags[i].flag;
166          break;
167        }
168      }
169      if( i == NUM_FLAGS )
170        fprintf( stderr, "Unknown flag '%s' line %d, ignored\n", str, lineno );
171    }
172    temp.count = 0;
173//    printf("%s %s %d %d\n", temp.name, temp.type, temp.polygon, offset );
174   
175    int flag = 0;
176    if( strstr( osmtype, "node" ) )
177    {
178      memcpy( &exportList[ OSMTYPE_NODE ][ exportListCount[ OSMTYPE_NODE ] ], &temp, sizeof(temp) );
179      exportListCount[ OSMTYPE_NODE ]++;
180      flag = 1;
181    }
182    if( strstr( osmtype, "way" ) )
183    {
184      memcpy( &exportList[ OSMTYPE_WAY ][ exportListCount[ OSMTYPE_WAY ] ], &temp, sizeof(temp) );
185      exportListCount[ OSMTYPE_WAY ]++;
186      flag = 1;
187    }
188    if( !flag )
189    {
190      fprintf( stderr, "Weird style line %d\n", lineno );
191      exit_nicely();
192    }
193  }
194  fclose(in);
195}
196
197static void free_style_refs(const char *name, const char *type)
198{
199    // Find and remove any other references to these pointers
200    // This would be way easier if we kept a single list of styles
201    // Currently this scales with n^2 number of styles
202    int i,j;
203
204    for (i=0; i<NUM_TABLES; i++) {
205        for(j=0; j<exportListCount[i]; j++) {
206            if (exportList[i][j].name == name)
207                exportList[i][j].name = NULL;
208            if (exportList[i][j].type == type)
209                exportList[i][j].type = NULL;
210        }
211    }
212}
213
214static void free_style(void)
215{
216    int i, j;
217    for (i=0; i<NUM_TABLES; i++) {
218        for(j=0; j<exportListCount[i]; j++) {
219            free(exportList[i][j].name);
220            free(exportList[i][j].type);
221            free_style_refs(exportList[i][j].name, exportList[i][j].type);
222        }
223    }
224    for (i=0; i<NUM_TABLES; i++)
225        free(exportList[i]);
226}
227
228/* Handles copying out, but coalesces the data into large chunks for
229 * efficiency. PostgreSQL doesn't actually need this, but each time you send
230 * a block of data you get 5 bytes of overhead. Since we go column by column
231 * with most empty and one byte delimiters, without this optimisation we
232 * transfer three times the amount of data necessary.
233 */
234void copy_to_table(enum table_id table, const char *sql)
235{
236    PGconn *sql_conn = tables[table].sql_conn;
237    unsigned int len = strlen(sql);
238    unsigned int buflen = tables[table].buflen;
239    char *buffer = tables[table].buffer;
240
241    /* Return to copy mode if we dropped out */
242    if( !tables[table].copyMode )
243    {
244        pgsql_exec(sql_conn, PGRES_COPY_IN, "COPY %s FROM STDIN", tables[table].name);
245        tables[table].copyMode = 1;
246    }
247    /* If the combination of old and new data is too big, flush old data */
248    if( (unsigned)(buflen + len) > sizeof( tables[table].buffer )-10 )
249    {
250      pgsql_CopyData(tables[table].name, sql_conn, buffer);
251      buflen = 0;
252
253      /* If new data by itself is also too big, output it immediately */
254      if( (unsigned)len > sizeof( tables[table].buffer )-10 )
255      {
256        pgsql_CopyData(tables[table].name, sql_conn, sql);
257        len = 0;
258      }
259    }
260    /* Normal case, just append to buffer */
261    if( len > 0 )
262    {
263      strcpy( buffer+buflen, sql );
264      buflen += len;
265      len = 0;
266    }
267
268    /* If we have completed a line, output it */
269    if( buflen > 0 && buffer[buflen-1] == '\n' )
270    {
271      pgsql_CopyData(tables[table].name, sql_conn, buffer);
272      buflen = 0;
273    }
274
275    tables[table].buflen = buflen;
276}
277
278static int add_z_order_polygon(struct keyval *tags, int *roads)
279{
280    const char *natural = getItem(tags, "natural");
281    const char *layer   = getItem(tags, "layer");
282
283    int z_order, l;
284    char z[13];
285
286    /* Discard polygons with the tag natural=coastline */
287    if (natural && !strcmp(natural, "coastline"))
288        return 1;
289
290    l = layer ? strtol(layer, NULL, 10) : 0;
291    z_order = 10 * l;
292    *roads = 0;
293
294    snprintf(z, sizeof(z), "%d", z_order);
295    addItem(tags, "z_order", z, 0);
296
297    return 0;
298}
299
300
301static int add_z_order_line(struct keyval *tags, int *roads)
302{
303    const char *layer   = getItem(tags, "layer");
304    const char *highway = getItem(tags, "highway");
305    const char *bridge  = getItem(tags, "bridge");
306    const char *tunnel  = getItem(tags, "tunnel");
307    const char *railway = getItem(tags, "railway");
308    const char *boundary= getItem(tags, "boundary");
309    int z_order = 0;
310    int l;
311    unsigned int i;
312    char z[13];
313
314    l = layer ? strtol(layer, NULL, 10) : 0;
315    z_order = 10 * l;
316    *roads = 0;
317
318    if (highway) {
319        for (i=0; i<nLayers; i++) {
320            if (!strcmp(layers[i].highway, highway)) {
321                z_order += layers[i].offset;
322                *roads   = layers[i].roads;
323                break;
324            }
325        }
326    }
327
328    if (railway && strlen(railway)) {
329        z_order += 5;
330        *roads = 1;
331    }
332    // Administrative boundaries are rendered at low zooms so we prefer to use the roads table
333    if (boundary && !strcmp(boundary, "administrative"))
334        *roads = 1;
335
336    if (bridge && (!strcmp(bridge, "true") || !strcmp(bridge, "yes") || !strcmp(bridge, "1")))
337        z_order += 10;
338
339    if (tunnel && (!strcmp(tunnel, "true") || !strcmp(tunnel, "yes") || !strcmp(tunnel, "1")))
340        z_order -= 10;
341
342    snprintf(z, sizeof(z), "%d", z_order);
343    addItem(tags, "z_order", z, 0);
344
345    return 0;
346}
347
348static int add_z_order(struct keyval* tags, int polygon, int *roads)
349{
350    return polygon ? add_z_order_polygon(tags, roads) : add_z_order_line(tags, roads);
351}
352
353
354static void fix_motorway_shields(struct keyval *tags)
355{
356    const char *highway = getItem(tags, "highway");
357    const char *name    = getItem(tags, "name");
358    const char *ref     = getItem(tags, "ref");
359
360    /* The current mapnik style uses ref= for motorway shields but some roads just have name= */
361    if (!highway || strcmp(highway, "motorway"))
362        return;
363
364    if (name && !ref)
365        addItem(tags, "ref", name, 0);
366}
367
368
369/* Append all alternate name:xx on to the name tag with space sepatators.
370 * name= always comes first, the alternates are in no particular order
371 * Note: A new line may be better but this does not work with Mapnik
372 *
373 *    <tag k="name" v="Ben Nevis" />
374 *    <tag k="name:gd" v="Ben Nibheis" />
375 * becomes:
376 *    <tag k="name" v="Ben Nevis Ben Nibheis" />
377 */
378void compress_tag_name(struct keyval *tags)
379{
380    const char *name = getItem(tags, "name");
381    struct keyval *name_ext = getMatches(tags, "name:");
382    struct keyval *p;
383    char out[2048];
384
385    if (!name_ext)
386        return;
387
388    out[0] = '\0';
389    if (name) {
390        strncat(out, name, sizeof(out)-1);
391        strncat(out, " ", sizeof(out)-1);
392    }
393    while((p = popItem(name_ext)) != NULL) {
394        /* Exclude name:source = "dicataphone" and duplicates */
395        if (strcmp(p->key, "name:source") && !strstr(out, p->value)) {
396            strncat(out, p->value, sizeof(out)-1);
397            strncat(out, " ", sizeof(out)-1);
398        }
399        freeItem(p);
400    }
401    free(name_ext);
402
403    // Remove trailing space
404    out[strlen(out)-1] = '\0';
405    //fprintf(stderr, "*** New name: %s\n", out);
406    updateItem(tags, "name", out);
407}
408
409
410
411static void pgsql_out_cleanup(void)
412{
413    int i;
414
415    for (i=0; i<NUM_TABLES; i++) {
416        if (tables[i].sql_conn) {
417            PQfinish(tables[i].sql_conn);
418            tables[i].sql_conn = NULL;
419        }
420    }
421}
422
423/* Escape data appropriate to the type */
424static void escape_type(char *sql, int len, const char *value, const char *type) {
425    int items, from, to;
426
427    if ( !strcmp(type, "int4") ) {
428        /* For integers we take the first number, or the average if it's a-b */
429        items = sscanf(value, "%d-%d", &from, &to);
430        if ( items == 1 ) {
431            sprintf(sql, "%d", from);
432        } else if ( items == 2 ) {
433            sprintf(sql, "%d", (from + to) / 2);
434        } else {
435            sprintf(sql, "\\N");
436        }
437    } else {
438        escape(sql, len, value);
439    }
440}
441
442
443/* example from: pg_dump -F p -t planet_osm gis
444COPY planet_osm (osm_id, name, place, landuse, leisure, "natural", man_made, waterway, highway, railway, amenity, tourism, learning, building, bridge, layer, way) FROM stdin;
44517959841        \N      \N      \N      \N      \N      \N      \N      bus_stop        \N      \N      \N      \N      \N      \N    -\N      0101000020E610000030CCA462B6C3D4BF92998C9B38E04940
44617401934        The Horn        \N      \N      \N      \N      \N      \N      \N      \N      pub     \N      \N      \N      \N    -\N      0101000020E6100000C12FC937140FD5BFB4D2F4FB0CE04940
447...
448
449mine - 01 01000000 48424298424242424242424256427364
450psql - 01 01000020 E6100000 30CCA462B6C3D4BF92998C9B38E04940
451       01 01000020 E6100000 48424298424242424242424256427364
4520x2000_0000 = hasSRID, following 4 bytes = srid, not supported by geos WKBWriter
453Workaround - output SRID=4326;<WKB>
454*/
455
456static int pgsql_out_node(int id, struct keyval *tags, double node_lat, double node_lon)
457{
458    char sql[2048], *v;
459    int i;
460
461    sprintf(sql, "%d\t", id);
462    copy_to_table(t_point, sql);
463
464    for (i=0; i < exportListCount[OSMTYPE_NODE]; i++) {
465        if( exportList[OSMTYPE_NODE][i].flags & FLAG_DELETE )
466            continue;
467        if ((v = getItem(tags, exportList[OSMTYPE_NODE][i].name)))
468        {
469            escape_type(sql, sizeof(sql), v, exportList[OSMTYPE_NODE][i].type);
470            exportList[OSMTYPE_NODE][i].count++;
471        }
472        else
473            sprintf(sql, "\\N");
474
475        copy_to_table(t_point, sql);
476        copy_to_table(t_point, "\t");
477    }
478
479    sprintf(sql, "SRID=%d;POINT(%.15g %.15g)", SRID, node_lon, node_lat);
480    copy_to_table(t_point, sql);
481    copy_to_table(t_point, "\n");
482
483    return 0;
484}
485
486
487
488static void write_wkts(int id, struct keyval *tags, const char *wkt, enum table_id table)
489{
490    int j;
491    char sql[2048];
492    const char*v;
493
494    sprintf(sql, "%d\t", id);
495    copy_to_table(table, sql);
496
497    for (j=0; j < exportListCount[OSMTYPE_WAY]; j++) {
498            if( exportList[OSMTYPE_WAY][j].flags & FLAG_DELETE )
499                continue;
500            if ((v = getItem(tags, exportList[OSMTYPE_WAY][j].name)))
501            {
502                exportList[OSMTYPE_WAY][j].count++;
503                escape_type(sql, sizeof(sql), v, exportList[OSMTYPE_WAY][j].type);
504            }
505            else
506                sprintf(sql, "\\N");
507
508            copy_to_table(table, sql);
509            copy_to_table(table, "\t");
510    }
511
512    sprintf(sql, "SRID=%d;", SRID);
513    copy_to_table(table, sql);
514    copy_to_table(table, wkt);
515    copy_to_table(table, "\n");
516}
517
518void add_parking_node(int id, struct keyval *tags, double node_lat, double node_lon)
519{
520// insert into planet_osm_point(osm_id,name,amenity,way) select osm_id,name,amenity,centroid(way) from planet_osm_polygon where amenity='parking';
521        const char *access  = getItem(tags, "access");
522        const char *amenity = getItem(tags, "amenity");
523        const char *name    = getItem(tags, "name");
524        struct keyval nodeTags;
525       
526        if (!amenity || strcmp(amenity, "parking"))
527                return;
528
529        // Do not add a 'P' symbol if access is defined and something other than public.
530        if (access && strcmp(access, "public"))
531                return;
532
533        initList(&nodeTags);
534        addItem(&nodeTags, "amenity", amenity, 0);
535        if (name)
536                addItem(&nodeTags, "name",    name,    0);
537        if (access)
538                addItem(&nodeTags, "access",  access,    0);
539       
540        pgsql_out_node(id, &nodeTags, node_lat, node_lon);
541        resetList(&nodeTags);
542}
543
544static int tag_indicates_polygon(enum OsmType type, const char *key)
545{
546    int i;
547
548    for (i=0; i < exportListCount[type]; i++) {
549        if( strcmp( exportList[type][i].name, key ) == 0 )
550            return exportList[type][i].flags & FLAG_POLYGON;
551    }
552    return 0;
553}
554
555/* Go through the given tags and determine the union of flags. Also remove
556 * any tags from the list that we don't know about */
557unsigned int pgsql_filter_tags(enum OsmType type, struct keyval *tags, int *polygon)
558{
559    int i, filter = 1;
560    int flags = 0;
561
562    const char *area;
563    struct keyval *item;
564    struct keyval temp;
565    initList(&temp);
566
567    /* 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 */
568    while( (item = popItem(tags)) != NULL )
569    {
570        for (i=0; i < exportListCount[type]; i++)
571        {
572            if( strcmp( exportList[type][i].name, item->key ) == 0 )
573            {
574                if( exportList[type][i].flags & FLAG_DELETE )
575                {
576                    freeItem( item );
577                    item = NULL;
578                    break;
579                }
580
581                filter = 0;
582                flags |= exportList[type][i].flags;
583
584                pushItem( &temp, item );
585                item = NULL;
586                break;
587            }
588        }
589        if( i == exportListCount[type] )
590        {
591            freeItem( item );
592            item = NULL;
593        }
594    }
595
596    /* Move from temp list back to original list */
597    while( (item = popItem(&temp)) != NULL )
598        pushItem( tags, item );
599
600    *polygon = flags & FLAG_POLYGON;
601
602    /* Special case allowing area= to override anything else */
603    if ((area = getItem(tags, "area"))) {
604        if (!strcmp(area, "yes") || !strcmp(area, "true") ||!strcmp(area, "1"))
605            *polygon = 1;
606        else if (!strcmp(area, "no") || !strcmp(area, "false") || !strcmp(area, "0"))
607            *polygon = 0;
608    }
609
610    return filter;
611}
612
613/*
614COPY planet_osm (osm_id, name, place, landuse, leisure, "natural", man_made, waterway, highway, railway, amenity, tourism, learning, bu
615ilding, bridge, layer, way) FROM stdin;
616198497  Bedford Road    \N      \N      \N      \N      \N      \N      residential     \N      \N      \N      \N      \N      \N    \N       0102000020E610000004000000452BF702B342D5BF1C60E63BF8DF49406B9C4D470037D5BF5471E316F3DF4940DFA815A6EF35D5BF9AE95E27F5DF4940B41EB
617E4C1421D5BF24D06053E7DF4940
618212696  Oswald Road     \N      \N      \N      \N      \N      \N      minor   \N      \N      \N      \N      \N      \N      \N    0102000020E610000004000000467D923B6C22D5BFA359D93EE4DF4940B3976DA7AD11D5BF84BBB376DBDF4940997FF44D9A06D5BF4223D8B8FEDF49404D158C4AEA04D
6195BF5BB39597FCDF4940
620*/
621static int pgsql_out_way(int id, struct keyval *tags, struct osmNode *nodes, int count, int exists)
622{
623    int polygon = 0, roads = 0;
624    char *wkt;
625    double area, interior_lat, interior_lon;
626   
627    /* If the flag says this object may exist already, delete it first */
628    if(exists)
629        pgsql_delete_way_from_output(id);
630
631    if (pgsql_filter_tags(OSMTYPE_WAY, tags, &polygon) || add_z_order(tags, polygon, &roads))
632        return 0;
633
634    //compress_tag_name(tags);
635
636    fix_motorway_shields(tags);
637
638    wkt = get_wkt_simple(nodes, count, polygon, &area, &interior_lon, &interior_lat);
639    if (wkt && strlen(wkt)) {
640        /* FIXME: there should be a better way to detect polygons */
641        if (!strncmp(wkt, "POLYGON", strlen("POLYGON"))) {
642            if (area > 0.0) {
643                char tmp[32];
644                snprintf(tmp, sizeof(tmp), "%f", area);
645                addItem(tags, "way_area", tmp, 0);
646            }
647            write_wkts(id, tags, wkt, t_poly);
648            add_parking_node(id, tags, interior_lat, interior_lon);
649        } else {
650            write_wkts(id, tags, wkt, t_line);
651            if (roads)
652                write_wkts(id, tags, wkt, t_roads);
653        }
654    }
655    free(wkt);
656       
657    return 0;
658}
659
660static int pgsql_out_relation(int id, struct keyval *rel_tags, struct osmNode **xnodes, struct keyval *xtags, int *xcount, int *xid, const char **xrole)
661{
662    int i, wkt_size;
663    double interior_lat, interior_lon;
664    int polygon = 0, roads = 0;
665    int make_polygon = 0;
666    struct keyval tags, *p, poly_tags;
667    char *type;
668#if 0
669    fprintf(stderr, "Got relation with counts:");
670    for (i=0; xcount[i]; i++)
671        fprintf(stderr, " %d", xcount[i]);
672    fprintf(stderr, "\n");
673#endif
674    /* Get the type, if there's no type we don't care */
675    type = getItem(rel_tags, "type");
676    if( !type )
677        return 0;
678
679    initList(&tags);
680    initList(&poly_tags);
681
682    p = rel_tags->next;
683    while (p != rel_tags) {
684        addItem(&tags, p->key, p->value, 1);
685        p = p->next;
686    }
687
688    if( strcmp(type, "route") == 0 )
689    {
690        make_polygon = 0;
691        char *state = getItem(rel_tags, "state");
692        if (state == NULL) {
693            state = "";
694        }
695
696        int networknr = -1;
697
698        if (getItem(rel_tags, "network") != NULL) {
699            char *netw = getItem(rel_tags, "network");
700
701            if (strcmp(netw, "lcn") == 0) {
702                networknr = 10;
703                if (strcmp(state, "alternate") == 0) {
704                    addItem(&tags, "lcn", "alternate", 1);
705                } else if (strcmp(state, "connection") == 0) {
706                    addItem(&tags, "lcn", "connection", 1);
707                } else {
708                    addItem(&tags, "lcn", "yes", 1);
709                }
710            } else if (strcmp(netw, "rcn") == 0) {
711                networknr = 11;
712                if (strcmp(state, "alternate") == 0) {
713                    addItem(&tags, "rcn", "alternate", 1);
714                } else if (strcmp(state, "connection") == 0) {
715                    addItem(&tags, "rcn", "connection", 1);
716                } else {
717                    addItem(&tags, "rcn", "yes", 1);
718                }
719            } else if (strcmp(netw, "ncn") == 0) {
720                networknr = 12;
721                if (strcmp(state, "alternate") == 0) {
722                    addItem(&tags, "ncn", "alternate", 1);
723                } else if (strcmp(state, "connection") == 0) {
724                    addItem(&tags, "ncn", "connection", 1);
725                } else {
726                    addItem(&tags, "ncn", "yes", 1);
727                }
728
729
730            } else if (strcmp(netw, "lwn") == 0) {
731                networknr = 20;
732                if (strcmp(state, "alternate") == 0) {
733                    addItem(&tags, "lwn", "alternate", 1);
734                } else if (strcmp(state, "connection") == 0) {
735                    addItem(&tags, "lwn", "connection", 1);
736                } else {
737                    addItem(&tags, "lwn", "yes", 1);
738                }
739            } else if (strcmp(netw, "rwn") == 0) {
740                networknr = 21;
741                if (strcmp(state, "alternate") == 0) {
742                    addItem(&tags, "rwn", "alternate", 1);
743                } else if (strcmp(state, "connection") == 0) {
744                    addItem(&tags, "rwn", "connection", 1);
745                } else {
746                    addItem(&tags, "rwn", "yes", 1);
747                }
748            } else if (strcmp(netw, "nwn") == 0) {
749                networknr = 22;
750                if (strcmp(state, "alternate") == 0) {
751                    addItem(&tags, "nwn", "alternate", 1);
752                } else if (strcmp(state, "connection") == 0) {
753                    addItem(&tags, "nwn", "connection", 1);
754                } else {
755                    addItem(&tags, "nwn", "yes", 1);
756                }
757            }
758        }
759
760        if (getItem(rel_tags, "preferred_color") != NULL) {
761            char *a = getItem(rel_tags, "preferred_color");
762            if (strcmp(a, "0") == 0 || strcmp(a, "1") == 0 || strcmp(a, "2") == 0 || strcmp(a, "3") == 0 || strcmp(a, "4") == 0) {
763                addItem(&tags, "route_pref_color", a, 1);
764            } else {
765                addItem(&tags, "route_pref_color", "0", 1);
766            }
767        } else {
768            addItem(&tags, "route_pref_color", "0", 1);
769        }
770
771        if (getItem(rel_tags, "name") != NULL) {
772            addItem(&tags, "route_name", getItem(rel_tags, "name"), 1);
773        }
774
775        if (getItem(rel_tags, "ref") != NULL) {
776            if (networknr == 10) {
777                addItem(&tags, "lcn_ref", getItem(rel_tags, "ref"), 1);
778            } else if (networknr == 11) {
779                addItem(&tags, "rcn_ref", getItem(rel_tags, "ref"), 1);
780            } else if (networknr == 12) {
781                addItem(&tags, "ncn_ref", getItem(rel_tags, "ref"), 1);
782            } else if (networknr == 20) {
783                addItem(&tags, "lwn_ref", getItem(rel_tags, "ref"), 1);
784            } else if (networknr == 21) {
785                addItem(&tags, "rwn_ref", getItem(rel_tags, "ref"), 1);
786            } else if (networknr == 22) {
787                addItem(&tags, "nwn_ref", getItem(rel_tags, "ref"), 1);
788            }
789        }
790    }
791    else if( strcmp( type, "multipolygon" ) == 0 )
792    {
793        make_polygon = 1;
794        /* For multipolygons we add the tags on any non-inner rings */
795        for (i=0; xcount[i]; i++) {
796            if (xrole[i] && !strcmp(xrole[i], "inner"))
797                continue;
798
799            p = xtags[i].next;
800            while (p != &(xtags[i])) {
801                addItem(&tags, p->key, p->value, 1);
802                // Collect a list of polygon-like tags, these are later used to
803                // identify if an inner rings looks like it should be rendered seperately
804                if (tag_indicates_polygon(OSMTYPE_WAY, p->key)) {
805                    addItem(&poly_tags, p->key, p->value, 1);
806                    //fprintf(stderr, "found a polygon tag: %s=%s\n", p->key, p->value);
807                }
808                p = p->next;
809            }
810        }
811    }
812    else
813    {
814        /* Unknown type, just exit */
815        resetList(&tags);
816        resetList(&poly_tags);
817        return 0;
818    }
819
820    if (pgsql_filter_tags(OSMTYPE_WAY, &tags, &polygon) || add_z_order(&tags, polygon, &roads)) {
821        resetList(&tags);
822        resetList(&poly_tags);
823        return 0;
824    }
825
826    wkt_size = build_geometry(id, xnodes, xcount, make_polygon);
827
828    if (!wkt_size) {
829        resetList(&tags);
830        resetList(&poly_tags);
831        return 0;
832    }
833
834    for (i=0;i<wkt_size;i++)
835    {
836        char *wkt = get_wkt(i);
837
838        if (strlen(wkt)) {
839            /* FIXME: there should be a better way to detect polygons */
840            if (!strncmp(wkt, "POLYGON", strlen("POLYGON"))) {
841                double area = get_area(i);
842                if (area > 0.0) {
843                    char tmp[32];
844                    snprintf(tmp, sizeof(tmp), "%f", area);
845                    addItem(&tags, "way_area", tmp, 0);
846                }
847                write_wkts(-id, &tags, wkt, t_poly);
848                get_interior(i, &interior_lat, &interior_lon);
849                add_parking_node(-id, &tags, interior_lat, interior_lon);
850        } else {
851                write_wkts(-id, &tags, wkt, t_line);
852                if (roads)
853                    write_wkts(-id, &tags, wkt, t_roads);
854            }
855    }
856        free(wkt);
857    }
858
859    clear_wkts();
860
861    // If we are creating a multipolygon then we
862    // mark each member so that we can skip them during iterate_ways
863    // but only if the polygon-tags look the same as the outer ring
864    if (make_polygon) {
865        for (i=0; xcount[i]; i++) {
866            int match = 1;
867            struct keyval *p = poly_tags.next;
868            while (p != &poly_tags) {
869                const char *v = getItem(&xtags[i], p->key);
870                //fprintf(stderr, "compare polygon tag: %s=%s vs %s\n", p->key, p->value, v ? v : "null");
871                if (!v || strcmp(v, p->value)) {
872                    match = 0;
873                    break;
874                }
875                p = p->next;
876            }
877            if (match) {
878                //fprintf(stderr, "match for %d\n", xid[i]);
879                Options->mid->ways_done(xid[i]);
880            }
881        }
882    }
883
884    resetList(&tags);
885    resetList(&poly_tags);
886    return 0;
887}
888
889static int pgsql_out_start(const struct output_options *options)
890{
891    char sql[1024], tmp[128];
892    PGresult   *res;
893    int i,j;
894
895    Options = options;
896
897    read_style_file( "default.style" );
898
899    for (i=0; i<NUM_TABLES; i++) {
900        PGconn *sql_conn;
901
902        /* Substitute prefix into name of table */
903        {
904            char *temp = malloc( strlen(options->prefix) + strlen(tables[i].name) + 1 );
905            sprintf( temp, tables[i].name, options->prefix );
906            tables[i].name = temp;
907        }
908        fprintf(stderr, "Setting up table: %s\n", tables[i].name);
909        sql_conn = PQconnectdb(options->conninfo);
910
911        /* Check to see that the backend connection was successfully made */
912        if (PQstatus(sql_conn) != CONNECTION_OK) {
913            fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(sql_conn));
914            exit_nicely();
915        }
916        tables[i].sql_conn = sql_conn;
917
918        if (!options->append) {
919            sprintf( sql, "DROP TABLE %s;", tables[i].name);
920            res = PQexec(sql_conn, sql);
921            PQclear(res); /* Will be an error if table does not exist */
922        }
923
924        pgsql_exec(sql_conn, PGRES_COMMAND_OK, "BEGIN");
925
926        enum OsmType type = (i == t_point)?OSMTYPE_NODE:OSMTYPE_WAY;
927        int numTags = exportListCount[type];
928        struct taginfo *exportTags = exportList[type];
929        if (!options->append) {
930            sprintf(sql, "CREATE TABLE %s ( osm_id int4", tables[i].name );
931            for (j=0; j < numTags; j++) {
932                if( exportTags[j].flags & FLAG_DELETE )
933                    continue;
934                sprintf(tmp, ",\"%s\" %s", exportTags[j].name, exportTags[j].type);
935                strcat(sql, tmp);
936            }
937            strcat(sql, " );\n");
938            pgsql_exec(sql_conn, PGRES_COMMAND_OK, sql);
939            pgsql_exec(sql_conn, PGRES_TUPLES_OK, "SELECT AddGeometryColumn('%s', 'way', %d, '%s', 2 );\n",
940                        tables[i].name, SRID, tables[i].type );
941            pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ALTER TABLE %s ALTER COLUMN way SET NOT NULL;\n", tables[i].name);
942            /* slim mode needs this to be able to apply diffs */
943            if( Options->slim )
944                pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CREATE INDEX %s_pkey ON %s USING BTREE (osm_id);\n", tables[i].name, tables[i].name);
945        }
946
947        pgsql_exec(sql_conn, PGRES_COPY_IN, "COPY %s FROM STDIN", tables[i].name);
948        tables[i].copyMode = 1;
949    }
950
951    options->mid->start(options);
952
953    return 0;
954}
955
956static void pgsql_pause_copy(struct s_table *table)
957{
958    PGresult   *res;
959    if( !table->copyMode )
960        return;
961       
962    /* Terminate any pending COPY */
963    int stop = PQputCopyEnd(table->sql_conn, NULL);
964    if (stop != 1) {
965       fprintf(stderr, "COPY_END for %s failed: %s\n", table->name, PQerrorMessage(table->sql_conn));
966       exit_nicely();
967    }
968
969    res = PQgetResult(table->sql_conn);
970    if (PQresultStatus(res) != PGRES_COMMAND_OK) {
971       fprintf(stderr, "COPY_END for %s failed: %s\n", table->name, PQerrorMessage(table->sql_conn));
972       PQclear(res);
973       exit_nicely();
974    }
975    PQclear(res);
976    table->copyMode = 0;
977}
978
979static void *pgsql_out_stop_one(void *arg)
980{
981    struct s_table *table = arg;
982    PGconn *sql_conn = table->sql_conn;
983
984    if( table->buflen != 0 )
985    {
986       fprintf( stderr, "Internal error: Buffer for %s has %d bytes after end copy", table->name, table->buflen );
987       exit_nicely();
988    }
989
990    pgsql_pause_copy(table);
991    // Commit transaction
992    pgsql_exec(sql_conn, PGRES_COMMAND_OK, "COMMIT");
993
994    pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ANALYZE %s;\n", table->name);
995    pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CREATE TABLE %s_tmp AS SELECT * FROM %s ORDER BY way;\n", table->name, table->name);
996    pgsql_exec(sql_conn, PGRES_COMMAND_OK, "DROP TABLE %s;\n", table->name);
997    pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ALTER TABLE %s_tmp RENAME TO %s;\n", table->name, table->name);
998    pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CREATE INDEX %s_index ON %s USING GIST (way GIST_GEOMETRY_OPS);\n", table->name, table->name);
999    /* slim mode needs this to be able to apply diffs */
1000    if( Options->slim )
1001        pgsql_exec(sql_conn, PGRES_COMMAND_OK, "CREATE INDEX %s_pkey ON %s USING BTREE (osm_id);\n", table->name, table->name);
1002    pgsql_exec(sql_conn, PGRES_COMMAND_OK, "GRANT SELECT ON %s TO PUBLIC;\n", table->name);
1003    pgsql_exec(sql_conn, PGRES_COMMAND_OK, "ANALYZE %s;\n", table->name);
1004    free(table->name);
1005    return NULL;
1006}
1007static void pgsql_out_stop()
1008{
1009    int i;
1010#ifdef HAVE_PTHREAD
1011    pthread_t threads[NUM_TABLES];
1012#endif
1013
1014    /* Processing any remaing to be processed ways */
1015    Options->mid->iterate_ways( pgsql_out_way );
1016    Options->mid->iterate_relations( pgsql_process_relation );
1017    /* No longer need to access middle layer -- release memory */
1018    Options->mid->stop();
1019
1020#ifdef HAVE_PTHREAD
1021    for (i=0; i<NUM_TABLES; i++) {
1022        int ret = pthread_create(&threads[i], NULL, pgsql_out_stop_one, &tables[i]);
1023        if (ret) {
1024            fprintf(stderr, "pthread_create() returned an error (%d)", ret);
1025            exit_nicely();
1026        }
1027    }
1028    for (i=0; i<NUM_TABLES; i++) {
1029        int ret = pthread_join(threads[i], NULL);
1030        if (ret) {
1031            fprintf(stderr, "pthread_join() returned an error (%d)", ret);
1032            exit_nicely();
1033        }
1034    }
1035#else
1036    for (i=0; i<NUM_TABLES; i++)
1037        pgsql_out_stop_one(&tables[i]);
1038#endif
1039
1040    pgsql_out_cleanup();
1041    free_style();
1042}
1043
1044static int pgsql_add_node(int id, double lat, double lon, struct keyval *tags)
1045{
1046  int polygon;
1047  int filter = pgsql_filter_tags(OSMTYPE_NODE, tags, &polygon);
1048 
1049  Options->mid->nodes_set(id, lat, lon, tags);
1050  if( !filter )
1051      pgsql_out_node(id, tags, lat, lon);
1052  return 0;
1053}
1054
1055static int pgsql_add_way(int id, int *nds, int nd_count, struct keyval *tags)
1056{
1057  int polygon = 0;
1058
1059  // Check whether the way is: (1) Exportable, (2) Maybe a polygon
1060  int filter = pgsql_filter_tags(OSMTYPE_WAY, tags, &polygon);
1061
1062  // Memory saving hack:-
1063  // If we're not in slim mode and it's not wanted, we can quit right away */
1064  if( !Options->slim && filter )
1065    return 1;
1066   
1067  // If this isn't a polygon then it can not be part of a multipolygon
1068  // Hence only polygons are "pending"
1069  Options->mid->ways_set(id, nds, nd_count, tags, (!filter && polygon) ? 1 : 0);
1070
1071  if( !polygon && !filter )
1072  {
1073    /* Get actual node data and generate output */
1074    struct osmNode *nodes = malloc( sizeof(struct osmNode) * nd_count );
1075    int count = Options->mid->nodes_get_list( nodes, nds, nd_count );
1076    pgsql_out_way(id, tags, nodes, count, 0);
1077    free(nodes);
1078  }
1079  return 0;
1080}
1081
1082/* This is the workhorse of pgsql_add_relation, split out because it is used as the callback for iterate relations */
1083static int pgsql_process_relation(int id, struct member *members, int member_count, struct keyval *tags, int exists)
1084{
1085  // (int id, struct keyval *rel_tags, struct osmNode **xnodes, struct keyval **xtags, int *xcount)
1086  int i, count;
1087  int *xid = malloc( (member_count+1) * sizeof(int) );
1088  const char **xrole = malloc( (member_count+1) * sizeof(const char *) );
1089  int *xcount = malloc( (member_count+1) * sizeof(int) );
1090  struct keyval *xtags  = malloc( (member_count+1) * sizeof(struct keyval) );
1091  struct osmNode **xnodes = malloc( (member_count+1) * sizeof(struct osmNode*) );
1092
1093  /* If the flag says this object may exist already, delete it first */
1094  if(exists)
1095      pgsql_delete_relation_from_output(id);
1096
1097  count = 0;
1098  for( i=0; i<member_count; i++ )
1099  {
1100    /* Need to handle more than just ways... */
1101    if( members[i].type != OSMTYPE_WAY )
1102        continue;
1103
1104    initList(&(xtags[count]));
1105    if( Options->mid->ways_get( members[i].id, &(xtags[count]), &(xnodes[count]), &(xcount[count]) ) )
1106      continue;
1107    xid[count] = members[i].id;
1108    xrole[count] = members[i].role;
1109    count++;
1110  }
1111  xnodes[count] = NULL;
1112  xcount[count] = 0;
1113  xid[count] = 0;
1114  xrole[count] = NULL;
1115
1116  // At some point we might want to consider storing the retreived data in the members, rather than as seperate arrays
1117  pgsql_out_relation(id, tags, xnodes, xtags, xcount, xid, xrole);
1118
1119  for( i=0; i<count; i++ )
1120  {
1121    resetList( &(xtags[i]) );
1122    free( xnodes[i] );
1123  }
1124
1125  free(xid);
1126  free(xrole);
1127  free(xcount);
1128  free(xtags);
1129  free(xnodes);
1130  return 0;
1131}
1132
1133static int pgsql_add_relation(int id, struct member *members, int member_count, struct keyval *tags)
1134{
1135  const char *type = getItem(tags, "type");
1136
1137  // Must have a type field or we ignore it
1138  if (!type)
1139      return 0;
1140
1141  /* In slim mode we remember these*/
1142  if(Options->mid->relations_set)
1143    Options->mid->relations_set(id, members, member_count, tags);
1144  // (int id, struct keyval *rel_tags, struct osmNode **xnodes, struct keyval **xtags, int *xcount)
1145
1146  return pgsql_process_relation(id, members, member_count, tags, 0);
1147}
1148#define __unused  __attribute__ ((unused))
1149
1150/* Delete is easy, just remove all traces of this object. We don't need to
1151 * worry about finding objects that depend on it, since the same diff must
1152 * contain the change for that also. */
1153static int pgsql_delete_node(int osm_id)
1154{
1155    if( !Options->slim )
1156    {
1157        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1158        exit_nicely();
1159    }
1160    pgsql_pause_copy(&tables[t_point]);
1161    pgsql_exec(tables[t_point].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_point].name, osm_id );
1162    Options->mid->nodes_delete(osm_id);
1163    return 0;
1164}
1165
1166/* Seperated out because we use it elsewhere */
1167static int pgsql_delete_way_from_output(int osm_id)
1168{
1169    /* Optimisation: we only need this is slim mode */
1170    if( !Options->slim )
1171        return 0;
1172    pgsql_pause_copy(&tables[t_roads]);
1173    pgsql_pause_copy(&tables[t_line]);
1174    pgsql_pause_copy(&tables[t_poly]);
1175    pgsql_exec(tables[t_roads].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_roads].name, osm_id );
1176    pgsql_exec(tables[t_line].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_line].name, osm_id );
1177    pgsql_exec(tables[t_poly].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_poly].name, osm_id );
1178    return 0;
1179}
1180
1181static int pgsql_delete_way(int osm_id)
1182{
1183    if( !Options->slim )
1184    {
1185        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1186        exit_nicely();
1187    }
1188    pgsql_delete_way_from_output(osm_id);
1189    Options->mid->ways_delete(osm_id);
1190    return 0;
1191}
1192
1193/* Relations are identified by using negative IDs */
1194static int pgsql_delete_relation_from_output(int osm_id)
1195{
1196    pgsql_pause_copy(&tables[t_roads]);
1197    pgsql_pause_copy(&tables[t_line]);
1198    pgsql_pause_copy(&tables[t_poly]);
1199    pgsql_exec(tables[t_roads].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_roads].name, -osm_id );
1200    pgsql_exec(tables[t_line].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_line].name, -osm_id );
1201    pgsql_exec(tables[t_poly].sql_conn, PGRES_COMMAND_OK, "DELETE FROM %s WHERE osm_id = %d", tables[t_poly].name, -osm_id );
1202    return 0;
1203}
1204
1205static int pgsql_delete_relation(int osm_id)
1206{
1207    if( !Options->slim )
1208    {
1209        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1210        exit_nicely();
1211    }
1212    pgsql_delete_relation_from_output(osm_id);
1213    Options->mid->relations_delete(osm_id);
1214    return 0;
1215}
1216
1217/* Modify is slightly trickier. The basic idea is we simply delete the
1218 * object and create it with the new parameters. Then we need to mark the
1219 * objects that depend on this one */
1220static int pgsql_modify_node(int osm_id, double lat, double lon, struct keyval *tags)
1221{
1222    if( !Options->slim )
1223    {
1224        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1225        exit_nicely();
1226    }
1227    pgsql_delete_node(osm_id);
1228    pgsql_add_node(osm_id, lat, lon, tags);
1229    Options->mid->node_changed(osm_id);
1230    return 0;
1231}
1232
1233static int pgsql_modify_way(int osm_id, int *nodes, int node_count, struct keyval *tags)
1234{
1235    if( !Options->slim )
1236    {
1237        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1238        exit_nicely();
1239    }
1240    pgsql_delete_way(osm_id);
1241    pgsql_add_way(osm_id, nodes, node_count, tags);
1242    Options->mid->way_changed(osm_id);
1243    return 0;
1244}
1245
1246static int pgsql_modify_relation(int osm_id, struct member *members, int member_count, struct keyval *tags)
1247{
1248    if( !Options->slim )
1249    {
1250        fprintf( stderr, "Cannot apply diffs unless in slim mode\n" );
1251        exit_nicely();
1252    }
1253    pgsql_delete_relation(osm_id);
1254    pgsql_add_relation(osm_id, members, member_count, tags);
1255    Options->mid->relation_changed(osm_id);
1256    return 0;
1257}
1258
1259struct output_t out_pgsql = {
1260        start:     pgsql_out_start,
1261        stop:      pgsql_out_stop,
1262        cleanup:   pgsql_out_cleanup,
1263        node_add:      pgsql_add_node,
1264        way_add:       pgsql_add_way,
1265        relation_add:  pgsql_add_relation,
1266       
1267        node_modify: pgsql_modify_node,
1268        way_modify: pgsql_modify_way,
1269        relation_modify: pgsql_modify_relation,
1270
1271        node_delete: pgsql_delete_node,
1272        way_delete: pgsql_delete_way,
1273        relation_delete: pgsql_delete_relation
1274};
Note: See TracBrowser for help on using the repository browser.