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