1 : /******************************************************************************
2 : * $Id: ogrgpxlayer.cpp 20996 2010-10-28 18:38:15Z rouault $
3 : *
4 : * Project: GPX Translator
5 : * Purpose: Implements OGRGPXLayer class.
6 : * Author: Even Rouault, even dot rouault at mines dash paris dot org
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2007, Even Rouault
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 "ogr_gpx.h"
31 : #include "cpl_conv.h"
32 : #include "cpl_string.h"
33 : #include "cpl_minixml.h"
34 : #include "ogr_p.h"
35 :
36 : CPL_CVSID("$Id: ogrgpxlayer.cpp 20996 2010-10-28 18:38:15Z rouault $");
37 :
38 : #define FLD_TRACK_FID 0
39 : #define FLD_TRACK_SEG_ID 1
40 : #define FLD_TRACK_PT_ID 2
41 : #define FLD_TRACK_NAME 3
42 :
43 : #define FLD_ROUTE_FID 0
44 : #define FLD_ROUTE_PT_ID 1
45 : #define FLD_ROUTE_NAME 2
46 :
47 : /************************************************************************/
48 : /* OGRGPXLayer() */
49 : /* */
50 : /* Note that the OGRGPXLayer assumes ownership of the passed */
51 : /* file pointer. */
52 : /************************************************************************/
53 :
54 37 : OGRGPXLayer::OGRGPXLayer( const char* pszFilename,
55 : const char* pszLayerName,
56 : GPXGeometryType gpxGeomType,
57 : OGRGPXDataSource* poDS,
58 37 : int bWriteMode)
59 :
60 : {
61 37 : const char* gpxVersion = poDS->GetVersion();
62 :
63 : int i;
64 :
65 37 : eof = FALSE;
66 37 : nNextFID = 0;
67 :
68 37 : this->poDS = poDS;
69 37 : this->bWriteMode = bWriteMode;
70 37 : this->gpxGeomType = gpxGeomType;
71 :
72 37 : pszElementToScan = pszLayerName;
73 :
74 37 : nMaxLinks = atoi(CPLGetConfigOption("GPX_N_MAX_LINKS", "2"));
75 37 : if (nMaxLinks < 0)
76 0 : nMaxLinks = 2;
77 37 : if (nMaxLinks > 100)
78 0 : nMaxLinks = 100;
79 :
80 37 : nFeatures = 0;
81 :
82 37 : bEleAs25D = CSLTestBoolean(CPLGetConfigOption("GPX_ELE_AS_25D", "NO"));
83 :
84 37 : int bShortNames = CSLTestBoolean(CPLGetConfigOption("GPX_SHORT_NAMES", "NO"));
85 :
86 37 : poFeatureDefn = new OGRFeatureDefn( pszLayerName );
87 37 : poFeatureDefn->Reference();
88 :
89 37 : if (gpxGeomType == GPX_TRACK_POINT)
90 : {
91 : /* Don't move this code. This fields must be number 0, 1 and 2 */
92 : /* in order to make OGRGPXLayer::startElementCbk work */
93 8 : OGRFieldDefn oFieldTrackFID("track_fid", OFTInteger );
94 8 : poFeatureDefn->AddFieldDefn( &oFieldTrackFID );
95 :
96 8 : OGRFieldDefn oFieldTrackSegID((bShortNames) ? "trksegid" : "track_seg_id", OFTInteger );
97 8 : poFeatureDefn->AddFieldDefn( &oFieldTrackSegID );
98 :
99 8 : OGRFieldDefn oFieldTrackSegPointID((bShortNames) ? "trksegptid" : "track_seg_point_id", OFTInteger );
100 8 : poFeatureDefn->AddFieldDefn( &oFieldTrackSegPointID );
101 :
102 8 : if (bWriteMode)
103 : {
104 2 : OGRFieldDefn oFieldName("track_name", OFTString );
105 2 : poFeatureDefn->AddFieldDefn( &oFieldName );
106 8 : }
107 : }
108 29 : else if (gpxGeomType == GPX_ROUTE_POINT)
109 : {
110 : /* Don't move this code. See above */
111 7 : OGRFieldDefn oFieldRouteFID("route_fid", OFTInteger );
112 7 : poFeatureDefn->AddFieldDefn( &oFieldRouteFID );
113 :
114 7 : OGRFieldDefn oFieldRoutePointID((bShortNames) ? "rteptid" : "route_point_id", OFTInteger );
115 7 : poFeatureDefn->AddFieldDefn( &oFieldRoutePointID );
116 :
117 7 : if (bWriteMode)
118 : {
119 1 : OGRFieldDefn oFieldName("route_name", OFTString );
120 1 : poFeatureDefn->AddFieldDefn( &oFieldName );
121 7 : }
122 : }
123 :
124 37 : iFirstGPXField = poFeatureDefn->GetFieldCount();
125 :
126 60 : if (gpxGeomType == GPX_WPT ||
127 : gpxGeomType == GPX_TRACK_POINT ||
128 : gpxGeomType == GPX_ROUTE_POINT)
129 : {
130 23 : poFeatureDefn->SetGeomType((bEleAs25D) ? wkbPoint25D : wkbPoint);
131 : /* Position info */
132 :
133 23 : OGRFieldDefn oFieldEle("ele", OFTReal );
134 23 : poFeatureDefn->AddFieldDefn( &oFieldEle );
135 :
136 23 : OGRFieldDefn oFieldTime("time", OFTDateTime );
137 23 : poFeatureDefn->AddFieldDefn( &oFieldTime );
138 :
139 23 : if (gpxGeomType == GPX_TRACK_POINT &&
140 : gpxVersion && strcmp(gpxVersion, "1.0") == 0)
141 : {
142 0 : OGRFieldDefn oFieldCourse("course", OFTReal );
143 0 : poFeatureDefn->AddFieldDefn( &oFieldCourse );
144 :
145 0 : OGRFieldDefn oFieldSpeed("speed", OFTReal );
146 0 : poFeatureDefn->AddFieldDefn( &oFieldSpeed );
147 : }
148 :
149 23 : OGRFieldDefn oFieldMagVar("magvar", OFTReal );
150 23 : poFeatureDefn->AddFieldDefn( &oFieldMagVar );
151 :
152 23 : OGRFieldDefn oFieldGeoidHeight("geoidheight", OFTReal );
153 23 : poFeatureDefn->AddFieldDefn( &oFieldGeoidHeight );
154 :
155 : /* Description info */
156 :
157 23 : OGRFieldDefn oFieldName("name", OFTString );
158 23 : poFeatureDefn->AddFieldDefn( &oFieldName );
159 :
160 23 : OGRFieldDefn oFieldCmt("cmt", OFTString );
161 23 : poFeatureDefn->AddFieldDefn( &oFieldCmt );
162 :
163 23 : OGRFieldDefn oFieldDesc("desc", OFTString );
164 23 : poFeatureDefn->AddFieldDefn( &oFieldDesc );
165 :
166 23 : OGRFieldDefn oFieldSrc("src", OFTString );
167 23 : poFeatureDefn->AddFieldDefn( &oFieldSrc );
168 :
169 23 : if (gpxVersion && strcmp(gpxVersion, "1.0") == 0)
170 : {
171 0 : OGRFieldDefn oFieldUrl("url", OFTString );
172 0 : poFeatureDefn->AddFieldDefn( &oFieldUrl );
173 :
174 0 : OGRFieldDefn oFieldUrlName("urlname", OFTString );
175 0 : poFeatureDefn->AddFieldDefn( &oFieldUrlName );
176 : }
177 : else
178 : {
179 69 : for(i=1;i<=nMaxLinks;i++)
180 : {
181 : char szFieldName[32];
182 46 : sprintf(szFieldName, "link%d_href", i);
183 46 : OGRFieldDefn oFieldLinkHref( szFieldName, OFTString );
184 46 : poFeatureDefn->AddFieldDefn( &oFieldLinkHref );
185 :
186 46 : sprintf(szFieldName, "link%d_text", i);
187 46 : OGRFieldDefn oFieldLinkText( szFieldName, OFTString );
188 46 : poFeatureDefn->AddFieldDefn( &oFieldLinkText );
189 :
190 46 : sprintf(szFieldName, "link%d_type", i);
191 46 : OGRFieldDefn oFieldLinkType( szFieldName, OFTString );
192 46 : poFeatureDefn->AddFieldDefn( &oFieldLinkType );
193 : }
194 : }
195 :
196 23 : OGRFieldDefn oFieldSym("sym", OFTString );
197 23 : poFeatureDefn->AddFieldDefn( &oFieldSym );
198 :
199 23 : OGRFieldDefn oFieldType("type", OFTString );
200 23 : poFeatureDefn->AddFieldDefn( &oFieldType );
201 :
202 : /* Accuracy info */
203 :
204 23 : OGRFieldDefn oFieldFix("fix", OFTString );
205 23 : poFeatureDefn->AddFieldDefn( &oFieldFix );
206 :
207 23 : OGRFieldDefn oFieldSat("sat", OFTInteger );
208 23 : poFeatureDefn->AddFieldDefn( &oFieldSat );
209 :
210 23 : OGRFieldDefn oFieldHdop("hdop", OFTReal );
211 23 : poFeatureDefn->AddFieldDefn( &oFieldHdop );
212 :
213 23 : OGRFieldDefn oFieldVdop("vdop", OFTReal );
214 23 : poFeatureDefn->AddFieldDefn( &oFieldVdop );
215 :
216 23 : OGRFieldDefn oFieldPdop("pdop", OFTReal );
217 23 : poFeatureDefn->AddFieldDefn( &oFieldPdop );
218 :
219 23 : OGRFieldDefn oFieldAgeofgpsdata("ageofdgpsdata", OFTReal );
220 23 : poFeatureDefn->AddFieldDefn( &oFieldAgeofgpsdata );
221 :
222 23 : OGRFieldDefn oFieldDgpsid("dgpsid", OFTInteger );
223 23 : poFeatureDefn->AddFieldDefn( &oFieldDgpsid );
224 : }
225 : else
226 : {
227 14 : if (gpxGeomType == GPX_TRACK)
228 7 : poFeatureDefn->SetGeomType((bEleAs25D) ? wkbMultiLineString25D : wkbMultiLineString);
229 : else
230 7 : poFeatureDefn->SetGeomType((bEleAs25D) ? wkbLineString25D : wkbLineString);
231 :
232 14 : OGRFieldDefn oFieldName("name", OFTString );
233 14 : poFeatureDefn->AddFieldDefn( &oFieldName );
234 :
235 14 : OGRFieldDefn oFieldCmt("cmt", OFTString );
236 14 : poFeatureDefn->AddFieldDefn( &oFieldCmt );
237 :
238 14 : OGRFieldDefn oFieldDesc("desc", OFTString );
239 14 : poFeatureDefn->AddFieldDefn( &oFieldDesc );
240 :
241 14 : OGRFieldDefn oFieldSrc("src", OFTString );
242 14 : poFeatureDefn->AddFieldDefn( &oFieldSrc );
243 :
244 42 : for(i=1;i<=nMaxLinks;i++)
245 : {
246 : char szFieldName[32];
247 28 : sprintf(szFieldName, "link%d_href", i);
248 28 : OGRFieldDefn oFieldLinkHref( szFieldName, OFTString );
249 28 : poFeatureDefn->AddFieldDefn( &oFieldLinkHref );
250 :
251 28 : sprintf(szFieldName, "link%d_text", i);
252 28 : OGRFieldDefn oFieldLinkText( szFieldName, OFTString );
253 28 : poFeatureDefn->AddFieldDefn( &oFieldLinkText );
254 :
255 28 : sprintf(szFieldName, "link%d_type", i);
256 28 : OGRFieldDefn oFieldLinkType( szFieldName, OFTString );
257 28 : poFeatureDefn->AddFieldDefn( &oFieldLinkType );
258 : }
259 :
260 14 : OGRFieldDefn oFieldNumber("number", OFTInteger );
261 14 : poFeatureDefn->AddFieldDefn( &oFieldNumber );
262 :
263 14 : OGRFieldDefn oFieldType("type", OFTString );
264 14 : poFeatureDefn->AddFieldDefn( &oFieldType );
265 : }
266 :
267 : /* Number of 'standard' GPX attributes */
268 37 : nGPXFields = poFeatureDefn->GetFieldCount();
269 :
270 37 : ppoFeatureTab = NULL;
271 37 : nFeatureTabIndex = 0;
272 37 : nFeatureTabLength = 0;
273 37 : pszSubElementName = NULL;
274 37 : pszSubElementValue = NULL;
275 37 : nSubElementValueLen = 0;
276 37 : bStopParsing = FALSE;
277 :
278 : poSRS = new OGRSpatialReference("GEOGCS[\"WGS 84\", "
279 : " DATUM[\"WGS_1984\","
280 : " SPHEROID[\"WGS 84\",6378137,298.257223563,"
281 : " AUTHORITY[\"EPSG\",\"7030\"]],"
282 : " AUTHORITY[\"EPSG\",\"6326\"]],"
283 : " PRIMEM[\"Greenwich\",0,"
284 : " AUTHORITY[\"EPSG\",\"8901\"]],"
285 : " UNIT[\"degree\",0.01745329251994328,"
286 : " AUTHORITY[\"EPSG\",\"9122\"]],"
287 37 : " AUTHORITY[\"EPSG\",\"4326\"]]");
288 :
289 37 : poFeature = NULL;
290 :
291 : #ifdef HAVE_EXPAT
292 37 : oParser = NULL;
293 : #endif
294 :
295 37 : if (bWriteMode == FALSE)
296 : {
297 30 : fpGPX = VSIFOpenL( pszFilename, "r" );
298 30 : if( fpGPX == NULL )
299 : {
300 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s", pszFilename);
301 0 : return;
302 : }
303 :
304 30 : if (poDS->GetUseExtensions() ||
305 : CSLTestBoolean(CPLGetConfigOption("GPX_USE_EXTENSIONS", "FALSE")))
306 : {
307 15 : LoadExtensionsSchema();
308 : }
309 : }
310 : else
311 7 : fpGPX = NULL;
312 :
313 37 : ResetReading();
314 0 : }
315 :
316 : /************************************************************************/
317 : /* ~OGRGPXLayer() */
318 : /************************************************************************/
319 :
320 37 : OGRGPXLayer::~OGRGPXLayer()
321 :
322 : {
323 : #ifdef HAVE_EXPAT
324 37 : if (oParser)
325 30 : XML_ParserFree(oParser);
326 : #endif
327 37 : poFeatureDefn->Release();
328 :
329 37 : if( poSRS != NULL )
330 37 : poSRS->Release();
331 :
332 37 : CPLFree(pszSubElementName);
333 37 : CPLFree(pszSubElementValue);
334 :
335 : int i;
336 42 : for(i=nFeatureTabIndex;i<nFeatureTabLength;i++)
337 5 : delete ppoFeatureTab[i];
338 37 : CPLFree(ppoFeatureTab);
339 :
340 37 : if (poFeature)
341 0 : delete poFeature;
342 :
343 37 : if (fpGPX)
344 30 : VSIFCloseL( fpGPX );
345 37 : }
346 :
347 : #ifdef HAVE_EXPAT
348 :
349 1554 : static void XMLCALL startElementCbk(void *pUserData, const char *pszName, const char **ppszAttr)
350 : {
351 1554 : ((OGRGPXLayer*)pUserData)->startElementCbk(pszName, ppszAttr);
352 1554 : }
353 :
354 1554 : static void XMLCALL endElementCbk(void *pUserData, const char *pszName)
355 : {
356 1554 : ((OGRGPXLayer*)pUserData)->endElementCbk(pszName);
357 1554 : }
358 :
359 4233 : static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen)
360 : {
361 4233 : ((OGRGPXLayer*)pUserData)->dataHandlerCbk(data, nLen);
362 4233 : }
363 :
364 : #endif
365 :
366 : /************************************************************************/
367 : /* ResetReading() */
368 : /************************************************************************/
369 :
370 86 : void OGRGPXLayer::ResetReading()
371 :
372 : {
373 86 : eof = FALSE;
374 86 : nNextFID = 0;
375 86 : if (fpGPX)
376 : {
377 79 : VSIFSeekL( fpGPX, 0, SEEK_SET );
378 : #ifdef HAVE_EXPAT
379 79 : if (oParser)
380 49 : XML_ParserFree(oParser);
381 :
382 79 : oParser = OGRCreateExpatXMLParser();
383 79 : XML_SetElementHandler(oParser, ::startElementCbk, ::endElementCbk);
384 79 : XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk);
385 79 : XML_SetUserData(oParser, this);
386 : #endif
387 : }
388 86 : hasFoundLat = FALSE;
389 86 : hasFoundLon = FALSE;
390 86 : inInterestingElement = FALSE;
391 86 : CPLFree(pszSubElementName);
392 86 : pszSubElementName = NULL;
393 86 : CPLFree(pszSubElementValue);
394 86 : pszSubElementValue = NULL;
395 86 : nSubElementValueLen = 0;
396 :
397 : int i;
398 86 : for(i=nFeatureTabIndex;i<nFeatureTabLength;i++)
399 0 : delete ppoFeatureTab[i];
400 86 : CPLFree(ppoFeatureTab);
401 86 : nFeatureTabIndex = 0;
402 86 : nFeatureTabLength = 0;
403 86 : ppoFeatureTab = NULL;
404 86 : if (poFeature)
405 0 : delete poFeature;
406 86 : poFeature = NULL;
407 86 : multiLineString = NULL;
408 86 : lineString = NULL;
409 :
410 86 : depthLevel = 0;
411 86 : interestingDepthLevel = 0;
412 :
413 86 : trkFID = trkSegId = trkSegPtId = 0;
414 86 : rteFID = rtePtId = 0;
415 86 : }
416 :
417 : #ifdef HAVE_EXPAT
418 :
419 : /************************************************************************/
420 : /* startElementCbk() */
421 : /************************************************************************/
422 :
423 : /** Replace ':' from XML NS element name by '_' more OGR friendly */
424 48 : static char* OGRGPX_GetOGRCompatibleTagName(const char* pszName)
425 : {
426 48 : char* pszModName = CPLStrdup(pszName);
427 : int i;
428 704 : for(i=0;pszModName[i] != 0;i++)
429 : {
430 656 : if (pszModName[i] == ':')
431 48 : pszModName[i] = '_';
432 : }
433 48 : return pszModName;
434 : }
435 :
436 0 : void OGRGPXLayer::AddStrToSubElementValue(const char* pszStr)
437 : {
438 0 : int len = strlen(pszStr);
439 : char* pszNewSubElementValue = (char*)
440 0 : VSIRealloc(pszSubElementValue, nSubElementValueLen + len + 1);
441 0 : if (pszNewSubElementValue == NULL)
442 : {
443 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
444 0 : XML_StopParser(oParser, XML_FALSE);
445 0 : bStopParsing = TRUE;
446 0 : return;
447 : }
448 0 : pszSubElementValue = pszNewSubElementValue;
449 0 : memcpy(pszSubElementValue + nSubElementValueLen, pszStr, len);
450 0 : nSubElementValueLen += len;
451 : }
452 :
453 1554 : void OGRGPXLayer::startElementCbk(const char *pszName, const char **ppszAttr)
454 : {
455 : int i;
456 :
457 1554 : if (bStopParsing) return;
458 :
459 1554 : nWithoutEventCounter = 0;
460 :
461 1620 : if ((gpxGeomType == GPX_WPT && strcmp(pszName, "wpt") == 0) ||
462 : (gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rtept") == 0) ||
463 : (gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkpt") == 0))
464 : {
465 66 : interestingDepthLevel = depthLevel;
466 :
467 66 : if (poFeature)
468 0 : delete poFeature;
469 :
470 66 : poFeature = new OGRFeature( poFeatureDefn );
471 66 : inInterestingElement = TRUE;
472 66 : hasFoundLat = FALSE;
473 66 : hasFoundLon = FALSE;
474 66 : inExtensions = FALSE;
475 66 : inLink = FALSE;
476 66 : iCountLink = 0;
477 :
478 198 : for (i = 0; ppszAttr[i]; i += 2)
479 : {
480 132 : if (strcmp(ppszAttr[i], "lat") == 0)
481 : {
482 66 : hasFoundLat = TRUE;
483 66 : latVal = CPLAtof(ppszAttr[i + 1]);
484 : }
485 66 : else if (strcmp(ppszAttr[i], "lon") == 0)
486 : {
487 66 : hasFoundLon = TRUE;
488 66 : lonVal = CPLAtof(ppszAttr[i + 1]);
489 : }
490 : }
491 :
492 66 : if (hasFoundLat && hasFoundLon)
493 : {
494 66 : poFeature->SetFID( nNextFID++ );
495 66 : poFeature->SetGeometryDirectly( new OGRPoint( lonVal, latVal ) );
496 :
497 66 : if (gpxGeomType == GPX_ROUTE_POINT)
498 : {
499 6 : rtePtId++;
500 6 : poFeature->SetField( FLD_ROUTE_FID, rteFID-1);
501 6 : poFeature->SetField( FLD_ROUTE_PT_ID, rtePtId-1);
502 : }
503 60 : else if (gpxGeomType == GPX_TRACK_POINT)
504 : {
505 8 : trkSegPtId++;
506 :
507 8 : poFeature->SetField( FLD_TRACK_FID, trkFID-1);
508 8 : poFeature->SetField( FLD_TRACK_SEG_ID, trkSegId-1);
509 8 : poFeature->SetField( FLD_TRACK_PT_ID, trkSegPtId-1);
510 : }
511 : }
512 : }
513 1499 : else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0)
514 : {
515 11 : interestingDepthLevel = depthLevel;
516 :
517 11 : if (poFeature)
518 0 : delete poFeature;
519 11 : inExtensions = FALSE;
520 11 : inLink = FALSE;
521 11 : iCountLink = 0;
522 11 : poFeature = new OGRFeature( poFeatureDefn );
523 11 : inInterestingElement = TRUE;
524 :
525 22 : multiLineString = new OGRMultiLineString ();
526 11 : lineString = NULL;
527 :
528 11 : poFeature->SetFID( nNextFID++ );
529 11 : poFeature->SetGeometryDirectly( multiLineString );
530 : }
531 1485 : else if (gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trk") == 0)
532 : {
533 8 : trkFID++;
534 8 : trkSegId = 0;
535 : }
536 1477 : else if (gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkseg") == 0)
537 : {
538 8 : trkSegId++;
539 8 : trkSegPtId = 0;
540 : }
541 1467 : else if (gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0)
542 : {
543 6 : interestingDepthLevel = depthLevel;
544 :
545 6 : if (poFeature)
546 0 : delete poFeature;
547 :
548 6 : poFeature = new OGRFeature( poFeatureDefn );
549 6 : inInterestingElement = TRUE;
550 6 : inExtensions = FALSE;
551 6 : inLink = FALSE;
552 6 : iCountLink = 0;
553 :
554 12 : lineString = new OGRLineString ();
555 6 : poFeature->SetFID( nNextFID++ );
556 6 : poFeature->SetGeometryDirectly( lineString );
557 : }
558 1459 : else if (gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rte") == 0)
559 : {
560 4 : rteFID++;
561 4 : rtePtId = 0;
562 : }
563 1451 : else if (inInterestingElement)
564 : {
565 430 : if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trkseg") == 0 &&
566 : depthLevel == interestingDepthLevel + 1)
567 : {
568 11 : if (multiLineString)
569 : {
570 11 : lineString = new OGRLineString ();
571 11 : multiLineString->addGeometryDirectly( lineString );
572 : }
573 : }
574 420 : else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trkpt") == 0 &&
575 : depthLevel == interestingDepthLevel + 2)
576 : {
577 12 : if (lineString)
578 : {
579 12 : hasFoundLat = FALSE;
580 12 : hasFoundLon = FALSE;
581 36 : for (i = 0; ppszAttr[i]; i += 2)
582 : {
583 24 : if (strcmp(ppszAttr[i], "lat") == 0)
584 : {
585 12 : hasFoundLat = TRUE;
586 12 : latVal = CPLAtof(ppszAttr[i + 1]);
587 : }
588 12 : else if (strcmp(ppszAttr[i], "lon") == 0)
589 : {
590 12 : hasFoundLon = TRUE;
591 12 : lonVal = CPLAtof(ppszAttr[i + 1]);
592 : }
593 : }
594 :
595 12 : if (hasFoundLat && hasFoundLon)
596 : {
597 12 : lineString->addPoint(lonVal, latVal);
598 : }
599 : }
600 : }
601 405 : else if (gpxGeomType == GPX_ROUTE && strcmp(pszName, "rtept") == 0 &&
602 : depthLevel == interestingDepthLevel + 1)
603 : {
604 9 : if (lineString)
605 : {
606 9 : hasFoundLat = FALSE;
607 9 : hasFoundLon = FALSE;
608 27 : for (i = 0; ppszAttr[i]; i += 2)
609 : {
610 18 : if (strcmp(ppszAttr[i], "lat") == 0)
611 : {
612 9 : hasFoundLat = TRUE;
613 9 : latVal = CPLAtof(ppszAttr[i + 1]);
614 : }
615 9 : else if (strcmp(ppszAttr[i], "lon") == 0)
616 : {
617 9 : hasFoundLon = TRUE;
618 9 : lonVal = CPLAtof(ppszAttr[i + 1]);
619 : }
620 : }
621 :
622 9 : if (hasFoundLat && hasFoundLon)
623 : {
624 9 : lineString->addPoint(lonVal, latVal);
625 : }
626 : }
627 : }
628 387 : else if (bEleAs25D &&
629 : strcmp(pszName, "ele") == 0 &&
630 : lineString != NULL &&
631 : ((gpxGeomType == GPX_ROUTE && depthLevel == interestingDepthLevel + 2) ||
632 : (gpxGeomType == GPX_TRACK && depthLevel == interestingDepthLevel + 3)))
633 : {
634 0 : CPLFree(pszSubElementName);
635 0 : pszSubElementName = CPLStrdup(pszName);
636 : }
637 404 : else if (depthLevel == interestingDepthLevel + 1 &&
638 : strcmp(pszName, "extensions") == 0)
639 : {
640 17 : if (poDS->GetUseExtensions())
641 : {
642 17 : inExtensions = TRUE;
643 : }
644 : }
645 614 : else if (depthLevel == interestingDepthLevel + 1 ||
646 : (inExtensions && depthLevel == interestingDepthLevel + 2) )
647 : {
648 244 : CPLFree(pszSubElementName);
649 244 : pszSubElementName = NULL;
650 244 : iCurrentField = -1;
651 :
652 244 : if (strcmp(pszName, "link") == 0)
653 : {
654 53 : iCountLink++;
655 53 : if (iCountLink <= nMaxLinks)
656 : {
657 42 : if (ppszAttr[0] && ppszAttr[1] &&
658 : strcmp(ppszAttr[0], "href") == 0)
659 : {
660 : char szFieldName[32];
661 42 : sprintf(szFieldName, "link%d_href", iCountLink);
662 42 : iCurrentField = poFeatureDefn->GetFieldIndex(szFieldName);
663 42 : poFeature->SetField( iCurrentField, ppszAttr[1]);
664 : }
665 : }
666 : else
667 : {
668 : static int once = 1;
669 11 : if (once)
670 : {
671 1 : once = 0;
672 : CPLError(CE_Warning, CPLE_AppDefined,
673 : "GPX driver only reads %d links per element. Others will be ignored. "
674 : "This can be changed with the GPX_N_MAX_LINKS environment variable",
675 1 : nMaxLinks);
676 : }
677 : }
678 53 : inLink = TRUE;
679 53 : iCurrentField = -1;
680 : }
681 : else
682 : {
683 1234 : for( int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ )
684 : {
685 : int bMatch;
686 1234 : if (iField >= nGPXFields)
687 : {
688 36 : char* pszCompatibleName = OGRGPX_GetOGRCompatibleTagName(pszName);
689 : bMatch = (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
690 36 : pszCompatibleName ) == 0);
691 36 : CPLFree(pszCompatibleName);
692 : }
693 : else
694 : bMatch = (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
695 1198 : pszName ) == 0);
696 :
697 1234 : if (bMatch)
698 : {
699 191 : iCurrentField = iField;
700 191 : pszSubElementName = CPLStrdup(pszName);
701 191 : break;
702 : }
703 : }
704 : }
705 : }
706 232 : else if (depthLevel == interestingDepthLevel + 2 && inLink)
707 : {
708 : char szFieldName[32];
709 106 : CPLFree(pszSubElementName);
710 106 : pszSubElementName = NULL;
711 106 : iCurrentField = -1;
712 106 : if (iCountLink <= nMaxLinks)
713 : {
714 84 : if (strcmp(pszName, "type") == 0)
715 : {
716 42 : sprintf(szFieldName, "link%d_type", iCountLink);
717 42 : iCurrentField = poFeatureDefn->GetFieldIndex(szFieldName);
718 42 : pszSubElementName = CPLStrdup(pszName);
719 : }
720 42 : else if (strcmp(pszName, "text") == 0)
721 : {
722 42 : sprintf(szFieldName, "link%d_text", iCountLink);
723 42 : iCurrentField = poFeatureDefn->GetFieldIndex(szFieldName);
724 42 : pszSubElementName = CPLStrdup(pszName);
725 : }
726 : }
727 : }
728 20 : else if (inExtensions && depthLevel > interestingDepthLevel + 2)
729 : {
730 : AddStrToSubElementValue(
731 0 : (ppszAttr[0] == NULL) ? CPLSPrintf("<%s>", pszName) :
732 0 : CPLSPrintf("<%s ", pszName));
733 : int i;
734 0 : for (i = 0; ppszAttr[i]; i += 2)
735 : {
736 : AddStrToSubElementValue(
737 0 : CPLSPrintf("%s=\"%s\" ", ppszAttr[i], ppszAttr[i + 1]));
738 : }
739 0 : if (ppszAttr[0] != NULL)
740 : {
741 0 : AddStrToSubElementValue(">");
742 : }
743 : }
744 : }
745 :
746 1554 : depthLevel++;
747 : }
748 :
749 : /************************************************************************/
750 : /* endElementCbk() */
751 : /************************************************************************/
752 :
753 1554 : void OGRGPXLayer::endElementCbk(const char *pszName)
754 : {
755 1554 : if (bStopParsing) return;
756 :
757 1554 : nWithoutEventCounter = 0;
758 :
759 1554 : depthLevel--;
760 :
761 1554 : if (inInterestingElement)
762 : {
763 568 : if ((gpxGeomType == GPX_WPT && strcmp(pszName, "wpt") == 0) ||
764 : (gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rtept") == 0) ||
765 : (gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkpt") == 0))
766 : {
767 66 : int bIsValid = (hasFoundLat && hasFoundLon);
768 66 : inInterestingElement = FALSE;
769 :
770 66 : if( bIsValid
771 : && (m_poFilterGeom == NULL
772 : || FilterGeometry( poFeature->GetGeometryRef() ) )
773 : && (m_poAttrQuery == NULL
774 : || m_poAttrQuery->Evaluate( poFeature )) )
775 : {
776 66 : if( poFeature->GetGeometryRef() != NULL )
777 : {
778 66 : poFeature->GetGeometryRef()->assignSpatialReference( poSRS );
779 :
780 66 : if (bEleAs25D)
781 : {
782 0 : for( int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ )
783 : {
784 0 : if (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), "ele" ) == 0)
785 : {
786 0 : if( poFeature->IsFieldSet( iField ) )
787 : {
788 0 : double val = poFeature->GetFieldAsDouble( iField);
789 0 : ((OGRPoint*)poFeature->GetGeometryRef())->setZ(val);
790 0 : poFeature->GetGeometryRef()->setCoordinateDimension(3);
791 : }
792 0 : break;
793 : }
794 : }
795 : }
796 : }
797 :
798 : ppoFeatureTab = (OGRFeature**)
799 : CPLRealloc(ppoFeatureTab,
800 66 : sizeof(OGRFeature*) * (nFeatureTabLength + 1));
801 66 : ppoFeatureTab[nFeatureTabLength] = poFeature;
802 66 : nFeatureTabLength++;
803 : }
804 : else
805 : {
806 0 : delete poFeature;
807 : }
808 66 : poFeature = NULL;
809 : }
810 447 : else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0)
811 : {
812 11 : inInterestingElement = FALSE;
813 11 : if( (m_poFilterGeom == NULL
814 : || FilterGeometry( poFeature->GetGeometryRef() ) )
815 : && (m_poAttrQuery == NULL
816 : || m_poAttrQuery->Evaluate( poFeature )) )
817 : {
818 11 : if( poFeature->GetGeometryRef() != NULL )
819 : {
820 11 : poFeature->GetGeometryRef()->assignSpatialReference( poSRS );
821 : }
822 :
823 : ppoFeatureTab = (OGRFeature**)
824 : CPLRealloc(ppoFeatureTab,
825 11 : sizeof(OGRFeature*) * (nFeatureTabLength + 1));
826 11 : ppoFeatureTab[nFeatureTabLength] = poFeature;
827 11 : nFeatureTabLength++;
828 : }
829 : else
830 : {
831 0 : delete poFeature;
832 : }
833 11 : poFeature = NULL;
834 11 : multiLineString = NULL;
835 11 : lineString = NULL;
836 : }
837 436 : else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trkseg") == 0 &&
838 : depthLevel == interestingDepthLevel + 1)
839 : {
840 11 : lineString = NULL;
841 : }
842 420 : else if (gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0)
843 : {
844 6 : inInterestingElement = FALSE;
845 6 : if( (m_poFilterGeom == NULL
846 : || FilterGeometry( poFeature->GetGeometryRef() ) )
847 : && (m_poAttrQuery == NULL
848 : || m_poAttrQuery->Evaluate( poFeature )) )
849 : {
850 6 : if( poFeature->GetGeometryRef() != NULL )
851 : {
852 6 : poFeature->GetGeometryRef()->assignSpatialReference( poSRS );
853 : }
854 :
855 : ppoFeatureTab = (OGRFeature**)
856 : CPLRealloc(ppoFeatureTab,
857 6 : sizeof(OGRFeature*) * (nFeatureTabLength + 1));
858 6 : ppoFeatureTab[nFeatureTabLength] = poFeature;
859 6 : nFeatureTabLength++;
860 : }
861 : else
862 : {
863 0 : delete poFeature;
864 : }
865 6 : poFeature = NULL;
866 6 : lineString = NULL;
867 : }
868 408 : else if (bEleAs25D &&
869 : strcmp(pszName, "ele") == 0 &&
870 : lineString != NULL &&
871 : ((gpxGeomType == GPX_ROUTE && depthLevel == interestingDepthLevel + 2) ||
872 : (gpxGeomType == GPX_TRACK && depthLevel == interestingDepthLevel + 3)))
873 : {
874 0 : poFeature->GetGeometryRef()->setCoordinateDimension(3);
875 :
876 0 : if (nSubElementValueLen)
877 : {
878 0 : pszSubElementValue[nSubElementValueLen] = 0;
879 :
880 0 : double val = CPLAtof(pszSubElementValue);
881 0 : int i = lineString->getNumPoints() - 1;
882 0 : if (i >= 0)
883 0 : lineString->setPoint(i, lineString->getX(i), lineString->getY(i), val);
884 : }
885 :
886 0 : CPLFree(pszSubElementName);
887 0 : pszSubElementName = NULL;
888 0 : CPLFree(pszSubElementValue);
889 0 : pszSubElementValue = NULL;
890 0 : nSubElementValueLen = 0;
891 : }
892 425 : else if (depthLevel == interestingDepthLevel + 1 &&
893 : strcmp(pszName, "extensions") == 0)
894 : {
895 17 : inExtensions = FALSE;
896 : }
897 582 : else if ((depthLevel == interestingDepthLevel + 1 ||
898 : (inExtensions && depthLevel == interestingDepthLevel + 2) ) &&
899 : pszSubElementName && strcmp(pszName, pszSubElementName) == 0)
900 : {
901 191 : if (poFeature && pszSubElementValue && nSubElementValueLen)
902 : {
903 188 : pszSubElementValue[nSubElementValueLen] = 0;
904 188 : if (strcmp(pszSubElementName, "time") == 0)
905 : {
906 : int year, month, day, hour, minute, TZ;
907 : float second;
908 23 : if (OGRParseXMLDateTime(pszSubElementValue, &year, &month, &day, &hour, &minute, &second, &TZ))
909 : {
910 23 : poFeature->SetField(iCurrentField, year, month, day, hour, minute, (int)(second + .5), TZ);
911 : }
912 : else
913 : {
914 : CPLError(CE_Warning, CPLE_AppDefined,
915 0 : "Could not parse %s as a valid dateTime", pszSubElementValue);
916 : }
917 : }
918 : else
919 : {
920 165 : poFeature->SetField( iCurrentField, pszSubElementValue);
921 : }
922 : }
923 191 : if (strcmp(pszName, "link") == 0)
924 0 : inLink = FALSE;
925 :
926 191 : CPLFree(pszSubElementName);
927 191 : pszSubElementName = NULL;
928 191 : CPLFree(pszSubElementValue);
929 191 : pszSubElementValue = NULL;
930 191 : nSubElementValueLen = 0;
931 : }
932 306 : else if (inLink && depthLevel == interestingDepthLevel + 2)
933 : {
934 106 : if (iCurrentField != -1 && pszSubElementName &&
935 : strcmp(pszName, pszSubElementName) == 0 && poFeature && pszSubElementValue && nSubElementValueLen)
936 : {
937 84 : pszSubElementValue[nSubElementValueLen] = 0;
938 84 : poFeature->SetField( iCurrentField, pszSubElementValue);
939 : }
940 :
941 106 : CPLFree(pszSubElementName);
942 106 : pszSubElementName = NULL;
943 106 : CPLFree(pszSubElementValue);
944 106 : pszSubElementValue = NULL;
945 106 : nSubElementValueLen = 0;
946 : }
947 94 : else if (inExtensions && depthLevel > interestingDepthLevel + 2)
948 : {
949 0 : AddStrToSubElementValue(CPLSPrintf("</%s>", pszName));
950 : }
951 : }
952 : }
953 :
954 : /************************************************************************/
955 : /* dataHandlerCbk() */
956 : /************************************************************************/
957 :
958 4233 : void OGRGPXLayer::dataHandlerCbk(const char *data, int nLen)
959 : {
960 4233 : if (bStopParsing) return;
961 :
962 4233 : nDataHandlerCounter ++;
963 4233 : if (nDataHandlerCounter >= BUFSIZ)
964 : {
965 0 : CPLError(CE_Failure, CPLE_AppDefined, "File probably corrupted (million laugh pattern)");
966 0 : XML_StopParser(oParser, XML_FALSE);
967 0 : bStopParsing = TRUE;
968 0 : return;
969 : }
970 :
971 4233 : nWithoutEventCounter = 0;
972 :
973 4233 : if (pszSubElementName)
974 : {
975 272 : if (inExtensions && depthLevel > interestingDepthLevel + 2)
976 : {
977 15 : if (data[0] == '\n')
978 0 : return;
979 : }
980 272 : char* pszNewSubElementValue = (char*) VSIRealloc(pszSubElementValue, nSubElementValueLen + nLen + 1);
981 272 : if (pszNewSubElementValue == NULL)
982 : {
983 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
984 0 : XML_StopParser(oParser, XML_FALSE);
985 0 : bStopParsing = TRUE;
986 0 : return;
987 : }
988 272 : pszSubElementValue = pszNewSubElementValue;
989 272 : memcpy(pszSubElementValue + nSubElementValueLen, data, nLen);
990 272 : nSubElementValueLen += nLen;
991 272 : if (nSubElementValueLen > 100000)
992 : {
993 : CPLError(CE_Failure, CPLE_AppDefined,
994 0 : "Too much data inside one element. File probably corrupted");
995 0 : XML_StopParser(oParser, XML_FALSE);
996 0 : bStopParsing = TRUE;
997 : }
998 : }
999 : }
1000 : #endif
1001 :
1002 : /************************************************************************/
1003 : /* GetNextFeature() */
1004 : /************************************************************************/
1005 :
1006 114 : OGRFeature *OGRGPXLayer::GetNextFeature()
1007 : {
1008 114 : if (bWriteMode)
1009 : {
1010 : CPLError(CE_Failure, CPLE_NotSupported,
1011 0 : "Cannot read features when writing a GPX file");
1012 0 : return NULL;
1013 : }
1014 :
1015 114 : if (fpGPX == NULL)
1016 0 : return NULL;
1017 :
1018 114 : if (bStopParsing)
1019 0 : return NULL;
1020 :
1021 : #ifdef HAVE_EXPAT
1022 114 : if (nFeatureTabIndex < nFeatureTabLength)
1023 : {
1024 40 : return ppoFeatureTab[nFeatureTabIndex++];
1025 : }
1026 :
1027 74 : if (VSIFEofL(fpGPX))
1028 30 : return NULL;
1029 :
1030 : char aBuf[BUFSIZ];
1031 :
1032 44 : CPLFree(ppoFeatureTab);
1033 44 : ppoFeatureTab = NULL;
1034 44 : nFeatureTabLength = 0;
1035 44 : nFeatureTabIndex = 0;
1036 44 : nWithoutEventCounter = 0;
1037 :
1038 : int nDone;
1039 44 : do
1040 : {
1041 44 : nDataHandlerCounter = 0;
1042 44 : unsigned int nLen = (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpGPX );
1043 44 : nDone = VSIFEofL(fpGPX);
1044 44 : if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
1045 : {
1046 : CPLError(CE_Failure, CPLE_AppDefined,
1047 : "XML parsing of GPX file failed : %s at line %d, column %d",
1048 : XML_ErrorString(XML_GetErrorCode(oParser)),
1049 : (int)XML_GetCurrentLineNumber(oParser),
1050 0 : (int)XML_GetCurrentColumnNumber(oParser));
1051 0 : bStopParsing = TRUE;
1052 0 : break;
1053 : }
1054 44 : nWithoutEventCounter ++;
1055 : } while (!nDone && nFeatureTabLength == 0 && !bStopParsing && nWithoutEventCounter < 10);
1056 :
1057 44 : if (nWithoutEventCounter == 10)
1058 : {
1059 : CPLError(CE_Failure, CPLE_AppDefined,
1060 0 : "Too much data inside one element. File probably corrupted");
1061 0 : bStopParsing = TRUE;
1062 : }
1063 :
1064 44 : return (nFeatureTabLength) ? ppoFeatureTab[nFeatureTabIndex++] : NULL;
1065 : #else
1066 : return NULL;
1067 : #endif
1068 : }
1069 :
1070 : /************************************************************************/
1071 : /* GetSpatialRef() */
1072 : /************************************************************************/
1073 :
1074 0 : OGRSpatialReference *OGRGPXLayer::GetSpatialRef()
1075 :
1076 : {
1077 0 : return poSRS;
1078 : }
1079 :
1080 : /************************************************************************/
1081 : /* OGRGPX_GetXMLCompatibleTagName() */
1082 : /************************************************************************/
1083 :
1084 6 : static char* OGRGPX_GetXMLCompatibleTagName(const char* pszExtensionsNS,
1085 : const char* pszName)
1086 : {
1087 : /* Skip "ogr_" for example if NS is "ogr". Usefull for GPX -> GPX roundtrip */
1088 6 : if (strncmp(pszName, pszExtensionsNS, strlen(pszExtensionsNS)) == 0 &&
1089 0 : pszName[strlen(pszExtensionsNS)] == '_')
1090 : {
1091 0 : pszName += strlen(pszExtensionsNS) + 1;
1092 : }
1093 :
1094 6 : char* pszModName = CPLStrdup(pszName);
1095 : int i;
1096 66 : for(i=0;pszModName[i] != 0;i++)
1097 : {
1098 60 : if (pszModName[i] == ' ')
1099 6 : pszModName[i] = '_';
1100 : }
1101 6 : return pszModName;
1102 : }
1103 :
1104 : /************************************************************************/
1105 : /* OGRGPX_GetUTF8String() */
1106 : /************************************************************************/
1107 :
1108 0 : static char* OGRGPX_GetUTF8String(const char* pszString)
1109 : {
1110 : char *pszEscaped;
1111 0 : if (!CPLIsUTF8(pszString, -1) &&
1112 : CSLTestBoolean(CPLGetConfigOption("OGR_FORCE_ASCII", "YES")))
1113 : {
1114 : static int bFirstTime = TRUE;
1115 0 : if (bFirstTime)
1116 : {
1117 0 : bFirstTime = FALSE;
1118 : CPLError(CE_Warning, CPLE_AppDefined,
1119 : "%s is not a valid UTF-8 string. Forcing it to ASCII.\n"
1120 : "If you still want the original string and change the XML file encoding\n"
1121 : "afterwards, you can define OGR_FORCE_ASCII=NO as configuration option.\n"
1122 0 : "This warning won't be issued anymore", pszString);
1123 : }
1124 : else
1125 : {
1126 : CPLDebug("OGR", "%s is not a valid UTF-8 string. Forcing it to ASCII",
1127 0 : pszString);
1128 : }
1129 0 : pszEscaped = CPLForceToASCII(pszString, -1, '?');
1130 : }
1131 : else
1132 0 : pszEscaped = CPLStrdup(pszString);
1133 :
1134 0 : return pszEscaped;
1135 : }
1136 :
1137 : /************************************************************************/
1138 : /* OGRGPX_WriteXMLExtension() */
1139 : /************************************************************************/
1140 :
1141 0 : int OGRGPXLayer::OGRGPX_WriteXMLExtension(const char* pszTagName,
1142 : const char* pszContent)
1143 : {
1144 0 : CPLXMLNode* poXML = CPLParseXMLString(pszContent);
1145 0 : if (poXML)
1146 : {
1147 : char* pszTagNameWithNS;
1148 0 : const char* pszXMLNS = NULL;
1149 0 : const char* pszUnderscore = strchr(pszTagName, '_');
1150 0 : pszTagNameWithNS = CPLStrdup(pszTagName);
1151 0 : if (pszUnderscore)
1152 0 : pszTagNameWithNS[pszUnderscore - pszTagName] = ':';
1153 :
1154 : /* If we detect a Garmin GPX extension, add its xmlns */
1155 0 : if (strcmp(pszTagName, "gpxx_WaypointExtension") == 0)
1156 0 : pszXMLNS = " xmlns:gpxx=\"http://www.garmin.com/xmlschemas/GpxExtensions/v3\"";
1157 :
1158 : /* Don't XML escape here */
1159 0 : char *pszUTF8 = OGRGPX_GetUTF8String( pszContent );
1160 : poDS->PrintLine(" <%s%s>%s</%s>",
1161 0 : pszTagNameWithNS, (pszXMLNS) ? pszXMLNS : "", pszUTF8, pszTagNameWithNS);
1162 0 : CPLFree(pszUTF8);
1163 :
1164 0 : CPLFree(pszTagNameWithNS);
1165 0 : CPLDestroyXMLNode(poXML);
1166 :
1167 0 : return TRUE;
1168 : }
1169 :
1170 0 : return FALSE;
1171 : }
1172 :
1173 : /************************************************************************/
1174 : /* WriteFeatureAttributes() */
1175 : /************************************************************************/
1176 :
1177 30 : static void AddIdent(VSILFILE* fp, int nIdentLevel)
1178 : {
1179 : int i;
1180 80 : for(i=0;i<nIdentLevel;i++)
1181 50 : VSIFPrintfL(fp, " ");
1182 30 : }
1183 :
1184 18 : void OGRGPXLayer::WriteFeatureAttributes( OGRFeature *poFeature, int nIdentLevel )
1185 : {
1186 18 : VSILFILE* fp = poDS->GetOutputFP();
1187 : int i;
1188 :
1189 : /* Begin with standard GPX fields */
1190 377 : for(i=iFirstGPXField;i<nGPXFields;i++)
1191 : {
1192 359 : OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn( i );
1193 359 : if( poFeature->IsFieldSet( i ) )
1194 : {
1195 24 : const char* pszName = poFieldDefn->GetNameRef();
1196 24 : if (strcmp(pszName, "time") == 0)
1197 : {
1198 : int year, month, day, hour, minute, second, TZFlag;
1199 2 : if (poFeature->GetFieldAsDateTime(i, &year, &month, &day,
1200 : &hour, &minute, &second, &TZFlag))
1201 : {
1202 2 : char* pszDate = OGRGetXMLDateTime(year, month, day, hour, minute, second, TZFlag);
1203 2 : AddIdent(fp, nIdentLevel);
1204 2 : poDS->PrintLine("<time>%s</time>", pszDate);
1205 2 : CPLFree(pszDate);
1206 : }
1207 : }
1208 22 : else if (strncmp(pszName, "link", 4) == 0)
1209 : {
1210 6 : if (strstr(pszName, "href"))
1211 : {
1212 2 : AddIdent(fp, nIdentLevel);
1213 2 : VSIFPrintfL(fp, "<link href=\"%s\">", poFeature->GetFieldAsString( i ));
1214 2 : if( poFeature->IsFieldSet( i + 1 ) )
1215 2 : VSIFPrintfL(fp, "<text>%s</text>", poFeature->GetFieldAsString( i + 1 ));
1216 2 : if( poFeature->IsFieldSet( i + 2 ) )
1217 2 : VSIFPrintfL(fp, "<type>%s</type>", poFeature->GetFieldAsString( i + 2 ));
1218 2 : poDS->PrintLine("</link>");
1219 : }
1220 : }
1221 16 : else if (poFieldDefn->GetType() == OFTReal)
1222 : {
1223 : char szValue[64];
1224 4 : OGRFormatDouble(szValue, sizeof(szValue), poFeature->GetFieldAsDouble(i), '.');
1225 4 : AddIdent(fp, nIdentLevel);
1226 4 : poDS->PrintLine("<%s>%s</%s>", pszName, szValue, pszName);
1227 : }
1228 : else
1229 : {
1230 : char* pszValue =
1231 12 : OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( i ));
1232 12 : AddIdent(fp, nIdentLevel);
1233 12 : poDS->PrintLine("<%s>%s</%s>", pszName, pszValue, pszName);
1234 12 : CPLFree(pszValue);
1235 : }
1236 : }
1237 : }
1238 :
1239 : /* Write "extra" fields within the <extensions> tag */
1240 18 : int n = poFeatureDefn->GetFieldCount();
1241 18 : if (i < n)
1242 : {
1243 2 : const char* pszExtensionsNS = poDS->GetExtensionsNS();
1244 2 : AddIdent(fp, nIdentLevel);
1245 2 : poDS->PrintLine("<extensions>");
1246 8 : for(;i<n;i++)
1247 : {
1248 6 : OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn( i );
1249 6 : if( poFeature->IsFieldSet( i ) )
1250 : {
1251 : char* compatibleName =
1252 6 : OGRGPX_GetXMLCompatibleTagName(pszExtensionsNS, poFieldDefn->GetNameRef());
1253 :
1254 6 : if (poFieldDefn->GetType() == OFTReal)
1255 : {
1256 : char szValue[64];
1257 0 : OGRFormatDouble(szValue, sizeof(szValue), poFeature->GetFieldAsDouble(i), '.');
1258 0 : AddIdent(fp, nIdentLevel + 1);
1259 : poDS->PrintLine("<%s:%s>%s</%s:%s>",
1260 : pszExtensionsNS,
1261 : compatibleName,
1262 : szValue,
1263 : pszExtensionsNS,
1264 0 : compatibleName);
1265 : }
1266 : else
1267 : {
1268 6 : const char *pszRaw = poFeature->GetFieldAsString( i );
1269 :
1270 : /* Try to detect XML content */
1271 6 : if (pszRaw[0] == '<' && pszRaw[strlen(pszRaw) - 1] == '>')
1272 : {
1273 0 : if (OGRGPX_WriteXMLExtension( compatibleName, pszRaw))
1274 0 : continue;
1275 : }
1276 :
1277 : /* Try to detect XML escaped content */
1278 6 : else if (strncmp(pszRaw, "<", 4) == 0 &&
1279 : strncmp(pszRaw + strlen(pszRaw) - 4, ">", 4) == 0)
1280 : {
1281 0 : char* pszUnescapedContent = CPLUnescapeString( pszRaw, NULL, CPLES_XML );
1282 :
1283 0 : if (OGRGPX_WriteXMLExtension(compatibleName, pszUnescapedContent))
1284 : {
1285 0 : CPLFree(pszUnescapedContent);
1286 0 : continue;
1287 : }
1288 :
1289 0 : CPLFree(pszUnescapedContent);
1290 : }
1291 :
1292 : /* Remove leading spaces for a numeric field */
1293 6 : if (poFieldDefn->GetType() == OFTInteger || poFieldDefn->GetType() == OFTReal)
1294 : {
1295 0 : while( *pszRaw == ' ' )
1296 0 : pszRaw++;
1297 : }
1298 :
1299 6 : char *pszEscaped = OGRGetXML_UTF8_EscapedString( pszRaw );
1300 6 : AddIdent(fp, nIdentLevel + 1);
1301 : poDS->PrintLine("<%s:%s>%s</%s:%s>",
1302 : pszExtensionsNS,
1303 : compatibleName,
1304 : pszEscaped,
1305 : pszExtensionsNS,
1306 6 : compatibleName);
1307 6 : CPLFree(pszEscaped);
1308 : }
1309 6 : CPLFree(compatibleName);
1310 : }
1311 : }
1312 2 : AddIdent(fp, nIdentLevel);
1313 2 : poDS->PrintLine("</extensions>");
1314 : }
1315 18 : }
1316 :
1317 : /************************************************************************/
1318 : /* CheckAndFixCoordinatesValidity() */
1319 : /************************************************************************/
1320 :
1321 20 : OGRErr OGRGPXLayer::CheckAndFixCoordinatesValidity( double* pdfLatitude, double* pdfLongitude )
1322 : {
1323 20 : if (pdfLatitude != NULL && (*pdfLatitude < -90 || *pdfLatitude > 90))
1324 : {
1325 : static int bFirstWarning = TRUE;
1326 0 : if (bFirstWarning)
1327 : {
1328 : CPLError(CE_Failure, CPLE_AppDefined,
1329 : "Latitude %f is invalid. Valid range is [-90,90]. This warning will not be issued any more",
1330 0 : *pdfLatitude);
1331 0 : bFirstWarning = FALSE;
1332 : }
1333 0 : return CE_Failure;
1334 : }
1335 :
1336 20 : if (pdfLongitude != NULL && (*pdfLongitude < -180 || *pdfLongitude > 180))
1337 : {
1338 : static int bFirstWarning = TRUE;
1339 0 : if (bFirstWarning)
1340 : {
1341 : CPLError(CE_Warning, CPLE_AppDefined,
1342 : "Longitude %f has been modified to fit into range [-180,180]. This warning will not be issued any more",
1343 0 : *pdfLongitude);
1344 0 : bFirstWarning = FALSE;
1345 : }
1346 :
1347 0 : if (*pdfLongitude > 180)
1348 0 : *pdfLongitude -= ((int) ((*pdfLongitude+180)/360)*360);
1349 0 : else if (*pdfLongitude < -180)
1350 0 : *pdfLongitude += ((int) (180 - *pdfLongitude)/360)*360;
1351 :
1352 0 : return CE_None;
1353 : }
1354 :
1355 20 : return CE_None;
1356 : }
1357 :
1358 : /************************************************************************/
1359 : /* CreateFeature() */
1360 : /************************************************************************/
1361 :
1362 18 : OGRErr OGRGPXLayer::CreateFeature( OGRFeature *poFeature )
1363 :
1364 : {
1365 18 : VSILFILE* fp = poDS->GetOutputFP();
1366 18 : if (fp == NULL)
1367 0 : return CE_Failure;
1368 :
1369 : char szLat[64];
1370 : char szLon[64];
1371 : char szAlt[64];
1372 :
1373 18 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1374 :
1375 18 : if (gpxGeomType == GPX_WPT)
1376 : {
1377 4 : if (poDS->GetLastGPXGeomTypeWritten() == GPX_ROUTE)
1378 : {
1379 : CPLError( CE_Failure, CPLE_NotSupported,
1380 0 : "Cannot write a 'wpt' element after a 'rte' element.\n");
1381 0 : return OGRERR_FAILURE;
1382 : }
1383 : else
1384 4 : if (poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK)
1385 : {
1386 : CPLError( CE_Failure, CPLE_NotSupported,
1387 0 : "Cannot write a 'wpt' element after a 'trk' element.\n");
1388 0 : return OGRERR_FAILURE;
1389 : }
1390 :
1391 4 : poDS->SetLastGPXGeomTypeWritten(gpxGeomType);
1392 :
1393 4 : if ( poGeom == NULL || wkbFlatten(poGeom->getGeometryType()) != wkbPoint )
1394 : {
1395 : CPLError( CE_Failure, CPLE_AppDefined,
1396 0 : "Features without geometry or with non-ponctual geometries not supported by GPX writer in waypoints layer." );
1397 0 : return OGRERR_FAILURE;
1398 : }
1399 :
1400 4 : if ( poGeom->getCoordinateDimension() == 0 )
1401 : {
1402 : CPLError( CE_Failure, CPLE_AppDefined,
1403 0 : "POINT EMPTY geometries not supported by GPX writer." );
1404 0 : return OGRERR_FAILURE;
1405 : }
1406 :
1407 4 : OGRPoint* point = (OGRPoint*)poGeom;
1408 4 : double lat = point->getY();
1409 4 : double lon = point->getX();
1410 4 : CheckAndFixCoordinatesValidity(&lat, &lon);
1411 4 : poDS->AddCoord(lon, lat);
1412 4 : OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1413 4 : OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1414 4 : poDS->PrintLine("<wpt lat=\"%s\" lon=\"%s\">", szLat, szLon);
1415 4 : WriteFeatureAttributes(poFeature);
1416 4 : poDS->PrintLine("</wpt>");
1417 : }
1418 14 : else if (gpxGeomType == GPX_ROUTE)
1419 : {
1420 2 : if (poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK ||
1421 : poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK_POINT)
1422 : {
1423 : CPLError( CE_Failure, CPLE_NotSupported,
1424 0 : "Cannot write a 'rte' element after a 'trk' element.\n");
1425 0 : return OGRERR_FAILURE;
1426 : }
1427 :
1428 2 : if (poDS->GetLastGPXGeomTypeWritten() == GPX_ROUTE_POINT && poDS->nLastRteId != -1)
1429 : {
1430 0 : poDS->PrintLine("</rte>");
1431 0 : poDS->nLastRteId = -1;
1432 : }
1433 :
1434 2 : poDS->SetLastGPXGeomTypeWritten(gpxGeomType);
1435 :
1436 2 : OGRLineString* line = NULL;
1437 :
1438 2 : if ( poGeom == NULL )
1439 : {
1440 0 : poDS->PrintLine("<rte>");
1441 0 : WriteFeatureAttributes(poFeature);
1442 0 : poDS->PrintLine("</rte>");
1443 0 : return OGRERR_NONE;
1444 : }
1445 :
1446 2 : switch( poGeom->getGeometryType() )
1447 : {
1448 : case wkbLineString:
1449 : case wkbLineString25D:
1450 : {
1451 2 : line = (OGRLineString*)poGeom;
1452 2 : break;
1453 : }
1454 :
1455 : case wkbMultiLineString:
1456 : case wkbMultiLineString25D:
1457 : {
1458 0 : int nGeometries = ((OGRGeometryCollection*)poGeom)->getNumGeometries ();
1459 0 : if (nGeometries == 0)
1460 : {
1461 0 : line = NULL;
1462 : }
1463 0 : else if (nGeometries == 1)
1464 : {
1465 0 : line = (OGRLineString*) ( ((OGRGeometryCollection*)poGeom)->getGeometryRef(0) );
1466 : }
1467 : else
1468 : {
1469 : CPLError( CE_Failure, CPLE_NotSupported,
1470 0 : "Multiline with more than one line is not supported for 'rte' element.\n");
1471 0 : return OGRERR_FAILURE;
1472 : }
1473 0 : break;
1474 : }
1475 :
1476 : default:
1477 : {
1478 : CPLError( CE_Failure, CPLE_NotSupported,
1479 : "Geometry type of `%s' not supported for 'rte' element.\n",
1480 0 : OGRGeometryTypeToName(poGeom->getGeometryType()) );
1481 0 : return OGRERR_FAILURE;
1482 : }
1483 : }
1484 :
1485 2 : int n = (line) ? line->getNumPoints() : 0;
1486 : int i;
1487 2 : poDS->PrintLine("<rte>");
1488 2 : WriteFeatureAttributes(poFeature);
1489 5 : for(i=0;i<n;i++)
1490 : {
1491 3 : double lat = line->getY(i);
1492 3 : double lon = line->getX(i);
1493 3 : CheckAndFixCoordinatesValidity(&lat, &lon);
1494 3 : poDS->AddCoord(lon, lat);
1495 3 : OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1496 3 : OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1497 3 : poDS->PrintLine(" <rtept lat=\"%s\" lon=\"%s\">", szLat, szLon);
1498 6 : if (poGeom->getGeometryType() == wkbLineString25D ||
1499 3 : poGeom->getGeometryType() == wkbMultiLineString25D)
1500 : {
1501 0 : OGRFormatDouble(szAlt, sizeof(szAlt), line->getZ(i), '.');
1502 0 : poDS->PrintLine(" <ele>%s</ele>", szAlt);
1503 : }
1504 3 : poDS->PrintLine(" </rtept>");
1505 : }
1506 2 : poDS->PrintLine("</rte>");
1507 : }
1508 12 : else if (gpxGeomType == GPX_TRACK)
1509 : {
1510 3 : if (poDS->GetLastGPXGeomTypeWritten() == GPX_ROUTE_POINT && poDS->nLastRteId != -1)
1511 : {
1512 0 : poDS->PrintLine("</rte>");
1513 0 : poDS->nLastRteId = -1;
1514 : }
1515 3 : if (poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK_POINT && poDS->nLastTrkId != -1)
1516 : {
1517 0 : poDS->PrintLine(" </trkseg>");
1518 0 : poDS->PrintLine("</trk>");
1519 0 : poDS->nLastTrkId = -1;
1520 0 : poDS->nLastTrkSegId = -1;
1521 : }
1522 :
1523 3 : poDS->SetLastGPXGeomTypeWritten(gpxGeomType);
1524 :
1525 3 : if (poGeom == NULL)
1526 : {
1527 0 : poDS->PrintLine("<trk>");
1528 0 : WriteFeatureAttributes(poFeature);
1529 0 : poDS->PrintLine("</trk>");
1530 0 : return OGRERR_NONE;
1531 : }
1532 :
1533 3 : switch( poGeom->getGeometryType() )
1534 : {
1535 : case wkbLineString:
1536 : case wkbLineString25D:
1537 : {
1538 0 : OGRLineString* line = (OGRLineString*)poGeom;
1539 0 : int n = line->getNumPoints();
1540 : int i;
1541 0 : poDS->PrintLine("<trk>");
1542 0 : WriteFeatureAttributes(poFeature);
1543 0 : poDS->PrintLine(" <trkseg>");
1544 0 : for(i=0;i<n;i++)
1545 : {
1546 0 : double lat = line->getY(i);
1547 0 : double lon = line->getX(i);
1548 0 : CheckAndFixCoordinatesValidity(&lat, &lon);
1549 0 : poDS->AddCoord(lon, lat);
1550 0 : OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1551 0 : OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1552 0 : poDS->PrintLine(" <trkpt lat=\"%s\" lon=\"%s\">", szLat, szLon);
1553 0 : if (line->getGeometryType() == wkbLineString25D)
1554 : {
1555 0 : OGRFormatDouble(szAlt, sizeof(szAlt), line->getZ(i), '.');
1556 0 : poDS->PrintLine(" <ele>%s</ele>", szAlt);
1557 : }
1558 0 : poDS->PrintLine(" </trkpt>");
1559 : }
1560 0 : poDS->PrintLine(" </trkseg>");
1561 0 : poDS->PrintLine("</trk>");
1562 0 : break;
1563 : }
1564 :
1565 : case wkbMultiLineString:
1566 : case wkbMultiLineString25D:
1567 : {
1568 3 : int nGeometries = ((OGRGeometryCollection*)poGeom)->getNumGeometries ();
1569 3 : poDS->PrintLine("<trk>");
1570 3 : WriteFeatureAttributes(poFeature);
1571 : int j;
1572 6 : for(j=0;j<nGeometries;j++)
1573 : {
1574 3 : OGRLineString* line = (OGRLineString*) ( ((OGRGeometryCollection*)poGeom)->getGeometryRef(j) );
1575 3 : int n = (line) ? line->getNumPoints() : 0;
1576 : int i;
1577 3 : poDS->PrintLine(" <trkseg>");
1578 7 : for(i=0;i<n;i++)
1579 : {
1580 4 : double lat = line->getY(i);
1581 4 : double lon = line->getX(i);
1582 4 : CheckAndFixCoordinatesValidity(&lat, &lon);
1583 4 : poDS->AddCoord(lon, lat);
1584 4 : OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1585 4 : OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1586 4 : poDS->PrintLine(" <trkpt lat=\"%s\" lon=\"%s\">", szLat, szLon);
1587 4 : if (line->getGeometryType() == wkbLineString25D)
1588 : {
1589 0 : OGRFormatDouble(szAlt, sizeof(szAlt), line->getZ(i), '.');
1590 0 : poDS->PrintLine(" <ele>%s</ele>", szAlt);
1591 : }
1592 4 : poDS->PrintLine(" </trkpt>");
1593 : }
1594 3 : poDS->PrintLine(" </trkseg>");
1595 : }
1596 3 : poDS->PrintLine("</trk>");
1597 3 : break;
1598 : }
1599 :
1600 : default:
1601 : {
1602 : CPLError( CE_Failure, CPLE_NotSupported,
1603 : "Geometry type of `%s' not supported for 'trk' element.\n",
1604 0 : OGRGeometryTypeToName(poGeom->getGeometryType()) );
1605 0 : return OGRERR_FAILURE;
1606 : }
1607 : }
1608 : }
1609 9 : else if (gpxGeomType == GPX_ROUTE_POINT)
1610 : {
1611 4 : if (poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK ||
1612 : poDS->GetLastGPXGeomTypeWritten() == GPX_TRACK_POINT)
1613 : {
1614 : CPLError( CE_Failure, CPLE_NotSupported,
1615 0 : "Cannot write a 'rte' element after a 'trk' element.\n");
1616 0 : return OGRERR_FAILURE;
1617 : }
1618 :
1619 4 : if ( poGeom == NULL || wkbFlatten(poGeom->getGeometryType()) != wkbPoint )
1620 : {
1621 : CPLError( CE_Failure, CPLE_AppDefined,
1622 0 : "Features without geometry or with non-ponctual geometries not supported by GPX writer in route_points layer." );
1623 0 : return OGRERR_FAILURE;
1624 : }
1625 :
1626 4 : if ( poGeom->getCoordinateDimension() == 0 )
1627 : {
1628 : CPLError( CE_Failure, CPLE_AppDefined,
1629 0 : "POINT EMPTY geometries not supported by GPX writer." );
1630 0 : return OGRERR_FAILURE;
1631 : }
1632 :
1633 4 : if ( !poFeature->IsFieldSet(FLD_ROUTE_FID) )
1634 : {
1635 : CPLError( CE_Failure, CPLE_AppDefined,
1636 0 : "Field %s must be set.", poFeatureDefn->GetFieldDefn(FLD_ROUTE_FID)->GetNameRef() );
1637 0 : return OGRERR_FAILURE;
1638 : }
1639 4 : if ( poFeature->GetFieldAsInteger(FLD_ROUTE_FID) < 0 )
1640 : {
1641 : CPLError( CE_Failure, CPLE_AppDefined,
1642 0 : "Invalid value for field %s.", poFeatureDefn->GetFieldDefn(FLD_ROUTE_FID)->GetNameRef() );
1643 0 : return OGRERR_FAILURE;
1644 : }
1645 :
1646 4 : poDS->SetLastGPXGeomTypeWritten(gpxGeomType);
1647 :
1648 4 : if ( poDS->nLastRteId != poFeature->GetFieldAsInteger(FLD_ROUTE_FID))
1649 : {
1650 2 : if (poDS->nLastRteId != -1)
1651 : {
1652 1 : poDS->PrintLine("</rte>");
1653 : }
1654 2 : poDS->PrintLine("<rte>");
1655 2 : if ( poFeature->IsFieldSet(FLD_ROUTE_NAME) )
1656 : {
1657 : char* pszValue =
1658 2 : OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( FLD_ROUTE_NAME ));
1659 : poDS->PrintLine(" <%s>%s</%s>",
1660 2 : "name", pszValue, "name");
1661 2 : CPLFree(pszValue);
1662 : }
1663 : }
1664 :
1665 4 : poDS->nLastRteId = poFeature->GetFieldAsInteger(FLD_ROUTE_FID);
1666 :
1667 4 : OGRPoint* point = (OGRPoint*)poGeom;
1668 4 : double lat = point->getY();
1669 4 : double lon = point->getX();
1670 4 : CheckAndFixCoordinatesValidity(&lat, &lon);
1671 4 : poDS->AddCoord(lon, lat);
1672 4 : OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1673 4 : OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1674 4 : poDS->PrintLine(" <rtept lat=\"%s\" lon=\"%s\">", szLat, szLon);
1675 4 : WriteFeatureAttributes(poFeature, 2);
1676 4 : poDS->PrintLine(" </rtept>");
1677 :
1678 : }
1679 : else
1680 : {
1681 5 : if (poDS->GetLastGPXGeomTypeWritten() == GPX_ROUTE_POINT && poDS->nLastRteId != -1)
1682 : {
1683 1 : poDS->PrintLine("</rte>");
1684 1 : poDS->nLastRteId = -1;
1685 : }
1686 :
1687 5 : if ( poGeom == NULL || wkbFlatten(poGeom->getGeometryType()) != wkbPoint )
1688 : {
1689 : CPLError( CE_Failure, CPLE_AppDefined,
1690 0 : "Features without geometry or with non-ponctual geometries not supported by GPX writer in track_points layer." );
1691 0 : return OGRERR_FAILURE;
1692 : }
1693 :
1694 5 : if ( poGeom->getCoordinateDimension() == 0 )
1695 : {
1696 : CPLError( CE_Failure, CPLE_AppDefined,
1697 0 : "POINT EMPTY geometries not supported by GPX writer." );
1698 0 : return OGRERR_FAILURE;
1699 : }
1700 :
1701 5 : if ( !poFeature->IsFieldSet(FLD_TRACK_FID) )
1702 : {
1703 : CPLError( CE_Failure, CPLE_AppDefined,
1704 0 : "Field %s must be set.", poFeatureDefn->GetFieldDefn(FLD_TRACK_FID)->GetNameRef() );
1705 0 : return OGRERR_FAILURE;
1706 : }
1707 5 : if ( poFeature->GetFieldAsInteger(FLD_TRACK_FID) < 0 )
1708 : {
1709 : CPLError( CE_Failure, CPLE_AppDefined,
1710 0 : "Invalid value for field %s.", poFeatureDefn->GetFieldDefn(FLD_TRACK_FID)->GetNameRef() );
1711 0 : return OGRERR_FAILURE;
1712 : }
1713 5 : if ( !poFeature->IsFieldSet(FLD_TRACK_SEG_ID) )
1714 : {
1715 : CPLError( CE_Failure, CPLE_AppDefined,
1716 0 : "Field %s must be set.", poFeatureDefn->GetFieldDefn(FLD_TRACK_SEG_ID)->GetNameRef() );
1717 0 : return OGRERR_FAILURE;
1718 : }
1719 5 : if ( poFeature->GetFieldAsInteger(FLD_TRACK_SEG_ID) < 0 )
1720 : {
1721 : CPLError( CE_Failure, CPLE_AppDefined,
1722 0 : "Invalid value for field %s.", poFeatureDefn->GetFieldDefn(FLD_TRACK_SEG_ID)->GetNameRef() );
1723 0 : return OGRERR_FAILURE;
1724 : }
1725 :
1726 5 : poDS->SetLastGPXGeomTypeWritten(gpxGeomType);
1727 :
1728 5 : if ( poDS->nLastTrkId != poFeature->GetFieldAsInteger(FLD_TRACK_FID))
1729 : {
1730 3 : if (poDS->nLastTrkId != -1)
1731 : {
1732 1 : poDS->PrintLine(" </trkseg>");
1733 1 : poDS->PrintLine("</trk>");
1734 : }
1735 3 : poDS->PrintLine("<trk>");
1736 :
1737 3 : if ( poFeature->IsFieldSet(FLD_TRACK_NAME) )
1738 : {
1739 : char* pszValue =
1740 3 : OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( FLD_TRACK_NAME ));
1741 : poDS->PrintLine(" <%s>%s</%s>",
1742 3 : "name", pszValue, "name");
1743 3 : CPLFree(pszValue);
1744 : }
1745 :
1746 3 : poDS->PrintLine(" <trkseg>");
1747 : }
1748 2 : else if (poDS->nLastTrkSegId != poFeature->GetFieldAsInteger(FLD_TRACK_SEG_ID))
1749 : {
1750 1 : poDS->PrintLine(" </trkseg>");
1751 1 : poDS->PrintLine(" <trkseg>");
1752 : }
1753 :
1754 5 : poDS->nLastTrkId = poFeature->GetFieldAsInteger(FLD_TRACK_FID);
1755 5 : poDS->nLastTrkSegId = poFeature->GetFieldAsInteger(FLD_TRACK_SEG_ID);
1756 :
1757 5 : OGRPoint* point = (OGRPoint*)poGeom;
1758 5 : double lat = point->getY();
1759 5 : double lon = point->getX();
1760 5 : CheckAndFixCoordinatesValidity(&lat, &lon);
1761 5 : poDS->AddCoord(lon, lat);
1762 5 : OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1763 5 : OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1764 5 : poDS->PrintLine(" <trkpt lat=\"%s\" lon=\"%s\">", szLat, szLon);
1765 5 : WriteFeatureAttributes(poFeature, 3);
1766 5 : poDS->PrintLine(" </trkpt>");
1767 : }
1768 :
1769 18 : return OGRERR_NONE;
1770 : }
1771 :
1772 :
1773 :
1774 : /************************************************************************/
1775 : /* CreateField() */
1776 : /************************************************************************/
1777 :
1778 :
1779 3 : OGRErr OGRGPXLayer::CreateField( OGRFieldDefn *poField, int bApproxOK )
1780 :
1781 : {
1782 75 : for( int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ )
1783 : {
1784 72 : if (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
1785 : poField->GetNameRef() ) == 0)
1786 : {
1787 0 : return OGRERR_NONE;
1788 : }
1789 : }
1790 3 : if (poDS->GetUseExtensions() == FALSE)
1791 : {
1792 : CPLError(CE_Failure, CPLE_NotSupported,
1793 : "Field of name '%s' is not supported in GPX schema. "
1794 : "Use GPX_USE_EXTENSIONS creation option to allow use of the <extensions> element.",
1795 0 : poField->GetNameRef());
1796 0 : return OGRERR_FAILURE;
1797 : }
1798 : else
1799 : {
1800 3 : poFeatureDefn->AddFieldDefn( poField );
1801 3 : return OGRERR_NONE;
1802 : }
1803 : }
1804 :
1805 : /************************************************************************/
1806 : /* TestCapability() */
1807 : /************************************************************************/
1808 :
1809 0 : int OGRGPXLayer::TestCapability( const char * pszCap )
1810 :
1811 : {
1812 0 : if( EQUAL(pszCap,OLCSequentialWrite) )
1813 0 : return bWriteMode;
1814 :
1815 0 : else if( EQUAL(pszCap,OLCStringsAsUTF8) )
1816 0 : return TRUE;
1817 :
1818 : else
1819 0 : return FALSE;
1820 : }
1821 :
1822 :
1823 : /************************************************************************/
1824 : /* LoadExtensionsSchema() */
1825 : /************************************************************************/
1826 :
1827 : #ifdef HAVE_EXPAT
1828 :
1829 555 : static void XMLCALL startElementLoadSchemaCbk(void *pUserData, const char *pszName, const char **ppszAttr)
1830 : {
1831 555 : ((OGRGPXLayer*)pUserData)->startElementLoadSchemaCbk(pszName, ppszAttr);
1832 555 : }
1833 :
1834 555 : static void XMLCALL endElementLoadSchemaCbk(void *pUserData, const char *pszName)
1835 : {
1836 555 : ((OGRGPXLayer*)pUserData)->endElementLoadSchemaCbk(pszName);
1837 555 : }
1838 :
1839 1550 : static void XMLCALL dataHandlerLoadSchemaCbk(void *pUserData, const char *data, int nLen)
1840 : {
1841 1550 : ((OGRGPXLayer*)pUserData)->dataHandlerLoadSchemaCbk(data, nLen);
1842 1550 : }
1843 :
1844 :
1845 : /** This function parses the whole file to detect the extensions fields */
1846 15 : void OGRGPXLayer::LoadExtensionsSchema()
1847 : {
1848 15 : oSchemaParser = OGRCreateExpatXMLParser();
1849 15 : XML_SetElementHandler(oSchemaParser, ::startElementLoadSchemaCbk, ::endElementLoadSchemaCbk);
1850 15 : XML_SetCharacterDataHandler(oSchemaParser, ::dataHandlerLoadSchemaCbk);
1851 15 : XML_SetUserData(oSchemaParser, this);
1852 :
1853 15 : VSIFSeekL( fpGPX, 0, SEEK_SET );
1854 :
1855 15 : inInterestingElement = FALSE;
1856 15 : inExtensions = FALSE;
1857 15 : depthLevel = 0;
1858 15 : currentFieldDefn = NULL;
1859 15 : pszSubElementName = NULL;
1860 15 : pszSubElementValue = NULL;
1861 15 : nSubElementValueLen = 0;
1862 15 : nWithoutEventCounter = 0;
1863 15 : bStopParsing = FALSE;
1864 :
1865 : char aBuf[BUFSIZ];
1866 : int nDone;
1867 15 : do
1868 : {
1869 15 : nDataHandlerCounter = 0;
1870 15 : unsigned int nLen = (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpGPX );
1871 15 : nDone = VSIFEofL(fpGPX);
1872 15 : if (XML_Parse(oSchemaParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
1873 : {
1874 : CPLError(CE_Failure, CPLE_AppDefined,
1875 : "XML parsing of GPX file failed : %s at line %d, column %d",
1876 : XML_ErrorString(XML_GetErrorCode(oSchemaParser)),
1877 : (int)XML_GetCurrentLineNumber(oSchemaParser),
1878 0 : (int)XML_GetCurrentColumnNumber(oSchemaParser));
1879 0 : bStopParsing = TRUE;
1880 0 : break;
1881 : }
1882 15 : nWithoutEventCounter ++;
1883 : } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
1884 :
1885 15 : if (nWithoutEventCounter == 10)
1886 : {
1887 : CPLError(CE_Failure, CPLE_AppDefined,
1888 0 : "Too much data inside one element. File probably corrupted");
1889 0 : bStopParsing = TRUE;
1890 : }
1891 :
1892 15 : XML_ParserFree(oSchemaParser);
1893 15 : oSchemaParser = NULL;
1894 :
1895 15 : VSIFSeekL( fpGPX, 0, SEEK_SET );
1896 15 : }
1897 :
1898 :
1899 : /************************************************************************/
1900 : /* startElementLoadSchemaCbk() */
1901 : /************************************************************************/
1902 :
1903 :
1904 555 : void OGRGPXLayer::startElementLoadSchemaCbk(const char *pszName, const char **ppszAttr)
1905 : {
1906 555 : if (bStopParsing) return;
1907 :
1908 555 : nWithoutEventCounter = 0;
1909 :
1910 561 : if (gpxGeomType == GPX_WPT && strcmp(pszName, "wpt") == 0)
1911 : {
1912 6 : inInterestingElement = TRUE;
1913 6 : inExtensions = FALSE;
1914 6 : interestingDepthLevel = depthLevel;
1915 : }
1916 555 : else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0)
1917 : {
1918 6 : inInterestingElement = TRUE;
1919 6 : inExtensions = FALSE;
1920 6 : interestingDepthLevel = depthLevel;
1921 : }
1922 547 : else if (gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0)
1923 : {
1924 4 : inInterestingElement = TRUE;
1925 4 : inExtensions = FALSE;
1926 4 : interestingDepthLevel = depthLevel;
1927 : }
1928 547 : else if (gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkpt") == 0)
1929 : {
1930 8 : inInterestingElement = TRUE;
1931 8 : inExtensions = FALSE;
1932 8 : interestingDepthLevel = depthLevel;
1933 : }
1934 537 : else if (gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rtept") == 0)
1935 : {
1936 6 : inInterestingElement = TRUE;
1937 6 : inExtensions = FALSE;
1938 6 : interestingDepthLevel = depthLevel;
1939 : }
1940 525 : else if (inInterestingElement)
1941 : {
1942 114 : if (depthLevel == interestingDepthLevel + 1 &&
1943 : strcmp(pszName, "extensions") == 0)
1944 : {
1945 4 : inExtensions = TRUE;
1946 4 : extensionsDepthLevel = depthLevel;
1947 : }
1948 106 : else if (inExtensions && depthLevel == extensionsDepthLevel + 1)
1949 : {
1950 6 : CPLFree(pszSubElementName);
1951 6 : pszSubElementName = CPLStrdup(pszName);
1952 :
1953 : int iField;
1954 150 : for(iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ )
1955 : {
1956 : int bMatch;
1957 147 : if (iField >= nGPXFields)
1958 : {
1959 9 : char* pszCompatibleName = OGRGPX_GetOGRCompatibleTagName(pszName);
1960 9 : bMatch = (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), pszCompatibleName ) == 0);
1961 9 : CPLFree(pszCompatibleName);
1962 : }
1963 : else
1964 138 : bMatch = (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), pszName ) == 0);
1965 :
1966 147 : if (bMatch)
1967 : {
1968 3 : currentFieldDefn = poFeatureDefn->GetFieldDefn(iField);
1969 3 : break;
1970 : }
1971 : }
1972 6 : if (iField == poFeatureDefn->GetFieldCount())
1973 : {
1974 3 : char* pszCompatibleName = OGRGPX_GetOGRCompatibleTagName(pszName);
1975 3 : OGRFieldDefn newFieldDefn(pszCompatibleName, OFTInteger);
1976 3 : CPLFree(pszCompatibleName);
1977 :
1978 3 : poFeatureDefn->AddFieldDefn(&newFieldDefn);
1979 3 : currentFieldDefn = poFeatureDefn->GetFieldDefn(poFeatureDefn->GetFieldCount() - 1);
1980 :
1981 3 : if (poFeatureDefn->GetFieldCount() == 100)
1982 : {
1983 : CPLError(CE_Failure, CPLE_AppDefined,
1984 0 : "Too many fields. File probably corrupted");
1985 0 : XML_StopParser(oSchemaParser, XML_FALSE);
1986 0 : bStopParsing = TRUE;
1987 3 : }
1988 : }
1989 : }
1990 : }
1991 :
1992 555 : depthLevel++;
1993 : }
1994 :
1995 :
1996 : /************************************************************************/
1997 : /* endElementLoadSchemaCbk() */
1998 : /************************************************************************/
1999 :
2000 0 : static int OGRGPXIsInt(const char* pszStr)
2001 : {
2002 : int i;
2003 :
2004 0 : while(*pszStr == ' ')
2005 0 : pszStr++;
2006 :
2007 0 : for(i=0;pszStr[i];i++)
2008 : {
2009 0 : if (pszStr[i] == '+' || pszStr[i] == '-')
2010 : {
2011 0 : if (i != 0)
2012 0 : return FALSE;
2013 : }
2014 0 : else if (!(pszStr[i] >= '0' && pszStr[i] <= '9'))
2015 0 : return FALSE;
2016 : }
2017 0 : return TRUE;
2018 : }
2019 :
2020 :
2021 555 : void OGRGPXLayer::endElementLoadSchemaCbk(const char *pszName)
2022 : {
2023 555 : if (bStopParsing) return;
2024 :
2025 555 : nWithoutEventCounter = 0;
2026 :
2027 555 : depthLevel--;
2028 :
2029 555 : if (inInterestingElement)
2030 : {
2031 146 : if (gpxGeomType == GPX_WPT && strcmp(pszName, "wpt") == 0)
2032 : {
2033 6 : inInterestingElement = FALSE;
2034 6 : inExtensions = FALSE;
2035 : }
2036 140 : else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0)
2037 : {
2038 6 : inInterestingElement = FALSE;
2039 6 : inExtensions = FALSE;
2040 : }
2041 132 : else if (gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0)
2042 : {
2043 4 : inInterestingElement = FALSE;
2044 4 : inExtensions = FALSE;
2045 : }
2046 132 : else if (gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkpt") == 0)
2047 : {
2048 8 : inInterestingElement = FALSE;
2049 8 : inExtensions = FALSE;
2050 : }
2051 122 : else if (gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rtept") == 0)
2052 : {
2053 6 : inInterestingElement = FALSE;
2054 6 : inExtensions = FALSE;
2055 : }
2056 114 : else if (depthLevel == interestingDepthLevel + 1 &&
2057 : strcmp(pszName, "extensions") == 0)
2058 : {
2059 4 : inExtensions = FALSE;
2060 : }
2061 106 : else if (inExtensions && depthLevel == extensionsDepthLevel + 1 &&
2062 : pszSubElementName && strcmp(pszName, pszSubElementName) == 0)
2063 : {
2064 6 : if (pszSubElementValue && nSubElementValueLen && currentFieldDefn)
2065 : {
2066 5 : pszSubElementValue[nSubElementValueLen] = 0;
2067 5 : if (currentFieldDefn->GetType() == OFTInteger ||
2068 : currentFieldDefn->GetType() == OFTReal)
2069 : {
2070 3 : char* pszRemainingStr = NULL;
2071 3 : CPLStrtod(pszSubElementValue, &pszRemainingStr);
2072 3 : if (pszRemainingStr == NULL ||
2073 : *pszRemainingStr == 0 ||
2074 : *pszRemainingStr == ' ')
2075 : {
2076 0 : if (currentFieldDefn->GetType() == OFTInteger)
2077 : {
2078 0 : if (OGRGPXIsInt(pszSubElementValue) == FALSE)
2079 : {
2080 0 : currentFieldDefn->SetType(OFTReal);
2081 : }
2082 : }
2083 : }
2084 : else
2085 : {
2086 3 : currentFieldDefn->SetType(OFTString);
2087 : }
2088 : }
2089 : }
2090 :
2091 6 : CPLFree(pszSubElementName);
2092 6 : pszSubElementName = NULL;
2093 6 : CPLFree(pszSubElementValue);
2094 6 : pszSubElementValue = NULL;
2095 6 : nSubElementValueLen = 0;
2096 6 : currentFieldDefn = NULL;
2097 : }
2098 : }
2099 : }
2100 :
2101 : /************************************************************************/
2102 : /* dataHandlerLoadSchemaCbk() */
2103 : /************************************************************************/
2104 :
2105 1550 : void OGRGPXLayer::dataHandlerLoadSchemaCbk(const char *data, int nLen)
2106 : {
2107 1550 : if (bStopParsing) return;
2108 :
2109 1550 : nDataHandlerCounter ++;
2110 1550 : if (nDataHandlerCounter >= BUFSIZ)
2111 : {
2112 0 : CPLError(CE_Failure, CPLE_AppDefined, "File probably corrupted (million laugh pattern)");
2113 0 : XML_StopParser(oSchemaParser, XML_FALSE);
2114 0 : bStopParsing = TRUE;
2115 0 : return;
2116 : }
2117 :
2118 1550 : nWithoutEventCounter = 0;
2119 :
2120 1550 : if (pszSubElementName)
2121 : {
2122 5 : char* pszNewSubElementValue = (char*) VSIRealloc(pszSubElementValue, nSubElementValueLen + nLen + 1);
2123 5 : if (pszNewSubElementValue == NULL)
2124 : {
2125 0 : CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
2126 0 : XML_StopParser(oSchemaParser, XML_FALSE);
2127 0 : bStopParsing = TRUE;
2128 0 : return;
2129 : }
2130 5 : pszSubElementValue = pszNewSubElementValue;
2131 5 : memcpy(pszSubElementValue + nSubElementValueLen, data, nLen);
2132 5 : nSubElementValueLen += nLen;
2133 5 : if (nSubElementValueLen > 100000)
2134 : {
2135 : CPLError(CE_Failure, CPLE_AppDefined,
2136 0 : "Too much data inside one element. File probably corrupted");
2137 0 : XML_StopParser(oSchemaParser, XML_FALSE);
2138 0 : bStopParsing = TRUE;
2139 : }
2140 : }
2141 : }
2142 : #else
2143 : void OGRGPXLayer::LoadExtensionsSchema()
2144 : {
2145 : }
2146 : #endif
|