1 : /******************************************************************************
2 : * $Id: ogrgeorssdatasource.cpp 17848 2009-10-17 12:59:16Z rouault $
3 : *
4 : * Project: GeoRSS Translator
5 : * Purpose: Implements OGRGeoRSSDataSource class
6 : * Author: Even Rouault, even dot rouault at mines dash paris dot org
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2008, 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_georss.h"
31 : #include "cpl_conv.h"
32 : #include "cpl_string.h"
33 : #include "cpl_csv.h"
34 :
35 : CPL_CVSID("$Id: ogrgeorssdatasource.cpp 17848 2009-10-17 12:59:16Z rouault $");
36 :
37 : /************************************************************************/
38 : /* OGRGeoRSSDataSource() */
39 : /************************************************************************/
40 :
41 27 : OGRGeoRSSDataSource::OGRGeoRSSDataSource()
42 :
43 : {
44 27 : papoLayers = NULL;
45 27 : nLayers = 0;
46 :
47 27 : fpOutput = NULL;
48 :
49 27 : pszName = NULL;
50 :
51 27 : eFormat = GEORSS_RSS;
52 27 : eGeomDialect = GEORSS_SIMPLE;
53 27 : bUseExtensions = FALSE;
54 27 : bWriteHeaderAndFooter = TRUE;
55 27 : }
56 :
57 : /************************************************************************/
58 : /* ~OGRGeoRSSDataSource() */
59 : /************************************************************************/
60 :
61 54 : OGRGeoRSSDataSource::~OGRGeoRSSDataSource()
62 :
63 : {
64 27 : if ( fpOutput != NULL )
65 : {
66 7 : if (bWriteHeaderAndFooter)
67 : {
68 7 : if (eFormat == GEORSS_RSS)
69 : {
70 6 : VSIFPrintf(fpOutput, " </channel>\n");
71 6 : VSIFPrintf(fpOutput, "</rss>\n");
72 : }
73 : else
74 : {
75 1 : VSIFPrintf(fpOutput, "</feed>\n");
76 : }
77 : }
78 7 : if ( fpOutput != stdout )
79 7 : VSIFClose( fpOutput);
80 : }
81 :
82 46 : for( int i = 0; i < nLayers; i++ )
83 19 : delete papoLayers[i];
84 27 : CPLFree( papoLayers );
85 27 : CPLFree( pszName );
86 54 : }
87 :
88 : /************************************************************************/
89 : /* TestCapability() */
90 : /************************************************************************/
91 :
92 0 : int OGRGeoRSSDataSource::TestCapability( const char * pszCap )
93 :
94 : {
95 0 : if( EQUAL(pszCap,ODsCCreateLayer) )
96 0 : return TRUE;
97 0 : else if( EQUAL(pszCap,ODsCDeleteLayer) )
98 0 : return FALSE;
99 : else
100 0 : return FALSE;
101 : }
102 :
103 : /************************************************************************/
104 : /* GetLayer() */
105 : /************************************************************************/
106 :
107 12 : OGRLayer *OGRGeoRSSDataSource::GetLayer( int iLayer )
108 :
109 : {
110 12 : if( iLayer < 0 || iLayer >= nLayers )
111 0 : return NULL;
112 : else
113 12 : return papoLayers[iLayer];
114 : }
115 :
116 : /************************************************************************/
117 : /* CreateLayer() */
118 : /************************************************************************/
119 :
120 7 : OGRLayer * OGRGeoRSSDataSource::CreateLayer( const char * pszLayerName,
121 : OGRSpatialReference *poSRS,
122 : OGRwkbGeometryType eType,
123 : char ** papszOptions )
124 :
125 : {
126 7 : if (fpOutput == NULL)
127 0 : return NULL;
128 :
129 7 : if (poSRS != NULL && eGeomDialect != GEORSS_GML)
130 : {
131 1 : OGRSpatialReference oSRS;
132 1 : oSRS.SetWellKnownGeogCS("WGS84");
133 1 : if (poSRS->IsSame(&oSRS) == FALSE)
134 : {
135 : CPLError(CE_Failure, CPLE_NotSupported,
136 1 : "For a non GML dialect, only WGS84 SRS is supported");
137 1 : return NULL;
138 0 : }
139 : }
140 :
141 6 : nLayers++;
142 6 : papoLayers = (OGRGeoRSSLayer **) CPLRealloc(papoLayers, nLayers * sizeof(OGRGeoRSSLayer*));
143 6 : papoLayers[nLayers-1] = new OGRGeoRSSLayer( pszName, pszLayerName, this, poSRS, TRUE );
144 :
145 6 : return papoLayers[nLayers-1];
146 : }
147 :
148 : #ifdef HAVE_EXPAT
149 : /************************************************************************/
150 : /* startElementValidateCbk() */
151 : /************************************************************************/
152 :
153 307 : void OGRGeoRSSDataSource::startElementValidateCbk(const char *pszName, const char **ppszAttr)
154 : {
155 307 : if (validity == GEORSS_VALIDITY_UNKNOWN)
156 : {
157 14 : if (strcmp(pszName, "rss") == 0)
158 : {
159 11 : validity = GEORSS_VALIDITY_VALID;
160 11 : eFormat = GEORSS_RSS;
161 : }
162 3 : else if (strcmp(pszName, "feed") == 0)
163 : {
164 3 : validity = GEORSS_VALIDITY_VALID;
165 3 : eFormat = GEORSS_ATOM;
166 : }
167 : else
168 : {
169 0 : validity = GEORSS_VALIDITY_INVALID;
170 : }
171 : }
172 307 : }
173 :
174 :
175 : /************************************************************************/
176 : /* dataHandlerValidateCbk() */
177 : /************************************************************************/
178 :
179 889 : void OGRGeoRSSDataSource::dataHandlerValidateCbk(const char *data, int nLen)
180 : {
181 889 : nDataHandlerCounter ++;
182 889 : if (nDataHandlerCounter >= BUFSIZ)
183 : {
184 0 : CPLError(CE_Failure, CPLE_AppDefined, "File probably corrupted (million laugh pattern)");
185 0 : XML_StopParser(oCurrentParser, XML_FALSE);
186 : }
187 889 : }
188 :
189 :
190 307 : static void XMLCALL startElementValidateCbk(void *pUserData, const char *pszName, const char **ppszAttr)
191 : {
192 307 : OGRGeoRSSDataSource* poDS = (OGRGeoRSSDataSource*) pUserData;
193 307 : poDS->startElementValidateCbk(pszName, ppszAttr);
194 307 : }
195 :
196 889 : static void XMLCALL dataHandlerValidateCbk(void *pUserData, const char *data, int nLen)
197 : {
198 889 : OGRGeoRSSDataSource* poDS = (OGRGeoRSSDataSource*) pUserData;
199 889 : poDS->dataHandlerValidateCbk(data, nLen);
200 889 : }
201 : #endif
202 :
203 : /************************************************************************/
204 : /* Open() */
205 : /************************************************************************/
206 :
207 20 : int OGRGeoRSSDataSource::Open( const char * pszFilename, int bUpdateIn)
208 :
209 : {
210 20 : if (bUpdateIn)
211 : {
212 : CPLError(CE_Failure, CPLE_NotSupported,
213 0 : "OGR/GeoRSS driver does not support opening a file in update mode");
214 0 : return FALSE;
215 : }
216 : #ifdef HAVE_EXPAT
217 20 : pszName = CPLStrdup( pszFilename );
218 :
219 : /* -------------------------------------------------------------------- */
220 : /* Determine what sort of object this is. */
221 : /* -------------------------------------------------------------------- */
222 : VSIStatBufL sStatBuf;
223 :
224 20 : if( VSIStatL( pszFilename, &sStatBuf ) != 0 )
225 2 : return FALSE;
226 :
227 18 : if( VSI_ISDIR(sStatBuf.st_mode) )
228 0 : return FALSE;
229 :
230 18 : FILE* fp = VSIFOpenL(pszFilename, "r");
231 18 : if (fp == NULL)
232 0 : return FALSE;
233 :
234 18 : validity = GEORSS_VALIDITY_UNKNOWN;
235 :
236 18 : XML_Parser oParser = OGRCreateExpatXMLParser();
237 18 : XML_SetUserData(oParser, this);
238 18 : XML_SetElementHandler(oParser, ::startElementValidateCbk, NULL);
239 18 : XML_SetCharacterDataHandler(oParser, ::dataHandlerValidateCbk);
240 18 : oCurrentParser = oParser;
241 :
242 : char aBuf[BUFSIZ];
243 : int nDone;
244 : unsigned int nLen;
245 18 : int nCount = 0;
246 :
247 : /* Begin to parse the file and look for the <rss> or <feed> element */
248 : /* It *MUST* be the first element of an XML file */
249 : /* So once we have read the first element, we know if we can */
250 : /* handle the file or not with that driver */
251 0 : do
252 : {
253 18 : nDataHandlerCounter = 0;
254 18 : nLen = (unsigned int) VSIFReadL( aBuf, 1, sizeof(aBuf), fp );
255 18 : nDone = VSIFEofL(fp);
256 18 : if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
257 : {
258 5 : if (nLen <= BUFSIZ-1)
259 3 : aBuf[nLen] = 0;
260 : else
261 2 : aBuf[BUFSIZ-1] = 0;
262 5 : if (strstr(aBuf, "<?xml") && (strstr(aBuf, "<rss") || strstr(aBuf, "<feed")))
263 : {
264 : CPLError(CE_Failure, CPLE_AppDefined,
265 : "XML parsing of GeoRSS file failed : %s at line %d, column %d",
266 : XML_ErrorString(XML_GetErrorCode(oParser)),
267 : (int)XML_GetCurrentLineNumber(oParser),
268 1 : (int)XML_GetCurrentColumnNumber(oParser));
269 : }
270 5 : validity = GEORSS_VALIDITY_INVALID;
271 5 : break;
272 : }
273 13 : if (validity == GEORSS_VALIDITY_INVALID)
274 : {
275 0 : break;
276 : }
277 13 : else if (validity == GEORSS_VALIDITY_VALID)
278 : {
279 13 : break;
280 : }
281 : else
282 : {
283 : /* After reading 50 * BUFSIZ bytes, and not finding whether the file */
284 : /* is GeoRSS or not, we give up and fail silently */
285 0 : nCount ++;
286 0 : if (nCount == 50)
287 0 : break;
288 : }
289 : } while (!nDone && nLen > 0 );
290 :
291 18 : XML_ParserFree(oParser);
292 :
293 18 : VSIFCloseL(fp);
294 :
295 18 : if (validity == GEORSS_VALIDITY_VALID)
296 : {
297 13 : CPLDebug("GeoRSS", "%s seems to be a GeoRSS file.", pszFilename);
298 :
299 13 : nLayers = 1;
300 13 : papoLayers = (OGRGeoRSSLayer **) CPLRealloc(papoLayers, nLayers * sizeof(OGRGeoRSSLayer*));
301 13 : papoLayers[0] = new OGRGeoRSSLayer( pszName, "georss", this, NULL, FALSE );
302 : }
303 :
304 18 : return (validity == GEORSS_VALIDITY_VALID);
305 : #else
306 : char aBuf[256];
307 : FILE* fp = VSIFOpenL(pszFilename, "r");
308 : if (fp)
309 : {
310 : unsigned int nLen = (unsigned int)VSIFReadL( aBuf, 1, 255, fp );
311 : aBuf[nLen] = 0;
312 : if (strstr(aBuf, "<?xml") && (strstr(aBuf, "<rss") || strstr(aBuf, "<feed")))
313 : {
314 : CPLError(CE_Failure, CPLE_NotSupported,
315 : "OGR/GeoRSS driver has not been built with read support. Expat library required");
316 : }
317 : VSIFCloseL(fp);
318 : }
319 : return FALSE;
320 : #endif
321 : }
322 :
323 :
324 : /************************************************************************/
325 : /* Create() */
326 : /************************************************************************/
327 :
328 7 : int OGRGeoRSSDataSource::Create( const char *pszFilename,
329 : char **papszOptions )
330 : {
331 7 : if( fpOutput != NULL)
332 : {
333 : CPLAssert( FALSE );
334 0 : return FALSE;
335 : }
336 :
337 : /* -------------------------------------------------------------------- */
338 : /* Do not override exiting file. */
339 : /* -------------------------------------------------------------------- */
340 : VSIStatBufL sStatBuf;
341 :
342 7 : if( VSIStatL( pszFilename, &sStatBuf ) == 0 )
343 : {
344 : CPLError(CE_Failure, CPLE_NotSupported,
345 : "You have to delete %s before being able to create it with the GeoRSS driver",
346 0 : pszFilename);
347 0 : return FALSE;
348 : }
349 :
350 : /* -------------------------------------------------------------------- */
351 : /* Create the output file. */
352 : /* -------------------------------------------------------------------- */
353 7 : pszName = CPLStrdup( pszFilename );
354 :
355 7 : if( EQUAL(pszFilename,"stdout") )
356 0 : fpOutput = stdout;
357 : else
358 7 : fpOutput = VSIFOpen( pszFilename, "w" );
359 7 : if( fpOutput == NULL )
360 : {
361 : CPLError( CE_Failure, CPLE_OpenFailed,
362 : "Failed to create GeoRSS file %s.",
363 0 : pszFilename );
364 0 : return FALSE;
365 : }
366 :
367 7 : const char* pszFormat = CSLFetchNameValue(papszOptions, "FORMAT");
368 7 : if (pszFormat)
369 : {
370 1 : if (EQUAL(pszFormat, "RSS"))
371 0 : eFormat = GEORSS_RSS;
372 1 : else if (EQUAL(pszFormat, "ATOM"))
373 1 : eFormat = GEORSS_ATOM;
374 : else
375 : {
376 : CPLError(CE_Warning, CPLE_NotSupported,
377 0 : "Unsupported value for %s : %s", "FORMAT", pszFormat);
378 : }
379 : }
380 :
381 7 : const char* pszGeomDialect = CSLFetchNameValue(papszOptions, "GEOM_DIALECT");
382 7 : if (pszGeomDialect)
383 : {
384 3 : if (EQUAL(pszGeomDialect, "GML"))
385 2 : eGeomDialect = GEORSS_GML;
386 1 : else if (EQUAL(pszGeomDialect, "SIMPLE"))
387 0 : eGeomDialect = GEORSS_SIMPLE;
388 1 : else if (EQUAL(pszGeomDialect, "W3C_GEO"))
389 1 : eGeomDialect = GEORSS_W3C_GEO;
390 : else
391 : {
392 : CPLError(CE_Warning, CPLE_NotSupported,
393 0 : "Unsupported value for %s : %s", "GEOM_DIALECT", pszGeomDialect);
394 : }
395 : }
396 :
397 7 : const char* pszWriteHeaderAndFooter = CSLFetchNameValue(papszOptions, "WRITE_HEADER_AND_FOOTER");
398 7 : if (pszWriteHeaderAndFooter && CSLTestBoolean(pszWriteHeaderAndFooter) == FALSE)
399 : {
400 0 : bWriteHeaderAndFooter = FALSE;
401 0 : return TRUE;
402 : }
403 :
404 7 : const char* pszHeader = NULL;
405 7 : const char* pszTitle = NULL;
406 7 : const char* pszDescription = NULL;
407 7 : const char* pszLink = NULL;
408 7 : const char* pszUpdated = NULL;
409 7 : const char* pszAuthorName = NULL;
410 7 : const char* pszId = NULL;
411 :
412 7 : pszHeader = CSLFetchNameValue(papszOptions, "HEADER");
413 :
414 13 : if (eFormat == GEORSS_RSS && pszHeader == NULL)
415 : {
416 6 : pszTitle = CSLFetchNameValue(papszOptions, "TITLE");
417 6 : if (pszTitle == NULL)
418 6 : pszTitle = "title";
419 :
420 6 : pszDescription = CSLFetchNameValue(papszOptions, "DESCRIPTION");
421 6 : if (pszDescription == NULL)
422 6 : pszDescription = "channel_description";
423 :
424 6 : pszLink = CSLFetchNameValue(papszOptions, "LINK");
425 6 : if (pszLink == NULL)
426 6 : pszLink = "channel_link";
427 :
428 : }
429 1 : else if (eFormat == GEORSS_ATOM && pszHeader == NULL)
430 : {
431 1 : pszTitle = CSLFetchNameValue(papszOptions, "TITLE");
432 1 : if (pszTitle == NULL)
433 1 : pszTitle = "title";
434 :
435 1 : pszUpdated = CSLFetchNameValue(papszOptions, "UPDATED");
436 1 : if (pszUpdated == NULL)
437 1 : pszUpdated = "2009-01-01T00:00:00Z";
438 :
439 1 : pszAuthorName = CSLFetchNameValue(papszOptions, "AUTHOR_NAME");
440 1 : if (pszAuthorName == NULL)
441 1 : pszAuthorName = "author";
442 :
443 1 : pszId = CSLFetchNameValue(papszOptions, "ID");
444 1 : if (pszId == NULL)
445 1 : pszId = "id";
446 : }
447 :
448 7 : const char* pszUseExtensions = CSLFetchNameValue( papszOptions, "USE_EXTENSIONS");
449 7 : bUseExtensions = (pszUseExtensions && CSLTestBoolean(pszUseExtensions));
450 :
451 : /* -------------------------------------------------------------------- */
452 : /* Output header of GeoRSS file. */
453 : /* -------------------------------------------------------------------- */
454 7 : VSIFPrintf(fpOutput, "<?xml version=\"1.0\"?>\n");
455 7 : if (eFormat == GEORSS_RSS)
456 : {
457 6 : VSIFPrintf(fpOutput, "<rss version=\"2.0\" ");
458 6 : if (eGeomDialect == GEORSS_GML)
459 2 : VSIFPrintf(fpOutput, "xmlns:georss=\"http://www.georss.org/georss\" xmlns:gml=\"http://www.opengis.net/gml\"");
460 4 : else if (eGeomDialect == GEORSS_SIMPLE)
461 3 : VSIFPrintf(fpOutput, "xmlns:georss=\"http://www.georss.org/georss\"");
462 : else
463 1 : VSIFPrintf(fpOutput, "xmlns:geo=\"http://www.w3.org/2003/01/geo/wgs84_pos#\"");
464 6 : VSIFPrintf(fpOutput, ">\n");
465 6 : VSIFPrintf(fpOutput, " <channel>\n");
466 6 : if (pszHeader)
467 : {
468 0 : VSIFPrintf(fpOutput, "%s", pszHeader);
469 : }
470 : else
471 : {
472 6 : VSIFPrintf(fpOutput, " <title>%s</title>\n", pszTitle);
473 6 : VSIFPrintf(fpOutput, " <description>%s</description>\n", pszDescription);
474 6 : VSIFPrintf(fpOutput, " <link>%s</link>\n", pszLink);
475 : }
476 : }
477 : else
478 : {
479 1 : VSIFPrintf(fpOutput, "<feed xmlns=\"http://www.w3.org/2005/Atom\" ");
480 1 : if (eGeomDialect == GEORSS_GML)
481 0 : VSIFPrintf(fpOutput, "xmlns:gml=\"http://www.opengis.net/gml\"");
482 1 : else if (eGeomDialect == GEORSS_SIMPLE)
483 1 : VSIFPrintf(fpOutput, "xmlns:georss=\"http://www.georss.org/georss\"");
484 : else
485 0 : VSIFPrintf(fpOutput, "xmlns:geo=\"http://www.w3.org/2003/01/geo/wgs84_pos#\"");
486 1 : VSIFPrintf(fpOutput, ">\n");
487 1 : if (pszHeader)
488 : {
489 0 : VSIFPrintf(fpOutput, "%s", pszHeader);
490 : }
491 : else
492 : {
493 1 : VSIFPrintf(fpOutput, " <title>%s</title>\n", pszTitle);
494 1 : VSIFPrintf(fpOutput, " <updated>%s</updated>\n", pszUpdated);
495 1 : VSIFPrintf(fpOutput, " <author><name>%s</name></author>\n", pszAuthorName);
496 1 : VSIFPrintf(fpOutput, " <id>%s</id>\n", pszId);
497 : }
498 : }
499 :
500 7 : return TRUE;
501 : }
|