1 /******************************************************************************
2 *
3 * Project: OpenGIS Simple Features Reference Implementation
4 * Purpose: Implementation of OGRGeoJSONDriver class (OGR GeoJSON Driver).
5 * Author: Mateusz Loskot, mateusz@loskot.net
6 *
7 ******************************************************************************
8 * Copyright (c) 2007, Mateusz Loskot
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a
11 * copy of this software and associated documentation files (the "Software"),
12 * to deal in the Software without restriction, including without limitation
13 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 * and/or sell copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 ****************************************************************************/
28
29 #include "cpl_port.h"
30 #include "ogr_geojson.h"
31
32 #include <stdlib.h>
33 #include <string.h>
34
35 #include "cpl_conv.h"
36 #include "cpl_error.h"
37 #include "cpl_http.h"
38 #include "cpl_multiproc.h"
39 #include "cpl_string.h"
40 #include "cpl_vsi.h"
41 // #include "json_object.h"
42 #include "gdal.h"
43 #include "gdal_priv.h"
44 #include "ogr_core.h"
45 #include "ogr_feature.h"
46 #include "ogrgeojsonutils.h"
47 #include "ogrsf_frmts.h"
48
49 CPL_CVSID("$Id: ogrgeojsondriver.cpp 834742f5172f1c34b1ffdfd6a0197c88449ffd62 2021-09-02 22:36:02 +0200 Even Rouault $")
50
51 static CPLMutex* ghMutex = nullptr;
52 static char* gpszSource = nullptr;
53 static char* gpszText = nullptr;
54
55 class OGRESRIFeatureServiceDataset;
56
57 /************************************************************************/
58 /* OGRESRIFeatureServiceLayer */
59 /************************************************************************/
60
61 class OGRESRIFeatureServiceLayer final: public OGRLayer
62 {
63 OGRESRIFeatureServiceDataset* poDS;
64 OGRFeatureDefn* poFeatureDefn;
65 GIntBig nFeaturesRead;
66 GIntBig nFirstFID;
67 GIntBig nLastFID;
68 bool bOtherPage;
69 bool bUseSequentialFID;
70
71 public:
72 explicit OGRESRIFeatureServiceLayer( OGRESRIFeatureServiceDataset* poDS );
73 virtual ~OGRESRIFeatureServiceLayer();
74
75 void ResetReading() override;
76 OGRFeature* GetNextFeature() override;
77 GIntBig GetFeatureCount( int bForce = TRUE ) override;
78 OGRErr GetExtent(OGREnvelope *psExtent, int bForce = TRUE) override;
GetExtent(int iGeomField,OGREnvelope * psExtent,int bForce)79 virtual OGRErr GetExtent( int iGeomField, OGREnvelope *psExtent,
80 int bForce) override
81 { return OGRLayer::GetExtent(iGeomField, psExtent, bForce); }
82 int TestCapability( const char* pszCap ) override;
GetLayerDefn()83 OGRFeatureDefn* GetLayerDefn() override { return poFeatureDefn; }
84 };
85
86 /************************************************************************/
87 /* OGRESRIFeatureServiceDataset */
88 /************************************************************************/
89
90 class OGRESRIFeatureServiceDataset final: public GDALDataset
91 {
92 CPLString osURL;
93 GIntBig nFirstOffset;
94 GIntBig nLastOffset;
95 OGRGeoJSONDataSource *poCurrent;
96 OGRESRIFeatureServiceLayer *poLayer;
97
98 int LoadPage();
99
100 public:
101 OGRESRIFeatureServiceDataset( const CPLString &osURL,
102 OGRGeoJSONDataSource* poFirst );
103 ~OGRESRIFeatureServiceDataset();
104
GetLayerCount()105 int GetLayerCount() override { return 1; }
GetLayer(int nLayer)106 OGRLayer* GetLayer( int nLayer ) override
107 { return (nLayer == 0) ? poLayer : nullptr; }
108
GetUnderlyingLayer()109 OGRLayer* GetUnderlyingLayer() { return poCurrent->GetLayer(0); }
110
111 int MyResetReading();
112 int LoadNextPage();
113
GetURL()114 const CPLString& GetURL() { return osURL; }
115 };
116
117 /************************************************************************/
118 /* OGRESRIFeatureServiceLayer() */
119 /************************************************************************/
120
OGRESRIFeatureServiceLayer(OGRESRIFeatureServiceDataset * poDSIn)121 OGRESRIFeatureServiceLayer::OGRESRIFeatureServiceLayer(
122 OGRESRIFeatureServiceDataset* poDSIn) :
123 poDS(poDSIn),
124 nFeaturesRead(0),
125 nFirstFID(0),
126 nLastFID(0),
127 bOtherPage(false),
128 bUseSequentialFID(false)
129 {
130 OGRFeatureDefn* poSrcFeatDefn = poDS->GetUnderlyingLayer()->GetLayerDefn();
131 poFeatureDefn = new OGRFeatureDefn(poSrcFeatDefn->GetName());
132 SetDescription(poFeatureDefn->GetName());
133 poFeatureDefn->Reference();
134 poFeatureDefn->SetGeomType(wkbNone);
135
136 for( int i = 0; i < poSrcFeatDefn->GetFieldCount(); i++ )
137 poFeatureDefn->AddFieldDefn(poSrcFeatDefn->GetFieldDefn(i));
138
139 for( int i = 0; i <poSrcFeatDefn->GetGeomFieldCount(); i++ )
140 poFeatureDefn->AddGeomFieldDefn(poSrcFeatDefn->GetGeomFieldDefn(i));
141 }
142
143 /************************************************************************/
144 /* ~OGRESRIFeatureServiceLayer() */
145 /************************************************************************/
146
~OGRESRIFeatureServiceLayer()147 OGRESRIFeatureServiceLayer::~OGRESRIFeatureServiceLayer()
148 {
149 poFeatureDefn->Release();
150 }
151
152 /************************************************************************/
153 /* ResetReading() */
154 /************************************************************************/
155
ResetReading()156 void OGRESRIFeatureServiceLayer::ResetReading()
157 {
158 poDS->MyResetReading();
159 nFeaturesRead = 0;
160 nLastFID = 0;
161 bOtherPage = false;
162 bUseSequentialFID = false;
163 }
164
165 /************************************************************************/
166 /* GetNextFeature() */
167 /************************************************************************/
168
GetNextFeature()169 OGRFeature* OGRESRIFeatureServiceLayer::GetNextFeature()
170 {
171 while( true )
172 {
173 const bool bWasInFirstPage = !bOtherPage;
174 OGRFeature* poSrcFeat = poDS->GetUnderlyingLayer()->GetNextFeature();
175 if( poSrcFeat == nullptr )
176 {
177 if( !poDS->LoadNextPage() )
178 return nullptr;
179 poSrcFeat = poDS->GetUnderlyingLayer()->GetNextFeature();
180 if( poSrcFeat == nullptr )
181 return nullptr;
182 bOtherPage = true;
183 if( bWasInFirstPage && poSrcFeat->GetFID() != 0 &&
184 poSrcFeat->GetFID() == nFirstFID )
185 {
186 // End-less looping
187 CPLDebug("ESRIJSON", "Scrolling not working. Stopping");
188 delete poSrcFeat;
189 return nullptr;
190 }
191 if( bWasInFirstPage && poSrcFeat->GetFID() == 0 &&
192 nLastFID == nFeaturesRead - 1 )
193 {
194 bUseSequentialFID = true;
195 }
196 }
197 if( nFeaturesRead == 0 )
198 nFirstFID = poSrcFeat->GetFID();
199
200 OGRFeature* poFeature = new OGRFeature(poFeatureDefn);
201 poFeature->SetFrom(poSrcFeat);
202 if( bUseSequentialFID )
203 poFeature->SetFID(nFeaturesRead);
204 else
205 poFeature->SetFID(poSrcFeat->GetFID());
206 nLastFID = poFeature->GetFID();
207 nFeaturesRead ++;
208 delete poSrcFeat;
209
210 if( (m_poFilterGeom == nullptr
211 || FilterGeometry( poFeature->GetGeometryRef() ) )
212 && (m_poAttrQuery == nullptr
213 || m_poAttrQuery->Evaluate( poFeature )) )
214 {
215 return poFeature;
216 }
217 delete poFeature;
218 }
219 }
220
221 /************************************************************************/
222 /* TestCapability() */
223 /************************************************************************/
224
TestCapability(const char * pszCap)225 int OGRESRIFeatureServiceLayer::TestCapability( const char* pszCap )
226 {
227 if( EQUAL(pszCap, OLCFastFeatureCount) )
228 return m_poAttrQuery == nullptr && m_poFilterGeom == nullptr;
229 if( EQUAL(pszCap, OLCFastGetExtent) )
230 return FALSE;
231 return poDS->GetUnderlyingLayer()->TestCapability(pszCap);
232 }
233
234 /************************************************************************/
235 /* GetFeatureCount() */
236 /************************************************************************/
237
GetFeatureCount(int bForce)238 GIntBig OGRESRIFeatureServiceLayer::GetFeatureCount( int bForce )
239 {
240 GIntBig nFeatureCount = -1;
241 if( m_poAttrQuery == nullptr && m_poFilterGeom == nullptr )
242 {
243 CPLString osNewURL =
244 CPLURLAddKVP(poDS->GetURL(), "returnCountOnly", "true");
245 osNewURL = CPLURLAddKVP(osNewURL, "resultRecordCount", nullptr);
246 CPLErrorReset();
247 CPLHTTPResult* pResult = CPLHTTPFetch( osNewURL, nullptr );
248 if( pResult != nullptr &&
249 pResult->nDataLen != 0 &&
250 CPLGetLastErrorNo() == 0 &&
251 pResult->nStatus == 0 )
252 {
253 const char* pszCount =
254 strstr((const char*)pResult->pabyData, "\"count\"");
255 if( pszCount )
256 {
257 pszCount = strchr(pszCount, ':');
258 if( pszCount )
259 {
260 pszCount++;
261 nFeatureCount = CPLAtoGIntBig(pszCount);
262 }
263 }
264 }
265 CPLHTTPDestroyResult( pResult );
266 }
267 if( nFeatureCount < 0 )
268 nFeatureCount = OGRLayer::GetFeatureCount(bForce);
269 return nFeatureCount;
270 }
271
272 /************************************************************************/
273 /* GetExtent() */
274 /************************************************************************/
275
GetExtent(OGREnvelope * psExtent,int bForce)276 OGRErr OGRESRIFeatureServiceLayer::GetExtent( OGREnvelope *psExtent,
277 int bForce )
278 {
279 OGRErr eErr = OGRERR_FAILURE;
280 CPLString osNewURL =
281 CPLURLAddKVP(poDS->GetURL(), "returnExtentOnly", "true");
282 osNewURL = CPLURLAddKVP(osNewURL, "resultRecordCount", nullptr);
283 osNewURL = CPLURLAddKVP(osNewURL, "f", "geojson");
284 CPLErrorReset();
285 CPLHTTPResult* pResult = CPLHTTPFetch( osNewURL, nullptr );
286 if( pResult != nullptr && pResult->nDataLen != 0 && CPLGetLastErrorNo() == 0 &&
287 pResult->nStatus == 0 )
288 {
289 const char* pszBBox =
290 strstr((const char*)pResult->pabyData, "\"bbox\"");
291 if( pszBBox )
292 {
293 pszBBox = strstr(pszBBox, ":[");
294 if( pszBBox )
295 {
296 pszBBox += 2;
297 char** papszTokens = CSLTokenizeString2(pszBBox, ",", 0);
298 if( CSLCount(papszTokens) >= 4 )
299 {
300 psExtent->MinX = CPLAtof(papszTokens[0]);
301 psExtent->MinY = CPLAtof(papszTokens[1]);
302 psExtent->MaxX = CPLAtof(papszTokens[2]);
303 psExtent->MaxY = CPLAtof(papszTokens[3]);
304 eErr = OGRERR_NONE;
305 }
306 CSLDestroy(papszTokens);
307 }
308 }
309 }
310 CPLHTTPDestroyResult( pResult );
311 if( eErr == OGRERR_FAILURE )
312 eErr = OGRLayer::GetExtent(psExtent, bForce);
313 return eErr;
314 }
315
316 /************************************************************************/
317 /* OGRESRIFeatureServiceDataset() */
318 /************************************************************************/
319
OGRESRIFeatureServiceDataset(const CPLString & osURLIn,OGRGeoJSONDataSource * poFirst)320 OGRESRIFeatureServiceDataset::OGRESRIFeatureServiceDataset(
321 const CPLString &osURLIn,
322 OGRGeoJSONDataSource* poFirst) :
323 poCurrent(poFirst)
324 {
325 poLayer = new OGRESRIFeatureServiceLayer(this);
326 osURL = osURLIn;
327 if( CPLURLGetValue(osURL, "resultRecordCount").empty() )
328 {
329 // We assume that if the server sets the exceededTransferLimit, the
330 // and resultRecordCount is not set, the number of features returned
331 // in our first request is the maximum allowed by the server
332 // So set it for following requests.
333 osURL =
334 CPLURLAddKVP(
335 this->osURL, "resultRecordCount",
336 CPLSPrintf(
337 "%d",
338 static_cast<int>(poFirst->GetLayer(0)->GetFeatureCount())));
339 }
340 else
341 {
342 const int nUserSetRecordCount =
343 atoi(CPLURLGetValue(osURL, "resultRecordCount"));
344 if( nUserSetRecordCount > poFirst->GetLayer(0)->GetFeatureCount() )
345 {
346 CPLError(CE_Warning, CPLE_AppDefined,
347 "Specified resultRecordCount=%d is greater than "
348 "the maximum %d supported by the server",
349 nUserSetRecordCount,
350 static_cast<int>(poFirst->GetLayer(0)->GetFeatureCount()));
351 }
352 }
353 nFirstOffset = CPLAtoGIntBig(CPLURLGetValue(osURL, "resultOffset"));
354 nLastOffset = nFirstOffset;
355 }
356
357 /************************************************************************/
358 /* ~OGRESRIFeatureServiceDataset() */
359 /************************************************************************/
360
~OGRESRIFeatureServiceDataset()361 OGRESRIFeatureServiceDataset::~OGRESRIFeatureServiceDataset()
362 {
363 delete poCurrent;
364 delete poLayer;
365 }
366
367 /************************************************************************/
368 /* MyResetReading() */
369 /************************************************************************/
370
MyResetReading()371 int OGRESRIFeatureServiceDataset::MyResetReading()
372 {
373 if( nLastOffset > nFirstOffset )
374 {
375 nLastOffset = nFirstOffset;
376 return LoadPage();
377 }
378
379 poCurrent->GetLayer(0)->ResetReading();
380 return TRUE;
381 }
382
383 /************************************************************************/
384 /* LoadNextPage() */
385 /************************************************************************/
386
LoadNextPage()387 int OGRESRIFeatureServiceDataset::LoadNextPage()
388 {
389 if( !poCurrent->HasOtherPages() )
390 return FALSE;
391 nLastOffset += poCurrent->GetLayer(0)->GetFeatureCount();
392 return LoadPage();
393 }
394
395 /************************************************************************/
396 /* LoadPage() */
397 /************************************************************************/
398
LoadPage()399 int OGRESRIFeatureServiceDataset::LoadPage()
400 {
401 CPLString osNewURL = CPLURLAddKVP(osURL, "resultOffset",
402 CPLSPrintf(CPL_FRMT_GIB, nLastOffset));
403 OGRGeoJSONDataSource* poDS = new OGRGeoJSONDataSource();
404 GDALOpenInfo oOpenInfo(osNewURL, GA_ReadOnly);
405 GeoJSONSourceType nSrcType;
406 if( EQUAL(poCurrent->GetJSonFlavor(), "GeoJSON") )
407 nSrcType = GeoJSONGetSourceType( &oOpenInfo );
408 else
409 nSrcType = ESRIJSONDriverGetSourceType( &oOpenInfo );
410 if( !poDS->Open( &oOpenInfo, nSrcType,
411 poCurrent->GetJSonFlavor() ) ||
412 poDS->GetLayerCount() == 0 )
413 {
414 delete poDS;
415 poDS = nullptr;
416 return FALSE;
417 }
418 delete poCurrent;
419 poCurrent = poDS;
420 return TRUE;
421 }
422
423 /************************************************************************/
424 /* OGRGeoJSONDriverIdentify() */
425 /************************************************************************/
426
OGRGeoJSONDriverIdentifyInternal(GDALOpenInfo * poOpenInfo,GeoJSONSourceType & nSrcType)427 static int OGRGeoJSONDriverIdentifyInternal( GDALOpenInfo* poOpenInfo,
428 GeoJSONSourceType& nSrcType )
429 {
430 /* -------------------------------------------------------------------- */
431 /* Determine type of data source: text file (.geojson, .json), */
432 /* Web Service or text passed directly and load data. */
433 /* -------------------------------------------------------------------- */
434
435 nSrcType = GeoJSONGetSourceType( poOpenInfo );
436 if( nSrcType == eGeoJSONSourceUnknown )
437 return FALSE;
438 if( nSrcType == eGeoJSONSourceService &&
439 !STARTS_WITH_CI(poOpenInfo->pszFilename, "GeoJSON:") )
440 {
441 return -1;
442 }
443
444 // If this looks like a file that can be handled by the STACTA driver,
445 // and that one is available, then don't identify the file.
446 const char* pszHeader = reinterpret_cast<const char*>(poOpenInfo->pabyHeader);
447 if( pszHeader != nullptr &&
448 strstr(pszHeader, "\"stac_extensions\"") != nullptr &&
449 strstr(pszHeader, "\"tiled-assets\"") != nullptr &&
450 GDALGetDriverByName("STACTA") != nullptr )
451 {
452 return FALSE;
453 }
454
455 return TRUE;
456 }
457
458 /************************************************************************/
459 /* OGRGeoJSONDriverIdentify() */
460 /************************************************************************/
461
OGRGeoJSONDriverIdentify(GDALOpenInfo * poOpenInfo)462 static int OGRGeoJSONDriverIdentify( GDALOpenInfo* poOpenInfo )
463 {
464 GeoJSONSourceType nSrcType;
465 return OGRGeoJSONDriverIdentifyInternal(poOpenInfo, nSrcType);
466 }
467
468 /************************************************************************/
469 /* Open() */
470 /************************************************************************/
471
OGRGeoJSONDriverOpen(GDALOpenInfo * poOpenInfo)472 static GDALDataset* OGRGeoJSONDriverOpen( GDALOpenInfo* poOpenInfo )
473 {
474 GeoJSONSourceType nSrcType;
475 if( OGRGeoJSONDriverIdentifyInternal(poOpenInfo, nSrcType) == FALSE )
476 {
477 return nullptr;
478 }
479 return OGRGeoJSONDriverOpenInternal(poOpenInfo, nSrcType, "GeoJSON");
480 }
481
482 /************************************************************************/
483 /* OGRGeoJSONDriverOpenInternal() */
484 /************************************************************************/
485
OGRGeoJSONDriverOpenInternal(GDALOpenInfo * poOpenInfo,GeoJSONSourceType nSrcType,const char * pszJSonFlavor)486 GDALDataset* OGRGeoJSONDriverOpenInternal( GDALOpenInfo* poOpenInfo,
487 GeoJSONSourceType nSrcType,
488 const char* pszJSonFlavor )
489 {
490 OGRGeoJSONDataSource* poDS = new OGRGeoJSONDataSource();
491
492 /* -------------------------------------------------------------------- */
493 /* Processing configuration options. */
494 /* -------------------------------------------------------------------- */
495
496 // TODO: Currently, options are based on environment variables.
497 // This is workaround for not yet implemented Andrey's concept
498 // described in document 'RFC 10: OGR Open Parameters'.
499
500 poDS->SetGeometryTranslation( OGRGeoJSONDataSource::eGeometryPreserve );
501 const char* pszOpt = CPLGetConfigOption("GEOMETRY_AS_COLLECTION", nullptr);
502 if( nullptr != pszOpt && STARTS_WITH_CI(pszOpt, "YES") )
503 {
504 poDS->SetGeometryTranslation(
505 OGRGeoJSONDataSource::eGeometryAsCollection );
506 }
507
508 poDS->SetAttributesTranslation( OGRGeoJSONDataSource::eAttributesPreserve );
509 pszOpt = CPLGetConfigOption("ATTRIBUTES_SKIP", nullptr);
510 if( nullptr != pszOpt && STARTS_WITH_CI(pszOpt, "YES") )
511 {
512 poDS->SetAttributesTranslation(
513 OGRGeoJSONDataSource::eAttributesSkip );
514 }
515
516 /* -------------------------------------------------------------------- */
517 /* Open and start processing GeoJSON datasource to OGR objects. */
518 /* -------------------------------------------------------------------- */
519 if( !poDS->Open( poOpenInfo, nSrcType, pszJSonFlavor ) )
520 {
521 delete poDS;
522 poDS = nullptr;
523 }
524
525 if( poDS != nullptr && poDS->HasOtherPages() )
526 {
527 const char* pszFilename = poOpenInfo->pszFilename;
528 if( STARTS_WITH_CI(pszFilename, "ESRIJSON:") )
529 pszFilename += strlen("ESRIJSON:");
530 if( STARTS_WITH(pszFilename, "http") ||
531 STARTS_WITH(pszFilename, "/vsimem/") )
532 {
533 const char* pszFSP = CSLFetchNameValue(poOpenInfo->papszOpenOptions,
534 "FEATURE_SERVER_PAGING");
535 const bool bHasResultOffset =
536 !CPLURLGetValue(pszFilename, "resultOffset").empty();
537 if( (!bHasResultOffset && (pszFSP == nullptr || CPLTestBool(pszFSP))) ||
538 (bHasResultOffset && pszFSP != nullptr && CPLTestBool(pszFSP)) )
539 {
540 return new OGRESRIFeatureServiceDataset(pszFilename,
541 poDS);
542 }
543 }
544 }
545
546 return poDS;
547 }
548
549 /************************************************************************/
550 /* Create() */
551 /************************************************************************/
552
OGRGeoJSONDriverCreate(const char * pszName,int,int,int,GDALDataType,char ** papszOptions)553 static GDALDataset *OGRGeoJSONDriverCreate( const char * pszName,
554 int /* nBands */,
555 int /* nXSize */,
556 int /* nYSize */,
557 GDALDataType /* eDT */,
558 char **papszOptions )
559 {
560 OGRGeoJSONDataSource* poDS = new OGRGeoJSONDataSource();
561
562 if( !poDS->Create( pszName, papszOptions ) )
563 {
564 delete poDS;
565 poDS = nullptr;
566 }
567
568 return poDS;
569 }
570
571 /************************************************************************/
572 /* Delete() */
573 /************************************************************************/
574
OGRGeoJSONDriverDelete(const char * pszFilename)575 static CPLErr OGRGeoJSONDriverDelete( const char *pszFilename )
576 {
577 if( VSIUnlink( pszFilename ) == 0 )
578 {
579 return CE_None;
580 }
581
582 CPLDebug( "GeoJSON", "Failed to delete \'%s\'", pszFilename);
583
584 return CE_Failure;
585 }
586
587 /************************************************************************/
588 /* OGRGeoJSONDriverStoreContent() */
589 /************************************************************************/
590
OGRGeoJSONDriverStoreContent(const char * pszSource,char * pszText)591 void OGRGeoJSONDriverStoreContent( const char* pszSource, char* pszText )
592 {
593 CPLMutexHolderD(&ghMutex);
594 CPLAssert( pszSource );
595 CPLAssert( pszText );
596
597 CPLFree(gpszSource);
598 CPLFree(gpszText);
599 gpszSource = CPLStrdup(pszSource);
600 gpszText = pszText;
601 }
602
603 /************************************************************************/
604 /* OGRGeoJSONDriverStealStoredContent() */
605 /************************************************************************/
606
OGRGeoJSONDriverStealStoredContent(const char * pszSource)607 char* OGRGeoJSONDriverStealStoredContent( const char* pszSource )
608 {
609 CPLMutexHolderD(&ghMutex);
610 if( gpszSource && EQUAL(pszSource, gpszSource) )
611 {
612 char* pszRet = gpszText;
613 CPLFree(gpszSource);
614 gpszSource = nullptr;
615 gpszText = nullptr;
616 return pszRet;
617 }
618 return nullptr;
619 }
620
621 /************************************************************************/
622 /* OGRGeoJSONDriverUnload() */
623 /************************************************************************/
624
OGRGeoJSONDriverUnload(GDALDriver *)625 static void OGRGeoJSONDriverUnload( GDALDriver* )
626 {
627 if( ghMutex )
628 CPLDestroyMutex(ghMutex);
629 ghMutex = nullptr;
630 CPLFree(gpszSource);
631 CPLFree(gpszText);
632 gpszSource = nullptr;
633 gpszText = nullptr;
634 }
635
636 /************************************************************************/
637 /* RegisterOGRGeoJSON() */
638 /************************************************************************/
639
RegisterOGRGeoJSON()640 void RegisterOGRGeoJSON()
641 {
642 if( !GDAL_CHECK_VERSION("OGR/GeoJSON driver") )
643 return;
644
645 if( GDALGetDriverByName( "GeoJSON" ) != nullptr )
646 return;
647
648 GDALDriver *poDriver = new GDALDriver();
649
650 poDriver->SetDescription( "GeoJSON" );
651 poDriver->SetMetadataItem( GDAL_DCAP_VECTOR, "YES" );
652 poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "GeoJSON" );
653 poDriver->SetMetadataItem( GDAL_DMD_EXTENSIONS, "json geojson" );
654 poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "drivers/vector/geojson.html" );
655
656 poDriver->SetMetadataItem( GDAL_DMD_OPENOPTIONLIST,
657 "<OpenOptionList>"
658 " <Option name='FLATTEN_NESTED_ATTRIBUTES' type='boolean' description='Whether to recursively explore nested objects and produce flatten OGR attributes' default='NO'/>"
659 " <Option name='NESTED_ATTRIBUTE_SEPARATOR' type='string' description='Separator between components of nested attributes' default='_'/>"
660 " <Option name='FEATURE_SERVER_PAGING' type='boolean' description='Whether to automatically scroll through results with a ArcGIS Feature Service endpoint'/>"
661 " <Option name='NATIVE_DATA' type='boolean' description='Whether to store the native JSon representation at FeatureCollection and Feature level' default='NO'/>"
662 " <Option name='ARRAY_AS_STRING' type='boolean' description='Whether to expose JSon arrays of strings, integers or reals as a OGR String' default='NO'/>"
663 " <Option name='DATE_AS_STRING' type='boolean' description='Whether to expose date/time/date-time content using dedicated OGR date/time/date-time types or as a OGR String' default='NO'/>"
664 "</OpenOptionList>");
665
666 poDriver->SetMetadataItem( GDAL_DMD_CREATIONOPTIONLIST,
667 "<CreationOptionList/>");
668
669 poDriver->SetMetadataItem( GDAL_DS_LAYER_CREATIONOPTIONLIST,
670 "<LayerCreationOptionList>"
671 " <Option name='WRITE_BBOX' type='boolean' description='whether to write a bbox property with the bounding box of the geometries at the feature and feature collection level' default='NO'/>"
672 " <Option name='COORDINATE_PRECISION' type='int' description='Number of decimal for coordinates. Default is 15 for GJ2008 and 7 for RFC7946'/>"
673 " <Option name='SIGNIFICANT_FIGURES' type='int' description='Number of significant figures for floating-point values' default='17'/>"
674 " <Option name='NATIVE_DATA' type='string' description='FeatureCollection level elements.'/>"
675 " <Option name='NATIVE_MEDIA_TYPE' type='string' description='Format of NATIVE_DATA. Must be \"application/vnd.geo+json\", otherwise NATIVE_DATA will be ignored.'/>"
676 " <Option name='RFC7946' type='boolean' description='Whether to use RFC 7946 standard. Otherwise GeoJSON 2008 initial version will be used' default='NO'/>"
677 " <Option name='WRITE_NAME' type='boolean' description='Whether to write a "name" property at feature collection level with layer name' default='YES'/>"
678 " <Option name='DESCRIPTION' type='string' description='(Long) description to write in a "description" property at feature collection level'/>"
679 " <Option name='ID_FIELD' type='string' description='Name of the source field that must be used as the id member of Feature features'/>"
680 " <Option name='ID_TYPE' type='string-select' description='Type of the id member of Feature features'>"
681 " <Value>AUTO</Value>"
682 " <Value>String</Value>"
683 " <Value>Integer</Value>"
684 " </Option>"
685 " <Option name='ID_GENERATE' type='boolean' description='Auto-generate feature ids' />"
686 " <Option name='WRITE_NON_FINITE_VALUES' type='boolean' description='Whether to write NaN / Infinity values' default='NO'/>"
687 "</LayerCreationOptionList>");
688
689 poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
690 poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATATYPES,
691 "Integer Integer64 Real String IntegerList "
692 "Integer64List RealList StringList Date DateTime" );
693 poDriver->SetMetadataItem( GDAL_DMD_CREATIONFIELDDATASUBTYPES, "Boolean" );
694
695 poDriver->pfnOpen = OGRGeoJSONDriverOpen;
696 poDriver->pfnIdentify = OGRGeoJSONDriverIdentify;
697 poDriver->pfnCreate = OGRGeoJSONDriverCreate;
698 poDriver->pfnDelete = OGRGeoJSONDriverDelete;
699 poDriver->pfnUnloadDriver = OGRGeoJSONDriverUnload;
700
701 GetGDALDriverManager()->RegisterDriver( poDriver );
702 }
703