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-2025, NextGIS <info@nextgis.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : *******************************************************************************/
13 :
14 : #include "ogr_ngw.h"
15 :
16 : #include "cpl_http.h"
17 : #include "gdal_proxy.h"
18 :
19 : #include <limits>
20 :
21 : class NGWWrapperRasterBand : public GDALProxyRasterBand
22 : {
23 : GDALRasterBand *poBaseBand;
24 :
25 : protected:
26 : virtual GDALRasterBand *
27 0 : RefUnderlyingRasterBand(bool /*bForceOpen*/) const override
28 : {
29 0 : return poBaseBand;
30 : }
31 :
32 : public:
33 0 : explicit NGWWrapperRasterBand(GDALRasterBand *poBaseBandIn)
34 0 : : poBaseBand(poBaseBandIn)
35 : {
36 0 : eDataType = poBaseBand->GetRasterDataType();
37 0 : poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
38 0 : }
39 :
40 0 : virtual ~NGWWrapperRasterBand()
41 0 : {
42 0 : }
43 : };
44 :
45 0 : static const char *FormGDALTMSConnectionString(const std::string &osUrl,
46 : const std::string &osResourceId,
47 : int nEPSG, int nCacheExpires,
48 : int nCacheMaxSize)
49 : {
50 0 : std::string osRasterUrl = NGWAPI::GetTMSURL(osUrl, osResourceId);
51 0 : char *pszRasterUrl = CPLEscapeString(osRasterUrl.c_str(), -1, CPLES_XML);
52 : const char *pszConnStr =
53 0 : CPLSPrintf("<GDAL_WMS><Service name=\"TMS\">"
54 : "<ServerUrl>%s</ServerUrl></Service><DataWindow>"
55 : "<UpperLeftX>-20037508.34</"
56 : "UpperLeftX><UpperLeftY>20037508.34</UpperLeftY>"
57 : "<LowerRightX>20037508.34</"
58 : "LowerRightX><LowerRightY>-20037508.34</LowerRightY>"
59 : "<TileLevel>%d</TileLevel><TileCountX>1</TileCountX>"
60 : "<TileCountY>1</TileCountY><YOrigin>top</YOrigin></"
61 : "DataWindow>"
62 : "<Projection>EPSG:%d</Projection><BlockSizeX>256</"
63 : "BlockSizeX>"
64 : "<BlockSizeY>256</BlockSizeY><BandsCount>%d</BandsCount>"
65 : "<Cache><Type>file</Type><Expires>%d</Expires><MaxSize>%d</"
66 : "MaxSize>"
67 : "</Cache><ZeroBlockHttpCodes>204,404</ZeroBlockHttpCodes></"
68 : "GDAL_WMS>",
69 : pszRasterUrl,
70 : 22, // NOTE: We have no limit in zoom levels.
71 : nEPSG, // NOTE: Default SRS is EPSG:3857.
72 : 4, nCacheExpires, nCacheMaxSize);
73 :
74 0 : CPLFree(pszRasterUrl);
75 0 : return pszConnStr;
76 : }
77 :
78 0 : static std::string GetStylesIdentifiers(const CPLJSONArray &aoStyles, int nDeep)
79 : {
80 0 : std::string sOut;
81 0 : if (nDeep > 255)
82 : {
83 0 : return sOut;
84 : }
85 :
86 0 : for (const auto &subobj : aoStyles)
87 : {
88 0 : auto sType = subobj.GetString("item_type");
89 0 : if (sType == "layer")
90 : {
91 0 : auto sId = subobj.GetString("layer_style_id");
92 0 : if (!sId.empty())
93 : {
94 0 : if (sOut.empty())
95 : {
96 0 : sOut = sId;
97 : }
98 : else
99 : {
100 0 : sOut += "," + sId;
101 : }
102 : }
103 : }
104 : else
105 : {
106 0 : auto aoChildren = subobj.GetArray("children");
107 0 : auto sId = GetStylesIdentifiers(aoChildren, nDeep + 1);
108 0 : if (!sId.empty())
109 : {
110 0 : if (sOut.empty())
111 : {
112 0 : sOut = sId;
113 : }
114 : else
115 : {
116 0 : sOut += "," + sId;
117 : }
118 : }
119 : }
120 : }
121 0 : return sOut;
122 : }
123 :
124 : /*
125 : * OGRNGWDataset()
126 : */
127 0 : OGRNGWDataset::OGRNGWDataset()
128 : : nBatchSize(-1), nPageSize(-1), bFetchedPermissions(false),
129 : bHasFeaturePaging(false), bExtInNativeData(false), bMetadataDerty(false),
130 : poRasterDS(nullptr), nRasters(0), nCacheExpires(604800), // 7 days
131 : nCacheMaxSize(67108864), // 64 MB
132 0 : osJsonDepth("32")
133 : {
134 0 : }
135 :
136 : /*
137 : * ~OGRNGWDataset()
138 : */
139 0 : OGRNGWDataset::~OGRNGWDataset()
140 : {
141 : // Last sync with server.
142 0 : OGRNGWDataset::FlushCache(true);
143 :
144 0 : if (poRasterDS != nullptr)
145 : {
146 0 : GDALClose(poRasterDS);
147 0 : poRasterDS = nullptr;
148 : }
149 0 : }
150 :
151 : /*
152 : * FetchPermissions()
153 : */
154 0 : void OGRNGWDataset::FetchPermissions()
155 : {
156 0 : if (bFetchedPermissions)
157 : {
158 0 : return;
159 : }
160 :
161 0 : if (IsUpdateMode())
162 : {
163 : // Check connection and is it read only.
164 : stPermissions = NGWAPI::CheckPermissions(
165 0 : osUrl, osResourceId, GetHeaders(false), IsUpdateMode());
166 : }
167 : else
168 : {
169 0 : stPermissions.bDataCanRead = true;
170 0 : stPermissions.bResourceCanRead = true;
171 0 : stPermissions.bDatastructCanRead = true;
172 0 : stPermissions.bMetadataCanRead = true;
173 : }
174 0 : bFetchedPermissions = true;
175 : }
176 :
177 : /*
178 : * TestCapability()
179 : */
180 0 : int OGRNGWDataset::TestCapability(const char *pszCap)
181 : {
182 0 : FetchPermissions();
183 0 : if (EQUAL(pszCap, ODsCCreateLayer))
184 : {
185 0 : return stPermissions.bResourceCanCreate;
186 : }
187 0 : else if (EQUAL(pszCap, ODsCDeleteLayer))
188 : {
189 0 : return stPermissions.bResourceCanDelete;
190 : }
191 0 : else if (EQUAL(pszCap, "RenameLayer"))
192 : {
193 0 : return stPermissions.bResourceCanUpdate;
194 : }
195 0 : else if (EQUAL(pszCap, ODsCRandomLayerWrite))
196 : {
197 0 : return stPermissions.bDataCanWrite; // FIXME: Check on resource level
198 : // is this permission set?
199 : }
200 0 : else if (EQUAL(pszCap, ODsCRandomLayerRead))
201 : {
202 0 : return stPermissions.bDataCanRead;
203 : }
204 0 : else if (EQUAL(pszCap, ODsCZGeometries))
205 : {
206 0 : return TRUE;
207 : }
208 0 : else if (EQUAL(pszCap, ODsCAddFieldDomain))
209 : {
210 0 : return stPermissions.bResourceCanCreate;
211 : }
212 0 : else if (EQUAL(pszCap, ODsCDeleteFieldDomain))
213 : {
214 0 : return stPermissions.bResourceCanDelete;
215 : }
216 0 : else if (EQUAL(pszCap, ODsCUpdateFieldDomain))
217 : {
218 0 : return stPermissions.bResourceCanUpdate;
219 : }
220 : else
221 : {
222 0 : return FALSE;
223 : }
224 : }
225 :
226 : /*
227 : * GetLayer()
228 : */
229 0 : OGRLayer *OGRNGWDataset::GetLayer(int iLayer)
230 : {
231 0 : if (iLayer < 0 || iLayer >= GetLayerCount())
232 : {
233 0 : return nullptr;
234 : }
235 : else
236 : {
237 0 : return aoLayers[iLayer].get();
238 : }
239 : }
240 :
241 : /*
242 : * Open()
243 : */
244 0 : bool OGRNGWDataset::Open(const std::string &osUrlIn,
245 : const std::string &osResourceIdIn,
246 : char **papszOpenOptionsIn, bool bUpdateIn,
247 : int nOpenFlagsIn)
248 : {
249 0 : osUrl = osUrlIn;
250 0 : osResourceId = osResourceIdIn;
251 :
252 0 : eAccess = bUpdateIn ? GA_Update : GA_ReadOnly;
253 :
254 : osUserPwd = CSLFetchNameValueDef(papszOpenOptionsIn, "USERPWD",
255 0 : CPLGetConfigOption("NGW_USERPWD", ""));
256 :
257 0 : nBatchSize =
258 0 : atoi(CSLFetchNameValueDef(papszOpenOptionsIn, "BATCH_SIZE",
259 : CPLGetConfigOption("NGW_BATCH_SIZE", "-1")));
260 :
261 0 : nPageSize =
262 0 : atoi(CSLFetchNameValueDef(papszOpenOptionsIn, "PAGE_SIZE",
263 : CPLGetConfigOption("NGW_PAGE_SIZE", "-1")));
264 0 : if (nPageSize == 0)
265 : {
266 0 : nPageSize = -1;
267 : }
268 :
269 0 : nCacheExpires = atoi(CSLFetchNameValueDef(
270 : papszOpenOptionsIn, "CACHE_EXPIRES",
271 : CPLGetConfigOption("NGW_CACHE_EXPIRES", "604800")));
272 :
273 0 : nCacheMaxSize = atoi(CSLFetchNameValueDef(
274 : papszOpenOptionsIn, "CACHE_MAX_SIZE",
275 : CPLGetConfigOption("NGW_CACHE_MAX_SIZE", "67108864")));
276 :
277 0 : bExtInNativeData =
278 0 : CPLFetchBool(papszOpenOptionsIn, "NATIVE_DATA",
279 0 : CPLTestBool(CPLGetConfigOption("NGW_NATIVE_DATA", "NO")));
280 :
281 : osJsonDepth =
282 : CSLFetchNameValueDef(papszOpenOptionsIn, "JSON_DEPTH",
283 0 : CPLGetConfigOption("NGW_JSON_DEPTH", "32"));
284 :
285 : osExtensions =
286 : CSLFetchNameValueDef(papszOpenOptionsIn, "EXTENSIONS",
287 0 : CPLGetConfigOption("NGW_EXTENSIONS", ""));
288 :
289 : osConnectTimeout =
290 : CSLFetchNameValueDef(papszOpenOptionsIn, "CONNECTTIMEOUT",
291 0 : CPLGetConfigOption("NGW_CONNECTTIMEOUT", ""));
292 : osTimeout = CSLFetchNameValueDef(papszOpenOptionsIn, "TIMEOUT",
293 0 : CPLGetConfigOption("NGW_TIMEOUT", ""));
294 : osRetryCount =
295 : CSLFetchNameValueDef(papszOpenOptionsIn, "MAX_RETRY",
296 0 : CPLGetConfigOption("NGW_MAX_RETRY", ""));
297 : osRetryDelay =
298 : CSLFetchNameValueDef(papszOpenOptionsIn, "RETRY_DELAY",
299 0 : CPLGetConfigOption("NGW_RETRY_DELAY", ""));
300 :
301 0 : if (osExtensions.empty())
302 : {
303 0 : bExtInNativeData = false;
304 : }
305 :
306 0 : CPLDebug("NGW",
307 : "Open options:\n"
308 : " BATCH_SIZE %d\n"
309 : " PAGE_SIZE %d\n"
310 : " CACHE_EXPIRES %d\n"
311 : " CACHE_MAX_SIZE %d\n"
312 : " JSON_DEPTH %s\n"
313 : " EXTENSIONS %s\n"
314 : " CONNECTTIMEOUT %s\n"
315 : " TIMEOUT %s\n"
316 : " MAX_RETRY %s\n"
317 : " RETRY_DELAY %s",
318 : nBatchSize, nPageSize, nCacheExpires, nCacheMaxSize,
319 : osJsonDepth.c_str(), osExtensions.c_str(),
320 : osConnectTimeout.c_str(), osTimeout.c_str(), osRetryCount.c_str(),
321 : osRetryDelay.c_str());
322 :
323 0 : return Init(nOpenFlagsIn);
324 : }
325 :
326 : /*
327 : * Open()
328 : *
329 : * The pszFilename templates:
330 : * - NGW:http://some.nextgis.com/resource/0
331 : * - NGW:http://some.nextgis.com:8000/test/resource/0
332 : */
333 0 : bool OGRNGWDataset::Open(const char *pszFilename, char **papszOpenOptionsIn,
334 : bool bUpdateIn, int nOpenFlagsIn)
335 : {
336 0 : NGWAPI::Uri stUri = NGWAPI::ParseUri(pszFilename);
337 :
338 0 : if (stUri.osPrefix != "NGW")
339 : {
340 0 : CPLError(CE_Failure, CPLE_NotSupported, "Unsupported name %s",
341 : pszFilename);
342 0 : return false;
343 : }
344 :
345 0 : osUrl = stUri.osAddress;
346 0 : osResourceId = stUri.osResourceId;
347 :
348 0 : return Open(stUri.osAddress, stUri.osResourceId, papszOpenOptionsIn,
349 0 : bUpdateIn, nOpenFlagsIn);
350 : }
351 :
352 : /*
353 : * SetupRasterDSWrapper()
354 : */
355 0 : void OGRNGWDataset::SetupRasterDSWrapper(const OGREnvelope &stExtent)
356 : {
357 0 : if (poRasterDS)
358 : {
359 0 : nRasterXSize = poRasterDS->GetRasterXSize();
360 0 : nRasterYSize = poRasterDS->GetRasterYSize();
361 :
362 0 : for (int iBand = 1; iBand <= poRasterDS->GetRasterCount(); iBand++)
363 : {
364 0 : SetBand(iBand,
365 0 : new NGWWrapperRasterBand(poRasterDS->GetRasterBand(iBand)));
366 : }
367 :
368 0 : if (stExtent.IsInit())
369 : {
370 : // Set pixel limits.
371 0 : bool bHasTransform = false;
372 0 : double geoTransform[6] = {0.0};
373 0 : double invGeoTransform[6] = {0.0};
374 0 : if (poRasterDS->GetGeoTransform(geoTransform) == CE_None)
375 : {
376 0 : bHasTransform =
377 0 : GDALInvGeoTransform(geoTransform, invGeoTransform) == TRUE;
378 : }
379 :
380 0 : if (bHasTransform)
381 : {
382 0 : GDALApplyGeoTransform(invGeoTransform, stExtent.MinX,
383 0 : stExtent.MinY, &stPixelExtent.MinX,
384 : &stPixelExtent.MaxY);
385 :
386 0 : GDALApplyGeoTransform(invGeoTransform, stExtent.MaxX,
387 0 : stExtent.MaxY, &stPixelExtent.MaxX,
388 : &stPixelExtent.MinY);
389 :
390 0 : CPLDebug("NGW", "Raster extent in px is: %f, %f, %f, %f",
391 : stPixelExtent.MinX, stPixelExtent.MinY,
392 : stPixelExtent.MaxX, stPixelExtent.MaxY);
393 : }
394 : else
395 : {
396 0 : stPixelExtent.MinX = 0.0;
397 0 : stPixelExtent.MinY = 0.0;
398 0 : stPixelExtent.MaxX = std::numeric_limits<double>::max();
399 0 : stPixelExtent.MaxY = std::numeric_limits<double>::max();
400 : }
401 : }
402 : }
403 0 : }
404 :
405 : /*
406 : * Init()
407 : */
408 0 : bool OGRNGWDataset::Init(int nOpenFlagsIn)
409 : {
410 : // NOTE: Skip check API version at that moment. We expected API v3 or higher.
411 :
412 : // Get resource details.
413 0 : CPLJSONDocument oResourceDetailsReq;
414 0 : auto aosHTTPOptions = GetHeaders(false);
415 0 : bool bResult = oResourceDetailsReq.LoadUrl(
416 0 : NGWAPI::GetResourceURL(osUrl, osResourceId), aosHTTPOptions);
417 :
418 0 : CPLDebug("NGW", "Get resource %s details %s", osResourceId.c_str(),
419 : bResult ? "success" : "failed");
420 :
421 0 : if (bResult)
422 : {
423 0 : CPLJSONObject oRoot = oResourceDetailsReq.GetRoot();
424 :
425 0 : if (oRoot.IsValid())
426 : {
427 0 : auto osResourceType = oRoot.GetString("resource/cls");
428 0 : FillMetadata(oRoot);
429 :
430 0 : if (osResourceType == "resource_group")
431 : {
432 : // Check feature paging.
433 0 : FillCapabilities(aosHTTPOptions);
434 0 : if (oRoot.GetBool("resource/children", false))
435 : {
436 : // Get child resources.
437 0 : bResult = FillResources(aosHTTPOptions, nOpenFlagsIn);
438 : }
439 : }
440 0 : else if (NGWAPI::CheckSupportedType(false, osResourceType))
441 : {
442 : // Check feature paging.
443 0 : FillCapabilities(aosHTTPOptions);
444 : // Add vector layer.
445 0 : AddLayer(oRoot, aosHTTPOptions, nOpenFlagsIn);
446 : }
447 0 : else if (osResourceType == "mapserver_style" ||
448 0 : osResourceType == "qgis_vector_style" ||
449 0 : osResourceType == "raster_style" ||
450 0 : osResourceType == "qgis_raster_style")
451 : {
452 : // GetExtent from parent.
453 0 : OGREnvelope stExtent;
454 0 : std::string osParentId = oRoot.GetString("resource/parent/id");
455 0 : bool bExtentResult = NGWAPI::GetExtent(
456 0 : osUrl, osParentId, aosHTTPOptions, 3857, stExtent);
457 :
458 0 : if (!bExtentResult)
459 : {
460 : // Set full extent for EPSG:3857.
461 0 : stExtent.MinX = -20037508.34;
462 0 : stExtent.MaxX = 20037508.34;
463 0 : stExtent.MinY = -20037508.34;
464 0 : stExtent.MaxY = 20037508.34;
465 : }
466 :
467 0 : CPLDebug("NGW", "Raster extent is: %f, %f, %f, %f",
468 : stExtent.MinX, stExtent.MinY, stExtent.MaxX,
469 : stExtent.MaxY);
470 :
471 0 : int nEPSG = 3857;
472 : // NOTE: Get parent details. We can skip this as default SRS in
473 : // NGW is 3857.
474 0 : CPLJSONDocument oResourceReq;
475 0 : bResult = oResourceReq.LoadUrl(
476 0 : NGWAPI::GetResourceURL(osUrl, osResourceId),
477 0 : aosHTTPOptions);
478 :
479 0 : if (bResult)
480 : {
481 0 : CPLJSONObject oParentRoot = oResourceReq.GetRoot();
482 0 : if (osResourceType == "mapserver_style" ||
483 0 : osResourceType == "qgis_vector_style")
484 : {
485 0 : nEPSG = oParentRoot.GetInteger("vector_layer/srs/id",
486 : nEPSG);
487 : }
488 0 : else if (osResourceType == "raster_style" ||
489 0 : osResourceType == "qgis_raster_style")
490 : {
491 0 : nEPSG = oParentRoot.GetInteger("raster_layer/srs/id",
492 : nEPSG);
493 : }
494 : }
495 :
496 0 : const char *pszConnStr = FormGDALTMSConnectionString(
497 0 : osUrl, osResourceId, nEPSG, nCacheExpires, nCacheMaxSize);
498 0 : CPLDebug("NGW", "Open %s as '%s'", osResourceType.c_str(),
499 : pszConnStr);
500 0 : poRasterDS = GDALDataset::FromHandle(GDALOpenEx(
501 : pszConnStr,
502 : GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL,
503 : nullptr, nullptr, nullptr));
504 0 : SetupRasterDSWrapper(stExtent);
505 : }
506 0 : else if (osResourceType == "wmsclient_layer")
507 : {
508 0 : OGREnvelope stExtent;
509 : // Set full extent for EPSG:3857.
510 0 : stExtent.MinX = -20037508.34;
511 0 : stExtent.MaxX = 20037508.34;
512 0 : stExtent.MinY = -20037508.34;
513 0 : stExtent.MaxY = 20037508.34;
514 :
515 0 : CPLDebug("NGW", "Raster extent is: %f, %f, %f, %f",
516 : stExtent.MinX, stExtent.MinY, stExtent.MaxX,
517 : stExtent.MaxY);
518 :
519 0 : int nEPSG = oRoot.GetInteger("wmsclient_layer/srs/id", 3857);
520 :
521 0 : const char *pszConnStr = FormGDALTMSConnectionString(
522 0 : osUrl, osResourceId, nEPSG, nCacheExpires, nCacheMaxSize);
523 0 : poRasterDS = GDALDataset::FromHandle(GDALOpenEx(
524 : pszConnStr,
525 : GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL,
526 : nullptr, nullptr, nullptr));
527 0 : SetupRasterDSWrapper(stExtent);
528 : }
529 0 : else if (osResourceType == "basemap_layer")
530 : {
531 0 : auto osTMSURL = oRoot.GetString("basemap_layer/url");
532 0 : int nEPSG = 3857;
533 0 : auto osQMS = oRoot.GetString("basemap_layer/qms");
534 0 : if (!osQMS.empty())
535 : {
536 0 : CPLJSONDocument oDoc;
537 0 : if (oDoc.LoadMemory(osQMS))
538 : {
539 0 : auto oQMLRoot = oDoc.GetRoot();
540 0 : nEPSG = oQMLRoot.GetInteger("epsg");
541 : }
542 : }
543 :
544 : // TODO: for EPSG != 3857 need to calc full extent
545 0 : if (nEPSG != 3857)
546 : {
547 0 : bResult = false;
548 : }
549 : else
550 : {
551 0 : OGREnvelope stExtent;
552 : // Set full extent for EPSG:3857.
553 0 : stExtent.MinX = -20037508.34;
554 0 : stExtent.MaxX = 20037508.34;
555 0 : stExtent.MinY = -20037508.34;
556 0 : stExtent.MaxY = 20037508.34;
557 :
558 0 : const char *pszConnStr = FormGDALTMSConnectionString(
559 0 : osTMSURL, osResourceId, nEPSG, nCacheExpires,
560 : nCacheMaxSize);
561 0 : poRasterDS = GDALDataset::FromHandle(GDALOpenEx(
562 : pszConnStr,
563 : GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL,
564 : nullptr, nullptr, nullptr));
565 0 : SetupRasterDSWrapper(stExtent);
566 : }
567 : }
568 0 : else if (osResourceType == "webmap")
569 : {
570 0 : OGREnvelope stExtent;
571 : // Set full extent for EPSG:3857.
572 0 : stExtent.MinX = -20037508.34;
573 0 : stExtent.MaxX = 20037508.34;
574 0 : stExtent.MinY = -20037508.34;
575 0 : stExtent.MaxY = 20037508.34;
576 :
577 : // Get all styles
578 0 : auto aoChildren = oRoot.GetArray("webmap/children");
579 0 : auto sIdentifiers = GetStylesIdentifiers(aoChildren, 0);
580 :
581 0 : const char *pszConnStr = FormGDALTMSConnectionString(
582 0 : osUrl, sIdentifiers, 3857, nCacheExpires, nCacheMaxSize);
583 0 : poRasterDS = GDALDataset::FromHandle(GDALOpenEx(
584 : pszConnStr,
585 : GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL,
586 : nullptr, nullptr, nullptr));
587 0 : SetupRasterDSWrapper(stExtent);
588 : }
589 0 : else if (osResourceType == "raster_layer")
590 : {
591 0 : auto osCogURL = NGWAPI::GetCOGURL(osUrl, osResourceId);
592 0 : auto osConnStr = std::string("/vsicurl/") + osCogURL;
593 :
594 0 : CPLDebug("NGW", "Raster url is: %s", osConnStr.c_str());
595 :
596 0 : poRasterDS = GDALDataset::FromHandle(GDALOpenEx(
597 : osConnStr.c_str(),
598 : GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL,
599 : nullptr, nullptr, nullptr));
600 :
601 : // Add styles if exists
602 0 : auto osRasterResourceId = oRoot.GetString("resource/id");
603 0 : CPLJSONDocument oResourceRequest;
604 0 : bool bLoadResult = oResourceRequest.LoadUrl(
605 0 : NGWAPI::GetChildrenURL(osUrl, osRasterResourceId),
606 0 : aosHTTPOptions);
607 0 : if (bLoadResult)
608 : {
609 0 : CPLJSONArray oChildren(oResourceRequest.GetRoot());
610 0 : for (const auto &oChild : oChildren)
611 : {
612 0 : AddRaster(oChild);
613 : }
614 : }
615 :
616 0 : SetupRasterDSWrapper(OGREnvelope());
617 : }
618 : else
619 : {
620 0 : bResult = false;
621 : }
622 :
623 : // TODO: Add support for wfsserver_service, wmsserver_service,
624 : // raster_mosaic, tileset.
625 : }
626 : }
627 :
628 0 : return bResult;
629 : }
630 :
631 : /*
632 : * FillResources()
633 : */
634 0 : bool OGRNGWDataset::FillResources(const CPLStringList &aosHTTPOptions,
635 : int nOpenFlagsIn)
636 : {
637 0 : CPLJSONDocument oResourceDetailsReq;
638 : // Fill domains
639 0 : bool bResult = oResourceDetailsReq.LoadUrl(
640 0 : NGWAPI::GetSearchURL(osUrl, "cls", "lookup_table"), aosHTTPOptions);
641 0 : if (bResult)
642 : {
643 0 : CPLJSONArray oChildren(oResourceDetailsReq.GetRoot());
644 0 : for (const auto &oChild : oChildren)
645 : {
646 0 : OGRNGWCodedFieldDomain oDomain(oChild);
647 0 : if (oDomain.GetID() > 0)
648 : {
649 0 : moDomains[oDomain.GetID()] = oDomain;
650 : }
651 : }
652 : }
653 :
654 : // Fill child resources
655 0 : bResult = oResourceDetailsReq.LoadUrl(
656 0 : NGWAPI::GetChildrenURL(osUrl, osResourceId), aosHTTPOptions);
657 :
658 0 : if (bResult)
659 : {
660 0 : CPLJSONArray oChildren(oResourceDetailsReq.GetRoot());
661 0 : for (const auto &oChild : oChildren)
662 : {
663 0 : if (nOpenFlagsIn & GDAL_OF_VECTOR)
664 : {
665 : // Add vector layer. If failed, try next layer.
666 0 : AddLayer(oChild, aosHTTPOptions, nOpenFlagsIn);
667 : }
668 :
669 0 : if (nOpenFlagsIn & GDAL_OF_RASTER)
670 : {
671 0 : AddRaster(oChild);
672 : }
673 : }
674 : }
675 0 : return bResult;
676 : }
677 :
678 : /*
679 : * AddLayer()
680 : */
681 0 : void OGRNGWDataset::AddLayer(const CPLJSONObject &oResourceJsonObject,
682 : const CPLStringList &aosHTTPOptions,
683 : int nOpenFlagsIn)
684 : {
685 0 : auto osResourceType = oResourceJsonObject.GetString("resource/cls");
686 0 : if (!NGWAPI::CheckSupportedType(false, osResourceType))
687 : {
688 : // NOTE: Only vector_layer and postgis_layer types now supported
689 0 : return;
690 : }
691 :
692 0 : auto osLayerResourceId = oResourceJsonObject.GetString("resource/id");
693 0 : if (nOpenFlagsIn & GDAL_OF_VECTOR)
694 : {
695 0 : OGRNGWLayerPtr poLayer(new OGRNGWLayer(this, oResourceJsonObject));
696 0 : aoLayers.emplace_back(poLayer);
697 0 : osLayerResourceId = poLayer->GetResourceId();
698 : }
699 :
700 : // Check styles exist and add them as rasters.
701 0 : if (nOpenFlagsIn & GDAL_OF_RASTER &&
702 0 : oResourceJsonObject.GetBool("resource/children", false))
703 : {
704 0 : CPLJSONDocument oResourceChildReq;
705 0 : bool bResult = oResourceChildReq.LoadUrl(
706 0 : NGWAPI::GetChildrenURL(osUrl, osLayerResourceId), aosHTTPOptions);
707 :
708 0 : if (bResult)
709 : {
710 0 : CPLJSONArray oChildren(oResourceChildReq.GetRoot());
711 0 : for (const auto &oChild : oChildren)
712 : {
713 0 : AddRaster(oChild);
714 : }
715 : }
716 : }
717 : }
718 :
719 : /*
720 : * AddRaster()
721 : */
722 0 : void OGRNGWDataset::AddRaster(const CPLJSONObject &oRasterJsonObj)
723 : {
724 0 : auto osResourceType = oRasterJsonObj.GetString("resource/cls");
725 0 : if (!NGWAPI::CheckSupportedType(true, osResourceType))
726 : {
727 0 : return;
728 : }
729 :
730 0 : auto osOutResourceId = oRasterJsonObj.GetString("resource/id");
731 0 : auto osOutResourceName = oRasterJsonObj.GetString("resource/display_name");
732 :
733 0 : if (osOutResourceName.empty())
734 : {
735 0 : osOutResourceName = "raster_" + osOutResourceId;
736 : }
737 :
738 0 : CPLDebug("NGW", "Add raster %s: %s", osOutResourceId.c_str(),
739 : osOutResourceName.c_str());
740 :
741 0 : GDALDataset::SetMetadataItem(CPLSPrintf("SUBDATASET_%d_NAME", nRasters + 1),
742 : CPLSPrintf("NGW:%s/resource/%s", osUrl.c_str(),
743 : osOutResourceId.c_str()),
744 : "SUBDATASETS");
745 0 : GDALDataset::SetMetadataItem(CPLSPrintf("SUBDATASET_%d_DESC", nRasters + 1),
746 : CPLSPrintf("%s (%s)",
747 : osOutResourceName.c_str(),
748 : osResourceType.c_str()),
749 : "SUBDATASETS");
750 0 : nRasters++;
751 : }
752 :
753 : /*
754 : * ICreateLayer
755 : */
756 0 : OGRLayer *OGRNGWDataset::ICreateLayer(const char *pszNameIn,
757 : const OGRGeomFieldDefn *poGeomFieldDefn,
758 : CSLConstList papszOptions)
759 : {
760 0 : if (!IsUpdateMode())
761 : {
762 0 : CPLError(CE_Failure, CPLE_AppDefined,
763 : "Operation not available in read-only mode");
764 0 : return nullptr;
765 : }
766 :
767 0 : const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
768 : const auto poSpatialRef =
769 0 : poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
770 :
771 : // Check permissions as we create new layer in memory and will create in
772 : // during SyncToDisk.
773 0 : FetchPermissions();
774 :
775 0 : if (!stPermissions.bResourceCanCreate)
776 : {
777 0 : CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
778 0 : return nullptr;
779 : }
780 :
781 : // Check input parameters.
782 0 : if ((eGType < wkbPoint || eGType > wkbMultiPolygon) &&
783 0 : (eGType < wkbPoint25D || eGType > wkbMultiPolygon25D))
784 : {
785 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unsupported geometry type: %s",
786 : OGRGeometryTypeToName(eGType));
787 0 : return nullptr;
788 : }
789 :
790 0 : if (!poSpatialRef)
791 : {
792 0 : CPLError(CE_Failure, CPLE_AppDefined, "Undefined spatial reference");
793 0 : return nullptr;
794 : }
795 :
796 0 : OGRSpatialReference *poSRSClone = poSpatialRef->Clone();
797 0 : poSRSClone->AutoIdentifyEPSG();
798 0 : const char *pszEPSG = poSRSClone->GetAuthorityCode(nullptr);
799 0 : int nEPSG = -1;
800 0 : if (pszEPSG != nullptr)
801 : {
802 0 : nEPSG = atoi(pszEPSG);
803 : }
804 :
805 0 : if (nEPSG != 3857) // TODO: Check NextGIS Web supported SRS.
806 : {
807 0 : CPLError(CE_Failure, CPLE_AppDefined,
808 : "Unsupported spatial reference EPSG code: %d", nEPSG);
809 0 : poSRSClone->Release();
810 0 : return nullptr;
811 : }
812 :
813 : // Do we already have this layer? If so, should we blow it away?
814 0 : bool bOverwrite = CPLFetchBool(papszOptions, "OVERWRITE", false);
815 0 : for (int iLayer = 0; iLayer < GetLayerCount(); ++iLayer)
816 : {
817 0 : if (EQUAL(pszNameIn, aoLayers[iLayer]->GetName()))
818 : {
819 0 : if (bOverwrite)
820 : {
821 0 : DeleteLayer(iLayer);
822 0 : break;
823 : }
824 : else
825 : {
826 0 : CPLError(CE_Failure, CPLE_AppDefined,
827 : "Layer %s already exists, CreateLayer failed.\n"
828 : "Use the layer creation option OVERWRITE=YES to "
829 : "replace it.",
830 : pszNameIn);
831 0 : poSRSClone->Release();
832 0 : return nullptr;
833 : }
834 : }
835 : }
836 :
837 : // Create layer.
838 0 : std::string osKey = CSLFetchNameValueDef(papszOptions, "KEY", "");
839 0 : std::string osDesc = CSLFetchNameValueDef(papszOptions, "DESCRIPTION", "");
840 0 : poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
841 : OGRNGWLayerPtr poLayer(
842 0 : new OGRNGWLayer(this, pszNameIn, poSRSClone, eGType, osKey, osDesc));
843 0 : poSRSClone->Release();
844 0 : aoLayers.emplace_back(poLayer);
845 0 : return poLayer.get();
846 : }
847 :
848 : /*
849 : * DeleteLayer()
850 : */
851 0 : OGRErr OGRNGWDataset::DeleteLayer(int iLayer)
852 : {
853 0 : if (!IsUpdateMode())
854 : {
855 0 : CPLError(CE_Failure, CPLE_AppDefined,
856 : "Operation not available in read-only mode.");
857 0 : return OGRERR_FAILURE;
858 : }
859 :
860 0 : if (iLayer < 0 || iLayer >= GetLayerCount())
861 : {
862 0 : CPLError(CE_Failure, CPLE_AppDefined,
863 : "Layer %d not in legal range of 0 to %d.", iLayer,
864 0 : GetLayerCount() - 1);
865 0 : return OGRERR_FAILURE;
866 : }
867 :
868 0 : auto poLayer = aoLayers[iLayer];
869 0 : if (poLayer->GetResourceId() != "-1")
870 : {
871 : // For layers from server we can check permissions.
872 :
873 : // We can skip check permissions here as papoLayers[iLayer]->Delete()
874 : // will return false if no delete permission available.
875 0 : FetchPermissions();
876 :
877 0 : if (!stPermissions.bResourceCanDelete)
878 : {
879 0 : CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
880 0 : return OGRERR_FAILURE;
881 : }
882 : }
883 :
884 0 : if (poLayer->Delete())
885 : {
886 0 : aoLayers.erase(aoLayers.begin() + iLayer);
887 : }
888 :
889 0 : return OGRERR_NONE;
890 : }
891 :
892 : /*
893 : * FillMetadata()
894 : */
895 0 : void OGRNGWDataset::FillMetadata(const CPLJSONObject &oRootObject)
896 : {
897 0 : std::string osCreateDate = oRootObject.GetString("resource/creation_date");
898 0 : if (!osCreateDate.empty())
899 : {
900 0 : GDALDataset::SetMetadataItem("creation_date", osCreateDate.c_str());
901 : }
902 0 : osName = oRootObject.GetString("resource/display_name");
903 0 : SetDescription(osName.c_str());
904 0 : GDALDataset::SetMetadataItem("display_name", osName.c_str());
905 0 : std::string osDescription = oRootObject.GetString("resource/description");
906 0 : if (!osDescription.empty())
907 : {
908 0 : GDALDataset::SetMetadataItem("description", osDescription.c_str());
909 : }
910 0 : std::string osResourceType = oRootObject.GetString("resource/cls");
911 0 : if (!osResourceType.empty())
912 : {
913 0 : GDALDataset::SetMetadataItem("resource_type", osResourceType.c_str());
914 : }
915 : std::string osResourceParentId =
916 0 : oRootObject.GetString("resource/parent/id");
917 0 : if (!osResourceParentId.empty())
918 : {
919 0 : GDALDataset::SetMetadataItem("parent_id", osResourceParentId.c_str());
920 : }
921 0 : GDALDataset::SetMetadataItem("id", osResourceId.c_str());
922 :
923 : std::vector<CPLJSONObject> items =
924 0 : oRootObject.GetObj("resmeta/items").GetChildren();
925 :
926 0 : for (const CPLJSONObject &item : items)
927 : {
928 0 : std::string osSuffix = NGWAPI::GetResmetaSuffix(item.GetType());
929 0 : GDALDataset::SetMetadataItem((item.GetName() + osSuffix).c_str(),
930 0 : item.ToString().c_str(), "NGW");
931 : }
932 0 : }
933 :
934 : /*
935 : * FlushMetadata()
936 : */
937 0 : bool OGRNGWDataset::FlushMetadata(char **papszMetadata)
938 : {
939 0 : if (!bMetadataDerty)
940 : {
941 0 : return true;
942 : }
943 :
944 0 : bool bResult = NGWAPI::FlushMetadata(osUrl, osResourceId, papszMetadata,
945 0 : GetHeaders(false));
946 0 : if (bResult)
947 : {
948 0 : bMetadataDerty = false;
949 : }
950 :
951 0 : return bResult;
952 : }
953 :
954 : /*
955 : * SetMetadata()
956 : */
957 0 : CPLErr OGRNGWDataset::SetMetadata(char **papszMetadata, const char *pszDomain)
958 : {
959 0 : FetchPermissions();
960 0 : if (!stPermissions.bMetadataCanWrite)
961 : {
962 0 : CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
963 0 : return CE_Failure;
964 : }
965 :
966 0 : CPLErr eResult = GDALDataset::SetMetadata(papszMetadata, pszDomain);
967 0 : if (eResult == CE_None && pszDomain != nullptr && EQUAL(pszDomain, "NGW"))
968 : {
969 0 : eResult = FlushMetadata(papszMetadata) ? CE_None : CE_Failure;
970 : }
971 0 : return eResult;
972 : }
973 :
974 : /*
975 : * SetMetadataItem()
976 : */
977 0 : CPLErr OGRNGWDataset::SetMetadataItem(const char *pszName, const char *pszValue,
978 : const char *pszDomain)
979 : {
980 0 : FetchPermissions();
981 0 : if (!stPermissions.bMetadataCanWrite)
982 : {
983 0 : CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
984 0 : return CE_Failure;
985 : }
986 0 : if (pszDomain != nullptr && EQUAL(pszDomain, "NGW"))
987 : {
988 0 : bMetadataDerty = true;
989 : }
990 0 : return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
991 : }
992 :
993 : /*
994 : * FlushCache()
995 : */
996 0 : CPLErr OGRNGWDataset::FlushCache(bool bAtClosing)
997 : {
998 0 : CPLErr eErr = GDALDataset::FlushCache(bAtClosing);
999 0 : if (!FlushMetadata(GetMetadata("NGW")))
1000 0 : eErr = CE_Failure;
1001 0 : return eErr;
1002 : }
1003 :
1004 : /*
1005 : * GetHeaders()
1006 : */
1007 0 : CPLStringList OGRNGWDataset::GetHeaders(bool bSkipRetry) const
1008 : {
1009 0 : CPLStringList aosOptions;
1010 0 : aosOptions.AddNameValue("HEADERS", "Accept: */*");
1011 0 : aosOptions.AddNameValue("JSON_DEPTH", osJsonDepth.c_str());
1012 0 : if (!osUserPwd.empty())
1013 : {
1014 0 : aosOptions.AddNameValue("HTTPAUTH", "BASIC");
1015 0 : aosOptions.AddNameValue("USERPWD", osUserPwd.c_str());
1016 : }
1017 :
1018 0 : if (!osConnectTimeout.empty())
1019 : {
1020 0 : aosOptions.AddNameValue("CONNECTTIMEOUT", osConnectTimeout.c_str());
1021 : }
1022 :
1023 0 : if (!osTimeout.empty())
1024 : {
1025 0 : aosOptions.AddNameValue("TIMEOUT", osTimeout.c_str());
1026 : }
1027 :
1028 0 : if (!bSkipRetry)
1029 : {
1030 0 : if (!osRetryCount.empty())
1031 : {
1032 0 : aosOptions.AddNameValue("MAX_RETRY", osRetryCount.c_str());
1033 : }
1034 0 : if (!osRetryDelay.empty())
1035 : {
1036 0 : aosOptions.AddNameValue("RETRY_DELAY", osRetryDelay.c_str());
1037 : }
1038 : }
1039 0 : return aosOptions;
1040 : }
1041 :
1042 : /*
1043 : * SQLUnescape()
1044 : * Get from gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteutility.cpp as we don't want
1045 : * dependency on sqlite
1046 : */
1047 0 : static CPLString SQLUnescape(const char *pszVal)
1048 : {
1049 0 : char chQuoteChar = pszVal[0];
1050 0 : if (chQuoteChar != '\'' && chQuoteChar != '"')
1051 0 : return pszVal;
1052 :
1053 0 : CPLString osRet;
1054 0 : pszVal++;
1055 0 : while (*pszVal != '\0')
1056 : {
1057 0 : if (*pszVal == chQuoteChar)
1058 : {
1059 0 : if (pszVal[1] == chQuoteChar)
1060 0 : pszVal++;
1061 : else
1062 0 : break;
1063 : }
1064 0 : osRet += *pszVal;
1065 0 : pszVal++;
1066 : }
1067 0 : return osRet;
1068 : }
1069 :
1070 : /*
1071 : * SQLTokenize()
1072 : * Get from gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteutility.cpp as we don't want
1073 : * dependency on sqlite
1074 : */
1075 0 : static char **SQLTokenize(const char *pszStr)
1076 : {
1077 0 : char **papszTokens = nullptr;
1078 0 : bool bInQuote = false;
1079 0 : char chQuoteChar = '\0';
1080 0 : bool bInSpace = true;
1081 0 : CPLString osCurrentToken;
1082 0 : while (*pszStr != '\0')
1083 : {
1084 0 : if (*pszStr == ' ' && !bInQuote)
1085 : {
1086 0 : if (!bInSpace)
1087 : {
1088 0 : papszTokens = CSLAddString(papszTokens, osCurrentToken);
1089 0 : osCurrentToken.clear();
1090 : }
1091 0 : bInSpace = true;
1092 : }
1093 0 : else if ((*pszStr == '(' || *pszStr == ')' || *pszStr == ',') &&
1094 0 : !bInQuote)
1095 : {
1096 0 : if (!bInSpace)
1097 : {
1098 0 : papszTokens = CSLAddString(papszTokens, osCurrentToken);
1099 0 : osCurrentToken.clear();
1100 : }
1101 0 : osCurrentToken.clear();
1102 0 : osCurrentToken += *pszStr;
1103 0 : papszTokens = CSLAddString(papszTokens, osCurrentToken);
1104 0 : osCurrentToken.clear();
1105 0 : bInSpace = true;
1106 : }
1107 0 : else if (*pszStr == '"' || *pszStr == '\'')
1108 : {
1109 0 : if (bInQuote && *pszStr == chQuoteChar && pszStr[1] == chQuoteChar)
1110 : {
1111 0 : osCurrentToken += *pszStr;
1112 0 : osCurrentToken += *pszStr;
1113 0 : pszStr += 2;
1114 0 : continue;
1115 : }
1116 0 : else if (bInQuote && *pszStr == chQuoteChar)
1117 : {
1118 0 : osCurrentToken += *pszStr;
1119 0 : papszTokens = CSLAddString(papszTokens, osCurrentToken);
1120 0 : osCurrentToken.clear();
1121 0 : bInSpace = true;
1122 0 : bInQuote = false;
1123 0 : chQuoteChar = '\0';
1124 : }
1125 0 : else if (bInQuote)
1126 : {
1127 0 : osCurrentToken += *pszStr;
1128 : }
1129 : else
1130 : {
1131 0 : chQuoteChar = *pszStr;
1132 0 : osCurrentToken.clear();
1133 0 : osCurrentToken += chQuoteChar;
1134 0 : bInQuote = true;
1135 0 : bInSpace = false;
1136 : }
1137 : }
1138 : else
1139 : {
1140 0 : osCurrentToken += *pszStr;
1141 0 : bInSpace = false;
1142 : }
1143 0 : pszStr++;
1144 : }
1145 :
1146 0 : if (!osCurrentToken.empty())
1147 0 : papszTokens = CSLAddString(papszTokens, osCurrentToken);
1148 :
1149 0 : return papszTokens;
1150 : }
1151 :
1152 : /*
1153 : * ExecuteSQL()
1154 : */
1155 0 : OGRLayer *OGRNGWDataset::ExecuteSQL(const char *pszStatement,
1156 : OGRGeometry *poSpatialFilter,
1157 : const char *pszDialect)
1158 : {
1159 : // Clean statement string.
1160 0 : CPLString osStatement(pszStatement);
1161 0 : osStatement = osStatement.Trim().replaceAll(" ", " ");
1162 :
1163 0 : if (STARTS_WITH_CI(osStatement, "DELLAYER:"))
1164 : {
1165 0 : CPLString osLayerName = osStatement.substr(strlen("DELLAYER:"));
1166 0 : if (osLayerName.endsWith(";"))
1167 : {
1168 0 : osLayerName = osLayerName.substr(0, osLayerName.size() - 1);
1169 0 : osLayerName.Trim();
1170 : }
1171 :
1172 0 : CPLDebug("NGW", "Delete layer with name %s.", osLayerName.c_str());
1173 :
1174 0 : for (int iLayer = 0; iLayer < GetLayerCount(); ++iLayer)
1175 : {
1176 0 : if (EQUAL(aoLayers[iLayer]->GetName(), osLayerName))
1177 : {
1178 0 : DeleteLayer(iLayer);
1179 0 : return nullptr;
1180 : }
1181 : }
1182 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
1183 : osLayerName.c_str());
1184 :
1185 0 : return nullptr;
1186 : }
1187 :
1188 0 : if (STARTS_WITH_CI(osStatement, "DELETE FROM"))
1189 : {
1190 0 : osStatement = osStatement.substr(strlen("DELETE FROM "));
1191 0 : if (osStatement.endsWith(";"))
1192 : {
1193 0 : osStatement = osStatement.substr(0, osStatement.size() - 1);
1194 0 : osStatement.Trim();
1195 : }
1196 :
1197 0 : std::size_t found = osStatement.find("WHERE");
1198 0 : CPLString osLayerName;
1199 0 : if (found == std::string::npos)
1200 : { // No where clause
1201 0 : osLayerName = osStatement;
1202 0 : osStatement.clear();
1203 : }
1204 : else
1205 : {
1206 0 : osLayerName = osStatement.substr(0, found);
1207 0 : osLayerName.Trim();
1208 0 : osStatement = osStatement.substr(found + strlen("WHERE "));
1209 : }
1210 :
1211 : OGRNGWLayer *poLayer =
1212 0 : reinterpret_cast<OGRNGWLayer *>(GetLayerByName(osLayerName));
1213 0 : if (nullptr == poLayer)
1214 : {
1215 0 : CPLError(CE_Failure, CPLE_AppDefined,
1216 : "Layer %s not found in dataset.", osName.c_str());
1217 0 : return nullptr;
1218 : }
1219 :
1220 0 : if (osStatement.empty())
1221 : {
1222 0 : poLayer->DeleteAllFeatures();
1223 : }
1224 : else
1225 : {
1226 0 : CPLDebug("NGW", "Delete features with statement %s",
1227 : osStatement.c_str());
1228 0 : OGRFeatureQuery oQuery;
1229 0 : OGRErr eErr = oQuery.Compile(poLayer->GetLayerDefn(), osStatement);
1230 0 : if (eErr != OGRERR_NONE)
1231 : {
1232 0 : return nullptr;
1233 : }
1234 :
1235 : // Ignore all fields except first and ignore geometry
1236 0 : auto poLayerDefn = poLayer->GetLayerDefn();
1237 0 : poLayerDefn->SetGeometryIgnored(TRUE);
1238 0 : if (poLayerDefn->GetFieldCount() > 0)
1239 : {
1240 0 : std::set<std::string> osFields;
1241 0 : OGRFieldDefn *poFieldDefn = poLayerDefn->GetFieldDefn(0);
1242 0 : osFields.insert(poFieldDefn->GetNameRef());
1243 0 : poLayer->SetSelectedFields(osFields);
1244 : }
1245 : CPLString osNgwDelete =
1246 0 : "NGW:" +
1247 0 : OGRNGWLayer::TranslateSQLToFilter(
1248 0 : reinterpret_cast<swq_expr_node *>(oQuery.GetSWQExpr()));
1249 :
1250 0 : poLayer->SetAttributeFilter(osNgwDelete);
1251 :
1252 0 : std::vector<GIntBig> aiFeaturesIDs;
1253 : OGRFeature *poFeat;
1254 0 : while ((poFeat = poLayer->GetNextFeature()) != nullptr)
1255 : {
1256 0 : aiFeaturesIDs.push_back(poFeat->GetFID());
1257 0 : OGRFeature::DestroyFeature(poFeat);
1258 : }
1259 :
1260 0 : poLayer->DeleteFeatures(aiFeaturesIDs);
1261 :
1262 : // Reset all filters and ignores
1263 0 : poLayerDefn->SetGeometryIgnored(FALSE);
1264 0 : poLayer->SetAttributeFilter(nullptr);
1265 0 : poLayer->SetIgnoredFields(nullptr);
1266 : }
1267 0 : return nullptr;
1268 : }
1269 :
1270 0 : if (STARTS_WITH_CI(osStatement, "DROP TABLE"))
1271 : {
1272 : // Get layer name from pszStatement DELETE FROM layer;.
1273 0 : CPLString osLayerName = osStatement.substr(strlen("DROP TABLE "));
1274 0 : if (osLayerName.endsWith(";"))
1275 : {
1276 0 : osLayerName = osLayerName.substr(0, osLayerName.size() - 1);
1277 0 : osLayerName.Trim();
1278 : }
1279 :
1280 0 : CPLDebug("NGW", "Delete layer with name %s.", osLayerName.c_str());
1281 :
1282 0 : for (int iLayer = 0; iLayer < GetLayerCount(); ++iLayer)
1283 : {
1284 0 : if (EQUAL(aoLayers[iLayer]->GetName(), osLayerName))
1285 : {
1286 0 : DeleteLayer(iLayer);
1287 0 : return nullptr;
1288 : }
1289 : }
1290 :
1291 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
1292 : osLayerName.c_str());
1293 :
1294 0 : return nullptr;
1295 : }
1296 :
1297 0 : if (STARTS_WITH_CI(osStatement, "ALTER TABLE "))
1298 : {
1299 0 : if (osStatement.endsWith(";"))
1300 : {
1301 0 : osStatement = osStatement.substr(0, osStatement.size() - 1);
1302 0 : osStatement.Trim();
1303 : }
1304 :
1305 0 : CPLStringList aosTokens(SQLTokenize(osStatement));
1306 : /* ALTER TABLE src_table RENAME TO dst_table */
1307 0 : if (aosTokens.size() == 6 && EQUAL(aosTokens[3], "RENAME") &&
1308 0 : EQUAL(aosTokens[4], "TO"))
1309 : {
1310 0 : const char *pszSrcTableName = aosTokens[2];
1311 0 : const char *pszDstTableName = aosTokens[5];
1312 :
1313 : OGRNGWLayer *poLayer = static_cast<OGRNGWLayer *>(
1314 0 : GetLayerByName(SQLUnescape(pszSrcTableName)));
1315 0 : if (poLayer)
1316 : {
1317 0 : poLayer->Rename(SQLUnescape(pszDstTableName));
1318 0 : return nullptr;
1319 : }
1320 :
1321 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
1322 : pszSrcTableName);
1323 : }
1324 : else
1325 : {
1326 0 : CPLError(CE_Failure, CPLE_AppDefined,
1327 : "Unsupported alter table operation. Only rename table to "
1328 : "... support.");
1329 : }
1330 0 : return nullptr;
1331 : }
1332 :
1333 : // SELECT xxxxx FROM yyyy WHERE zzzzzz;
1334 0 : if (STARTS_WITH_CI(osStatement, "SELECT "))
1335 : {
1336 0 : swq_select oSelect;
1337 0 : CPLDebug("NGW", "Select statement: %s", osStatement.c_str());
1338 0 : if (oSelect.preparse(osStatement) != CE_None)
1339 : {
1340 0 : return nullptr;
1341 : }
1342 :
1343 0 : if (oSelect.join_count == 0 && oSelect.poOtherSelect == nullptr &&
1344 0 : oSelect.table_count == 1 && oSelect.order_specs == 0)
1345 : {
1346 : OGRNGWLayer *poLayer = reinterpret_cast<OGRNGWLayer *>(
1347 0 : GetLayerByName(oSelect.table_defs[0].table_name));
1348 0 : if (nullptr == poLayer)
1349 : {
1350 0 : CPLError(CE_Failure, CPLE_AppDefined,
1351 : "Layer %s not found in dataset.",
1352 0 : oSelect.table_defs[0].table_name);
1353 0 : return nullptr;
1354 : }
1355 :
1356 0 : std::set<std::string> aosFields;
1357 0 : bool bSkip = false;
1358 0 : for (int i = 0; i < oSelect.result_columns(); ++i)
1359 : {
1360 0 : swq_col_func col_func = oSelect.column_defs[i].col_func;
1361 0 : if (col_func != SWQCF_NONE)
1362 : {
1363 0 : bSkip = true;
1364 0 : break;
1365 : }
1366 :
1367 0 : if (oSelect.column_defs[i].distinct_flag)
1368 : {
1369 0 : CPLError(CE_Warning, CPLE_AppDefined,
1370 : "Distinct not supported.");
1371 0 : bSkip = true;
1372 0 : break;
1373 : }
1374 :
1375 0 : if (oSelect.column_defs[i].field_name != nullptr)
1376 : {
1377 0 : if (EQUAL(oSelect.column_defs[i].field_name, "*"))
1378 : {
1379 0 : aosFields.clear();
1380 0 : aosFields.emplace(oSelect.column_defs[i].field_name);
1381 0 : break;
1382 : }
1383 : else
1384 : {
1385 0 : aosFields.emplace(oSelect.column_defs[i].field_name);
1386 : }
1387 : }
1388 : }
1389 :
1390 0 : std::string osNgwSelect;
1391 0 : for (int iKey = 0; iKey < oSelect.order_specs; iKey++)
1392 : {
1393 0 : swq_order_def *psKeyDef = oSelect.order_defs + iKey;
1394 0 : if (iKey > 0)
1395 : {
1396 0 : osNgwSelect += ",";
1397 : }
1398 :
1399 0 : if (psKeyDef->ascending_flag == TRUE)
1400 : {
1401 0 : osNgwSelect += psKeyDef->field_name;
1402 : }
1403 : else
1404 : {
1405 0 : osNgwSelect += "-" + std::string(psKeyDef->field_name);
1406 : }
1407 : }
1408 :
1409 0 : if (oSelect.where_expr != nullptr)
1410 : {
1411 0 : if (!osNgwSelect.empty())
1412 : {
1413 0 : osNgwSelect += "&";
1414 : }
1415 : osNgwSelect +=
1416 0 : OGRNGWLayer::TranslateSQLToFilter(oSelect.where_expr);
1417 : }
1418 :
1419 0 : if (osNgwSelect.empty())
1420 : {
1421 0 : bSkip = true;
1422 : }
1423 :
1424 0 : if (!bSkip)
1425 : {
1426 0 : if (aosFields.empty())
1427 : {
1428 0 : CPLError(
1429 : CE_Failure, CPLE_AppDefined,
1430 : "SELECT statement is invalid: field list is empty.");
1431 0 : return nullptr;
1432 : }
1433 :
1434 0 : if (poLayer->SyncToDisk() != OGRERR_NONE)
1435 : {
1436 0 : return nullptr;
1437 : }
1438 :
1439 0 : OGRNGWLayer *poOutLayer = poLayer->Clone();
1440 0 : if (aosFields.size() == 1 && *(aosFields.begin()) == "*")
1441 : {
1442 0 : poOutLayer->SetIgnoredFields(nullptr);
1443 : }
1444 : else
1445 : {
1446 0 : poOutLayer->SetSelectedFields(aosFields);
1447 : }
1448 0 : poOutLayer->SetSpatialFilter(poSpatialFilter);
1449 :
1450 0 : if (osNgwSelect
1451 0 : .empty()) // If we here oSelect.where_expr is empty
1452 : {
1453 0 : poOutLayer->SetAttributeFilter(nullptr);
1454 : }
1455 : else
1456 : {
1457 0 : std::string osAttributeFilte = "NGW:" + osNgwSelect;
1458 0 : poOutLayer->SetAttributeFilter(osAttributeFilte.c_str());
1459 : }
1460 0 : return poOutLayer;
1461 : }
1462 : }
1463 : }
1464 :
1465 0 : return GDALDataset::ExecuteSQL(pszStatement, poSpatialFilter, pszDialect);
1466 : }
1467 :
1468 : /*
1469 : * GetProjectionRef()
1470 : */
1471 0 : const OGRSpatialReference *OGRNGWDataset::GetSpatialRef() const
1472 : {
1473 0 : if (poRasterDS != nullptr)
1474 : {
1475 0 : return poRasterDS->GetSpatialRef();
1476 : }
1477 0 : return GDALDataset::GetSpatialRef();
1478 : }
1479 :
1480 : /*
1481 : * GetGeoTransform()
1482 : */
1483 0 : CPLErr OGRNGWDataset::GetGeoTransform(double *padfTransform)
1484 : {
1485 0 : if (poRasterDS != nullptr)
1486 : {
1487 0 : return poRasterDS->GetGeoTransform(padfTransform);
1488 : }
1489 0 : return GDALDataset::GetGeoTransform(padfTransform);
1490 : }
1491 :
1492 : /*
1493 : * IRasterIO()
1494 : */
1495 0 : CPLErr OGRNGWDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1496 : int nXSize, int nYSize, void *pData,
1497 : int nBufXSize, int nBufYSize,
1498 : GDALDataType eBufType, int nBandCount,
1499 : BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
1500 : GSpacing nLineSpace, GSpacing nBandSpace,
1501 : GDALRasterIOExtraArg *psExtraArg)
1502 : {
1503 0 : if (poRasterDS != nullptr)
1504 : {
1505 0 : if (stPixelExtent.IsInit())
1506 : {
1507 0 : OGREnvelope stTestExtent;
1508 0 : stTestExtent.MinX = static_cast<double>(nXOff);
1509 0 : stTestExtent.MinY = static_cast<double>(nYOff);
1510 0 : stTestExtent.MaxX = static_cast<double>(nXOff + nXSize);
1511 0 : stTestExtent.MaxY = static_cast<double>(nYOff + nYSize);
1512 :
1513 0 : if (!stPixelExtent.Intersects(stTestExtent))
1514 : {
1515 0 : CPLDebug("NGW", "Raster extent in px is: %f, %f, %f, %f",
1516 : stPixelExtent.MinX, stPixelExtent.MinY,
1517 : stPixelExtent.MaxX, stPixelExtent.MaxY);
1518 0 : CPLDebug("NGW", "RasterIO extent is: %f, %f, %f, %f",
1519 : stTestExtent.MinX, stTestExtent.MinY,
1520 : stTestExtent.MaxX, stTestExtent.MaxY);
1521 :
1522 : // Fill buffer transparent color.
1523 0 : memset(pData, 0,
1524 0 : static_cast<size_t>(nBufXSize) * nBufYSize * nBandCount *
1525 0 : GDALGetDataTypeSizeBytes(eBufType));
1526 0 : return CE_None;
1527 : }
1528 : }
1529 : }
1530 0 : return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
1531 : nBufXSize, nBufYSize, eBufType, nBandCount,
1532 : panBandMap, nPixelSpace, nLineSpace,
1533 0 : nBandSpace, psExtraArg);
1534 : }
1535 :
1536 : /*
1537 : * FillCapabilities()
1538 : */
1539 0 : void OGRNGWDataset::FillCapabilities(const CPLStringList &aosHTTPOptions)
1540 : {
1541 : // Check NGW version. Paging available from 3.1
1542 0 : CPLJSONDocument oRouteReq;
1543 0 : if (oRouteReq.LoadUrl(NGWAPI::GetVersionURL(osUrl), aosHTTPOptions))
1544 : {
1545 0 : CPLJSONObject oRoot = oRouteReq.GetRoot();
1546 :
1547 0 : if (oRoot.IsValid())
1548 : {
1549 0 : std::string osVersion = oRoot.GetString("nextgisweb", "0.0");
1550 0 : bHasFeaturePaging = NGWAPI::CheckVersion(osVersion, 3, 1);
1551 :
1552 0 : CPLDebug("NGW", "Is feature paging supported: %s",
1553 0 : bHasFeaturePaging ? "yes" : "no");
1554 : }
1555 : }
1556 0 : }
1557 :
1558 : /*
1559 : * Extensions()
1560 : */
1561 0 : std::string OGRNGWDataset::Extensions() const
1562 : {
1563 0 : return osExtensions;
1564 : }
1565 :
1566 : /*
1567 : * GetFieldDomainNames()
1568 : */
1569 0 : std::vector<std::string> OGRNGWDataset::GetFieldDomainNames(CSLConstList) const
1570 : {
1571 0 : std::vector<std::string> oDomainNamesList;
1572 0 : std::array<OGRFieldType, 3> aeFieldTypes{OFTString, OFTInteger,
1573 : OFTInteger64};
1574 0 : for (auto const &oDom : moDomains)
1575 : {
1576 0 : for (auto eFieldType : aeFieldTypes)
1577 : {
1578 0 : auto pOgrDom = oDom.second.ToFieldDomain(eFieldType);
1579 0 : if (pOgrDom != nullptr)
1580 : {
1581 0 : oDomainNamesList.emplace_back(pOgrDom->GetName());
1582 : }
1583 : }
1584 : }
1585 0 : return oDomainNamesList;
1586 : }
1587 :
1588 : /*
1589 : * GetFieldDomain()
1590 : */
1591 : const OGRFieldDomain *
1592 0 : OGRNGWDataset::GetFieldDomain(const std::string &name) const
1593 : {
1594 0 : std::array<OGRFieldType, 3> aeFieldTypes{OFTString, OFTInteger,
1595 : OFTInteger64};
1596 0 : for (auto const &oDom : moDomains)
1597 : {
1598 0 : for (auto eFieldType : aeFieldTypes)
1599 : {
1600 0 : auto pOgrDom = oDom.second.ToFieldDomain(eFieldType);
1601 0 : if (pOgrDom != nullptr)
1602 : {
1603 0 : if (pOgrDom->GetName() == name)
1604 : {
1605 0 : return pOgrDom;
1606 : }
1607 : }
1608 : }
1609 : }
1610 0 : return nullptr;
1611 : }
1612 :
1613 : /*
1614 : * DeleteFieldDomain()
1615 : */
1616 0 : bool OGRNGWDataset::DeleteFieldDomain(const std::string &name,
1617 : std::string &failureReason)
1618 : {
1619 0 : if (eAccess != GA_Update)
1620 : {
1621 : failureReason =
1622 0 : "DeleteFieldDomain() not supported on read-only dataset";
1623 0 : return false;
1624 : }
1625 :
1626 0 : std::array<OGRFieldType, 3> aeFieldTypes{OFTString, OFTInteger,
1627 : OFTInteger64};
1628 0 : for (auto const &oDom : moDomains)
1629 : {
1630 0 : for (auto eFieldType : aeFieldTypes)
1631 : {
1632 0 : auto pOgrDom = oDom.second.ToFieldDomain(eFieldType);
1633 0 : if (pOgrDom != nullptr)
1634 : {
1635 0 : if (pOgrDom->GetName() == name)
1636 : {
1637 0 : auto nResourceID = oDom.second.GetID();
1638 :
1639 0 : CPLError(CE_Warning, CPLE_AppDefined,
1640 : "Delete following domains with common "
1641 : "identifier " CPL_FRMT_GIB ": %s.",
1642 : nResourceID,
1643 0 : oDom.second.GetDomainsNames().c_str());
1644 :
1645 0 : auto result = NGWAPI::DeleteResource(
1646 0 : GetUrl(), std::to_string(nResourceID),
1647 0 : GetHeaders(false));
1648 0 : if (!result)
1649 : {
1650 0 : failureReason = CPLGetLastErrorMsg();
1651 0 : return result;
1652 : }
1653 :
1654 0 : moDomains.erase(nResourceID);
1655 :
1656 : // Remove domain from fields definitions
1657 0 : for (const auto &oLayer : aoLayers)
1658 : {
1659 0 : for (int i = 0;
1660 0 : i < oLayer->GetLayerDefn()->GetFieldCount(); ++i)
1661 : {
1662 : OGRFieldDefn *poFieldDefn =
1663 0 : oLayer->GetLayerDefn()->GetFieldDefn(i);
1664 0 : if (oDom.second.HasDomainName(
1665 : poFieldDefn->GetDomainName()))
1666 : {
1667 : auto oTemporaryUnsealer(
1668 0 : poFieldDefn->GetTemporaryUnsealer());
1669 0 : poFieldDefn->SetDomainName(std::string());
1670 : }
1671 : }
1672 : }
1673 0 : return true;
1674 : }
1675 : }
1676 : }
1677 : }
1678 0 : failureReason = "Domain does not exist";
1679 0 : return false;
1680 : }
1681 :
1682 : /*
1683 : * CreateNGWLookupTableJson()
1684 : */
1685 0 : static std::string CreateNGWLookupTableJson(OGRCodedFieldDomain *pDomain,
1686 : GIntBig nResourceId)
1687 : {
1688 0 : CPLJSONObject oResourceJson;
1689 : // Add resource json item.
1690 0 : CPLJSONObject oResource("resource", oResourceJson);
1691 0 : oResource.Add("cls", "lookup_table");
1692 0 : CPLJSONObject oResourceParent("parent", oResource);
1693 0 : oResourceParent.Add("id", nResourceId);
1694 0 : oResource.Add("display_name", pDomain->GetName());
1695 0 : oResource.Add("description", pDomain->GetDescription());
1696 :
1697 : // Add vector_layer json item.
1698 0 : CPLJSONObject oLookupTable("lookup_table", oResourceJson);
1699 0 : CPLJSONObject oLookupTableItems("items", oLookupTable);
1700 0 : const auto enumeration = pDomain->GetEnumeration();
1701 0 : for (int i = 0; enumeration[i].pszCode != nullptr; ++i)
1702 : {
1703 0 : const char *pszValCurrent = "";
1704 : // NGW not supported null as coded value, so set it as ""
1705 0 : if (enumeration[i].pszValue != nullptr)
1706 : {
1707 0 : pszValCurrent = enumeration[i].pszValue;
1708 : }
1709 0 : oLookupTableItems.Add(enumeration[i].pszCode, pszValCurrent);
1710 : }
1711 :
1712 0 : return oResourceJson.Format(CPLJSONObject::PrettyFormat::Plain);
1713 : }
1714 :
1715 : /*
1716 : * AddFieldDomain()
1717 : */
1718 0 : bool OGRNGWDataset::AddFieldDomain(std::unique_ptr<OGRFieldDomain> &&domain,
1719 : std::string &failureReason)
1720 : {
1721 0 : const std::string domainName(domain->GetName());
1722 0 : if (eAccess != GA_Update)
1723 : {
1724 0 : failureReason = "Add field domain not supported on read-only dataset";
1725 0 : return false;
1726 : }
1727 :
1728 0 : if (GetFieldDomain(domainName) != nullptr)
1729 : {
1730 0 : failureReason = "A domain of identical name already exists";
1731 0 : return false;
1732 : }
1733 :
1734 0 : if (domain->GetDomainType() != OFDT_CODED)
1735 : {
1736 0 : failureReason = "Unsupported domain type";
1737 0 : return false;
1738 : }
1739 :
1740 : auto osPalyload = CreateNGWLookupTableJson(
1741 0 : dynamic_cast<OGRCodedFieldDomain *>(domain.get()),
1742 0 : static_cast<GIntBig>(std::stol(osResourceId)));
1743 :
1744 : std::string osResourceIdInt =
1745 0 : NGWAPI::CreateResource(osUrl, osPalyload, GetHeaders());
1746 0 : if (osResourceIdInt == "-1")
1747 : {
1748 0 : failureReason = CPLGetLastErrorMsg();
1749 0 : return false;
1750 : }
1751 0 : auto osNewResourceUrl = NGWAPI::GetResourceURL(osUrl, osResourceIdInt);
1752 0 : CPLJSONDocument oResourceDetailsReq;
1753 : bool bResult =
1754 0 : oResourceDetailsReq.LoadUrl(osNewResourceUrl, GetHeaders(false));
1755 0 : if (!bResult)
1756 : {
1757 0 : failureReason = CPLGetLastErrorMsg();
1758 0 : return false;
1759 : }
1760 :
1761 0 : OGRNGWCodedFieldDomain oDomain(oResourceDetailsReq.GetRoot());
1762 0 : if (oDomain.GetID() == 0)
1763 : {
1764 0 : failureReason = "Failed to parse domain detailes from NGW";
1765 0 : return false;
1766 : }
1767 0 : moDomains[oDomain.GetID()] = oDomain;
1768 0 : return true;
1769 : }
1770 :
1771 : /*
1772 : * UpdateFieldDomain()
1773 : */
1774 0 : bool OGRNGWDataset::UpdateFieldDomain(std::unique_ptr<OGRFieldDomain> &&domain,
1775 : std::string &failureReason)
1776 : {
1777 0 : const std::string domainName(domain->GetName());
1778 0 : if (eAccess != GA_Update)
1779 : {
1780 0 : failureReason = "Add field domain not supported on read-only dataset";
1781 0 : return false;
1782 : }
1783 :
1784 0 : if (GetFieldDomain(domainName) == nullptr)
1785 : {
1786 0 : failureReason = "The domain should already exist to be updated";
1787 0 : return false;
1788 : }
1789 :
1790 0 : if (domain->GetDomainType() != OFDT_CODED)
1791 : {
1792 0 : failureReason = "Unsupported domain type";
1793 0 : return false;
1794 : }
1795 :
1796 0 : auto nResourceId = GetDomainIdByName(domainName);
1797 0 : if (nResourceId == 0)
1798 : {
1799 0 : failureReason = "Failed get NGW domain identifier";
1800 0 : return false;
1801 : }
1802 :
1803 : auto osPayload = CreateNGWLookupTableJson(
1804 0 : dynamic_cast<OGRCodedFieldDomain *>(domain.get()),
1805 0 : static_cast<GIntBig>(std::stol(osResourceId)));
1806 :
1807 0 : if (!NGWAPI::UpdateResource(osUrl, osResourceId, osPayload, GetHeaders()))
1808 : {
1809 0 : failureReason = CPLGetLastErrorMsg();
1810 0 : return false;
1811 : }
1812 :
1813 0 : auto osNewResourceUrl = NGWAPI::GetResourceURL(osUrl, osResourceId);
1814 0 : CPLJSONDocument oResourceDetailsReq;
1815 : bool bResult =
1816 0 : oResourceDetailsReq.LoadUrl(osNewResourceUrl, GetHeaders(false));
1817 0 : if (!bResult)
1818 : {
1819 0 : failureReason = CPLGetLastErrorMsg();
1820 0 : return false;
1821 : }
1822 :
1823 0 : OGRNGWCodedFieldDomain oDomain(oResourceDetailsReq.GetRoot());
1824 0 : if (oDomain.GetID() == 0)
1825 : {
1826 0 : failureReason = "Failed to parse domain detailes from NGW";
1827 0 : return false;
1828 : }
1829 0 : moDomains[oDomain.GetID()] = oDomain;
1830 0 : return true;
1831 : }
1832 :
1833 : /*
1834 : * GetDomainByID()
1835 : */
1836 0 : OGRNGWCodedFieldDomain OGRNGWDataset::GetDomainByID(GIntBig id) const
1837 : {
1838 0 : auto pos = moDomains.find(id);
1839 0 : if (pos == moDomains.end())
1840 : {
1841 0 : return OGRNGWCodedFieldDomain();
1842 : }
1843 : else
1844 : {
1845 0 : return pos->second;
1846 : }
1847 : }
1848 :
1849 : /*
1850 : * GetDomainIdByName()
1851 : */
1852 0 : GIntBig OGRNGWDataset::GetDomainIdByName(const std::string &osDomainName) const
1853 : {
1854 0 : for (auto oDom : moDomains)
1855 : {
1856 0 : if (oDom.second.HasDomainName(osDomainName))
1857 : {
1858 0 : return oDom.first;
1859 : }
1860 : }
1861 0 : return 0L;
1862 : }
|