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

Last change on this file since 1669 was 1669, checked in by nick, 14 years ago

added natural type

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