cc22f2257cd151ed83b9bb3c98e9311c29c0e558
[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[2048];
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 == 9) || (*in == 10) || (*in == 13)) {
70             len+=sprintf(&escape_tmp[len], "&#%d;", *in);
71         } else if ((*in >= 0) && (*in < 32)) {
72            /* These characters are not valid in XML output
73             * http://www.w3.org/TR/REC-xml/#NT-Char
74             * (tab, newline, carriage return are handled above)
75             */
76             escape_tmp[len] = '?';
77             len++;
78         } else {
79             escape_tmp[len] = *in;
80             len++;
81         }
82         
83         in++;
84     }
85     escape_tmp[len] = '\0';
86     return escape_tmp;
87 }
88
89 void osm_tags(struct keyval *tags)
90 {
91     struct keyval *p;
92
93     while ((p = popItem(tags)) != NULL) {
94         printf(INDENT INDENT "<tag k=\"%s\"", xmlescape(p->key));
95         printf(" v=\"%s\" />\n", xmlescape(p->value));
96         freeItem(p);
97     }
98
99    resetList(tags);
100 }
101
102 void osm_changeset(int id, const char *user, const char *created_at, const char *closed_at, 
103            int num_changes, int has_bbox, 
104                    long double min_lat, long double max_lat, long double min_lon, long double max_lon, 
105                    int open, struct keyval *tags) {
106   printf(INDENT "<changeset id=\"%d\" created_at=\"%s\" num_changes=\"%d\" ", id, created_at, num_changes);
107   if (open) {
108     printf("open=\"true\" ");
109   } else {
110     printf("closed_at=\"%s\" open=\"false\" ", closed_at);
111   }
112   if (has_bbox) {
113     printf("min_lon=\"%.7Lf\" min_lat=\"%.7Lf\" max_lon=\"%.7Lf\" max_lat=\"%.7Lf\" ",
114            min_lon, min_lat, max_lon, max_lat);
115   }
116   printf("%s", user);
117   if (listHasData(tags)) {
118     printf(">\n");
119     osm_tags(tags);
120     printf(INDENT "</changeset>\n");
121   } else {
122     printf("/>\n");
123   }
124 }
125
126 void osm_node(int id, long double lat, long double lon, struct keyval *tags, const char *ts, const char *user, int version, int changeset)
127 {
128   if (listHasData(tags)) {
129     printf(INDENT "<node id=\"%d\" lat=\"%.7Lf\" lon=\"%.7Lf\" "
130            "timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s>\n", 
131            id, lat, lon, ts, version, changeset, user);
132     osm_tags(tags);
133     printf(INDENT "</node>\n");
134   } else {
135     printf(INDENT "<node id=\"%d\" lat=\"%.7Lf\" lon=\"%.7Lf\" "
136            "timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s/>\n", 
137            id, lat, lon, ts, version, changeset, user);
138   }
139 }
140
141 void osm_way(int id, struct keyval *nodes, struct keyval *tags, const char *ts, const char *user, int version, int changeset)
142 {
143   struct keyval *p;
144   
145   if (listHasData(tags) || listHasData(nodes)) {
146     printf(INDENT "<way id=\"%d\" timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s>\n", id, ts, version, changeset, user);
147     while ((p = popItem(nodes)) != NULL) {
148       printf(INDENT INDENT "<nd ref=\"%s\" />\n", p->value);
149       freeItem(p);
150     }
151     osm_tags(tags);
152     printf(INDENT "</way>\n");
153   } else {
154     printf(INDENT "<way id=\"%d\" timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s/>\n", id, ts, version, changeset, user);
155   }
156 }
157
158 void osm_relation(int id, struct keyval *members, struct keyval *roles, struct keyval *tags, const char *ts, const char *user, int version, int changeset)
159 {
160   struct keyval *p, *q;
161   
162   if (listHasData(tags) || listHasData(members)) {
163     printf(INDENT "<relation id=\"%d\" timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s>\n", id, ts, version, changeset, user);
164     while (((p = popItem(members)) != NULL) && ((q = popItem(roles)) != NULL)) {
165       char *m_type = p->key;
166       char *i; 
167       for (i = m_type; *i; i++) *i = tolower(*i);
168       const char *m_id   = p->value;
169       const char *m_role = q->value;
170       printf(INDENT INDENT "<member type=\"%s\" ref=\"%s\" role=\"%s\"/>\n", m_type, m_id, xmlescape(m_role));
171       freeItem(p);
172       freeItem(q);
173     }
174     osm_tags(tags);
175     printf(INDENT "</relation>\n");
176   } else {
177     printf(INDENT "<relation id=\"%d\" timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s/>\n", id, ts, version, changeset, user);
178   }
179 }
180
181 /**
182  * output the header of the osm XML file.
183  *
184  * note the *lovely* C-style error handling...
185  */
186 void osm_header() {
187   char timestamp[200];
188   time_t t;
189   struct tm *tmp;
190
191   t = time(NULL);
192   tmp = gmtime(&t);
193   if (tmp == NULL) {
194     perror("gmtime");
195     exit(1);
196   }
197
198   if (strftime(timestamp, sizeof(timestamp), 
199                "%Y-%m-%dT%H:%M:%SZ", tmp) == 0) {
200     fprintf(stderr, "ERROR: strftime returned NULL.\n");
201     exit(1);
202   }
203
204   printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
205   printf("<osm version=\"0.6\" generator=\"OpenStreetMap planet.c\" "
206          "timestamp=\"%s\">\n", timestamp);
207   printf(INDENT "<bound box=\"-90,-180,90,180\" "
208          "origin=\"http://www.openstreetmap.org/api/0.6\" />\n");
209 }
210
211 /**
212  * close off the XML file
213  */
214 void osm_footer() {
215   printf("</osm>\n");
216 }