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

Last change on this file since 2158 was 2158, checked in by artem, 13 years ago

use geos to create geometries

File size: 19.7 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 > planet.sql
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
28#include <stdio.h>
29#include <unistd.h>
30#include <stdlib.h>
31#include <string.h>
32#include <assert.h>
33
34#include <libxml/xmlstring.h>
35#include <libxml/xmlreader.h>
36
37#include "bst.h"
38#include "avl.h"
39#include "build_geometry.h"
40
41#define WKT_MAX 128000
42#define SQL_MAX 140000
43       
44#if 0
45#define DEBUG printf
46#else
47#define DEBUG(x, ...)
48#endif
49
50struct tagDesc {
51      const char *name;
52      const char *type;
53      const int polygon;
54}; 
55
56static struct tagDesc exportTags[] = {
57   {"name",    "text", 0},
58   {"place",   "text", 0},
59   {"landuse", "text", 1},
60   {"leisure", "text", 1},
61   {"natural", "text", 1},
62   {"man_made","text", 0},
63   {"waterway","text", 0},
64   {"highway", "text", 0},
65   {"railway", "text", 0},
66   {"amenity", "text", 1},
67   {"tourism", "text", 0},
68   {"learning","text", 0}
69};
70
71static const char *table_name = "planet_osm";
72
73#define MAX_ID_NODE (35000000)
74#define MAX_ID_SEGMENT (35000000)
75       
76struct osmNode {
77      unsigned int id;
78      double lon;
79      double lat;
80};
81       
82struct osmSegment {
83      unsigned int id;
84      unsigned int from;
85      unsigned int to;
86};
87       
88struct osmWay {
89      unsigned int id;
90      char *values;
91      char *wkt;     
92};
93
94static struct osmNode    nodes[MAX_ID_NODE+1];
95static struct osmSegment segments[MAX_ID_SEGMENT+1];
96
97struct bst_table *node_positions;
98struct avl_table *segment_unique;
99struct avl_table *way_tree;
100
101static int count_node, count_all_node, count_dupe_node;
102static int count_segment, count_all_segment, count_dupe_segment;
103static int count_way, count_all_way, count_dupe_way;
104static int count_way_seg;
105
106// Enable this to suppress duplicate ways in the output
107// This is useful on the planet-061128.osm dump and earlier
108// to remove lots of redundant data in the US Tiger import.
109// Note: This approximately doubles the RAM usage!
110static int suppress_dupes=0;
111
112struct keyval {
113      char *key;
114      char *value;
115      struct keyval *next;
116      struct keyval *prev;
117};
118       
119
120static struct keyval keys, tags, segs;
121
122
123void usage(const char *arg0)
124{
125   fprintf(stderr, "Usage error:\n\t%s planet.osm  > planet.sql\n", arg0);
126   fprintf(stderr, "or\n\tgzip -dc planet.osm.gz | %s - | gzip -c > planet.sql.gz\n", arg0);
127}
128
129void initList(struct keyval *head)
130{
131   head->next = head;
132   head->prev = head;
133   head->key = NULL;
134   head->value = NULL;
135}
136
137void freeItem(struct keyval *p)
138{
139   free(p->key);
140   free(p->value);
141   free(p);
142}
143
144
145unsigned int countList(struct keyval *head) 
146{
147   struct keyval *p = head->next;
148   unsigned int count = 0;     
149
150   while(p != head) {
151      count++;
152      p = p->next;
153   }
154   return count;
155}
156
157int listHasData(struct keyval *head) 
158{
159   return (head->next != head);
160}
161
162
163char *getItem(struct keyval *head, const char *name)
164{
165   struct keyval *p = head->next;
166   while(p != head) {
167      if (!strcmp(p->key, name))
168         return p->value;
169      p = p->next;
170   }
171   return NULL;
172}       
173
174
175struct keyval *popItem(struct keyval *head)
176{
177   struct keyval *p = head->next;
178   if (p == head)
179      return NULL;
180
181   head->next = p->next;
182   p->next->prev = head;
183
184   p->next = NULL;
185   p->prev = NULL;
186
187   return p;
188}       
189
190
191void pushItem(struct keyval *head, struct keyval *item)
192{
193   item->next = head;
194   item->prev = head->prev;
195   head->prev->next = item;
196   head->prev = item;
197}       
198
199int addItem(struct keyval *head, const char *name, const char *value, int noDupe)
200{
201   struct keyval *item;
202       
203   if (noDupe) {
204      item = head->next;
205      while (item != head) {
206         if (!strcmp(item->value, value) && !strcmp(item->key, name)) {
207            //fprintf(stderr, "Discarded %s=%s\n", name, value);
208            return 1;
209         }
210         item = item->next;
211      }
212   }
213       
214   item = malloc(sizeof(struct keyval));
215               
216   if (!item) {
217      fprintf(stderr, "Error allocating keyval\n");
218      return 2;
219   }
220
221   item->key   = strdup(name);
222   item->value = strdup(value);
223
224   item->next = head->next;
225   item->prev = head;
226   head->next->prev = item;
227   head->next = item;
228
229   return 0;
230}
231
232void resetList(struct keyval *head) 
233{
234   struct keyval *item;
235       
236   while((item = popItem(head))) 
237      freeItem(item);
238}
239
240size_t WKT(int polygon)
241{
242   while (listHasData(&segs))
243   {
244      struct keyval *p;
245      unsigned int id, to, from;
246      double x0, y0, x1, y1;
247      p = popItem(&segs);
248      id = strtoul(p->value, NULL, 10);
249
250      from = segments[id].from;
251      to   = segments[id].to; 
252
253      x0 = nodes[from].lon;
254      y0 = nodes[from].lat;
255      x1 = nodes[to].lon;
256      y1 = nodes[to].lat;
257      add_segment(x0,y0,x1,y1);
258   }
259   return  build_geometry(polygon);
260}
261
262
263void StartElement(xmlTextReaderPtr reader, const xmlChar *name)
264{
265   xmlChar *xid, *xlat, *xlon, *xfrom, *xto, *xk, *xv;
266   unsigned int id, to, from;
267   double lon, lat;
268   char *k;
269
270   if (xmlStrEqual(name, BAD_CAST "node")) {
271      struct osmNode *node, *dupe;
272      xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
273      xlon = xmlTextReaderGetAttribute(reader, BAD_CAST "lon");
274      xlat = xmlTextReaderGetAttribute(reader, BAD_CAST "lat");
275      assert(xid); assert(xlon); assert(xlat);
276      id  = strtoul((char *)xid, NULL, 10);
277      lon = strtod((char *)xlon, NULL);
278      lat = strtod((char *)xlat, NULL);
279
280      assert(id > 0 && id < MAX_ID_NODE);
281      count_all_node++;
282      if (count_all_node%10000 == 0) 
283         fprintf(stderr, "\rProcessing: Node(%dk)", count_all_node/1000);
284
285      node = &nodes[id];
286      node->id  = id;
287      node->lon = lon;
288      node->lat = lat;
289
290      dupe = suppress_dupes ? bst_insert(node_positions, (void *)node) : NULL;
291               
292      if (!dupe) {
293         DEBUG("NODE(%d) %f %f\n", id, lon, lat);
294      } else {
295         node->id = dupe->id;
296         count_dupe_node++;
297         DEBUG("NODE(%d) %f %f - dupe %d\n", id, lon, lat, dupe->id);
298      }
299      addItem(&keys, "id", (char *)xid, 0);
300
301      xmlFree(xid);
302      xmlFree(xlon);
303      xmlFree(xlat);
304   } else if (xmlStrEqual(name, BAD_CAST "segment")) {
305      xid   = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
306      xfrom = xmlTextReaderGetAttribute(reader, BAD_CAST "from");
307      xto   = xmlTextReaderGetAttribute(reader, BAD_CAST "to");
308      assert(xid); assert(xfrom); assert(xto);
309      id   = strtoul((char *)xid, NULL, 10);
310      from = strtoul((char *)xfrom, NULL, 10);
311      to   = strtoul((char *)xto, NULL, 10);
312
313      assert(id > 0 && id < MAX_ID_SEGMENT);
314      if (count_all_segment == 0) {
315         //fprintf(stderr, "\nBalancing node tree\n");
316         bst_balance(node_positions);
317         fprintf(stderr, "\n");
318      }
319
320      count_all_segment++;
321      if (count_all_segment%10000 == 0) 
322         fprintf(stderr, "\rProcessing: Segment(%dk)", count_all_segment/1000);
323
324      if (!nodes[to].id) {
325         DEBUG("SEGMENT(%d), NODE(%d) is missing\n", id, to);
326      } else if (!nodes[from].id) {
327         DEBUG("SEGMENT(%d), NODE(%d) is missing\n", id, from);
328      } else {
329         from = nodes[from].id;
330         to   = nodes[to].id;
331         if (from != to) {
332            struct osmSegment *segment, *dupe;
333            segment = &segments[id];
334            segment->id   = id;
335            segment->to   = to;
336            segment->from = from;
337
338            dupe = suppress_dupes ? avl_insert(segment_unique, (void *)segment) : NULL;
339
340            if (!dupe) {
341               count_segment++;
342               DEBUG("SEGMENT(%d) %d, %d\n", id, from, to);
343            } else {
344               count_dupe_segment++;
345               segment->id = dupe->id;
346               DEBUG("SEGMENT(%d) %d, %d - dupe %d\n", id, from, to, dupe->id);
347            }
348         }
349      }
350
351      xmlFree(xid);
352      xmlFree(xfrom);
353      xmlFree(xto);
354   } else if (xmlStrEqual(name, BAD_CAST "tag")) {
355      char *p;
356      xk = xmlTextReaderGetAttribute(reader, BAD_CAST "k");
357      xv = xmlTextReaderGetAttribute(reader, BAD_CAST "v");
358      assert(xk); assert(xv);
359      k  = (char *)xmlStrdup(xk);
360      /* FIXME: This does not look safe on UTF-8 data */
361      while ((p = strchr(k, ':')))
362         *p = '_';
363      while ((p = strchr(k, ' ')))
364         *p = '_';
365      addItem(&tags, k, (char *)xv, 0);
366      DEBUG("\t%s = %s\n", xk, xv);
367      xmlFree(k);
368      xmlFree(xk);
369      xmlFree(xv);
370   } else if (xmlStrEqual(name, BAD_CAST "way")) {
371      xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
372      assert(xid);
373      addItem(&keys, "id", (char *)xid, 0);
374      DEBUG("WAY(%s)\n", xid);
375
376      if (count_all_way == 0)
377         fprintf(stderr, "\n");
378               
379      count_all_way++;
380      if (count_all_way%1000 == 0) 
381         fprintf(stderr, "\rProcessing: Way(%dk)", count_all_way/1000);
382
383      xmlFree(xid);
384   } else if (xmlStrEqual(name, BAD_CAST "seg")) {
385      xid  = xmlTextReaderGetAttribute(reader, BAD_CAST "id");
386      assert(xid);
387      id   = strtoul((char *)xid, NULL, 10);
388      if (!id || (id > MAX_ID_SEGMENT))
389         DEBUG("\tSEG(%s) - invalid segment ID\n", xid);
390      else if (!segments[id].id)
391         DEBUG("\tSEG(%s) - missing segment\n", xid);
392      else {
393         char *tmp;
394         // Find unique segment
395         id = segments[id].id;
396         asprintf(&tmp, "%d", id);
397         if (addItem(&segs, "id", tmp, 1)) {
398            const char *way_id = getItem(&keys, "id");
399            if (!way_id) way_id = "???";
400            //fprintf(stderr, "Way %s with duplicate segment id %d\n", way_id, id);
401            count_way_seg++;
402         }
403         DEBUG("\tSEG(%s)\n", xid);
404         free(tmp);
405      }
406      xmlFree(xid);
407   } else if (xmlStrEqual(name, BAD_CAST "osm")) {
408      /* ignore */
409   } else {
410      fprintf(stderr, "%s: Unknown element name: %s\n", __FUNCTION__, name);
411   }
412}
413
414void EndElement(xmlTextReaderPtr reader, const xmlChar *name)
415{
416   unsigned int id;
417
418   DEBUG("%s: %s\n", __FUNCTION__, name);
419
420   if (xmlStrEqual(name, BAD_CAST "node")) {
421      int i;
422      char *values = NULL, *names = NULL;
423      char *osm_id = getItem(&keys, "id");
424      if (!osm_id) {
425         fprintf(stderr, "%s: Node ID not in keys\n", __FUNCTION__);
426         resetList(&keys);
427         resetList(&tags);
428         return;
429      }
430      id  = strtoul(osm_id, NULL, 10);
431      assert(nodes[id].id);
432#if 0
433      if (id != nodes[id].id) {
434         // TODO: Consider dropping all duplicate nodes or compare tags?
435         // Don't really want to store all node attributes for comparison.
436         resetList(&keys);
437         resetList(&tags);
438         return;
439      }
440#endif
441      for (i=0; i < sizeof(exportTags) / sizeof(exportTags[0]); i++) {
442         char *v;
443         if ((v = getItem(&tags, exportTags[i].name))) {
444            if (values) {
445               char *oldval = values, *oldnam = names;
446               asprintf(&names,  "%s,\"%s\"", oldnam, exportTags[i].name);
447               asprintf(&values, "%s,$$%s$$", oldval, v);
448               free(oldnam);
449               free(oldval);
450            } else {
451               asprintf(&names,  "\"%s\"", exportTags[i].name);
452               asprintf(&values, "$$%s$$", v);
453            }
454         }
455      }
456      if (values) {
457         char wkt[WKT_MAX];
458         count_node++;
459         snprintf(wkt, sizeof(wkt)-1, 
460                  "POINT(%.15g %.15g)", nodes[id].lon, nodes[id].lat);
461         wkt[sizeof(wkt)-1] = '\0';
462         printf("insert into %s (osm_id,%s,way) values (%s,%s,GeomFromText('%s',4326));\n", table_name,names,osm_id,values,wkt);
463      }
464      resetList(&keys);
465      resetList(&tags);
466      free(values);
467      free(names);
468   } else if (xmlStrEqual(name, BAD_CAST "segment")) {
469      resetList(&tags);
470   } else if (xmlStrEqual(name, BAD_CAST "tag")) {
471      /* Separate tag list so tag stack unused */
472   } else if (xmlStrEqual(name, BAD_CAST "way")) {
473      int i, polygon = 0; 
474      char *values = NULL, *names = NULL;
475      char *osm_id = getItem(&keys, "id");
476     
477      if (!osm_id) {
478         fprintf(stderr, "%s: WAY ID not in keys\n", __FUNCTION__);
479         resetList(&keys);
480         resetList(&tags);
481         resetList(&segs);
482         return;
483      }
484
485      if (!listHasData(&segs)) {
486         DEBUG("%s: WAY(%s) has no segments\n", __FUNCTION__, osm_id);
487         resetList(&keys);
488         resetList(&tags);
489         resetList(&segs);
490         return;
491      }
492      id  = strtoul(osm_id, NULL, 10);
493
494      for (i=0; i < sizeof(exportTags) / sizeof(exportTags[0]); i++) {
495         char *v;
496         if ((v = getItem(&tags, exportTags[i].name))) {
497            if (values) {
498               char *oldval = values, *oldnam = names;
499               asprintf(&names,  "%s,\"%s\"", oldnam, exportTags[i].name);
500               asprintf(&values, "%s,$$%s$$", oldval, v);
501               free(oldnam);
502               free(oldval);
503            } else {
504               asprintf(&names,  "\"%s\"", exportTags[i].name);
505               asprintf(&values, "$$%s$$", v);
506            }
507            polygon |= exportTags[i].polygon;
508         }
509      }     
510      if (values) {
511         
512         size_t wkt_size = WKT(polygon);
513         
514         if (wkt_size)
515         {
516            unsigned i;
517            for (i=0;i<wkt_size;i++)
518            {
519               const char * wkt = get_wkt(i);
520               if (strlen(wkt)) {
521                  struct osmWay *dupe = NULL;   
522                  if (suppress_dupes) {
523                     struct osmWay *way = malloc(sizeof(struct osmWay));
524                     assert(way);
525                     way->id = id;
526                     way->values = strdup(values);
527                     way->wkt    = strdup(wkt);
528                     assert(way->values);
529                     assert(way->wkt[i]);
530                     dupe = avl_insert(way_tree, (void *)way);
531                     if (dupe) {
532                        DEBUG("WAY(%d) - duplicate of %d\n", id, dupe->id);
533                        count_dupe_way++;
534                        free(way->values);
535                        free(way->wkt);
536                        free(way);
537                     }
538                  } 
539                  if (!dupe) {
540                     printf("insert into %s (osm_id,%s,way) values (%s,%s,GeomFromText('%s',4326));\n", table_name,names,osm_id,values,wkt);
541                     count_way++;       
542                  }
543               }
544            }
545            clear_wkts();
546         }
547      }
548     
549      resetList(&keys);
550      resetList(&tags);
551      resetList(&segs);
552      free(values);
553      free(names);
554   } else if (xmlStrEqual(name, BAD_CAST "seg")) {
555      /* ignore */
556   } else if (xmlStrEqual(name, BAD_CAST "osm")) {
557      /* ignore */
558   } else {
559      fprintf(stderr, "%s: Unknown element name: %s\n", __FUNCTION__, name);
560   }
561}
562
563static void processNode(xmlTextReaderPtr reader) {
564   xmlChar *name;
565   name = xmlTextReaderName(reader);
566   if (name == NULL)
567      name = xmlStrdup(BAD_CAST "--");
568       
569   switch(xmlTextReaderNodeType(reader)) {
570      case XML_READER_TYPE_ELEMENT:
571         StartElement(reader, name);   
572         if (xmlTextReaderIsEmptyElement(reader))
573            EndElement(reader, name); /* No end_element for self closing tags! */
574         break;
575      case XML_READER_TYPE_END_ELEMENT:
576         EndElement(reader, name);
577         break;
578      case XML_READER_TYPE_SIGNIFICANT_WHITESPACE:
579         /* Ignore */
580         break;
581      default:
582         fprintf(stderr, "Unknown node type %d\n", xmlTextReaderNodeType(reader));
583         break;
584   }
585       
586   xmlFree(name);
587}
588
589void streamFile(char *filename) {
590   xmlTextReaderPtr reader;
591   int ret;
592       
593   reader = xmlNewTextReaderFilename(filename);
594   if (reader != NULL) {
595      ret = xmlTextReaderRead(reader);
596      while (ret == 1) {
597         processNode(reader);
598         ret = xmlTextReaderRead(reader);
599      }
600       
601      if (ret != 0) {
602         fprintf(stderr, "%s : failed to parse\n", filename);
603         return;
604      }
605       
606      xmlFreeTextReader(reader);
607   } else {
608      fprintf(stderr, "Unable to open %s\n", filename);
609   }
610}
611
612int compare_node(const void *bst_a, const void *bst_b, void *bst_param)
613{
614   const struct osmNode *nA = bst_a;
615   const struct osmNode *nB = bst_b;
616
617   if (nA == nB) return 0;
618   if (nA->id == nB->id) return 0;
619
620   if (nA->lon < nB->lon)
621      return -1;
622   else if (nA->lon > nB->lon)
623      return +1;
624       
625   if (nA->lat < nB->lat)
626      return -1;
627   else if (nA->lat > nB->lat)
628      return +1;
629
630   return 0; 
631}
632
633int compare_segment(const void *avl_a, const void *avl_b, void *avl_param)
634{
635   const struct osmSegment *sA = avl_a;
636   const struct osmSegment *sB = avl_b;
637
638   if (sA == sB) return 0;
639   if (sA->id == sB->id) return 0;
640
641   if (sA->from < sB->from)
642      return -1;
643   else if (sA->from > sB->from)
644      return +1;
645
646   if (sA->to < sB->to)
647      return -1;
648   else if (sA->to > sB->to)
649      return +1;
650   return 0;
651}
652
653int compare_way(const void *avl_a, const void *avl_b, void *avl_param)
654{
655   const struct osmWay *wA = avl_a;
656   const struct osmWay *wB = avl_b;
657   int c;
658
659   if (wA == wB) return 0;
660   if (wA->id == wB->id) return 0;
661
662   // TODO: Maybe keeping a hash of WKT would be better?
663   c = strcmp(wA->wkt, wB->wkt);
664   if (c) return c;
665
666   return strcmp(wA->values, wB->values);
667}
668
669
670int main(int argc, char *argv[])
671{
672   int i;
673       
674   if (argc != 2) {
675      usage(argv[0]);
676      exit(1);
677   }
678 
679   node_positions = bst_create(compare_node, NULL, NULL);
680   assert(node_positions);
681   segment_unique = avl_create(compare_segment, NULL, NULL);
682   assert(segment_unique);
683   way_tree = avl_create(compare_way, NULL, NULL);
684   assert(way_tree);
685
686   initList(&keys);
687   initList(&tags);
688   initList(&segs);
689
690   LIBXML_TEST_VERSION
691       
692   printf("drop table %s ;\n", table_name);
693   printf("create table %s ( osm_id int4",table_name);
694   for (i=0; i < sizeof(exportTags) / sizeof(exportTags[0]); i++)
695      printf(",\"%s\" %s", exportTags[i].name, exportTags[i].type);
696   printf(" );\n");
697   printf("select AddGeometryColumn('%s', 'way', 4326, 'GEOMETRY', 2 );\n", table_name);
698   printf("begin;\n");
699       
700   streamFile(argv[1]);
701       
702   printf("commit;\n");
703   printf("vacuum analyze %s;\n", table_name);
704   printf("CREATE INDEX way_index ON %s USING GIST (way GIST_GEOMETRY_OPS);\n", table_name);
705   printf("vacuum analyze %s;\n", table_name);
706       
707   xmlCleanupParser();
708   xmlMemoryDump();
709       
710   fprintf(stderr, "\n");
711       
712   if (count_all_node) {
713      fprintf(stderr, "Node stats: out(%d), dupe(%d) (%.1f%%), total(%d)\n",
714              count_node, count_dupe_node, 100.0 * count_dupe_node / count_all_node, count_all_node);
715   }
716   if (count_all_segment) {
717      fprintf(stderr, "Segment stats: out(%d), dupe(%d) (%.1f%%), total(%d)\n",
718              count_segment, count_dupe_segment, 100.0 * count_dupe_segment / count_all_segment, count_all_segment);
719   }
720   if (count_all_way) {
721      fprintf(stderr, "Way stats: out(%d), dupe(%d) (%.1f%%), total(%d)\n",
722              count_way, count_dupe_way, 100.0 * count_dupe_way / count_all_way, count_all_way);
723   }
724   fprintf(stderr, "Way stats: duplicate segments in ways %d\n", count_way_seg);
725
726   return 0;
727}
Note: See TracBrowser for help on using the repository browser.