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