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 74 : OGRGPXLayer::OGRGPXLayer( const char* pszFilename,
55 : const char* pszLayerName,
56 : GPXGeometryType gpxGeomType,
57 : OGRGPXDataSource* poDS,
58 74 : int bWriteMode)
59 :
60 : {
61 74 : const char* gpxVersion = poDS->GetVersion();
62 :
63 : int i;
64 :
65 74 : eof = FALSE;
66 74 : nNextFID = 0;
67 :
68 74 : this->poDS = poDS;
69 74 : this->bWriteMode = bWriteMode;
70 74 : this->gpxGeomType = gpxGeomType;
71 :
72 74 : pszElementToScan = pszLayerName;
73 :
74 74 : nMaxLinks = atoi(CPLGetConfigOption("GPX_N_MAX_LINKS", "2"));
75 74 : if (nMaxLinks < 0)
76 0 : nMaxLinks = 2;
77 74 : if (nMaxLinks > 100)
78 0 : nMaxLinks = 100;
79 :
80 74 : nFeatures = 0;
81 :
82 74 : bEleAs25D = CSLTestBoolean(CPLGetConfigOption("GPX_ELE_AS_25D", "NO"));
83 :
84 74 : int bShortNames = CSLTestBoolean(CPLGetConfigOption("GPX_SHORT_NAMES", "NO"));
85 :
86 74 : poFeatureDefn = new OGRFeatureDefn( pszLayerName );
87 74 : poFeatureDefn->Reference();
88 :
89 74 : 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 16 : OGRFieldDefn oFieldTrackFID("track_fid", OFTInteger );
94 16 : poFeatureDefn->AddFieldDefn( &oFieldTrackFID );
95 :
96 16 : OGRFieldDefn oFieldTrackSegID((bShortNames) ? "trksegid" : "track_seg_id", OFTInteger );
97 16 : poFeatureDefn->AddFieldDefn( &oFieldTrackSegID );
98 :
99 16 : OGRFieldDefn oFieldTrackSegPointID((bShortNames) ? "trksegptid" : "track_seg_point_id", OFTInteger );
100 16 : poFeatureDefn->AddFieldDefn( &oFieldTrackSegPointID );
101 :
102 16 : if (bWriteMode)
103 : {
104 4 : OGRFieldDefn oFieldName("track_name", OFTString );
105 4 : poFeatureDefn->AddFieldDefn( &oFieldName );
106 16 : }
107 : }
108 58 : else if (gpxGeomType == GPX_ROUTE_POINT)
109 : {
110 : /* Don't move this code. See above */
111 14 : OGRFieldDefn oFieldRouteFID("route_fid", OFTInteger );
112 14 : poFeatureDefn->AddFieldDefn( &oFieldRouteFID );
113 :
114 14 : OGRFieldDefn oFieldRoutePointID((bShortNames) ? "rteptid" : "route_point_id", OFTInteger );
115 14 : poFeatureDefn->AddFieldDefn( &oFieldRoutePointID );
116 :
117 14 : if (bWriteMode)
118 : {
119 2 : OGRFieldDefn oFieldName("route_name", OFTString );
120 2 : poFeatureDefn->AddFieldDefn( &oFieldName );
121 14 : }
122 : }
123 :
124 74 : iFirstGPXField = poFeatureDefn->GetFieldCount();
125 :
126 120 : if (gpxGeomType == GPX_WPT ||
127 : gpxGeomType == GPX_TRACK_POINT ||
128 : gpxGeomType == GPX_ROUTE_POINT)
129 : {
130 46 : poFeatureDefn->SetGeomType((bEleAs25D) ? wkbPoint25D : wkbPoint);
131 : /* Position info */
132 :
133 46 : OGRFieldDefn oFieldEle("ele", OFTReal );
134 46 : poFeatureDefn->AddFieldDefn( &oFieldEle );
135 :
136 46 : OGRFieldDefn oFieldTime("time", OFTDateTime );
137 46 : poFeatureDefn->AddFieldDefn( &oFieldTime );
138 :
139 46 : 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 46 : OGRFieldDefn oFieldMagVar("magvar", OFTReal );
150 46 : poFeatureDefn->AddFieldDefn( &oFieldMagVar );
151 :
152 46 : OGRFieldDefn oFieldGeoidHeight("geoidheight", OFTReal );
153 46 : poFeatureDefn->AddFieldDefn( &oFieldGeoidHeight );
154 :
155 : /* Description info */
156 :
157 46 : OGRFieldDefn oFieldName("name", OFTString );
158 46 : poFeatureDefn->AddFieldDefn( &oFieldName );
159 :
160 46 : OGRFieldDefn oFieldCmt("cmt", OFTString );
161 46 : poFeatureDefn->AddFieldDefn( &oFieldCmt );
162 :
163 46 : OGRFieldDefn oFieldDesc("desc", OFTString );
164 46 : poFeatureDefn->AddFieldDefn( &oFieldDesc );
165 :
166 46 : OGRFieldDefn oFieldSrc("src", OFTString );
167 46 : poFeatureDefn->AddFieldDefn( &oFieldSrc );
168 :
169 46 : 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 138 : for(i=1;i<=nMaxLinks;i++)
180 : {
181 : char szFieldName[32];
182 92 : sprintf(szFieldName, "link%d_href", i);
183 92 : OGRFieldDefn oFieldLinkHref( szFieldName, OFTString );
184 92 : poFeatureDefn->AddFieldDefn( &oFieldLinkHref );
185 :
186 92 : sprintf(szFieldName, "link%d_text", i);
187 92 : OGRFieldDefn oFieldLinkText( szFieldName, OFTString );
188 92 : poFeatureDefn->AddFieldDefn( &oFieldLinkText );
189 :
190 92 : sprintf(szFieldName, "link%d_type", i);
191 92 : OGRFieldDefn oFieldLinkType( szFieldName, OFTString );
192 92 : poFeatureDefn->AddFieldDefn( &oFieldLinkType );
193 : }
194 : }
195 :
196 46 : OGRFieldDefn oFieldSym("sym", OFTString );
197 46 : poFeatureDefn->AddFieldDefn( &oFieldSym );
198 :
199 46 : OGRFieldDefn oFieldType("type", OFTString );
200 46 : poFeatureDefn->AddFieldDefn( &oFieldType );
201 :
202 : /* Accuracy info */
203 :
204 46 : OGRFieldDefn oFieldFix("fix", OFTString );
205 46 : poFeatureDefn->AddFieldDefn( &oFieldFix );
206 :
207 46 : OGRFieldDefn oFieldSat("sat", OFTInteger );
208 46 : poFeatureDefn->AddFieldDefn( &oFieldSat );
209 :
210 46 : OGRFieldDefn oFieldHdop("hdop", OFTReal );
211 46 : poFeatureDefn->AddFieldDefn( &oFieldHdop );
212 :
213 46 : OGRFieldDefn oFieldVdop("vdop", OFTReal );
214 46 : poFeatureDefn->AddFieldDefn( &oFieldVdop );
215 :
216 46 : OGRFieldDefn oFieldPdop("pdop", OFTReal );
217 46 : poFeatureDefn->AddFieldDefn( &oFieldPdop );
218 :
219 46 : OGRFieldDefn oFieldAgeofgpsdata("ageofdgpsdata", OFTReal );
220 46 : poFeatureDefn->AddFieldDefn( &oFieldAgeofgpsdata );
221 :
222 46 : OGRFieldDefn oFieldDgpsid("dgpsid", OFTInteger );
223 46 : poFeatureDefn->AddFieldDefn( &oFieldDgpsid );
224 : }
225 : else
226 : {
227 28 : if (gpxGeomType == GPX_TRACK)
228 14 : poFeatureDefn->SetGeomType((bEleAs25D) ? wkbMultiLineString25D : wkbMultiLineString);
229 : else
230 14 : poFeatureDefn->SetGeomType((bEleAs25D) ? wkbLineString25D : wkbLineString);
231 :
232 28 : OGRFieldDefn oFieldName("name", OFTString );
233 28 : poFeatureDefn->AddFieldDefn( &oFieldName );
234 :
235 28 : OGRFieldDefn oFieldCmt("cmt", OFTString );
236 28 : poFeatureDefn->AddFieldDefn( &oFieldCmt );
237 :
238 28 : OGRFieldDefn oFieldDesc("desc", OFTString );
239 28 : poFeatureDefn->AddFieldDefn( &oFieldDesc );
240 :
241 28 : OGRFieldDefn oFieldSrc("src", OFTString );
242 28 : poFeatureDefn->AddFieldDefn( &oFieldSrc );
243 :
244 84 : for(i=1;i<=nMaxLinks;i++)
245 : {
246 : char szFieldName[32];
247 56 : sprintf(szFieldName, "link%d_href", i);
248 56 : OGRFieldDefn oFieldLinkHref( szFieldName, OFTString );
249 56 : poFeatureDefn->AddFieldDefn( &oFieldLinkHref );
250 :
251 56 : sprintf(szFieldName, "link%d_text", i);
252 56 : OGRFieldDefn oFieldLinkText( szFieldName, OFTString );
253 56 : poFeatureDefn->AddFieldDefn( &oFieldLinkText );
254 :
255 56 : sprintf(szFieldName, "link%d_type", i);
256 56 : OGRFieldDefn oFieldLinkType( szFieldName, OFTString );
257 56 : poFeatureDefn->AddFieldDefn( &oFieldLinkType );
258 : }
259 :
260 28 : OGRFieldDefn oFieldNumber("number", OFTInteger );
261 28 : poFeatureDefn->AddFieldDefn( &oFieldNumber );
262 :
263 28 : OGRFieldDefn oFieldType("type", OFTString );
264 28 : poFeatureDefn->AddFieldDefn( &oFieldType );
265 : }
266 :
267 : /* Number of 'standard' GPX attributes */
268 74 : nGPXFields = poFeatureDefn->GetFieldCount();
269 :
270 74 : ppoFeatureTab = NULL;
271 74 : nFeatureTabIndex = 0;
272 74 : nFeatureTabLength = 0;
273 74 : pszSubElementName = NULL;
274 74 : pszSubElementValue = NULL;
275 74 : nSubElementValueLen = 0;
276 74 : 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 74 : " AUTHORITY[\"EPSG\",\"4326\"]]");
288 :
289 74 : poFeature = NULL;
290 :
291 : #ifdef HAVE_EXPAT
292 74 : oParser = NULL;
293 : #endif
294 :
295 74 : if (bWriteMode == FALSE)
296 : {
297 60 : fpGPX = VSIFOpenL( pszFilename, "r" );
298 60 : if( fpGPX == NULL )
299 : {
300 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot open %s", pszFilename);
301 0 : return;
302 : }
303 :
304 60 : if (poDS->GetUseExtensions() ||
305 : CSLTestBoolean(CPLGetConfigOption("GPX_USE_EXTENSIONS", "FALSE")))
306 : {
307 30 : LoadExtensionsSchema();
308 : }
309 : }
310 : else
311 14 : fpGPX = NULL;
312 :
313 74 : ResetReading();
314 0 : }
315 :
316 : /************************************************************************/
317 : /* ~OGRGPXLayer() */
318 : /************************************************************************/
319 :
320 74 : OGRGPXLayer::~OGRGPXLayer()
321 :
322 : {
323 : #ifdef HAVE_EXPAT
324 74 : if (oParser)
325 60 : XML_ParserFree(oParser);
326 : #endif
327 74 : poFeatureDefn->Release();
328 :
329 74 : if( poSRS != NULL )
330 74 : poSRS->Release();
331 :
332 74 : CPLFree(pszSubElementName);
333 74 : CPLFree(pszSubElementValue);
334 :
335 : int i;
336 84 : for(i=nFeatureTabIndex;i<nFeatureTabLength;i++)
337 10 : delete ppoFeatureTab[i];
338 74 : CPLFree(ppoFeatureTab);
339 :
340 74 : if (poFeature)
341 0 : delete poFeature;
342 :
343 74 : if (fpGPX)
344 60 : VSIFCloseL( fpGPX );
345 74 : }
346 :
347 : #ifdef HAVE_EXPAT
348 :
349 3108 : static void XMLCALL startElementCbk(void *pUserData, const char *pszName, const char **ppszAttr)
350 : {
351 3108 : ((OGRGPXLayer*)pUserData)->startElementCbk(pszName, ppszAttr);
352 3108 : }
353 :
354 3108 : static void XMLCALL endElementCbk(void *pUserData, const char *pszName)
355 : {
356 3108 : ((OGRGPXLayer*)pUserData)->endElementCbk(pszName);
357 3108 : }
358 :
359 8466 : static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen)
360 : {
361 8466 : ((OGRGPXLayer*)pUserData)->dataHandlerCbk(data, nLen);
362 8466 : }
363 :
364 : #endif
365 :
366 : /************************************************************************/
367 : /* ResetReading() */
368 : /************************************************************************/
369 :
370 172 : void OGRGPXLayer::ResetReading()
371 :
372 : {
373 172 : eof = FALSE;
374 172 : nNextFID = 0;
375 172 : if (fpGPX)
376 : {
377 158 : VSIFSeekL( fpGPX, 0, SEEK_SET );
378 : #ifdef HAVE_EXPAT
379 158 : if (oParser)
380 98 : XML_ParserFree(oParser);
381 :
382 158 : oParser = OGRCreateExpatXMLParser();
383 158 : XML_SetElementHandler(oParser, ::startElementCbk, ::endElementCbk);
384 158 : XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk);
385 158 : XML_SetUserData(oParser, this);
386 : #endif
387 : }
388 172 : hasFoundLat = FALSE;
389 172 : hasFoundLon = FALSE;
390 172 : inInterestingElement = FALSE;
391 172 : CPLFree(pszSubElementName);
392 172 : pszSubElementName = NULL;
393 172 : CPLFree(pszSubElementValue);
394 172 : pszSubElementValue = NULL;
395 172 : nSubElementValueLen = 0;
396 :
397 : int i;
398 172 : for(i=nFeatureTabIndex;i<nFeatureTabLength;i++)
399 0 : delete ppoFeatureTab[i];
400 172 : CPLFree(ppoFeatureTab);
401 172 : nFeatureTabIndex = 0;
402 172 : nFeatureTabLength = 0;
403 172 : ppoFeatureTab = NULL;
404 172 : if (poFeature)
405 0 : delete poFeature;
406 172 : poFeature = NULL;
407 172 : multiLineString = NULL;
408 172 : lineString = NULL;
409 :
410 172 : depthLevel = 0;
411 172 : interestingDepthLevel = 0;
412 :
413 172 : trkFID = trkSegId = trkSegPtId = 0;
414 172 : rteFID = rtePtId = 0;
415 172 : }
416 :
417 : #ifdef HAVE_EXPAT
418 :
419 : /************************************************************************/
420 : /* startElementCbk() */
421 : /************************************************************************/
422 :
423 : /** Replace ':' from XML NS element name by '_' more OGR friendly */
424 96 : static char* OGRGPX_GetOGRCompatibleTagName(const char* pszName)
425 : {
426 96 : char* pszModName = CPLStrdup(pszName);
427 : int i;
428 1408 : for(i=0;pszModName[i] != 0;i++)
429 : {
430 1312 : if (pszModName[i] == ':')
431 96 : pszModName[i] = '_';
432 : }
433 96 : 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 3108 : void OGRGPXLayer::startElementCbk(const char *pszName, const char **ppszAttr)
454 : {
455 : int i;
456 :
457 3108 : if (bStopParsing) return;
458 :
459 3108 : nWithoutEventCounter = 0;
460 :
461 3240 : 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 132 : interestingDepthLevel = depthLevel;
466 :
467 132 : if (poFeature)
468 0 : delete poFeature;
469 :
470 132 : poFeature = new OGRFeature( poFeatureDefn );
471 132 : inInterestingElement = TRUE;
472 132 : hasFoundLat = FALSE;
473 132 : hasFoundLon = FALSE;
474 132 : inExtensions = FALSE;
475 132 : inLink = FALSE;
476 132 : iCountLink = 0;
477 :
478 396 : for (i = 0; ppszAttr[i]; i += 2)
479 : {
480 264 : if (strcmp(ppszAttr[i], "lat") == 0)
481 : {
482 132 : hasFoundLat = TRUE;
483 132 : latVal = CPLAtof(ppszAttr[i + 1]);
484 : }
485 132 : else if (strcmp(ppszAttr[i], "lon") == 0)
486 : {
487 132 : hasFoundLon = TRUE;
488 132 : lonVal = CPLAtof(ppszAttr[i + 1]);
489 : }
490 : }
491 :
492 132 : if (hasFoundLat && hasFoundLon)
493 : {
494 132 : poFeature->SetFID( nNextFID++ );
495 132 : poFeature->SetGeometryDirectly( new OGRPoint( lonVal, latVal ) );
496 :
497 132 : if (gpxGeomType == GPX_ROUTE_POINT)
498 : {
499 12 : rtePtId++;
500 12 : poFeature->SetField( FLD_ROUTE_FID, rteFID-1);
501 12 : poFeature->SetField( FLD_ROUTE_PT_ID, rtePtId-1);
502 : }
503 120 : else if (gpxGeomType == GPX_TRACK_POINT)
504 : {
505 16 : trkSegPtId++;
506 :
507 16 : poFeature->SetField( FLD_TRACK_FID, trkFID-1);
508 16 : poFeature->SetField( FLD_TRACK_SEG_ID, trkSegId-1);
509 16 : poFeature->SetField( FLD_TRACK_PT_ID, trkSegPtId-1);
510 : }
511 : }
512 : }
513 2998 : else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0)
514 : {
515 22 : interestingDepthLevel = depthLevel;
516 :
517 22 : if (poFeature)
518 0 : delete poFeature;
519 22 : inExtensions = FALSE;
520 22 : inLink = FALSE;
521 22 : iCountLink = 0;
522 22 : poFeature = new OGRFeature( poFeatureDefn );
523 22 : inInterestingElement = TRUE;
524 :
525 44 : multiLineString = new OGRMultiLineString ();
526 22 : lineString = NULL;
527 :
528 22 : poFeature->SetFID( nNextFID++ );
529 22 : poFeature->SetGeometryDirectly( multiLineString );
530 : }
531 2970 : else if (gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trk") == 0)
532 : {
533 16 : trkFID++;
534 16 : trkSegId = 0;
535 : }
536 2954 : else if (gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkseg") == 0)
537 : {
538 16 : trkSegId++;
539 16 : trkSegPtId = 0;
540 : }
541 2934 : else if (gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0)
542 : {
543 12 : interestingDepthLevel = depthLevel;
544 :
545 12 : if (poFeature)
546 0 : delete poFeature;
547 :
548 12 : poFeature = new OGRFeature( poFeatureDefn );
549 12 : inInterestingElement = TRUE;
550 12 : inExtensions = FALSE;
551 12 : inLink = FALSE;
552 12 : iCountLink = 0;
553 :
554 24 : lineString = new OGRLineString ();
555 12 : poFeature->SetFID( nNextFID++ );
556 12 : poFeature->SetGeometryDirectly( lineString );
557 : }
558 2918 : else if (gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rte") == 0)
559 : {
560 8 : rteFID++;
561 8 : rtePtId = 0;
562 : }
563 2902 : else if (inInterestingElement)
564 : {
565 860 : if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trkseg") == 0 &&
566 : depthLevel == interestingDepthLevel + 1)
567 : {
568 22 : if (multiLineString)
569 : {
570 22 : lineString = new OGRLineString ();
571 22 : multiLineString->addGeometryDirectly( lineString );
572 : }
573 : }
574 840 : else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trkpt") == 0 &&
575 : depthLevel == interestingDepthLevel + 2)
576 : {
577 24 : if (lineString)
578 : {
579 24 : hasFoundLat = FALSE;
580 24 : hasFoundLon = FALSE;
581 72 : for (i = 0; ppszAttr[i]; i += 2)
582 : {
583 48 : if (strcmp(ppszAttr[i], "lat") == 0)
584 : {
585 24 : hasFoundLat = TRUE;
586 24 : latVal = CPLAtof(ppszAttr[i + 1]);
587 : }
588 24 : else if (strcmp(ppszAttr[i], "lon") == 0)
589 : {
590 24 : hasFoundLon = TRUE;
591 24 : lonVal = CPLAtof(ppszAttr[i + 1]);
592 : }
593 : }
594 :
595 24 : if (hasFoundLat && hasFoundLon)
596 : {
597 24 : lineString->addPoint(lonVal, latVal);
598 : }
599 : }
600 : }
601 810 : else if (gpxGeomType == GPX_ROUTE && strcmp(pszName, "rtept") == 0 &&
602 : depthLevel == interestingDepthLevel + 1)
603 : {
604 18 : if (lineString)
605 : {
606 18 : hasFoundLat = FALSE;
607 18 : hasFoundLon = FALSE;
608 54 : for (i = 0; ppszAttr[i]; i += 2)
609 : {
610 36 : if (strcmp(ppszAttr[i], "lat") == 0)
611 : {
612 18 : hasFoundLat = TRUE;
613 18 : latVal = CPLAtof(ppszAttr[i + 1]);
614 : }
615 18 : else if (strcmp(ppszAttr[i], "lon") == 0)
616 : {
617 18 : hasFoundLon = TRUE;
618 18 : lonVal = CPLAtof(ppszAttr[i + 1]);
619 : }
620 : }
621 :
622 18 : if (hasFoundLat && hasFoundLon)
623 : {
624 18 : lineString->addPoint(lonVal, latVal);
625 : }
626 : }
627 : }
628 774 : 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 808 : else if (depthLevel == interestingDepthLevel + 1 &&
638 : strcmp(pszName, "extensions") == 0)
639 : {
640 34 : if (poDS->GetUseExtensions())
641 : {
642 34 : inExtensions = TRUE;
643 : }
644 : }
645 1228 : else if (depthLevel == interestingDepthLevel + 1 ||
646 : (inExtensions && depthLevel == interestingDepthLevel + 2) )
647 : {
648 488 : CPLFree(pszSubElementName);
649 488 : pszSubElementName = NULL;
650 488 : iCurrentField = -1;
651 :
652 488 : if (strcmp(pszName, "link") == 0)
653 : {
654 106 : iCountLink++;
655 106 : if (iCountLink <= nMaxLinks)
656 : {
657 84 : if (ppszAttr[0] && ppszAttr[1] &&
658 : strcmp(ppszAttr[0], "href") == 0)
659 : {
660 : char szFieldName[32];
661 84 : sprintf(szFieldName, "link%d_href", iCountLink);
662 84 : iCurrentField = poFeatureDefn->GetFieldIndex(szFieldName);
663 84 : poFeature->SetField( iCurrentField, ppszAttr[1]);
664 : }
665 : }
666 : else
667 : {
668 : static int once = 1;
669 22 : if (once)
670 : {
671 2 : 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 2 : nMaxLinks);
676 : }
677 : }
678 106 : inLink = TRUE;
679 106 : iCurrentField = -1;
680 : }
681 : else
682 : {
683 2468 : for( int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ )
684 : {
685 : int bMatch;
686 2468 : if (iField >= nGPXFields)
687 : {
688 72 : char* pszCompatibleName = OGRGPX_GetOGRCompatibleTagName(pszName);
689 : bMatch = (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
690 72 : pszCompatibleName ) == 0);
691 72 : CPLFree(pszCompatibleName);
692 : }
693 : else
694 : bMatch = (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
695 2396 : pszName ) == 0);
696 :
697 2468 : if (bMatch)
698 : {
699 382 : iCurrentField = iField;
700 382 : pszSubElementName = CPLStrdup(pszName);
701 382 : break;
702 : }
703 : }
704 : }
705 : }
706 464 : else if (depthLevel == interestingDepthLevel + 2 && inLink)
707 : {
708 : char szFieldName[32];
709 212 : CPLFree(pszSubElementName);
710 212 : pszSubElementName = NULL;
711 212 : iCurrentField = -1;
712 212 : if (iCountLink <= nMaxLinks)
713 : {
714 168 : if (strcmp(pszName, "type") == 0)
715 : {
716 84 : sprintf(szFieldName, "link%d_type", iCountLink);
717 84 : iCurrentField = poFeatureDefn->GetFieldIndex(szFieldName);
718 84 : pszSubElementName = CPLStrdup(pszName);
719 : }
720 84 : else if (strcmp(pszName, "text") == 0)
721 : {
722 84 : sprintf(szFieldName, "link%d_text", iCountLink);
723 84 : iCurrentField = poFeatureDefn->GetFieldIndex(szFieldName);
724 84 : pszSubElementName = CPLStrdup(pszName);
725 : }
726 : }
727 : }
728 40 : 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 3108 : depthLevel++;
747 : }
748 :
749 : /************************************************************************/
750 : /* endElementCbk() */
751 : /************************************************************************/
752 :
753 3108 : void OGRGPXLayer::endElementCbk(const char *pszName)
754 : {
755 3108 : if (bStopParsing) return;
756 :
757 3108 : nWithoutEventCounter = 0;
758 :
759 3108 : depthLevel--;
760 :
761 3108 : if (inInterestingElement)
762 : {
763 1136 : 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 132 : int bIsValid = (hasFoundLat && hasFoundLon);
768 132 : inInterestingElement = FALSE;
769 :
770 132 : if( bIsValid
771 : && (m_poFilterGeom == NULL
772 : || FilterGeometry( poFeature->GetGeometryRef() ) )
773 : && (m_poAttrQuery == NULL
774 : || m_poAttrQuery->Evaluate( poFeature )) )
775 : {
776 132 : if( poFeature->GetGeometryRef() != NULL )
777 : {
778 132 : poFeature->GetGeometryRef()->assignSpatialReference( poSRS );
779 :
780 132 : 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 132 : sizeof(OGRFeature*) * (nFeatureTabLength + 1));
801 132 : ppoFeatureTab[nFeatureTabLength] = poFeature;
802 132 : nFeatureTabLength++;
803 : }
804 : else
805 : {
806 0 : delete poFeature;
807 : }
808 132 : poFeature = NULL;
809 : }
810 894 : else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0)
811 : {
812 22 : inInterestingElement = FALSE;
813 22 : if( (m_poFilterGeom == NULL
814 : || FilterGeometry( poFeature->GetGeometryRef() ) )
815 : && (m_poAttrQuery == NULL
816 : || m_poAttrQuery->Evaluate( poFeature )) )
817 : {
818 22 : if( poFeature->GetGeometryRef() != NULL )
819 : {
820 22 : poFeature->GetGeometryRef()->assignSpatialReference( poSRS );
821 : }
822 :
823 : ppoFeatureTab = (OGRFeature**)
824 : CPLRealloc(ppoFeatureTab,
825 22 : sizeof(OGRFeature*) * (nFeatureTabLength + 1));
826 22 : ppoFeatureTab[nFeatureTabLength] = poFeature;
827 22 : nFeatureTabLength++;
828 : }
829 : else
830 : {
831 0 : delete poFeature;
832 : }
833 22 : poFeature = NULL;
834 22 : multiLineString = NULL;
835 22 : lineString = NULL;
836 : }
837 872 : else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trkseg") == 0 &&
838 : depthLevel == interestingDepthLevel + 1)
839 : {
840 22 : lineString = NULL;
841 : }
842 840 : else if (gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0)
843 : {
844 12 : inInterestingElement = FALSE;
845 12 : if( (m_poFilterGeom == NULL
846 : || FilterGeometry( poFeature->GetGeometryRef() ) )
847 : && (m_poAttrQuery == NULL
848 : || m_poAttrQuery->Evaluate( poFeature )) )
849 : {
850 12 : if( poFeature->GetGeometryRef() != NULL )
851 : {
852 12 : poFeature->GetGeometryRef()->assignSpatialReference( poSRS );
853 : }
854 :
855 : ppoFeatureTab = (OGRFeature**)
856 : CPLRealloc(ppoFeatureTab,
857 12 : sizeof(OGRFeature*) * (nFeatureTabLength + 1));
858 12 : ppoFeatureTab[nFeatureTabLength] = poFeature;
859 12 : nFeatureTabLength++;
860 : }
861 : else
862 : {
863 0 : delete poFeature;
864 : }
865 12 : poFeature = NULL;
866 12 : lineString = NULL;
867 : }
868 816 : 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 850 : else if (depthLevel == interestingDepthLevel + 1 &&
893 : strcmp(pszName, "extensions") == 0)
894 : {
895 34 : inExtensions = FALSE;
896 : }
897 1164 : else if ((depthLevel == interestingDepthLevel + 1 ||
898 : (inExtensions && depthLevel == interestingDepthLevel + 2) ) &&
899 : pszSubElementName && strcmp(pszName, pszSubElementName) == 0)
900 : {
901 382 : if (poFeature && pszSubElementValue && nSubElementValueLen)
902 : {
903 376 : pszSubElementValue[nSubElementValueLen] = 0;
904 376 : if (strcmp(pszSubElementName, "time") == 0)
905 : {
906 : int year, month, day, hour, minute, TZ;
907 : float second;
908 46 : if (OGRParseXMLDateTime(pszSubElementValue, &year, &month, &day, &hour, &minute, &second, &TZ))
909 : {
910 46 : 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 330 : poFeature->SetField( iCurrentField, pszSubElementValue);
921 : }
922 : }
923 382 : if (strcmp(pszName, "link") == 0)
924 0 : inLink = FALSE;
925 :
926 382 : CPLFree(pszSubElementName);
927 382 : pszSubElementName = NULL;
928 382 : CPLFree(pszSubElementValue);
929 382 : pszSubElementValue = NULL;
930 382 : nSubElementValueLen = 0;
931 : }
932 612 : else if (inLink && depthLevel == interestingDepthLevel + 2)
933 : {
934 212 : if (iCurrentField != -1 && pszSubElementName &&
935 : strcmp(pszName, pszSubElementName) == 0 && poFeature && pszSubElementValue && nSubElementValueLen)
936 : {
937 168 : pszSubElementValue[nSubElementValueLen] = 0;
938 168 : poFeature->SetField( iCurrentField, pszSubElementValue);
939 : }
940 :
941 212 : CPLFree(pszSubElementName);
942 212 : pszSubElementName = NULL;
943 212 : CPLFree(pszSubElementValue);
944 212 : pszSubElementValue = NULL;
945 212 : nSubElementValueLen = 0;
946 : }
947 188 : else if (inExtensions && depthLevel > interestingDepthLevel + 2)
948 : {
949 0 : AddStrToSubElementValue(CPLSPrintf("</%s>", pszName));
950 : }
951 : }
952 : }
953 :
954 : /************************************************************************/
955 : /* dataHandlerCbk() */
956 : /************************************************************************/
957 :
958 8466 : void OGRGPXLayer::dataHandlerCbk(const char *data, int nLen)
959 : {
960 8466 : if (bStopParsing) return;
961 :
962 8466 : nDataHandlerCounter ++;
963 8466 : 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 8466 : nWithoutEventCounter = 0;
972 :
973 8466 : if (pszSubElementName)
974 : {
975 544 : if (inExtensions && depthLevel > interestingDepthLevel + 2)
976 : {
977 30 : if (data[0] == '\n')
978 0 : return;
979 : }
980 544 : char* pszNewSubElementValue = (char*) VSIRealloc(pszSubElementValue, nSubElementValueLen + nLen + 1);
981 544 : 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 544 : pszSubElementValue = pszNewSubElementValue;
989 544 : memcpy(pszSubElementValue + nSubElementValueLen, data, nLen);
990 544 : nSubElementValueLen += nLen;
991 544 : 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 228 : OGRFeature *OGRGPXLayer::GetNextFeature()
1007 : {
1008 228 : 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 228 : if (fpGPX == NULL)
1016 0 : return NULL;
1017 :
1018 228 : if (bStopParsing)
1019 0 : return NULL;
1020 :
1021 : #ifdef HAVE_EXPAT
1022 228 : if (nFeatureTabIndex < nFeatureTabLength)
1023 : {
1024 80 : return ppoFeatureTab[nFeatureTabIndex++];
1025 : }
1026 :
1027 148 : if (VSIFEofL(fpGPX))
1028 60 : return NULL;
1029 :
1030 : char aBuf[BUFSIZ];
1031 :
1032 88 : CPLFree(ppoFeatureTab);
1033 88 : ppoFeatureTab = NULL;
1034 88 : nFeatureTabLength = 0;
1035 88 : nFeatureTabIndex = 0;
1036 88 : nWithoutEventCounter = 0;
1037 :
1038 : int nDone;
1039 88 : do
1040 : {
1041 88 : nDataHandlerCounter = 0;
1042 88 : unsigned int nLen = (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpGPX );
1043 88 : nDone = VSIFEofL(fpGPX);
1044 88 : 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 88 : nWithoutEventCounter ++;
1055 : } while (!nDone && nFeatureTabLength == 0 && !bStopParsing && nWithoutEventCounter < 10);
1056 :
1057 88 : 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 88 : 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 12 : 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 12 : if (strncmp(pszName, pszExtensionsNS, strlen(pszExtensionsNS)) == 0 &&
1089 0 : pszName[strlen(pszExtensionsNS)] == '_')
1090 : {
1091 0 : pszName += strlen(pszExtensionsNS) + 1;
1092 : }
1093 :
1094 12 : char* pszModName = CPLStrdup(pszName);
1095 : int i;
1096 132 : for(i=0;pszModName[i] != 0;i++)
1097 : {
1098 120 : if (pszModName[i] == ' ')
1099 12 : pszModName[i] = '_';
1100 : }
1101 12 : 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 60 : static void AddIdent(VSILFILE* fp, int nIdentLevel)
1178 : {
1179 : int i;
1180 160 : for(i=0;i<nIdentLevel;i++)
1181 100 : VSIFPrintfL(fp, " ");
1182 60 : }
1183 :
1184 36 : void OGRGPXLayer::WriteFeatureAttributes( OGRFeature *poFeature, int nIdentLevel )
1185 : {
1186 36 : VSILFILE* fp = poDS->GetOutputFP();
1187 : int i;
1188 :
1189 : /* Begin with standard GPX fields */
1190 754 : for(i=iFirstGPXField;i<nGPXFields;i++)
1191 : {
1192 718 : OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn( i );
1193 718 : if( poFeature->IsFieldSet( i ) )
1194 : {
1195 48 : const char* pszName = poFieldDefn->GetNameRef();
1196 48 : if (strcmp(pszName, "time") == 0)
1197 : {
1198 : int year, month, day, hour, minute, second, TZFlag;
1199 4 : if (poFeature->GetFieldAsDateTime(i, &year, &month, &day,
1200 : &hour, &minute, &second, &TZFlag))
1201 : {
1202 4 : char* pszDate = OGRGetXMLDateTime(year, month, day, hour, minute, second, TZFlag);
1203 4 : AddIdent(fp, nIdentLevel);
1204 4 : poDS->PrintLine("<time>%s</time>", pszDate);
1205 4 : CPLFree(pszDate);
1206 : }
1207 : }
1208 44 : else if (strncmp(pszName, "link", 4) == 0)
1209 : {
1210 12 : if (strstr(pszName, "href"))
1211 : {
1212 4 : AddIdent(fp, nIdentLevel);
1213 4 : VSIFPrintfL(fp, "<link href=\"%s\">", poFeature->GetFieldAsString( i ));
1214 4 : if( poFeature->IsFieldSet( i + 1 ) )
1215 4 : VSIFPrintfL(fp, "<text>%s</text>", poFeature->GetFieldAsString( i + 1 ));
1216 4 : if( poFeature->IsFieldSet( i + 2 ) )
1217 4 : VSIFPrintfL(fp, "<type>%s</type>", poFeature->GetFieldAsString( i + 2 ));
1218 4 : poDS->PrintLine("</link>");
1219 : }
1220 : }
1221 32 : else if (poFieldDefn->GetType() == OFTReal)
1222 : {
1223 : char szValue[64];
1224 8 : OGRFormatDouble(szValue, sizeof(szValue), poFeature->GetFieldAsDouble(i), '.');
1225 8 : AddIdent(fp, nIdentLevel);
1226 8 : poDS->PrintLine("<%s>%s</%s>", pszName, szValue, pszName);
1227 : }
1228 : else
1229 : {
1230 : char* pszValue =
1231 24 : OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( i ));
1232 24 : AddIdent(fp, nIdentLevel);
1233 24 : poDS->PrintLine("<%s>%s</%s>", pszName, pszValue, pszName);
1234 24 : CPLFree(pszValue);
1235 : }
1236 : }
1237 : }
1238 :
1239 : /* Write "extra" fields within the <extensions> tag */
1240 36 : int n = poFeatureDefn->GetFieldCount();
1241 36 : if (i < n)
1242 : {
1243 4 : const char* pszExtensionsNS = poDS->GetExtensionsNS();
1244 4 : AddIdent(fp, nIdentLevel);
1245 4 : poDS->PrintLine("<extensions>");
1246 16 : for(;i<n;i++)
1247 : {
1248 12 : OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn( i );
1249 12 : if( poFeature->IsFieldSet( i ) )
1250 : {
1251 : char* compatibleName =
1252 12 : OGRGPX_GetXMLCompatibleTagName(pszExtensionsNS, poFieldDefn->GetNameRef());
1253 :
1254 12 : 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 12 : const char *pszRaw = poFeature->GetFieldAsString( i );
1269 :
1270 : /* Try to detect XML content */
1271 12 : 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 12 : 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 12 : if (poFieldDefn->GetType() == OFTInteger || poFieldDefn->GetType() == OFTReal)
1294 : {
1295 0 : while( *pszRaw == ' ' )
1296 0 : pszRaw++;
1297 : }
1298 :
1299 12 : char *pszEscaped = OGRGetXML_UTF8_EscapedString( pszRaw );
1300 12 : AddIdent(fp, nIdentLevel + 1);
1301 : poDS->PrintLine("<%s:%s>%s</%s:%s>",
1302 : pszExtensionsNS,
1303 : compatibleName,
1304 : pszEscaped,
1305 : pszExtensionsNS,
1306 12 : compatibleName);
1307 12 : CPLFree(pszEscaped);
1308 : }
1309 12 : CPLFree(compatibleName);
1310 : }
1311 : }
1312 4 : AddIdent(fp, nIdentLevel);
1313 4 : poDS->PrintLine("</extensions>");
1314 : }
1315 36 : }
1316 :
1317 : /************************************************************************/
1318 : /* CheckAndFixCoordinatesValidity() */
1319 : /************************************************************************/
1320 :
1321 40 : OGRErr OGRGPXLayer::CheckAndFixCoordinatesValidity( double* pdfLatitude, double* pdfLongitude )
1322 : {
1323 40 : 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 40 : 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 40 : return CE_None;
1356 : }
1357 :
1358 : /************************************************************************/
1359 : /* CreateFeature() */
1360 : /************************************************************************/
1361 :
1362 36 : OGRErr OGRGPXLayer::CreateFeature( OGRFeature *poFeature )
1363 :
1364 : {
1365 36 : VSILFILE* fp = poDS->GetOutputFP();
1366 36 : if (fp == NULL)
1367 0 : return CE_Failure;
1368 :
1369 : char szLat[64];
1370 : char szLon[64];
1371 : char szAlt[64];
1372 :
1373 36 : OGRGeometry *poGeom = poFeature->GetGeometryRef();
1374 :
1375 36 : if (gpxGeomType == GPX_WPT)
1376 : {
1377 8 : 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 8 : 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 8 : poDS->SetLastGPXGeomTypeWritten(gpxGeomType);
1392 :
1393 8 : 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 8 : 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 8 : OGRPoint* point = (OGRPoint*)poGeom;
1408 8 : double lat = point->getY();
1409 8 : double lon = point->getX();
1410 8 : CheckAndFixCoordinatesValidity(&lat, &lon);
1411 8 : poDS->AddCoord(lon, lat);
1412 8 : OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1413 8 : OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1414 8 : poDS->PrintLine("<wpt lat=\"%s\" lon=\"%s\">", szLat, szLon);
1415 8 : WriteFeatureAttributes(poFeature);
1416 8 : poDS->PrintLine("</wpt>");
1417 : }
1418 28 : else if (gpxGeomType == GPX_ROUTE)
1419 : {
1420 4 : 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 4 : if (poDS->GetLastGPXGeomTypeWritten() == GPX_ROUTE_POINT && poDS->nLastRteId != -1)
1429 : {
1430 0 : poDS->PrintLine("</rte>");
1431 0 : poDS->nLastRteId = -1;
1432 : }
1433 :
1434 4 : poDS->SetLastGPXGeomTypeWritten(gpxGeomType);
1435 :
1436 4 : OGRLineString* line = NULL;
1437 :
1438 4 : 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 4 : switch( poGeom->getGeometryType() )
1447 : {
1448 : case wkbLineString:
1449 : case wkbLineString25D:
1450 : {
1451 4 : line = (OGRLineString*)poGeom;
1452 4 : 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 4 : int n = (line) ? line->getNumPoints() : 0;
1486 : int i;
1487 4 : poDS->PrintLine("<rte>");
1488 4 : WriteFeatureAttributes(poFeature);
1489 10 : for(i=0;i<n;i++)
1490 : {
1491 6 : double lat = line->getY(i);
1492 6 : double lon = line->getX(i);
1493 6 : CheckAndFixCoordinatesValidity(&lat, &lon);
1494 6 : poDS->AddCoord(lon, lat);
1495 6 : OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1496 6 : OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1497 6 : poDS->PrintLine(" <rtept lat=\"%s\" lon=\"%s\">", szLat, szLon);
1498 12 : if (poGeom->getGeometryType() == wkbLineString25D ||
1499 6 : poGeom->getGeometryType() == wkbMultiLineString25D)
1500 : {
1501 0 : OGRFormatDouble(szAlt, sizeof(szAlt), line->getZ(i), '.');
1502 0 : poDS->PrintLine(" <ele>%s</ele>", szAlt);
1503 : }
1504 6 : poDS->PrintLine(" </rtept>");
1505 : }
1506 4 : poDS->PrintLine("</rte>");
1507 : }
1508 24 : else if (gpxGeomType == GPX_TRACK)
1509 : {
1510 6 : if (poDS->GetLastGPXGeomTypeWritten() == GPX_ROUTE_POINT && poDS->nLastRteId != -1)
1511 : {
1512 0 : poDS->PrintLine("</rte>");
1513 0 : poDS->nLastRteId = -1;
1514 : }
1515 6 : 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 6 : poDS->SetLastGPXGeomTypeWritten(gpxGeomType);
1524 :
1525 6 : 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 6 : 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 6 : int nGeometries = ((OGRGeometryCollection*)poGeom)->getNumGeometries ();
1569 6 : poDS->PrintLine("<trk>");
1570 6 : WriteFeatureAttributes(poFeature);
1571 : int j;
1572 12 : for(j=0;j<nGeometries;j++)
1573 : {
1574 6 : OGRLineString* line = (OGRLineString*) ( ((OGRGeometryCollection*)poGeom)->getGeometryRef(j) );
1575 6 : int n = (line) ? line->getNumPoints() : 0;
1576 : int i;
1577 6 : poDS->PrintLine(" <trkseg>");
1578 14 : for(i=0;i<n;i++)
1579 : {
1580 8 : double lat = line->getY(i);
1581 8 : double lon = line->getX(i);
1582 8 : CheckAndFixCoordinatesValidity(&lat, &lon);
1583 8 : poDS->AddCoord(lon, lat);
1584 8 : OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1585 8 : OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1586 8 : poDS->PrintLine(" <trkpt lat=\"%s\" lon=\"%s\">", szLat, szLon);
1587 8 : if (line->getGeometryType() == wkbLineString25D)
1588 : {
1589 0 : OGRFormatDouble(szAlt, sizeof(szAlt), line->getZ(i), '.');
1590 0 : poDS->PrintLine(" <ele>%s</ele>", szAlt);
1591 : }
1592 8 : poDS->PrintLine(" </trkpt>");
1593 : }
1594 6 : poDS->PrintLine(" </trkseg>");
1595 : }
1596 6 : poDS->PrintLine("</trk>");
1597 6 : 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 18 : else if (gpxGeomType == GPX_ROUTE_POINT)
1610 : {
1611 8 : 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 8 : 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 8 : 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 8 : 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 8 : 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 8 : poDS->SetLastGPXGeomTypeWritten(gpxGeomType);
1647 :
1648 8 : if ( poDS->nLastRteId != poFeature->GetFieldAsInteger(FLD_ROUTE_FID))
1649 : {
1650 4 : if (poDS->nLastRteId != -1)
1651 : {
1652 2 : poDS->PrintLine("</rte>");
1653 : }
1654 4 : poDS->PrintLine("<rte>");
1655 4 : if ( poFeature->IsFieldSet(FLD_ROUTE_NAME) )
1656 : {
1657 : char* pszValue =
1658 4 : OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( FLD_ROUTE_NAME ));
1659 : poDS->PrintLine(" <%s>%s</%s>",
1660 4 : "name", pszValue, "name");
1661 4 : CPLFree(pszValue);
1662 : }
1663 : }
1664 :
1665 8 : poDS->nLastRteId = poFeature->GetFieldAsInteger(FLD_ROUTE_FID);
1666 :
1667 8 : OGRPoint* point = (OGRPoint*)poGeom;
1668 8 : double lat = point->getY();
1669 8 : double lon = point->getX();
1670 8 : CheckAndFixCoordinatesValidity(&lat, &lon);
1671 8 : poDS->AddCoord(lon, lat);
1672 8 : OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1673 8 : OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1674 8 : poDS->PrintLine(" <rtept lat=\"%s\" lon=\"%s\">", szLat, szLon);
1675 8 : WriteFeatureAttributes(poFeature, 2);
1676 8 : poDS->PrintLine(" </rtept>");
1677 :
1678 : }
1679 : else
1680 : {
1681 10 : if (poDS->GetLastGPXGeomTypeWritten() == GPX_ROUTE_POINT && poDS->nLastRteId != -1)
1682 : {
1683 2 : poDS->PrintLine("</rte>");
1684 2 : poDS->nLastRteId = -1;
1685 : }
1686 :
1687 10 : 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 10 : 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 10 : 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 10 : 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 10 : 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 10 : 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 10 : poDS->SetLastGPXGeomTypeWritten(gpxGeomType);
1727 :
1728 10 : if ( poDS->nLastTrkId != poFeature->GetFieldAsInteger(FLD_TRACK_FID))
1729 : {
1730 6 : if (poDS->nLastTrkId != -1)
1731 : {
1732 2 : poDS->PrintLine(" </trkseg>");
1733 2 : poDS->PrintLine("</trk>");
1734 : }
1735 6 : poDS->PrintLine("<trk>");
1736 :
1737 6 : if ( poFeature->IsFieldSet(FLD_TRACK_NAME) )
1738 : {
1739 : char* pszValue =
1740 6 : OGRGetXML_UTF8_EscapedString(poFeature->GetFieldAsString( FLD_TRACK_NAME ));
1741 : poDS->PrintLine(" <%s>%s</%s>",
1742 6 : "name", pszValue, "name");
1743 6 : CPLFree(pszValue);
1744 : }
1745 :
1746 6 : poDS->PrintLine(" <trkseg>");
1747 : }
1748 4 : else if (poDS->nLastTrkSegId != poFeature->GetFieldAsInteger(FLD_TRACK_SEG_ID))
1749 : {
1750 2 : poDS->PrintLine(" </trkseg>");
1751 2 : poDS->PrintLine(" <trkseg>");
1752 : }
1753 :
1754 10 : poDS->nLastTrkId = poFeature->GetFieldAsInteger(FLD_TRACK_FID);
1755 10 : poDS->nLastTrkSegId = poFeature->GetFieldAsInteger(FLD_TRACK_SEG_ID);
1756 :
1757 10 : OGRPoint* point = (OGRPoint*)poGeom;
1758 10 : double lat = point->getY();
1759 10 : double lon = point->getX();
1760 10 : CheckAndFixCoordinatesValidity(&lat, &lon);
1761 10 : poDS->AddCoord(lon, lat);
1762 10 : OGRFormatDouble(szLat, sizeof(szLat), lat, '.');
1763 10 : OGRFormatDouble(szLon, sizeof(szLon), lon, '.');
1764 10 : poDS->PrintLine(" <trkpt lat=\"%s\" lon=\"%s\">", szLat, szLon);
1765 10 : WriteFeatureAttributes(poFeature, 3);
1766 10 : poDS->PrintLine(" </trkpt>");
1767 : }
1768 :
1769 36 : return OGRERR_NONE;
1770 : }
1771 :
1772 :
1773 :
1774 : /************************************************************************/
1775 : /* CreateField() */
1776 : /************************************************************************/
1777 :
1778 :
1779 6 : OGRErr OGRGPXLayer::CreateField( OGRFieldDefn *poField, int bApproxOK )
1780 :
1781 : {
1782 150 : for( int iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ )
1783 : {
1784 144 : if (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(),
1785 : poField->GetNameRef() ) == 0)
1786 : {
1787 0 : return OGRERR_NONE;
1788 : }
1789 : }
1790 6 : 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 6 : poFeatureDefn->AddFieldDefn( poField );
1801 6 : 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 1110 : static void XMLCALL startElementLoadSchemaCbk(void *pUserData, const char *pszName, const char **ppszAttr)
1830 : {
1831 1110 : ((OGRGPXLayer*)pUserData)->startElementLoadSchemaCbk(pszName, ppszAttr);
1832 1110 : }
1833 :
1834 1110 : static void XMLCALL endElementLoadSchemaCbk(void *pUserData, const char *pszName)
1835 : {
1836 1110 : ((OGRGPXLayer*)pUserData)->endElementLoadSchemaCbk(pszName);
1837 1110 : }
1838 :
1839 3100 : static void XMLCALL dataHandlerLoadSchemaCbk(void *pUserData, const char *data, int nLen)
1840 : {
1841 3100 : ((OGRGPXLayer*)pUserData)->dataHandlerLoadSchemaCbk(data, nLen);
1842 3100 : }
1843 :
1844 :
1845 : /** This function parses the whole file to detect the extensions fields */
1846 30 : void OGRGPXLayer::LoadExtensionsSchema()
1847 : {
1848 30 : oSchemaParser = OGRCreateExpatXMLParser();
1849 30 : XML_SetElementHandler(oSchemaParser, ::startElementLoadSchemaCbk, ::endElementLoadSchemaCbk);
1850 30 : XML_SetCharacterDataHandler(oSchemaParser, ::dataHandlerLoadSchemaCbk);
1851 30 : XML_SetUserData(oSchemaParser, this);
1852 :
1853 30 : VSIFSeekL( fpGPX, 0, SEEK_SET );
1854 :
1855 30 : inInterestingElement = FALSE;
1856 30 : inExtensions = FALSE;
1857 30 : depthLevel = 0;
1858 30 : currentFieldDefn = NULL;
1859 30 : pszSubElementName = NULL;
1860 30 : pszSubElementValue = NULL;
1861 30 : nSubElementValueLen = 0;
1862 30 : nWithoutEventCounter = 0;
1863 30 : bStopParsing = FALSE;
1864 :
1865 : char aBuf[BUFSIZ];
1866 : int nDone;
1867 30 : do
1868 : {
1869 30 : nDataHandlerCounter = 0;
1870 30 : unsigned int nLen = (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpGPX );
1871 30 : nDone = VSIFEofL(fpGPX);
1872 30 : 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 30 : nWithoutEventCounter ++;
1883 : } while (!nDone && !bStopParsing && nWithoutEventCounter < 10);
1884 :
1885 30 : 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 30 : XML_ParserFree(oSchemaParser);
1893 30 : oSchemaParser = NULL;
1894 :
1895 30 : VSIFSeekL( fpGPX, 0, SEEK_SET );
1896 30 : }
1897 :
1898 :
1899 : /************************************************************************/
1900 : /* startElementLoadSchemaCbk() */
1901 : /************************************************************************/
1902 :
1903 :
1904 1110 : void OGRGPXLayer::startElementLoadSchemaCbk(const char *pszName, const char **ppszAttr)
1905 : {
1906 1110 : if (bStopParsing) return;
1907 :
1908 1110 : nWithoutEventCounter = 0;
1909 :
1910 1122 : if (gpxGeomType == GPX_WPT && strcmp(pszName, "wpt") == 0)
1911 : {
1912 12 : inInterestingElement = TRUE;
1913 12 : inExtensions = FALSE;
1914 12 : interestingDepthLevel = depthLevel;
1915 : }
1916 1110 : else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0)
1917 : {
1918 12 : inInterestingElement = TRUE;
1919 12 : inExtensions = FALSE;
1920 12 : interestingDepthLevel = depthLevel;
1921 : }
1922 1094 : else if (gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0)
1923 : {
1924 8 : inInterestingElement = TRUE;
1925 8 : inExtensions = FALSE;
1926 8 : interestingDepthLevel = depthLevel;
1927 : }
1928 1094 : else if (gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkpt") == 0)
1929 : {
1930 16 : inInterestingElement = TRUE;
1931 16 : inExtensions = FALSE;
1932 16 : interestingDepthLevel = depthLevel;
1933 : }
1934 1074 : else if (gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rtept") == 0)
1935 : {
1936 12 : inInterestingElement = TRUE;
1937 12 : inExtensions = FALSE;
1938 12 : interestingDepthLevel = depthLevel;
1939 : }
1940 1050 : else if (inInterestingElement)
1941 : {
1942 228 : if (depthLevel == interestingDepthLevel + 1 &&
1943 : strcmp(pszName, "extensions") == 0)
1944 : {
1945 8 : inExtensions = TRUE;
1946 8 : extensionsDepthLevel = depthLevel;
1947 : }
1948 212 : else if (inExtensions && depthLevel == extensionsDepthLevel + 1)
1949 : {
1950 12 : CPLFree(pszSubElementName);
1951 12 : pszSubElementName = CPLStrdup(pszName);
1952 :
1953 : int iField;
1954 300 : for(iField = 0; iField < poFeatureDefn->GetFieldCount(); iField++ )
1955 : {
1956 : int bMatch;
1957 294 : if (iField >= nGPXFields)
1958 : {
1959 18 : char* pszCompatibleName = OGRGPX_GetOGRCompatibleTagName(pszName);
1960 18 : bMatch = (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), pszCompatibleName ) == 0);
1961 18 : CPLFree(pszCompatibleName);
1962 : }
1963 : else
1964 276 : bMatch = (strcmp(poFeatureDefn->GetFieldDefn(iField)->GetNameRef(), pszName ) == 0);
1965 :
1966 294 : if (bMatch)
1967 : {
1968 6 : currentFieldDefn = poFeatureDefn->GetFieldDefn(iField);
1969 6 : break;
1970 : }
1971 : }
1972 12 : if (iField == poFeatureDefn->GetFieldCount())
1973 : {
1974 6 : char* pszCompatibleName = OGRGPX_GetOGRCompatibleTagName(pszName);
1975 6 : OGRFieldDefn newFieldDefn(pszCompatibleName, OFTInteger);
1976 6 : CPLFree(pszCompatibleName);
1977 :
1978 6 : poFeatureDefn->AddFieldDefn(&newFieldDefn);
1979 6 : currentFieldDefn = poFeatureDefn->GetFieldDefn(poFeatureDefn->GetFieldCount() - 1);
1980 :
1981 6 : 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 6 : }
1988 : }
1989 : }
1990 : }
1991 :
1992 1110 : 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 1110 : void OGRGPXLayer::endElementLoadSchemaCbk(const char *pszName)
2022 : {
2023 1110 : if (bStopParsing) return;
2024 :
2025 1110 : nWithoutEventCounter = 0;
2026 :
2027 1110 : depthLevel--;
2028 :
2029 1110 : if (inInterestingElement)
2030 : {
2031 292 : if (gpxGeomType == GPX_WPT && strcmp(pszName, "wpt") == 0)
2032 : {
2033 12 : inInterestingElement = FALSE;
2034 12 : inExtensions = FALSE;
2035 : }
2036 280 : else if (gpxGeomType == GPX_TRACK && strcmp(pszName, "trk") == 0)
2037 : {
2038 12 : inInterestingElement = FALSE;
2039 12 : inExtensions = FALSE;
2040 : }
2041 264 : else if (gpxGeomType == GPX_ROUTE && strcmp(pszName, "rte") == 0)
2042 : {
2043 8 : inInterestingElement = FALSE;
2044 8 : inExtensions = FALSE;
2045 : }
2046 264 : else if (gpxGeomType == GPX_TRACK_POINT && strcmp(pszName, "trkpt") == 0)
2047 : {
2048 16 : inInterestingElement = FALSE;
2049 16 : inExtensions = FALSE;
2050 : }
2051 244 : else if (gpxGeomType == GPX_ROUTE_POINT && strcmp(pszName, "rtept") == 0)
2052 : {
2053 12 : inInterestingElement = FALSE;
2054 12 : inExtensions = FALSE;
2055 : }
2056 228 : else if (depthLevel == interestingDepthLevel + 1 &&
2057 : strcmp(pszName, "extensions") == 0)
2058 : {
2059 8 : inExtensions = FALSE;
2060 : }
2061 212 : else if (inExtensions && depthLevel == extensionsDepthLevel + 1 &&
2062 : pszSubElementName && strcmp(pszName, pszSubElementName) == 0)
2063 : {
2064 12 : if (pszSubElementValue && nSubElementValueLen && currentFieldDefn)
2065 : {
2066 10 : pszSubElementValue[nSubElementValueLen] = 0;
2067 10 : if (currentFieldDefn->GetType() == OFTInteger ||
2068 : currentFieldDefn->GetType() == OFTReal)
2069 : {
2070 6 : char* pszRemainingStr = NULL;
2071 6 : CPLStrtod(pszSubElementValue, &pszRemainingStr);
2072 6 : 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 6 : currentFieldDefn->SetType(OFTString);
2087 : }
2088 : }
2089 : }
2090 :
2091 12 : CPLFree(pszSubElementName);
2092 12 : pszSubElementName = NULL;
2093 12 : CPLFree(pszSubElementValue);
2094 12 : pszSubElementValue = NULL;
2095 12 : nSubElementValueLen = 0;
2096 12 : currentFieldDefn = NULL;
2097 : }
2098 : }
2099 : }
2100 :
2101 : /************************************************************************/
2102 : /* dataHandlerLoadSchemaCbk() */
2103 : /************************************************************************/
2104 :
2105 3100 : void OGRGPXLayer::dataHandlerLoadSchemaCbk(const char *data, int nLen)
2106 : {
2107 3100 : if (bStopParsing) return;
2108 :
2109 3100 : nDataHandlerCounter ++;
2110 3100 : 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 3100 : nWithoutEventCounter = 0;
2119 :
2120 3100 : if (pszSubElementName)
2121 : {
2122 10 : char* pszNewSubElementValue = (char*) VSIRealloc(pszSubElementValue, nSubElementValueLen + nLen + 1);
2123 10 : 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 10 : pszSubElementValue = pszNewSubElementValue;
2131 10 : memcpy(pszSubElementValue + nSubElementValueLen, data, nLen);
2132 10 : nSubElementValueLen += nLen;
2133 10 : 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
|