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

Last change on this file since 8319 was 8319, checked in by jonb, 12 years ago
osm2pgsql: Make spherical mercator the default, old format is now -Moldmerc
  • Property svn:keywords set to Rev
File size: 18.6 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 output_t *out;
56
57/* Since {node,way} elements are not nested we can guarantee the
58   values in an end tag must match those of the corresponding
59   start tag and can therefore be cached.
60*/
61static double node_lon, node_lat;
62static struct keyval tags;
63static int *nds;
64static int nd_count, nd_max;
65static struct member *members;
66static int member_count, member_max;
67static int osm_id;
68
69#define INIT_MAX_MEMBERS 64
70#define INIT_MAX_NODES  4096
71
72int verbose;
73static void realloc_nodes();
74static void realloc_members();
75
76// Bounding box to filter imported data
77const char *bbox = NULL;
78static double minlon, minlat, maxlon, maxlat;
79
80static void printStatus(void)
81{
82    fprintf(stderr, "\rProcessing: Node(%dk) Way(%dk) Relation(%dk)",
83            count_node/1000, count_way/1000, count_rel/1000);
84}
85
86static int parse_bbox(void)
87{
88    int n;
89
90    if (!bbox)
91        return 0;
92
93    n = sscanf(bbox, "%lf,%lf,%lf,%lf", &minlon, &minlat, &maxlon, &maxlat);
94    if (n != 4) {
95        fprintf(stderr, "Bounding box must be specified like: minlon,minlat,maxlon,maxlat\n");
96        return 1;
97    }
98    if (maxlon <= minlon) {
99        fprintf(stderr, "Bounding box failed due to maxlon <= minlon\n");
100        return 1;
101    }
102    if (maxlat <= minlat) {
103        fprintf(stderr, "Bounding box failed due to maxlat <= minlat\n");
104        return 1;
105    }
106    printf("Applying Bounding box: %f,%f to %f,%f\n", minlon,minlat,maxlon,maxlat);
107    return 0;
108}
109
110static int node_wanted(double lat, double lon)
111{
112    if (!bbox)
113        return 1;
114
115    if (lat < minlat || lat > maxlat)
116        return 0;
117    if (lon < minlon || lon > maxlon)
118        return 0;
119    return 1;
120}
121
122void StartElement(xmlTextReaderPtr reader, const xmlChar *name)
123{
124    xmlChar *xid, *xlat, *xlon, *xk, *xv, *xrole, *xtype;
125    char *k;
126
127    if (xmlStrEqual(name, BAD_CAST "node")) {
128        xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
129        xlon = xmlTextReaderGetAttribute(reader, BAD_CAST "lon");
130        xlat = xmlTextReaderGetAttribute(reader, BAD_CAST "lat");
131        assert(xid); assert(xlon); assert(xlat);
132
133        osm_id  = strtol((char *)xid, NULL, 10);
134        node_lon = strtod((char *)xlon, NULL);
135        node_lat = strtod((char *)xlat, NULL);
136
137        if (osm_id > max_node)
138            max_node = osm_id;
139
140        count_node++;
141        if (count_node%10000 == 0)
142            printStatus();
143
144        xmlFree(xid);
145        xmlFree(xlon);
146        xmlFree(xlat);
147    } else if (xmlStrEqual(name, BAD_CAST "tag")) {
148        xk = xmlTextReaderGetAttribute(reader, BAD_CAST "k");
149        assert(xk);
150
151        /* 'created_by' and 'source' are common and not interesting to mapnik renderer */
152        if (strcmp((char *)xk, "created_by") && strcmp((char *)xk, "source")) {
153            char *p;
154            xv = xmlTextReaderGetAttribute(reader, BAD_CAST "v");
155            assert(xv);
156            k  = (char *)xmlStrdup(xk);
157            while ((p = strchr(k, ' ')))
158                *p = '_';
159
160            addItem(&tags, k, (char *)xv, 0);
161            xmlFree(k);
162            xmlFree(xv);
163        }
164        xmlFree(xk);
165    } else if (xmlStrEqual(name, BAD_CAST "way")) {
166        xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
167        assert(xid);
168        osm_id   = strtol((char *)xid, NULL, 10);
169
170        if (osm_id > max_way)
171            max_way = osm_id;
172
173        count_way++;
174        if (count_way%1000 == 0)
175            printStatus();
176
177        nd_count = 0;
178        xmlFree(xid);
179    } else if (xmlStrEqual(name, BAD_CAST "nd")) {
180        xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "ref");
181        assert(xid);
182
183        nds[nd_count++] = strtol( (char *)xid, NULL, 10 );
184
185        if( nd_count >= nd_max )
186          realloc_nodes();
187        xmlFree(xid);
188    } else if (xmlStrEqual(name, BAD_CAST "relation")) {
189        xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
190        assert(xid);
191        osm_id   = strtol((char *)xid, NULL, 10);
192
193        if (osm_id > max_rel)
194            max_rel = osm_id;
195
196        count_rel++;
197        if (count_rel%1000 == 0)
198            printStatus();
199
200        member_count = 0;
201        xmlFree(xid);
202    } else if (xmlStrEqual(name, BAD_CAST "member")) {
203        xrole = xmlTextReaderGetAttribute(reader, BAD_CAST "role");
204        assert(xrole);
205
206        xtype = xmlTextReaderGetAttribute(reader, BAD_CAST "type");
207        assert(xtype);
208
209        xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "ref");
210        assert(xid);
211
212        members[member_count].id   = strtol( (char *)xid, NULL, 0 );
213        members[member_count].role = strdup( (char *)xrole );
214       
215        /* Currently we are only interested in 'way' members since these form polygons with holes */
216        if (xmlStrEqual(xtype, BAD_CAST "way"))
217            members[member_count].type = OSMTYPE_WAY;
218        if (xmlStrEqual(xtype, BAD_CAST "node"))
219            members[member_count].type = OSMTYPE_NODE;
220        if (xmlStrEqual(xtype, BAD_CAST "relation"))
221            members[member_count].type = OSMTYPE_RELATION;
222        member_count++;
223
224        if( member_count >= member_max )
225          realloc_members();
226        xmlFree(xid);
227        xmlFree(xrole);
228        xmlFree(xtype);
229    } else if (xmlStrEqual(name, BAD_CAST "osm")) {
230        /* ignore */
231    } else if (xmlStrEqual(name, BAD_CAST "bound")) {
232        /* ignore */
233    } else {
234        fprintf(stderr, "%s: Unknown element name: %s\n", __FUNCTION__, name);
235    }
236}
237
238static void resetMembers()
239{
240  int i;
241  for(i=0; i<member_count; i++ )
242    free( members[i].role );
243}
244
245void EndElement(const xmlChar *name)
246{
247    if (xmlStrEqual(name, BAD_CAST "node")) {
248        if (node_wanted(node_lat, node_lon)) {
249            reproject(&node_lat, &node_lon);
250            out->node_add(osm_id, node_lat, node_lon, &tags);
251        }
252        resetList(&tags);
253    } else if (xmlStrEqual(name, BAD_CAST "way")) {
254        out->way_add(osm_id, nds, nd_count, &tags );
255        resetList(&tags);
256    } else if (xmlStrEqual(name, BAD_CAST "relation")) {
257        out->relation_add(osm_id, members, member_count, &tags);
258        resetList(&tags);
259        resetMembers();
260    } else if (xmlStrEqual(name, BAD_CAST "tag")) {
261        /* ignore */
262    } else if (xmlStrEqual(name, BAD_CAST "nd")) {
263        /* ignore */
264    } else if (xmlStrEqual(name, BAD_CAST "member")) {
265        /* ignore */
266    } else if (xmlStrEqual(name, BAD_CAST "osm")) {
267        printStatus();
268    } else if (xmlStrEqual(name, BAD_CAST "bound")) {
269        /* ignore */
270    } else {
271        fprintf(stderr, "%s: Unknown element name: %s\n", __FUNCTION__, name);
272    }
273}
274
275static void processNode(xmlTextReaderPtr reader) {
276    xmlChar *name;
277    name = xmlTextReaderName(reader);
278    if (name == NULL)
279        name = xmlStrdup(BAD_CAST "--");
280       
281    switch(xmlTextReaderNodeType(reader)) {
282        case XML_READER_TYPE_ELEMENT:
283            StartElement(reader, name);
284            if (xmlTextReaderIsEmptyElement(reader))
285                EndElement(name); /* No end_element for self closing tags! */
286            break;
287        case XML_READER_TYPE_END_ELEMENT:
288            EndElement(name);
289            break;
290        case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
291            /* Ignore */
292            break;
293        default:
294            fprintf(stderr, "Unknown node type %d\n", xmlTextReaderNodeType(reader));
295            break;
296    }
297
298    xmlFree(name);
299}
300
301static int streamFile(char *filename, int sanitize) {
302    xmlTextReaderPtr reader;
303    int ret = 0;
304
305    if (sanitize)
306        reader = sanitizerOpen(filename);
307    else
308        reader = inputUTF8(filename);
309
310    if (reader != NULL) {
311        ret = xmlTextReaderRead(reader);
312        while (ret == 1) {
313            processNode(reader);
314            ret = xmlTextReaderRead(reader);
315        }
316
317        if (ret != 0) {
318            fprintf(stderr, "%s : failed to parse\n", filename);
319            return ret;
320        }
321
322        xmlFreeTextReader(reader);
323    } else {
324        fprintf(stderr, "Unable to open %s\n", filename);
325        return 1;
326    }
327    return 0;
328}
329
330void exit_nicely(void)
331{
332    fprintf(stderr, "Error occurred, cleaning up\n");
333    out->cleanup();
334    exit(1);
335}
336 
337static void usage(const char *arg0)
338{
339    int i;
340    const char *name = basename(arg0);
341
342    fprintf(stderr, "Usage:\n");
343    fprintf(stderr, "\t%s [options] planet.osm\n", name);
344    fprintf(stderr, "\t%s [options] planet.osm.{gz,bz2}\n", name);
345    fprintf(stderr, "\t%s [options] file1.osm file2.osm file3.osm\n", name);
346    fprintf(stderr, "\nThis will import the data from the OSM file(s) into a PostgreSQL database\n");
347    fprintf(stderr, "suitable for use by the Mapnik renderer\n");
348    fprintf(stderr, "\nOptions:\n");
349    fprintf(stderr, "   -a|--append\t\tAdd the OSM file into the database without removing\n");
350    fprintf(stderr, "              \t\texisting data.\n");
351    fprintf(stderr, "   -b|--bbox\t\tApply a bounding box filter on the imported data\n");
352    fprintf(stderr, "              \t\tMust be specified as: minlon,minlat,maxlon,maxlat\n");
353    fprintf(stderr, "              \t\te.g. --bbox -0.5,51.25,0.5,51.75\n");
354    fprintf(stderr, "   -c|--create\t\tRemove existing data from the database. This is the \n");
355    fprintf(stderr, "              \t\tdefault if --append is not specified.\n");
356    fprintf(stderr, "   -d|--database\tThe name of the PostgreSQL database to connect\n");
357    fprintf(stderr, "                \tto (default: gis).\n");
358    fprintf(stderr, "   -l|--latlong\t\tStore data in degrees of latitude & longitude.\n");
359    fprintf(stderr, "   -m|--merc\t\tStore data in proper spherical mercator (default)\n");
360    fprintf(stderr, "   -M|--oldmerc\t\tStore data in the legacy OSM mercator format\n");
361    fprintf(stderr, "   -E|--proj num\tUse projection EPSG:num\n");
362    fprintf(stderr, "   -u|--utf8-sanitize\tRepair bad UTF8 input data (present in planet\n");
363    fprintf(stderr, "                \tdumps prior to August 2007). Adds about 10%% overhead.\n");
364    fprintf(stderr, "   -p|--prefix\t\tPrefix for table names (default planet_osm)\n");
365    fprintf(stderr, "   -s|--slim\t\tStore temporary data in the database. This greatly\n");
366    fprintf(stderr, "            \t\treduces the RAM usage but is much slower.\n");
367    fprintf(stderr, "   -C|--cache\t\tOnly for slim mode: Use upto this many MB for caching nodes\n");
368    fprintf(stderr, "             \t\tDefault is 800\n");
369    fprintf(stderr, "   -U|--username\tPostgresql user name.\n");
370    fprintf(stderr, "   -W|--password\tForce password prompt.\n");
371    fprintf(stderr, "   -H|--host\t\tDatabase server hostname or socket location.\n");
372    fprintf(stderr, "   -P|--port\t\tDatabase server port.\n");
373    fprintf(stderr, "   -h|--help\t\tHelp information.\n");
374    fprintf(stderr, "   -v|--verbose\t\tVerbose output.\n");
375    fprintf(stderr, "\n");
376    if(!verbose)
377    {
378        fprintf(stderr, "Add -v to display supported projections.\n");
379        fprintf(stderr, "Use -E to access any espg projections (usually in /usr/share/proj/epsg)\n" );
380    }
381    else
382    {
383        fprintf(stderr, "Supported projections:\n" );
384        for(i=0; i<PROJ_COUNT; i++ )
385        {
386            fprintf( stderr, "%-20s(%2s) SRS:%6d %s\n", 
387                    Projection_Info[i].descr, Projection_Info[i].option, Projection_Info[i].srs, Projection_Info[i].proj4text);
388        }
389    }
390}
391
392const char *build_conninfo(const char *db, const char *username, const char *password, const char *host, const char *port)
393{
394    static char conninfo[1024];
395
396    conninfo[0]='\0';
397    strcat(conninfo, "dbname='");
398    strcat(conninfo, db);
399    strcat(conninfo, "'");
400
401    if (username) {
402        strcat(conninfo, " user='");
403        strcat(conninfo, username);
404        strcat(conninfo, "'");
405    }
406    if (password) {
407        strcat(conninfo, " password='");
408        strcat(conninfo, password);
409        strcat(conninfo, "'");
410    }
411    if (host) {
412        strcat(conninfo, " host='");
413        strcat(conninfo, host);
414        strcat(conninfo, "'");
415    }
416    if (port) {
417        strcat(conninfo, " port='");
418        strcat(conninfo, port);
419        strcat(conninfo, "'");
420    }
421
422    return conninfo;
423}
424
425static void realloc_nodes()
426{
427  if( nd_max == 0 )
428    nd_max = INIT_MAX_NODES;
429  else
430    nd_max <<= 1;
431   
432  nds = realloc( nds, nd_max * sizeof( nds[0] ) );
433  if( !nds )
434  {
435    fprintf( stderr, "Failed to expand node list to %d\n", nd_max );
436    exit_nicely();
437  }
438}
439
440static void realloc_members()
441{
442  if( member_max == 0 )
443    member_max = INIT_MAX_NODES;
444  else
445    member_max <<= 1;
446   
447  members = realloc( members, member_max * sizeof( members[0] ) );
448  if( !members )
449  {
450    fprintf( stderr, "Failed to expand member list to %d\n", member_max );
451    exit_nicely();
452  }
453}
454
455int main(int argc, char *argv[])
456{
457    int append=0;
458    int create=0;
459    int slim=0;
460    int sanitize=0;
461    int pass_prompt=0;
462    int projection = PROJ_SPHERE_MERC;
463    const char *db = "gis";
464    const char *username=NULL;
465    const char *host=NULL;
466    const char *password=NULL;
467    const char *port = "5432";
468    const char *conninfo = NULL;
469    const char *prefix = "planet_osm";
470    int cache = 800;
471    struct output_options options;
472    PGconn *sql_conn;
473
474    fprintf(stderr, "osm2pgsql SVN version %s $Rev: 8319 $ \n\n", VERSION);
475
476    while (1) {
477        int c, option_index = 0;
478        static struct option long_options[] = {
479            {"append",   0, 0, 'a'},
480            {"bbox",     1, 0, 'b'},
481            {"create",   0, 0, 'c'},
482            {"database", 1, 0, 'd'},
483            {"latlong",  0, 0, 'l'},
484            {"verbose",  0, 0, 'v'},
485            {"slim",     0, 0, 's'},
486            {"prefix",   1, 0, 'p'},
487            {"proj",     1, 0, 'E'},
488            {"merc",     0, 0, 'm'},
489            {"oldmerc",  0, 0, 'M'},
490            {"utf8-sanitize", 0, 0, 'u'},
491            {"cache",    1, 0, 'C'},
492            {"username", 1, 0, 'U'},
493            {"password", 0, 0, 'W'},
494            {"host",     1, 0, 'H'},
495            {"port",     1, 0, 'P'},
496            {"help",     0, 0, 'h'},
497            {0, 0, 0, 0}
498        };
499
500        c = getopt_long (argc, argv, "ab:cd:hlmMp:suvU:WH:P:E:C:", long_options, &option_index);
501        if (c == -1)
502            break;
503
504        switch (c) {
505            case 'a': append=1;   break;
506            case 'b': bbox=optarg; break;
507            case 'c': create=1;   break;
508            case 'v': verbose=1;  break;
509            case 's': slim=1;     break;
510            case 'u': sanitize=1; break;
511            case 'l': projection=PROJ_LATLONG;  break;
512            case 'm': projection=PROJ_SPHERE_MERC; break;
513            case 'M': projection=PROJ_MERC; break;
514            case 'E': projection=-atoi(optarg); break;
515            case 'p': prefix=optarg; break;
516            case 'd': db=optarg;  break;
517            case 'C': cache = atoi(optarg); break;
518            case 'U': username=optarg; break;
519            case 'W': pass_prompt=1; break;
520            case 'H': host=optarg; break;
521            case 'P': port=optarg; break;
522
523            case 'h':
524            case '?':
525            default:
526                usage(argv[0]);
527                exit(EXIT_FAILURE);
528        }
529    }
530
531    if (argc == optind) {  // No non-switch arguments
532        usage(argv[0]);
533        exit(EXIT_FAILURE);
534    }
535
536    if (append && create) {
537        fprintf(stderr, "Error: --append and --create options can not be used at the same time!\n");
538        exit(EXIT_FAILURE);
539    }
540   
541    if( cache < 0 ) cache = 0;
542
543    if (username || pass_prompt)
544        password = simple_prompt("Password:", 100, 0);
545
546    conninfo = build_conninfo(db, username, password, host, port);
547    sql_conn = PQconnectdb(conninfo);
548    if (PQstatus(sql_conn) != CONNECTION_OK) {
549        fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(sql_conn));
550        exit(EXIT_FAILURE);
551    }
552    PQfinish(sql_conn);
553
554    text_init();
555    initList(&tags);
556
557    count_node = max_node = 0;
558    count_way = max_way = 0;
559    count_rel = max_rel = 0;
560
561    LIBXML_TEST_VERSION
562
563    project_init(projection);
564    fprintf(stderr, "Using projection SRS %d (%s)\n", 
565        project_getprojinfo()->srs, project_getprojinfo()->descr );
566
567    if (parse_bbox())
568        return 1;
569
570    options.conninfo = conninfo;
571    options.prefix = prefix;
572    options.append = append;
573    options.slim = slim;
574    options.projection = project_getprojinfo()->srs;
575    options.scale = (projection==PROJ_LATLONG)?10000000:100;
576    options.mid = slim ? &mid_pgsql : &mid_ram;
577    options.cache = cache;
578    out = &out_pgsql;
579
580    out->start(&options);
581
582    realloc_nodes();
583    realloc_members();
584
585    while (optind < argc) {
586        fprintf(stderr, "\nReading in file: %s\n", argv[optind]);
587        if (streamFile(argv[optind], sanitize) != 0)
588            exit_nicely();
589        optind++;
590    }
591
592    xmlCleanupParser();
593    xmlMemoryDump();
594   
595    if (count_node || count_way || count_rel) {
596        fprintf(stderr, "\n");
597        fprintf(stderr, "Node stats: total(%d), max(%d)\n", count_node, max_node);
598        fprintf(stderr, "Way stats: total(%d), max(%d)\n", count_way, max_way);
599        fprintf(stderr, "Relation stats: total(%d), max(%d)\n", count_rel, max_rel);
600    }
601    out->stop();
602   
603    free(nds);
604    free(members);
605
606    project_exit();
607    text_exit();
608    fprintf(stderr, "\n");
609
610    return 0;
611}
Note: See TracBrowser for help on using the repository browser.