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