source: subversion/applications/utils/export/osm2pgsql/osm2pgsql.c @ 7141

Last change on this file since 7141 was 7141, checked in by martinvoosterhout, 12 years ago
Add support for a -Eproj option which allows users to use any epsg

projection that be used by proj4's +init=epsg:<num> format.

Should help all those people wanting to do maps in different projections.

  • Property svn:keywords set to Rev
File size: 16.8 KB
Line 
1/*
2#-----------------------------------------------------------------------------
3# osm2pgsql - converts planet.osm file into PostgreSQL
4# compatible output suitable to be rendered by mapnik
5# Use: osm2pgsql planet.osm.bz2
6#-----------------------------------------------------------------------------
7# Original Python implementation by Artem Pavlenko
8# Re-implementation by Jon Burgess, Copyright 2006
9#
10# This program is free software; you can redistribute it and/or
11# modify it under the terms of the GNU General Public License
12# as published by the Free Software Foundation; either version 2
13# of the License, or (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18# GNU General Public License for more details.
19#
20# You should have received a copy of the GNU General Public License
21# along with this program; if not, write to the Free Software
22# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
23#-----------------------------------------------------------------------------
24*/
25
26#define _GNU_SOURCE
27#include <stdio.h>
28#include <unistd.h>
29#include <stdlib.h>
30#include <string.h>
31#include <assert.h>
32#include <getopt.h>
33
34#include <libpq-fe.h>
35
36#include <libxml/xmlstring.h>
37#include <libxml/xmlreader.h>
38
39#include "osmtypes.h"
40#include "build_geometry.h"
41#include "keyvals.h"
42#include "middle-pgsql.h"
43#include "middle-ram.h"
44#include "output-pgsql.h"
45#include "sanitizer.h"
46#include "reprojection.h"
47#include "text-tree.h"
48#include "input.h"
49#include "sprompt.h"
50
51static int count_node,    max_node;
52static int count_way,     max_way;
53static int count_rel,     max_rel;
54
55struct middle_t *mid;
56struct output_t *out;
57
58/* Since {node,way} elements are not nested we can guarantee the
59   values in an end tag must match those of the corresponding
60   start tag and can therefore be cached.
61*/
62static double node_lon, node_lat;
63static struct keyval tags, nds, members;
64static int osm_id;
65
66int verbose;
67
68// Bounding box to filter imported data
69const char *bbox = NULL;
70static double minlon, minlat, maxlon, maxlat;
71
72static void printStatus(void)
73{
74    fprintf(stderr, "\rProcessing: Node(%dk) Way(%dk) Relation(%dk)",
75            count_node/1000, count_way/1000, count_rel/1000);
76}
77
78static int parse_bbox(void)
79{
80    int n;
81
82    if (!bbox)
83        return 0;
84
85    n = sscanf(bbox, "%lf,%lf,%lf,%lf", &minlon, &minlat, &maxlon, &maxlat);
86    if (n != 4) {
87        fprintf(stderr, "Bounding box must be specified like: minlon,minlat,maxlon,maxlat\n");
88        return 1;
89    }
90    if (maxlon <= minlon) {
91        fprintf(stderr, "Bounding box failed due to maxlon <= minlon\n");
92        return 1;
93    }
94    if (maxlat <= minlat) {
95        fprintf(stderr, "Bounding box failed due to maxlat <= minlat\n");
96        return 1;
97    }
98    printf("Applying Bounding box: %f,%f to %f,%f\n", minlon,minlat,maxlon,maxlat);
99    return 0;
100}
101
102static int node_wanted(double lat, double lon)
103{
104    if (!bbox)
105        return 1;
106
107    if (lat < minlat || lat > maxlat)
108        return 0;
109    if (lon < minlon || lon > maxlon)
110        return 0;
111    return 1;
112}
113
114void StartElement(xmlTextReaderPtr reader, const xmlChar *name)
115{
116    xmlChar *xid, *xlat, *xlon, *xk, *xv, *xrole, *xtype;
117    char *k;
118
119    if (xmlStrEqual(name, BAD_CAST "node")) {
120        xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
121        xlon = xmlTextReaderGetAttribute(reader, BAD_CAST "lon");
122        xlat = xmlTextReaderGetAttribute(reader, BAD_CAST "lat");
123        assert(xid); assert(xlon); assert(xlat);
124
125        osm_id  = strtol((char *)xid, NULL, 10);
126        node_lon = strtod((char *)xlon, NULL);
127        node_lat = strtod((char *)xlat, NULL);
128
129        if (osm_id > max_node)
130            max_node = osm_id;
131
132        count_node++;
133        if (count_node%10000 == 0)
134            printStatus();
135
136        xmlFree(xid);
137        xmlFree(xlon);
138        xmlFree(xlat);
139    } else if (xmlStrEqual(name, BAD_CAST "tag")) {
140        xk = xmlTextReaderGetAttribute(reader, BAD_CAST "k");
141        assert(xk);
142
143        /* 'created_by' and 'source' are common and not interesting to mapnik renderer */
144        if (strcmp((char *)xk, "created_by") && strcmp((char *)xk, "source")) {
145            char *p;
146            xv = xmlTextReaderGetAttribute(reader, BAD_CAST "v");
147            assert(xv);
148            k  = (char *)xmlStrdup(xk);
149            while ((p = strchr(k, ' ')))
150                *p = '_';
151
152            addItem(&tags, k, (char *)xv, 0);
153            xmlFree(k);
154            xmlFree(xv);
155        }
156        xmlFree(xk);
157    } else if (xmlStrEqual(name, BAD_CAST "way")) {
158        xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
159        assert(xid);
160        osm_id   = strtol((char *)xid, NULL, 10);
161
162        if (osm_id > max_way)
163            max_way = osm_id;
164
165        count_way++;
166        if (count_way%1000 == 0)
167            printStatus();
168
169        xmlFree(xid);
170    } else if (xmlStrEqual(name, BAD_CAST "nd")) {
171        xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "ref");
172        assert(xid);
173
174        addItem(&nds, "id", (char *)xid, 0);
175
176        xmlFree(xid);
177    } else if (xmlStrEqual(name, BAD_CAST "relation")) {
178        xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
179        assert(xid);
180        osm_id   = strtol((char *)xid, NULL, 10);
181
182        if (osm_id > max_rel)
183            max_rel = osm_id;
184
185        count_rel++;
186        if (count_rel%1000 == 0)
187            printStatus();
188
189        xmlFree(xid);
190    } else if (xmlStrEqual(name, BAD_CAST "member")) {
191        xrole = xmlTextReaderGetAttribute(reader, BAD_CAST "role");
192        assert(xrole);
193
194        xtype = xmlTextReaderGetAttribute(reader, BAD_CAST "type");
195        assert(xtype);
196
197        xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "ref");
198        assert(xid);
199
200        /* Currently we are only interested in 'way' members since these form polygons with holes */
201        if (xmlStrEqual(xtype, BAD_CAST "way"))
202            addItem(&members, (char *)xrole, (char *)xid, 0);
203
204        xmlFree(xid);
205        xmlFree(xrole);
206        xmlFree(xtype);
207    } else if (xmlStrEqual(name, BAD_CAST "osm")) {
208        /* ignore */
209    } else if (xmlStrEqual(name, BAD_CAST "bound")) {
210        /* ignore */
211    } else {
212        fprintf(stderr, "%s: Unknown element name: %s\n", __FUNCTION__, name);
213    }
214}
215
216void EndElement(const xmlChar *name)
217{
218    if (xmlStrEqual(name, BAD_CAST "node")) {
219        if (node_wanted(node_lat, node_lon)) {
220            reproject(&node_lat, &node_lon);
221            mid->nodes_set(osm_id, node_lat, node_lon, &tags);
222        }
223        resetList(&tags);
224    } else if (xmlStrEqual(name, BAD_CAST "way")) {
225        mid->ways_set(osm_id, &nds, &tags);
226        resetList(&tags);
227        resetList(&nds);
228    } else if (xmlStrEqual(name, BAD_CAST "relation")) {
229        mid->relations_set(osm_id, &members, &tags);
230        resetList(&tags);
231        resetList(&members);
232    } else if (xmlStrEqual(name, BAD_CAST "tag")) {
233        /* ignore */
234    } else if (xmlStrEqual(name, BAD_CAST "nd")) {
235        /* ignore */
236    } else if (xmlStrEqual(name, BAD_CAST "member")) {
237        /* ignore */
238    } else if (xmlStrEqual(name, BAD_CAST "osm")) {
239        printStatus();
240    } else if (xmlStrEqual(name, BAD_CAST "bound")) {
241        /* ignore */
242    } else {
243        fprintf(stderr, "%s: Unknown element name: %s\n", __FUNCTION__, name);
244    }
245}
246
247static void processNode(xmlTextReaderPtr reader) {
248    xmlChar *name;
249    name = xmlTextReaderName(reader);
250    if (name == NULL)
251        name = xmlStrdup(BAD_CAST "--");
252       
253    switch(xmlTextReaderNodeType(reader)) {
254        case XML_READER_TYPE_ELEMENT:
255            StartElement(reader, name);
256            if (xmlTextReaderIsEmptyElement(reader))
257                EndElement(name); /* No end_element for self closing tags! */
258            break;
259        case XML_READER_TYPE_END_ELEMENT:
260            EndElement(name);
261            break;
262        case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
263            /* Ignore */
264            break;
265        default:
266            fprintf(stderr, "Unknown node type %d\n", xmlTextReaderNodeType(reader));
267            break;
268    }
269
270    xmlFree(name);
271}
272
273static int streamFile(char *filename, int sanitize) {
274    xmlTextReaderPtr reader;
275    int ret = 0;
276
277    if (sanitize)
278        reader = sanitizerOpen(filename);
279    else
280        reader = inputUTF8(filename);
281
282    if (reader != NULL) {
283        ret = xmlTextReaderRead(reader);
284        while (ret == 1) {
285            processNode(reader);
286            ret = xmlTextReaderRead(reader);
287        }
288
289        if (ret != 0) {
290            fprintf(stderr, "%s : failed to parse\n", filename);
291            return ret;
292        }
293
294        xmlFreeTextReader(reader);
295    } else {
296        fprintf(stderr, "Unable to open %s\n", filename);
297        return 1;
298    }
299    return 0;
300}
301
302void exit_nicely(void)
303{
304    fprintf(stderr, "Error occurred, cleaning up\n");
305    out->cleanup();
306    mid->cleanup();
307    exit(1);
308}
309 
310static void usage(const char *arg0)
311{
312    int i;
313    const char *name = basename(arg0);
314
315    fprintf(stderr, "Usage:\n");
316    fprintf(stderr, "\t%s [options] planet.osm\n", name);
317    fprintf(stderr, "\t%s [options] planet.osm.{gz,bz2}\n", name);
318    fprintf(stderr, "\t%s [options] file1.osm file2.osm file3.osm\n", name);
319    fprintf(stderr, "\nThis will import the data from the OSM file(s) into a PostgreSQL database\n");
320    fprintf(stderr, "suitable for use by the Mapnik renderer\n");
321    fprintf(stderr, "\nOptions:\n");
322    fprintf(stderr, "   -a|--append\t\tAdd the OSM file into the database without removing\n");
323    fprintf(stderr, "              \t\texisting data.\n");
324    fprintf(stderr, "   -b|--bbox\t\tApply a bounding box filter on the imported data\n");
325    fprintf(stderr, "              \t\tMust be specified as: minlon,minlat,maxlon,maxlat\n");
326    fprintf(stderr, "              \t\te.g. --bbox -0.5,51.25,0.5,51.75\n");
327    fprintf(stderr, "   -c|--create\t\tRemove existing data from the database. This is the \n");
328    fprintf(stderr, "              \t\tdefault if --append is not specified.\n");
329    fprintf(stderr, "   -d|--database\tThe name of the PostgreSQL database to connect\n");
330    fprintf(stderr, "                \tto (default: gis).\n");
331    fprintf(stderr, "   -l|--latlong\t\tStore data in degrees of latitude & longitude.\n");
332    fprintf(stderr, "   -m|--merc\t\tStore data in proper spherical mercator, not OSM merc\n");
333    fprintf(stderr, "   -E|--proj num\tUse projection EPSG:num\n");
334    fprintf(stderr, "   -u|--utf8-sanitize\tRepair bad UTF8 input data (present in planet\n");
335    fprintf(stderr, "                \tdumps prior to August 2007). Adds about 10%% overhead.\n");
336    fprintf(stderr, "   -p|--prefix\t\tPrefix for table names (default planet_osm)\n");
337#ifdef BROKEN_SLIM
338    fprintf(stderr, "   -s|--slim\t\tStore temporary data in the database. This greatly\n");
339    fprintf(stderr, "            \t\treduces the RAM usage but is much slower.\n");
340#endif
341    fprintf(stderr, "   -U|--username\tPostgresql user name.\n");
342    fprintf(stderr, "   -W|--password\tForce password prompt.\n");
343    fprintf(stderr, "   -H|--host\t\tDatabase server hostname or socket location.\n");
344    fprintf(stderr, "   -P|--port\t\tDatabase server port.\n");
345    fprintf(stderr, "   -h|--help\t\tHelp information.\n");
346    fprintf(stderr, "   -v|--verbose\t\tVerbose output.\n");
347    fprintf(stderr, "\n");
348    if(!verbose)
349    {
350        fprintf(stderr, "Add -v to display supported projections.\n");
351        fprintf(stderr, "Use -E to access any espg projections (usually in /usr/share/proj/epsg)\n" );
352    }
353    else
354    {
355        fprintf(stderr, "Supported projections:\n" );
356        for(i=0; i<PROJ_COUNT; i++ )
357        {
358            fprintf( stderr, "%-20s(%2s) SRS:%6d %s\n", 
359                    Projection_Info[i].descr, Projection_Info[i].option, Projection_Info[i].srs, Projection_Info[i].proj4text);
360        }
361    }
362}
363
364const char *build_conninfo(const char *db, const char *username, const char *password, const char *host, const char *port)
365{
366    static char conninfo[1024];
367
368    conninfo[0]='\0';
369    strcat(conninfo, "dbname='");
370    strcat(conninfo, db);
371    strcat(conninfo, "'");
372
373    if (username) {
374        strcat(conninfo, " user='");
375        strcat(conninfo, username);
376        strcat(conninfo, "'");
377    }
378    if (password) {
379        strcat(conninfo, " password='");
380        strcat(conninfo, password);
381        strcat(conninfo, "'");
382    }
383    if (host) {
384        strcat(conninfo, " host='");
385        strcat(conninfo, host);
386        strcat(conninfo, "'");
387    }
388    if (port) {
389        strcat(conninfo, " port='");
390        strcat(conninfo, port);
391        strcat(conninfo, "'");
392    }
393
394    return conninfo;
395}
396
397int main(int argc, char *argv[])
398{
399    int append=0;
400    int create=0;
401    int slim=0;
402    int sanitize=0;
403    int pass_prompt=0;
404    int projection = PROJ_MERC;
405    const char *db = "gis";
406    const char *username=NULL;
407    const char *host=NULL;
408    const char *password=NULL;
409    const char *port = "5432";
410    const char *conninfo = NULL;
411    const char *prefix = "planet_osm";
412    PGconn *sql_conn;
413
414    fprintf(stderr, "osm2pgsql SVN version %s $Rev: 7141 $ \n\n", VERSION);
415
416    while (1) {
417        int c, option_index = 0;
418        static struct option long_options[] = {
419            {"append",   0, 0, 'a'},
420            {"bbox",     1, 0, 'b'},
421            {"create",   0, 0, 'c'},
422            {"database", 1, 0, 'd'},
423            {"latlong",  0, 0, 'l'},
424            {"verbose",  0, 0, 'v'},
425#ifdef BROKEN_SLIM
426            {"slim",     0, 0, 's'},
427#endif
428            {"prefix",   1, 0, 'p'},
429            {"proj",     1, 0, 'E'},
430            {"merc",     0, 0, 'm'},
431            {"utf8-sanitize", 0, 0, 'u'},
432            {"username", 1, 0, 'U'},
433            {"password", 0, 0, 'W'},
434            {"host",     1, 0, 'H'},
435            {"port",     1, 0, 'P'},
436            {"help",     0, 0, 'h'},
437            {0, 0, 0, 0}
438        };
439
440        c = getopt_long (argc, argv, "ab:cd:hlmp:suvU:WH:P:E:", long_options, &option_index);
441        if (c == -1)
442            break;
443
444        switch (c) {
445            case 'a': append=1;   break;
446            case 'b': bbox=optarg; break;
447            case 'c': create=1;   break;
448            case 'v': verbose=1;  break;
449#ifdef BROKEN_SLIM
450            case 's': slim=1;     break;
451#endif
452            case 'u': sanitize=1; break;
453            case 'l': projection=PROJ_LATLONG;  break;
454            case 'm': projection=PROJ_SPHERE_MERC; break;
455            case 'E': projection=-atoi(optarg); break;
456            case 'p': prefix=optarg; break;
457            case 'd': db=optarg;  break;
458            case 'U': username=optarg; break;
459            case 'W': pass_prompt=1; break;
460            case 'H': host=optarg; break;
461            case 'P': port=optarg; break;
462
463            case 'h':
464            case '?':
465            default:
466                usage(argv[0]);
467                exit(EXIT_FAILURE);
468        }
469    }
470
471    if (argc == optind) {  // No non-switch arguments
472        usage(argv[0]);
473        exit(EXIT_FAILURE);
474    }
475
476    if (append && create) {
477        fprintf(stderr, "Error: --append and --create options can not be used at the same time!\n");
478        exit(EXIT_FAILURE);
479    }
480
481    if (username || pass_prompt)
482        password = simple_prompt("Password:", 100, 0);
483
484    conninfo = build_conninfo(db, username, password, host, port);
485    sql_conn = PQconnectdb(conninfo);
486    if (PQstatus(sql_conn) != CONNECTION_OK) {
487        fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(sql_conn));
488        exit(EXIT_FAILURE);
489    }
490    PQfinish(sql_conn);
491
492    text_init();
493    initList(&tags);
494    initList(&nds);
495    initList(&members);
496
497    count_node = max_node = 0;
498    count_way = max_way = 0;
499    count_rel = max_rel = 0;
500
501    LIBXML_TEST_VERSION
502
503    project_init(projection);
504    fprintf(stderr, "Using projection SRS %d (%s)\n", 
505        project_getprojinfo()->srs, project_getprojinfo()->descr );
506
507    if (parse_bbox())
508        return 1;
509
510    mid = slim ? &mid_pgsql : &mid_ram;
511    out = &out_pgsql;
512
513    out->start(conninfo, prefix, append);
514
515    while (optind < argc) {
516        fprintf(stderr, "\nReading in file: %s\n", argv[optind]);
517        mid->start(conninfo, projection==PROJ_LATLONG);
518        if (streamFile(argv[optind], sanitize) != 0)
519            exit_nicely();
520        mid->end();
521        mid->analyze();
522
523        //mid->iterate_nodes(out->node);
524        mid->iterate_relations(out->relation);
525        mid->iterate_ways(out->way);
526        mid->stop();
527        optind++;
528    }
529
530    xmlCleanupParser();
531    xmlMemoryDump();
532
533    if (count_node || count_way || count_rel) {
534        fprintf(stderr, "\n");
535        fprintf(stderr, "Node stats: total(%d), max(%d)\n", count_node, max_node);
536        fprintf(stderr, "Way stats: total(%d), max(%d)\n", count_way, max_way);
537        fprintf(stderr, "Relation stats: total(%d), max(%d)\n", count_rel, max_rel);
538    }
539    //fprintf(stderr, "\n\nEnding data import\n");
540    //out->process(mid);
541    out->stop(append);
542
543    project_exit();
544    text_exit();
545    fprintf(stderr, "\n");
546
547    return 0;
548}
Note: See TracBrowser for help on using the repository browser.