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

Revision 24605, 19.9 KB checked in by jonb, 3 years ago (diff)

osm2pgsql: Add command line switch to include natural=coastline ways in the database. Based on patch from Dane Springmeyer. Closes #3350

  • Property svn:keywords set to Rev
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#include "config.h"
27
28#define _GNU_SOURCE
29#include <stdio.h>
30#include <unistd.h>
31#include <stdlib.h>
32#include <string.h>
33#include <assert.h>
34#include <getopt.h>
35#include <libgen.h>
36#include <time.h>
37
38#include <libpq-fe.h>
39
40#include <libxml/xmlstring.h>
41#include <libxml/xmlreader.h>
42
43#include "osmtypes.h"
44#include "build_geometry.h"
45#include "middle-pgsql.h"
46#include "middle-ram.h"
47#include "output-pgsql.h"
48#include "output-gazetteer.h"
49#include "output-null.h"
50#include "sanitizer.h"
51#include "reprojection.h"
52#include "text-tree.h"
53#include "input.h"
54#include "sprompt.h"
55#include "parse-xml2.h"
56#include "parse-primitive.h"
57
58#ifdef BUILD_READER_PBF
59#  include "parse-pbf.h"
60#endif
61
62#define INIT_MAX_MEMBERS 64
63#define INIT_MAX_NODES  4096
64
65int verbose;
66
67// Data structure carrying all parsing related variables
68static struct osmdata_t osmdata = { 
69  .filetype = FILETYPE_NONE,
70  .action   = ACTION_NONE,
71  .bbox     = NULL
72};
73
74
75static int parse_bbox(struct osmdata_t *osmdata)
76{
77    int n;
78
79    if (!osmdata->bbox)
80        return 0;
81
82    n = sscanf(osmdata->bbox, "%lf,%lf,%lf,%lf", &(osmdata->minlon), &(osmdata->minlat), &(osmdata->maxlon), &(osmdata->maxlat));
83    if (n != 4) {
84        fprintf(stderr, "Bounding box must be specified like: minlon,minlat,maxlon,maxlat\n");
85        return 1;
86    }
87    if (osmdata->maxlon <= osmdata->minlon) {
88        fprintf(stderr, "Bounding box failed due to maxlon <= minlon\n");
89        return 1;
90    }
91    if (osmdata->maxlat <= osmdata->minlat) {
92        fprintf(stderr, "Bounding box failed due to maxlat <= minlat\n");
93        return 1;
94    }
95    printf("Applying Bounding box: %f,%f to %f,%f\n", osmdata->minlon, osmdata->minlat, osmdata->maxlon, osmdata->maxlat);
96    return 0;
97}
98
99
100
101void exit_nicely()
102{
103    fprintf(stderr, "Error occurred, cleaning up\n");
104    osmdata.out->cleanup();
105    exit(1);
106}
107 
108static void short_usage(char *arg0)
109{
110    const char *name = basename(arg0);
111
112    fprintf(stderr, "Usage error. For further information see:\n");
113    fprintf(stderr, "\t%s -h|--help\n", name);
114}
115
116static void long_usage(char *arg0)
117{
118    int i;
119    const char *name = basename(arg0);
120
121    printf("Usage:\n");
122    printf("\t%s [options] planet.osm\n", name);
123    printf("\t%s [options] planet.osm.{gz,bz2}\n", name);
124    printf("\t%s [options] file1.osm file2.osm file3.osm\n", name);
125    printf("\nThis will import the data from the OSM file(s) into a PostgreSQL database\n");
126    printf("suitable for use by the Mapnik renderer\n");
127    printf("\nOptions:\n");
128    printf("   -a|--append\t\tAdd the OSM file into the database without removing\n");
129    printf("              \t\texisting data.\n");
130    printf("   -b|--bbox\t\tApply a bounding box filter on the imported data\n");
131    printf("              \t\tMust be specified as: minlon,minlat,maxlon,maxlat\n");
132    printf("              \t\te.g. --bbox -0.5,51.25,0.5,51.75\n");
133    printf("   -c|--create\t\tRemove existing data from the database. This is the \n");
134    printf("              \t\tdefault if --append is not specified.\n");
135    printf("   -d|--database\tThe name of the PostgreSQL database to connect\n");
136    printf("                \tto (default: gis).\n");
137    printf("   -i|--tablespace-index\tThe name of the PostgreSQL tablespace where indexes will be create\n");
138    printf("                \tto (default: pg_default).\n");
139    printf("   -l|--latlong\t\tStore data in degrees of latitude & longitude.\n");
140    printf("   -m|--merc\t\tStore data in proper spherical mercator (default)\n");
141    printf("   -M|--oldmerc\t\tStore data in the legacy OSM mercator format\n");
142    printf("   -E|--proj num\tUse projection EPSG:num\n");
143    printf("   -u|--utf8-sanitize\tRepair bad UTF8 input data (present in planet\n");
144    printf("                \tdumps prior to August 2007). Adds about 10%% overhead.\n");
145    printf("   -p|--prefix\t\tPrefix for table names (default planet_osm)\n");
146    printf("   -s|--slim\t\tStore temporary data in the database. This greatly\n");
147    printf("            \t\treduces the RAM usage but is much slower.\n");
148
149    if (sizeof(int*) == 4) {
150        printf("            \t\tYou are running this on 32bit system, so at most\n");
151        printf("            \t\t3GB of RAM will be used. If you encounter unexpected\n");
152        printf("            \t\texceptions during import, you should try this switch.\n");
153    }
154   
155    printf("   -S|--style\t\tLocation of the style file. Defaults to " OSM2PGSQL_DATADIR "/default.style\n");
156    printf("   -C|--cache\t\tOnly for slim mode: Use upto this many MB for caching nodes\n");
157    printf("             \t\tDefault is 800\n");
158    printf("   -U|--username\tPostgresql user name.\n");
159    printf("   -W|--password\tForce password prompt.\n");
160    printf("   -H|--host\t\tDatabase server hostname or socket location.\n");
161    printf("   -P|--port\t\tDatabase server port.\n");
162    printf("   -e|--expire-tiles [min_zoom-]max_zoom\tCreate a tile expiry list.\n");
163    printf("   -o|--expire-output filename\tOutput filename for expired tiles list.\n");
164    printf("   -r|--input-reader\tInput frontend.\n");
165    printf("              \t\tlibxml2   - Parse XML using libxml2. (default)\n");
166    printf("              \t\tprimitive - Primitive XML parsing.\n");
167#ifdef BUILD_READER_PBF
168    printf("              \t\tpbf       - OSM binary format.\n");
169#endif
170    printf("   -O|--output\t\tOutput backend.\n");
171    printf("              \t\tpgsql - Output to a PostGIS database. (default)\n");
172    printf("              \t\tgazetteer - Output to a PostGIS database suitable for gazetteer\n");
173    printf("              \t\tnull  - No output. Useful for testing.\n");
174    printf("   -x|--extra-attributes\n");
175    printf("              \t\tInclude attributes for each object in the database.\n");
176    printf("              \t\tThis includes the username, userid, timestamp and version.\n"); 
177    printf("              \t\tNote: this option also requires additional entries in your style file.\n"); 
178    printf("   -k|--hstore\t\tGenerate an additional hstore (key/value) column to  postgresql tables\n");
179    printf("   -z|--hstore-column\tGenerate an additional hstore (key/value) column to containing all tags\n");
180    printf("                     \tthat start with the specified string, eg --hstore-column \"name:\" will\n");
181    printf("                     \tproduce an extra hstore column that contains all name:xx tags\n");
182    printf("   -G|--multi-geometry\tGenerate multi-geometry features in postgresql tables.\n");
183    printf("   -K|--keep-coastlines\tKeep coastline data rather than filtering it out.\n");
184    printf("              \t\tBy default natural=coastline tagged data will be discarded based on the\n");
185    printf("              \t\tassumption that post-processed Coastline Checker shapefiles will be used.\n");
186    printf("   -h|--help\t\tHelp information.\n");
187    printf("   -v|--verbose\t\tVerbose output.\n");
188    printf("\n");
189    if(!verbose)
190    {
191        printf("Add -v to display supported projections.\n");
192        printf("Use -E to access any espg projections (usually in /usr/share/proj/epsg)\n" );
193    }
194    else
195    {
196        printf("Supported projections:\n" );
197        for(i=0; i<PROJ_COUNT; i++ )
198        {
199            printf( "%-20s(%2s) SRS:%6d %s\n", 
200                    Projection_Info[i].descr, Projection_Info[i].option, Projection_Info[i].srs, Projection_Info[i].proj4text);
201        }
202    }
203}
204
205const char *build_conninfo(const char *db, const char *username, const char *password, const char *host, const char *port)
206{
207    static char conninfo[1024];
208
209    conninfo[0]='\0';
210    strcat(conninfo, "dbname='");
211    strcat(conninfo, db);
212    strcat(conninfo, "'");
213
214    if (username) {
215        strcat(conninfo, " user='");
216        strcat(conninfo, username);
217        strcat(conninfo, "'");
218    }
219    if (password) {
220        strcat(conninfo, " password='");
221        strcat(conninfo, password);
222        strcat(conninfo, "'");
223    }
224    if (host) {
225        strcat(conninfo, " host='");
226        strcat(conninfo, host);
227        strcat(conninfo, "'");
228    }
229    if (port) {
230        strcat(conninfo, " port='");
231        strcat(conninfo, port);
232        strcat(conninfo, "'");
233    }
234
235    return conninfo;
236}
237
238void realloc_nodes(struct osmdata_t *osmdata)
239{
240  if( osmdata->nd_max == 0 )
241    osmdata->nd_max = INIT_MAX_NODES;
242  else
243    osmdata->nd_max <<= 1;
244   
245  osmdata->nds = realloc( osmdata->nds, osmdata->nd_max * sizeof( osmdata->nds[0] ) );
246  if( !osmdata->nds )
247  {
248    fprintf( stderr, "Failed to expand node list to %d\n", osmdata->nd_max );
249    exit_nicely();
250  }
251}
252
253void realloc_members(struct osmdata_t *osmdata)
254{
255  if( osmdata->member_max == 0 )
256    osmdata->member_max = INIT_MAX_NODES;
257  else
258    osmdata->member_max <<= 1;
259   
260  osmdata->members = realloc( osmdata->members, osmdata->member_max * sizeof( osmdata->members[0] ) );
261  if( !osmdata->members )
262  {
263    fprintf( stderr, "Failed to expand member list to %d\n", osmdata->member_max );
264    exit_nicely();
265  }
266}
267
268void resetMembers(struct osmdata_t *osmdata)
269{
270  for(unsigned i = 0; i < osmdata->member_count; i++ )
271    free( osmdata->members[i].role );
272}
273
274void printStatus(struct osmdata_t *osmdata)
275{
276    fprintf(stderr, "\rProcessing: Node(%dk) Way(%dk) Relation(%dk)",
277            osmdata->count_node/1000, osmdata->count_way/1000, osmdata->count_rel/1000);
278}
279
280int node_wanted(struct osmdata_t *osmdata, double lat, double lon)
281{
282    if (!osmdata->bbox)
283        return 1;
284
285    if (lat < osmdata->minlat || lat > osmdata->maxlat)
286        return 0;
287    if (lon < osmdata->minlon || lon > osmdata->maxlon)
288        return 0;
289    return 1;
290}
291
292int main(int argc, char *argv[])
293{
294    int append=0;
295    int create=0;
296    int slim=0;
297    int sanitize=0;
298    int long_usage_bool=0;
299    int pass_prompt=0;
300    int projection = PROJ_SPHERE_MERC;
301    int expire_tiles_zoom = -1;
302    int expire_tiles_zoom_min = -1;
303    int enable_hstore = 0;
304    int enable_multi = 0;
305    const char *expire_tiles_filename = "dirty_tiles";
306    const char *db = "gis";
307    const char *username=NULL;
308    const char *host=NULL;
309    const char *password=NULL;
310    const char *port = "5432";
311    const char *tblsindex = "pg_default"; // default TABLESPACE for index
312    const char *conninfo = NULL;
313    const char *prefix = "planet_osm";
314    const char *style = OSM2PGSQL_DATADIR "/default.style";
315    const char *temparg;
316    const char *output_backend = "pgsql";
317    const char *input_reader = "auto";
318    const char **hstore_columns = NULL;
319    int n_hstore_columns = 0;
320    int keep_coastlines=0;
321    int cache = 800;
322    struct output_options options;
323    PGconn *sql_conn;
324   
325    int (*streamFile)(char *, int, struct osmdata_t *);
326
327    printf("osm2pgsql SVN version %s\n\n", VERSION);
328
329    while (1) {
330        int c, option_index = 0;
331        static struct option long_options[] = {
332            {"append",   0, 0, 'a'},
333            {"bbox",     1, 0, 'b'},
334            {"create",   0, 0, 'c'},
335            {"database", 1, 0, 'd'},
336            {"latlong",  0, 0, 'l'},
337            {"verbose",  0, 0, 'v'},
338            {"slim",     0, 0, 's'},
339            {"prefix",   1, 0, 'p'},
340            {"proj",     1, 0, 'E'},
341            {"merc",     0, 0, 'm'},
342            {"oldmerc",  0, 0, 'M'},
343            {"utf8-sanitize", 0, 0, 'u'},
344            {"cache",    1, 0, 'C'},
345            {"username", 1, 0, 'U'},
346            {"password", 0, 0, 'W'},
347            {"host",     1, 0, 'H'},
348            {"port",     1, 0, 'P'},
349            {"tablespace-index", 1, 0, 'i'},
350            {"help",     0, 0, 'h'},
351            {"style",    1, 0, 'S'},
352            {"expire-tiles", 1, 0, 'e'},
353            {"expire-output", 1, 0, 'o'},
354            {"output",   1, 0, 'O'},
355            {"extra-attributes", 0, 0, 'x'},
356            {"hstore", 0, 0, 'k'},
357            {"hstore-column", 1, 0, 'z'},
358            {"multi-geometry", 0, 0, 'G'},
359            {"keep-coastlines", 0, 0, 'K'},
360            {"input-reader", 1, 0, 'r'},
361            {"version", 0, 0, 'V'},
362            {0, 0, 0, 0}
363        };
364
365        c = getopt_long (argc, argv, "ab:cd:KhlmMp:suvU:WH:P:i:E:C:S:e:o:O:xkGz:r:V", long_options, &option_index);
366        if (c == -1)
367            break;
368
369        switch (c) {
370            case 'a': append=1;   break;
371            case 'b': osmdata.bbox=optarg; break;
372            case 'c': create=1;   break;
373            case 'v': verbose=1;  break;
374            case 's': slim=1;     break;
375            case 'K': keep_coastlines=1;     break;
376            case 'u': sanitize=1; break;
377            case 'l': projection=PROJ_LATLONG;  break;
378            case 'm': projection=PROJ_SPHERE_MERC; break;
379            case 'M': projection=PROJ_MERC; break;
380            case 'E': projection=-atoi(optarg); break;
381            case 'p': prefix=optarg; break;
382            case 'd': db=optarg;  break;
383            case 'C': cache = atoi(optarg); break;
384            case 'U': username=optarg; break;
385            case 'W': pass_prompt=1; break;
386            case 'H': host=optarg; break;
387            case 'P': port=optarg; break;
388            case 'S': style=optarg; break;
389            case 'i': tblsindex=optarg; break;
390            case 'e':
391                expire_tiles_zoom_min = atoi(optarg);
392                temparg = strchr(optarg, '-');
393                if (temparg) expire_tiles_zoom = atoi(temparg + 1);
394                if (expire_tiles_zoom < expire_tiles_zoom_min) expire_tiles_zoom = expire_tiles_zoom_min;
395                break;
396            case 'o': expire_tiles_filename=optarg; break;
397            case 'O': output_backend = optarg; break;
398            case 'x': osmdata.extra_attributes=1; break;
399            case 'k': enable_hstore=1; break;
400            case 'z':
401                n_hstore_columns++;
402                hstore_columns = (const char**)realloc(hstore_columns, sizeof(&n_hstore_columns) * n_hstore_columns);
403                hstore_columns[n_hstore_columns-1] = optarg;
404                break;
405            case 'G': enable_multi=1; break;
406            case 'r': input_reader = optarg; break;
407            case 'h': long_usage_bool=1; break;
408            case 'V': exit(EXIT_SUCCESS);
409            case '?':
410            default:
411                short_usage(argv[0]);
412                exit(EXIT_FAILURE);
413        }
414    }
415
416    if (long_usage_bool) {
417        long_usage(argv[0]);
418        exit(EXIT_SUCCESS);
419    }
420
421    if (argc == optind) {  // No non-switch arguments
422        short_usage(argv[0]);
423        exit(EXIT_FAILURE);
424    }
425
426    if (append && create) {
427        fprintf(stderr, "Error: --append and --create options can not be used at the same time!\n");
428        exit(EXIT_FAILURE);
429    }
430
431    if( cache < 0 ) cache = 0;
432
433    if (pass_prompt)
434        password = simple_prompt("Password:", 100, 0);
435    else {
436        password = getenv("PGPASS");
437    }   
438       
439
440    conninfo = build_conninfo(db, username, password, host, port);
441    sql_conn = PQconnectdb(conninfo);
442    if (PQstatus(sql_conn) != CONNECTION_OK) {
443        fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(sql_conn));
444        exit(EXIT_FAILURE);
445    }
446    PQfinish(sql_conn);
447
448    text_init();
449    initList(&osmdata.tags);
450
451    osmdata.count_node = osmdata.max_node = 0;
452    osmdata.count_way  = osmdata.max_way  = 0;
453    osmdata.count_rel  = osmdata.max_rel  = 0;
454
455    LIBXML_TEST_VERSION
456
457    project_init(projection);
458    fprintf(stderr, "Using projection SRS %d (%s)\n", 
459        project_getprojinfo()->srs, project_getprojinfo()->descr );
460
461    if (parse_bbox(&osmdata))
462        return 1;
463
464    options.conninfo = conninfo;
465    options.prefix = prefix;
466    options.append = append;
467    options.slim = slim;
468    options.projection = project_getprojinfo()->srs;
469    options.scale = (projection==PROJ_LATLONG)?10000000:100;
470    options.mid = slim ? &mid_pgsql : &mid_ram;
471    options.cache = cache;
472    options.style = style;
473    options.tblsindex = tblsindex;
474    options.expire_tiles_zoom = expire_tiles_zoom;
475    options.expire_tiles_zoom_min = expire_tiles_zoom_min;
476    options.expire_tiles_filename = expire_tiles_filename;
477    options.enable_multi = enable_multi;
478    options.enable_hstore = enable_hstore;
479    options.hstore_columns = hstore_columns;
480    options.n_hstore_columns = n_hstore_columns;
481    options.keep_coastlines = keep_coastlines;
482
483    if (strcmp("pgsql", output_backend) == 0) {
484      osmdata.out = &out_pgsql;
485    } else if (strcmp("gazetteer", output_backend) == 0) {
486      osmdata.out = &out_gazetteer;
487    } else if (strcmp("null", output_backend) == 0) {
488      osmdata.out = &out_null;
489    } else {
490      fprintf(stderr, "Output backend `%s' not recognised. Should be one of [pgsql, gazetteer, null].\n", output_backend);
491      exit(EXIT_FAILURE);
492    }
493
494    if (strcmp("auto", input_reader) != 0) {
495      if (strcmp("libxml2", input_reader) == 0) {
496        streamFile = &streamFileXML2;
497      } else if (strcmp("primitive", input_reader) == 0) {
498        streamFile = &streamFilePrimitive;
499#ifdef BUILD_READER_PBF
500      } else if (strcmp("pbf", input_reader) == 0) {
501        streamFile = &streamFilePbf;
502#endif
503      } else {
504        fprintf(stderr, "Input parser `%s' not recognised. Should be one of [libxml2, primitive"
505#ifdef BUILD_READER_PBF
506              ", pbf"
507#endif
508              "].\n", input_reader);
509      exit(EXIT_FAILURE);
510      }
511    }
512
513    osmdata.out->start(&options);
514
515    realloc_nodes(&osmdata);
516    realloc_members(&osmdata);
517
518    if (sizeof(int*) == 4 && options.slim != 1) {
519        fprintf(stderr, "\n!! You are running this on 32bit system, so at most\n");
520        fprintf(stderr, "!! 3GB of RAM can be used. If you encounter unexpected\n");
521        fprintf(stderr, "!! exceptions during import, you should try running in slim\n");
522        fprintf(stderr, "!! mode using parameter -s.\n");
523    }
524
525    while (optind < argc) {
526        /* if input_reader is not forced by -r switch try to auto-detect it
527           by file extension */
528        if (strcmp("auto", input_reader) == 0) {
529#ifdef BUILD_READER_PBF
530          if (strcasecmp(".pbf",argv[optind]+strlen(argv[optind])-4) == 0) {
531            streamFile = &streamFilePbf;
532          } else {
533            streamFile = &streamFileXML2;
534          }
535#else
536          streamFile = &streamFileXML2;
537#endif
538        }
539        time_t start, end;
540
541        fprintf(stderr, "\nReading in file: %s\n", argv[optind]);
542        time(&start);
543        if (streamFile(argv[optind], sanitize, &osmdata) != 0)
544            exit_nicely();
545        time(&end);
546        fprintf(stderr, "  parse time: %ds\n", (int)(end - start));
547        optind++;
548    }
549
550    xmlCleanupParser();
551    xmlMemoryDump();
552   
553    if (osmdata.count_node || osmdata.count_way || osmdata.count_rel) {
554        fprintf(stderr, "\n");
555        fprintf(stderr, "Node stats: total(%d), max(%d)\n", osmdata.count_node, osmdata.max_node);
556        fprintf(stderr, "Way stats: total(%d), max(%d)\n", osmdata.count_way, osmdata.max_way);
557        fprintf(stderr, "Relation stats: total(%d), max(%d)\n", osmdata.count_rel, osmdata.max_rel);
558    }
559    osmdata.out->stop();
560   
561    free(osmdata.nds);
562    free(osmdata.members);
563   
564    // free the column pointer buffer
565    free(hstore_columns);
566
567    project_exit();
568    text_exit();
569    fprintf(stderr, "\n");
570
571    return 0;
572}
Note: See TracBrowser for help on using the repository browser.