Planet dumper for 0.6 postgres DBs.
[planetdump.git] / output_osm.c
1 #include "output_osm.h"
2 #include <string.h>
3 #include <assert.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6 #include <time.h>
7
8 #define INDENT "  "
9
10 #ifdef USE_ICONV
11 #include <iconv.h>
12 #define ICONV_ERROR ((iconv_t)-1)
13 static iconv_t cd = ICONV_ERROR;
14 #endif
15
16 /* const char *xmlescape(char *in)
17  *
18  * Character escaping for valid XML output as per http://www.w3.org/TR/REC-xml/
19  *
20  * WARNING: this function uses a static buffer so do not rely on the result
21  * being constant if called more than once
22  */
23 const char *xmlescape(const char *in)
24
25     static char escape_tmp[1024];
26     int len;
27     // Convert from DB charset to UTF8
28     // Note: this assumes that inbuf is C-string compatible, i.e. has no embedded NUL like UTF16!
29     // To fix this we'd need to fix the DB output parameters too
30 #ifdef USE_ICONV 
31     if (cd != ICONV_ERROR) {
32         char iconv_tmp[1024];
33         char *inbuf = in, *outbuf = iconv_tmp;
34         size_t ret;
35         size_t inlen = strlen(inbuf);
36         size_t outlen = sizeof(iconv_tmp);
37         bzero(iconv_tmp, sizeof(iconv_tmp));
38         iconv(cd, NULL, 0, NULL, 0);
39
40         ret = iconv(cd, &inbuf, &inlen, &outbuf, &outlen);
41
42         if (ret == -1) {
43             fprintf(stderr, "failed to convert '%s'\n", in);
44             // Carry on regardless
45         }
46         in = iconv_tmp;
47     }
48 #endif
49
50     len = 0;
51     while(*in) {
52         int left = sizeof(escape_tmp) - len - 1;
53
54         if (left < 7)
55             break;
56
57         if (*in == '&') {
58             strcpy(&escape_tmp[len], "&amp;");
59             len += strlen("&amp;");
60         } else if (*in == '<') {
61             strcpy(&escape_tmp[len], "&lt;");
62             len += strlen("&lt;");
63         } else if (*in == '>') {
64             strcpy(&escape_tmp[len], "&gt;");
65             len += strlen("&lt;");
66         } else if (*in == '"') {
67             strcpy(&escape_tmp[len], "&quot;");
68             len += strlen("&quot;");
69         } else if ((*in >= 0) && (*in < 32)) {
70             escape_tmp[len] = '?';
71             len++;
72         } else {
73             escape_tmp[len] = *in;
74             len++;
75         }
76         
77         in++;
78     }
79     escape_tmp[len] = '\0';
80     return escape_tmp;
81 }
82
83 void osm_tags(struct keyval *tags)
84 {
85     struct keyval *p;
86
87     while ((p = popItem(tags)) != NULL) {
88         printf(INDENT INDENT "<tag k=\"%s\"", xmlescape(p->key));
89         printf(" v=\"%s\" />\n", xmlescape(p->value));
90         freeItem(p);
91     }
92
93    resetList(tags);
94 }
95
96 void osm_node(int id, long double lat, long double lon, struct keyval *tags, const char *ts, const char *user, int version, int changeset)
97 {
98   if (listHasData(tags)) {
99     printf(INDENT "<node id=\"%d\" lat=\"%.7Lf\" lon=\"%.7Lf\" "
100            "timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s>\n", 
101            id, lat, lon, ts, version, changeset, user);
102     osm_tags(tags);
103     printf(INDENT "</node>\n");
104   } else {
105     printf(INDENT "<node id=\"%d\" lat=\"%.7Lf\" lon=\"%.7Lf\" "
106            "timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s/>\n", 
107            id, lat, lon, ts, version, changeset, user);
108   }
109 }
110
111 void osm_way(int id, struct keyval *nodes, struct keyval *tags, const char *ts, const char *user, int version, int changeset)
112 {
113   struct keyval *p;
114   
115   if (listHasData(tags) || listHasData(nodes)) {
116     printf(INDENT "<way id=\"%d\" timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s>\n", id, ts, version, changeset, user);
117     while ((p = popItem(nodes)) != NULL) {
118       printf(INDENT INDENT "<nd ref=\"%s\" />\n", p->value);
119       freeItem(p);
120     }
121     osm_tags(tags);
122     printf(INDENT "</way>\n");
123   } else {
124     printf(INDENT "<way id=\"%d\" timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s/>\n", id, ts, version, changeset, user);
125   }
126 }
127
128 void osm_relation(int id, struct keyval *members, struct keyval *roles, struct keyval *tags, const char *ts, const char *user, int version, int changeset)
129 {
130   struct keyval *p, *q;
131   
132   if (listHasData(tags) || listHasData(members)) {
133     printf(INDENT "<relation id=\"%d\" timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s>\n", id, ts, version, changeset, user);
134     while (((p = popItem(members)) != NULL) && ((q = popItem(roles)) != NULL)) {
135       const char *m_type = p->key;
136       const char *m_id   = p->value;
137       const char *m_role = q->value;
138       printf(INDENT INDENT "<member type=\"%s\" ref=\"%s\" role=\"%s\"/>\n", m_type, m_id, m_role);
139       freeItem(p);
140       freeItem(q);
141     }
142     osm_tags(tags);
143     printf(INDENT "</relation>\n");
144   } else {
145     printf(INDENT "<relation id=\"%d\" timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s/>\n", id, ts, version, changeset, user);
146   }
147 }
148
149 /**
150  * output the header of the osm XML file.
151  *
152  * note the *lovely* C-style error handling...
153  */
154 void osm_header() {
155   char timestamp[200];
156   time_t t;
157   struct tm *tmp;
158
159   t = time(NULL);
160   tmp = gmtime(&t);
161   if (tmp == NULL) {
162     perror("gmtime");
163     exit(1);
164   }
165
166   if (strftime(timestamp, sizeof(timestamp), 
167                "%Y-%m-%dT%H:%M:%SZ", tmp) == 0) {
168     fprintf(stderr, "ERROR: strftime returned NULL.\n");
169     exit(1);
170   }
171
172   printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
173   printf("<osm version=\"0.6\" generator=\"OpenStreetMap planet.c\" "
174          "timestamp=\"%s\">\n", timestamp);
175   printf(INDENT "<bound box=\"-90,-180,90,180\" "
176          "origin=\"http://www.openstreetmap.org/api/0.6\" />\n");
177 }
178
179 /**
180  * close off the XML file
181  */
182 void osm_footer() {
183   printf("</osm>\n");
184 }