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