Coverage Report

Created: 2025-12-03 08:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/ngw/ogrngwlayer.cpp
Line
Count
Source
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
/*
17
 * OGRGeometryToWKT()
18
 */
19
static std::string OGRGeometryToWKT(OGRGeometry *poGeom)
20
0
{
21
0
    std::string osOut;
22
0
    if (nullptr == poGeom)
23
0
    {
24
0
        return osOut;
25
0
    }
26
27
0
    char *pszWkt = nullptr;
28
0
    if (poGeom->exportToWkt(&pszWkt) == OGRERR_NONE)
29
0
    {
30
0
        osOut = pszWkt;
31
0
    }
32
0
    CPLFree(pszWkt);
33
34
0
    return osOut;
35
0
}
36
37
/*
38
 * JSONToFeature()
39
 */
40
static OGRFeature *JSONToFeature(const CPLJSONObject &featureJson,
41
                                 OGRFeatureDefn *poFeatureDefn,
42
                                 bool bCheckIgnoredFields = false,
43
                                 bool bStoreExtensionData = false)
44
0
{
45
0
    OGRFeature *poFeature = new OGRFeature(poFeatureDefn);
46
0
    poFeature->SetFID(featureJson.GetLong("id"));
47
0
    CPLJSONObject oFields = featureJson.GetObj("fields");
48
0
    for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
49
0
    {
50
0
        OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
51
0
        if (bCheckIgnoredFields && poFieldDefn->IsIgnored())
52
0
        {
53
0
            continue;
54
0
        }
55
0
        CPLJSONObject oJSONField = oFields[poFieldDefn->GetNameRef()];
56
0
        if (oJSONField.IsValid() &&
57
0
            oJSONField.GetType() != CPLJSONObject::Type::Null)
58
0
        {
59
0
            switch (poFieldDefn->GetType())
60
0
            {
61
0
                case OFTInteger:
62
0
                    poFeature->SetField(iField, oJSONField.ToInteger());
63
0
                    break;
64
0
                case OFTInteger64:
65
0
                    poFeature->SetField(iField, oJSONField.ToLong());
66
0
                    break;
67
0
                case OFTReal:
68
0
                    poFeature->SetField(iField, oJSONField.ToDouble());
69
0
                    break;
70
0
                case OFTBinary:
71
                    // Not supported.
72
0
                    break;
73
0
                case OFTString:
74
0
                case OFTIntegerList:
75
0
                case OFTInteger64List:
76
0
                case OFTRealList:
77
0
                case OFTStringList:
78
0
                    poFeature->SetField(iField, oJSONField.ToString().c_str());
79
0
                    break;
80
0
                case OFTDate:
81
0
                case OFTTime:
82
0
                case OFTDateTime:
83
0
                {
84
0
                    int nYear = oJSONField.GetInteger("year");
85
0
                    int nMonth = oJSONField.GetInteger("month");
86
0
                    int nDay = oJSONField.GetInteger("day");
87
0
                    int nHour = oJSONField.GetInteger("hour");
88
0
                    int nMinute = oJSONField.GetInteger("minute");
89
0
                    int nSecond = oJSONField.GetInteger("second");
90
0
                    poFeature->SetField(iField, nYear, nMonth, nDay, nHour,
91
0
                                        nMinute, float(nSecond));
92
0
                    break;
93
0
                }
94
0
                default:
95
0
                    break;
96
0
            }
97
0
        }
98
0
    }
99
100
0
    bool bFillGeometry =
101
0
        !(bCheckIgnoredFields && poFeatureDefn->IsGeometryIgnored());
102
103
0
    if (bFillGeometry)
104
0
    {
105
0
        OGRGeometry *poGeometry = nullptr;
106
0
        OGRGeometryFactory::createFromWkt(featureJson.GetString("geom").c_str(),
107
0
                                          nullptr, &poGeometry);
108
0
        if (poGeometry != nullptr)
109
0
        {
110
0
            const OGRSpatialReference *poSpatialRef =
111
0
                poFeatureDefn->GetGeomFieldDefn(0)->GetSpatialRef();
112
0
            if (poSpatialRef != nullptr)
113
0
            {
114
0
                poGeometry->assignSpatialReference(poSpatialRef);
115
0
            }
116
0
            poFeature->SetGeomFieldDirectly(0, poGeometry);
117
0
        }
118
0
    }
119
120
    // Get extensions key and store it in native data.
121
0
    if (bStoreExtensionData)
122
0
    {
123
0
        CPLJSONObject oExtensions = featureJson.GetObj("extensions");
124
0
        if (oExtensions.IsValid() &&
125
0
            oExtensions.GetType() != CPLJSONObject::Type::Null)
126
0
        {
127
0
            poFeature->SetNativeData(
128
0
                oExtensions.Format(CPLJSONObject::PrettyFormat::Plain).c_str());
129
0
            poFeature->SetNativeMediaType("application/json");
130
0
        }
131
0
    }
132
133
0
    return poFeature;
134
0
}
135
136
/*
137
 * FeatureToJson()
138
 */
139
static CPLJSONObject FeatureToJson(OGRFeature *poFeature)
140
0
{
141
0
    CPLJSONObject oFeatureJson;
142
0
    if (poFeature == nullptr)
143
0
    {
144
        // Should not happen.
145
0
        return oFeatureJson;
146
0
    }
147
148
0
    if (poFeature->GetFID() >= 0)
149
0
    {
150
0
        oFeatureJson.Add("id", poFeature->GetFID());
151
0
    }
152
153
0
    OGRGeometry *poGeom = poFeature->GetGeometryRef();
154
0
    std::string osGeomWKT = OGRGeometryToWKT(poGeom);
155
0
    if (!osGeomWKT.empty())
156
0
    {
157
0
        oFeatureJson.Add("geom", osGeomWKT);
158
0
    }
159
160
0
    const OGRFeatureDefn *poFeatureDefn = poFeature->GetDefnRef();
161
0
    CPLJSONObject oFieldsJson("fields", oFeatureJson);
162
0
    for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
163
0
    {
164
0
        const OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
165
0
        if (poFeature->IsFieldNull(iField) == TRUE)
166
0
        {
167
0
            oFieldsJson.AddNull(poFieldDefn->GetNameRef());
168
0
            continue;
169
0
        }
170
171
0
        if (poFeature->IsFieldSet(iField) == TRUE)
172
0
        {
173
0
            switch (poFieldDefn->GetType())
174
0
            {
175
0
                case OFTInteger:
176
0
                    oFieldsJson.Add(poFieldDefn->GetNameRef(),
177
0
                                    poFeature->GetFieldAsInteger(iField));
178
0
                    break;
179
0
                case OFTInteger64:
180
0
                    oFieldsJson.Add(
181
0
                        poFieldDefn->GetNameRef(),
182
0
                        static_cast<GInt64>(
183
0
                            poFeature->GetFieldAsInteger64(iField)));
184
0
                    break;
185
0
                case OFTReal:
186
0
                    oFieldsJson.Add(poFieldDefn->GetNameRef(),
187
0
                                    poFeature->GetFieldAsDouble(iField));
188
0
                    break;
189
0
                case OFTBinary:
190
                    // Not supported.
191
0
                    break;
192
0
                case OFTString:
193
0
                case OFTIntegerList:
194
0
                case OFTInteger64List:
195
0
                case OFTRealList:
196
0
                case OFTStringList:
197
0
                    oFieldsJson.Add(poFieldDefn->GetNameRef(),
198
0
                                    poFeature->GetFieldAsString(iField));
199
0
                    break;
200
0
                case OFTDate:
201
0
                case OFTTime:
202
0
                case OFTDateTime:
203
0
                {
204
0
                    int nYear, nMonth, nDay, nHour, nMinute, nSecond, nTZFlag;
205
0
                    if (poFeature->GetFieldAsDateTime(
206
0
                            iField, &nYear, &nMonth, &nDay, &nHour, &nMinute,
207
0
                            &nSecond, &nTZFlag) == TRUE)
208
0
                    {
209
                        // TODO: Convert timestamp to UTC.
210
0
                        if (nTZFlag == 0 || nTZFlag == 100)
211
0
                        {
212
0
                            CPLJSONObject oDateJson(poFieldDefn->GetNameRef(),
213
0
                                                    oFieldsJson);
214
215
0
                            oDateJson.Add("year", nYear);
216
0
                            oDateJson.Add("month", nMonth);
217
0
                            oDateJson.Add("day", nDay);
218
0
                            oDateJson.Add("hour", nHour);
219
0
                            oDateJson.Add("minute", nMinute);
220
0
                            oDateJson.Add("second", nSecond);
221
0
                        }
222
0
                    }
223
0
                    break;
224
0
                }
225
0
                default:
226
0
                    break;
227
0
            }
228
0
        }
229
0
    }
230
231
0
    if (poFeature->GetNativeData())
232
0
    {
233
0
        CPLJSONDocument oExtensions;
234
0
        if (oExtensions.LoadMemory(poFeature->GetNativeData()))
235
0
        {
236
0
            oFeatureJson.Add("extensions", oExtensions.GetRoot());
237
0
        }
238
0
    }
239
240
0
    return oFeatureJson;
241
0
}
242
243
/*
244
 * FeatureToJsonString()
245
 */
246
static std::string FeatureToJsonString(OGRFeature *poFeature)
247
0
{
248
0
    return FeatureToJson(poFeature).Format(CPLJSONObject::PrettyFormat::Plain);
249
0
}
250
251
/*
252
 * FeaturesIDToJsonString()
253
 */
254
static std::string
255
FeaturesIDToJsonString(const std::vector<GIntBig> &vFeaturesID)
256
0
{
257
0
    CPLJSONArray oFeaturesIDJsonArray;
258
259
0
    for (GIntBig nFeatureID : vFeaturesID)
260
0
    {
261
0
        CPLJSONObject oFeatureIDJson;
262
0
        oFeatureIDJson.Add("id", nFeatureID);
263
264
0
        oFeaturesIDJsonArray.Add(oFeatureIDJson);
265
0
    }
266
267
0
    return oFeaturesIDJsonArray.Format(CPLJSONObject::PrettyFormat::Plain);
268
0
}
269
270
/*
271
 * FreeMap()
272
 */
273
static void FreeMap(std::map<GIntBig, OGRFeature *> &moFeatures)
274
0
{
275
    // cppcheck-suppress constVariableReference
276
0
    for (auto &oPair : moFeatures)
277
0
    {
278
0
        OGRFeature::DestroyFeature(oPair.second);
279
0
    }
280
281
0
    moFeatures.clear();
282
0
}
283
284
static bool CheckFieldNameUnique(OGRFeatureDefn *poFeatureDefn, int iField,
285
                                 const char *pszFieldName)
286
0
{
287
0
    for (int i = 0; i < poFeatureDefn->GetFieldCount(); ++i)
288
0
    {
289
0
        if (i == iField)
290
0
        {
291
0
            continue;
292
0
        }
293
294
0
        OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(i);
295
0
        if (poFieldDefn && EQUAL(poFieldDefn->GetNameRef(), pszFieldName))
296
0
        {
297
0
            CPLError(CE_Failure, CPLE_NotSupported,
298
0
                     "Field name %s already present in field %d.", pszFieldName,
299
0
                     i);
300
0
            return false;
301
0
        }
302
0
    }
303
0
    return true;
304
0
}
305
306
/*
307
 * TranslateSQLToFilter()
308
 */
309
std::string OGRNGWLayer::TranslateSQLToFilter(swq_expr_node *poNode)
310
0
{
311
0
    if (nullptr == poNode)
312
0
    {
313
0
        return "";
314
0
    }
315
316
0
    if (poNode->eNodeType == SNT_OPERATION)
317
0
    {
318
0
        if (poNode->nOperation == SWQ_AND && poNode->nSubExprCount == 2)
319
0
        {
320
0
            std::string osFilter1 =
321
0
                TranslateSQLToFilter(poNode->papoSubExpr[0]);
322
0
            std::string osFilter2 =
323
0
                TranslateSQLToFilter(poNode->papoSubExpr[1]);
324
325
0
            if (osFilter1.empty() || osFilter2.empty())
326
0
            {
327
0
                return "";
328
0
            }
329
0
            return osFilter1 + "&" + osFilter2;
330
0
        }
331
0
        else if ((poNode->nOperation == SWQ_EQ ||
332
0
                  poNode->nOperation == SWQ_NE ||
333
0
                  poNode->nOperation == SWQ_GE ||
334
0
                  poNode->nOperation == SWQ_LE ||
335
0
                  poNode->nOperation == SWQ_LT ||
336
0
                  poNode->nOperation == SWQ_GT ||
337
0
                  poNode->nOperation == SWQ_LIKE ||
338
0
                  poNode->nOperation == SWQ_ILIKE) &&
339
0
                 poNode->nSubExprCount == 2 &&
340
0
                 poNode->papoSubExpr[0]->eNodeType == SNT_COLUMN &&
341
0
                 poNode->papoSubExpr[1]->eNodeType == SNT_CONSTANT)
342
0
        {
343
0
            if (poNode->papoSubExpr[0]->string_value == nullptr)
344
0
            {
345
0
                return "";
346
0
            }
347
0
            char *pszNameEncoded = CPLEscapeString(
348
0
                poNode->papoSubExpr[0]->string_value, -1, CPLES_URL);
349
0
            std::string osFieldName = "fld_" + std::string(pszNameEncoded);
350
0
            CPLFree(pszNameEncoded);
351
352
0
            switch (poNode->nOperation)
353
0
            {
354
0
                case SWQ_EQ:
355
0
                    osFieldName += "__eq";
356
0
                    break;
357
0
                case SWQ_NE:
358
0
                    osFieldName += "__ne";
359
0
                    break;
360
0
                case SWQ_GE:
361
0
                    osFieldName += "__ge";
362
0
                    break;
363
0
                case SWQ_LE:
364
0
                    osFieldName += "__le";
365
0
                    break;
366
0
                case SWQ_LT:
367
0
                    osFieldName += "__lt";
368
0
                    break;
369
0
                case SWQ_GT:
370
0
                    osFieldName += "__gt";
371
0
                    break;
372
0
                case SWQ_LIKE:
373
0
                    osFieldName += "__like";
374
0
                    break;
375
0
                case SWQ_ILIKE:
376
0
                    osFieldName += "__ilike";
377
0
                    break;
378
0
                default:
379
0
                    CPLAssert(false);
380
0
                    break;
381
0
            }
382
383
0
            std::string osVal;
384
0
            switch (poNode->papoSubExpr[1]->field_type)
385
0
            {
386
0
                case SWQ_INTEGER64:
387
0
                case SWQ_INTEGER:
388
0
                    osVal = std::to_string(poNode->papoSubExpr[1]->int_value);
389
0
                    break;
390
0
                case SWQ_FLOAT:
391
0
                    osVal = std::to_string(poNode->papoSubExpr[1]->float_value);
392
0
                    break;
393
0
                case SWQ_STRING:
394
0
                    if (poNode->papoSubExpr[1]->string_value)
395
0
                    {
396
0
                        char *pszValueEncoded = CPLEscapeString(
397
0
                            poNode->papoSubExpr[1]->string_value, -1,
398
0
                            CPLES_URL);
399
0
                        osVal = pszValueEncoded;
400
0
                        CPLFree(pszValueEncoded);
401
0
                    }
402
0
                    break;
403
0
                case SWQ_DATE:
404
0
                case SWQ_TIME:
405
0
                case SWQ_TIMESTAMP:
406
0
                    if (poNode->papoSubExpr[1]->string_value)
407
0
                    {
408
0
                        char *pszValueEncoded = CPLEscapeString(
409
0
                            poNode->papoSubExpr[1]->string_value, -1,
410
0
                            CPLES_URL);
411
0
                        osVal = pszValueEncoded;
412
0
                        CPLFree(pszValueEncoded);
413
0
                    }
414
0
                    break;
415
0
                default:
416
0
                    break;
417
0
            }
418
0
            if (osFieldName.empty() || osVal.empty())
419
0
            {
420
0
                CPLDebug("NGW", "Unsupported filter operation for server side");
421
0
                return "";
422
0
            }
423
424
0
            return osFieldName + "=" + osVal;
425
0
        }
426
0
        else
427
0
        {
428
0
            CPLDebug("NGW", "Unsupported filter operation for server side");
429
0
            return "";
430
0
        }
431
0
    }
432
0
    return "";
433
0
}
434
435
/*
436
 * OGRNGWLayer()
437
 */
438
OGRNGWLayer::OGRNGWLayer(OGRNGWDataset *poDSIn,
439
                         const CPLJSONObject &oResourceJsonObject)
440
0
    : osResourceId(oResourceJsonObject.GetString("resource/id", "-1")),
441
0
      poDS(poDSIn), bFetchedPermissions(false), poFeatureDefn(nullptr),
442
0
      nFeatureCount(-1), oNextPos(moFeatures.begin()), nPageStart(0),
443
0
      bNeedSyncData(false), bNeedSyncStructure(false),
444
0
      bClientSideAttributeFilter(false)
445
0
{
446
0
    Fill(oResourceJsonObject);
447
0
}
448
449
/*
450
 * OGRNGWLayer()
451
 */
452
OGRNGWLayer::OGRNGWLayer(const std::string &osResourceIdIn,
453
                         OGRNGWDataset *poDSIn,
454
                         const NGWAPI::Permissions &stPermissionsIn,
455
                         OGRFeatureDefn *poFeatureDefnIn,
456
                         GIntBig nFeatureCountIn, const OGREnvelope &stExtentIn)
457
0
    : osResourceId(osResourceIdIn), poDS(poDSIn),
458
0
      stPermissions(stPermissionsIn), bFetchedPermissions(true),
459
0
      poFeatureDefn(poFeatureDefnIn), nFeatureCount(nFeatureCountIn),
460
0
      stExtent(stExtentIn), oNextPos(moFeatures.begin()), nPageStart(0),
461
0
      bNeedSyncData(false), bNeedSyncStructure(false),
462
0
      bClientSideAttributeFilter(false)
463
0
{
464
0
    poFeatureDefn->Reference();
465
0
    SetDescription(poFeatureDefn->GetName());
466
0
}
467
468
/*
469
 * OGRNGWLayer()
470
 */
471
OGRNGWLayer::OGRNGWLayer(OGRNGWDataset *poDSIn, const std::string &osNameIn,
472
                         OGRSpatialReference *poSpatialRef,
473
                         OGRwkbGeometryType eGType, const std::string &osKeyIn,
474
                         const std::string &osDescIn)
475
0
    : osResourceId("-1"), poDS(poDSIn), bFetchedPermissions(false),
476
0
      nFeatureCount(0), oNextPos(moFeatures.begin()), nPageStart(0),
477
0
      bNeedSyncData(false), bNeedSyncStructure(false),
478
0
      bClientSideAttributeFilter(false)
479
0
{
480
0
    poFeatureDefn = new OGRFeatureDefn(osNameIn.c_str());
481
0
    poFeatureDefn->Reference();
482
483
0
    poFeatureDefn->SetGeomType(eGType);
484
485
0
    if (poSpatialRef)
486
0
    {
487
0
        if (poFeatureDefn->GetGeomFieldCount() != 0)
488
0
        {
489
0
            poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSpatialRef);
490
0
        }
491
0
    }
492
493
0
    if (!osDescIn.empty())
494
0
    {
495
0
        OGRLayer::SetMetadataItem("description", osDescIn.c_str());
496
0
    }
497
0
    if (!osKeyIn.empty())
498
0
    {
499
0
        OGRLayer::SetMetadataItem("keyname", osKeyIn.c_str());
500
0
    }
501
502
0
    SetDescription(poFeatureDefn->GetName());
503
504
0
    stPermissions.bDatastructCanRead = true;
505
0
    stPermissions.bDatastructCanWrite = true;
506
0
    stPermissions.bDataCanRead = true;
507
0
    stPermissions.bDataCanWrite = true;
508
0
    stPermissions.bMetadataCanRead = true;
509
0
    stPermissions.bMetadataCanWrite = true;
510
0
}
511
512
/*
513
 * ~OGRNGWLayer()
514
 */
515
OGRNGWLayer::~OGRNGWLayer()
516
0
{
517
0
    FreeFeaturesCache(true);
518
0
    if (poFeatureDefn != nullptr)
519
0
    {
520
0
        poFeatureDefn->Release();
521
0
    }
522
0
}
523
524
/*
525
 * FreeFeaturesCache()
526
 */
527
void OGRNGWLayer::FreeFeaturesCache(bool bForce)
528
0
{
529
0
    if (!soChangedIds.empty())
530
0
    {
531
0
        bNeedSyncData = true;
532
0
    }
533
534
0
    if (SyncFeatures() == OGRERR_NONE || bForce)  // Try sync first
535
0
    {
536
        // Free only if synced with server successfully or executed from
537
        // destructor.
538
0
        FreeMap(moFeatures);
539
0
    }
540
0
}
541
542
/*
543
 * GetResourceId()
544
 */
545
std::string OGRNGWLayer::GetResourceId() const
546
0
{
547
0
    return osResourceId;
548
0
}
549
550
/*
551
 * Delete()
552
 */
553
bool OGRNGWLayer::Delete()
554
0
{
555
0
    if (osResourceId == "-1")
556
0
    {
557
0
        return true;
558
0
    }
559
560
    // Headers free in DeleteResource method.
561
0
    return NGWAPI::DeleteResource(poDS->GetUrl(), osResourceId,
562
0
                                  poDS->GetHeaders(false));
563
0
}
564
565
/*
566
 * Rename()
567
 */
568
OGRErr OGRNGWLayer::Rename(const char *pszNewName)
569
0
{
570
0
    bool bResult = true;
571
0
    if (osResourceId != "-1")
572
0
    {
573
0
        bResult = NGWAPI::RenameResource(poDS->GetUrl(), osResourceId,
574
0
                                         pszNewName, poDS->GetHeaders(false));
575
0
    }
576
0
    if (bResult)
577
0
    {
578
0
        poFeatureDefn->SetName(pszNewName);
579
0
        SetDescription(poFeatureDefn->GetName());
580
0
    }
581
0
    else
582
0
    {
583
0
        CPLError(CE_Failure, CPLE_AppDefined, "Rename layer to %s failed",
584
0
                 pszNewName);
585
0
    }
586
0
    return bResult ? OGRERR_NONE : OGRERR_FAILURE;
587
0
}
588
589
/*
590
 * ResetReading()
591
 */
592
void OGRNGWLayer::ResetReading()
593
0
{
594
0
    m_bEOF = false;
595
0
    SyncToDisk();
596
0
    FreeFeaturesCache();
597
0
    if (poDS->GetPageSize() > 0)
598
0
    {
599
0
        nPageStart = 0;
600
0
    }
601
0
    oNextPos = moFeatures.begin();
602
0
}
603
604
CPLJSONObject OGRNGWLayer::LoadUrl(const std::string &osUrl) const
605
0
{
606
0
    CPLErrorReset();
607
0
    auto aosHTTPOptions = poDS->GetHeaders(false);
608
0
    CPLJSONDocument oFeatureReq;
609
610
0
    bool bResult = oFeatureReq.LoadUrl(osUrl, aosHTTPOptions);
611
0
    CPLJSONObject oRoot = oFeatureReq.GetRoot();
612
0
    if (NGWAPI::CheckRequestResult(bResult, oRoot,
613
0
                                   "GetFeatures request failed"))
614
0
    {
615
0
        CPLErrorReset();  // If we are here no error occurred
616
0
        return oRoot;
617
0
    }
618
619
0
    return CPLJSONObject();
620
0
}
621
622
/*
623
 * FillFeatures()
624
 */
625
bool OGRNGWLayer::FillFeatures(const std::string &osUrl)
626
0
{
627
0
    CPLDebug("NGW", "GetNextFeature: Url: %s", osUrl.c_str());
628
629
0
    CPLJSONObject oRoot = LoadUrl(osUrl);
630
0
    if (!oRoot.IsValid())
631
0
    {
632
0
        return false;
633
0
    }
634
635
0
    CPLJSONArray aoJSONFeatures = oRoot.ToArray();
636
0
    for (int i = 0; i < aoJSONFeatures.Size(); ++i)
637
0
    {
638
0
        OGRFeature *poFeature = JSONToFeature(aoJSONFeatures[i], poFeatureDefn,
639
0
                                              true, poDS->IsExtInNativeData());
640
0
        moFeatures[poFeature->GetFID()] = poFeature;
641
0
    }
642
643
0
    return true;
644
0
}
645
646
/*
647
 * SetNextByIndex()
648
 */
649
OGRErr OGRNGWLayer::SetNextByIndex(GIntBig nIndex)
650
0
{
651
0
    SyncToDisk();
652
0
    if (nIndex < 0)
653
0
    {
654
0
        CPLError(CE_Failure, CPLE_AppDefined,
655
0
                 "Feature index must be greater or equal 0. Got " CPL_FRMT_GIB,
656
0
                 nIndex);
657
0
        return OGRERR_NON_EXISTING_FEATURE;
658
0
    }
659
0
    if (poDS->GetPageSize() > 0)
660
0
    {
661
        // Check if index is in current cache
662
0
        if (nPageStart > nIndex && nIndex <= nPageStart - poDS->GetPageSize())
663
0
        {
664
0
            if (moFeatures.empty() ||
665
0
                static_cast<GIntBig>(moFeatures.size()) <= nIndex)
666
0
            {
667
0
                oNextPos = moFeatures.end();
668
0
            }
669
0
            else
670
0
            {
671
0
                oNextPos = moFeatures.begin();
672
0
                std::advance(oNextPos, static_cast<size_t>(nIndex));
673
0
            }
674
0
        }
675
0
        else
676
0
        {
677
0
            ResetReading();
678
0
            nPageStart = nIndex;
679
0
        }
680
0
    }
681
0
    else
682
0
    {
683
0
        if (moFeatures.empty() && GetMaxFeatureCount(false) > 0)
684
0
        {
685
0
            std::string osUrl;
686
0
            if (poDS->HasFeaturePaging())
687
0
            {
688
0
                osUrl = NGWAPI::GetFeaturePageURL(
689
0
                    poDS->GetUrl(), osResourceId, 0, 0, osFields, osWhere,
690
0
                    osSpatialFilter, poDS->Extensions(),
691
0
                    poFeatureDefn->IsGeometryIgnored() == TRUE);
692
0
            }
693
0
            else
694
0
            {
695
0
                osUrl = NGWAPI::GetFeatureURL(poDS->GetUrl(), osResourceId);
696
0
            }
697
698
0
            if (!FillFeatures(osUrl))
699
0
            {
700
0
                return OGRERR_FAILURE;
701
0
            }
702
0
        }
703
704
0
        if (moFeatures.empty() ||
705
0
            static_cast<GIntBig>(moFeatures.size()) <= nIndex)
706
0
        {
707
0
            oNextPos = moFeatures.end();
708
0
        }
709
0
        else
710
0
        {
711
0
            oNextPos = moFeatures.begin();
712
0
            std::advance(oNextPos, static_cast<size_t>(nIndex));
713
0
        }
714
0
    }
715
0
    return OGRERR_NONE;
716
0
}
717
718
/*
719
 * GetNextFeature()
720
 */
721
OGRFeature *OGRNGWLayer::GetNextFeature()
722
0
{
723
0
    if (m_bEOF)
724
0
        return nullptr;
725
0
    std::string osUrl;
726
727
0
    if (poDS->GetPageSize() > 0)
728
0
    {
729
0
        if (oNextPos == moFeatures.end() &&
730
0
            nPageStart < GetMaxFeatureCount(false))
731
0
        {
732
0
            FreeFeaturesCache();
733
734
0
            osUrl = NGWAPI::GetFeaturePageURL(
735
0
                poDS->GetUrl(), osResourceId, nPageStart, poDS->GetPageSize(),
736
0
                osFields, osWhere, osSpatialFilter, poDS->Extensions(),
737
0
                poFeatureDefn->IsGeometryIgnored() == TRUE);
738
0
            nPageStart += poDS->GetPageSize();
739
0
        }
740
0
    }
741
0
    else if (moFeatures.empty() && GetMaxFeatureCount(false) > 0)
742
0
    {
743
0
        if (poDS->HasFeaturePaging())
744
0
        {
745
0
            osUrl = NGWAPI::GetFeaturePageURL(
746
0
                poDS->GetUrl(), osResourceId, 0, 0, osFields, osWhere,
747
0
                osSpatialFilter, poDS->Extensions(),
748
0
                poFeatureDefn->IsGeometryIgnored() == TRUE);
749
0
        }
750
0
        else
751
0
        {
752
0
            osUrl = NGWAPI::GetFeatureURL(poDS->GetUrl(), osResourceId);
753
0
        }
754
0
    }
755
756
0
    bool bFinalRead = true;
757
0
    if (!osUrl.empty())
758
0
    {
759
0
        if (!FillFeatures(osUrl))
760
0
        {
761
0
            return nullptr;
762
0
        }
763
764
0
        oNextPos = moFeatures.begin();
765
766
0
        if (poDS->GetPageSize() < 1)
767
0
        {
768
            // Without paging we read all features at once.
769
0
            m_nFeaturesRead = moFeatures.size();
770
0
        }
771
0
        else
772
0
        {
773
0
            if (poDS->GetPageSize() - moFeatures.size() == 0)
774
0
            {
775
0
                m_nFeaturesRead = nPageStart;
776
0
                bFinalRead = false;
777
0
            }
778
0
            else
779
0
            {
780
0
                m_nFeaturesRead =
781
0
                    nPageStart - poDS->GetPageSize() + moFeatures.size();
782
0
            }
783
0
        }
784
0
    }
785
786
0
    while (oNextPos != moFeatures.end())
787
0
    {
788
0
        OGRFeature *poFeature = oNextPos->second;
789
0
        ++oNextPos;
790
791
0
        if (poFeature == nullptr)  // Feature may be deleted.
792
0
        {
793
0
            continue;
794
0
        }
795
796
        // Check local filters only for new features which not send to server
797
        // yet or if attribute filter process on client side.
798
0
        if (poFeature->GetFID() < 0 || bClientSideAttributeFilter)
799
0
        {
800
0
            if ((m_poFilterGeom == nullptr ||
801
0
                 FilterGeometry(poFeature->GetGeometryRef())) &&
802
0
                (m_poAttrQuery == nullptr ||
803
0
                 m_poAttrQuery->Evaluate(poFeature)))
804
0
            {
805
0
                return poFeature->Clone();
806
0
            }
807
0
        }
808
0
        else
809
0
        {
810
0
            return poFeature->Clone();
811
0
        }
812
0
    }
813
814
0
    if (poDS->GetPageSize() > 0 && !bFinalRead)
815
0
    {
816
0
        return GetNextFeature();
817
0
    }
818
0
    return nullptr;
819
0
}
820
821
/*
822
 * GetFeature()
823
 */
824
OGRFeature *OGRNGWLayer::GetFeature(GIntBig nFID)
825
0
{
826
    // Check feature in cache.
827
0
    if (moFeatures[nFID] != nullptr)
828
0
    {
829
0
        return moFeatures[nFID]->Clone();
830
0
    }
831
0
    auto osUrl = NGWAPI::GetFeatureURL(poDS->GetUrl(), osResourceId) +
832
0
                 std::to_string(nFID);
833
0
    CPLJSONObject oRoot = LoadUrl(osUrl);
834
0
    if (!oRoot.IsValid())
835
0
    {
836
0
        return nullptr;
837
0
    }
838
839
    // Don't store feature in cache. This can broke sequence read.
840
0
    return JSONToFeature(oRoot, poFeatureDefn, true, poDS->IsExtInNativeData());
841
0
}
842
843
/*
844
 * GetLayerDefn()
845
 */
846
const OGRFeatureDefn *OGRNGWLayer::GetLayerDefn() const
847
0
{
848
0
    return poFeatureDefn;
849
0
}
850
851
/*
852
 * TestCapability()
853
 */
854
int OGRNGWLayer::TestCapability(const char *pszCap) const
855
0
{
856
0
    const_cast<OGRNGWLayer *>(this)->FetchPermissions();
857
0
    if (EQUAL(pszCap, OLCRandomRead))
858
0
        return TRUE;
859
0
    else if (EQUAL(pszCap, OLCSequentialWrite))
860
0
        return stPermissions.bDataCanWrite && poDS->IsUpdateMode();
861
0
    else if (EQUAL(pszCap, OLCRandomWrite))
862
0
        return stPermissions.bDataCanWrite && poDS->IsUpdateMode();
863
0
    else if (EQUAL(pszCap, OLCFastFeatureCount))
864
0
        return m_poFilterGeom == nullptr && m_poAttrQuery == nullptr;
865
0
    else if (EQUAL(pszCap, OLCFastGetExtent))
866
0
        return TRUE;
867
0
    else if (EQUAL(pszCap, OLCAlterFieldDefn))
868
0
        return stPermissions.bDatastructCanWrite && poDS->IsUpdateMode();
869
0
    else if (EQUAL(pszCap, OLCDeleteFeature))
870
0
        return stPermissions.bDataCanWrite && poDS->IsUpdateMode();
871
0
    else if (EQUAL(pszCap, OLCStringsAsUTF8))
872
0
        return TRUE;
873
0
    else if (EQUAL(pszCap, OLCFastSetNextByIndex))
874
0
        return TRUE;
875
0
    else if (EQUAL(pszCap, OLCCreateField))
876
0
        return stPermissions.bDatastructCanWrite && poDS->IsUpdateMode();
877
0
    else if (EQUAL(pszCap, OLCIgnoreFields))
878
0
        return poDS->HasFeaturePaging();  // Ignore fields, paging support and
879
                                          // attribute/spatial filters were
880
                                          // introduced in NGW v3.1
881
0
    else if (EQUAL(pszCap, OLCFastSpatialFilter))
882
0
        return poDS->HasFeaturePaging();
883
0
    else if (EQUAL(pszCap, OLCRename))
884
0
        return poDS->IsUpdateMode();
885
0
    else if (EQUAL(pszCap, OLCZGeometries))
886
0
        return TRUE;
887
0
    return FALSE;
888
0
}
889
890
/*
891
 * Fill()
892
 */
893
void OGRNGWLayer::Fill(const CPLJSONObject &oRootObject)
894
0
{
895
0
    auto osName = oRootObject.GetString("resource/display_name");
896
0
    CPLStringList soIgnoredFieldsNames;
897
0
    if (nullptr != poFeatureDefn)
898
0
    {
899
0
        while (poFeatureDefn->GetFieldCount() > 0)
900
0
        {
901
0
            OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(0);
902
0
            if (poFieldDefn->IsIgnored())
903
0
            {
904
0
                soIgnoredFieldsNames.AddString(poFieldDefn->GetNameRef());
905
0
            }
906
0
            poFeatureDefn->DeleteFieldDefn(0);
907
0
        }
908
0
        poFeatureDefn->SetName(osName.c_str());
909
910
0
        poFeatureDefn->SetGeomType(NGWAPI::NGWGeomTypeToOGRGeomType(
911
0
            oRootObject.GetString("vector_layer/geometry_type")));
912
0
    }
913
0
    else
914
0
    {
915
0
        poFeatureDefn = new OGRFeatureDefn(osName.c_str());
916
0
        poFeatureDefn->Reference();
917
918
0
        poFeatureDefn->SetGeomType(NGWAPI::NGWGeomTypeToOGRGeomType(
919
0
            oRootObject.GetString("vector_layer/geometry_type")));
920
921
0
        OGRSpatialReference *poSRS = new OGRSpatialReference;
922
0
        poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
923
0
        int nEPSG = oRootObject.GetInteger(
924
0
            "vector_layer/srs/id",
925
0
            3857);  // Default NGW SRS is Web mercator EPSG:3857.
926
0
        if (poSRS->importFromEPSG(nEPSG) == OGRERR_NONE)
927
0
        {
928
0
            if (poFeatureDefn->GetGeomFieldCount() != 0)
929
0
            {
930
0
                poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
931
0
            }
932
0
        }
933
0
        poSRS->Release();
934
0
    }
935
936
0
    CPLJSONArray oFields = oRootObject.GetArray("feature_layer/fields");
937
0
    FillFields(oFields, soIgnoredFieldsNames);
938
0
    FillMetadata(oRootObject);
939
940
0
    auto osDescription = oRootObject.GetString("resource/description");
941
0
    SetDescription(osDescription.c_str());
942
    // SetDescription(poFeatureDefn->GetName());
943
0
}
944
945
/*
946
 * FillMetadata()
947
 */
948
void OGRNGWLayer::FillMetadata(const CPLJSONObject &oRootObject)
949
0
{
950
0
    std::string osCreateDate = oRootObject.GetString("resource/creation_date");
951
0
    if (!osCreateDate.empty())
952
0
    {
953
0
        OGRLayer::SetMetadataItem("creation_date", osCreateDate.c_str());
954
0
    }
955
0
    std::string osDescription = oRootObject.GetString("resource/description");
956
0
    if (!osDescription.empty())
957
0
    {
958
0
        OGRLayer::SetMetadataItem("description", osDescription.c_str());
959
0
    }
960
0
    std::string osKeyName = oRootObject.GetString("resource/keyname");
961
0
    if (!osKeyName.empty())
962
0
    {
963
0
        OGRLayer::SetMetadataItem("keyname", osKeyName.c_str());
964
0
    }
965
0
    std::string osResourceType = oRootObject.GetString("resource/cls");
966
0
    if (!osResourceType.empty())
967
0
    {
968
0
        OGRLayer::SetMetadataItem("resource_type", osResourceType.c_str());
969
0
    }
970
0
    std::string osResourceParentId =
971
0
        oRootObject.GetString("resource/parent/id");
972
0
    if (!osResourceParentId.empty())
973
0
    {
974
0
        OGRLayer::SetMetadataItem("parent_id", osResourceParentId.c_str());
975
0
    }
976
0
    OGRLayer::SetMetadataItem("id", osResourceId.c_str());
977
978
0
    std::vector<CPLJSONObject> items =
979
0
        oRootObject.GetObj("resmeta/items").GetChildren();
980
981
0
    for (const CPLJSONObject &item : items)
982
0
    {
983
0
        std::string osSuffix = NGWAPI::GetResmetaSuffix(item.GetType());
984
0
        OGRLayer::SetMetadataItem((item.GetName() + osSuffix).c_str(),
985
0
                                  item.ToString().c_str(), "NGW");
986
0
    }
987
0
}
988
989
/*
990
 * FillFields()
991
 */
992
void OGRNGWLayer::FillFields(const CPLJSONArray &oFields,
993
                             const CPLStringList &soIgnoredFieldsNames)
994
0
{
995
0
    for (const auto &oField : oFields)
996
0
    {
997
0
        std::string osFieldName = oField.GetString("keyname");
998
0
        OGRFieldType eFieldtype =
999
0
            NGWAPI::NGWFieldTypeToOGRFieldType(oField.GetString("datatype"));
1000
1001
0
        OGRFieldDefn oFieldDefn(osFieldName.c_str(), eFieldtype);
1002
0
        std::string osFieldAlias = oField.GetString("display_name");
1003
0
        oFieldDefn.SetAlternativeName(osFieldAlias.c_str());
1004
1005
        // Add additional parameters to comment as JSON string
1006
0
        CPLJSONObject oFieldComment;
1007
0
        oFieldComment.Add("id", oField.GetLong("id"));
1008
0
        oFieldComment.Add("label_field", oField.GetBool("label_field"));
1009
0
        oFieldComment.Add("grid_visibility", oField.GetBool("grid_visibility"));
1010
0
        oFieldComment.Add("text_search", oField.GetBool("text_search"));
1011
0
        oFieldDefn.SetComment(
1012
0
            oFieldComment.Format(CPLJSONObject::PrettyFormat::Plain));
1013
1014
        // Domain
1015
0
        auto nDomainID = oField.GetInteger("lookup_table/id", -1);
1016
0
        if (nDomainID != -1)
1017
0
        {
1018
0
            auto oDom = poDS->GetDomainByID(nDomainID);
1019
0
            auto pOgrDom = oDom.ToFieldDomain(eFieldtype);
1020
0
            if (pOgrDom != nullptr)
1021
0
            {
1022
0
                oFieldDefn.SetDomainName(pOgrDom->GetName());
1023
0
            }
1024
0
        }
1025
1026
0
        poFeatureDefn->AddFieldDefn(&oFieldDefn);
1027
0
    }
1028
1029
0
    OGRLayer::SetIgnoredFields(soIgnoredFieldsNames);
1030
0
}
1031
1032
/*
1033
 * GetMaxFeatureCount()
1034
 */
1035
GIntBig OGRNGWLayer::GetMaxFeatureCount(bool bForce)
1036
0
{
1037
0
    if (nFeatureCount < 0 || bForce)
1038
0
    {
1039
0
        CPLJSONObject oRoot =
1040
0
            LoadUrl(NGWAPI::GetFeatureCount(poDS->GetUrl(), osResourceId));
1041
0
        if (!oRoot.IsValid())
1042
0
        {
1043
0
            return nFeatureCount;
1044
0
        }
1045
1046
0
        nFeatureCount = oRoot.GetLong("total_count");
1047
0
        nFeatureCount += GetNewFeaturesCount();
1048
0
    }
1049
0
    return nFeatureCount;
1050
0
}
1051
1052
/*
1053
 * GetFeatureCount()
1054
 */
1055
GIntBig OGRNGWLayer::GetFeatureCount(int bForce)
1056
0
{
1057
0
    if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
1058
0
    {
1059
0
        return GetMaxFeatureCount(CPL_TO_BOOL(bForce));
1060
0
    }
1061
0
    else
1062
0
    {
1063
0
        return OGRLayer::GetFeatureCount(bForce);
1064
0
    }
1065
0
}
1066
1067
/*
1068
 * IGetExtent()
1069
 */
1070
OGRErr OGRNGWLayer::IGetExtent(int /* iGeomField */, OGREnvelope *psExtent,
1071
                               bool bForce)
1072
0
{
1073
0
    if (!stExtent.IsInit() || CPL_TO_BOOL(bForce))
1074
0
    {
1075
0
        auto aosHTTPOptions = poDS->GetHeaders(false);
1076
0
        bool bResult = NGWAPI::GetExtent(poDS->GetUrl(), osResourceId,
1077
0
                                         aosHTTPOptions, 3857, stExtent);
1078
0
        if (!bResult)
1079
0
        {
1080
0
            return OGRERR_FAILURE;
1081
0
        }
1082
0
    }
1083
0
    *psExtent = stExtent;
1084
0
    return OGRERR_NONE;
1085
0
}
1086
1087
/*
1088
 * FetchPermissions()
1089
 */
1090
void OGRNGWLayer::FetchPermissions()
1091
0
{
1092
0
    if (bFetchedPermissions || osResourceId == "-1")
1093
0
    {
1094
0
        return;
1095
0
    }
1096
1097
0
    if (poDS->IsUpdateMode())
1098
0
    {
1099
0
        auto aosHTTPOptions = poDS->GetHeaders(false);
1100
0
        stPermissions = NGWAPI::CheckPermissions(
1101
0
            poDS->GetUrl(), osResourceId, aosHTTPOptions, poDS->IsUpdateMode());
1102
0
    }
1103
0
    else
1104
0
    {
1105
0
        stPermissions.bDataCanRead = true;
1106
0
        stPermissions.bResourceCanRead = true;
1107
0
        stPermissions.bDatastructCanRead = true;
1108
0
        stPermissions.bMetadataCanRead = true;
1109
0
    }
1110
0
    bFetchedPermissions = true;
1111
0
}
1112
1113
/*
1114
 * CreateField()
1115
 */
1116
OGRErr OGRNGWLayer::CreateField(const OGRFieldDefn *poField,
1117
                                CPL_UNUSED int bApproxOK)
1118
0
{
1119
0
    CPLAssert(nullptr != poField);
1120
1121
0
    if (!CheckFieldNameUnique(poFeatureDefn, -1, poField->GetNameRef()))
1122
0
    {
1123
0
        return OGRERR_FAILURE;
1124
0
    }
1125
1126
0
    OGRFieldDefn oModFieldDefn(poField);
1127
0
    poFeatureDefn->AddFieldDefn(&oModFieldDefn);
1128
0
    bNeedSyncStructure = true;
1129
0
    return OGRERR_NONE;
1130
0
}
1131
1132
/*
1133
 * DeleteField()
1134
 */
1135
OGRErr OGRNGWLayer::DeleteField(int iField)
1136
0
{
1137
0
    if (osResourceId != "-1")
1138
0
    {
1139
0
        auto poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
1140
0
        if (poFieldDefn == nullptr)
1141
0
        {
1142
0
            return OGRERR_FAILURE;
1143
0
        }
1144
        // Get field NGW ID
1145
0
        CPLJSONDocument oComment;
1146
0
        if (oComment.LoadMemory(poFieldDefn->GetComment()))
1147
0
        {
1148
0
            auto oRoot = oComment.GetRoot();
1149
0
            auto nNGWID = oRoot.GetLong("id", -1);
1150
0
            if (nNGWID != -1)
1151
0
            {
1152
0
                soDeletedFieldsIds.insert(nNGWID);
1153
0
            }
1154
0
        }
1155
0
    }
1156
0
    return poFeatureDefn->DeleteFieldDefn(iField);
1157
0
}
1158
1159
/*
1160
 * ReorderFields()
1161
 */
1162
OGRErr OGRNGWLayer::ReorderFields(int *panMap)
1163
0
{
1164
0
    return poFeatureDefn->ReorderFieldDefns(panMap);
1165
0
}
1166
1167
/*
1168
 * AlterFieldDefn()
1169
 */
1170
OGRErr OGRNGWLayer::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
1171
                                   int nFlagsIn)
1172
0
{
1173
0
    CPLDebug("NGW", "AlterFieldDefn fld #%d", iField);
1174
0
    CPLAssert(nullptr != poNewFieldDefn);
1175
1176
0
    if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
1177
0
    {
1178
0
        CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
1179
0
        return OGRERR_FAILURE;
1180
0
    }
1181
1182
0
    FetchPermissions();
1183
0
    if (!stPermissions.bDatastructCanWrite)
1184
0
    {
1185
0
        CPLError(CE_Failure, CPLE_NotSupported, "Insufficient permissions");
1186
0
        return OGRERR_FAILURE;
1187
0
    }
1188
1189
0
    if (!poDS->IsUpdateMode())
1190
0
    {
1191
0
        CPLError(CE_Failure, CPLE_NotSupported, "Layer is read only");
1192
0
        return OGRERR_FAILURE;
1193
0
    }
1194
1195
0
    OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
1196
    // Check new field name is not equal for another fields.
1197
0
    if (!CheckFieldNameUnique(poFeatureDefn, iField,
1198
0
                              poNewFieldDefn->GetNameRef()))
1199
0
    {
1200
0
        return OGRERR_FAILURE;
1201
0
    }
1202
1203
0
    if (osResourceId == "-1")  // Can full alter field only on new layers
1204
                               // (not synced with server).
1205
0
    {
1206
        // Field name 'id' forbidden.
1207
0
        OGRFieldDefn oModFieldDefn(poNewFieldDefn);
1208
1209
0
        poFieldDefn->SetName(oModFieldDefn.GetNameRef());
1210
0
        poFieldDefn->SetAlternativeName(oModFieldDefn.GetAlternativeNameRef());
1211
0
        poFieldDefn->SetComment(oModFieldDefn.GetComment());
1212
0
        poFieldDefn->SetType(oModFieldDefn.GetType());
1213
0
        poFieldDefn->SetSubType(oModFieldDefn.GetSubType());
1214
0
        poFieldDefn->SetWidth(oModFieldDefn.GetWidth());
1215
0
        poFieldDefn->SetPrecision(oModFieldDefn.GetPrecision());
1216
0
    }
1217
0
    else
1218
0
    {
1219
0
        if (nFlagsIn & ALTER_NAME_FLAG)
1220
0
        {
1221
0
            bNeedSyncStructure = true;
1222
0
            poFieldDefn->SetName(poNewFieldDefn->GetNameRef());
1223
0
        }
1224
0
        if (nFlagsIn & ALTER_DOMAIN_FLAG)
1225
0
        {
1226
0
            bNeedSyncStructure = true;
1227
0
            poFieldDefn->SetDomainName(poNewFieldDefn->GetDomainName());
1228
0
        }
1229
0
        if (nFlagsIn & ALTER_ALTERNATIVE_NAME_FLAG)
1230
0
        {
1231
0
            bNeedSyncStructure = true;
1232
0
            poFieldDefn->SetAlternativeName(
1233
0
                poNewFieldDefn->GetAlternativeNameRef());
1234
0
        }
1235
0
        if (nFlagsIn & ALTER_COMMENT_FLAG)
1236
0
        {
1237
0
            bNeedSyncStructure = true;
1238
0
            poFieldDefn->SetComment(poNewFieldDefn->GetComment());
1239
0
        }
1240
0
    }
1241
0
    ResetReading();
1242
0
    return OGRERR_NONE;
1243
0
}
1244
1245
/*
1246
 * SetMetadata()
1247
 */
1248
CPLErr OGRNGWLayer::SetMetadata(char **papszMetadata, const char *pszDomain)
1249
0
{
1250
0
    bNeedSyncStructure = true;
1251
0
    return OGRLayer::SetMetadata(papszMetadata, pszDomain);
1252
0
}
1253
1254
/*
1255
 * SetMetadataItem()
1256
 */
1257
CPLErr OGRNGWLayer::SetMetadataItem(const char *pszName, const char *pszValue,
1258
                                    const char *pszDomain)
1259
0
{
1260
0
    bNeedSyncStructure = true;
1261
0
    return OGRLayer::SetMetadataItem(pszName, pszValue, pszDomain);
1262
0
}
1263
1264
/*
1265
 * CreateNGWResourceJson()
1266
 */
1267
std::string OGRNGWLayer::CreateNGWResourceJson()
1268
0
{
1269
0
    CPLJSONObject oResourceJson;
1270
1271
    // Add resource json item.
1272
0
    CPLJSONObject oResource("resource", oResourceJson);
1273
0
    oResource.Add("cls", "vector_layer");
1274
0
    CPLJSONObject oResourceParent("parent", oResource);
1275
0
    oResourceParent.Add("id",
1276
0
                        static_cast<GIntBig>(std::stol(poDS->GetResourceId())));
1277
0
    oResource.Add("display_name", GetName());
1278
0
    const char *pszKeyName = GetMetadataItem("keyname");
1279
0
    if (pszKeyName)
1280
0
    {
1281
0
        oResource.Add("keyname", pszKeyName);
1282
0
    }
1283
0
    const char *pszDescription = GetMetadataItem("description");
1284
0
    if (pszDescription)
1285
0
    {
1286
0
        oResource.Add("description", pszDescription);
1287
0
    }
1288
1289
    // Add vector_layer json item.
1290
0
    CPLJSONObject oVectorLayer("vector_layer", oResourceJson);
1291
0
    CPLJSONObject oVectorLayerSrs("srs", oVectorLayer);
1292
1293
0
    int nEPSG = 3857;
1294
0
    if (const auto poSRSConst = GetSpatialRef())
1295
0
    {
1296
0
        OGRSpatialReference oSRS(*poSRSConst);
1297
0
        oSRS.AutoIdentifyEPSG();
1298
0
        const char *pszEPSG = oSRS.GetAuthorityCode(nullptr);
1299
0
        if (pszEPSG != nullptr)
1300
0
        {
1301
0
            nEPSG = atoi(pszEPSG);
1302
0
        }
1303
0
    }
1304
0
    oVectorLayerSrs.Add("id", nEPSG);
1305
    // In OGRNGWDataset::ICreateLayer we limit supported geometry types.
1306
0
    oVectorLayer.Add("geometry_type",
1307
0
                     NGWAPI::OGRGeomTypeToNGWGeomType(GetGeomType()));
1308
1309
    // Fill fields
1310
0
    CPLJSONArray oVectorLayerFields;
1311
0
    for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
1312
0
    {
1313
0
        OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
1314
0
        CPLJSONObject oField;
1315
0
        auto const &sComment = poFieldDefn->GetComment();
1316
0
        if (!sComment.empty())
1317
0
        {
1318
0
            CPLJSONDocument oComment;
1319
0
            if (oComment.LoadMemory(sComment))
1320
0
            {
1321
0
                oField = oComment.GetRoot();
1322
0
            }
1323
0
        }
1324
0
        oField.Add("keyname", poFieldDefn->GetNameRef());
1325
0
        oField.Add("datatype",
1326
0
                   NGWAPI::OGRFieldTypeToNGWFieldType(poFieldDefn->GetType()));
1327
0
        std::string osFieldAliasName = poFieldDefn->GetAlternativeNameRef();
1328
        // Get alias from metadata.
1329
0
        if (osFieldAliasName.empty())
1330
0
        {
1331
0
            osFieldAliasName = poFieldDefn->GetNameRef();
1332
0
        }
1333
0
        oField.Add("display_name", osFieldAliasName);
1334
1335
0
        if (poFieldDefn->GetDomainName().empty())
1336
0
        {
1337
0
            oField.AddNull("lookup_table");
1338
0
        }
1339
0
        else
1340
0
        {
1341
0
            CPLJSONObject oFieldDom("lookup_table", oField);
1342
0
            auto nDomId = poDS->GetDomainIdByName(poFieldDefn->GetDomainName());
1343
0
            oFieldDom.Add("id", nDomId);
1344
0
        }
1345
1346
0
        oVectorLayerFields.Add(oField);
1347
0
    }
1348
0
    for (auto nFieldID : soDeletedFieldsIds)
1349
0
    {
1350
0
        CPLJSONObject oField;
1351
0
        oField.Add("id", nFieldID);
1352
0
        oField.Add("delete", true);
1353
0
        oVectorLayerFields.Add(oField);
1354
0
    }
1355
1356
0
    CPLJSONObject oFeatureLayer("feature_layer", oResourceJson);
1357
0
    oFeatureLayer.Add("fields", oVectorLayerFields);
1358
1359
    // Add resmeta json item.
1360
0
    NGWAPI::FillResmeta(oResourceJson, GetMetadata("NGW"));
1361
1362
0
    return oResourceJson.Format(CPLJSONObject::PrettyFormat::Plain);
1363
0
}
1364
1365
/*
1366
 * SyncFeatures()
1367
 */
1368
OGRErr OGRNGWLayer::SyncFeatures()
1369
0
{
1370
0
    if (!bNeedSyncData)
1371
0
    {
1372
0
        return OGRERR_NONE;
1373
0
    }
1374
1375
0
    CPLJSONArray oFeatureJsonArray;
1376
0
    std::vector<GIntBig> aoPatchedFIDs;
1377
0
    for (GIntBig nFID : soChangedIds)
1378
0
    {
1379
0
        if (moFeatures[nFID] != nullptr)
1380
0
        {
1381
0
            oFeatureJsonArray.Add(FeatureToJson(moFeatures[nFID]));
1382
0
            aoPatchedFIDs.push_back(nFID);
1383
0
        }
1384
0
    }
1385
1386
0
    if (!aoPatchedFIDs.empty())
1387
0
    {
1388
0
        auto osIDs = NGWAPI::PatchFeatures(
1389
0
            poDS->GetUrl(), osResourceId,
1390
0
            oFeatureJsonArray.Format(CPLJSONObject::PrettyFormat::Plain),
1391
0
            poDS->GetHeaders());
1392
0
        if (!osIDs.empty())
1393
0
        {
1394
0
            bNeedSyncData = false;
1395
0
            nFeatureCount += GetNewFeaturesCount();
1396
0
            soChangedIds.clear();
1397
0
            if (osIDs.size() !=
1398
0
                aoPatchedFIDs.size())  // Expected equal identifier count.
1399
0
            {
1400
0
                CPLDebug("NGW", "Patched feature count is not equal. Reload "
1401
0
                                "features from server.");
1402
0
                FreeMap(moFeatures);
1403
0
            }
1404
0
            else  // Just update identifiers.
1405
0
            {
1406
0
                int nCounter = 0;
1407
0
                for (GIntBig nFID : aoPatchedFIDs)
1408
0
                {
1409
0
                    GIntBig nNewFID = osIDs[nCounter++];
1410
0
                    OGRFeature *poFeature = moFeatures[nFID];
1411
0
                    poFeature->SetFID(nNewFID);
1412
0
                    moFeatures.erase(nFID);
1413
0
                    moFeatures[nNewFID] = poFeature;
1414
0
                }
1415
0
            }
1416
0
        }
1417
0
        else
1418
0
        {
1419
            // Error message should set in NGWAPI::PatchFeatures function.
1420
0
            if (CPLGetLastErrorNo() != 0)
1421
0
            {
1422
0
                return OGRERR_FAILURE;
1423
0
            }
1424
0
        }
1425
0
    }
1426
0
    return OGRERR_NONE;
1427
0
}
1428
1429
/*
1430
 * SyncToDisk()
1431
 */
1432
OGRErr OGRNGWLayer::SyncToDisk()
1433
0
{
1434
0
    if (osResourceId == "-1")  // Create vector layer at NextGIS Web.
1435
0
    {
1436
0
        bNeedSyncData = !moFeatures.empty();
1437
0
        std::string osResourceIdInt = NGWAPI::CreateResource(
1438
0
            poDS->GetUrl(), CreateNGWResourceJson(), poDS->GetHeaders());
1439
0
        if (osResourceIdInt == "-1")
1440
0
        {
1441
            // Error message should set in CreateResource.
1442
0
            return OGRERR_FAILURE;
1443
0
        }
1444
0
        osResourceId = std::move(osResourceIdInt);
1445
0
        OGRLayer::SetMetadataItem("id", osResourceId.c_str());
1446
0
        FetchPermissions();
1447
0
        bNeedSyncStructure = false;
1448
0
    }
1449
0
    else if (bNeedSyncStructure)  // Update vector layer at NextGIS Web.
1450
0
    {
1451
0
        if (!NGWAPI::UpdateResource(poDS->GetUrl(), GetResourceId(),
1452
0
                                    CreateNGWResourceJson(),
1453
0
                                    poDS->GetHeaders()))
1454
0
        {
1455
            // Error message should set in UpdateResource.
1456
0
            return OGRERR_FAILURE;
1457
0
        }
1458
0
        bNeedSyncStructure = false;
1459
0
        soDeletedFieldsIds.clear();
1460
0
    }
1461
1462
0
    auto oRoot = LoadUrl(NGWAPI::GetResourceURL(poDS->GetUrl(), osResourceId));
1463
0
    if (!oRoot.IsValid())
1464
0
    {
1465
0
        return OGRERR_FAILURE;
1466
0
    }
1467
0
    Fill(oRoot);
1468
1469
    // Update NGW layer state
1470
0
    FetchPermissions();
1471
1472
    // Sync features.
1473
0
    return SyncFeatures();
1474
0
}
1475
1476
/*
1477
 * DeleteFeature()
1478
 */
1479
OGRErr OGRNGWLayer::DeleteFeature(GIntBig nFID)
1480
0
{
1481
0
    CPLErrorReset();
1482
0
    if (nFID < 0)
1483
0
    {
1484
0
        if (moFeatures[nFID] != nullptr)
1485
0
        {
1486
0
            OGRFeature::DestroyFeature(moFeatures[nFID]);
1487
0
            moFeatures[nFID] = nullptr;
1488
0
            nFeatureCount--;
1489
0
            soChangedIds.erase(nFID);
1490
0
            return OGRERR_NONE;
1491
0
        }
1492
0
        CPLError(CE_Failure, CPLE_AppDefined,
1493
0
                 "Feature with id " CPL_FRMT_GIB " not found.", nFID);
1494
0
        return OGRERR_FAILURE;
1495
0
    }
1496
0
    else
1497
0
    {
1498
0
        FetchPermissions();
1499
0
        if (stPermissions.bDataCanWrite && poDS->IsUpdateMode())
1500
0
        {
1501
0
            bool bResult = NGWAPI::DeleteFeature(poDS->GetUrl(), osResourceId,
1502
0
                                                 std::to_string(nFID),
1503
0
                                                 poDS->GetHeaders(false));
1504
0
            if (bResult)
1505
0
            {
1506
0
                if (moFeatures[nFID] != nullptr)
1507
0
                {
1508
0
                    OGRFeature::DestroyFeature(moFeatures[nFID]);
1509
0
                    moFeatures[nFID] = nullptr;
1510
0
                }
1511
0
                nFeatureCount--;
1512
0
                soChangedIds.erase(nFID);
1513
0
                return OGRERR_NONE;
1514
0
            }
1515
0
            return OGRERR_FAILURE;
1516
0
        }
1517
0
        CPLError(CE_Failure, CPLE_AppDefined,
1518
0
                 "Delete feature " CPL_FRMT_GIB " operation is not permitted.",
1519
0
                 nFID);
1520
0
        return OGRERR_FAILURE;
1521
0
    }
1522
0
}
1523
1524
/*
1525
 * DeleteFeatures()
1526
 */
1527
OGRErr OGRNGWLayer::DeleteFeatures(const std::vector<GIntBig> &vFeaturesID)
1528
0
{
1529
0
    CPLErrorReset();
1530
1531
    // Try to delete local features (not synchronized with NGW)
1532
0
    std::vector<GIntBig> vFeaturesIDInt(vFeaturesID);
1533
0
    for (size_t i = 0; i < vFeaturesIDInt.size(); i++)
1534
0
    {
1535
0
        auto nFID = vFeaturesIDInt[i];
1536
0
        if (nFID < 0)
1537
0
        {
1538
0
            if (moFeatures[nFID] != nullptr)
1539
0
            {
1540
0
                OGRFeature::DestroyFeature(moFeatures[nFID]);
1541
0
                moFeatures[nFID] = nullptr;
1542
0
                nFeatureCount--;
1543
0
                soChangedIds.erase(nFID);
1544
0
                vFeaturesIDInt.erase(vFeaturesIDInt.begin() + i);
1545
0
            }
1546
0
        }
1547
0
    }
1548
1549
0
    FetchPermissions();
1550
0
    if (stPermissions.bDataCanWrite && poDS->IsUpdateMode())
1551
0
    {
1552
0
        bool bResult = NGWAPI::DeleteFeatures(
1553
0
            poDS->GetUrl(), osResourceId,
1554
0
            FeaturesIDToJsonString(vFeaturesIDInt), poDS->GetHeaders(false));
1555
0
        if (bResult)
1556
0
        {
1557
0
            for (GIntBig nFID : vFeaturesIDInt)
1558
0
            {
1559
0
                if (moFeatures[nFID] != nullptr)
1560
0
                {
1561
0
                    OGRFeature::DestroyFeature(moFeatures[nFID]);
1562
0
                    moFeatures[nFID] = nullptr;
1563
0
                    nFeatureCount--;
1564
0
                    soChangedIds.erase(nFID);
1565
0
                }
1566
0
                nFeatureCount--;
1567
0
                soChangedIds.erase(nFID);
1568
0
            }
1569
0
            return OGRERR_NONE;
1570
0
        }
1571
0
    }
1572
0
    CPLError(CE_Failure, CPLE_AppDefined, "Delete features failed");
1573
0
    return OGRERR_FAILURE;
1574
0
}
1575
1576
/*
1577
 * DeleteAllFeatures()
1578
 */
1579
bool OGRNGWLayer::DeleteAllFeatures()
1580
0
{
1581
0
    if (osResourceId == "-1")
1582
0
    {
1583
0
        soChangedIds.clear();
1584
0
        bNeedSyncData = false;
1585
0
        FreeFeaturesCache();
1586
0
        nFeatureCount = 0;
1587
0
        return true;
1588
0
    }
1589
0
    else
1590
0
    {
1591
0
        FetchPermissions();
1592
0
        if (stPermissions.bDataCanWrite && poDS->IsUpdateMode())
1593
0
        {
1594
0
            bool bResult = NGWAPI::DeleteFeature(poDS->GetUrl(), osResourceId,
1595
0
                                                 "", poDS->GetHeaders(false));
1596
0
            if (bResult)
1597
0
            {
1598
0
                soChangedIds.clear();
1599
0
                bNeedSyncData = false;
1600
0
                FreeFeaturesCache();
1601
0
                nFeatureCount = 0;
1602
0
            }
1603
0
            return bResult;
1604
0
        }
1605
0
    }
1606
0
    CPLErrorReset();
1607
0
    CPLError(CE_Failure, CPLE_AppDefined,
1608
0
             "Delete all features operation is not permitted.");
1609
0
    return false;
1610
0
}
1611
1612
/*
1613
 * ISetFeature()
1614
 */
1615
OGRErr OGRNGWLayer::ISetFeature(OGRFeature *poFeature)
1616
0
{
1617
0
    if (poDS->IsBatchMode())
1618
0
    {
1619
0
        if (moFeatures[poFeature->GetFID()] == nullptr)
1620
0
        {
1621
0
            if (poFeature->GetFID() < 0)
1622
0
            {
1623
0
                CPLError(CE_Failure, CPLE_AppDefined,
1624
0
                         "Cannot update not existing feature " CPL_FRMT_GIB,
1625
0
                         poFeature->GetFID());
1626
0
                return OGRERR_FAILURE;
1627
0
            }
1628
0
        }
1629
0
        else
1630
0
        {
1631
0
            OGRFeature::DestroyFeature(moFeatures[poFeature->GetFID()]);
1632
0
        }
1633
0
        moFeatures[poFeature->GetFID()] = poFeature->Clone();
1634
0
        soChangedIds.insert(poFeature->GetFID());
1635
1636
0
        if (soChangedIds.size() > static_cast<size_t>(poDS->GetBatchSize()))
1637
0
        {
1638
0
            bNeedSyncData = true;
1639
0
        }
1640
1641
0
        return SyncToDisk();
1642
0
    }
1643
0
    else
1644
0
    {
1645
0
        OGRErr eResult =
1646
0
            SyncToDisk();  // For create new layer if not yet created.
1647
0
        if (eResult == OGRERR_NONE)
1648
0
        {
1649
0
            if (poFeature->GetFID() < 0)
1650
0
            {
1651
0
                CPLError(CE_Failure, CPLE_AppDefined,
1652
0
                         "Cannot update not existing feature " CPL_FRMT_GIB,
1653
0
                         poFeature->GetFID());
1654
0
                return OGRERR_FAILURE;
1655
0
            }
1656
1657
0
            bool bResult = NGWAPI::UpdateFeature(
1658
0
                poDS->GetUrl(), osResourceId,
1659
0
                std::to_string(poFeature->GetFID()),
1660
0
                FeatureToJsonString(poFeature), poDS->GetHeaders());
1661
0
            if (bResult)
1662
0
            {
1663
0
                CPLDebug("NGW", "ISetFeature with FID " CPL_FRMT_GIB,
1664
0
                         poFeature->GetFID());
1665
1666
0
                OGRFeature::DestroyFeature(moFeatures[poFeature->GetFID()]);
1667
0
                moFeatures[poFeature->GetFID()] = poFeature->Clone();
1668
0
                return OGRERR_NONE;
1669
0
            }
1670
0
            else
1671
0
            {
1672
                // CPLError should be set in NGWAPI::UpdateFeature.
1673
0
                return OGRERR_FAILURE;
1674
0
            }
1675
0
        }
1676
0
        else
1677
0
        {
1678
0
            return eResult;
1679
0
        }
1680
0
    }
1681
0
}
1682
1683
/*
1684
 * ICreateFeature()
1685
 */
1686
OGRErr OGRNGWLayer::ICreateFeature(OGRFeature *poFeature)
1687
0
{
1688
0
    if (poDS->IsBatchMode())
1689
0
    {
1690
0
        GIntBig nNewFID = -1;
1691
0
        if (!soChangedIds.empty())
1692
0
        {
1693
0
            nNewFID = *(soChangedIds.begin()) - 1;
1694
0
        }
1695
0
        poFeature->SetFID(nNewFID);
1696
0
        moFeatures[nNewFID] = poFeature->Clone();
1697
0
        soChangedIds.insert(nNewFID);
1698
0
        nFeatureCount++;
1699
1700
0
        if (soChangedIds.size() > static_cast<size_t>(poDS->GetBatchSize()))
1701
0
        {
1702
0
            bNeedSyncData = true;
1703
0
        }
1704
1705
0
        return SyncToDisk();
1706
0
    }
1707
0
    else
1708
0
    {
1709
0
        OGRErr eResult =
1710
0
            SyncToDisk();  // For create new layer if not yet created.
1711
0
        if (eResult == OGRERR_NONE)
1712
0
        {
1713
0
            GIntBig nNewFID = NGWAPI::CreateFeature(
1714
0
                poDS->GetUrl(), osResourceId, FeatureToJsonString(poFeature),
1715
0
                poDS->GetHeaders());
1716
0
            if (nNewFID >= 0)
1717
0
            {
1718
0
                poFeature->SetFID(nNewFID);
1719
0
                moFeatures[nNewFID] = poFeature->Clone();
1720
0
                nFeatureCount++;
1721
0
                return OGRERR_NONE;
1722
0
            }
1723
0
            else
1724
0
            {
1725
                // CPLError should be set in NGWAPI::CreateFeature.
1726
0
                return OGRERR_FAILURE;
1727
0
            }
1728
0
        }
1729
0
        else
1730
0
        {
1731
0
            return eResult;
1732
0
        }
1733
0
    }
1734
0
}
1735
1736
/*
1737
 * SetIgnoredFields()
1738
 */
1739
OGRErr OGRNGWLayer::SetIgnoredFields(CSLConstList papszFields)
1740
0
{
1741
0
    OGRErr eResult = OGRLayer::SetIgnoredFields(papszFields);
1742
0
    if (eResult != OGRERR_NONE)
1743
0
    {
1744
0
        return eResult;
1745
0
    }
1746
1747
0
    osFields.clear();
1748
1749
0
    for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
1750
0
    {
1751
0
        OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
1752
0
        if (poFieldDefn->IsIgnored())
1753
0
        {
1754
0
            CPLDebug("NGW", "SetIgnoredFields: Field '%s' set to ignored",
1755
0
                     poFieldDefn->GetNameRef());
1756
0
            continue;
1757
0
        }
1758
1759
0
        if (osFields.empty())
1760
0
        {
1761
0
            osFields = poFieldDefn->GetNameRef();
1762
0
        }
1763
0
        else
1764
0
        {
1765
0
            osFields += "," + std::string(poFieldDefn->GetNameRef());
1766
0
        }
1767
0
    }
1768
1769
    // Encode osFields string for URL
1770
0
    if (!osFields.empty())
1771
0
    {
1772
0
        char *pszValuesEncoded = CPLEscapeString(
1773
0
            osFields.c_str(), static_cast<int>(osFields.size()), CPLES_URL);
1774
0
        osFields = pszValuesEncoded;
1775
0
        CPLFree(pszValuesEncoded);
1776
0
    }
1777
1778
0
    CPLDebug("NGW", "SetIgnoredFields: NGW fields filter set to '%s'",
1779
0
             osFields.c_str());
1780
1781
0
    ResetReading();
1782
0
    return OGRERR_NONE;
1783
0
}
1784
1785
/*
1786
 * ISetSpatialFilter()
1787
 */
1788
OGRErr OGRNGWLayer::ISetSpatialFilter(int iGeomField, const OGRGeometry *poGeom)
1789
0
{
1790
0
    OGRLayer::ISetSpatialFilter(iGeomField, poGeom);
1791
1792
0
    if (nullptr == m_poFilterGeom)
1793
0
    {
1794
0
        CPLDebug("NGW", "Spatial filter unset");
1795
0
        osSpatialFilter.clear();
1796
0
    }
1797
0
    else
1798
0
    {
1799
0
        OGREnvelope sEnvelope;
1800
0
        m_poFilterGeom->getEnvelope(&sEnvelope);
1801
1802
0
        OGREnvelope sBigEnvelope;
1803
0
        sBigEnvelope.MinX = -40000000.0;
1804
0
        sBigEnvelope.MinY = -40000000.0;
1805
0
        sBigEnvelope.MaxX = 40000000.0;
1806
0
        sBigEnvelope.MaxY = 40000000.0;
1807
1808
        // Case for infinity filter
1809
0
        if (sEnvelope.Contains(sBigEnvelope) == TRUE)
1810
0
        {
1811
0
            CPLDebug("NGW", "Spatial filter unset as filter envelope covers "
1812
0
                            "whole features.");
1813
0
            osSpatialFilter.clear();
1814
0
        }
1815
0
        else
1816
0
        {
1817
0
            if (sEnvelope.MinX == sEnvelope.MaxX &&
1818
0
                sEnvelope.MinY == sEnvelope.MaxY)
1819
0
            {
1820
0
                OGRPoint p(sEnvelope.MinX, sEnvelope.MinY);
1821
0
                InstallFilter(&p);
1822
0
            }
1823
1824
0
            osSpatialFilter = OGRGeometryToWKT(m_poFilterGeom);
1825
0
            CPLDebug("NGW", "Spatial filter: %s", osSpatialFilter.c_str());
1826
0
            char *pszSpatFilterEncoded = CPLEscapeString(
1827
0
                osSpatialFilter.c_str(),
1828
0
                static_cast<int>(osSpatialFilter.size()), CPLES_URL);
1829
0
            osSpatialFilter = pszSpatFilterEncoded;
1830
0
            CPLFree(pszSpatFilterEncoded);
1831
0
        }
1832
0
    }
1833
1834
0
    ResetReading();
1835
1836
0
    return OGRERR_NONE;
1837
0
}
1838
1839
/*
1840
 * SetAttributeFilter()
1841
 */
1842
OGRErr OGRNGWLayer::SetAttributeFilter(const char *pszQuery)
1843
0
{
1844
0
    OGRErr eResult = OGRERR_NONE;
1845
0
    if (nullptr == pszQuery)
1846
0
    {
1847
0
        eResult = OGRLayer::SetAttributeFilter(pszQuery);
1848
0
        osWhere.clear();
1849
0
        bClientSideAttributeFilter = false;
1850
0
    }
1851
0
    else if (STARTS_WITH_CI(pszQuery,
1852
0
                            "NGW:"))  // Already formatted for NGW REST API
1853
0
    {
1854
0
        osWhere = pszQuery + strlen("NGW:");
1855
0
        bClientSideAttributeFilter = false;
1856
0
    }
1857
0
    else
1858
0
    {
1859
0
        eResult = OGRLayer::SetAttributeFilter(pszQuery);
1860
0
        if (eResult == OGRERR_NONE && m_poAttrQuery != nullptr)
1861
0
        {
1862
0
            swq_expr_node *poNode =
1863
0
                reinterpret_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
1864
0
            std::string osWhereIn = TranslateSQLToFilter(poNode);
1865
0
            if (osWhereIn.empty())
1866
0
            {
1867
0
                osWhere.clear();
1868
0
                bClientSideAttributeFilter = true;
1869
0
                CPLDebug(
1870
0
                    "NGW",
1871
0
                    "Attribute filter '%s' will be evaluated on client side.",
1872
0
                    pszQuery);
1873
0
            }
1874
0
            else
1875
0
            {
1876
0
                bClientSideAttributeFilter = false;
1877
0
                CPLDebug("NGW", "Attribute filter: %s", osWhereIn.c_str());
1878
0
                osWhere = std::move(osWhereIn);
1879
0
            }
1880
0
        }
1881
0
    }
1882
1883
0
    ResetReading();
1884
0
    return eResult;
1885
0
}
1886
1887
/*
1888
 * SetSelectedFields()
1889
 */
1890
OGRErr OGRNGWLayer::SetSelectedFields(const std::set<std::string> &aosFields)
1891
0
{
1892
0
    CPLStringList aosIgnoreFields;
1893
0
    for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
1894
0
    {
1895
0
        OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
1896
0
        if (aosFields.find(poFieldDefn->GetNameRef()) != aosFields.end())
1897
0
        {
1898
0
            continue;
1899
0
        }
1900
0
        aosIgnoreFields.AddString(poFieldDefn->GetNameRef());
1901
0
    }
1902
0
    return SetIgnoredFields(aosIgnoreFields.List());
1903
0
}
1904
1905
/*
1906
 * Clone()
1907
 */
1908
OGRNGWLayer *OGRNGWLayer::Clone() const
1909
0
{
1910
0
    return new OGRNGWLayer(osResourceId, poDS, stPermissions,
1911
0
                           poFeatureDefn->Clone(), nFeatureCount, stExtent);
1912
0
}
1913
1914
/*
1915
 * GetNewFeaturesCount()
1916
 */
1917
GIntBig OGRNGWLayer::GetNewFeaturesCount() const
1918
0
{
1919
0
    if (soChangedIds.empty())
1920
0
    {
1921
0
        return 0;
1922
0
    }
1923
1924
0
    if (*soChangedIds.begin() >= 0)
1925
0
    {
1926
0
        return 0;
1927
0
    }
1928
1929
    // The lowest negative identifier equal new feature count
1930
0
    return *soChangedIds.begin() * -1;
1931
0
}