source: subversion/utils/osm2pgsql/osm2pgsql.c @ 1604

Last change on this file since 1604 was 1604, checked in by jonb, 14 years ago

Handle missing nodes and segments instead of putting 0,0 into linestrings which was causing lots of rogue lines to appear on the map.
Improved linestring generation for non-contiguous ways.
Added a GIST index into the SQL output.
Corrected usage info and added a gzip example.
Removed some redundant lines.

File size: 12.1 KB
Line 
1#include <stdio.h>
2#include <unistd.h>
3#include <stdlib.h>
4#include <string.h>
5
6#include <libxml/xmlreader.h>
7
8#define WKT_MAX 128000
9#define SQL_MAX 140000
10
11#if 0
12#define DEBUG printf
13#else
14#define DEBUG(x, ...)
15#endif
16
17struct tagDesc {
18        const char *name;
19        const char *type;
20}; 
21
22static struct tagDesc exportTags[] = {
23                {"name","varchar(64)"},
24               {"place","varchar(32)"},
25               {"landuse","varchar(32)"},
26               {"leisure","varchar(32)"},
27               {"waterway","varchar(32)"},
28               {"highway","varchar(32)"},
29               {"amenity","varchar(32)"},
30               {"tourism","varchar(32)"},
31               {"learning","varchar(32)"}
32};
33
34//segments = {}
35static const char *table_name = "planet_osm";
36char fieldNames[128];
37
38#define MAX_ID_NODE (25000000)
39#define MAX_ID_SEGMENT (25000000)
40
41struct osmNode {
42        double lon;
43        double lat;
44};
45
46struct osmSegment {
47        unsigned long from;
48        unsigned long to;
49};
50
51static struct osmNode    nodes[MAX_ID_NODE+1];
52static struct osmSegment segments[MAX_ID_SEGMENT+1];
53
54struct keyval;
55
56struct keyval {
57        char *key;
58        char *value;
59        struct keyval *next;
60};
61
62static struct keyval kvTail = { NULL, NULL, NULL };
63static struct keyval *keys = &kvTail;
64static struct keyval *tags = &kvTail;
65static struct keyval *segs = &kvTail;
66
67void usage(const char *arg0)
68{
69        fprintf(stderr, "Usage error:\n\t%s planet.osm  > planet.sql\n", arg0);
70        fprintf(stderr, "or\n\tgzip -dc planet.osm.gz | %s - | gzip -c > planet.sql.gz\n", arg0);
71}
72
73
74void freeItem(struct keyval *p)
75{
76        free(p->key);
77        free(p->value);
78        free(p);
79}
80
81
82void resetList(struct keyval **list) 
83{
84        struct keyval *p = *list;
85        struct keyval *next;
86       
87        while((next = p->next)) {               
88                freeItem(p);
89                p = next;
90        }
91        *list = p;     
92}
93
94char *getItem(struct keyval **list, const char *name)
95{
96        struct keyval *p = *list;
97        while(p->next) {
98                if (!strcmp(p->key, name))
99                        return p->value;
100                p = p->next;
101        }
102        return NULL;
103}       
104
105
106struct keyval *popItem(struct keyval **list)
107{
108        struct keyval *p = *list;
109        if (!p->next)
110                return NULL;
111
112        *list = p->next;
113        return p;
114}       
115
116
117void pushItem(struct keyval **list, struct keyval *item)
118{
119        struct keyval *p = *list;
120        struct keyval *q = NULL;
121       
122        /* TODO: Improve this inefficient algorithm to locate end of list
123         * e.g. cache tail or use double link list */
124        while(p->next) {
125                q = p;
126                p = p->next;
127        }
128
129        item->next = p;
130        if (q) 
131                q->next = item;
132        else 
133                *list = item;
134}       
135
136void addItem(struct keyval **list, const char *name, const char *value)
137{
138        struct keyval *p = malloc(sizeof(struct keyval));
139       
140        if (!p) {
141                fprintf(stderr, "Error allocating keyval\n");
142                return;
143        }
144        p->key   = strdup(name);
145        p->value = strdup(value);
146        p->next = *list;
147        *list = p;
148}
149
150
151void WKT(char *wkt, int polygon)
152{
153        struct keyval *p = segs;
154        double start_x, start_y, end_x, end_y;
155        int first = 1;
156        char tmpwkt[WKT_MAX];
157        int i = 0; 
158        int max;
159        wkt[0] = '\0';
160       
161        while(p->next) {
162                i++;
163                p = p->next;
164        }
165        max = i * i;
166
167        i = 0;
168        while (segs->next && i < max) {
169                unsigned long id;
170                int from, to;
171                double x0, y0, x1, y1;
172
173                p = popItem(&segs);
174                id = strtoul(p->value, NULL, 10);
175                from = segments[id].from;
176                to   = segments[id].to; 
177                i++;
178                if (!from || !to) {
179                        freeItem(p);
180                        continue;
181                }
182                x0 = nodes[from].lon;
183                y0 = nodes[from].lat;
184                x1 = nodes[to].lon;
185                y1 = nodes[to].lat;
186
187                if (first) {
188                        first = 0;
189                        start_x = x0;
190                        start_y = y0;
191                        end_x = x1;
192                        end_y = y1;
193                        snprintf(wkt, WKT_MAX-1, "%.15g %.15g,%.15g %.15g", x0, y0, x1, y1);
194                } else {
195                        strcpy(tmpwkt, wkt);
196                        if (start_x == x0 && start_y == y0)  {
197                                start_x = x1;
198                                start_y = y1;
199                                snprintf(wkt, WKT_MAX-1, "%.15g %.15g,%s", x1, y1, tmpwkt);
200                        } else if (start_x == x1 && start_y == y1)  {
201                                start_x = x0;
202                                start_y = y0;
203                                snprintf(wkt, WKT_MAX-1, "%.15g %.15g,%s", x0, y0, tmpwkt);
204                        } else if (end_x == x0 && end_y == y0)  {
205                                end_x = x1;
206                                end_y = y1;
207                                snprintf(wkt, WKT_MAX-1, "%s,%.15g %.15g", tmpwkt, x1, y1);
208                        } else if (end_x == x1 && end_y == y1)  {
209                                end_x = x0;
210                                end_y = y0;
211                                snprintf(wkt, WKT_MAX-1, "%s,%.15g %.15g", tmpwkt, x0, y0);
212                        } else {
213                                pushItem(&segs, p);
214                                continue;
215                        }
216                }
217                freeItem(p);
218        }
219
220        if (strlen(wkt)) {
221                strcpy(tmpwkt, wkt);
222                if (polygon) 
223                        snprintf(wkt, WKT_MAX-1, "POLYGON((%s,%.15g %.15g))", tmpwkt, start_x, start_y);
224                else 
225                        snprintf(wkt, WKT_MAX-1, "LINESTRING(%s)", tmpwkt);
226        }
227}
228
229void StartElement(xmlTextReaderPtr reader, const xmlChar *name)
230{
231        xmlChar *xid, *xlat, *xlon, *xfrom, *xto, *xk, *xv;
232        unsigned long id, to, from;
233        double lon, lat;
234        char *k, *v;
235
236        if (!strcmp(name, "node")) {
237                xid  = xmlTextReaderGetAttribute(reader, "id");
238                xlon = xmlTextReaderGetAttribute(reader, "lon");
239                xlat = xmlTextReaderGetAttribute(reader, "lat");
240                id  = strtoul(xid, NULL, 10);
241                lon = strtod(xlon, NULL);
242                lat = strtod(xlat, NULL);
243                if (id > 0 && id < MAX_ID_NODE) {
244                        nodes[id].lon = lon;
245                        nodes[id].lat = lat;
246                        DEBUG("NODE(%d) %f %f\n", id, lon, lat);
247                        addItem(&keys, "id", xid);
248                } else {
249                        fprintf(stderr, "%s: Invalid node ID %d (max %d)\n", __FUNCTION__, id, MAX_ID_NODE);
250                        exit(1);
251                }
252                xmlFree(xid);
253                xmlFree(xlon);
254                xmlFree(xlat);
255        } else if (!strcmp(name, "segment")) {
256                xid   = xmlTextReaderGetAttribute(reader, "id");
257                xfrom = xmlTextReaderGetAttribute(reader, "from");
258                xto   = xmlTextReaderGetAttribute(reader, "to");
259                id   = strtoul(xid, NULL, 10);
260                from = strtoul(xfrom, NULL, 10);
261                to   = strtoul(xto, NULL, 10);
262                if (id > 0 && id < MAX_ID_SEGMENT) {
263                        if (!nodes[to].lat && !nodes[to].lon) 
264                                DEBUG("SEGMENT(%d), NODE(%d) is missing\n", id, to);
265                        else if (!nodes[from].lat && !nodes[from].lon)
266                                DEBUG("SEGMENT(%d), NODE(%d) is missing\n", id, from);
267                        else {
268                                segments[id].to   = to;
269                                segments[id].from = from;
270                                DEBUG("SEGMENT(%d) %d, %d\n", id, from, to);
271                        }
272                } else {
273                        fprintf(stderr, "%s: Invalid segment ID %d (max %d)\n", __FUNCTION__, id, MAX_ID_SEGMENT);
274                        exit(1);
275                }
276                xmlFree(xid);
277                xmlFree(xfrom);
278                xmlFree(xto);
279        } else if (!strcmp(name, "tag")) {
280                char *p;
281                xk = xmlTextReaderGetAttribute(reader, "k");
282                xv = xmlTextReaderGetAttribute(reader, "v");
283                k  = xmlStrdup(xk);
284                /* FIXME: This does not look safe on UTF-8 data */
285                while ((p = strchr(k, ':')))
286                        *p = '_';
287                while ((p = strchr(k, ' ')))
288                        *p = '_';
289                addItem(&tags, k, xv);
290                DEBUG("\t%s = %s\n", xk, xv);
291                xmlFree(k);
292                xmlFree(xk);
293                xmlFree(xv);
294        } else if (!strcmp(name, "way")) {
295                xid  = xmlTextReaderGetAttribute(reader, "id");
296                addItem(&keys, "id", xid);
297                DEBUG("WAY(%s)\n", xid);
298                xmlFree(xid);
299        } else if (!strcmp(name, "seg")) {
300                xid  = xmlTextReaderGetAttribute(reader, "id");
301                id   = strtoul(xid, NULL, 10);
302                if (!id || (id > MAX_ID_SEGMENT))
303                        DEBUG("\tSEG(%s) - invalid segment ID\n", xid);
304                else if (!segments[id].to || !segments[id].from)
305                        DEBUG("\tSEG(%s) - empty segment\n", xid);
306                else {
307                        addItem(&segs, "id", xid);
308                        DEBUG("\tSEG(%s)\n", xid);
309                }
310                xmlFree(xid);
311        } else if (!strcmp(name, "osm")) {
312                /* ignore */
313        } else {
314                fprintf(stderr, "%s: Unknown element name: %s\n", __FUNCTION__, name);
315        }
316}
317
318void EndElement(xmlTextReaderPtr reader, const xmlChar *name)
319{
320        xmlChar *xid, *xlat, *xlon, *xfrom, *xto, *xk, *xv;
321        unsigned long id, to, from;
322        double lon, lat;
323        char *k, *v;
324
325        DEBUG("%s: %s\n", __FUNCTION__, name);
326
327        if (!strcmp(name, "node")) {
328                int i, count = 0; 
329                char *values = NULL;
330                char *osm_id = getItem(&keys, "id");
331                if (!osm_id) {
332                        fprintf(stderr, "%s: Node ID not in keys\n", __FUNCTION__);
333                        resetList(&keys);
334                        resetList(&tags);
335                        return;
336                }
337                id  = strtoul(osm_id, NULL, 10);
338                for (i=0; i < sizeof(exportTags) / sizeof(exportTags[0]); i++) {
339                        char *oldval = values;
340                        int width = strcmp(exportTags[i].name, "name")?32:64;
341                        if ((v = getItem(&tags, exportTags[i].name)))
342                                count++;
343                        else
344                                v = "";
345
346                        if (oldval)                             
347                                asprintf(&values, "%s,$$%.*s$$", oldval, width, v);
348                        else
349                                asprintf(&values, "$$%.*s$$", width, v);
350
351                        free(oldval);
352                }
353                if (count) {
354                        char wkt[WKT_MAX], sql[SQL_MAX];
355                        snprintf(wkt, sizeof(wkt)-1, 
356                                "POINT(%.15g %.15g)", nodes[id].lon, nodes[id].lat);
357                        wkt[sizeof(wkt)-1] = '\0';
358                        snprintf(sql, sizeof(sql)-1, 
359                                "insert into %s (osm_id,%s,way) values (%s,%s,GeomFromText('%s',4326));", table_name,fieldNames,osm_id,values,wkt);
360                        printf("%s\n", sql);
361                }
362                resetList(&keys);
363                resetList(&tags);
364                free(values);
365        } else if (!strcmp(name, "segment")) {
366                resetList(&tags);
367        } else if (!strcmp(name, "tag")) {
368                /* Separate tag list so tag stack unused */
369        } else if (!strcmp(name, "way")) {
370                int i, polygon = 0; 
371                char *values = NULL;
372                char wkt[WKT_MAX], sql[SQL_MAX];
373                char *osm_id = getItem(&keys, "id");
374
375                if (!osm_id) {
376                        fprintf(stderr, "%s: WAY ID not in keys\n", __FUNCTION__);
377                        resetList(&keys);
378                        resetList(&tags);
379                        resetList(&segs);
380                        return;
381                }
382                if (!segs->next) {
383                        DEBUG(stderr, "%s: WAY(%s) has no segments\n", __FUNCTION__, osm_id);
384                        resetList(&keys);
385                        resetList(&tags);
386                        resetList(&segs);
387                        return;
388                }
389                id  = strtoul(osm_id, NULL, 10);
390                for (i=0; i < sizeof(exportTags) / sizeof(exportTags[0]); i++) {
391                        char *oldval = values;
392                        const char *name = exportTags[i].name;
393                        if ((v = getItem(&tags, name))) {
394                                if (!strcmp(name, "landuse") || !strcmp(name, "leisure"))
395                                        polygon = 1;
396                        } else
397                                v = "";                 
398
399                        if (oldval)                             
400                                asprintf(&values, "%s,$$%s$$", oldval, v);
401                        else
402                                asprintf(&values, "$$%s$$", v);
403
404                        free(oldval);
405                }
406
407                do {
408                        WKT(wkt, polygon); 
409                        if (strlen(wkt)) {
410                                snprintf(sql, sizeof(sql)-1, 
411                                "insert into %s (osm_id,%s,way) values (%s,%s,GeomFromText('%s',4326));", table_name,fieldNames,osm_id,values,wkt);
412                                printf("%s\n", sql);
413                        }
414                } while (segs->next);
415                resetList(&keys);
416                resetList(&tags);
417                resetList(&segs);
418                free(values);
419        } else if (!strcmp(name, "seg")) {
420                /* ignore */
421        } else if (!strcmp(name, "osm")) {
422                /* ignore */
423        } else {
424                fprintf(stderr, "%s: Unknown element name: %s\n", __FUNCTION__, name);
425        }
426}
427
428static void processNode(xmlTextReaderPtr reader) {
429    xmlChar *name, *value;
430    name = xmlTextReaderName(reader);
431    if (name == NULL)
432        name = xmlStrdup(BAD_CAST "--");
433
434   switch(xmlTextReaderNodeType(reader)) {
435   case XML_READER_TYPE_ELEMENT:
436        StartElement(reader, name);     
437        if (xmlTextReaderIsEmptyElement(reader))
438                EndElement(reader, name); /* No end_element for self closing tags! */
439        break;
440   case XML_READER_TYPE_END_ELEMENT:
441        EndElement(reader, name);
442        break;
443   case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
444        /* Ignore */
445        break;
446   default:
447        fprintf(stderr, "Unknown node type %d\n", xmlTextReaderNodeType(reader));
448        break;
449   }
450
451
452   xmlFree(name);
453}
454
455int streamFile(char *filename) {
456    xmlTextReaderPtr reader;
457    int ret;
458
459    reader = xmlNewTextReaderFilename(filename);
460    if (reader != NULL) {
461        ret = xmlTextReaderRead(reader);
462        while (ret == 1) {
463            processNode(reader);
464            ret = xmlTextReaderRead(reader);
465        }
466
467        if (ret != 0) {
468            fprintf(stderr, "%s : failed to parse\n", filename);
469                return;
470        }
471
472        xmlFreeTextReader(reader);
473    } else {
474        fprintf(stderr, "Unable to open %s\n", filename);
475    }
476}
477
478
479int main(int argc, char *argv[])
480{
481        int i;
482
483        if (argc != 2) {
484                usage(argv[0]);
485                exit(1);
486        }
487 
488    /*
489     * this initialize the library and check potential ABI mismatches
490     * between the version it was compiled for and the actual shared
491     * library used.
492     */
493    LIBXML_TEST_VERSION
494
495    printf("drop table %s ;\n", table_name);
496    printf("create table %s ( osm_id int4",table_name);
497    fieldNames[0] = '\0';
498    for (i=0; i < sizeof(exportTags) / sizeof(exportTags[0]); i++) {
499        char tmp[32];
500        sprintf(tmp, i?",%s":"%s", exportTags[i].name);
501        strcat(fieldNames, tmp);
502        printf(",%s %s", exportTags[i].name, exportTags[i].type);
503    }   
504    printf(" );\n");
505    printf("select AddGeometryColumn('%s', 'way', 4326, 'GEOMETRY', 2 );\n", table_name);
506    printf("begin;\n");
507
508    streamFile(argv[1]);
509
510    printf("commit;\n");
511    printf("vacuum analyze %s;\n", table_name);
512    printf("CREATE INDEX way_index ON %s USING GIST (way);\n", table_name);
513    printf("vacuum analyze %s;\n", table_name);
514
515    /*
516     * Cleanup function for the XML library.
517     */
518    xmlCleanupParser();
519    /*
520     * this is to debug memory for regression tests
521     */
522    xmlMemoryDump();
523
524    return 0;
525}
Note: See TracBrowser for help on using the repository browser.