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