• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/ogr/ogrsf_frmts/ngw/gdalngwdataset.cpp
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 <array>
20
#include <limits>
21

22
class NGWWrapperRasterBand : public GDALProxyRasterBand
23
{
24
    GDALRasterBand *poBaseBand;
25

26
  protected:
27
    virtual GDALRasterBand *
28
    RefUnderlyingRasterBand(bool /*bForceOpen*/) const override;
29

30
  public:
31
    explicit NGWWrapperRasterBand(GDALRasterBand *poBaseBandIn)
×
32
        : poBaseBand(poBaseBandIn)
×
33
    {
34
        eDataType = poBaseBand->GetRasterDataType();
×
35
        poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
×
36
    }
×
37
};
38

39
GDALRasterBand *
40
NGWWrapperRasterBand::RefUnderlyingRasterBand(bool /*bForceOpen*/) const
×
41
{
42
    return poBaseBand;
×
43
}
44

45
static const char *FormGDALTMSConnectionString(const std::string &osUrl,
×
46
                                               const std::string &osResourceId,
47
                                               int nEPSG, int nCacheExpires,
48
                                               int nCacheMaxSize)
49
{
50
    std::string osRasterUrl = NGWAPI::GetTMSURL(osUrl, osResourceId);
×
51
    char *pszRasterUrl = CPLEscapeString(osRasterUrl.c_str(), -1, CPLES_XML);
×
52
    const char *pszConnStr =
53
        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
    CPLFree(pszRasterUrl);
×
75
    return pszConnStr;
×
76
}
77

78
static std::string GetStylesIdentifiers(const CPLJSONArray &aoStyles, int nDeep)
×
79
{
80
    std::string sOut;
×
81
    if (nDeep > 255)
×
82
    {
83
        return sOut;
×
84
    }
85

86
    for (const auto &subobj : aoStyles)
×
87
    {
88
        auto sType = subobj.GetString("item_type");
×
89
        if (sType == "layer")
×
90
        {
91
            auto sId = subobj.GetString("layer_style_id");
×
92
            if (!sId.empty())
×
93
            {
94
                if (sOut.empty())
×
95
                {
96
                    sOut = std::move(sId);
×
97
                }
98
                else
99
                {
100
                    sOut += "," + sId;
×
101
                }
102
            }
103
        }
104
        else
105
        {
106
            auto aoChildren = subobj.GetArray("children");
×
107
            auto sId = GetStylesIdentifiers(aoChildren, nDeep + 1);
×
108
            if (!sId.empty())
×
109
            {
110
                if (sOut.empty())
×
111
                {
112
                    sOut = std::move(sId);
×
113
                }
114
                else
115
                {
116
                    sOut += "," + sId;
×
117
                }
118
            }
119
        }
120
    }
121
    return sOut;
×
122
}
123

124
/*
125
 * OGRNGWDataset()
126
 */
127
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
      osJsonDepth("32")
×
133
{
134
}
×
135

136
/*
137
 * ~OGRNGWDataset()
138
 */
139
OGRNGWDataset::~OGRNGWDataset()
×
140
{
141
    // Last sync with server.
142
    OGRNGWDataset::FlushCache(true);
×
143

144
    if (poRasterDS != nullptr)
×
145
    {
146
        GDALClose(poRasterDS);
×
147
        poRasterDS = nullptr;
×
148
    }
149
}
×
150

151
/*
152
 * FetchPermissions()
153
 */
154
void OGRNGWDataset::FetchPermissions()
×
155
{
156
    if (bFetchedPermissions)
×
157
    {
158
        return;
×
159
    }
160

161
    if (IsUpdateMode())
×
162
    {
163
        // Check connection and is it read only.
164
        stPermissions = NGWAPI::CheckPermissions(
165
            osUrl, osResourceId, GetHeaders(false), IsUpdateMode());
×
166
    }
167
    else
168
    {
169
        stPermissions.bDataCanRead = true;
×
170
        stPermissions.bResourceCanRead = true;
×
171
        stPermissions.bDatastructCanRead = true;
×
172
        stPermissions.bMetadataCanRead = true;
×
173
    }
174
    bFetchedPermissions = true;
×
175
}
176

177
/*
178
 * TestCapability()
179
 */
180
int OGRNGWDataset::TestCapability(const char *pszCap)
×
181
{
182
    FetchPermissions();
×
183
    if (EQUAL(pszCap, ODsCCreateLayer))
×
184
    {
185
        return stPermissions.bResourceCanCreate;
×
186
    }
187
    else if (EQUAL(pszCap, ODsCDeleteLayer))
×
188
    {
189
        return stPermissions.bResourceCanDelete;
×
190
    }
191
    else if (EQUAL(pszCap, "RenameLayer"))
×
192
    {
193
        return stPermissions.bResourceCanUpdate;
×
194
    }
195
    else if (EQUAL(pszCap, ODsCRandomLayerWrite))
×
196
    {
197
        return stPermissions.bDataCanWrite;  // FIXME: Check on resource level
×
198
                                             // is this permission set?
199
    }
200
    else if (EQUAL(pszCap, ODsCRandomLayerRead))
×
201
    {
202
        return stPermissions.bDataCanRead;
×
203
    }
204
    else if (EQUAL(pszCap, ODsCZGeometries))
×
205
    {
206
        return TRUE;
×
207
    }
208
    else if (EQUAL(pszCap, ODsCAddFieldDomain))
×
209
    {
210
        return stPermissions.bResourceCanCreate;
×
211
    }
212
    else if (EQUAL(pszCap, ODsCDeleteFieldDomain))
×
213
    {
214
        return stPermissions.bResourceCanDelete;
×
215
    }
216
    else if (EQUAL(pszCap, ODsCUpdateFieldDomain))
×
217
    {
218
        return stPermissions.bResourceCanUpdate;
×
219
    }
220
    else
221
    {
222
        return FALSE;
×
223
    }
224
}
225

226
/*
227
 * GetLayer()
228
 */
229
OGRLayer *OGRNGWDataset::GetLayer(int iLayer)
×
230
{
231
    if (iLayer < 0 || iLayer >= GetLayerCount())
×
232
    {
233
        return nullptr;
×
234
    }
235
    else
236
    {
237
        return aoLayers[iLayer].get();
×
238
    }
239
}
240

241
/*
242
 * Open()
243
 */
244
bool OGRNGWDataset::Open(const std::string &osUrlIn,
×
245
                         const std::string &osResourceIdIn,
246
                         char **papszOpenOptionsIn, bool bUpdateIn,
247
                         int nOpenFlagsIn)
248
{
249
    osUrl = osUrlIn;
×
250
    osResourceId = osResourceIdIn;
×
251

252
    eAccess = bUpdateIn ? GA_Update : GA_ReadOnly;
×
253

254
    osUserPwd = CSLFetchNameValueDef(papszOpenOptionsIn, "USERPWD",
255
                                     CPLGetConfigOption("NGW_USERPWD", ""));
×
256

257
    nBatchSize =
×
258
        atoi(CSLFetchNameValueDef(papszOpenOptionsIn, "BATCH_SIZE",
×
259
                                  CPLGetConfigOption("NGW_BATCH_SIZE", "-1")));
260

261
    nPageSize =
×
262
        atoi(CSLFetchNameValueDef(papszOpenOptionsIn, "PAGE_SIZE",
×
263
                                  CPLGetConfigOption("NGW_PAGE_SIZE", "-1")));
264
    if (nPageSize == 0)
×
265
    {
266
        nPageSize = -1;
×
267
    }
268

269
    nCacheExpires = atoi(CSLFetchNameValueDef(
×
270
        papszOpenOptionsIn, "CACHE_EXPIRES",
271
        CPLGetConfigOption("NGW_CACHE_EXPIRES", "604800")));
272

273
    nCacheMaxSize = atoi(CSLFetchNameValueDef(
×
274
        papszOpenOptionsIn, "CACHE_MAX_SIZE",
275
        CPLGetConfigOption("NGW_CACHE_MAX_SIZE", "67108864")));
276

277
    bExtInNativeData =
×
278
        CPLFetchBool(papszOpenOptionsIn, "NATIVE_DATA",
×
279
                     CPLTestBool(CPLGetConfigOption("NGW_NATIVE_DATA", "NO")));
×
280

281
    osJsonDepth =
282
        CSLFetchNameValueDef(papszOpenOptionsIn, "JSON_DEPTH",
283
                             CPLGetConfigOption("NGW_JSON_DEPTH", "32"));
×
284

285
    osExtensions =
286
        CSLFetchNameValueDef(papszOpenOptionsIn, "EXTENSIONS",
287
                             CPLGetConfigOption("NGW_EXTENSIONS", ""));
×
288

289
    osConnectTimeout =
290
        CSLFetchNameValueDef(papszOpenOptionsIn, "CONNECTTIMEOUT",
291
                             CPLGetConfigOption("NGW_CONNECTTIMEOUT", ""));
×
292
    osTimeout = CSLFetchNameValueDef(papszOpenOptionsIn, "TIMEOUT",
293
                                     CPLGetConfigOption("NGW_TIMEOUT", ""));
×
294
    osRetryCount =
295
        CSLFetchNameValueDef(papszOpenOptionsIn, "MAX_RETRY",
296
                             CPLGetConfigOption("NGW_MAX_RETRY", ""));
×
297
    osRetryDelay =
298
        CSLFetchNameValueDef(papszOpenOptionsIn, "RETRY_DELAY",
299
                             CPLGetConfigOption("NGW_RETRY_DELAY", ""));
×
300

301
    if (osExtensions.empty())
×
302
    {
303
        bExtInNativeData = false;
×
304
    }
305

306
    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
    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
bool OGRNGWDataset::Open(const char *pszFilename, char **papszOpenOptionsIn,
×
334
                         bool bUpdateIn, int nOpenFlagsIn)
335
{
336
    NGWAPI::Uri stUri = NGWAPI::ParseUri(pszFilename);
×
337

338
    if (stUri.osPrefix != "NGW")
×
339
    {
340
        CPLError(CE_Failure, CPLE_NotSupported, "Unsupported name %s",
×
341
                 pszFilename);
342
        return false;
×
343
    }
344

345
    osUrl = stUri.osAddress;
×
346
    osResourceId = stUri.osResourceId;
×
347

348
    return Open(stUri.osAddress, stUri.osResourceId, papszOpenOptionsIn,
×
349
                bUpdateIn, nOpenFlagsIn);
×
350
}
351

352
/*
353
 * SetupRasterDSWrapper()
354
 */
355
void OGRNGWDataset::SetupRasterDSWrapper(const OGREnvelope &stExtent)
×
356
{
357
    if (poRasterDS)
×
358
    {
359
        nRasterXSize = poRasterDS->GetRasterXSize();
×
360
        nRasterYSize = poRasterDS->GetRasterYSize();
×
361

362
        for (int iBand = 1; iBand <= poRasterDS->GetRasterCount(); iBand++)
×
363
        {
364
            SetBand(iBand,
×
365
                    new NGWWrapperRasterBand(poRasterDS->GetRasterBand(iBand)));
×
366
        }
367

368
        if (stExtent.IsInit())
×
369
        {
370
            // Set pixel limits.
371
            bool bHasTransform = false;
×
372
            GDALGeoTransform gt, invGT;
×
373
            if (poRasterDS->GetGeoTransform(gt) == CE_None)
×
374
            {
375
                bHasTransform = gt.GetInverse(invGT);
×
376
            }
377

378
            if (bHasTransform)
×
379
            {
380
                invGT.Apply(stExtent.MinX, stExtent.MinY, &stPixelExtent.MinX,
×
381
                            &stPixelExtent.MaxY);
382

383
                invGT.Apply(stExtent.MaxX, stExtent.MaxY, &stPixelExtent.MaxX,
×
384
                            &stPixelExtent.MinY);
385

386
                CPLDebug("NGW", "Raster extent in px is: %f, %f, %f, %f",
×
387
                         stPixelExtent.MinX, stPixelExtent.MinY,
388
                         stPixelExtent.MaxX, stPixelExtent.MaxY);
389
            }
390
            else
391
            {
392
                stPixelExtent.MinX = 0.0;
×
393
                stPixelExtent.MinY = 0.0;
×
394
                stPixelExtent.MaxX = std::numeric_limits<double>::max();
×
395
                stPixelExtent.MaxY = std::numeric_limits<double>::max();
×
396
            }
397
        }
398
    }
399
}
×
400

401
/*
402
 * Init()
403
 */
404
bool OGRNGWDataset::Init(int nOpenFlagsIn)
×
405
{
406
    // NOTE: Skip check API version at that moment. We expected API v3 or higher.
407

408
    // Get resource details.
409
    CPLJSONDocument oResourceDetailsReq;
×
410
    auto aosHTTPOptions = GetHeaders(false);
×
411
    bool bResult = oResourceDetailsReq.LoadUrl(
×
412
        NGWAPI::GetResourceURL(osUrl, osResourceId), aosHTTPOptions);
×
413

414
    CPLDebug("NGW", "Get resource %s details %s", osResourceId.c_str(),
×
415
             bResult ? "success" : "failed");
416

417
    if (bResult)
×
418
    {
419
        CPLJSONObject oRoot = oResourceDetailsReq.GetRoot();
×
420

421
        if (oRoot.IsValid())
×
422
        {
423
            auto osResourceType = oRoot.GetString("resource/cls");
×
424
            FillMetadata(oRoot);
×
425

426
            if (osResourceType == "resource_group")
×
427
            {
428
                // Check feature paging.
429
                FillCapabilities(aosHTTPOptions);
×
430
                if (oRoot.GetBool("resource/children", false))
×
431
                {
432
                    // Get child resources.
433
                    bResult = FillResources(aosHTTPOptions, nOpenFlagsIn);
×
434
                }
435
            }
436
            else if (NGWAPI::CheckSupportedType(false, osResourceType))
×
437
            {
438
                // Check feature paging.
439
                FillCapabilities(aosHTTPOptions);
×
440
                // Add vector layer.
441
                AddLayer(oRoot, aosHTTPOptions, nOpenFlagsIn);
×
442
            }
443
            else if (osResourceType == "mapserver_style" ||
×
444
                     osResourceType == "qgis_vector_style" ||
×
445
                     osResourceType == "raster_style" ||
×
446
                     osResourceType == "qgis_raster_style")
×
447
            {
448
                // GetExtent from parent.
449
                OGREnvelope stExtent;
×
450
                std::string osParentId = oRoot.GetString("resource/parent/id");
×
451
                bool bExtentResult = NGWAPI::GetExtent(
×
452
                    osUrl, osParentId, aosHTTPOptions, 3857, stExtent);
×
453

454
                if (!bExtentResult)
×
455
                {
456
                    // Set full extent for EPSG:3857.
457
                    stExtent.MinX = -20037508.34;
×
458
                    stExtent.MaxX = 20037508.34;
×
459
                    stExtent.MinY = -20037508.34;
×
460
                    stExtent.MaxY = 20037508.34;
×
461
                }
462

463
                CPLDebug("NGW", "Raster extent is: %f, %f, %f, %f",
×
464
                         stExtent.MinX, stExtent.MinY, stExtent.MaxX,
465
                         stExtent.MaxY);
466

467
                int nEPSG = 3857;
×
468
                // NOTE: Get parent details. We can skip this as default SRS in
469
                // NGW is 3857.
470
                CPLJSONDocument oResourceReq;
×
471
                bResult = oResourceReq.LoadUrl(
×
472
                    NGWAPI::GetResourceURL(osUrl, osResourceId),
×
473
                    aosHTTPOptions);
×
474

475
                if (bResult)
×
476
                {
477
                    CPLJSONObject oParentRoot = oResourceReq.GetRoot();
×
478
                    if (osResourceType == "mapserver_style" ||
×
479
                        osResourceType == "qgis_vector_style")
×
480
                    {
481
                        nEPSG = oParentRoot.GetInteger("vector_layer/srs/id",
×
482
                                                       nEPSG);
483
                    }
484
                    else if (osResourceType == "raster_style" ||
×
485
                             osResourceType == "qgis_raster_style")
×
486
                    {
487
                        nEPSG = oParentRoot.GetInteger("raster_layer/srs/id",
×
488
                                                       nEPSG);
489
                    }
490
                }
491

492
                const char *pszConnStr = FormGDALTMSConnectionString(
×
493
                    osUrl, osResourceId, nEPSG, nCacheExpires, nCacheMaxSize);
×
494
                CPLDebug("NGW", "Open %s as '%s'", osResourceType.c_str(),
×
495
                         pszConnStr);
496
                poRasterDS = GDALDataset::FromHandle(GDALOpenEx(
×
497
                    pszConnStr,
498
                    GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL,
499
                    nullptr, nullptr, nullptr));
500
                SetupRasterDSWrapper(stExtent);
×
501
            }
502
            else if (osResourceType == "wmsclient_layer")
×
503
            {
504
                OGREnvelope stExtent;
×
505
                // Set full extent for EPSG:3857.
506
                stExtent.MinX = -20037508.34;
×
507
                stExtent.MaxX = 20037508.34;
×
508
                stExtent.MinY = -20037508.34;
×
509
                stExtent.MaxY = 20037508.34;
×
510

511
                CPLDebug("NGW", "Raster extent is: %f, %f, %f, %f",
×
512
                         stExtent.MinX, stExtent.MinY, stExtent.MaxX,
513
                         stExtent.MaxY);
514

515
                int nEPSG = oRoot.GetInteger("wmsclient_layer/srs/id", 3857);
×
516

517
                const char *pszConnStr = FormGDALTMSConnectionString(
×
518
                    osUrl, osResourceId, nEPSG, nCacheExpires, nCacheMaxSize);
×
519
                poRasterDS = GDALDataset::FromHandle(GDALOpenEx(
×
520
                    pszConnStr,
521
                    GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL,
522
                    nullptr, nullptr, nullptr));
523
                SetupRasterDSWrapper(stExtent);
×
524
            }
525
            else if (osResourceType == "basemap_layer")
×
526
            {
527
                auto osTMSURL = oRoot.GetString("basemap_layer/url");
×
528
                int nEPSG = 3857;
×
529
                auto osQMS = oRoot.GetString("basemap_layer/qms");
×
530
                if (!osQMS.empty())
×
531
                {
532
                    CPLJSONDocument oDoc;
×
533
                    if (oDoc.LoadMemory(osQMS))
×
534
                    {
535
                        auto oQMLRoot = oDoc.GetRoot();
×
536
                        nEPSG = oQMLRoot.GetInteger("epsg");
×
537
                    }
538
                }
539

540
                // TODO: for EPSG != 3857 need to calc full extent
541
                if (nEPSG != 3857)
×
542
                {
543
                    bResult = false;
×
544
                }
545
                else
546
                {
547
                    OGREnvelope stExtent;
×
548
                    // Set full extent for EPSG:3857.
549
                    stExtent.MinX = -20037508.34;
×
550
                    stExtent.MaxX = 20037508.34;
×
551
                    stExtent.MinY = -20037508.34;
×
552
                    stExtent.MaxY = 20037508.34;
×
553

554
                    const char *pszConnStr = FormGDALTMSConnectionString(
×
555
                        osTMSURL, osResourceId, nEPSG, nCacheExpires,
×
556
                        nCacheMaxSize);
557
                    poRasterDS = GDALDataset::FromHandle(GDALOpenEx(
×
558
                        pszConnStr,
559
                        GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL,
560
                        nullptr, nullptr, nullptr));
561
                    SetupRasterDSWrapper(stExtent);
×
562
                }
563
            }
564
            else if (osResourceType == "webmap")
×
565
            {
566
                OGREnvelope stExtent;
×
567
                // Set full extent for EPSG:3857.
568
                stExtent.MinX = -20037508.34;
×
569
                stExtent.MaxX = 20037508.34;
×
570
                stExtent.MinY = -20037508.34;
×
571
                stExtent.MaxY = 20037508.34;
×
572

573
                // Get all styles
574
                auto aoChildren = oRoot.GetArray("webmap/children");
×
575
                auto sIdentifiers = GetStylesIdentifiers(aoChildren, 0);
×
576

577
                const char *pszConnStr = FormGDALTMSConnectionString(
×
578
                    osUrl, sIdentifiers, 3857, nCacheExpires, nCacheMaxSize);
×
579
                poRasterDS = GDALDataset::FromHandle(GDALOpenEx(
×
580
                    pszConnStr,
581
                    GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL,
582
                    nullptr, nullptr, nullptr));
583
                SetupRasterDSWrapper(stExtent);
×
584
            }
585
            else if (osResourceType == "raster_layer")
×
586
            {
587
                auto osCogURL = NGWAPI::GetCOGURL(osUrl, osResourceId);
×
588
                auto osConnStr = std::string("/vsicurl/") + osCogURL;
×
589

590
                CPLDebug("NGW", "Raster url is: %s", osConnStr.c_str());
×
591

592
                poRasterDS = GDALDataset::FromHandle(GDALOpenEx(
×
593
                    osConnStr.c_str(),
594
                    GDAL_OF_READONLY | GDAL_OF_RASTER | GDAL_OF_INTERNAL,
595
                    nullptr, nullptr, nullptr));
596

597
                // Add styles if exists
598
                auto osRasterResourceId = oRoot.GetString("resource/id");
×
599
                CPLJSONDocument oResourceRequest;
×
600
                bool bLoadResult = oResourceRequest.LoadUrl(
×
601
                    NGWAPI::GetChildrenURL(osUrl, osRasterResourceId),
×
602
                    aosHTTPOptions);
×
603
                if (bLoadResult)
×
604
                {
605
                    CPLJSONArray oChildren(oResourceRequest.GetRoot());
×
606
                    for (const auto &oChild : oChildren)
×
607
                    {
608
                        AddRaster(oChild);
×
609
                    }
610
                }
611

612
                SetupRasterDSWrapper(OGREnvelope());
×
613
            }
614
            else
615
            {
616
                bResult = false;
×
617
            }
618

619
            // TODO: Add support for wfsserver_service, wmsserver_service,
620
            // raster_mosaic, tileset.
621
        }
622
    }
623

624
    return bResult;
×
625
}
626

627
/*
628
 * FillResources()
629
 */
630
bool OGRNGWDataset::FillResources(const CPLStringList &aosHTTPOptions,
×
631
                                  int nOpenFlagsIn)
632
{
633
    CPLJSONDocument oResourceDetailsReq;
×
634
    // Fill domains
635
    bool bResult = oResourceDetailsReq.LoadUrl(
×
636
        NGWAPI::GetSearchURL(osUrl, "cls", "lookup_table"), aosHTTPOptions);
×
637
    if (bResult)
×
638
    {
639
        CPLJSONArray oChildren(oResourceDetailsReq.GetRoot());
×
640
        for (const auto &oChild : oChildren)
×
641
        {
642
            OGRNGWCodedFieldDomain oDomain(oChild);
×
643
            if (oDomain.GetID() > 0)
×
644
            {
645
                moDomains[oDomain.GetID()] = std::move(oDomain);
×
646
            }
647
        }
648
    }
649

650
    // Fill child resources
651
    bResult = oResourceDetailsReq.LoadUrl(
×
652
        NGWAPI::GetChildrenURL(osUrl, osResourceId), aosHTTPOptions);
×
653

654
    if (bResult)
×
655
    {
656
        CPLJSONArray oChildren(oResourceDetailsReq.GetRoot());
×
657
        for (const auto &oChild : oChildren)
×
658
        {
659
            if (nOpenFlagsIn & GDAL_OF_VECTOR)
×
660
            {
661
                // Add vector layer. If failed, try next layer.
662
                AddLayer(oChild, aosHTTPOptions, nOpenFlagsIn);
×
663
            }
664

665
            if (nOpenFlagsIn & GDAL_OF_RASTER)
×
666
            {
667
                AddRaster(oChild);
×
668
            }
669
        }
670
    }
671
    return bResult;
×
672
}
673

674
/*
675
 * AddLayer()
676
 */
677
void OGRNGWDataset::AddLayer(const CPLJSONObject &oResourceJsonObject,
×
678
                             const CPLStringList &aosHTTPOptions,
679
                             int nOpenFlagsIn)
680
{
681
    auto osResourceType = oResourceJsonObject.GetString("resource/cls");
×
682
    if (!NGWAPI::CheckSupportedType(false, osResourceType))
×
683
    {
684
        // NOTE: Only vector_layer and postgis_layer types now supported
685
        return;
×
686
    }
687

688
    auto osLayerResourceId = oResourceJsonObject.GetString("resource/id");
×
689
    if (nOpenFlagsIn & GDAL_OF_VECTOR)
×
690
    {
691
        OGRNGWLayerPtr poLayer(new OGRNGWLayer(this, oResourceJsonObject));
×
692
        aoLayers.emplace_back(poLayer);
×
693
        osLayerResourceId = poLayer->GetResourceId();
×
694
    }
695

696
    // Check styles exist and add them as rasters.
697
    if (nOpenFlagsIn & GDAL_OF_RASTER &&
×
698
        oResourceJsonObject.GetBool("resource/children", false))
×
699
    {
700
        CPLJSONDocument oResourceChildReq;
×
701
        bool bResult = oResourceChildReq.LoadUrl(
×
702
            NGWAPI::GetChildrenURL(osUrl, osLayerResourceId), aosHTTPOptions);
×
703

704
        if (bResult)
×
705
        {
706
            CPLJSONArray oChildren(oResourceChildReq.GetRoot());
×
707
            for (const auto &oChild : oChildren)
×
708
            {
709
                AddRaster(oChild);
×
710
            }
711
        }
712
    }
713
}
714

715
/*
716
 * AddRaster()
717
 */
718
void OGRNGWDataset::AddRaster(const CPLJSONObject &oRasterJsonObj)
×
719
{
720
    auto osResourceType = oRasterJsonObj.GetString("resource/cls");
×
721
    if (!NGWAPI::CheckSupportedType(true, osResourceType))
×
722
    {
723
        return;
×
724
    }
725

726
    auto osOutResourceId = oRasterJsonObj.GetString("resource/id");
×
727
    auto osOutResourceName = oRasterJsonObj.GetString("resource/display_name");
×
728

729
    if (osOutResourceName.empty())
×
730
    {
731
        osOutResourceName = "raster_" + osOutResourceId;
×
732
    }
733

734
    CPLDebug("NGW", "Add raster %s: %s", osOutResourceId.c_str(),
×
735
             osOutResourceName.c_str());
736

737
    GDALDataset::SetMetadataItem(CPLSPrintf("SUBDATASET_%d_NAME", nRasters + 1),
×
738
                                 CPLSPrintf("NGW:%s/resource/%s", osUrl.c_str(),
739
                                            osOutResourceId.c_str()),
740
                                 "SUBDATASETS");
741
    GDALDataset::SetMetadataItem(CPLSPrintf("SUBDATASET_%d_DESC", nRasters + 1),
×
742
                                 CPLSPrintf("%s (%s)",
743
                                            osOutResourceName.c_str(),
744
                                            osResourceType.c_str()),
745
                                 "SUBDATASETS");
746
    nRasters++;
×
747
}
748

749
/*
750
 * ICreateLayer
751
 */
752
OGRLayer *OGRNGWDataset::ICreateLayer(const char *pszNameIn,
×
753
                                      const OGRGeomFieldDefn *poGeomFieldDefn,
754
                                      CSLConstList papszOptions)
755
{
756
    if (!IsUpdateMode())
×
757
    {
758
        CPLError(CE_Failure, CPLE_AppDefined,
×
759
                 "Operation not available in read-only mode");
760
        return nullptr;
×
761
    }
762

763
    const auto eGType = poGeomFieldDefn ? poGeomFieldDefn->GetType() : wkbNone;
×
764
    const auto poSpatialRef =
765
        poGeomFieldDefn ? poGeomFieldDefn->GetSpatialRef() : nullptr;
×
766

767
    // Check permissions as we create new layer in memory and will create in
768
    // during SyncToDisk.
769
    FetchPermissions();
×
770

771
    if (!stPermissions.bResourceCanCreate)
×
772
    {
773
        CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
×
774
        return nullptr;
×
775
    }
776

777
    // Check input parameters.
778
    if ((eGType < wkbPoint || eGType > wkbMultiPolygon) &&
×
779
        (eGType < wkbPoint25D || eGType > wkbMultiPolygon25D))
×
780
    {
781
        CPLError(CE_Failure, CPLE_AppDefined, "Unsupported geometry type: %s",
×
782
                 OGRGeometryTypeToName(eGType));
783
        return nullptr;
×
784
    }
785

786
    if (!poSpatialRef)
×
787
    {
788
        CPLError(CE_Failure, CPLE_AppDefined, "Undefined spatial reference");
×
789
        return nullptr;
×
790
    }
791

792
    OGRSpatialReference *poSRSClone = poSpatialRef->Clone();
×
793
    poSRSClone->AutoIdentifyEPSG();
×
794
    const char *pszEPSG = poSRSClone->GetAuthorityCode(nullptr);
×
795
    int nEPSG = -1;
×
796
    if (pszEPSG != nullptr)
×
797
    {
798
        nEPSG = atoi(pszEPSG);
×
799
    }
800

801
    if (nEPSG != 3857)  // TODO: Check NextGIS Web supported SRS.
×
802
    {
803
        CPLError(CE_Failure, CPLE_AppDefined,
×
804
                 "Unsupported spatial reference EPSG code: %d", nEPSG);
805
        poSRSClone->Release();
×
806
        return nullptr;
×
807
    }
808

809
    // Do we already have this layer?  If so, should we blow it away?
810
    bool bOverwrite = CPLFetchBool(papszOptions, "OVERWRITE", false);
×
811
    for (int iLayer = 0; iLayer < GetLayerCount(); ++iLayer)
×
812
    {
813
        if (EQUAL(pszNameIn, aoLayers[iLayer]->GetName()))
×
814
        {
815
            if (bOverwrite)
×
816
            {
817
                DeleteLayer(iLayer);
×
818
                break;
×
819
            }
820
            else
821
            {
822
                CPLError(CE_Failure, CPLE_AppDefined,
×
823
                         "Layer %s already exists, CreateLayer failed.\n"
824
                         "Use the layer creation option OVERWRITE=YES to "
825
                         "replace it.",
826
                         pszNameIn);
827
                poSRSClone->Release();
×
828
                return nullptr;
×
829
            }
830
        }
831
    }
832

833
    // Create layer.
834
    std::string osKey = CSLFetchNameValueDef(papszOptions, "KEY", "");
×
835
    std::string osDesc = CSLFetchNameValueDef(papszOptions, "DESCRIPTION", "");
×
836
    poSRSClone->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
×
837
    OGRNGWLayerPtr poLayer(
838
        new OGRNGWLayer(this, pszNameIn, poSRSClone, eGType, osKey, osDesc));
×
839
    poSRSClone->Release();
×
840
    aoLayers.emplace_back(poLayer);
×
841
    return poLayer.get();
×
842
}
843

844
/*
845
 * DeleteLayer()
846
 */
847
OGRErr OGRNGWDataset::DeleteLayer(int iLayer)
×
848
{
849
    if (!IsUpdateMode())
×
850
    {
851
        CPLError(CE_Failure, CPLE_AppDefined,
×
852
                 "Operation not available in read-only mode.");
853
        return OGRERR_FAILURE;
×
854
    }
855

856
    if (iLayer < 0 || iLayer >= GetLayerCount())
×
857
    {
858
        CPLError(CE_Failure, CPLE_AppDefined,
×
859
                 "Layer %d not in legal range of 0 to %d.", iLayer,
860
                 GetLayerCount() - 1);
×
861
        return OGRERR_FAILURE;
×
862
    }
863

864
    auto poLayer = aoLayers[iLayer];
×
865
    if (poLayer->GetResourceId() != "-1")
×
866
    {
867
        // For layers from server we can check permissions.
868

869
        // We can skip check permissions here as papoLayers[iLayer]->Delete()
870
        // will return false if no delete permission available.
871
        FetchPermissions();
×
872

873
        if (!stPermissions.bResourceCanDelete)
×
874
        {
875
            CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
×
876
            return OGRERR_FAILURE;
×
877
        }
878
    }
879

880
    if (poLayer->Delete())
×
881
    {
882
        aoLayers.erase(aoLayers.begin() + iLayer);
×
883
    }
884

885
    return OGRERR_NONE;
×
886
}
887

888
/*
889
 * FillMetadata()
890
 */
891
void OGRNGWDataset::FillMetadata(const CPLJSONObject &oRootObject)
×
892
{
893
    std::string osCreateDate = oRootObject.GetString("resource/creation_date");
×
894
    if (!osCreateDate.empty())
×
895
    {
896
        GDALDataset::SetMetadataItem("creation_date", osCreateDate.c_str());
×
897
    }
898
    osName = oRootObject.GetString("resource/display_name");
×
899
    SetDescription(osName.c_str());
×
900
    GDALDataset::SetMetadataItem("display_name", osName.c_str());
×
901
    std::string osDescription = oRootObject.GetString("resource/description");
×
902
    if (!osDescription.empty())
×
903
    {
904
        GDALDataset::SetMetadataItem("description", osDescription.c_str());
×
905
    }
906
    std::string osResourceType = oRootObject.GetString("resource/cls");
×
907
    if (!osResourceType.empty())
×
908
    {
909
        GDALDataset::SetMetadataItem("resource_type", osResourceType.c_str());
×
910
    }
911
    std::string osResourceParentId =
912
        oRootObject.GetString("resource/parent/id");
×
913
    if (!osResourceParentId.empty())
×
914
    {
915
        GDALDataset::SetMetadataItem("parent_id", osResourceParentId.c_str());
×
916
    }
917
    GDALDataset::SetMetadataItem("id", osResourceId.c_str());
×
918

919
    std::vector<CPLJSONObject> items =
920
        oRootObject.GetObj("resmeta/items").GetChildren();
×
921

922
    for (const CPLJSONObject &item : items)
×
923
    {
924
        std::string osSuffix = NGWAPI::GetResmetaSuffix(item.GetType());
×
925
        GDALDataset::SetMetadataItem((item.GetName() + osSuffix).c_str(),
×
926
                                     item.ToString().c_str(), "NGW");
×
927
    }
928
}
×
929

930
/*
931
 * FlushMetadata()
932
 */
933
bool OGRNGWDataset::FlushMetadata(char **papszMetadata)
×
934
{
935
    if (!bMetadataDerty)
×
936
    {
937
        return true;
×
938
    }
939

940
    bool bResult = NGWAPI::FlushMetadata(osUrl, osResourceId, papszMetadata,
×
941
                                         GetHeaders(false));
×
942
    if (bResult)
×
943
    {
944
        bMetadataDerty = false;
×
945
    }
946

947
    return bResult;
×
948
}
949

950
/*
951
 * SetMetadata()
952
 */
953
CPLErr OGRNGWDataset::SetMetadata(char **papszMetadata, const char *pszDomain)
×
954
{
955
    FetchPermissions();
×
956
    if (!stPermissions.bMetadataCanWrite)
×
957
    {
958
        CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
×
959
        return CE_Failure;
×
960
    }
961

962
    CPLErr eResult = GDALDataset::SetMetadata(papszMetadata, pszDomain);
×
963
    if (eResult == CE_None && pszDomain != nullptr && EQUAL(pszDomain, "NGW"))
×
964
    {
965
        eResult = FlushMetadata(papszMetadata) ? CE_None : CE_Failure;
×
966
    }
967
    return eResult;
×
968
}
969

970
/*
971
 * SetMetadataItem()
972
 */
973
CPLErr OGRNGWDataset::SetMetadataItem(const char *pszName, const char *pszValue,
×
974
                                      const char *pszDomain)
975
{
976
    FetchPermissions();
×
977
    if (!stPermissions.bMetadataCanWrite)
×
978
    {
979
        CPLError(CE_Failure, CPLE_AppDefined, "Operation not permitted.");
×
980
        return CE_Failure;
×
981
    }
982
    if (pszDomain != nullptr && EQUAL(pszDomain, "NGW"))
×
983
    {
984
        bMetadataDerty = true;
×
985
    }
986
    return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
×
987
}
988

989
/*
990
 * FlushCache()
991
 */
992
CPLErr OGRNGWDataset::FlushCache(bool bAtClosing)
×
993
{
994
    CPLErr eErr = GDALDataset::FlushCache(bAtClosing);
×
995
    if (!FlushMetadata(GetMetadata("NGW")))
×
996
        eErr = CE_Failure;
×
997
    return eErr;
×
998
}
999

1000
/*
1001
 * GetHeaders()
1002
 */
1003
CPLStringList OGRNGWDataset::GetHeaders(bool bSkipRetry) const
×
1004
{
1005
    CPLStringList aosOptions;
×
1006
    aosOptions.AddNameValue("HEADERS", "Accept: */*");
×
1007
    aosOptions.AddNameValue("JSON_DEPTH", osJsonDepth.c_str());
×
1008
    if (!osUserPwd.empty())
×
1009
    {
1010
        aosOptions.AddNameValue("HTTPAUTH", "BASIC");
×
1011
        aosOptions.AddNameValue("USERPWD", osUserPwd.c_str());
×
1012
    }
1013

1014
    if (!osConnectTimeout.empty())
×
1015
    {
1016
        aosOptions.AddNameValue("CONNECTTIMEOUT", osConnectTimeout.c_str());
×
1017
    }
1018

1019
    if (!osTimeout.empty())
×
1020
    {
1021
        aosOptions.AddNameValue("TIMEOUT", osTimeout.c_str());
×
1022
    }
1023

1024
    if (!bSkipRetry)
×
1025
    {
1026
        if (!osRetryCount.empty())
×
1027
        {
1028
            aosOptions.AddNameValue("MAX_RETRY", osRetryCount.c_str());
×
1029
        }
1030
        if (!osRetryDelay.empty())
×
1031
        {
1032
            aosOptions.AddNameValue("RETRY_DELAY", osRetryDelay.c_str());
×
1033
        }
1034
    }
1035
    return aosOptions;
×
1036
}
1037

1038
/*
1039
 * SQLUnescape()
1040
 * Get from gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteutility.cpp as we don't want
1041
 * dependency on sqlite
1042
 */
1043
static CPLString SQLUnescape(const char *pszVal)
×
1044
{
1045
    char chQuoteChar = pszVal[0];
×
1046
    if (chQuoteChar != '\'' && chQuoteChar != '"')
×
1047
        return pszVal;
×
1048

1049
    CPLString osRet;
×
1050
    pszVal++;
×
1051
    while (*pszVal != '\0')
×
1052
    {
1053
        if (*pszVal == chQuoteChar)
×
1054
        {
1055
            if (pszVal[1] == chQuoteChar)
×
1056
                pszVal++;
×
1057
            else
1058
                break;
×
1059
        }
1060
        osRet += *pszVal;
×
1061
        pszVal++;
×
1062
    }
1063
    return osRet;
×
1064
}
1065

1066
/*
1067
 * SQLTokenize()
1068
 * Get from gdal/ogr/ogrsf_frmts/sqlite/ogrsqliteutility.cpp as we don't want
1069
 * dependency on sqlite
1070
 */
1071
static char **SQLTokenize(const char *pszStr)
×
1072
{
1073
    char **papszTokens = nullptr;
×
1074
    bool bInQuote = false;
×
1075
    char chQuoteChar = '\0';
×
1076
    bool bInSpace = true;
×
1077
    CPLString osCurrentToken;
×
1078
    while (*pszStr != '\0')
×
1079
    {
1080
        if (*pszStr == ' ' && !bInQuote)
×
1081
        {
1082
            if (!bInSpace)
×
1083
            {
1084
                papszTokens = CSLAddString(papszTokens, osCurrentToken);
×
1085
                osCurrentToken.clear();
×
1086
            }
1087
            bInSpace = true;
×
1088
        }
1089
        else if ((*pszStr == '(' || *pszStr == ')' || *pszStr == ',') &&
×
1090
                 !bInQuote)
×
1091
        {
1092
            if (!bInSpace)
×
1093
            {
1094
                papszTokens = CSLAddString(papszTokens, osCurrentToken);
×
1095
                osCurrentToken.clear();
×
1096
            }
1097
            osCurrentToken.clear();
×
1098
            osCurrentToken += *pszStr;
×
1099
            papszTokens = CSLAddString(papszTokens, osCurrentToken);
×
1100
            osCurrentToken.clear();
×
1101
            bInSpace = true;
×
1102
        }
1103
        else if (*pszStr == '"' || *pszStr == '\'')
×
1104
        {
1105
            if (bInQuote && *pszStr == chQuoteChar && pszStr[1] == chQuoteChar)
×
1106
            {
1107
                osCurrentToken += *pszStr;
×
1108
                osCurrentToken += *pszStr;
×
1109
                pszStr += 2;
×
1110
                continue;
×
1111
            }
1112
            else if (bInQuote && *pszStr == chQuoteChar)
×
1113
            {
1114
                osCurrentToken += *pszStr;
×
1115
                papszTokens = CSLAddString(papszTokens, osCurrentToken);
×
1116
                osCurrentToken.clear();
×
1117
                bInSpace = true;
×
1118
                bInQuote = false;
×
1119
                chQuoteChar = '\0';
×
1120
            }
1121
            else if (bInQuote)
×
1122
            {
1123
                osCurrentToken += *pszStr;
×
1124
            }
1125
            else
1126
            {
1127
                chQuoteChar = *pszStr;
×
1128
                osCurrentToken.clear();
×
1129
                osCurrentToken += chQuoteChar;
×
1130
                bInQuote = true;
×
1131
                bInSpace = false;
×
1132
            }
1133
        }
1134
        else
1135
        {
1136
            osCurrentToken += *pszStr;
×
1137
            bInSpace = false;
×
1138
        }
1139
        pszStr++;
×
1140
    }
1141

1142
    if (!osCurrentToken.empty())
×
1143
        papszTokens = CSLAddString(papszTokens, osCurrentToken);
×
1144

1145
    return papszTokens;
×
1146
}
1147

1148
/*
1149
 * ExecuteSQL()
1150
 */
1151
OGRLayer *OGRNGWDataset::ExecuteSQL(const char *pszStatement,
×
1152
                                    OGRGeometry *poSpatialFilter,
1153
                                    const char *pszDialect)
1154
{
1155
    // Clean statement string.
1156
    CPLString osStatement(pszStatement);
×
1157
    osStatement = osStatement.Trim().replaceAll("  ", " ");
×
1158

1159
    if (STARTS_WITH_CI(osStatement, "DELLAYER:"))
×
1160
    {
1161
        CPLString osLayerName = osStatement.substr(strlen("DELLAYER:"));
×
1162
        if (osLayerName.endsWith(";"))
×
1163
        {
1164
            osLayerName = osLayerName.substr(0, osLayerName.size() - 1);
×
1165
            osLayerName.Trim();
×
1166
        }
1167

1168
        CPLDebug("NGW", "Delete layer with name %s.", osLayerName.c_str());
×
1169

1170
        for (int iLayer = 0; iLayer < GetLayerCount(); ++iLayer)
×
1171
        {
1172
            if (EQUAL(aoLayers[iLayer]->GetName(), osLayerName))
×
1173
            {
1174
                DeleteLayer(iLayer);
×
1175
                return nullptr;
×
1176
            }
1177
        }
1178
        CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
×
1179
                 osLayerName.c_str());
1180

1181
        return nullptr;
×
1182
    }
1183

1184
    if (STARTS_WITH_CI(osStatement, "DELETE FROM"))
×
1185
    {
1186
        osStatement = osStatement.substr(strlen("DELETE FROM "));
×
1187
        if (osStatement.endsWith(";"))
×
1188
        {
1189
            osStatement = osStatement.substr(0, osStatement.size() - 1);
×
1190
            osStatement.Trim();
×
1191
        }
1192

1193
        std::size_t found = osStatement.find("WHERE");
×
1194
        CPLString osLayerName;
×
1195
        if (found == std::string::npos)
×
1196
        {  // No where clause
1197
            osLayerName = osStatement;
×
1198
            osStatement.clear();
×
1199
        }
1200
        else
1201
        {
1202
            osLayerName = osStatement.substr(0, found);
×
1203
            osLayerName.Trim();
×
1204
            osStatement = osStatement.substr(found + strlen("WHERE "));
×
1205
        }
1206

1207
        OGRNGWLayer *poLayer =
1208
            reinterpret_cast<OGRNGWLayer *>(GetLayerByName(osLayerName));
×
1209
        if (nullptr == poLayer)
×
1210
        {
1211
            CPLError(CE_Failure, CPLE_AppDefined,
×
1212
                     "Layer %s not found in dataset.", osName.c_str());
1213
            return nullptr;
×
1214
        }
1215

1216
        if (osStatement.empty())
×
1217
        {
1218
            poLayer->DeleteAllFeatures();
×
1219
        }
1220
        else
1221
        {
1222
            CPLDebug("NGW", "Delete features with statement %s",
×
1223
                     osStatement.c_str());
1224
            OGRFeatureQuery oQuery;
×
1225
            OGRErr eErr = oQuery.Compile(poLayer->GetLayerDefn(), osStatement);
×
1226
            if (eErr != OGRERR_NONE)
×
1227
            {
1228
                return nullptr;
×
1229
            }
1230

1231
            // Ignore all fields except first and ignore geometry
1232
            auto poLayerDefn = poLayer->GetLayerDefn();
×
1233
            poLayerDefn->SetGeometryIgnored(TRUE);
×
1234
            if (poLayerDefn->GetFieldCount() > 0)
×
1235
            {
1236
                std::set<std::string> osFields;
×
1237
                OGRFieldDefn *poFieldDefn = poLayerDefn->GetFieldDefn(0);
×
1238
                osFields.insert(poFieldDefn->GetNameRef());
×
1239
                poLayer->SetSelectedFields(osFields);
×
1240
            }
1241
            CPLString osNgwDelete =
1242
                "NGW:" +
×
1243
                OGRNGWLayer::TranslateSQLToFilter(
×
1244
                    reinterpret_cast<swq_expr_node *>(oQuery.GetSWQExpr()));
×
1245

1246
            poLayer->SetAttributeFilter(osNgwDelete);
×
1247

1248
            std::vector<GIntBig> aiFeaturesIDs;
×
1249
            OGRFeature *poFeat;
1250
            while ((poFeat = poLayer->GetNextFeature()) != nullptr)
×
1251
            {
1252
                aiFeaturesIDs.push_back(poFeat->GetFID());
×
1253
                OGRFeature::DestroyFeature(poFeat);
×
1254
            }
1255

1256
            poLayer->DeleteFeatures(aiFeaturesIDs);
×
1257

1258
            // Reset all filters and ignores
1259
            poLayerDefn->SetGeometryIgnored(FALSE);
×
1260
            poLayer->SetAttributeFilter(nullptr);
×
1261
            poLayer->SetIgnoredFields(nullptr);
×
1262
        }
1263
        return nullptr;
×
1264
    }
1265

1266
    if (STARTS_WITH_CI(osStatement, "DROP TABLE"))
×
1267
    {
1268
        // Get layer name from pszStatement DELETE FROM layer;.
1269
        CPLString osLayerName = osStatement.substr(strlen("DROP TABLE "));
×
1270
        if (osLayerName.endsWith(";"))
×
1271
        {
1272
            osLayerName = osLayerName.substr(0, osLayerName.size() - 1);
×
1273
            osLayerName.Trim();
×
1274
        }
1275

1276
        CPLDebug("NGW", "Delete layer with name %s.", osLayerName.c_str());
×
1277

1278
        for (int iLayer = 0; iLayer < GetLayerCount(); ++iLayer)
×
1279
        {
1280
            if (EQUAL(aoLayers[iLayer]->GetName(), osLayerName))
×
1281
            {
1282
                DeleteLayer(iLayer);
×
1283
                return nullptr;
×
1284
            }
1285
        }
1286

1287
        CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
×
1288
                 osLayerName.c_str());
1289

1290
        return nullptr;
×
1291
    }
1292

1293
    if (STARTS_WITH_CI(osStatement, "ALTER TABLE "))
×
1294
    {
1295
        if (osStatement.endsWith(";"))
×
1296
        {
1297
            osStatement = osStatement.substr(0, osStatement.size() - 1);
×
1298
            osStatement.Trim();
×
1299
        }
1300

1301
        CPLStringList aosTokens(SQLTokenize(osStatement));
×
1302
        /* ALTER TABLE src_table RENAME TO dst_table */
1303
        if (aosTokens.size() == 6 && EQUAL(aosTokens[3], "RENAME") &&
×
1304
            EQUAL(aosTokens[4], "TO"))
×
1305
        {
1306
            const char *pszSrcTableName = aosTokens[2];
×
1307
            const char *pszDstTableName = aosTokens[5];
×
1308

1309
            OGRNGWLayer *poLayer = static_cast<OGRNGWLayer *>(
1310
                GetLayerByName(SQLUnescape(pszSrcTableName)));
×
1311
            if (poLayer)
×
1312
            {
1313
                poLayer->Rename(SQLUnescape(pszDstTableName));
×
1314
                return nullptr;
×
1315
            }
1316

1317
            CPLError(CE_Failure, CPLE_AppDefined, "Unknown layer : %s",
×
1318
                     pszSrcTableName);
1319
        }
1320
        else
1321
        {
1322
            CPLError(CE_Failure, CPLE_AppDefined,
×
1323
                     "Unsupported alter table operation. Only rename table to "
1324
                     "... support.");
1325
        }
1326
        return nullptr;
×
1327
    }
1328

1329
    // SELECT xxxxx FROM yyyy WHERE zzzzzz;
1330
    if (STARTS_WITH_CI(osStatement, "SELECT "))
×
1331
    {
1332
        swq_select oSelect;
×
1333
        CPLDebug("NGW", "Select statement: %s", osStatement.c_str());
×
1334
        if (oSelect.preparse(osStatement) != CE_None)
×
1335
        {
1336
            return nullptr;
×
1337
        }
1338

1339
        if (oSelect.join_count == 0 && oSelect.poOtherSelect == nullptr &&
×
1340
            oSelect.table_count == 1 && oSelect.order_specs == 0)
×
1341
        {
1342
            OGRNGWLayer *poLayer = reinterpret_cast<OGRNGWLayer *>(
1343
                GetLayerByName(oSelect.table_defs[0].table_name));
×
1344
            if (nullptr == poLayer)
×
1345
            {
1346
                CPLError(CE_Failure, CPLE_AppDefined,
×
1347
                         "Layer %s not found in dataset.",
1348
                         oSelect.table_defs[0].table_name);
×
1349
                return nullptr;
×
1350
            }
1351

1352
            std::set<std::string> aosFields;
×
1353
            bool bSkip = false;
×
1354
            for (int i = 0; i < oSelect.result_columns(); ++i)
×
1355
            {
1356
                swq_col_func col_func = oSelect.column_defs[i].col_func;
×
1357
                if (col_func != SWQCF_NONE)
×
1358
                {
1359
                    bSkip = true;
×
1360
                    break;
×
1361
                }
1362

1363
                if (oSelect.column_defs[i].distinct_flag)
×
1364
                {
1365
                    CPLError(CE_Warning, CPLE_AppDefined,
×
1366
                             "Distinct not supported.");
1367
                    bSkip = true;
×
1368
                    break;
×
1369
                }
1370

1371
                if (oSelect.column_defs[i].field_name != nullptr)
×
1372
                {
1373
                    if (EQUAL(oSelect.column_defs[i].field_name, "*"))
×
1374
                    {
1375
                        aosFields.clear();
×
1376
                        aosFields.emplace(oSelect.column_defs[i].field_name);
×
1377
                        break;
×
1378
                    }
1379
                    else
1380
                    {
1381
                        aosFields.emplace(oSelect.column_defs[i].field_name);
×
1382
                    }
1383
                }
1384
            }
1385

1386
            std::string osNgwSelect;
×
1387
            for (int iKey = 0; iKey < oSelect.order_specs; iKey++)
×
1388
            {
1389
                swq_order_def *psKeyDef = oSelect.order_defs + iKey;
×
1390
                if (iKey > 0)
×
1391
                {
1392
                    osNgwSelect += ",";
×
1393
                }
1394

1395
                if (psKeyDef->ascending_flag == TRUE)
×
1396
                {
1397
                    osNgwSelect += psKeyDef->field_name;
×
1398
                }
1399
                else
1400
                {
1401
                    osNgwSelect += "-" + std::string(psKeyDef->field_name);
×
1402
                }
1403
            }
1404

1405
            if (oSelect.where_expr != nullptr)
×
1406
            {
1407
                if (!osNgwSelect.empty())
×
1408
                {
1409
                    osNgwSelect += "&";
×
1410
                }
1411
                osNgwSelect +=
1412
                    OGRNGWLayer::TranslateSQLToFilter(oSelect.where_expr);
×
1413
            }
1414

1415
            if (osNgwSelect.empty())
×
1416
            {
1417
                bSkip = true;
×
1418
            }
1419

1420
            if (!bSkip)
×
1421
            {
1422
                if (aosFields.empty())
×
1423
                {
1424
                    CPLError(
×
1425
                        CE_Failure, CPLE_AppDefined,
1426
                        "SELECT statement is invalid: field list is empty.");
1427
                    return nullptr;
×
1428
                }
1429

1430
                if (poLayer->SyncToDisk() != OGRERR_NONE)
×
1431
                {
1432
                    return nullptr;
×
1433
                }
1434

1435
                OGRNGWLayer *poOutLayer = poLayer->Clone();
×
1436
                if (aosFields.size() == 1 && *(aosFields.begin()) == "*")
×
1437
                {
1438
                    poOutLayer->SetIgnoredFields(nullptr);
×
1439
                }
1440
                else
1441
                {
1442
                    poOutLayer->SetSelectedFields(aosFields);
×
1443
                }
1444
                poOutLayer->SetSpatialFilter(poSpatialFilter);
×
1445

1446
                if (osNgwSelect
×
1447
                        .empty())  // If we here oSelect.where_expr is empty
×
1448
                {
1449
                    poOutLayer->SetAttributeFilter(nullptr);
×
1450
                }
1451
                else
1452
                {
1453
                    std::string osAttributeFilte = "NGW:" + osNgwSelect;
×
1454
                    poOutLayer->SetAttributeFilter(osAttributeFilte.c_str());
×
1455
                }
1456
                return poOutLayer;
×
1457
            }
1458
        }
1459
    }
1460

1461
    return GDALDataset::ExecuteSQL(pszStatement, poSpatialFilter, pszDialect);
×
1462
}
1463

1464
/*
1465
 * GetProjectionRef()
1466
 */
1467
const OGRSpatialReference *OGRNGWDataset::GetSpatialRef() const
×
1468
{
1469
    if (poRasterDS != nullptr)
×
1470
    {
1471
        return poRasterDS->GetSpatialRef();
×
1472
    }
1473
    return GDALDataset::GetSpatialRef();
×
1474
}
1475

1476
/*
1477
 * GetGeoTransform()
1478
 */
1479
CPLErr OGRNGWDataset::GetGeoTransform(GDALGeoTransform &gt) const
×
1480
{
1481
    if (poRasterDS != nullptr)
×
1482
    {
1483
        return poRasterDS->GetGeoTransform(gt);
×
1484
    }
1485
    return GDALDataset::GetGeoTransform(gt);
×
1486
}
1487

1488
/*
1489
 * IRasterIO()
1490
 */
1491
CPLErr OGRNGWDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
×
1492
                                int nXSize, int nYSize, void *pData,
1493
                                int nBufXSize, int nBufYSize,
1494
                                GDALDataType eBufType, int nBandCount,
1495
                                BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
1496
                                GSpacing nLineSpace, GSpacing nBandSpace,
1497
                                GDALRasterIOExtraArg *psExtraArg)
1498
{
1499
    if (poRasterDS != nullptr)
×
1500
    {
1501
        if (stPixelExtent.IsInit())
×
1502
        {
1503
            OGREnvelope stTestExtent;
×
1504
            stTestExtent.MinX = static_cast<double>(nXOff);
×
1505
            stTestExtent.MinY = static_cast<double>(nYOff);
×
1506
            stTestExtent.MaxX = static_cast<double>(nXOff + nXSize);
×
1507
            stTestExtent.MaxY = static_cast<double>(nYOff + nYSize);
×
1508

1509
            if (!stPixelExtent.Intersects(stTestExtent))
×
1510
            {
1511
                CPLDebug("NGW", "Raster extent in px is: %f, %f, %f, %f",
×
1512
                         stPixelExtent.MinX, stPixelExtent.MinY,
1513
                         stPixelExtent.MaxX, stPixelExtent.MaxY);
1514
                CPLDebug("NGW", "RasterIO extent is: %f, %f, %f, %f",
×
1515
                         stTestExtent.MinX, stTestExtent.MinY,
1516
                         stTestExtent.MaxX, stTestExtent.MaxY);
1517

1518
                // Fill buffer transparent color.
1519
                memset(pData, 0,
×
1520
                       static_cast<size_t>(nBufXSize) * nBufYSize * nBandCount *
×
1521
                           GDALGetDataTypeSizeBytes(eBufType));
×
1522
                return CE_None;
×
1523
            }
1524
        }
1525
    }
1526
    return GDALDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
×
1527
                                  nBufXSize, nBufYSize, eBufType, nBandCount,
1528
                                  panBandMap, nPixelSpace, nLineSpace,
1529
                                  nBandSpace, psExtraArg);
×
1530
}
1531

1532
/*
1533
 * FillCapabilities()
1534
 */
1535
void OGRNGWDataset::FillCapabilities(const CPLStringList &aosHTTPOptions)
×
1536
{
1537
    // Check NGW version. Paging available from 3.1
1538
    CPLJSONDocument oRouteReq;
×
1539
    if (oRouteReq.LoadUrl(NGWAPI::GetVersionURL(osUrl), aosHTTPOptions))
×
1540
    {
1541
        CPLJSONObject oRoot = oRouteReq.GetRoot();
×
1542

1543
        if (oRoot.IsValid())
×
1544
        {
1545
            std::string osVersion = oRoot.GetString("nextgisweb", "0.0");
×
1546
            bHasFeaturePaging = NGWAPI::CheckVersion(osVersion, 3, 1);
×
1547

1548
            CPLDebug("NGW", "Is feature paging supported: %s",
×
1549
                     bHasFeaturePaging ? "yes" : "no");
×
1550
        }
1551
    }
1552
}
×
1553

1554
/*
1555
 * Extensions()
1556
 */
1557
std::string OGRNGWDataset::Extensions() const
×
1558
{
1559
    return osExtensions;
×
1560
}
1561

1562
/*
1563
 * GetFieldDomainNames()
1564
 */
1565
std::vector<std::string> OGRNGWDataset::GetFieldDomainNames(CSLConstList) const
×
1566
{
1567
    std::vector<std::string> oDomainNamesList;
×
1568
    std::array<OGRFieldType, 3> aeFieldTypes{OFTString, OFTInteger,
×
1569
                                             OFTInteger64};
1570
    for (auto const &oDom : moDomains)
×
1571
    {
1572
        for (auto eFieldType : aeFieldTypes)
×
1573
        {
1574
            auto pOgrDom = oDom.second.ToFieldDomain(eFieldType);
×
1575
            if (pOgrDom != nullptr)
×
1576
            {
1577
                oDomainNamesList.emplace_back(pOgrDom->GetName());
×
1578
            }
1579
        }
1580
    }
1581
    return oDomainNamesList;
×
1582
}
1583

1584
/*
1585
 * GetFieldDomain()
1586
 */
1587
const OGRFieldDomain *
1588
OGRNGWDataset::GetFieldDomain(const std::string &name) const
×
1589
{
1590
    std::array<OGRFieldType, 3> aeFieldTypes{OFTString, OFTInteger,
×
1591
                                             OFTInteger64};
1592
    for (auto const &oDom : moDomains)
×
1593
    {
1594
        for (auto eFieldType : aeFieldTypes)
×
1595
        {
1596
            auto pOgrDom = oDom.second.ToFieldDomain(eFieldType);
×
1597
            if (pOgrDom != nullptr)
×
1598
            {
1599
                if (pOgrDom->GetName() == name)
×
1600
                {
1601
                    return pOgrDom;
×
1602
                }
1603
            }
1604
        }
1605
    }
1606
    return nullptr;
×
1607
}
1608

1609
/*
1610
 * DeleteFieldDomain()
1611
 */
1612
bool OGRNGWDataset::DeleteFieldDomain(const std::string &name,
×
1613
                                      std::string &failureReason)
1614
{
1615
    if (eAccess != GA_Update)
×
1616
    {
1617
        failureReason =
1618
            "DeleteFieldDomain() not supported on read-only dataset";
×
1619
        return false;
×
1620
    }
1621

1622
    std::array<OGRFieldType, 3> aeFieldTypes{OFTString, OFTInteger,
×
1623
                                             OFTInteger64};
1624
    for (auto const &oDom : moDomains)
×
1625
    {
1626
        for (auto eFieldType : aeFieldTypes)
×
1627
        {
1628
            auto pOgrDom = oDom.second.ToFieldDomain(eFieldType);
×
1629
            if (pOgrDom != nullptr)
×
1630
            {
1631
                if (pOgrDom->GetName() == name)
×
1632
                {
1633
                    auto nResourceID = oDom.second.GetID();
×
1634

1635
                    CPLError(CE_Warning, CPLE_AppDefined,
×
1636
                             "Delete following domains with common "
1637
                             "identifier " CPL_FRMT_GIB ": %s.",
1638
                             nResourceID,
1639
                             oDom.second.GetDomainsNames().c_str());
×
1640

1641
                    auto result = NGWAPI::DeleteResource(
×
1642
                        GetUrl(), std::to_string(nResourceID),
×
1643
                        GetHeaders(false));
×
1644
                    if (!result)
×
1645
                    {
1646
                        failureReason = CPLGetLastErrorMsg();
×
1647
                        return result;
×
1648
                    }
1649

1650
                    moDomains.erase(nResourceID);
×
1651

1652
                    // Remove domain from fields definitions
1653
                    for (const auto &oLayer : aoLayers)
×
1654
                    {
1655
                        for (int i = 0;
×
1656
                             i < oLayer->GetLayerDefn()->GetFieldCount(); ++i)
×
1657
                        {
1658
                            OGRFieldDefn *poFieldDefn =
1659
                                oLayer->GetLayerDefn()->GetFieldDefn(i);
×
1660
                            if (oDom.second.HasDomainName(
×
1661
                                    poFieldDefn->GetDomainName()))
1662
                            {
1663
                                auto oTemporaryUnsealer(
1664
                                    poFieldDefn->GetTemporaryUnsealer());
×
1665
                                poFieldDefn->SetDomainName(std::string());
×
1666
                            }
1667
                        }
1668
                    }
1669
                    return true;
×
1670
                }
1671
            }
1672
        }
1673
    }
1674
    failureReason = "Domain does not exist";
×
1675
    return false;
×
1676
}
1677

1678
/*
1679
 * CreateNGWLookupTableJson()
1680
 */
1681
static std::string CreateNGWLookupTableJson(const OGRCodedFieldDomain *pDomain,
×
1682
                                            GIntBig nResourceId)
1683
{
1684
    CPLJSONObject oResourceJson;
×
1685
    // Add resource json item.
1686
    CPLJSONObject oResource("resource", oResourceJson);
×
1687
    oResource.Add("cls", "lookup_table");
×
1688
    CPLJSONObject oResourceParent("parent", oResource);
×
1689
    oResourceParent.Add("id", nResourceId);
×
1690
    oResource.Add("display_name", pDomain->GetName());
×
1691
    oResource.Add("description", pDomain->GetDescription());
×
1692

1693
    // Add vector_layer json item.
1694
    CPLJSONObject oLookupTable("lookup_table", oResourceJson);
×
1695
    CPLJSONObject oLookupTableItems("items", oLookupTable);
×
1696
    const auto enumeration = pDomain->GetEnumeration();
×
1697
    for (int i = 0; enumeration[i].pszCode != nullptr; ++i)
×
1698
    {
1699
        const char *pszValCurrent = "";
×
1700
        // NGW not supported null as coded value, so set it as ""
1701
        if (enumeration[i].pszValue != nullptr)
×
1702
        {
1703
            pszValCurrent = enumeration[i].pszValue;
×
1704
        }
1705
        oLookupTableItems.Add(enumeration[i].pszCode, pszValCurrent);
×
1706
    }
1707

1708
    return oResourceJson.Format(CPLJSONObject::PrettyFormat::Plain);
×
1709
}
1710

1711
/*
1712
 * AddFieldDomain()
1713
 */
1714
bool OGRNGWDataset::AddFieldDomain(std::unique_ptr<OGRFieldDomain> &&domain,
×
1715
                                   std::string &failureReason)
1716
{
1717
    const std::string domainName(domain->GetName());
×
1718
    if (eAccess != GA_Update)
×
1719
    {
1720
        failureReason = "Add field domain not supported on read-only dataset";
×
1721
        return false;
×
1722
    }
1723

1724
    if (GetFieldDomain(domainName) != nullptr)
×
1725
    {
1726
        failureReason = "A domain of identical name already exists";
×
1727
        return false;
×
1728
    }
1729

1730
    if (domain->GetDomainType() != OFDT_CODED)
×
1731
    {
1732
        failureReason = "Unsupported domain type";
×
1733
        return false;
×
1734
    }
1735

1736
    auto osPalyload = CreateNGWLookupTableJson(
1737
        static_cast<OGRCodedFieldDomain *>(domain.get()),
×
1738
        static_cast<GIntBig>(std::stol(osResourceId)));
×
1739

1740
    std::string osResourceIdInt =
1741
        NGWAPI::CreateResource(osUrl, osPalyload, GetHeaders());
×
1742
    if (osResourceIdInt == "-1")
×
1743
    {
1744
        failureReason = CPLGetLastErrorMsg();
×
1745
        return false;
×
1746
    }
1747
    auto osNewResourceUrl = NGWAPI::GetResourceURL(osUrl, osResourceIdInt);
×
1748
    CPLJSONDocument oResourceDetailsReq;
×
1749
    bool bResult =
1750
        oResourceDetailsReq.LoadUrl(osNewResourceUrl, GetHeaders(false));
×
1751
    if (!bResult)
×
1752
    {
1753
        failureReason = CPLGetLastErrorMsg();
×
1754
        return false;
×
1755
    }
1756

1757
    OGRNGWCodedFieldDomain oDomain(oResourceDetailsReq.GetRoot());
×
1758
    if (oDomain.GetID() == 0)
×
1759
    {
1760
        failureReason = "Failed to parse domain detailes from NGW";
×
1761
        return false;
×
1762
    }
1763
    moDomains[oDomain.GetID()] = std::move(oDomain);
×
1764
    return true;
×
1765
}
1766

1767
/*
1768
 * UpdateFieldDomain()
1769
 */
1770
bool OGRNGWDataset::UpdateFieldDomain(std::unique_ptr<OGRFieldDomain> &&domain,
×
1771
                                      std::string &failureReason)
1772
{
1773
    const std::string domainName(domain->GetName());
×
1774
    if (eAccess != GA_Update)
×
1775
    {
1776
        failureReason = "Add field domain not supported on read-only dataset";
×
1777
        return false;
×
1778
    }
1779

1780
    if (GetFieldDomain(domainName) == nullptr)
×
1781
    {
1782
        failureReason = "The domain should already exist to be updated";
×
1783
        return false;
×
1784
    }
1785

1786
    if (domain->GetDomainType() != OFDT_CODED)
×
1787
    {
1788
        failureReason = "Unsupported domain type";
×
1789
        return false;
×
1790
    }
1791

1792
    auto nResourceId = GetDomainIdByName(domainName);
×
1793
    if (nResourceId == 0)
×
1794
    {
1795
        failureReason = "Failed get NGW domain identifier";
×
1796
        return false;
×
1797
    }
1798

1799
    auto osPayload = CreateNGWLookupTableJson(
1800
        static_cast<const OGRCodedFieldDomain *>(domain.get()),
×
1801
        static_cast<GIntBig>(std::stol(osResourceId)));
×
1802

1803
    if (!NGWAPI::UpdateResource(osUrl, osResourceId, osPayload, GetHeaders()))
×
1804
    {
1805
        failureReason = CPLGetLastErrorMsg();
×
1806
        return false;
×
1807
    }
1808

1809
    auto osNewResourceUrl = NGWAPI::GetResourceURL(osUrl, osResourceId);
×
1810
    CPLJSONDocument oResourceDetailsReq;
×
1811
    bool bResult =
1812
        oResourceDetailsReq.LoadUrl(osNewResourceUrl, GetHeaders(false));
×
1813
    if (!bResult)
×
1814
    {
1815
        failureReason = CPLGetLastErrorMsg();
×
1816
        return false;
×
1817
    }
1818

1819
    OGRNGWCodedFieldDomain oDomain(oResourceDetailsReq.GetRoot());
×
1820
    if (oDomain.GetID() == 0)
×
1821
    {
1822
        failureReason = "Failed to parse domain detailes from NGW";
×
1823
        return false;
×
1824
    }
1825
    moDomains[oDomain.GetID()] = std::move(oDomain);
×
1826
    return true;
×
1827
}
1828

1829
/*
1830
 * GetDomainByID()
1831
 */
1832
OGRNGWCodedFieldDomain OGRNGWDataset::GetDomainByID(GIntBig id) const
×
1833
{
1834
    auto pos = moDomains.find(id);
×
1835
    if (pos == moDomains.end())
×
1836
    {
1837
        return OGRNGWCodedFieldDomain();
×
1838
    }
1839
    else
1840
    {
1841
        return pos->second;
×
1842
    }
1843
}
1844

1845
/*
1846
 *  GetDomainIdByName()
1847
 */
1848
GIntBig OGRNGWDataset::GetDomainIdByName(const std::string &osDomainName) const
×
1849
{
1850
    for (auto const &oDom : moDomains)
×
1851
    {
1852
        if (oDom.second.HasDomainName(osDomainName))
×
1853
        {
1854
            return oDom.first;
×
1855
        }
1856
    }
1857
    return 0L;
×
1858
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc