Fixed bug calling reformDate twice. Since it returns a pointer to static data, it...
[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_changeset(int id, const char *user, const char *created_at, const char *closed_at, int has_bbox, 
97                    long double min_lat, long double max_lat, long double min_lon, long double max_lon, 
98                    int open, struct keyval *tags) {
99   printf(INDENT "<changeset id=\"%d\" created_at=\"%s\" ", id, created_at);
100   if (open) {
101     printf("open=\"true\" ");
102   } else {
103     printf("closed_at=\"%s\" open=\"false\" ", closed_at);
104   }
105   if (has_bbox) {
106     printf("min_lon=\"%.7Lf\" min_lat=\"%.7Lf\" max_lon=\"%.7Lf\" max_lat=\"%.7Lf\" ",
107            min_lon, min_lat, max_lon, max_lat);
108   }
109   printf("%s", user);
110   if (listHasData(tags)) {
111     printf(">\n");
112     osm_tags(tags);
113     printf(INDENT "</changeset>\n");
114   } else {
115     printf("/>\n");
116   }
117 }
118
119 void osm_node(int id, long double lat, long double lon, struct keyval *tags, const char *ts, const char *user, int version, int changeset)
120 {
121   if (listHasData(tags)) {
122     printf(INDENT "<node id=\"%d\" lat=\"%.7Lf\" lon=\"%.7Lf\" "
123            "timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s>\n", 
124            id, lat, lon, ts, version, changeset, user);
125     osm_tags(tags);
126     printf(INDENT "</node>\n");
127   } else {
128     printf(INDENT "<node id=\"%d\" lat=\"%.7Lf\" lon=\"%.7Lf\" "
129            "timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s/>\n", 
130            id, lat, lon, ts, version, changeset, user);
131   }
132 }
133
134 void osm_way(int id, struct keyval *nodes, struct keyval *tags, const char *ts, const char *user, int version, int changeset)
135 {
136   struct keyval *p;
137   
138   if (listHasData(tags) || listHasData(nodes)) {
139     printf(INDENT "<way id=\"%d\" timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s>\n", id, ts, version, changeset, user);
140     while ((p = popItem(nodes)) != NULL) {
141       printf(INDENT INDENT "<nd ref=\"%s\" />\n", p->value);
142       freeItem(p);
143     }
144     osm_tags(tags);
145     printf(INDENT "</way>\n");
146   } else {
147     printf(INDENT "<way id=\"%d\" timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s/>\n", id, ts, version, changeset, user);
148   }
149 }
150
151 void osm_relation(int id, struct keyval *members, struct keyval *roles, struct keyval *tags, const char *ts, const char *user, int version, int changeset)
152 {
153   struct keyval *p, *q;
154   
155   if (listHasData(tags) || listHasData(members)) {
156     printf(INDENT "<relation id=\"%d\" timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s>\n", id, ts, version, changeset, user);
157     while (((p = popItem(members)) != NULL) && ((q = popItem(roles)) != NULL)) {
158       char *m_type = p->key;
159       char *i; 
160       for (i = m_type; *i; i++) *i = tolower(*i);
161       const char *m_id   = p->value;
162       const char *m_role = q->value;
163       printf(INDENT INDENT "<member type=\"%s\" ref=\"%s\" role=\"%s\"/>\n", m_type, m_id, m_role);
164       freeItem(p);
165       freeItem(q);
166     }
167     osm_tags(tags);
168     printf(INDENT "</relation>\n");
169   } else {
170     printf(INDENT "<relation id=\"%d\" timestamp=\"%s\" version=\"%d\" changeset=\"%d\"%s/>\n", id, ts, version, changeset, user);
171   }
172 }
173
174 /**
175  * output the header of the osm XML file.
176  *
177  * note the *lovely* C-style error handling...
178  */
179 void osm_header() {
180   char timestamp[200];
181   time_t t;
182   struct tm *tmp;
183
184   t = time(NULL);
185   tmp = gmtime(&t);
186   if (tmp == NULL) {
187     perror("gmtime");
188     exit(1);
189   }
190
191   if (strftime(timestamp, sizeof(timestamp), 
192                "%Y-%m-%dT%H:%M:%SZ", tmp) == 0) {
193     fprintf(stderr, "ERROR: strftime returned NULL.\n");
194     exit(1);
195   }
196
197   printf("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
198   printf("<osm version=\"0.6\" generator=\"OpenStreetMap planet.c\" "
199          "timestamp=\"%s\">\n", timestamp);
200   printf(INDENT "<bound box=\"-90,-180,90,180\" "
201          "origin=\"http://www.openstreetmap.org/api/0.6\" />\n");
202 }
203
204 /**
205  * close off the XML file
206  */
207 void osm_footer() {
208   printf("</osm>\n");
209 }