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