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

Last change on this file since 25786 was 25786, checked in by giggls, 9 years ago

Fix bug in hstore rework.

Bad idea to remove the k/v pairs from the linked list after
writing their colum, as write_wkts is called more than once.

So flag them to have their own column.

Afterwords check this flag to see if a given
k/v pair should be written to the hstore or not.

  • Property svn:keywords set to Rev
File size: 21.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#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("              \t\tto (default: gis).\n");
137    printf("   -i|--tablespace-index\tThe name of the PostgreSQL tablespace where\n");
138    printf("              \t\tall indexes will be created.\n");
139    printf("              \t\tThe following options allow more fine-grained control:\n");
140    printf("      --tablespace-main-data \ttablespace for main tables\n");
141    printf("      --tablespace-main-index\ttablespace for main table indexes\n");
142    printf("      --tablespace-slim-data \ttablespace for slim mode tables\n");
143    printf("      --tablespace-slim-index\ttablespace for slim mode indexes\n");
144    printf("              \t\t(if unset, use db's default; -i is equivalent to setting\n");
145    printf("              \t\t--tablespace-main-index and --tablespace-slim-index)\n");
146    printf("   -l|--latlong\t\tStore data in degrees of latitude & longitude.\n");
147    printf("   -m|--merc\t\tStore data in proper spherical mercator (default)\n");
148    printf("   -M|--oldmerc\t\tStore data in the legacy OSM mercator format\n");
149    printf("   -E|--proj num\tUse projection EPSG:num\n");
150    printf("   -u|--utf8-sanitize\tRepair bad UTF8 input data (present in planet\n");
151    printf("                \tdumps prior to August 2007). Adds about 10%% overhead.\n");
152    printf("   -p|--prefix\t\tPrefix for table names (default planet_osm)\n");
153    printf("   -s|--slim\t\tStore temporary data in the database. This greatly\n");
154    printf("            \t\treduces the RAM usage but is much slower.\n");
155
156    if (sizeof(int*) == 4) {
157        printf("            \t\tYou are running this on 32bit system, so at most\n");
158        printf("            \t\t3GB of RAM will be used. If you encounter unexpected\n");
159        printf("            \t\texceptions during import, you should try this switch.\n");
160    }
161   
162    printf("   -S|--style\t\tLocation of the style file. Defaults to " OSM2PGSQL_DATADIR "/default.style\n");
163    printf("   -C|--cache\t\tOnly for slim mode: Use upto this many MB for caching nodes\n");
164    printf("             \t\tDefault is 800\n");
165    printf("   -U|--username\tPostgresql user name.\n");
166    printf("   -W|--password\tForce password prompt.\n");
167    printf("   -H|--host\t\tDatabase server hostname or socket location.\n");
168    printf("   -P|--port\t\tDatabase server port.\n");
169    printf("   -e|--expire-tiles [min_zoom-]max_zoom\tCreate a tile expiry list.\n");
170    printf("   -o|--expire-output filename\tOutput filename for expired tiles list.\n");
171    printf("   -r|--input-reader\tInput frontend.\n");
172    printf("              \t\tlibxml2   - Parse XML using libxml2. (default)\n");
173    printf("              \t\tprimitive - Primitive XML parsing.\n");
174#ifdef BUILD_READER_PBF
175    printf("              \t\tpbf       - OSM binary format.\n");
176#endif
177    printf("   -O|--output\t\tOutput backend.\n");
178    printf("              \t\tpgsql - Output to a PostGIS database. (default)\n");
179    printf("              \t\tgazetteer - Output to a PostGIS database suitable for gazetteer\n");
180    printf("              \t\tnull  - No output. Useful for testing.\n");
181    printf("   -x|--extra-attributes\n");
182    printf("              \t\tInclude attributes for each object in the database.\n");
183    printf("              \t\tThis includes the username, userid, timestamp and version.\n"); 
184    printf("              \t\tNote: this option also requires additional entries in your style file.\n"); 
185    printf("   -k|--hstore\t\tAdd tags without column to an additional hstore (key/value) column to postgresql tables\n");
186    printf("   -j|--hstore-all\tAdd all tags to an additional hstore (key/value) column in postgresql tables\n");
187    printf("   -z|--hstore-column\tAdd an additional hstore (key/value) column containing all tags\n");
188    printf("                     \tthat start with the specified string, eg --hstore-column \"name:\" will\n");
189    printf("                     \tproduce an extra hstore column that contains all name:xx tags\n");
190    printf("   -G|--multi-geometry\tGenerate multi-geometry features in postgresql tables.\n");
191    printf("   -K|--keep-coastlines\tKeep coastline data rather than filtering it out.\n");
192    printf("              \t\tBy default natural=coastline tagged data will be discarded based on the\n");
193    printf("              \t\tassumption that post-processed Coastline Checker shapefiles will be used.\n");
194    printf("   -h|--help\t\tHelp information.\n");
195    printf("   -v|--verbose\t\tVerbose output.\n");
196    printf("\n");
197    if(!verbose)
198    {
199        printf("Add -v to display supported projections.\n");
200        printf("Use -E to access any espg projections (usually in /usr/share/proj/epsg)\n" );
201    }
202    else
203    {
204        printf("Supported projections:\n" );
205        for(i=0; i<PROJ_COUNT; i++ )
206        {
207            printf( "%-20s(%2s) SRS:%6d %s\n", 
208                    Projection_Info[i].descr, Projection_Info[i].option, Projection_Info[i].srs, Projection_Info[i].proj4text);
209        }
210    }
211}
212
213const char *build_conninfo(const char *db, const char *username, const char *password, const char *host, const char *port)
214{
215    static char conninfo[1024];
216
217    conninfo[0]='\0';
218    strcat(conninfo, "dbname='");
219    strcat(conninfo, db);
220    strcat(conninfo, "'");
221
222    if (username) {
223        strcat(conninfo, " user='");
224        strcat(conninfo, username);
225        strcat(conninfo, "'");
226    }
227    if (password) {
228        strcat(conninfo, " password='");
229        strcat(conninfo, password);
230        strcat(conninfo, "'");
231    }
232    if (host) {
233        strcat(conninfo, " host='");
234        strcat(conninfo, host);
235        strcat(conninfo, "'");
236    }
237    if (port) {
238        strcat(conninfo, " port='");
239        strcat(conninfo, port);
240        strcat(conninfo, "'");
241    }
242
243    return conninfo;
244}
245
246void realloc_nodes(struct osmdata_t *osmdata)
247{
248  if( osmdata->nd_max == 0 )
249    osmdata->nd_max = INIT_MAX_NODES;
250  else
251    osmdata->nd_max <<= 1;
252   
253  osmdata->nds = realloc( osmdata->nds, osmdata->nd_max * sizeof( osmdata->nds[0] ) );
254  if( !osmdata->nds )
255  {
256    fprintf( stderr, "Failed to expand node list to %d\n", osmdata->nd_max );
257    exit_nicely();
258  }
259}
260
261void realloc_members(struct osmdata_t *osmdata)
262{
263  if( osmdata->member_max == 0 )
264    osmdata->member_max = INIT_MAX_NODES;
265  else
266    osmdata->member_max <<= 1;
267   
268  osmdata->members = realloc( osmdata->members, osmdata->member_max * sizeof( osmdata->members[0] ) );
269  if( !osmdata->members )
270  {
271    fprintf( stderr, "Failed to expand member list to %d\n", osmdata->member_max );
272    exit_nicely();
273  }
274}
275
276void resetMembers(struct osmdata_t *osmdata)
277{
278  for(unsigned i = 0; i < osmdata->member_count; i++ )
279    free( osmdata->members[i].role );
280}
281
282void printStatus(struct osmdata_t *osmdata)
283{
284    fprintf(stderr, "\rProcessing: Node(%dk) Way(%dk) Relation(%d)",
285            osmdata->count_node/1000, osmdata->count_way/1000, osmdata->count_rel);
286}
287
288int node_wanted(struct osmdata_t *osmdata, double lat, double lon)
289{
290    if (!osmdata->bbox)
291        return 1;
292
293    if (lat < osmdata->minlat || lat > osmdata->maxlat)
294        return 0;
295    if (lon < osmdata->minlon || lon > osmdata->maxlon)
296        return 0;
297    return 1;
298}
299
300int main(int argc, char *argv[])
301{
302    int append=0;
303    int create=0;
304    int slim=0;
305    int sanitize=0;
306    int long_usage_bool=0;
307    int pass_prompt=0;
308    int projection = PROJ_SPHERE_MERC;
309    int expire_tiles_zoom = -1;
310    int expire_tiles_zoom_min = -1;
311    int enable_hstore = HSTORE_NONE;
312    int enable_multi = 0;
313    const char *expire_tiles_filename = "dirty_tiles";
314    const char *db = "gis";
315    const char *username=NULL;
316    const char *host=NULL;
317    const char *password=NULL;
318    const char *port = "5432";
319    const char *tblsmain_index = NULL; // no default TABLESPACE for index on main tables
320    const char *tblsmain_data = NULL;  // no default TABLESPACE for main tables
321    const char *tblsslim_index = NULL; // no default TABLESPACE for index on slim mode tables
322    const char *tblsslim_data = NULL;  // no default TABLESPACE for slim mode tables
323    const char *conninfo = NULL;
324    const char *prefix = "planet_osm";
325    const char *style = OSM2PGSQL_DATADIR "/default.style";
326    const char *temparg;
327    const char *output_backend = "pgsql";
328    const char *input_reader = "auto";
329    const char **hstore_columns = NULL;
330    int n_hstore_columns = 0;
331    int keep_coastlines=0;
332    int cache = 800;
333    struct output_options options;
334    PGconn *sql_conn;
335   
336    int (*streamFile)(char *, int, struct osmdata_t *);
337
338    printf("osm2pgsql SVN version %s\n\n", VERSION);
339
340    while (1) {
341        int c, option_index = 0;
342        static struct option long_options[] = {
343            {"append",   0, 0, 'a'},
344            {"bbox",     1, 0, 'b'},
345            {"create",   0, 0, 'c'},
346            {"database", 1, 0, 'd'},
347            {"latlong",  0, 0, 'l'},
348            {"verbose",  0, 0, 'v'},
349            {"slim",     0, 0, 's'},
350            {"prefix",   1, 0, 'p'},
351            {"proj",     1, 0, 'E'},
352            {"merc",     0, 0, 'm'},
353            {"oldmerc",  0, 0, 'M'},
354            {"utf8-sanitize", 0, 0, 'u'},
355            {"cache",    1, 0, 'C'},
356            {"username", 1, 0, 'U'},
357            {"password", 0, 0, 'W'},
358            {"host",     1, 0, 'H'},
359            {"port",     1, 0, 'P'},
360            {"tablespace-index", 1, 0, 'i'},
361            {"tablespace-slim-data", 1, 0, 200},
362            {"tablespace-slim-index", 1, 0, 201},
363            {"tablespace-main-data", 1, 0, 202},
364            {"tablespace-main-index", 1, 0, 203},
365            {"help",     0, 0, 'h'},
366            {"style",    1, 0, 'S'},
367            {"expire-tiles", 1, 0, 'e'},
368            {"expire-output", 1, 0, 'o'},
369            {"output",   1, 0, 'O'},
370            {"extra-attributes", 0, 0, 'x'},
371            {"hstore", 0, 0, 'k'},
372            {"hstore-all", 0, 0, 'j'},
373            {"hstore-column", 1, 0, 'z'},
374            {"multi-geometry", 0, 0, 'G'},
375            {"keep-coastlines", 0, 0, 'K'},
376            {"input-reader", 1, 0, 'r'},
377            {"version", 0, 0, 'V'},
378            {0, 0, 0, 0}
379        };
380
381        c = getopt_long (argc, argv, "ab:cd:KhlmMp:suvU:WH:P:i:E:C:S:e:o:O:xkjGz:r:V", long_options, &option_index);
382        if (c == -1)
383            break;
384
385        switch (c) {
386            case 'a': append=1;   break;
387            case 'b': osmdata.bbox=optarg; break;
388            case 'c': create=1;   break;
389            case 'v': verbose=1;  break;
390            case 's': slim=1;     break;
391            case 'K': keep_coastlines=1;     break;
392            case 'u': sanitize=1; break;
393            case 'l': projection=PROJ_LATLONG;  break;
394            case 'm': projection=PROJ_SPHERE_MERC; break;
395            case 'M': projection=PROJ_MERC; break;
396            case 'E': projection=-atoi(optarg); break;
397            case 'p': prefix=optarg; break;
398            case 'd': db=optarg;  break;
399            case 'C': cache = atoi(optarg); break;
400            case 'U': username=optarg; break;
401            case 'W': pass_prompt=1; break;
402            case 'H': host=optarg; break;
403            case 'P': port=optarg; break;
404            case 'S': style=optarg; break;
405            case 'i': tblsmain_index=tblsslim_index=optarg; break;
406            case 200: tblsslim_data=optarg; break;   
407            case 201: tblsslim_index=optarg; break;   
408            case 202: tblsmain_data=optarg; break;   
409            case 203: tblsmain_index=optarg; break;   
410            case 'e':
411                expire_tiles_zoom_min = atoi(optarg);
412                temparg = strchr(optarg, '-');
413                if (temparg) expire_tiles_zoom = atoi(temparg + 1);
414                if (expire_tiles_zoom < expire_tiles_zoom_min) expire_tiles_zoom = expire_tiles_zoom_min;
415                break;
416            case 'o': expire_tiles_filename=optarg; break;
417            case 'O': output_backend = optarg; break;
418            case 'x': osmdata.extra_attributes=1; break;
419            case 'k': enable_hstore=HSTORE_NORM; break;
420            case 'j': enable_hstore=HSTORE_ALL; break;
421            case 'z': 
422                n_hstore_columns++;
423                hstore_columns = (const char**)realloc(hstore_columns, sizeof(&n_hstore_columns) * n_hstore_columns);
424                hstore_columns[n_hstore_columns-1] = optarg;
425                break;
426            case 'G': enable_multi=1; break;
427            case 'r': input_reader = optarg; break;
428            case 'h': long_usage_bool=1; break;
429            case 'V': exit(EXIT_SUCCESS);
430            case '?':
431            default:
432                short_usage(argv[0]);
433                exit(EXIT_FAILURE);
434        }
435    }
436
437    if (long_usage_bool) {
438        long_usage(argv[0]);
439        exit(EXIT_SUCCESS);
440    }
441
442    if (argc == optind) {  // No non-switch arguments
443        short_usage(argv[0]);
444        exit(EXIT_FAILURE);
445    }
446
447    if (append && create) {
448        fprintf(stderr, "Error: --append and --create options can not be used at the same time!\n");
449        exit(EXIT_FAILURE);
450    }
451
452    if (cache < 0) cache = 0;
453
454    if (pass_prompt)
455        password = simple_prompt("Password:", 100, 0);
456    else {
457        password = getenv("PGPASS");
458    }   
459       
460
461    conninfo = build_conninfo(db, username, password, host, port);
462    sql_conn = PQconnectdb(conninfo);
463    if (PQstatus(sql_conn) != CONNECTION_OK) {
464        fprintf(stderr, "Connection to database failed: %s\n", PQerrorMessage(sql_conn));
465        exit(EXIT_FAILURE);
466    }
467    PQfinish(sql_conn);
468
469    text_init();
470    initList(&osmdata.tags);
471
472    osmdata.count_node = osmdata.max_node = 0;
473    osmdata.count_way  = osmdata.max_way  = 0;
474    osmdata.count_rel  = osmdata.max_rel  = 0;
475
476    LIBXML_TEST_VERSION
477
478    project_init(projection);
479    fprintf(stderr, "Using projection SRS %d (%s)\n", 
480        project_getprojinfo()->srs, project_getprojinfo()->descr );
481
482    if (parse_bbox(&osmdata))
483        return 1;
484
485    options.conninfo = conninfo;
486    options.prefix = prefix;
487    options.append = append;
488    options.slim = slim;
489    options.projection = project_getprojinfo()->srs;
490    options.scale = (projection==PROJ_LATLONG)?10000000:100;
491    options.mid = slim ? &mid_pgsql : &mid_ram;
492    options.cache = cache;
493    options.style = style;
494    options.tblsmain_index = tblsmain_index;
495    options.tblsmain_data = tblsmain_data;
496    options.tblsslim_index = tblsslim_index;
497    options.tblsslim_data = tblsslim_data;
498    options.expire_tiles_zoom = expire_tiles_zoom;
499    options.expire_tiles_zoom_min = expire_tiles_zoom_min;
500    options.expire_tiles_filename = expire_tiles_filename;
501    options.enable_multi = enable_multi;
502    options.enable_hstore = enable_hstore;
503    options.hstore_columns = hstore_columns;
504    options.n_hstore_columns = n_hstore_columns;
505    options.keep_coastlines = keep_coastlines;
506
507    if (strcmp("pgsql", output_backend) == 0) {
508      osmdata.out = &out_pgsql;
509    } else if (strcmp("gazetteer", output_backend) == 0) {
510      osmdata.out = &out_gazetteer;
511    } else if (strcmp("null", output_backend) == 0) {
512      osmdata.out = &out_null;
513    } else {
514      fprintf(stderr, "Output backend `%s' not recognised. Should be one of [pgsql, gazetteer, null].\n", output_backend);
515      exit(EXIT_FAILURE);
516    }
517
518    if (strcmp("auto", input_reader) != 0) {
519      if (strcmp("libxml2", input_reader) == 0) {
520        streamFile = &streamFileXML2;
521      } else if (strcmp("primitive", input_reader) == 0) {
522        streamFile = &streamFilePrimitive;
523#ifdef BUILD_READER_PBF
524      } else if (strcmp("pbf", input_reader) == 0) {
525        streamFile = &streamFilePbf;
526#endif
527      } else {
528        fprintf(stderr, "Input parser `%s' not recognised. Should be one of [libxml2, primitive"
529#ifdef BUILD_READER_PBF
530              ", pbf"
531#endif
532              "].\n", input_reader);
533      exit(EXIT_FAILURE);
534      }
535    }
536
537    osmdata.out->start(&options);
538
539    realloc_nodes(&osmdata);
540    realloc_members(&osmdata);
541
542    if (sizeof(int*) == 4 && options.slim != 1) {
543        fprintf(stderr, "\n!! You are running this on 32bit system, so at most\n");
544        fprintf(stderr, "!! 3GB of RAM can be used. If you encounter unexpected\n");
545        fprintf(stderr, "!! exceptions during import, you should try running in slim\n");
546        fprintf(stderr, "!! mode using parameter -s.\n");
547    }
548
549    while (optind < argc) {
550        /* if input_reader is not forced by -r switch try to auto-detect it
551           by file extension */
552        if (strcmp("auto", input_reader) == 0) {
553#ifdef BUILD_READER_PBF
554          if (strcasecmp(".pbf",argv[optind]+strlen(argv[optind])-4) == 0) {
555            streamFile = &streamFilePbf;
556          } else {
557            streamFile = &streamFileXML2;
558          }
559#else
560          streamFile = &streamFileXML2;
561#endif
562        }
563        time_t start, end;
564
565        fprintf(stderr, "\nReading in file: %s\n", argv[optind]);
566        time(&start);
567        if (streamFile(argv[optind], sanitize, &osmdata) != 0)
568            exit_nicely();
569        time(&end);
570        fprintf(stderr, "  parse time: %ds\n", (int)(end - start));
571        optind++;
572    }
573
574    xmlCleanupParser();
575    xmlMemoryDump();
576   
577    if (osmdata.count_node || osmdata.count_way || osmdata.count_rel) {
578        fprintf(stderr, "\n");
579        fprintf(stderr, "Node stats: total(%d), max(%d)\n", osmdata.count_node, osmdata.max_node);
580        fprintf(stderr, "Way stats: total(%d), max(%d)\n", osmdata.count_way, osmdata.max_way);
581        fprintf(stderr, "Relation stats: total(%d), max(%d)\n", osmdata.count_rel, osmdata.max_rel);
582    }
583    osmdata.out->stop();
584   
585    free(osmdata.nds);
586    free(osmdata.members);
587   
588    // free the column pointer buffer
589    free(hstore_columns);
590
591    project_exit();
592    text_exit();
593    fprintf(stderr, "\n");
594
595    return 0;
596}
Note: See TracBrowser for help on using the repository browser.