1 /******************************************************************************
2 * $Id: osm2osm.cpp 355b41831cd2685c85d1aabe5b95665a2c6e99b7 2019-06-19 17:07:04 +0200 Even Rouault $
3 *
4 * Project: OpenGIS Simple Features Reference Implementation
5 * Author: Even Rouault, <even dot rouault at spatialys.com>
6 * Purpose: osm2osm
7 *
8 ******************************************************************************
9 * Copyright (c) 2012, Even Rouault <even dot rouault at spatialys.com>
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included
19 * in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
28 ****************************************************************************/
29
30 #include "cpl_vsi.h"
31
32 #include "osm_parser.h"
33
34 #include <cstdio>
35 #include <ctime>
36
37 constexpr GIntBig SECSPERMIN = 60L;
38 constexpr GIntBig MINSPERHOUR = 60L;
39 constexpr GIntBig HOURSPERDAY = 24L;
40 constexpr GIntBig SECSPERHOUR = SECSPERMIN * MINSPERHOUR;
41 constexpr GIntBig SECSPERDAY = SECSPERHOUR * HOURSPERDAY;
42 constexpr GIntBig DAYSPERWEEK = 7;
43 constexpr GIntBig MONSPERYEAR = 12;
44
45 constexpr GIntBig EPOCH_YEAR = 1970;
46 constexpr GIntBig EPOCH_WDAY = 4;
47 constexpr GIntBig TM_YEAR_BASE = 1900;
48 constexpr GIntBig DAYSPERNYEAR = 365;
49 constexpr GIntBig DAYSPERLYEAR = 366;
50
isleap(GIntBig y)51 static bool isleap( GIntBig y ) {
52 return
53 ((y % 4) == 0 &&
54 (y % 100) != 0) ||
55 (y % 400) == 0;
56 }
57
LEAPS_THROUGH_END_OF(GIntBig y)58 static GIntBig LEAPS_THROUGH_END_OF( GIntBig y )
59 {
60 return y / 4 - y / 100 + y / 400;
61 }
62
63 static const int mon_lengths[2][MONSPERYEAR] = {
64 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
65 {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
66 } ;
67
68 static const int year_lengths[2] = { DAYSPERNYEAR, DAYSPERLYEAR };
69
70 /************************************************************************/
71 /* CPLUnixTimeToYMDHMS() */
72 /************************************************************************/
73
74 /** Converts a time value since the Epoch (aka "unix" time) to a broken-down
75 * UTC time.
76 *
77 * This function is similar to gmtime_r().
78 * This function will always set tm_isdst to 0.
79 *
80 * @param unixTime number of seconds since the Epoch.
81 * @param pRet address of the return structure.
82 *
83 * @return the structure pointed by pRet filled with a broken-down UTC time.
84 */
85
86 static
myCPLUnixTimeToYMDHMS(GIntBig unixTime,struct tm * pRet)87 struct tm * myCPLUnixTimeToYMDHMS(GIntBig unixTime, struct tm* pRet)
88 {
89 GIntBig days = unixTime / SECSPERDAY;
90 GIntBig rem = unixTime % SECSPERDAY;
91
92 while (rem < 0) {
93 rem += SECSPERDAY;
94 --days;
95 }
96
97 pRet->tm_hour = static_cast<int>(rem / SECSPERHOUR);
98 rem = rem % SECSPERHOUR;
99 pRet->tm_min = static_cast<int>(rem / SECSPERMIN);
100 /*
101 ** A positive leap second requires a special
102 ** representation. This uses "... ??:59:60" et seq.
103 */
104 pRet->tm_sec = static_cast<int>(rem % SECSPERMIN);
105 pRet->tm_wday = static_cast<int>((EPOCH_WDAY + days) % DAYSPERWEEK);
106 if( pRet->tm_wday < 0 )
107 pRet->tm_wday += DAYSPERWEEK;
108 int yleap = 0;
109 GIntBig y = EPOCH_YEAR;
110 while( days < 0 ||
111 days >= static_cast<GIntBig>(year_lengths[yleap = isleap(y)]) )
112 {
113 GIntBig newy = y + days / DAYSPERNYEAR;
114 if( days < 0 )
115 --newy;
116 days -= (newy - y) * DAYSPERNYEAR +
117 LEAPS_THROUGH_END_OF(newy - 1) -
118 LEAPS_THROUGH_END_OF(y - 1);
119 y = newy;
120 }
121 pRet->tm_year = static_cast<int>(y - TM_YEAR_BASE);
122 pRet->tm_yday = static_cast<int>(days);
123 const int* ip = mon_lengths[yleap];
124 for( pRet->tm_mon = 0; days >= static_cast<GIntBig>(ip[pRet->tm_mon]);
125 ++(pRet->tm_mon) )
126 days = days - static_cast<GIntBig>(ip[pRet->tm_mon]);
127
128 pRet->tm_mday = static_cast<int>(days + 1);
129 pRet->tm_isdst = 0;
130
131 return pRet;
132 }
133
WriteEscaped(const char * pszStr,VSILFILE * fp)134 static void WriteEscaped(const char* pszStr, VSILFILE* fp)
135 {
136 GByte ch;
137 while( (ch = *(pszStr ++)) != 0 )
138 {
139 if( ch == '<' )
140 {
141 /* printf("<"); */
142 VSIFPrintfL(fp, "<");
143 }
144 else if( ch == '>' )
145 {
146 /* printf(">"); */
147 VSIFPrintfL(fp, ">");
148 }
149 else if( ch == '&' )
150 {
151 /* printf("&"); */
152 VSIFPrintfL(fp, "&");
153 }
154 else if( ch == '"' )
155 {
156 /* printf("""); */
157 VSIFPrintfL(fp, """);
158 }
159 else if( ch == '\'' )
160 {
161 VSIFPrintfL(fp, "'");
162 }
163 else if( ch < 0x20 && ch != 0x9 && ch != 0xA && ch != 0xD )
164 {
165 /* These control characters are unrepresentable in XML format, */
166 /* so we just drop them. #4117 */
167 }
168 else
169 {
170 VSIFWriteL(&ch, 1, 1, fp);
171 }
172 }
173 }
174
175 #define WRITE_STR(str) VSIFWriteL(str, 1, strlen(str), fp)
176 #define WRITE_ESCAPED(str) WriteEscaped(str, fp)
177
178 static
myNotifyNodesFunc(unsigned int nNodes,OSMNode * pasNodes,OSMContext *,void * user_data)179 void myNotifyNodesFunc( unsigned int nNodes, OSMNode* pasNodes,
180 OSMContext* /* psOSMContext */, void* user_data )
181 {
182 VSILFILE* fp = static_cast<VSILFILE *>(user_data);
183
184 for( int k = 0; k < static_cast<int>(nNodes); k++ )
185 {
186 const OSMNode* psNode = &pasNodes[k];
187
188 WRITE_STR(" <node id=\"");
189 VSIFPrintfL(fp, "%d", static_cast<int>(psNode->nID));
190 WRITE_STR("\" lat=\"");
191 VSIFPrintfL(fp, "%.7f", psNode->dfLat);
192 WRITE_STR("\" lon=\"");
193 VSIFPrintfL(fp, "%.7f", psNode->dfLon);
194 WRITE_STR("\" version=\"");
195 VSIFPrintfL(fp, "%d", psNode->sInfo.nVersion);
196 WRITE_STR("\" changeset=\"");
197 VSIFPrintfL(fp, "%d", static_cast<int>(psNode->sInfo.nChangeset));
198 if (psNode->sInfo.nUID >= 0)
199 {
200 WRITE_STR("\" user=\"");
201 WRITE_ESCAPED(psNode->sInfo.pszUserSID);
202 WRITE_STR("\" uid=\"");
203 VSIFPrintfL(fp, "%d", psNode->sInfo.nUID);
204 }
205
206 if( !(psNode->sInfo.bTimeStampIsStr) )
207 {
208 WRITE_STR("\" timestamp=\"");
209 struct tm mytm;
210 myCPLUnixTimeToYMDHMS(psNode->sInfo.ts.nTimeStamp, &mytm);
211 VSIFPrintfL(fp, "%04d-%02d-%02dT%02d:%02d:%02dZ",
212 1900 + mytm.tm_year, mytm.tm_mon + 1, mytm.tm_mday,
213 mytm.tm_hour, mytm.tm_min, mytm.tm_sec);
214 }
215 else if( psNode->sInfo.ts.pszTimeStamp != NULL &&
216 psNode->sInfo.ts.pszTimeStamp[0] != '\0' )
217 {
218 WRITE_STR("\" timestamp=\"");
219 WRITE_STR(psNode->sInfo.ts.pszTimeStamp);
220 }
221
222 if( psNode->nTags )
223 {
224 WRITE_STR("\">\n");
225 OSMTag *pasTags = psNode->pasTags;
226 for( unsigned int l = 0; l < psNode->nTags; l++ )
227 {
228 WRITE_STR(" <tag k=\"");
229 WRITE_ESCAPED(pasTags[l].pszK);
230 WRITE_STR("\" v=\"");
231 WRITE_ESCAPED(pasTags[l].pszV);
232 WRITE_STR("\" />\n");
233 }
234 WRITE_STR(" </node>\n");
235 }
236 else
237 {
238 WRITE_STR("\"/>\n");
239 }
240 }
241 }
242
243 static
myNotifyWayFunc(OSMWay * psWay,OSMContext *,void * user_data)244 void myNotifyWayFunc( OSMWay* psWay, OSMContext* /* psOSMContext */,
245 void* user_data )
246 {
247 VSILFILE* fp = static_cast<VSILFILE *>(user_data);
248
249 WRITE_STR(" <way id=\"");
250 VSIFPrintfL(fp, "%d", static_cast<int>(psWay->nID));
251 WRITE_STR("\" version=\"");
252 VSIFPrintfL(fp, "%d", psWay->sInfo.nVersion);
253 WRITE_STR("\" changeset=\"");
254 VSIFPrintfL(fp, "%d", static_cast<int>(psWay->sInfo.nChangeset));
255 if (psWay->sInfo.nUID >= 0)
256 {
257 WRITE_STR("\" uid=\"");
258 VSIFPrintfL(fp, "%d", psWay->sInfo.nUID);
259 WRITE_STR("\" user=\"");
260 WRITE_ESCAPED(psWay->sInfo.pszUserSID);
261 }
262
263 if( !(psWay->sInfo.bTimeStampIsStr) )
264 {
265 WRITE_STR("\" timestamp=\"");
266 struct tm mytm;
267 myCPLUnixTimeToYMDHMS(psWay->sInfo.ts.nTimeStamp, &mytm);
268 VSIFPrintfL(fp, "%04d-%02d-%02dT%02d:%02d:%02dZ",
269 1900 + mytm.tm_year, mytm.tm_mon + 1, mytm.tm_mday,
270 mytm.tm_hour, mytm.tm_min, mytm.tm_sec);
271 }
272 else if( psWay->sInfo.ts.pszTimeStamp != NULL &&
273 psWay->sInfo.ts.pszTimeStamp[0] != '\0' )
274 {
275 WRITE_STR("\" timestamp=\"");
276 WRITE_STR(psWay->sInfo.ts.pszTimeStamp);
277 }
278
279 WRITE_STR("\">\n");
280
281 for( unsigned int l = 0; l < psWay->nRefs; l++ )
282 VSIFPrintfL(fp, " <nd ref=\"%d\"/>\n",
283 static_cast<int>(psWay->panNodeRefs[l]));
284
285 for( unsigned int l = 0; l < psWay->nTags; l++ )
286 {
287 WRITE_STR(" <tag k=\"");
288 WRITE_ESCAPED(psWay->pasTags[l].pszK);
289 WRITE_STR("\" v=\"");
290 WRITE_ESCAPED(psWay->pasTags[l].pszV);
291 WRITE_STR("\" />\n");
292 }
293 VSIFPrintfL(fp, " </way>\n");
294 }
295
296 static
myNotifyRelationFunc(OSMRelation * psRelation,OSMContext *,void * user_data)297 void myNotifyRelationFunc( OSMRelation* psRelation,
298 OSMContext* /* psOSMContext */, void* user_data )
299 {
300 VSILFILE* fp = static_cast<VSILFILE *>(user_data);
301
302
303 WRITE_STR(" <relation id=\"");
304 VSIFPrintfL(fp, "%d", static_cast<int>(psRelation->nID));
305 WRITE_STR("\" version=\"");
306 VSIFPrintfL(fp, "%d", psRelation->sInfo.nVersion);
307 WRITE_STR("\" changeset=\"");
308 VSIFPrintfL(fp, "%d", static_cast<int>(psRelation->sInfo.nChangeset));
309 if( psRelation->sInfo.nUID >= 0 )
310 {
311 WRITE_STR("\" uid=\"");
312 VSIFPrintfL(fp, "%d", psRelation->sInfo.nUID);
313 WRITE_STR("\" user=\"");
314 WRITE_ESCAPED(psRelation->sInfo.pszUserSID);
315 }
316
317 if( !(psRelation->sInfo.bTimeStampIsStr) )
318 {
319 struct tm mytm;
320 myCPLUnixTimeToYMDHMS(psRelation->sInfo.ts.nTimeStamp, &mytm);
321 WRITE_STR("\" timestamp=\"");
322 VSIFPrintfL(fp, "%04d-%02d-%02dT%02d:%02d:%02dZ",
323 1900 + mytm.tm_year, mytm.tm_mon + 1, mytm.tm_mday,
324 mytm.tm_hour, mytm.tm_min, mytm.tm_sec);
325 }
326 else if( psRelation->sInfo.ts.pszTimeStamp != NULL &&
327 psRelation->sInfo.ts.pszTimeStamp[0] != '\0' )
328 {
329 WRITE_STR("\" timestamp=\"");
330 WRITE_STR(psRelation->sInfo.ts.pszTimeStamp);
331 }
332
333 WRITE_STR("\">\n");
334
335 const OSMTag* pasTags = psRelation->pasTags;
336 const OSMMember* pasMembers = psRelation->pasMembers;
337
338 for( unsigned int l = 0; l < psRelation->nMembers; l++ )
339 {
340 WRITE_STR(" <member type=\"");
341 VSIFPrintfL(fp, "%s",
342 pasMembers[l].eType == MEMBER_NODE
343 ? "node"
344 : pasMembers[l].eType == MEMBER_WAY ? "way": "relation");
345 WRITE_STR("\" ref=\"");
346 VSIFPrintfL(fp, "%d", static_cast<int>(pasMembers[l].nID));
347 WRITE_STR("\" role=\"");
348 WRITE_ESCAPED(pasMembers[l].pszRole);
349 WRITE_STR("\"/>\n");
350 }
351
352 for( unsigned int l = 0; l < psRelation->nTags; l++ )
353 {
354 WRITE_STR(" <tag k=\"");
355 WRITE_ESCAPED(pasTags[l].pszK);
356 WRITE_STR("\" v=\"");
357 WRITE_ESCAPED(pasTags[l].pszV);
358 WRITE_STR("\" />\n");
359 }
360 VSIFPrintfL(fp, " </relation>\n");
361 }
362
363 static
myNotifyBoundsFunc(double dfXMin,double dfYMin,double dfXMax,double dfYMax,OSMContext *,void * user_data)364 void myNotifyBoundsFunc( double dfXMin, double dfYMin,
365 double dfXMax, double dfYMax,
366 OSMContext* /* psOSMContext */, void* user_data )
367 {
368 VSILFILE* fp = static_cast<VSILFILE *>(user_data);
369 VSIFPrintfL(fp, " <bounds minlat=\"%.7f\" minlon=\"%.7f\""
370 " maxlat=\"%.7f\" maxlon=\"%.7f\"/>\n",
371 dfYMin, dfXMin, dfYMax, dfXMax);
372 }
373
main(int argc,char * argv[])374 int main( int argc, char* argv[] )
375 {
376 if( argc != 3 )
377 {
378 fprintf(stderr, "Usage: osm2osm input.pbf output.osm\n"); /*ok*/
379 exit(1);
380 }
381
382 const char *pszSrcFilename = argv[1];
383 const char *pszDstFilename = argv[2];
384
385 VSILFILE* fp = VSIFOpenL(pszDstFilename, "wt");
386 if( fp == NULL )
387 {
388 fprintf(stderr, "Cannot create %s.\n", pszDstFilename); /*ok*/
389 exit(1);
390 }
391
392 OSMContext* psContext = OSM_Open(pszSrcFilename,
393 myNotifyNodesFunc,
394 myNotifyWayFunc,
395 myNotifyRelationFunc,
396 myNotifyBoundsFunc,
397 fp);
398 if( psContext == NULL )
399 {
400 fprintf(stderr, "Cannot process %s.\n", pszSrcFilename); /*ok*/
401 exit(1);
402 }
403
404 VSIFPrintfL(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
405 VSIFPrintfL(fp, "<osm version=\"0.6\" generator=\"pbttoosm\">\n");
406
407 while( OSM_ProcessBlock(psContext) == OSM_OK );
408
409 VSIFPrintfL(fp, "</osm>\n");
410
411 OSM_Close(psContext);
412
413 VSIFCloseL(fp);
414
415 return 0;
416 }
417