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