1 /*******************************************************************************
2 * Project: NextGIS Web Driver
3 * Purpose: Implements NextGIS Web Driver
4 * Author: Dmitry Baryshnikov, dmitry.baryshnikov@nextgis.com
5 * Language: C++
6 *******************************************************************************
7 * The MIT License (MIT)
8 *
9 * Copyright (c) 2018-2020, NextGIS <info@nextgis.com>
10 *
11 * Permission is hereby granted, free of charge, to any person obtaining a copy
12 * of this software and associated documentation files (the "Software"), to deal
13 * in the Software without restriction, including without limitation the rights
14 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15 * copies of the Software, and to permit persons to whom the Software is
16 * furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included in all
19 * copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27 * SOFTWARE.
28 *******************************************************************************/
29
30 #include "ogr_ngw.h"
31
32 #include "cpl_http.h"
33 #include "gdal_proxy.h"
34
35 class NGWWrapperRasterBand : public GDALProxyRasterBand
36 {
37 GDALRasterBand *poBaseBand;
38
39 protected:
RefUnderlyingRasterBand()40 virtual GDALRasterBand *RefUnderlyingRasterBand() override { return poBaseBand; }
41
42 public:
NGWWrapperRasterBand(GDALRasterBand * poBaseBandIn)43 explicit NGWWrapperRasterBand( GDALRasterBand* poBaseBandIn ) :
44 poBaseBand( poBaseBandIn )
45 {
46 eDataType = poBaseBand->GetRasterDataType();
47 poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
48 }
~NGWWrapperRasterBand()49 virtual ~NGWWrapperRasterBand() {}
50 };
51
52 /*
53 * OGRNGWDataset()
54 */
OGRNGWDataset()55 OGRNGWDataset::OGRNGWDataset() :
56 nBatchSize(-1),
57 nPageSize(-1),
58 bFetchedPermissions(false),
59 bHasFeaturePaging(false),
60 bExtInNativeData(false),
61 bMetadataDerty(false),
62 papoLayers(nullptr),
63 nLayers(0),
64 poRasterDS(nullptr),
65 nRasters(0),
66 nCacheExpires(604800), // 7 days
67 nCacheMaxSize(67108864),// 64 MB
68 osJsonDepth("32")
69 {}
70
71 /*
72 * ~OGRNGWDataset()
73 */
~OGRNGWDataset()74 OGRNGWDataset::~OGRNGWDataset()
75 {
76 // Last sync with server.
77 OGRNGWDataset::FlushCache();
78
79 if( poRasterDS != nullptr )
80 {
81 GDALClose( poRasterDS );
82 poRasterDS = nullptr;
83 }
84
85 for( int i = 0; i < nLayers; ++i )
86 {
87 delete papoLayers[i];
88 }
89 CPLFree( papoLayers );
90 }
91
92 /*
93 * FetchPermissions()
94 */
FetchPermissions()95 void OGRNGWDataset::FetchPermissions()
96 {
97 if( bFetchedPermissions )
98 {
99 return;
100 }
101
102 if( IsUpdateMode() )
103 {
104 // Check connection and is it read only.
105 char **papszHTTPOptions = GetHeaders();
106 stPermissions = NGWAPI::CheckPermissions( osUrl, osResourceId,
107 papszHTTPOptions, IsUpdateMode() );
108 CSLDestroy( papszHTTPOptions );
109 }
110 else
111 {
112 stPermissions.bDataCanRead = true;
113 stPermissions.bResourceCanRead = true;
114 stPermissions.bDatastructCanRead = true;
115 stPermissions.bMetadataCanRead = true;
116 }
117 bFetchedPermissions = true;
118 }
119
120 /*
121 * TestCapability()
122 */
TestCapability(const char * pszCap)123 int OGRNGWDataset::TestCapability( const char *pszCap )
124 {
125 FetchPermissions();
126 if( EQUAL(pszCap, ODsCCreateLayer) )
127 {
128 return stPermissions.bResourceCanCreate;
129 }
130 else if( EQUAL(pszCap, ODsCDeleteLayer) )
131 {
132 return stPermissions.bResourceCanDelete;
133 }
134 else if( EQUAL(pszCap, "RenameLayer") )
135 {
136 return stPermissions.bResourceCanUpdate;
137 }
138 else if( EQUAL(pszCap, ODsCRandomLayerWrite) )
139 {
140 return stPermissions.bDataCanWrite; // FIXME: Check on resource level is this permission set?
141 }
142 else if( EQUAL(pszCap, ODsCRandomLayerRead) )
143 {
144 return stPermissions.bDataCanRead;
145 }
146 else
147 {
148 return FALSE;
149 }
150 }
151
152 /*
153 * GetLayer()
154 */
GetLayer(int iLayer)155 OGRLayer *OGRNGWDataset::GetLayer( int iLayer )
156 {
157 if( iLayer < 0 || iLayer >= nLayers )
158 {
159 return nullptr;
160 }
161 else
162 {
163 return papoLayers[iLayer];
164 }
165 }
166
167 /*
168 * Open()
169 */
Open(const std::string & osUrlIn,const std::string & osResourceIdIn,char ** papszOpenOptionsIn,bool bUpdateIn,int nOpenFlagsIn)170 bool OGRNGWDataset::Open( const std::string &osUrlIn,
171 const std::string &osResourceIdIn, char **papszOpenOptionsIn,
172 bool bUpdateIn, int nOpenFlagsIn )
173 {
174 osUrl = osUrlIn;
175 osResourceId = osResourceIdIn;
176
177 eAccess = bUpdateIn ? GA_Update : GA_ReadOnly;
178
179 osUserPwd = CSLFetchNameValueDef( papszOpenOptionsIn, "USERPWD",
180 CPLGetConfigOption("NGW_USERPWD", ""));
181
182 nBatchSize = atoi( CSLFetchNameValueDef( papszOpenOptionsIn,
183 "BATCH_SIZE", CPLGetConfigOption("NGW_BATCH_SIZE", "-1") ) );
184
185 nPageSize = atoi( CSLFetchNameValueDef(papszOpenOptionsIn, "PAGE_SIZE",
186 CPLGetConfigOption("NGW_PAGE_SIZE", "-1") ) );
187 if( nPageSize == 0 )
188 {
189 nPageSize = -1;
190 }
191
192 nCacheExpires = atoi( CSLFetchNameValueDef(papszOpenOptionsIn, "CACHE_EXPIRES",
193 CPLGetConfigOption("NGW_CACHE_EXPIRES", "604800") ) );
194
195 nCacheMaxSize = atoi( CSLFetchNameValueDef(papszOpenOptionsIn, "CACHE_MAX_SIZE",
196 CPLGetConfigOption("NGW_CACHE_MAX_SIZE", "67108864") ) );
197
198 bExtInNativeData = CPLFetchBool( papszOpenOptionsIn, "NATIVE_DATA",
199 CPLTestBool( CPLGetConfigOption("NGW_NATIVE_DATA", "NO") ) );
200
201 osJsonDepth = CSLFetchNameValueDef( papszOpenOptionsIn, "JSON_DEPTH",
202 CPLGetConfigOption("NGW_JSON_DEPTH", "32"));
203
204 osExtensions = CSLFetchNameValueDef(papszOpenOptionsIn, "EXTENSIONS",
205 CPLGetConfigOption("NGW_EXTENSIONS", ""));
206
207 if (osExtensions.empty())
208 {
209 bExtInNativeData = false;
210 }
211
212 return Init( nOpenFlagsIn );
213 }
214
215 /*
216 * Open()
217 *
218 * The pszFilename templates:
219 * - NGW:http://some.nextgis.com/resource/0
220 * - NGW:http://some.nextgis.com:8000/test/resource/0
221 */
Open(const char * pszFilename,char ** papszOpenOptionsIn,bool bUpdateIn,int nOpenFlagsIn)222 bool OGRNGWDataset::Open( const char *pszFilename, char **papszOpenOptionsIn,
223 bool bUpdateIn, int nOpenFlagsIn )
224 {
225 NGWAPI::Uri stUri = NGWAPI::ParseUri(pszFilename);
226
227 if( stUri.osPrefix != "NGW" )
228 {
229 CPLError(CE_Failure, CPLE_NotSupported,
230 "Unsupported name %s", pszFilename);
231 return false;
232 }
233
234 osUrl = stUri.osAddress;
235 osResourceId = stUri.osResourceId;
236
237 return Open( stUri.osAddress, stUri.osResourceId, papszOpenOptionsIn,
238 bUpdateIn, nOpenFlagsIn );
239 }
240
241 /*
242 * Init()
243 */
Init(int nOpenFlagsIn)244 bool OGRNGWDataset::Init(int nOpenFlagsIn)
245 {
246 // NOTE: Skip check API version at that moment. We expected API v3.
247
248 // Get resource details.
249 CPLJSONDocument oResourceDetailsReq;
250 char **papszHTTPOptions = GetHeaders();
251 bool bResult = oResourceDetailsReq.LoadUrl( NGWAPI::GetResource( osUrl,
252 osResourceId ), papszHTTPOptions );
253
254 CPLDebug("NGW", "Get resource %s details %s", osResourceId.c_str(),
255 bResult ? "success" : "failed");
256
257 if( bResult )
258 {
259 CPLJSONObject oRoot = oResourceDetailsReq.GetRoot();
260
261 if( oRoot.IsValid() )
262 {
263 std::string osResourceType = oRoot.GetString("resource/cls");
264 FillMetadata( oRoot );
265
266 if( osResourceType == "resource_group" )
267 {
268 // Check feature paging.
269 FillCapabilities( papszHTTPOptions );
270 if( oRoot.GetBool( "resource/children", false ) ) {
271 // Get child resources.
272 bResult = FillResources( papszHTTPOptions, nOpenFlagsIn );
273 }
274 }
275 else if( (osResourceType == "vector_layer" ||
276 osResourceType == "postgis_layer") )
277 {
278 // Check feature paging.
279 FillCapabilities( papszHTTPOptions );
280 // Add vector layer.
281 AddLayer( oRoot, papszHTTPOptions, nOpenFlagsIn );
282 }
283 else if( osResourceType == "mapserver_style" ||
284 osResourceType == "qgis_vector_style" ||
285 osResourceType == "raster_style" ||
286 osResourceType == "qgis_raster_style" ||
287 osResourceType == "wmsclient_layer" )
288 {
289 // GetExtent from parent.
290 OGREnvelope stExtent;
291 std::string osParentId = oRoot.GetString("resource/parent/id");
292 bool bExtentResult = NGWAPI::GetExtent(osUrl, osParentId,
293 papszHTTPOptions, 3857, stExtent);
294
295 if( !bExtentResult )
296 {
297 // Set full extent for EPSG:3857.
298 stExtent.MinX = -20037508.34;
299 stExtent.MaxX = 20037508.34;
300 stExtent.MinY = -20037508.34;
301 stExtent.MaxY = 20037508.34;
302 }
303
304 CPLDebug("NGW", "Raster extent is: %f, %f, %f, %f",
305 stExtent.MinX, stExtent.MinY,
306 stExtent.MaxX, stExtent.MaxY);
307
308 int nEPSG = 3857;
309 // Get parent details. We can skip this as default SRS in NGW is 3857.
310 if( osResourceType == "wmsclient_layer" )
311 {
312 nEPSG = oRoot.GetInteger("wmsclient_layer/srs/id", nEPSG);
313 }
314 else
315 {
316 CPLJSONDocument oResourceReq;
317 bResult = oResourceReq.LoadUrl( NGWAPI::GetResource( osUrl,
318 osResourceId ), papszHTTPOptions );
319
320 if( bResult )
321 {
322 CPLJSONObject oParentRoot = oResourceReq.GetRoot();
323 if( osResourceType == "mapserver_style" ||
324 osResourceType == "qgis_vector_style" )
325 {
326 nEPSG = oParentRoot.GetInteger("vector_layer/srs/id", nEPSG);
327 }
328 else if( osResourceType == "raster_style" ||
329 osResourceType == "qgis_raster_style")
330 {
331 nEPSG = oParentRoot.GetInteger("raster_layer/srs/id", nEPSG);
332 }
333 }
334 }
335
336 // Create raster dataset.
337 std::string osRasterUrl = NGWAPI::GetTMS(osUrl, osResourceId);
338 char* pszRasterUrl = CPLEscapeString(osRasterUrl.c_str(), -1, CPLES_XML);
339 const char *pszConnStr = CPLSPrintf("<GDAL_WMS><Service name=\"TMS\">"
340 "<ServerUrl>%s</ServerUrl></Service><DataWindow>"
341 "<UpperLeftX>-20037508.34</UpperLeftX><UpperLeftY>20037508.34</UpperLeftY>"
342 "<LowerRightX>20037508.34</LowerRightX><LowerRightY>-20037508.34</LowerRightY>"
343 "<TileLevel>%d</TileLevel><TileCountX>1</TileCountX>"
344 "<TileCountY>1</TileCountY><YOrigin>top</YOrigin></DataWindow>"
345 "<Projection>EPSG:%d</Projection><BlockSizeX>256</BlockSizeX>"
346 "<BlockSizeY>256</BlockSizeY><BandsCount>%d</BandsCount>"
347 "<Cache><Type>file</Type><Expires>%d</Expires><MaxSize>%d</MaxSize>"
348 "</Cache><ZeroBlockHttpCodes>204,404</ZeroBlockHttpCodes></GDAL_WMS>",
349 pszRasterUrl,
350 22, // NOTE: We have no limit in zoom levels.
351 nEPSG, // NOTE: Default SRS is EPSG:3857.
352 4,
353 nCacheExpires,
354 nCacheMaxSize);
355
356 CPLFree( pszRasterUrl );
357
358 poRasterDS = reinterpret_cast<GDALDataset*>(GDALOpenEx(pszConnStr,
359 GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL, nullptr,
360 nullptr, nullptr));
361
362 if( poRasterDS )
363 {
364 bResult = true;
365 nRasterXSize = poRasterDS->GetRasterXSize();
366 nRasterYSize = poRasterDS->GetRasterYSize();
367
368 for( int iBand = 1; iBand <= poRasterDS->GetRasterCount();
369 iBand++ )
370 {
371 SetBand( iBand, new NGWWrapperRasterBand(
372 poRasterDS->GetRasterBand( iBand )) );
373 }
374
375 // Set pixel limits.
376 bool bHasTransform = false;
377 double geoTransform[6] = { 0.0 };
378 double invGeoTransform[6] = { 0.0 };
379 if(poRasterDS->GetGeoTransform(geoTransform) == CE_None)
380 {
381 bHasTransform = GDALInvGeoTransform(geoTransform,
382 invGeoTransform) == TRUE;
383 }
384
385 if(bHasTransform)
386 {
387 GDALApplyGeoTransform(invGeoTransform, stExtent.MinX,
388 stExtent.MinY, &stPixelExtent.MinX, &stPixelExtent.MaxY);
389
390 GDALApplyGeoTransform(invGeoTransform, stExtent.MaxX,
391 stExtent.MaxY, &stPixelExtent.MaxX, &stPixelExtent.MinY);
392
393 CPLDebug("NGW", "Raster extent in px is: %f, %f, %f, %f",
394 stPixelExtent.MinX, stPixelExtent.MinY,
395 stPixelExtent.MaxX, stPixelExtent.MaxY);
396 }
397 else
398 {
399 stPixelExtent.MinX = 0.0;
400 stPixelExtent.MinY = 0.0;
401 stPixelExtent.MaxX = std::numeric_limits<double>::max();
402 stPixelExtent.MaxY = std::numeric_limits<double>::max();
403 }
404 }
405 else
406 {
407 bResult = false;
408 }
409 }
410 else if( osResourceType == "raster_layer" ) //FIXME: Do we need this check? && nOpenFlagsIn & GDAL_OF_RASTER )
411 {
412 AddRaster( oRoot, papszHTTPOptions );
413 }
414 else
415 {
416 bResult = false;
417 }
418 // TODO: Add support for baselayers, webmap, wfsserver_service, wmsserver_service.
419 }
420 }
421
422 CSLDestroy( papszHTTPOptions );
423 return bResult;
424 }
425
426 /*
427 * FillResources()
428 */
FillResources(char ** papszOptions,int nOpenFlagsIn)429 bool OGRNGWDataset::FillResources( char **papszOptions, int nOpenFlagsIn )
430 {
431 CPLJSONDocument oResourceDetailsReq;
432 bool bResult = oResourceDetailsReq.LoadUrl( NGWAPI::GetChildren( osUrl,
433 osResourceId ), papszOptions );
434
435 if( bResult )
436 {
437 CPLJSONArray oChildren(oResourceDetailsReq.GetRoot());
438 for( int i = 0; i < oChildren.Size(); ++i )
439 {
440 CPLJSONObject oChild = oChildren[i];
441 std::string osResourceType = oChild.GetString("resource/cls");
442 if( (osResourceType == "vector_layer" ||
443 osResourceType == "postgis_layer") )
444 {
445 // Add vector layer. If failed, try next layer.
446 AddLayer( oChild, papszOptions, nOpenFlagsIn );
447 }
448 else if( (osResourceType == "raster_layer" ||
449 osResourceType == "wmsclient_layer") && nOpenFlagsIn & GDAL_OF_RASTER )
450 {
451 AddRaster( oChild, papszOptions );
452 }
453 // TODO: Add support for baselayers, webmap, wfsserver_service, wmsserver_service.
454 }
455 }
456 return bResult;
457 }
458
459 /*
460 * AddLayer()
461 */
AddLayer(const CPLJSONObject & oResourceJsonObject,char ** papszOptions,int nOpenFlagsIn)462 void OGRNGWDataset::AddLayer( const CPLJSONObject &oResourceJsonObject,
463 char **papszOptions, int nOpenFlagsIn )
464 {
465 std::string osLayerResourceId;
466 if( nOpenFlagsIn & GDAL_OF_VECTOR )
467 {
468 OGRNGWLayer *poLayer = new OGRNGWLayer( this, oResourceJsonObject );
469 papoLayers = (OGRNGWLayer**) CPLRealloc(papoLayers, (nLayers + 1) *
470 sizeof(OGRNGWLayer*));
471 papoLayers[nLayers++] = poLayer;
472 osLayerResourceId = poLayer->GetResourceId();
473 }
474 else
475 {
476 osLayerResourceId = oResourceJsonObject.GetString("resource/id");
477 }
478
479 // Check styles exist and add them as rasters.
480 if( nOpenFlagsIn & GDAL_OF_RASTER &&
481 oResourceJsonObject.GetBool( "resource/children", false ) )
482 {
483 CPLJSONDocument oResourceChildReq;
484 bool bResult = oResourceChildReq.LoadUrl( NGWAPI::GetChildren( osUrl,
485 osLayerResourceId ), papszOptions );
486
487 if( bResult )
488 {
489 CPLJSONArray oChildren( oResourceChildReq.GetRoot() );
490 for( int i = 0; i < oChildren.Size(); ++i )
491 {
492 AddRaster( oChildren[i], papszOptions );
493 }
494 }
495 }
496 }
497
498 /*
499 * AddRaster()
500 */
AddRaster(const CPLJSONObject & oRasterJsonObj,char ** papszOptions)501 void OGRNGWDataset::AddRaster( const CPLJSONObject &oRasterJsonObj,
502 char **papszOptions )
503 {
504 std::string osOutResourceId;
505 std::string osOutResourceName;
506 std::string osResourceType = oRasterJsonObj.GetString( "resource/cls" );
507 if( osResourceType == "mapserver_style" ||
508 osResourceType == "qgis_vector_style" ||
509 osResourceType == "raster_style" ||
510 osResourceType == "qgis_raster_style" ||
511 osResourceType == "wmsclient_layer" )
512 {
513 osOutResourceId = oRasterJsonObj.GetString( "resource/id" );
514 osOutResourceName = oRasterJsonObj.GetString( "resource/display_name" );
515 }
516 else if( osResourceType == "raster_layer" )
517 {
518 std::string osRasterResourceId = oRasterJsonObj.GetString( "resource/id" );
519 CPLJSONDocument oResourceRequest;
520 bool bResult = oResourceRequest.LoadUrl( NGWAPI::GetChildren( osUrl,
521 osRasterResourceId ), papszOptions );
522
523 if( bResult )
524 {
525 CPLJSONArray oChildren(oResourceRequest.GetRoot());
526 for( int i = 0; i < oChildren.Size(); ++i )
527 {
528 CPLJSONObject oChild = oChildren[i];
529 osResourceType = oChild.GetString("resource/cls");
530 if( osResourceType == "raster_style" ||
531 osResourceType == "qgis_raster_style" )
532 {
533 AddRaster( oChild, papszOptions );
534 }
535 }
536 }
537 }
538
539 if( !osOutResourceId.empty() )
540 {
541 if( osOutResourceName.empty() )
542 {
543 osOutResourceName = "raster_" + osOutResourceId;
544 }
545
546 CPLDebug("NGW", "Add raster %s: %s", osOutResourceId.c_str(),
547 osOutResourceName.c_str());
548
549 GDALDataset::SetMetadataItem( CPLSPrintf("SUBDATASET_%d_NAME", nRasters),
550 CPLSPrintf("NGW:%s/resource/%s", osUrl.c_str(),
551 osOutResourceId.c_str()), "SUBDATASETS" );
552 GDALDataset::SetMetadataItem( CPLSPrintf("SUBDATASET_%d_DESC", nRasters),
553 osOutResourceName.c_str(), "SUBDATASETS" );
554 nRasters++;
555 }
556 }
557
558 /*
559 * ICreateLayer
560 */
ICreateLayer(const char * pszNameIn,OGRSpatialReference * poSpatialRef,OGRwkbGeometryType eGType,char ** papszOptions)561 OGRLayer *OGRNGWDataset::ICreateLayer( const char *pszNameIn,
562 OGRSpatialReference *poSpatialRef,
563 OGRwkbGeometryType eGType,
564 char **papszOptions )
565 {
566 if( !IsUpdateMode() )
567 {
568 CPLError(CE_Failure, CPLE_AppDefined,
569 "Operation not available in read-only mode");
570 return nullptr;
571 }
572
573 // Check permissions as we create new layer in memory and will create in during SyncToDisk.
574 FetchPermissions();
575
576 if( !stPermissions.bResourceCanCreate )
577 {
578 CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
579 return nullptr;
580 }
581
582 // Check input parameters.
583 if( (eGType < wkbPoint || eGType > wkbMultiPolygon) &&
584 (eGType < wkbPoint25D || eGType > wkbMultiPolygon25D) )
585 {
586 CPLError(CE_Failure, CPLE_AppDefined,
587 "Unsupported geometry type: %s", OGRGeometryTypeToName(eGType));
588 return nullptr;
589 }
590
591 if( !poSpatialRef )
592 {
593 CPLError(CE_Failure, CPLE_AppDefined, "Undefined spatial reference");
594 return nullptr;
595 }
596
597 poSpatialRef->AutoIdentifyEPSG();
598 const char *pszEPSG = poSpatialRef->GetAuthorityCode( nullptr );
599 int nEPSG = -1;
600 if( pszEPSG != nullptr )
601 {
602 nEPSG = atoi( pszEPSG );
603 }
604
605 if( nEPSG != 3857 ) // TODO: Check NextGIS Web supported SRS.
606 {
607 CPLError(CE_Failure, CPLE_AppDefined,
608 "Unsupported spatial reference EPSG code: %d", nEPSG);
609 return nullptr;
610 }
611
612 // Do we already have this layer? If so, should we blow it away?
613 bool bOverwrite = CPLFetchBool(papszOptions, "OVERWRITE", false);
614 for( int iLayer = 0; iLayer < nLayers; ++iLayer )
615 {
616 if( EQUAL(pszNameIn, papoLayers[iLayer]->GetName()) )
617 {
618 if( bOverwrite )
619 {
620 DeleteLayer( iLayer );
621 break;
622 }
623 else
624 {
625 CPLError( CE_Failure, CPLE_AppDefined,
626 "Layer %s already exists, CreateLayer failed.\n"
627 "Use the layer creation option OVERWRITE=YES to "
628 "replace it.",
629 pszNameIn );
630 return nullptr;
631 }
632 }
633 }
634
635 // Create layer.
636 std::string osKey = CSLFetchNameValueDef( papszOptions, "KEY", "");
637 std::string osDesc = CSLFetchNameValueDef( papszOptions, "DESCRIPTION", "");
638 OGRSpatialReference* poSRSClone = poSpatialRef->Clone();
639 poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
640 OGRNGWLayer *poLayer = new OGRNGWLayer( this, pszNameIn, poSRSClone, eGType,
641 osKey, osDesc );
642 poSRSClone->Release();
643 papoLayers = (OGRNGWLayer**) CPLRealloc(papoLayers, (nLayers + 1) *
644 sizeof(OGRNGWLayer*));
645 papoLayers[nLayers++] = poLayer;
646 return poLayer;
647 }
648
649 /*
650 * DeleteLayer()
651 */
DeleteLayer(int iLayer)652 OGRErr OGRNGWDataset::DeleteLayer( int iLayer )
653 {
654 if( !IsUpdateMode() )
655 {
656 CPLError(CE_Failure, CPLE_AppDefined,
657 "Operation not available in read-only mode.");
658 return OGRERR_FAILURE;
659 }
660
661 if( iLayer < 0 || iLayer >= nLayers )
662 {
663 CPLError( CE_Failure, CPLE_AppDefined,
664 "Layer %d not in legal range of 0 to %d.", iLayer, nLayers-1 );
665 return OGRERR_FAILURE;
666 }
667
668 OGRNGWLayer *poLayer = static_cast<OGRNGWLayer*>(papoLayers[iLayer]);
669
670 if( poLayer->GetResourceId() != "-1" )
671 {
672 // For layers from server we can check permissions.
673
674 // We can skip check permissions here as papoLayers[iLayer]->Delete() will
675 // return false if no delete permission available.
676 FetchPermissions();
677
678 if( !stPermissions.bResourceCanDelete )
679 {
680 CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
681 return OGRERR_FAILURE;
682 }
683 }
684
685 if( poLayer->Delete() )
686 {
687 delete poLayer;
688 memmove( papoLayers + iLayer, papoLayers + iLayer + 1,
689 sizeof(void *) * (nLayers - iLayer - 1) );
690 nLayers--;
691 }
692
693 return OGRERR_NONE;
694 }
695
696 /*
697 * FillMetadata()
698 */
FillMetadata(const CPLJSONObject & oRootObject)699 void OGRNGWDataset::FillMetadata( const CPLJSONObject &oRootObject )
700 {
701 std::string osCreateDate = oRootObject.GetString("resource/creation_date");
702 if( !osCreateDate.empty() )
703 {
704 GDALDataset::SetMetadataItem( "creation_date", osCreateDate.c_str() );
705 }
706 osName = oRootObject.GetString("resource/display_name");
707 SetDescription( osName.c_str() );
708 GDALDataset::SetMetadataItem( "display_name", osName.c_str() );
709 std::string osDescription = oRootObject.GetString("resource/description");
710 if( !osDescription.empty() )
711 {
712 GDALDataset::SetMetadataItem( "description", osDescription.c_str() );
713 }
714 std::string osResourceType = oRootObject.GetString("resource/cls");
715 if( !osResourceType.empty() )
716 {
717 GDALDataset::SetMetadataItem( "resource_type", osResourceType.c_str() );
718 }
719 std::string osResourceParentId = oRootObject.GetString("resource/parent/id");
720 if( !osResourceParentId.empty() )
721 {
722 GDALDataset::SetMetadataItem( "parent_id", osResourceParentId.c_str() );
723 }
724 GDALDataset::SetMetadataItem( "id", osResourceId.c_str() );
725
726 std::vector<CPLJSONObject> items =
727 oRootObject.GetObj("resmeta/items").GetChildren();
728
729 for( const CPLJSONObject &item : items )
730 {
731 std::string osSuffix = NGWAPI::GetResmetaSuffix( item.GetType() );
732 GDALDataset::SetMetadataItem( (item.GetName() + osSuffix).c_str(),
733 item.ToString().c_str(), "NGW" );
734 }
735 }
736
737 /*
738 * FlushMetadata()
739 */
FlushMetadata(char ** papszMetadata)740 bool OGRNGWDataset::FlushMetadata( char **papszMetadata )
741 {
742 if( !bMetadataDerty )
743 {
744 return true;
745 }
746
747 bool bResult = NGWAPI::FlushMetadata(osUrl, osResourceId, papszMetadata,
748 GetHeaders());
749 if( bResult )
750 {
751 bMetadataDerty = false;
752 }
753
754 return bResult;
755 }
756
757 /*
758 * SetMetadata()
759 */
SetMetadata(char ** papszMetadata,const char * pszDomain)760 CPLErr OGRNGWDataset::SetMetadata( char **papszMetadata, const char *pszDomain)
761 {
762 FetchPermissions();
763 if( !stPermissions.bMetadataCanWrite )
764 {
765 CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
766 return CE_Failure;
767 }
768
769 CPLErr eResult = GDALDataset::SetMetadata(papszMetadata, pszDomain);
770 if( eResult == CE_None && pszDomain != nullptr && EQUAL(pszDomain, "NGW") )
771 {
772 eResult = FlushMetadata( papszMetadata ) ? CE_None : CE_Failure;
773 }
774 return eResult;
775 }
776
777 /*
778 * SetMetadataItem()
779 */
SetMetadataItem(const char * pszName,const char * pszValue,const char * pszDomain)780 CPLErr OGRNGWDataset::SetMetadataItem( const char *pszName,
781 const char *pszValue, const char *pszDomain)
782 {
783 FetchPermissions();
784 if( !stPermissions.bMetadataCanWrite )
785 {
786 CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
787 return CE_Failure;
788 }
789 if( pszDomain != nullptr && EQUAL(pszDomain, "NGW") )
790 {
791 bMetadataDerty = true;
792 }
793 return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
794 }
795
796 /*
797 * FlushCache()
798 */
FlushCache()799 void OGRNGWDataset::FlushCache()
800 {
801 GDALDataset::FlushCache();
802 FlushMetadata( GetMetadata("NGW") );
803 }
804
805 /*
806 * GetHeaders()
807 */
GetHeaders() const808 char **OGRNGWDataset::GetHeaders() const
809 {
810 char **papszOptions = nullptr;
811 papszOptions = CSLAddString(papszOptions, "HEADERS=Accept: */*");
812 papszOptions = CSLAddNameValue(papszOptions, "JSON_DEPTH", osJsonDepth.c_str());
813 if( !osUserPwd.empty() )
814 {
815 papszOptions = CSLAddString(papszOptions, "HTTPAUTH=BASIC");
816 std::string osUserPwdOption("USERPWD=");
817 osUserPwdOption += osUserPwd;
818 papszOptions = CSLAddString(papszOptions, osUserPwdOption.c_str());
819 }
820 return papszOptions;
821 }
822
823 /*
824 * SQLUnescape()
825 * Get from gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteutility.cpp as we don't want
826 * dependency on sqlite
827 */
SQLUnescape(const char * pszVal)828 static CPLString SQLUnescape( const char *pszVal )
829 {
830 char chQuoteChar = pszVal[0];
831 if( chQuoteChar != '\'' && chQuoteChar != '"' )
832 return pszVal;
833
834 CPLString osRet;
835 pszVal ++;
836 while( *pszVal != '\0' )
837 {
838 if( *pszVal == chQuoteChar )
839 {
840 if( pszVal[1] == chQuoteChar )
841 pszVal ++;
842 else
843 break;
844 }
845 osRet += *pszVal;
846 pszVal ++;
847 }
848 return osRet;
849 }
850
851 /*
852 * SQLTokenize()
853 * Get from gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteutility.cpp as we don't want
854 * dependency on sqlite
855 */
SQLTokenize(const char * pszStr)856 static char **SQLTokenize( const char *pszStr )
857 {
858 char** papszTokens = nullptr;
859 bool bInQuote = false;
860 char chQuoteChar = '\0';
861 bool bInSpace = true;
862 CPLString osCurrentToken;
863 while( *pszStr != '\0' )
864 {
865 if( *pszStr == ' ' && !bInQuote )
866 {
867 if( !bInSpace )
868 {
869 papszTokens = CSLAddString(papszTokens, osCurrentToken);
870 osCurrentToken.clear();
871 }
872 bInSpace = true;
873 }
874 else if( (*pszStr == '(' || *pszStr == ')' || *pszStr == ',') && !bInQuote )
875 {
876 if( !bInSpace )
877 {
878 papszTokens = CSLAddString(papszTokens, osCurrentToken);
879 osCurrentToken.clear();
880 }
881 osCurrentToken.clear();
882 osCurrentToken += *pszStr;
883 papszTokens = CSLAddString(papszTokens, osCurrentToken);
884 osCurrentToken.clear();
885 bInSpace = true;
886 }
887 else if( *pszStr == '"' || *pszStr == '\'' )
888 {
889 if( bInQuote && *pszStr == chQuoteChar && pszStr[1] == chQuoteChar )
890 {
891 osCurrentToken += *pszStr;
892 osCurrentToken += *pszStr;
893 pszStr += 2;
894 continue;
895 }
896 else if( bInQuote && *pszStr == chQuoteChar )
897 {
898 osCurrentToken += *pszStr;
899 papszTokens = CSLAddString(papszTokens, osCurrentToken);
900 osCurrentToken.clear();
901 bInSpace = true;
902 bInQuote = false;
903 chQuoteChar = '\0';
904 }
905 else if( bInQuote )
906 {
907 osCurrentToken += *pszStr;
908 }
909 else
910 {
911 chQuoteChar = *pszStr;
912 osCurrentToken.clear();
913 osCurrentToken += chQuoteChar;
914 bInQuote = true;
915 bInSpace = false;
916 }
917 }
918 else
919 {
920 osCurrentToken += *pszStr;
921 bInSpace = false;
922 }
923 pszStr ++;
924 }
925
926 if( !osCurrentToken.empty() )
927 papszTokens = CSLAddString(papszTokens, osCurrentToken);
928
929 return papszTokens;
930 }
931
932
933 /*
934 * ExecuteSQL()
935 */
ExecuteSQL(const char * pszStatement,OGRGeometry * poSpatialFilter,const char * pszDialect)936 OGRLayer *OGRNGWDataset::ExecuteSQL( const char *pszStatement,
937 OGRGeometry *poSpatialFilter, const char *pszDialect )
938 {
939 // Clean statement string.
940 CPLString osStatement(pszStatement);
941 osStatement = osStatement.Trim().replaceAll(" ", " ");
942
943 if( STARTS_WITH_CI(osStatement, "DELLAYER:") )
944 {
945 CPLString osLayerName = osStatement.substr(strlen("DELLAYER:"));
946 if( osLayerName.endsWith(";") )
947 {
948 osLayerName = osLayerName.substr(0, osLayerName.size() - 1);
949 osLayerName.Trim();
950 }
951
952 CPLDebug("NGW", "Delete layer with name %s.", osLayerName.c_str());
953
954 for( int iLayer = 0; iLayer < nLayers; ++iLayer )
955 {
956 if( EQUAL(papoLayers[iLayer]->GetName(), osLayerName ) )
957 {
958 DeleteLayer( iLayer );
959 return nullptr;
960 }
961 }
962 CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
963 osLayerName.c_str());
964
965 return nullptr;
966 }
967
968 if( STARTS_WITH_CI(osStatement, "DELETE FROM") )
969 {
970 // Get layer name from pszStatement DELETE FROM layer;.
971 CPLString osLayerName = osStatement.substr(strlen("DELETE FROM "));
972 if( osLayerName.endsWith(";") )
973 {
974 osLayerName = osLayerName.substr(0, osLayerName.size() - 1);
975 osLayerName.Trim();
976 }
977
978 CPLDebug("NGW", "Delete features from layer with name %s.", osLayerName.c_str());
979
980 OGRNGWLayer *poLayer = static_cast<OGRNGWLayer*>(GetLayerByName(osLayerName));
981 if( poLayer )
982 {
983 poLayer->DeleteAllFeatures();
984 }
985 else
986 {
987 CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
988 osLayerName.c_str());
989 }
990 return nullptr;
991 }
992
993 if( STARTS_WITH_CI(osStatement, "DROP TABLE") )
994 {
995 // Get layer name from pszStatement DELETE FROM layer;.
996 CPLString osLayerName = osStatement.substr(strlen("DROP TABLE "));
997 if( osLayerName.endsWith(";") )
998 {
999 osLayerName = osLayerName.substr(0, osLayerName.size() - 1);
1000 osLayerName.Trim();
1001 }
1002
1003 CPLDebug("NGW", "Delete layer with name %s.", osLayerName.c_str());
1004
1005 for( int iLayer = 0; iLayer < nLayers; ++iLayer )
1006 {
1007 if( EQUAL(papoLayers[iLayer]->GetName(), osLayerName ) )
1008 {
1009 DeleteLayer( iLayer );
1010 return nullptr;
1011 }
1012 }
1013
1014 CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
1015 osLayerName.c_str());
1016
1017 return nullptr;
1018 }
1019
1020 if( STARTS_WITH_CI(osStatement, "ALTER TABLE ") )
1021 {
1022 if( osStatement.endsWith(";") )
1023 {
1024 osStatement = osStatement.substr(0, osStatement.size() - 1);
1025 osStatement.Trim();
1026 }
1027
1028 CPLStringList aosTokens( SQLTokenize(osStatement) );
1029 /* ALTER TABLE src_table RENAME TO dst_table */
1030 if( aosTokens.size() == 6 && EQUAL(aosTokens[3], "RENAME") &&
1031 EQUAL(aosTokens[4], "TO") )
1032 {
1033 const char* pszSrcTableName = aosTokens[2];
1034 const char* pszDstTableName = aosTokens[5];
1035
1036 OGRNGWLayer *poLayer = static_cast<OGRNGWLayer*>(GetLayerByName(
1037 SQLUnescape(pszSrcTableName) ));
1038 if( poLayer )
1039 {
1040 poLayer->Rename( SQLUnescape(pszDstTableName) );
1041 return nullptr;
1042 }
1043
1044 CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
1045 pszSrcTableName);
1046 }
1047 else
1048 {
1049 CPLError(CE_Failure, CPLE_AppDefined,
1050 "Unsupported alter table operation. Only rename table to ... support.");
1051 }
1052 return nullptr;
1053 }
1054
1055 // SELECT xxxxx FROM yyyy WHERE zzzzzz;
1056 if( STARTS_WITH_CI(osStatement, "SELECT ") )
1057 {
1058 swq_select oSelect;
1059 CPLDebug("NGW", "Select statement: %s", osStatement.c_str());
1060 if( oSelect.preparse( osStatement ) != CE_None )
1061 {
1062 return nullptr;
1063 }
1064
1065 if( oSelect.join_count == 0 && oSelect.poOtherSelect == nullptr &&
1066 oSelect.table_count == 1 && oSelect.order_specs == 0 )
1067 {
1068 OGRNGWLayer *poLayer = reinterpret_cast<OGRNGWLayer*>(
1069 GetLayerByName( oSelect.table_defs[0].table_name ) );
1070 if( nullptr == poLayer )
1071 {
1072 CPLError(CE_Failure, CPLE_AppDefined,
1073 "Layer %s not found in dataset.", oSelect.table_defs[0].table_name);
1074 return nullptr;
1075 }
1076
1077 std::set<std::string> aosFields;
1078 bool bSkip = false;
1079 for( int i = 0; i < oSelect.result_columns; ++i )
1080 {
1081 swq_col_func col_func = oSelect.column_defs[i].col_func;
1082 if( col_func != SWQCF_NONE )
1083 {
1084 bSkip = true;
1085 break;
1086 }
1087
1088 if( oSelect.column_defs[i].distinct_flag )
1089 {
1090 CPLError(CE_Warning, CPLE_AppDefined,
1091 "Distinct not supported.");
1092 bSkip = true;
1093 break;
1094 }
1095
1096 if( oSelect.column_defs[i].field_name != nullptr )
1097 {
1098 if( EQUAL(oSelect.column_defs[i].field_name, "*") )
1099 {
1100 aosFields.clear();
1101 aosFields.emplace( oSelect.column_defs[i].field_name );
1102 break;
1103 }
1104 else
1105 {
1106 aosFields.emplace( oSelect.column_defs[i].field_name );
1107 }
1108 }
1109 }
1110
1111 std::string osNgwSelect;
1112 for( int iKey = 0; iKey < oSelect.order_specs; iKey++ )
1113 {
1114 swq_order_def *psKeyDef = oSelect.order_defs + iKey;
1115 if(iKey > 0 )
1116 {
1117 osNgwSelect += ",";
1118 }
1119
1120 if( psKeyDef->ascending_flag == TRUE )
1121 {
1122 osNgwSelect += psKeyDef->field_name;
1123 }
1124 else
1125 {
1126 osNgwSelect += "-" + std::string(psKeyDef->field_name);
1127 }
1128 }
1129
1130 if( oSelect.where_expr != nullptr )
1131 {
1132 if( !osNgwSelect.empty() )
1133 {
1134 osNgwSelect += "&";
1135 }
1136 osNgwSelect += OGRNGWLayer::TranslateSQLToFilter(
1137 oSelect.where_expr );
1138
1139 }
1140
1141 if( osNgwSelect.empty() )
1142 {
1143 bSkip = true;
1144 }
1145
1146 if( !bSkip )
1147 {
1148 if( aosFields.empty() )
1149 {
1150 CPLError(CE_Failure, CPLE_AppDefined,
1151 "SELECT statement is invalid: field list is empty.");
1152 return nullptr;
1153 }
1154
1155 if( poLayer->SyncToDisk() != OGRERR_NONE )
1156 {
1157 return nullptr;
1158 }
1159
1160 OGRNGWLayer *poOutLayer = poLayer->Clone();
1161 if( aosFields.size() == 1 && *(aosFields.begin()) == "*" )
1162 {
1163 poOutLayer->SetIgnoredFields(nullptr);
1164 }
1165 else
1166 {
1167 poOutLayer->SetSelectedFields(aosFields);
1168 }
1169 poOutLayer->SetSpatialFilter(poSpatialFilter);
1170
1171 if( osNgwSelect.empty() ) // If we here oSelect.where_expr is empty
1172 {
1173 poOutLayer->SetAttributeFilter(nullptr);
1174 }
1175 else
1176 {
1177 std::string osAttributeFilte = "NGW:" + osNgwSelect;
1178 poOutLayer->SetAttributeFilter(osAttributeFilte.c_str());
1179 }
1180 return poOutLayer;
1181 }
1182 }
1183 }
1184
1185 return GDALDataset::ExecuteSQL(pszStatement, poSpatialFilter, pszDialect);
1186 }
1187
1188 /*
1189 * GetProjectionRef()
1190 */
GetSpatialRef() const1191 const OGRSpatialReference *OGRNGWDataset::GetSpatialRef() const
1192 {
1193 if( poRasterDS != nullptr )
1194 {
1195 return poRasterDS->GetSpatialRef();
1196 }
1197 return GDALDataset::GetSpatialRef();
1198 }
1199
1200 /*
1201 * GetGeoTransform()
1202 */
GetGeoTransform(double * padfTransform)1203 CPLErr OGRNGWDataset::GetGeoTransform( double *padfTransform )
1204 {
1205 if( poRasterDS != nullptr )
1206 {
1207 return poRasterDS->GetGeoTransform( padfTransform );
1208 }
1209 return GDALDataset::GetGeoTransform( padfTransform );
1210 }
1211
1212 /*
1213 * IRasterIO()
1214 */
IRasterIO(GDALRWFlag eRWFlag,int nXOff,int nYOff,int nXSize,int nYSize,void * pData,int nBufXSize,int nBufYSize,GDALDataType eBufType,int nBandCount,int * panBandMap,GSpacing nPixelSpace,GSpacing nLineSpace,GSpacing nBandSpace,GDALRasterIOExtraArg * psExtraArg)1215 CPLErr OGRNGWDataset::IRasterIO( GDALRWFlag eRWFlag, int nXOff, int nYOff,
1216 int nXSize, int nYSize, void *pData, int nBufXSize, int nBufYSize,
1217 GDALDataType eBufType, int nBandCount, int *panBandMap,
1218 GSpacing nPixelSpace, GSpacing nLineSpace, GSpacing nBandSpace,
1219 GDALRasterIOExtraArg* psExtraArg )
1220 {
1221 if( poRasterDS != nullptr )
1222 {
1223 if( stPixelExtent.IsInit() )
1224 {
1225 OGREnvelope stTestExtent;
1226 stTestExtent.MinX = static_cast<double>(nXOff);
1227 stTestExtent.MinY = static_cast<double>(nYOff);
1228 stTestExtent.MaxX = static_cast<double>(nXOff + nXSize);
1229 stTestExtent.MaxY = static_cast<double>(nYOff + nYSize);
1230
1231 if( !stPixelExtent.Intersects(stTestExtent) )
1232 {
1233 CPLDebug("NGW", "Raster extent in px is: %f, %f, %f, %f",
1234 stPixelExtent.MinX, stPixelExtent.MinY,
1235 stPixelExtent.MaxX, stPixelExtent.MaxY);
1236 CPLDebug("NGW", "RasterIO extent is: %f, %f, %f, %f",
1237 stTestExtent.MinX, stTestExtent.MinY,
1238 stTestExtent.MaxX, stTestExtent.MaxY);
1239
1240 // Fill buffer transparent color.
1241 memset( pData, 0, nBufXSize * nBufYSize * nBandCount *
1242 GDALGetDataTypeSizeBytes(eBufType) );
1243 return CE_None;
1244 }
1245 }
1246 }
1247 return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
1248 nBufXSize, nBufYSize, eBufType, nBandCount, panBandMap, nPixelSpace,
1249 nLineSpace, nBandSpace, psExtraArg);
1250 }
1251
1252 /*
1253 * FillCapabilities()
1254 */
FillCapabilities(char ** papszOptions)1255 void OGRNGWDataset::FillCapabilities( char **papszOptions )
1256 {
1257 // Check NGW version. Paging available from 3.1
1258 CPLJSONDocument oRouteReq;
1259 if( oRouteReq.LoadUrl( NGWAPI::GetVersion(osUrl), papszOptions ) )
1260 {
1261 CPLJSONObject oRoot = oRouteReq.GetRoot();
1262
1263 if( oRoot.IsValid() )
1264 {
1265 std::string osVersion = oRoot.GetString("nextgisweb", "0.0");
1266 bHasFeaturePaging = NGWAPI::CheckVersion(osVersion, 3, 1);
1267
1268 CPLDebug("NGW", "Is feature paging supported: %s",
1269 bHasFeaturePaging ? "yes" : "no");
1270 }
1271 }
1272 }
1273
1274
1275 /*
1276 * Extensions()
1277 */
Extensions() const1278 std::string OGRNGWDataset::Extensions() const
1279 {
1280 return osExtensions;
1281 }
1282