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

Last change on this file since 3210 was 3008, checked in by artem, 13 years ago

added rendering direction arrows for oneway={yes,true,-1}

File size: 17.3 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
15#include <libpq-fe.h>
16
17#include "osmtypes.h"
18#include "output.h"
19#include "output-pgsql.h"
20#include "build_geometry.h"
21#include "middle-pgsql.h"
22
23/* Postgres database parameters */
24static const char *conninfo = "dbname = gis";
25
26enum table_id {
27    t_point, t_line, t_poly, t_roads
28};
29
30/* Tables to output */
31static struct {
32    //enum table_id table;
33    const char *name;
34    const char *type;
35} tables [] = {
36    { name: "planet_osm_point",   type: "POINT"     },
37    { name: "planet_osm_line",    type: "LINESTRING"},
38    { name: "planet_osm_polygon", type: "GEOMETRY"  },
39    { name: "planet_osm_roads",   type: "LINESTRING"}
40};
41static const unsigned int num_tables = sizeof(tables)/sizeof(*tables);
42
43/* Table columns, representing key= tags */
44static struct {
45    const char *name;
46    const char *type;
47    const int polygon;
48} exportTags[] = {
49    {"name",    "text", 0},
50    {"place",   "text", 0},
51    {"landuse", "text", 1},
52    {"leisure", "text", 1},
53    {"natural", "text", 1},
54    {"man_made","text", 0},
55    {"waterway","text", 0},
56    {"highway", "text", 0},
57    {"foot",    "text", 0},
58    {"horse",   "text", 0},
59    {"bicycle", "text", 0},
60    {"motorcar","text", 0},
61    {"residence","text", 0},
62    {"railway", "text", 0},
63    {"amenity", "text", 1},
64    {"tourism", "text", 1},
65    {"learning","text", 0},
66    {"building","text", 1},
67    {"bridge",  "text", 0},
68    {"layer",   "text", 0},
69    {"junction","text", 0},
70    {"sport",   "text", 1},
71    {"route",   "text", 0},
72    {"aeroway", "text", 0},
73    {"ref",     "text", 0},
74    {"oneway",  "text", 0},
75    {"z_order", "int4", 0}
76};
77static const unsigned int numTags = sizeof(exportTags) / sizeof(*exportTags);
78
79/* Data to generate z-order column and road table */
80static struct {
81    int offset;
82    const char *highway;
83    int roads;
84} layers[] = {
85    { 9, "motorway",      1 },
86    { 9, "motorway_link", 1 },
87    { 8, "trunk",         1 },
88    { 8, "trunk_link",    1 },
89    { 7, "primary",       1 },
90    { 7, "primary_link",  1 },
91    { 6, "secondary",     1 },
92    { 6, "secondary_link",1 },
93   // 5 = railway
94    { 4, "tertiary",      0 },
95    { 4, "tertiary_link", 0 },
96    { 3, "residential",   0 },
97    { 3, "unclassified",  0 },
98    { 3, "minor",         0 }
99};
100static const unsigned int nLayers = (sizeof(layers)/sizeof(*layers));
101
102
103static PGconn **sql_conns;
104
105
106
107static int add_z_order_polygon(struct keyval *tags, int *roads)
108{
109    const char *natural = getItem(tags, "natural");
110    const char *layer   = getItem(tags, "layer");
111    const char *landuse = getItem(tags, "landuse");
112    int z_order, l;
113    char z[13];
114
115    /* Discard polygons with the tag natural=coastline */
116    if (natural && !strcmp(natural, "coastline"))
117        return 1;
118
119    l = layer ? strtol(layer, NULL, 10) : 0;
120    z_order = 10 * l;
121    *roads = 0;
122
123    /* landuse tends to cover large areas and we want it under other polygons */
124    if (landuse)
125        z_order -= 1;
126
127    snprintf(z, sizeof(z), "%d", z_order);
128    addItem(tags, "z_order", z, 0);
129
130    return 0;
131}
132
133
134static int add_z_order_line(struct keyval *tags, int *roads)
135{
136    const char *layer   = getItem(tags, "layer");
137    const char *highway = getItem(tags, "highway");
138    const char *bridge  = getItem(tags, "bridge");
139    const char *railway = getItem(tags, "railway");
140    int z_order = 0;
141    int l;
142    unsigned int i;
143    char z[13];
144
145    l = layer ? strtol(layer, NULL, 10) : 0;
146    z_order = 10 * l;
147    *roads = 0;
148
149    if (highway) {
150        for (i=0; i<nLayers; i++) {
151            if (!strcmp(layers[i].highway, highway)) {
152                z_order += layers[i].offset;
153                *roads   = layers[i].roads;
154                break;
155            }
156        }
157    }
158
159    if (railway && strlen(railway)) {
160        z_order += 5;
161        *roads = 1;
162    }
163 
164    if (bridge && (!strcmp(bridge, "true") || !strcmp(bridge, "yes") || !strcmp(bridge, "1")))
165        z_order += 10;
166
167    snprintf(z, sizeof(z), "%d", z_order);
168    addItem(tags, "z_order", z, 0);
169
170    return 0;
171}
172
173static int add_z_order(struct keyval* tags, int polygon, int *roads)
174{
175    return polygon ? add_z_order_polygon(tags, roads) : add_z_order_line(tags, roads);
176}
177
178
179static void fix_motorway_shields(struct keyval *tags)
180{
181    const char *highway = getItem(tags, "highway");
182    const char *name    = getItem(tags, "name");
183    const char *ref     = getItem(tags, "ref");
184
185    /* The current mapnik style uses ref= for motorway shields but some data just has name= */
186    if (!highway || strcmp(highway, "motorway"))
187        return;
188
189    if (name && !ref)
190        addItem(tags, "ref", name, 0);
191}
192
193
194static void pgsql_out_cleanup(void)
195{
196    unsigned int i;
197
198    if (!sql_conns)
199           return;
200
201    for (i=0; i<num_tables; i++) {
202        if (sql_conns[i]) {
203            PQfinish(sql_conns[i]);
204            sql_conns[i] = NULL;
205        }
206    }
207    free(sql_conns);
208    sql_conns = NULL;
209}
210
211
212/* example from: pg_dump -F p -t planet_osm gis
213COPY planet_osm (osm_id, name, place, landuse, leisure, "natural", man_made, waterway, highway, railway, amenity, tourism, learning, building, bridge, layer, way) FROM stdin;
21417959841        \N      \N      \N      \N      \N      \N      \N      bus_stop        \N      \N      \N      \N      \N      \N    -\N      0101000020E610000030CCA462B6C3D4BF92998C9B38E04940
21517401934        The Horn        \N      \N      \N      \N      \N      \N      \N      \N      pub     \N      \N      \N      \N    -\N      0101000020E6100000C12FC937140FD5BFB4D2F4FB0CE04940
216...
217
218mine - 01 01000000 48424298424242424242424256427364
219psql - 01 01000020 E6100000 30CCA462B6C3D4BF92998C9B38E04940
220       01 01000020 E6100000 48424298424242424242424256427364
2210x2000_0000 = hasSRID, following 4 bytes = srid, not supported by geos WKBWriter
222Workaround - output SRID=4326;<WKB>
223*/
224
225static int pgsql_out_node(int id, struct keyval *tags, double node_lat, double node_lon)
226{
227    char sql[2048], *v;
228//    char *tmp;
229//    size_t len;
230    unsigned int i, r, export = 0;
231    PGconn *sql_conn = sql_conns[t_point];
232
233    for (i=0; i < numTags; i++) {
234        if ((v = getItem(tags, exportTags[i].name)))
235            export = 1; 
236    }
237
238    if (!export)
239       return 0;
240
241    sprintf(sql, "%d\t", id);
242    r = PQputCopyData(sql_conn, sql, strlen(sql));
243    if (r != 1) {
244        fprintf(stderr, "%s - bad result %d, line %s\n", __FUNCTION__, r, sql);
245        exit_nicely();
246    }
247
248    for (i=0; i < numTags; i++) {
249        if ((v = getItem(tags, exportTags[i].name)))
250            escape(sql, sizeof(sql), v);
251        else
252            sprintf(sql, "\\N");
253
254        r = PQputCopyData(sql_conn, sql, strlen(sql));
255        if (r != 1) {
256            fprintf(stderr, "%s - bad result %d, line %s\n", __FUNCTION__, r, sql);
257            exit_nicely();
258        }
259        r = PQputCopyData(sql_conn, "\t", 1);
260        if (r != 1) {
261            fprintf(stderr, "%s - bad result %d, tab\n", __FUNCTION__, r);
262            exit_nicely();
263        }
264    }
265
266    sprintf(sql, "SRID=4326;POINT(%.15g %.15g)", node_lon, node_lat);
267    r = PQputCopyData(sql_conn, sql, strlen(sql));
268    if (r != 1) {
269        fprintf(stderr, "%s - bad result %d, line %s\n", __FUNCTION__, r, sql);
270        exit_nicely();
271    }
272
273    sprintf(sql, "\n");
274    r = PQputCopyData(sql_conn, sql, strlen(sql));
275    if (r != 1) {
276        fprintf(stderr, "%s - bad result %d, line %s\n", __FUNCTION__, r, sql);
277        exit_nicely();
278    }
279 
280    return 0;
281}
282
283
284
285static size_t WKT(struct osmSegLL *segll, int count, int polygon)
286{
287    double x0, y0, x1, y1;
288    int i;
289
290    for(i=0; i<count; i++) {
291        x0 = segll[i].lon0;
292        y0 = segll[i].lat0;
293        x1 = segll[i].lon1;
294        y1 = segll[i].lat1;
295        add_segment(x0,y0,x1,y1);
296    }
297
298    return  build_geometry(polygon);
299}
300
301
302static void write_wkts(int id, struct keyval *tags, const char *wkt, PGconn *sql_conn)
303{
304    unsigned int j, r;
305    char sql[2048];
306    const char*v;
307
308    sprintf(sql, "%d\t", id);
309    r = PQputCopyData(sql_conn, sql, strlen(sql));
310    if (r != 1) {
311            fprintf(stderr, "%s - bad result %d, line %s\n", __FUNCTION__, r, sql);
312            exit_nicely();
313    }
314
315    for (j=0; j < sizeof(exportTags) / sizeof(exportTags[0]); j++) {
316            if ((v = getItem(tags, exportTags[j].name)))
317                    escape(sql, sizeof(sql), v);
318            else
319                    sprintf(sql, "\\N");
320
321            r = PQputCopyData(sql_conn, sql, strlen(sql));
322            if (r != 1) {
323                    fprintf(stderr, "%s - bad result %d, line %s\n", __FUNCTION__, r, sql);
324                    exit_nicely();
325            }
326            r = PQputCopyData(sql_conn, "\t", 1);
327            if (r != 1) {
328                    fprintf(stderr, "%s - bad result %d, tab\n", __FUNCTION__, r);
329                    exit_nicely();
330            }
331    }
332
333    sprintf(sql, "SRID=4326;");
334    r = PQputCopyData(sql_conn, sql, strlen(sql));
335    if (r != 1) {
336            fprintf(stderr, "%s - bad result %d, line %s\n", __FUNCTION__, r, sql);
337            exit_nicely();
338    }
339    r = PQputCopyData(sql_conn, wkt, strlen(wkt));
340    if (r != 1) {
341            fprintf(stderr, "%s - bad result %d, line %s\n", __FUNCTION__, r, wkt);
342            exit_nicely();
343    }
344    sprintf(sql, "\n");
345    r = PQputCopyData(sql_conn, sql, strlen(sql));
346    if (r != 1) {
347            fprintf(stderr, "%s - bad result %d, line %s\n", __FUNCTION__, r, sql);
348            exit_nicely();
349    }
350}
351
352void add_parking_node(int id, struct keyval *tags, size_t index)
353{
354// insert into planet_osm_point(osm_id,name,amenity,way) select osm_id,name,amenity,centroid(way) from planet_osm_polygon where amenity='parking';
355        const char *amenity = getItem(tags, "amenity");
356        const char *name    = getItem(tags, "name");
357        struct keyval nodeTags;
358        double node_lat = 0, node_lon = 0;
359       
360        if (!amenity || strcmp(amenity, "parking"))
361                return;
362
363        initList(&nodeTags);
364        addItem(&nodeTags, "amenity", amenity, 0);
365        if (name)
366                addItem(&nodeTags, "name",    name,    0);
367       
368        get_centroid(index, &node_lat, &node_lon);
369       
370        //fprintf(stderr, "Parking node: %s\t%f,%f\n", name ? name : "no_name", node_lat, node_lon);
371       
372        pgsql_out_node(id, &nodeTags, node_lat, node_lon);
373        resetList(&nodeTags);
374}
375
376
377/*
378COPY planet_osm (osm_id, name, place, landuse, leisure, "natural", man_made, waterway, highway, railway, amenity, tourism, learning, bu
379ilding, bridge, layer, way) FROM stdin;
380198497  Bedford Road    \N      \N      \N      \N      \N      \N      residential     \N      \N      \N      \N      \N      \N    \N       0102000020E610000004000000452BF702B342D5BF1C60E63BF8DF49406B9C4D470037D5BF5471E316F3DF4940DFA815A6EF35D5BF9AE95E27F5DF4940B41EB
381E4C1421D5BF24D06053E7DF4940
382212696  Oswald Road     \N      \N      \N      \N      \N      \N      minor   \N      \N      \N      \N      \N      \N      \N    0102000020E610000004000000467D923B6C22D5BFA359D93EE4DF4940B3976DA7AD11D5BF84BBB376DBDF4940997FF44D9A06D5BF4223D8B8FEDF49404D158C4AEA04D
3835BF5BB39597FCDF4940
384*/
385static int pgsql_out_way(int id, struct keyval *tags, struct osmSegLL *segll, int count)
386{
387    const char *v;
388    unsigned int i;
389    size_t wkt_size;
390    int polygon = 0, export = 0;
391    int roads = 0;
392
393    for (i=0; i < sizeof(exportTags) / sizeof(exportTags[0]); i++) {
394        if ((v = getItem(tags, exportTags[i].name))) {
395            polygon |= exportTags[i].polygon;
396            export = 1;
397        }
398    }
399
400    if (!export)
401        return 0;
402
403    if (add_z_order(tags, polygon, &roads))
404        return 0;
405
406    fix_motorway_shields(tags);
407
408    wkt_size = WKT(segll, count, polygon); 
409
410    for (i=0;i<wkt_size;i++)
411    {
412        char *wkt = get_wkt(i);
413        if (strlen(wkt)) {
414            /* FIXME: there should be a better way to detect polygons */
415            if (!strncmp(wkt, "POLYGON", strlen("POLYGON"))) {
416                write_wkts(id, tags, wkt, sql_conns[t_poly]);
417                add_parking_node(id, tags, i);
418            } else {
419                write_wkts(id, tags, wkt, sql_conns[t_line]);
420                if (roads)
421                    write_wkts(id, tags, wkt, sql_conns[t_roads]);
422            }
423        }
424        free(wkt);
425    }
426       
427    clear_wkts();
428    return 0;
429}
430
431
432static int pgsql_out_start(int dropcreate)
433{
434    char sql[1024], tmp[128];
435    PGresult   *res;
436    unsigned int i,j;
437
438    /* We use a connection per table to enable the use of COPY_IN */
439    sql_conns = calloc(num_tables, sizeof(PGconn *));
440    assert(sql_conns);
441
442    for (i=0; i<num_tables; i++) {
443        PGconn *sql_conn;
444
445        fprintf(stderr, "Setting up table: %s\n", tables[i].name);
446        sql_conn = PQconnectdb(conninfo);
447
448        /* Check to see that the backend connection was successfully made */
449        if (PQstatus(sql_conn) != CONNECTION_OK) {
450            fprintf(stderr, "Connection to database failed: %s", PQerrorMessage(sql_conn));
451            exit_nicely();
452        }
453        sql_conns[i] = sql_conn;
454
455        if (dropcreate) {
456            sql[0] = '\0';
457            strcat(sql, "DROP TABLE ");
458            strcat(sql, tables[i].name);
459            res = PQexec(sql_conn, sql);
460            PQclear(res); /* Will be an error if table does not exist */
461        }
462
463        res = PQexec(sql_conn, "BEGIN");
464        if (PQresultStatus(res) != PGRES_COMMAND_OK) {
465            fprintf(stderr, "BEGIN %s failed: %s", tables[i].name, PQerrorMessage(sql_conn));
466            PQclear(res);
467            exit_nicely();
468        }
469        PQclear(res);
470
471        if (dropcreate) {
472            sql[0] = '\0';
473            strcat(sql, "CREATE TABLE ");
474            strcat(sql, tables[i].name);
475            strcat(sql, " ( osm_id int4");
476            for (j=0; j < sizeof(exportTags) / sizeof(exportTags[0]); j++) {
477                sprintf(tmp, ",\"%s\" %s", exportTags[j].name, exportTags[j].type);
478                strcat(sql, tmp);
479            }
480            strcat(sql, " );\n");
481            strcat(sql, "SELECT AddGeometryColumn('");
482            strcat(sql, tables[i].name);
483            strcat(sql, "', 'way', 4326, '");
484            strcat(sql, tables[i].type);
485            strcat(sql, "', 2 );\n");
486
487            res = PQexec(sql_conn, sql);
488            if (PQresultStatus(res) != PGRES_TUPLES_OK) {
489                fprintf(stderr, "%s failed: %s", sql, PQerrorMessage(sql_conn));
490                PQclear(res);
491                exit_nicely();
492            }
493            PQclear(res);
494        }
495
496        sql[0] = '\0';
497        strcat(sql, "COPY ");
498        strcat(sql, tables[i].name);
499        strcat(sql, " FROM STDIN");
500
501        res = PQexec(sql_conn, sql);
502        if (PQresultStatus(res) != PGRES_COPY_IN) {
503            fprintf(stderr, "%s failed: %s", sql, PQerrorMessage(sql_conn));
504            PQclear(res);
505            exit_nicely();
506        }
507        PQclear(res);
508    }
509
510    return 0;
511}
512
513static void pgsql_out_stop(void)
514{
515    char sql[1024], tmp[128];
516    PGresult   *res;
517    unsigned int i;
518
519    for (i=0; i<num_tables; i++) {
520        PGconn *sql_conn = sql_conns[i];
521 
522        /* Terminate any pending COPY */
523        int stop = PQputCopyEnd(sql_conn, NULL);
524        if (stop != 1) {
525            fprintf(stderr, "COPY_END for %s failed: %s", tables[i].name, PQerrorMessage(sql_conn));
526            exit_nicely();
527        }
528
529        res = PQgetResult(sql_conn);
530        if (PQresultStatus(res) != PGRES_COMMAND_OK) {
531            fprintf(stderr, "COPY_END for %s failed: %s", tables[i].name, PQerrorMessage(sql_conn));
532            PQclear(res);
533            exit_nicely();
534        }
535        PQclear(res);
536
537        // Commit transaction
538        res = PQexec(sql_conn, "COMMIT");
539        if (PQresultStatus(res) != PGRES_COMMAND_OK) {
540            fprintf(stderr, "COMMIT %s failed: %s", tables[i].name, PQerrorMessage(sql_conn));
541            PQclear(res);
542            exit_nicely();
543        }
544        PQclear(res);
545
546        sql[0] = '\0';
547        strcat(sql, "VACUUM ANALYZE ");
548        strcat(sql, tables[i].name);
549        strcat(sql, ";\n");
550
551        strcat(sql, "CREATE INDEX way_index");
552        sprintf(tmp, "%d", i);
553        strcat(sql, tmp);
554        strcat(sql, " ON ");
555        strcat(sql, tables[i].name);
556        strcat(sql, " USING GIST (way GIST_GEOMETRY_OPS);\n");
557
558        strcat(sql, "CREATE INDEX z_index");
559        strcat(sql, tmp);
560        strcat(sql, " ON ");
561        strcat(sql, tables[i].name);
562        strcat(sql, " (z_order);\n");
563
564        strcat(sql, "ALTER TABLE ");
565        strcat(sql, tables[i].name);
566        strcat(sql, " ALTER COLUMN way SET NOT NULL;\n");
567#if 0
568        // z_order is now exported as a normal key column on all tables
569        if (i == t_line) {
570            strcat(sql, "ALTER TABLE ");
571            strcat(sql, tables[i].name);
572            strcat(sql, " ADD COLUMN z_order int4 default 0;\n");
573        }
574#endif
575        strcat(sql, "CLUSTER way_index");
576        strcat(sql, tmp);
577        strcat(sql, " ON ");
578        strcat(sql, tables[i].name);
579        strcat(sql, ";\n");
580
581        strcat(sql, "GRANT SELECT ON ");
582        strcat(sql, tables[i].name);
583        strcat(sql, " TO PUBLIC;\n");
584
585        strcat(sql, "VACUUM ANALYZE ");
586        strcat(sql, tables[i].name);
587        strcat(sql, ";\n");
588
589        res = PQexec(sql_conn, sql);
590        if (PQresultStatus(res) != PGRES_COMMAND_OK) {
591            fprintf(stderr, "%s failed: %s", sql, PQerrorMessage(sql_conn));
592            PQclear(res);
593            exit_nicely();
594        }
595        PQclear(res);
596    }
597
598    pgsql_out_cleanup();
599}
600
601 
602struct output_t out_pgsql = {
603        start:     pgsql_out_start,
604        stop:      pgsql_out_stop,
605        cleanup:   pgsql_out_cleanup,
606        node:      pgsql_out_node,
607        way:       pgsql_out_way
608};
Note: See TracBrowser for help on using the repository browser.