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

Last change on this file since 5715 was 5715, checked in by martinvoosterhout, 12 years ago

Add support for a --prefix option so that you can easily run multiple mapnik
instances out of the one DB. The default is ofcourse still "planet_osm" so
if you don't use it you won't see a difference. I did however need to change
the names of the indexes so they don't clash also, but this is unlikely to
break anything.

  • Property svn:keywords set to Rev
File size: 13.3 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 <libxml/xmlstring.h>
35#include <libxml/xmlreader.h>
36
37#include "osmtypes.h"
38#include "build_geometry.h"
39#include "keyvals.h"
40#include "middle-pgsql.h"
41#include "middle-ram.h"
42#include "output-pgsql.h"
43#include "sanitizer.h"
44#include "reprojection.h"
45#include "text-tree.h"
46#include "input.h"
47
48static int count_node,    max_node;
49static int count_way,     max_way;
50static int count_rel,     max_rel;
51
52struct middle_t *mid;
53struct output_t *out;
54
55/* Since {node,way} elements are not nested we can guarantee the
56   values in an end tag must match those of the corresponding
57   start tag and can therefore be cached.
58*/
59static double node_lon, node_lat;
60static struct keyval tags, nds, members;
61static int osm_id;
62
63int verbose;
64
65static void printStatus(void)
66{
67    fprintf(stderr, "\rProcessing: Node(%dk) Way(%dk) Relation(%dk)",
68            count_node/1000, count_way/1000, count_rel/1000);
69}
70
71
72void StartElement(xmlTextReaderPtr reader, const xmlChar *name)
73{
74    xmlChar *xid, *xlat, *xlon, *xk, *xv, *xrole, *xtype;
75    char *k;
76
77    if (xmlStrEqual(name, BAD_CAST "node")) {
78        xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
79        xlon = xmlTextReaderGetAttribute(reader, BAD_CAST "lon");
80        xlat = xmlTextReaderGetAttribute(reader, BAD_CAST "lat");
81        assert(xid); assert(xlon); assert(xlat);
82
83        osm_id  = strtol((char *)xid, NULL, 10);
84        node_lon = strtod((char *)xlon, NULL);
85        node_lat = strtod((char *)xlat, NULL);
86
87        if (osm_id > max_node)
88            max_node = osm_id;
89
90        count_node++;
91        if (count_node%10000 == 0)
92            printStatus();
93
94        xmlFree(xid);
95        xmlFree(xlon);
96        xmlFree(xlat);
97    } else if (xmlStrEqual(name, BAD_CAST "tag")) {
98        xk = xmlTextReaderGetAttribute(reader, BAD_CAST "k");
99        assert(xk);
100
101        /* 'created_by' and 'source' are common and not interesting to mapnik renderer */
102        if (strcmp((char *)xk, "created_by") && strcmp((char *)xk, "source")) {
103            char *p;
104            xv = xmlTextReaderGetAttribute(reader, BAD_CAST "v");
105            assert(xv);
106            k  = (char *)xmlStrdup(xk);
107            while ((p = strchr(k, ' ')))
108                *p = '_';
109
110            addItem(&tags, k, (char *)xv, 0);
111            xmlFree(k);
112            xmlFree(xv);
113        }
114        xmlFree(xk);
115    } else if (xmlStrEqual(name, BAD_CAST "way")) {
116        xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
117        assert(xid);
118        osm_id   = strtol((char *)xid, NULL, 10);
119
120        if (osm_id > max_way)
121            max_way = osm_id;
122
123        count_way++;
124        if (count_way%1000 == 0)
125            printStatus();
126
127        xmlFree(xid);
128    } else if (xmlStrEqual(name, BAD_CAST "nd")) {
129        xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "ref");
130        assert(xid);
131
132        addItem(&nds, "id", (char *)xid, 0);
133
134        xmlFree(xid);
135    } else if (xmlStrEqual(name, BAD_CAST "relation")) {
136        xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
137        assert(xid);
138        osm_id   = strtol((char *)xid, NULL, 10);
139
140        if (osm_id > max_rel)
141            max_rel = osm_id;
142
143        count_rel++;
144        if (count_rel%1000 == 0)
145            printStatus();
146
147        xmlFree(xid);
148    } else if (xmlStrEqual(name, BAD_CAST "member")) {
149        xrole = xmlTextReaderGetAttribute(reader, BAD_CAST "role");
150        assert(xrole);
151
152        xtype = xmlTextReaderGetAttribute(reader, BAD_CAST "type");
153        assert(xtype);
154
155        xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "ref");
156        assert(xid);
157
158        /* Currently we are only interested in 'way' members since these form polygons with holes */
159        if (xmlStrEqual(xtype, BAD_CAST "way"))
160            addItem(&members, (char *)xrole, (char *)xid, 0);
161
162        xmlFree(xid);
163        xmlFree(xrole);
164        xmlFree(xtype);
165    } else if (xmlStrEqual(name, BAD_CAST "osm")) {
166        /* ignore */
167    } else if (xmlStrEqual(name, BAD_CAST "bound")) {
168        /* ignore */
169    } else {
170        fprintf(stderr, "%s: Unknown element name: %s\n", __FUNCTION__, name);
171    }
172}
173
174void EndElement(const xmlChar *name)
175{
176    if (xmlStrEqual(name, BAD_CAST "node")) {
177        reproject(&node_lat, &node_lon);
178        mid->nodes_set(osm_id, node_lat, node_lon, &tags);
179        resetList(&tags);
180    } else if (xmlStrEqual(name, BAD_CAST "way")) {
181        mid->ways_set(osm_id, &nds, &tags);
182        resetList(&tags);
183        resetList(&nds);
184    } else if (xmlStrEqual(name, BAD_CAST "relation")) {
185        mid->relations_set(osm_id, &members, &tags);
186        resetList(&tags);
187        resetList(&members);
188    } else if (xmlStrEqual(name, BAD_CAST "tag")) {
189        /* ignore */
190    } else if (xmlStrEqual(name, BAD_CAST "nd")) {
191        /* ignore */
192    } else if (xmlStrEqual(name, BAD_CAST "member")) {
193        /* ignore */
194    } else if (xmlStrEqual(name, BAD_CAST "osm")) {
195        printStatus();
196    } else if (xmlStrEqual(name, BAD_CAST "bound")) {
197        /* ignore */
198    } else {
199        fprintf(stderr, "%s: Unknown element name: %s\n", __FUNCTION__, name);
200    }
201}
202
203static void processNode(xmlTextReaderPtr reader) {
204    xmlChar *name;
205    name = xmlTextReaderName(reader);
206    if (name == NULL)
207        name = xmlStrdup(BAD_CAST "--");
208       
209    switch(xmlTextReaderNodeType(reader)) {
210        case XML_READER_TYPE_ELEMENT:
211            StartElement(reader, name);
212            if (xmlTextReaderIsEmptyElement(reader))
213                EndElement(name); /* No end_element for self closing tags! */
214            break;
215        case XML_READER_TYPE_END_ELEMENT:
216            EndElement(name);
217            break;
218        case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
219            /* Ignore */
220            break;
221        default:
222            fprintf(stderr, "Unknown node type %d\n", xmlTextReaderNodeType(reader));
223            break;
224    }
225
226    xmlFree(name);
227}
228
229static int streamFile(char *filename, int sanitize) {
230    xmlTextReaderPtr reader;
231    int ret = 0;
232
233    if (sanitize)
234        reader = sanitizerOpen(filename);
235    else
236        reader = inputUTF8(filename);
237
238    if (reader != NULL) {
239        ret = xmlTextReaderRead(reader);
240        while (ret == 1) {
241            processNode(reader);
242            ret = xmlTextReaderRead(reader);
243        }
244
245        if (ret != 0) {
246            fprintf(stderr, "%s : failed to parse\n", filename);
247            return ret;
248        }
249
250        xmlFreeTextReader(reader);
251    } else {
252        fprintf(stderr, "Unable to open %s\n", filename);
253        return 1;
254    }
255    return 0;
256}
257
258void exit_nicely(void)
259{
260    fprintf(stderr, "Error occurred, cleaning up\n");
261    out->cleanup();
262    mid->cleanup();
263    exit(1);
264}
265 
266static void usage(const char *arg0)
267{
268    int i;
269    const char *name = basename(arg0);
270
271    fprintf(stderr, "Usage:\n");
272    fprintf(stderr, "\t%s [options] planet.osm\n", name);
273    fprintf(stderr, "\t%s [options[ planet.osm.{gz,bz2}\n", name);
274    fprintf(stderr, "\t%s [options] file1.osm file2.osm file3.osm\n", name);
275    fprintf(stderr, "\nThis will import the data from the OSM file(s) into a PostgreSQL database\n");
276    fprintf(stderr, "suitable for use by the Mapnik renderer\n");
277    fprintf(stderr, "\nOptions:\n");
278    fprintf(stderr, "   -a|--append\t\tAdd the OSM file into the database without removing\n");
279    fprintf(stderr, "              \t\texisting data.\n");
280    fprintf(stderr, "   -c|--create\t\tRemove existing data from the database. This is the \n");
281    fprintf(stderr, "              \t\tdefault if --append is not specified.\n");
282    fprintf(stderr, "   -d|--database\tThe name of the PostgreSQL database to connect\n");
283    fprintf(stderr, "                \tto (default: gis).\n");
284    fprintf(stderr, "   -l|--latlong\t\tStore data in degrees of latitude & longitude.\n");
285    fprintf(stderr, "   -m|--merc\t\tStore data in proper spherical mercator, not OSM merc\n");
286    fprintf(stderr, "   -u|--utf8-sanitize\tRepair bad UTF8 input data (present in planet\n");
287    fprintf(stderr, "                \tdumps prior to August 2007). Adds about 10%% overhead.\n");
288    fprintf(stderr, "   -p|--prefix\t\tPrefix for table names (default planet_osm)\n");
289    fprintf(stderr, "   -s|--slim\t\tStore temporary data in the database. This greatly\n");
290    fprintf(stderr, "            \t\treduces the RAM usage but is much slower.\n");
291    fprintf(stderr, "   -h|--help\t\tHelp information.\n");
292    fprintf(stderr, "   -v|--verbose\t\tVerbose output.\n");
293    fprintf(stderr, "\n");
294    if(!verbose)
295        fprintf(stderr, "Add -v to display supported projections.\n" );
296    else
297    {
298        fprintf(stderr, "Supported projections:\n" );
299        for(i=0; i<PROJ_COUNT; i++ )
300        {
301            fprintf( stderr, "%-20s(%2s) SRS:%6d %s\n", 
302                    Projection_Info[i].descr, Projection_Info[i].option, Projection_Info[i].srs, Projection_Info[i].proj4text);
303        }
304    }
305}
306
307int main(int argc, char *argv[])
308{
309    int append=0;
310    int create=0;
311    int slim=0;
312    int sanitize=0;
313    int latlong = 0, sphere_merc = 0;
314    const char *db = "gis";
315    const char *prefix = "planet_osm";
316
317    fprintf(stderr, "osm2pgsql SVN version %s $Rev: 5715 $ \n\n", VERSION);
318
319    while (1) {
320        int c, option_index = 0;
321        static struct option long_options[] = {
322            {"append",   0, 0, 'a'},
323            {"create",   0, 0, 'c'},
324            {"database", 1, 0, 'd'},
325            {"latlong",  0, 0, 'l'},
326            {"verbose",  0, 0, 'v'},
327            {"slim",     0, 0, 's'},
328            {"prefix",   1, 0, 'p'},
329            {"merc",     0, 0, 'm'},
330            {"utf8-sanitize", 0, 0, 'u'},
331            {"help",     0, 0, 'h'},
332            {0, 0, 0, 0}
333        };
334
335        c = getopt_long (argc, argv, "acd:hlmp:suv", long_options, &option_index);
336        if (c == -1)
337            break;
338
339        switch (c) {
340            case 'a': append=1;   break;
341            case 'c': create=1;   break;
342            case 'v': verbose=1;  break;
343            case 's': slim=1;     break;
344            case 'u': sanitize=1; break;
345            case 'l': latlong=1;  break;
346            case 'm': sphere_merc=1; break;
347            case 'p': prefix=optarg; break;
348            case 'd': db=optarg;  break;
349
350            case 'h':
351            case '?':
352            default:
353                usage(argv[0]);
354                exit(EXIT_FAILURE);
355        }
356    }
357
358    if (argc == optind) {  // No non-switch arguments
359        usage(argv[0]);
360        exit(EXIT_FAILURE);
361    }
362
363    if (append && create) {
364        fprintf(stderr, "Error: --append and --create options can not be used at the same time!\n");
365        exit(EXIT_FAILURE);
366    }
367
368    text_init();
369    initList(&tags);
370    initList(&nds);
371    initList(&members);
372
373    count_node = max_node = 0;
374    count_way = max_way = 0;
375    count_rel = max_rel = 0;
376
377    LIBXML_TEST_VERSION
378
379    if( latlong && sphere_merc )
380    {
381        fprintf(stderr, "Error: --latlong and --merc are mutually exclusive\n" );
382        exit(EXIT_FAILURE);
383    }
384    project_init(latlong ? PROJ_LATLONG : sphere_merc ? PROJ_SPHERE_MERC : PROJ_MERC );
385    fprintf(stderr, "Using projection SRS %d (%s)\n", 
386        project_getprojinfo()->srs, project_getprojinfo()->descr );
387
388    mid = slim ? &mid_pgsql : &mid_ram;
389    out = &out_pgsql;
390
391    out->start(db, prefix, append);
392
393    while (optind < argc) {
394        fprintf(stderr, "\nReading in file: %s\n", argv[optind]);
395        mid->start(db, latlong);
396        if (streamFile(argv[optind], sanitize) != 0)
397            exit_nicely();
398        mid->end();
399        mid->analyze();
400
401        //mid->iterate_nodes(out->node);
402        mid->iterate_relations(out->relation);
403        mid->iterate_ways(out->way);
404        mid->stop();
405        optind++;
406    }
407
408    xmlCleanupParser();
409    xmlMemoryDump();
410
411    if (count_node || count_way || count_rel) {
412        fprintf(stderr, "\n");
413        fprintf(stderr, "Node stats: total(%d), max(%d)\n", count_node, max_node);
414        fprintf(stderr, "Way stats: total(%d), max(%d)\n", count_way, max_way);
415        fprintf(stderr, "Relation stats: total(%d), max(%d)\n", count_rel, max_rel);
416    }
417    //fprintf(stderr, "\n\nEnding data import\n");
418    //out->process(mid);
419    out->stop(append);
420
421    project_exit();
422    text_exit();
423    fprintf(stderr, "\n");
424
425    return 0;
426}
Note: See TracBrowser for help on using the repository browser.