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

Last change on this file since 1621 was 1621, checked in by steve, 14 years ago

change varchars to text

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","text"},
24               {"place","text"},
25               {"landuse","text"},
26               {"leisure","text"},
27               {"waterway","text"},
28               {"highway","text"},
29               {"amenity","text"},
30               {"tourism","text"},
31               {"learning","text"}
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.