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

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

Initial version of C implmentation of OSM to Postgresql converter

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