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