1 : /******************************************************************************
2 : * $Id: ogrgeojsondatasource.cpp 24110 2012-03-12 18:57:42Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implementation of OGRGeoJSONDataSource class (OGR GeoJSON Driver).
6 : * Author: Mateusz Loskot, mateusz@loskot.net
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2007, Mateusz Loskot
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 : #include "ogr_geojson.h"
30 : #include "ogrgeojsonutils.h"
31 : #include "ogrgeojsonreader.h"
32 : #include <cpl_http.h>
33 : #include <jsonc/json.h> // JSON-C
34 : #include <cstddef>
35 : #include <cstdlib>
36 : using namespace std;
37 :
38 : /************************************************************************/
39 : /* OGRGeoJSONDataSource() */
40 : /************************************************************************/
41 :
42 880 : OGRGeoJSONDataSource::OGRGeoJSONDataSource()
43 : : pszName_(NULL), pszGeoData_(NULL),
44 : papoLayers_(NULL), nLayers_(0), fpOut_(NULL),
45 : flTransGeom_( OGRGeoJSONDataSource::eGeometryPreserve ),
46 : flTransAttrs_( OGRGeoJSONDataSource::eAtributesPreserve ),
47 : bFpOutputIsSeekable_( FALSE ),
48 880 : nBBOXInsertLocation_(0)
49 : {
50 : // I've got constructed. Lunch time!
51 880 : }
52 :
53 : /************************************************************************/
54 : /* ~OGRGeoJSONDataSource() */
55 : /************************************************************************/
56 :
57 880 : OGRGeoJSONDataSource::~OGRGeoJSONDataSource()
58 : {
59 880 : Clear();
60 :
61 880 : if( NULL != fpOut_ )
62 : {
63 0 : VSIFCloseL( fpOut_ );
64 0 : fpOut_ = NULL;
65 : }
66 880 : }
67 :
68 : /************************************************************************/
69 : /* Open() */
70 : /************************************************************************/
71 :
72 850 : int OGRGeoJSONDataSource::Open( const char* pszName )
73 : {
74 850 : CPLAssert( NULL != pszName );
75 :
76 : /* -------------------------------------------------------------------- */
77 : /* Release resources allocated during previous request. */
78 : /* -------------------------------------------------------------------- */
79 850 : if( NULL != papoLayers_ )
80 : {
81 0 : CPLAssert( nLayers_ > 0 );
82 0 : Clear();
83 : }
84 :
85 : /* -------------------------------------------------------------------- */
86 : /* Determine type of data source: text file (.geojson, .json), */
87 : /* Web Service or text passed directly and load data. */
88 : /* -------------------------------------------------------------------- */
89 : GeoJSONSourceType nSrcType;
90 :
91 850 : nSrcType = GeoJSONGetSourceType( pszName );
92 850 : if( eGeoJSONSourceService == nSrcType )
93 : {
94 0 : if( (strstr(pszName, "SERVICE=WFS") || strstr(pszName, "service=WFS") ||
95 : strstr(pszName, "service=wfs")) && !strstr(pszName, "json"))
96 0 : return FALSE;
97 :
98 0 : if( !ReadFromService( pszName ) )
99 0 : return FALSE;
100 : }
101 850 : else if( eGeoJSONSourceText == nSrcType )
102 : {
103 6 : pszGeoData_ = CPLStrdup( pszName );
104 : }
105 844 : else if( eGeoJSONSourceFile == nSrcType )
106 : {
107 76 : if( !ReadFromFile( pszName ) )
108 0 : return FALSE;
109 : }
110 : else
111 : {
112 768 : Clear();
113 768 : return FALSE;
114 : }
115 :
116 : /* -------------------------------------------------------------------- */
117 : /* Construct OGR layer and feature objects from */
118 : /* GeoJSON text tree. */
119 : /* -------------------------------------------------------------------- */
120 82 : if( NULL == pszGeoData_ ||
121 : strncmp(pszGeoData_, "{\"couchdb\":\"Welcome\"", strlen("{\"couchdb\":\"Welcome\"")) == 0 ||
122 : strncmp(pszGeoData_, "{\"db_name\":\"", strlen("{\"db_name\":\"")) == 0 ||
123 : strncmp(pszGeoData_, "{\"total_rows\":", strlen("{\"total_rows\":")) == 0 ||
124 : strncmp(pszGeoData_, "{\"rows\":[", strlen("{\"rows\":[")) == 0)
125 : {
126 0 : Clear();
127 0 : return FALSE;
128 : }
129 :
130 82 : OGRGeoJSONLayer* poLayer = LoadLayer();
131 82 : if( NULL == poLayer )
132 : {
133 0 : Clear();
134 :
135 : CPLError( CE_Failure, CPLE_OpenFailed,
136 0 : "Failed to read GeoJSON data" );
137 0 : return FALSE;
138 : }
139 :
140 82 : poLayer->DetectGeometryType();
141 :
142 : /* Return layer in readable state. */
143 82 : poLayer->ResetReading();
144 : /* -------------------------------------------------------------------- */
145 : /* NOTE: Currently, the driver generates only one layer per */
146 : /* single GeoJSON file, input or service request. */
147 : /* -------------------------------------------------------------------- */
148 82 : const int nLayerIndex = 0;
149 82 : nLayers_ = 1;
150 :
151 82 : papoLayers_ = (OGRLayer**)CPLMalloc( sizeof(OGRLayer*) * nLayers_ );
152 82 : papoLayers_[nLayerIndex] = poLayer;
153 :
154 82 : CPLAssert( NULL != papoLayers_ );
155 82 : CPLAssert( nLayers_ > 0 );
156 82 : return TRUE;
157 : }
158 :
159 : /************************************************************************/
160 : /* GetName() */
161 : /************************************************************************/
162 :
163 114 : const char* OGRGeoJSONDataSource::GetName()
164 : {
165 114 : return pszName_ ? pszName_ : "";
166 : }
167 :
168 : /************************************************************************/
169 : /* GetLayerCount() */
170 : /************************************************************************/
171 :
172 84 : int OGRGeoJSONDataSource::GetLayerCount()
173 : {
174 84 : return nLayers_;
175 : }
176 :
177 : /************************************************************************/
178 : /* GetLayer() */
179 : /************************************************************************/
180 :
181 82 : OGRLayer* OGRGeoJSONDataSource::GetLayer( int nLayer )
182 : {
183 82 : if( 0 <= nLayer || nLayer < nLayers_ )
184 : {
185 82 : return papoLayers_[nLayer];
186 : }
187 :
188 0 : return NULL;
189 : }
190 :
191 : /************************************************************************/
192 : /* CreateLayer() */
193 : /************************************************************************/
194 :
195 30 : OGRLayer* OGRGeoJSONDataSource::CreateLayer( const char* pszName_,
196 : OGRSpatialReference* poSRS,
197 : OGRwkbGeometryType eGType,
198 : char** papszOptions )
199 : {
200 30 : if ( NULL == fpOut_ )
201 : {
202 : CPLError(CE_Failure, CPLE_NotSupported,
203 0 : "GeoJSON driver doesn't support creating a layer on a read-only datasource");
204 0 : return NULL;
205 : }
206 :
207 30 : if ( nLayers_ != 0 )
208 : {
209 : CPLError(CE_Failure, CPLE_NotSupported,
210 0 : "GeoJSON driver doesn't support creating more than one layer");
211 0 : return NULL;
212 : }
213 :
214 30 : OGRGeoJSONWriteLayer* poLayer = NULL;
215 30 : poLayer = new OGRGeoJSONWriteLayer( pszName_, eGType, papszOptions, this );
216 :
217 : /* -------------------------------------------------------------------- */
218 : /* Add layer to data source layer list. */
219 : /* -------------------------------------------------------------------- */
220 :
221 : papoLayers_ = (OGRLayer **)
222 30 : CPLRealloc( papoLayers_, sizeof(OGRLayer*) * (nLayers_ + 1) );
223 :
224 30 : papoLayers_[nLayers_++] = poLayer;
225 :
226 30 : VSIFPrintfL( fpOut_, "{\n\"type\": \"FeatureCollection\",\n" );
227 :
228 : #ifdef notdef
229 : if (poSRS)
230 : {
231 : const char* pszAuthority = poSRS->GetAuthorityName(NULL);
232 : const char* pszAuthorityCode = poSRS->GetAuthorityCode(NULL);
233 : if (pszAuthority != NULL && pszAuthorityCode != NULL &&
234 : strcmp(pszAuthority, "EPSG") == 0)
235 : {
236 : json_object* poObjCRS = json_object_new_object();
237 : json_object_object_add(poObjCRS, "type",
238 : json_object_new_string("name"));
239 : json_object* poObjProperties = json_object_new_object();
240 : json_object_object_add(poObjCRS, "properties", poObjProperties);
241 :
242 : if (strcmp(pszAuthorityCode, "4326") == 0)
243 : {
244 : json_object_object_add(poObjProperties, "name",
245 : json_object_new_string("urn:ogc:def:crs:OGC:1.3:CRS84"));
246 : }
247 : else
248 : {
249 : /* FIXME?: the issue is that for geographic SRS, OGR will expose a latitude/longitude axis order */
250 : /* which is probably not what is written in the file! */
251 : json_object_object_add(poObjProperties, "name",
252 : json_object_new_string(CPLSPrintf("urn:ogc:def:crs:EPSG::%s", pszAuthorityCode)));
253 : }
254 :
255 : const char* pszCRS = json_object_to_json_string( poObjCRS );
256 : VSIFPrintfL( fpOut_, "\"crs\": %s,\n", pszCRS );
257 :
258 : json_object_put(poObjCRS);
259 : }
260 : }
261 : #endif
262 :
263 30 : if (bFpOutputIsSeekable_)
264 : {
265 16 : nBBOXInsertLocation_ = (int) VSIFTellL( fpOut_ );
266 :
267 : char szSpaceForBBOX[SPACE_FOR_BBOX+1];
268 16 : memset(szSpaceForBBOX, ' ', SPACE_FOR_BBOX);
269 16 : szSpaceForBBOX[SPACE_FOR_BBOX] = '\0';
270 16 : VSIFPrintfL( fpOut_, "%s\n", szSpaceForBBOX);
271 : }
272 :
273 30 : VSIFPrintfL( fpOut_, "\"features\": [\n" );
274 :
275 30 : return poLayer;
276 : }
277 :
278 : /************************************************************************/
279 : /* TestCapability() */
280 : /************************************************************************/
281 :
282 0 : int OGRGeoJSONDataSource::TestCapability( const char* pszCap )
283 : {
284 0 : if( EQUAL( pszCap, ODsCCreateLayer ) )
285 0 : return fpOut_ != NULL /* && nLayers_ == 0 */;
286 0 : else if( EQUAL( pszCap, ODsCDeleteLayer ) )
287 0 : return FALSE;
288 : else
289 0 : return FALSE;
290 : }
291 :
292 : /************************************************************************/
293 : /* Create() */
294 : /************************************************************************/
295 :
296 30 : int OGRGeoJSONDataSource::Create( const char* pszName, char** papszOptions )
297 : {
298 : UNREFERENCED_PARAM(papszOptions);
299 :
300 30 : CPLAssert( NULL == fpOut_ );
301 :
302 30 : if (strcmp(pszName, "/dev/stdout") == 0)
303 0 : pszName = "/vsistdout/";
304 :
305 : bFpOutputIsSeekable_ = !(strcmp(pszName,"/vsistdout/") == 0 ||
306 : strncmp(pszName,"/vsigzip/", 9) == 0 ||
307 30 : strncmp(pszName,"/vsizip/", 8) == 0);
308 :
309 : /* -------------------------------------------------------------------- */
310 : /* File overwrite not supported. */
311 : /* -------------------------------------------------------------------- */
312 : VSIStatBufL sStatBuf;
313 30 : if( 0 == VSIStatL( pszName, &sStatBuf ) )
314 : {
315 : CPLError( CE_Failure, CPLE_NotSupported,
316 0 : "The GeoJSON driver does not overwrite existing files." );
317 0 : return FALSE;
318 : }
319 :
320 : /* -------------------------------------------------------------------- */
321 : /* Create the output file. */
322 : /* -------------------------------------------------------------------- */
323 30 : fpOut_ = VSIFOpenL( pszName, "w" );
324 30 : if( NULL == fpOut_)
325 : {
326 : CPLError( CE_Failure, CPLE_OpenFailed,
327 : "Failed to create GeoJSON datasource: %s.",
328 0 : pszName );
329 0 : return FALSE;
330 : }
331 :
332 30 : pszName_ = CPLStrdup( pszName );
333 :
334 30 : return TRUE;
335 : }
336 :
337 : /************************************************************************/
338 : /* SetGeometryTranslation() */
339 : /************************************************************************/
340 :
341 : void
342 850 : OGRGeoJSONDataSource::SetGeometryTranslation( GeometryTranslation type )
343 : {
344 850 : flTransGeom_ = type;
345 850 : }
346 :
347 : /************************************************************************/
348 : /* SetAttributesTranslation() */
349 : /************************************************************************/
350 :
351 850 : void OGRGeoJSONDataSource::SetAttributesTranslation( AttributesTranslation type )
352 : {
353 850 : flTransAttrs_ = type;
354 850 : }
355 :
356 : /************************************************************************/
357 : /* PRIVATE FUNCTIONS IMPLEMENTATION */
358 : /************************************************************************/
359 :
360 1648 : void OGRGeoJSONDataSource::Clear()
361 : {
362 1760 : for( int i = 0; i < nLayers_; i++ )
363 : {
364 112 : CPLAssert( NULL != papoLayers_ );
365 112 : delete papoLayers_[i];
366 : }
367 :
368 1648 : CPLFree( papoLayers_ );
369 1648 : papoLayers_ = NULL;
370 1648 : nLayers_ = 0;
371 :
372 1648 : CPLFree( pszName_ );
373 1648 : pszName_ = NULL;
374 :
375 1648 : CPLFree( pszGeoData_ );
376 1648 : pszGeoData_ = NULL;
377 :
378 1648 : if( NULL != fpOut_ )
379 : {
380 30 : VSIFCloseL( fpOut_ );
381 : }
382 1648 : fpOut_ = NULL;
383 1648 : }
384 :
385 : /************************************************************************/
386 : /* ReadFromFile() */
387 : /************************************************************************/
388 :
389 76 : int OGRGeoJSONDataSource::ReadFromFile( const char* pszSource )
390 : {
391 76 : CPLAssert( NULL == pszGeoData_ );
392 :
393 76 : if( NULL == pszSource )
394 : {
395 0 : CPLDebug( "GeoJSON", "Input file path is null" );
396 0 : return FALSE;
397 : }
398 :
399 76 : VSILFILE* fp = NULL;
400 76 : fp = VSIFOpenL( pszSource, "rb" );
401 76 : if( NULL == fp )
402 : {
403 0 : CPLDebug( "GeoJSON", "Failed to open input file '%s'", pszSource );
404 0 : return FALSE;
405 : }
406 :
407 76 : vsi_l_offset nDataLen = 0;
408 :
409 76 : VSIFSeekL( fp, 0, SEEK_END );
410 76 : nDataLen = VSIFTellL( fp );
411 :
412 : // With "large" VSI I/O API we can read data chunks larger than VSIMalloc
413 : // could allocate. Catch it here.
414 : if ( nDataLen > (vsi_l_offset)(size_t)nDataLen )
415 : {
416 : CPLDebug( "GeoJSON", "Input file too large to be opened" );
417 : VSIFCloseL( fp );
418 : return FALSE;
419 : }
420 :
421 76 : VSIFSeekL( fp, 0, SEEK_SET );
422 :
423 76 : pszGeoData_ = (char*)VSIMalloc((size_t)(nDataLen + 1));
424 76 : if( NULL == pszGeoData_ )
425 : {
426 0 : VSIFCloseL(fp);
427 0 : return FALSE;
428 : }
429 :
430 76 : pszGeoData_[nDataLen] = '\0';
431 76 : if( ( nDataLen != VSIFReadL( pszGeoData_, 1, (size_t)nDataLen, fp ) ) )
432 : {
433 0 : Clear();
434 0 : VSIFCloseL( fp );
435 0 : return FALSE;
436 : }
437 76 : VSIFCloseL( fp );
438 :
439 76 : pszName_ = CPLStrdup( pszSource );
440 :
441 76 : CPLAssert( NULL != pszGeoData_ );
442 76 : return TRUE;
443 : }
444 :
445 : /************************************************************************/
446 : /* ReadFromService() */
447 : /************************************************************************/
448 :
449 0 : int OGRGeoJSONDataSource::ReadFromService( const char* pszSource )
450 : {
451 0 : CPLAssert( NULL == pszGeoData_ );
452 0 : CPLAssert( NULL != pszSource );
453 :
454 0 : if( eGeoJSONProtocolUnknown == GeoJSONGetProtocolType( pszSource ) )
455 : {
456 0 : CPLDebug( "GeoJSON", "Unknown service type (use HTTP, HTTPS, FTP)" );
457 0 : return FALSE;
458 : }
459 :
460 : /* -------------------------------------------------------------------- */
461 : /* Fetch the GeoJSON result. */
462 : /* -------------------------------------------------------------------- */
463 0 : CPLErrorReset();
464 :
465 0 : CPLHTTPResult* pResult = NULL;
466 0 : char* papsOptions[] = { (char*) "HEADERS=Accept: text/plain, application/json", NULL };
467 :
468 0 : pResult = CPLHTTPFetch( pszSource, papsOptions );
469 :
470 : /* -------------------------------------------------------------------- */
471 : /* Try to handle CURL/HTTP errors. */
472 : /* -------------------------------------------------------------------- */
473 0 : if( NULL == pResult
474 : || 0 == pResult->nDataLen || 0 != CPLGetLastErrorNo() )
475 : {
476 0 : CPLHTTPDestroyResult( pResult );
477 0 : return FALSE;
478 : }
479 :
480 0 : if( 0 != pResult->nStatus )
481 : {
482 : CPLError( CE_Failure, CPLE_AppDefined,
483 : "Curl reports error: %d: %s",
484 0 : pResult->nStatus, pResult->pszErrBuf );
485 0 : CPLHTTPDestroyResult( pResult );
486 0 : return FALSE;
487 : }
488 :
489 : /* -------------------------------------------------------------------- */
490 : /* Copy returned GeoJSON data to text buffer. */
491 : /* -------------------------------------------------------------------- */
492 0 : const char* pszData = reinterpret_cast<char*>(pResult->pabyData);
493 :
494 0 : if ( eGeoJSONProtocolUnknown != GeoJSONGetProtocolType( pszData ) )
495 : {
496 : CPLError( CE_Failure, CPLE_AppDefined,
497 : "The data that was downloaded also starts with "
498 : "protocol prefix (http://, https:// or ftp://) "
499 0 : "and cannot be processed as GeoJSON data.");
500 0 : CPLHTTPDestroyResult( pResult );
501 0 : return FALSE;
502 : }
503 :
504 : // Directly assign CPLHTTPResult::pabyData to pszGeoData_
505 0 : pszGeoData_ = (char*) pszData;
506 0 : pResult->pabyData = NULL;
507 :
508 0 : pszName_ = CPLStrdup( pszSource );
509 :
510 : /* -------------------------------------------------------------------- */
511 : /* Cleanup HTTP resources. */
512 : /* -------------------------------------------------------------------- */
513 0 : CPLHTTPDestroyResult( pResult );
514 :
515 0 : CPLAssert( NULL != pszGeoData_ );
516 0 : return TRUE;
517 : }
518 :
519 : /************************************************************************/
520 : /* LoadLayer() */
521 : /************************************************************************/
522 :
523 82 : OGRGeoJSONLayer* OGRGeoJSONDataSource::LoadLayer()
524 : {
525 82 : if( NULL == pszGeoData_ )
526 : {
527 : CPLError( CE_Failure, CPLE_ObjectNull,
528 0 : "GeoJSON data buffer empty" );
529 0 : return NULL;
530 : }
531 :
532 82 : if ( !GeoJSONIsObject( pszGeoData_) )
533 : {
534 0 : CPLDebug( "GeoJSON", "No valid GeoJSON data found in source '%s'", pszName_ );
535 0 : return NULL;
536 : }
537 :
538 82 : OGRErr err = OGRERR_NONE;
539 82 : OGRGeoJSONLayer* poLayer = NULL;
540 :
541 : /* -------------------------------------------------------------------- */
542 : /* Is it ESRI Feature Service data ? */
543 : /* -------------------------------------------------------------------- */
544 82 : if ( strstr(pszGeoData_, "esriGeometry") ||
545 : strstr(pszGeoData_, "esriFieldTypeOID") )
546 : {
547 16 : OGRESRIJSONReader reader;
548 16 : err = reader.Parse( pszGeoData_ );
549 16 : if( OGRERR_NONE == err )
550 : {
551 : // TODO: Think about better name selection
552 16 : poLayer = reader.ReadLayer( OGRGeoJSONLayer::DefaultName, this );
553 : }
554 :
555 16 : return poLayer;
556 : }
557 :
558 : /* -------------------------------------------------------------------- */
559 : /* Configure GeoJSON format translator. */
560 : /* -------------------------------------------------------------------- */
561 66 : OGRGeoJSONReader reader;
562 :
563 66 : if( eGeometryAsCollection == flTransGeom_ )
564 : {
565 0 : reader.SetPreserveGeometryType( false );
566 0 : CPLDebug( "GeoJSON", "Geometry as OGRGeometryCollection type." );
567 : }
568 :
569 66 : if( eAtributesSkip == flTransAttrs_ )
570 : {
571 0 : reader.SetSkipAttributes( true );
572 0 : CPLDebug( "GeoJSON", "Skip all attributes." );
573 : }
574 :
575 : /* -------------------------------------------------------------------- */
576 : /* Parse GeoJSON and build valid OGRLayer instance. */
577 : /* -------------------------------------------------------------------- */
578 66 : err = reader.Parse( pszGeoData_ );
579 66 : if( OGRERR_NONE == err )
580 : {
581 : // TODO: Think about better name selection
582 66 : poLayer = reader.ReadLayer( OGRGeoJSONLayer::DefaultName, this );
583 : }
584 :
585 66 : return poLayer;
586 : }
|