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

Last change on this file since 4895 was 4895, checked in by jonb, 12 years ago

osm2pgsql: update to handle polygons with holes in 0.5 API (described using relations). The code is nasty but appears to work on small datasets.

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