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/desciption");
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 oFieldCommnent;
1004 0 : oFieldCommnent.Add("id", oField.GetLong("id"));
1005 0 : oFieldCommnent.Add("label_field", oField.GetBool("label_field"));
1006 0 : oFieldCommnent.Add("grid_visibility",
1007 0 : oField.GetBool("grid_visibility"));
1008 0 : oFieldCommnent.Add("text_search", oField.GetBool("text_search"));
1009 0 : oFieldDefn.SetComment(
1010 0 : oFieldCommnent.Format(CPLJSONObject::PrettyFormat::Plain));
1011 :
1012 : // Domain
1013 0 : auto nDomainID = oField.GetInteger("lookup_table/id", -1);
1014 0 : if (nDomainID != -1)
1015 : {
1016 0 : auto oDom = poDS->GetDomainByID(nDomainID);
1017 0 : auto pOgrDom = oDom.ToFieldDomain(eFieldtype);
1018 0 : if (pOgrDom != nullptr)
1019 : {
1020 0 : oFieldDefn.SetDomainName(pOgrDom->GetName());
1021 : }
1022 : }
1023 :
1024 0 : poFeatureDefn->AddFieldDefn(&oFieldDefn);
1025 : }
1026 :
1027 0 : OGRLayer::SetIgnoredFields(soIgnoredFieldsNames);
1028 0 : }
1029 :
1030 : /*
1031 : * GetMaxFeatureCount()
1032 : */
1033 0 : GIntBig OGRNGWLayer::GetMaxFeatureCount(bool bForce)
1034 : {
1035 0 : if (nFeatureCount < 0 || bForce)
1036 : {
1037 : CPLJSONObject oRoot =
1038 0 : LoadUrl(NGWAPI::GetFeatureCount(poDS->GetUrl(), osResourceId));
1039 0 : if (!oRoot.IsValid())
1040 : {
1041 0 : return nFeatureCount;
1042 : }
1043 :
1044 0 : nFeatureCount = oRoot.GetLong("total_count");
1045 0 : nFeatureCount += GetNewFeaturesCount();
1046 : }
1047 0 : return nFeatureCount;
1048 : }
1049 :
1050 : /*
1051 : * GetFeatureCount()
1052 : */
1053 0 : GIntBig OGRNGWLayer::GetFeatureCount(int bForce)
1054 : {
1055 0 : if (m_poFilterGeom == nullptr && m_poAttrQuery == nullptr)
1056 : {
1057 0 : return GetMaxFeatureCount(CPL_TO_BOOL(bForce));
1058 : }
1059 : else
1060 : {
1061 0 : return OGRLayer::GetFeatureCount(bForce);
1062 : }
1063 : }
1064 :
1065 : /*
1066 : * IGetExtent()
1067 : */
1068 0 : OGRErr OGRNGWLayer::IGetExtent(int /* iGeomField */, OGREnvelope *psExtent,
1069 : bool bForce)
1070 : {
1071 0 : if (!stExtent.IsInit() || CPL_TO_BOOL(bForce))
1072 : {
1073 0 : auto aosHTTPOptions = poDS->GetHeaders(false);
1074 0 : bool bResult = NGWAPI::GetExtent(poDS->GetUrl(), osResourceId,
1075 0 : aosHTTPOptions, 3857, stExtent);
1076 0 : if (!bResult)
1077 : {
1078 0 : return OGRERR_FAILURE;
1079 : }
1080 : }
1081 0 : *psExtent = stExtent;
1082 0 : return OGRERR_NONE;
1083 : }
1084 :
1085 : /*
1086 : * FetchPermissions()
1087 : */
1088 0 : void OGRNGWLayer::FetchPermissions()
1089 : {
1090 0 : if (bFetchedPermissions || osResourceId == "-1")
1091 : {
1092 0 : return;
1093 : }
1094 :
1095 0 : if (poDS->IsUpdateMode())
1096 : {
1097 0 : auto aosHTTPOptions = poDS->GetHeaders(false);
1098 : stPermissions = NGWAPI::CheckPermissions(
1099 0 : poDS->GetUrl(), osResourceId, aosHTTPOptions, poDS->IsUpdateMode());
1100 : }
1101 : else
1102 : {
1103 0 : stPermissions.bDataCanRead = true;
1104 0 : stPermissions.bResourceCanRead = true;
1105 0 : stPermissions.bDatastructCanRead = true;
1106 0 : stPermissions.bMetadataCanRead = true;
1107 : }
1108 0 : bFetchedPermissions = true;
1109 : }
1110 :
1111 : /*
1112 : * CreateField()
1113 : */
1114 0 : OGRErr OGRNGWLayer::CreateField(const OGRFieldDefn *poField,
1115 : CPL_UNUSED int bApproxOK)
1116 : {
1117 0 : CPLAssert(nullptr != poField);
1118 :
1119 0 : if (!CheckFieldNameUnique(poFeatureDefn, -1, poField->GetNameRef()))
1120 : {
1121 0 : return OGRERR_FAILURE;
1122 : }
1123 :
1124 0 : OGRFieldDefn oModFieldDefn(poField);
1125 0 : poFeatureDefn->AddFieldDefn(&oModFieldDefn);
1126 0 : bNeedSyncStructure = true;
1127 0 : return OGRERR_NONE;
1128 : }
1129 :
1130 : /*
1131 : * DeleteField()
1132 : */
1133 0 : OGRErr OGRNGWLayer::DeleteField(int iField)
1134 : {
1135 0 : if (osResourceId != "-1")
1136 : {
1137 0 : auto poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
1138 0 : if (poFieldDefn == nullptr)
1139 : {
1140 0 : return OGRERR_FAILURE;
1141 : }
1142 : // Get field NGW ID
1143 0 : CPLJSONDocument oComment;
1144 0 : if (oComment.LoadMemory(poFieldDefn->GetComment()))
1145 : {
1146 0 : auto oRoot = oComment.GetRoot();
1147 0 : auto nNGWID = oRoot.GetLong("id", -1);
1148 0 : if (nNGWID != -1)
1149 : {
1150 0 : soDeletedFieldsIds.insert(nNGWID);
1151 : }
1152 : }
1153 : }
1154 0 : return poFeatureDefn->DeleteFieldDefn(iField);
1155 : }
1156 :
1157 : /*
1158 : * ReorderFields()
1159 : */
1160 0 : OGRErr OGRNGWLayer::ReorderFields(int *panMap)
1161 : {
1162 0 : return poFeatureDefn->ReorderFieldDefns(panMap);
1163 : }
1164 :
1165 : /*
1166 : * AlterFieldDefn()
1167 : */
1168 0 : OGRErr OGRNGWLayer::AlterFieldDefn(int iField, OGRFieldDefn *poNewFieldDefn,
1169 : int nFlagsIn)
1170 : {
1171 0 : CPLDebug("NGW", "AlterFieldDefn fld #%d", iField);
1172 0 : CPLAssert(nullptr != poNewFieldDefn);
1173 :
1174 0 : if (iField < 0 || iField >= poFeatureDefn->GetFieldCount())
1175 : {
1176 0 : CPLError(CE_Failure, CPLE_NotSupported, "Invalid field index");
1177 0 : return OGRERR_FAILURE;
1178 : }
1179 :
1180 0 : FetchPermissions();
1181 0 : if (!stPermissions.bDatastructCanWrite)
1182 : {
1183 0 : CPLError(CE_Failure, CPLE_NotSupported, "Insufficient permissions");
1184 0 : return OGRERR_FAILURE;
1185 : }
1186 :
1187 0 : if (!poDS->IsUpdateMode())
1188 : {
1189 0 : CPLError(CE_Failure, CPLE_NotSupported, "Layer is read only");
1190 0 : return OGRERR_FAILURE;
1191 : }
1192 :
1193 0 : OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
1194 : // Check new field name is not equal for another fields.
1195 0 : if (!CheckFieldNameUnique(poFeatureDefn, iField,
1196 : poNewFieldDefn->GetNameRef()))
1197 : {
1198 0 : return OGRERR_FAILURE;
1199 : }
1200 :
1201 0 : if (osResourceId == "-1") // Can full alter field only on new layers
1202 : // (not synced with server).
1203 : {
1204 : // Field name 'id' forbidden.
1205 0 : OGRFieldDefn oModFieldDefn(poNewFieldDefn);
1206 :
1207 0 : poFieldDefn->SetName(oModFieldDefn.GetNameRef());
1208 0 : poFieldDefn->SetAlternativeName(oModFieldDefn.GetAlternativeNameRef());
1209 0 : poFieldDefn->SetComment(oModFieldDefn.GetComment());
1210 0 : poFieldDefn->SetType(oModFieldDefn.GetType());
1211 0 : poFieldDefn->SetSubType(oModFieldDefn.GetSubType());
1212 0 : poFieldDefn->SetWidth(oModFieldDefn.GetWidth());
1213 0 : poFieldDefn->SetPrecision(oModFieldDefn.GetPrecision());
1214 : }
1215 : else
1216 : {
1217 0 : if (nFlagsIn & ALTER_NAME_FLAG)
1218 : {
1219 0 : bNeedSyncStructure = true;
1220 0 : poFieldDefn->SetName(poNewFieldDefn->GetNameRef());
1221 : }
1222 0 : if (nFlagsIn & ALTER_DOMAIN_FLAG)
1223 : {
1224 0 : bNeedSyncStructure = true;
1225 0 : poFieldDefn->SetDomainName(poNewFieldDefn->GetDomainName());
1226 : }
1227 0 : if (nFlagsIn & ALTER_ALTERNATIVE_NAME_FLAG)
1228 : {
1229 0 : bNeedSyncStructure = true;
1230 0 : poFieldDefn->SetAlternativeName(
1231 : poNewFieldDefn->GetAlternativeNameRef());
1232 : }
1233 0 : if (nFlagsIn & ALTER_COMMENT_FLAG)
1234 : {
1235 0 : bNeedSyncStructure = true;
1236 0 : poFieldDefn->SetComment(poNewFieldDefn->GetComment());
1237 : }
1238 : }
1239 0 : ResetReading();
1240 0 : return OGRERR_NONE;
1241 : }
1242 :
1243 : /*
1244 : * SetMetadata()
1245 : */
1246 0 : CPLErr OGRNGWLayer::SetMetadata(char **papszMetadata, const char *pszDomain)
1247 : {
1248 0 : bNeedSyncStructure = true;
1249 0 : return OGRLayer::SetMetadata(papszMetadata, pszDomain);
1250 : }
1251 :
1252 : /*
1253 : * SetMetadataItem()
1254 : */
1255 0 : CPLErr OGRNGWLayer::SetMetadataItem(const char *pszName, const char *pszValue,
1256 : const char *pszDomain)
1257 : {
1258 0 : bNeedSyncStructure = true;
1259 0 : return OGRLayer::SetMetadataItem(pszName, pszValue, pszDomain);
1260 : }
1261 :
1262 : /*
1263 : * CreateNGWResourceJson()
1264 : */
1265 0 : std::string OGRNGWLayer::CreateNGWResourceJson()
1266 : {
1267 0 : CPLJSONObject oResourceJson;
1268 :
1269 : // Add resource json item.
1270 0 : CPLJSONObject oResource("resource", oResourceJson);
1271 0 : oResource.Add("cls", "vector_layer");
1272 0 : CPLJSONObject oResourceParent("parent", oResource);
1273 0 : oResourceParent.Add("id",
1274 0 : static_cast<GIntBig>(std::stol(poDS->GetResourceId())));
1275 0 : oResource.Add("display_name", GetName());
1276 0 : const char *pszKeyName = GetMetadataItem("keyname");
1277 0 : if (pszKeyName)
1278 : {
1279 0 : oResource.Add("keyname", pszKeyName);
1280 : }
1281 0 : const char *pszDescription = GetMetadataItem("description");
1282 0 : if (pszDescription)
1283 : {
1284 0 : oResource.Add("description", pszDescription);
1285 : }
1286 :
1287 : // Add vector_layer json item.
1288 0 : CPLJSONObject oVectorLayer("vector_layer", oResourceJson);
1289 0 : CPLJSONObject oVectorLayerSrs("srs", oVectorLayer);
1290 :
1291 0 : OGRSpatialReference *poSpatialRef = GetSpatialRef();
1292 0 : int nEPSG = 3857;
1293 0 : if (poSpatialRef)
1294 : {
1295 0 : poSpatialRef->AutoIdentifyEPSG();
1296 0 : const char *pszEPSG = poSpatialRef->GetAuthorityCode(nullptr);
1297 0 : if (pszEPSG != nullptr)
1298 : {
1299 0 : nEPSG = atoi(pszEPSG);
1300 : }
1301 : }
1302 0 : oVectorLayerSrs.Add("id", nEPSG);
1303 : // In OGRNGWDataset::ICreateLayer we limit supported geometry types.
1304 0 : oVectorLayer.Add("geometry_type",
1305 0 : NGWAPI::OGRGeomTypeToNGWGeomType(GetGeomType()));
1306 :
1307 : // Fill fields
1308 0 : CPLJSONArray oVectorLayerFields;
1309 0 : for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
1310 : {
1311 0 : OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
1312 0 : CPLJSONObject oField;
1313 0 : auto const &sComment = poFieldDefn->GetComment();
1314 0 : if (!sComment.empty())
1315 : {
1316 0 : CPLJSONDocument oComment;
1317 0 : if (oComment.LoadMemory(sComment))
1318 : {
1319 0 : oField = oComment.GetRoot();
1320 : }
1321 : }
1322 0 : oField.Add("keyname", poFieldDefn->GetNameRef());
1323 0 : oField.Add("datatype",
1324 0 : NGWAPI::OGRFieldTypeToNGWFieldType(poFieldDefn->GetType()));
1325 0 : std::string osFieldAliasName = poFieldDefn->GetAlternativeNameRef();
1326 : // Get alias from metadata.
1327 0 : if (osFieldAliasName.empty())
1328 : {
1329 0 : osFieldAliasName = poFieldDefn->GetNameRef();
1330 : }
1331 0 : oField.Add("display_name", osFieldAliasName);
1332 :
1333 0 : if (poFieldDefn->GetDomainName().empty())
1334 : {
1335 0 : oField.AddNull("lookup_table");
1336 : }
1337 : else
1338 : {
1339 0 : CPLJSONObject oFieldDom("lookup_table", oField);
1340 0 : auto nDomId = poDS->GetDomainIdByName(poFieldDefn->GetDomainName());
1341 0 : oFieldDom.Add("id", nDomId);
1342 : }
1343 :
1344 0 : oVectorLayerFields.Add(oField);
1345 : }
1346 0 : for (auto nFieldID : soDeletedFieldsIds)
1347 : {
1348 0 : CPLJSONObject oField;
1349 0 : oField.Add("id", nFieldID);
1350 0 : oField.Add("delete", true);
1351 0 : oVectorLayerFields.Add(oField);
1352 : }
1353 :
1354 0 : CPLJSONObject oFeatureLayer("feature_layer", oResourceJson);
1355 0 : oFeatureLayer.Add("fields", oVectorLayerFields);
1356 :
1357 : // Add resmeta json item.
1358 0 : NGWAPI::FillResmeta(oResourceJson, GetMetadata("NGW"));
1359 :
1360 0 : return oResourceJson.Format(CPLJSONObject::PrettyFormat::Plain);
1361 : }
1362 :
1363 : /*
1364 : * SyncFeatures()
1365 : */
1366 0 : OGRErr OGRNGWLayer::SyncFeatures()
1367 : {
1368 0 : if (!bNeedSyncData)
1369 : {
1370 0 : return OGRERR_NONE;
1371 : }
1372 :
1373 0 : CPLJSONArray oFeatureJsonArray;
1374 0 : std::vector<GIntBig> aoPatchedFIDs;
1375 0 : for (GIntBig nFID : soChangedIds)
1376 : {
1377 0 : if (moFeatures[nFID] != nullptr)
1378 : {
1379 0 : oFeatureJsonArray.Add(FeatureToJson(moFeatures[nFID]));
1380 0 : aoPatchedFIDs.push_back(nFID);
1381 : }
1382 : }
1383 :
1384 0 : if (!aoPatchedFIDs.empty())
1385 : {
1386 : auto osIDs = NGWAPI::PatchFeatures(
1387 0 : poDS->GetUrl(), osResourceId,
1388 0 : oFeatureJsonArray.Format(CPLJSONObject::PrettyFormat::Plain),
1389 0 : poDS->GetHeaders());
1390 0 : if (!osIDs.empty())
1391 : {
1392 0 : bNeedSyncData = false;
1393 0 : nFeatureCount += GetNewFeaturesCount();
1394 0 : soChangedIds.clear();
1395 0 : if (osIDs.size() !=
1396 0 : aoPatchedFIDs.size()) // Expected equal identifier count.
1397 : {
1398 0 : CPLDebug("NGW", "Patched feature count is not equal. Reload "
1399 : "features from server.");
1400 0 : FreeMap(moFeatures);
1401 : }
1402 : else // Just update identifiers.
1403 : {
1404 0 : int nCounter = 0;
1405 0 : for (GIntBig nFID : aoPatchedFIDs)
1406 : {
1407 0 : GIntBig nNewFID = osIDs[nCounter++];
1408 0 : OGRFeature *poFeature = moFeatures[nFID];
1409 0 : poFeature->SetFID(nNewFID);
1410 0 : moFeatures.erase(nFID);
1411 0 : moFeatures[nNewFID] = poFeature;
1412 : }
1413 : }
1414 : }
1415 : else
1416 : {
1417 : // Error message should set in NGWAPI::PatchFeatures function.
1418 0 : if (CPLGetLastErrorNo() != 0)
1419 : {
1420 0 : return OGRERR_FAILURE;
1421 : }
1422 : }
1423 : }
1424 0 : return OGRERR_NONE;
1425 : }
1426 :
1427 : /*
1428 : * SyncToDisk()
1429 : */
1430 0 : OGRErr OGRNGWLayer::SyncToDisk()
1431 : {
1432 0 : if (osResourceId == "-1") // Create vector layer at NextGIS Web.
1433 : {
1434 0 : bNeedSyncData = !moFeatures.empty();
1435 : std::string osResourceIdInt = NGWAPI::CreateResource(
1436 0 : poDS->GetUrl(), CreateNGWResourceJson(), poDS->GetHeaders());
1437 0 : if (osResourceIdInt == "-1")
1438 : {
1439 : // Error message should set in CreateResource.
1440 0 : return OGRERR_FAILURE;
1441 : }
1442 0 : osResourceId = std::move(osResourceIdInt);
1443 0 : OGRLayer::SetMetadataItem("id", osResourceId.c_str());
1444 0 : FetchPermissions();
1445 0 : bNeedSyncStructure = false;
1446 : }
1447 0 : else if (bNeedSyncStructure) // Update vector layer at NextGIS Web.
1448 : {
1449 0 : if (!NGWAPI::UpdateResource(poDS->GetUrl(), GetResourceId(),
1450 0 : CreateNGWResourceJson(),
1451 0 : poDS->GetHeaders()))
1452 : {
1453 : // Error message should set in UpdateResource.
1454 0 : return OGRERR_FAILURE;
1455 : }
1456 0 : bNeedSyncStructure = false;
1457 0 : soDeletedFieldsIds.clear();
1458 : }
1459 :
1460 0 : auto oRoot = LoadUrl(NGWAPI::GetResourceURL(poDS->GetUrl(), osResourceId));
1461 0 : if (!oRoot.IsValid())
1462 : {
1463 0 : return OGRERR_FAILURE;
1464 : }
1465 0 : Fill(oRoot);
1466 :
1467 : // Update NGW layer state
1468 0 : FetchPermissions();
1469 :
1470 : // Sync features.
1471 0 : return SyncFeatures();
1472 : }
1473 :
1474 : /*
1475 : * DeleteFeature()
1476 : */
1477 0 : OGRErr OGRNGWLayer::DeleteFeature(GIntBig nFID)
1478 : {
1479 0 : CPLErrorReset();
1480 0 : if (nFID < 0)
1481 : {
1482 0 : if (moFeatures[nFID] != nullptr)
1483 : {
1484 0 : OGRFeature::DestroyFeature(moFeatures[nFID]);
1485 0 : moFeatures[nFID] = nullptr;
1486 0 : nFeatureCount--;
1487 0 : soChangedIds.erase(nFID);
1488 0 : return OGRERR_NONE;
1489 : }
1490 0 : CPLError(CE_Failure, CPLE_AppDefined,
1491 : "Feature with id " CPL_FRMT_GIB " not found.", nFID);
1492 0 : return OGRERR_FAILURE;
1493 : }
1494 : else
1495 : {
1496 0 : FetchPermissions();
1497 0 : if (stPermissions.bDataCanWrite && poDS->IsUpdateMode())
1498 : {
1499 0 : bool bResult = NGWAPI::DeleteFeature(poDS->GetUrl(), osResourceId,
1500 0 : std::to_string(nFID),
1501 0 : poDS->GetHeaders(false));
1502 0 : if (bResult)
1503 : {
1504 0 : if (moFeatures[nFID] != nullptr)
1505 : {
1506 0 : OGRFeature::DestroyFeature(moFeatures[nFID]);
1507 0 : moFeatures[nFID] = nullptr;
1508 : }
1509 0 : nFeatureCount--;
1510 0 : soChangedIds.erase(nFID);
1511 0 : return OGRERR_NONE;
1512 : }
1513 0 : return OGRERR_FAILURE;
1514 : }
1515 0 : CPLError(CE_Failure, CPLE_AppDefined,
1516 : "Delete feature " CPL_FRMT_GIB " operation is not permitted.",
1517 : nFID);
1518 0 : return OGRERR_FAILURE;
1519 : }
1520 : }
1521 :
1522 : /*
1523 : * DeleteFeatures()
1524 : */
1525 0 : OGRErr OGRNGWLayer::DeleteFeatures(const std::vector<GIntBig> &vFeaturesID)
1526 : {
1527 0 : CPLErrorReset();
1528 :
1529 : // Try to delete local features (not synchronized with NGW)
1530 0 : std::vector<GIntBig> vFeaturesIDInt(vFeaturesID);
1531 0 : for (size_t i = 0; i < vFeaturesIDInt.size(); i++)
1532 : {
1533 0 : auto nFID = vFeaturesIDInt[i];
1534 0 : if (nFID < 0)
1535 : {
1536 0 : if (moFeatures[nFID] != nullptr)
1537 : {
1538 0 : OGRFeature::DestroyFeature(moFeatures[nFID]);
1539 0 : moFeatures[nFID] = nullptr;
1540 0 : nFeatureCount--;
1541 0 : soChangedIds.erase(nFID);
1542 0 : vFeaturesIDInt.erase(vFeaturesIDInt.begin() + i);
1543 : }
1544 : }
1545 : }
1546 :
1547 0 : FetchPermissions();
1548 0 : if (stPermissions.bDataCanWrite && poDS->IsUpdateMode())
1549 : {
1550 0 : bool bResult = NGWAPI::DeleteFeatures(
1551 0 : poDS->GetUrl(), osResourceId,
1552 0 : FeaturesIDToJsonString(vFeaturesIDInt), poDS->GetHeaders(false));
1553 0 : if (bResult)
1554 : {
1555 0 : for (GIntBig nFID : vFeaturesIDInt)
1556 : {
1557 0 : if (moFeatures[nFID] != nullptr)
1558 : {
1559 0 : OGRFeature::DestroyFeature(moFeatures[nFID]);
1560 0 : moFeatures[nFID] = nullptr;
1561 0 : nFeatureCount--;
1562 0 : soChangedIds.erase(nFID);
1563 : }
1564 0 : nFeatureCount--;
1565 0 : soChangedIds.erase(nFID);
1566 : }
1567 0 : return OGRERR_NONE;
1568 : }
1569 : }
1570 0 : CPLError(CE_Failure, CPLE_AppDefined, "Delete features failed");
1571 0 : return OGRERR_FAILURE;
1572 : }
1573 :
1574 : /*
1575 : * DeleteAllFeatures()
1576 : */
1577 0 : bool OGRNGWLayer::DeleteAllFeatures()
1578 : {
1579 0 : if (osResourceId == "-1")
1580 : {
1581 0 : soChangedIds.clear();
1582 0 : bNeedSyncData = false;
1583 0 : FreeFeaturesCache();
1584 0 : nFeatureCount = 0;
1585 0 : return true;
1586 : }
1587 : else
1588 : {
1589 0 : FetchPermissions();
1590 0 : if (stPermissions.bDataCanWrite && poDS->IsUpdateMode())
1591 : {
1592 0 : bool bResult = NGWAPI::DeleteFeature(poDS->GetUrl(), osResourceId,
1593 0 : "", poDS->GetHeaders(false));
1594 0 : if (bResult)
1595 : {
1596 0 : soChangedIds.clear();
1597 0 : bNeedSyncData = false;
1598 0 : FreeFeaturesCache();
1599 0 : nFeatureCount = 0;
1600 : }
1601 0 : return bResult;
1602 : }
1603 : }
1604 0 : CPLErrorReset();
1605 0 : CPLError(CE_Failure, CPLE_AppDefined,
1606 : "Delete all features operation is not permitted.");
1607 0 : return false;
1608 : }
1609 :
1610 : /*
1611 : * ISetFeature()
1612 : */
1613 0 : OGRErr OGRNGWLayer::ISetFeature(OGRFeature *poFeature)
1614 : {
1615 0 : if (poDS->IsBatchMode())
1616 : {
1617 0 : if (moFeatures[poFeature->GetFID()] == nullptr)
1618 : {
1619 0 : if (poFeature->GetFID() < 0)
1620 : {
1621 0 : CPLError(CE_Failure, CPLE_AppDefined,
1622 : "Cannot update not existing feature " CPL_FRMT_GIB,
1623 : poFeature->GetFID());
1624 0 : return OGRERR_FAILURE;
1625 : }
1626 : }
1627 : else
1628 : {
1629 0 : OGRFeature::DestroyFeature(moFeatures[poFeature->GetFID()]);
1630 : }
1631 0 : moFeatures[poFeature->GetFID()] = poFeature->Clone();
1632 0 : soChangedIds.insert(poFeature->GetFID());
1633 :
1634 0 : if (soChangedIds.size() > static_cast<size_t>(poDS->GetBatchSize()))
1635 : {
1636 0 : bNeedSyncData = true;
1637 : }
1638 :
1639 0 : return SyncToDisk();
1640 : }
1641 : else
1642 : {
1643 : OGRErr eResult =
1644 0 : SyncToDisk(); // For create new layer if not yet created.
1645 0 : if (eResult == OGRERR_NONE)
1646 : {
1647 0 : if (poFeature->GetFID() < 0)
1648 : {
1649 0 : CPLError(CE_Failure, CPLE_AppDefined,
1650 : "Cannot update not existing feature " CPL_FRMT_GIB,
1651 : poFeature->GetFID());
1652 0 : return OGRERR_FAILURE;
1653 : }
1654 :
1655 0 : bool bResult = NGWAPI::UpdateFeature(
1656 0 : poDS->GetUrl(), osResourceId,
1657 0 : std::to_string(poFeature->GetFID()),
1658 0 : FeatureToJsonString(poFeature), poDS->GetHeaders());
1659 0 : if (bResult)
1660 : {
1661 0 : CPLDebug("NGW", "ISetFeature with FID " CPL_FRMT_GIB,
1662 : poFeature->GetFID());
1663 :
1664 0 : OGRFeature::DestroyFeature(moFeatures[poFeature->GetFID()]);
1665 0 : moFeatures[poFeature->GetFID()] = poFeature->Clone();
1666 0 : return OGRERR_NONE;
1667 : }
1668 : else
1669 : {
1670 : // CPLError should be set in NGWAPI::UpdateFeature.
1671 0 : return OGRERR_FAILURE;
1672 : }
1673 : }
1674 : else
1675 : {
1676 0 : return eResult;
1677 : }
1678 : }
1679 : }
1680 :
1681 : /*
1682 : * ICreateFeature()
1683 : */
1684 0 : OGRErr OGRNGWLayer::ICreateFeature(OGRFeature *poFeature)
1685 : {
1686 0 : if (poDS->IsBatchMode())
1687 : {
1688 0 : GIntBig nNewFID = -1;
1689 0 : if (!soChangedIds.empty())
1690 : {
1691 0 : nNewFID = *(soChangedIds.begin()) - 1;
1692 : }
1693 0 : poFeature->SetFID(nNewFID);
1694 0 : moFeatures[nNewFID] = poFeature->Clone();
1695 0 : soChangedIds.insert(nNewFID);
1696 0 : nFeatureCount++;
1697 :
1698 0 : if (soChangedIds.size() > static_cast<size_t>(poDS->GetBatchSize()))
1699 : {
1700 0 : bNeedSyncData = true;
1701 : }
1702 :
1703 0 : return SyncToDisk();
1704 : }
1705 : else
1706 : {
1707 : OGRErr eResult =
1708 0 : SyncToDisk(); // For create new layer if not yet created.
1709 0 : if (eResult == OGRERR_NONE)
1710 : {
1711 0 : GIntBig nNewFID = NGWAPI::CreateFeature(
1712 0 : poDS->GetUrl(), osResourceId, FeatureToJsonString(poFeature),
1713 0 : poDS->GetHeaders());
1714 0 : if (nNewFID >= 0)
1715 : {
1716 0 : poFeature->SetFID(nNewFID);
1717 0 : moFeatures[nNewFID] = poFeature->Clone();
1718 0 : nFeatureCount++;
1719 0 : return OGRERR_NONE;
1720 : }
1721 : else
1722 : {
1723 : // CPLError should be set in NGWAPI::CreateFeature.
1724 0 : return OGRERR_FAILURE;
1725 : }
1726 : }
1727 : else
1728 : {
1729 0 : return eResult;
1730 : }
1731 : }
1732 : }
1733 :
1734 : /*
1735 : * SetIgnoredFields()
1736 : */
1737 0 : OGRErr OGRNGWLayer::SetIgnoredFields(CSLConstList papszFields)
1738 : {
1739 0 : OGRErr eResult = OGRLayer::SetIgnoredFields(papszFields);
1740 0 : if (eResult != OGRERR_NONE)
1741 : {
1742 0 : return eResult;
1743 : }
1744 :
1745 0 : osFields.clear();
1746 :
1747 0 : for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
1748 : {
1749 0 : OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
1750 0 : if (poFieldDefn->IsIgnored())
1751 : {
1752 0 : CPLDebug("NGW", "SetIgnoredFields: Field '%s' set to ignored",
1753 : poFieldDefn->GetNameRef());
1754 0 : continue;
1755 : }
1756 :
1757 0 : if (osFields.empty())
1758 : {
1759 0 : osFields = poFieldDefn->GetNameRef();
1760 : }
1761 : else
1762 : {
1763 0 : osFields += "," + std::string(poFieldDefn->GetNameRef());
1764 : }
1765 : }
1766 :
1767 : // Encode osFields string for URL
1768 0 : if (!osFields.empty())
1769 : {
1770 0 : char *pszValuesEncoded = CPLEscapeString(
1771 0 : osFields.c_str(), static_cast<int>(osFields.size()), CPLES_URL);
1772 0 : osFields = pszValuesEncoded;
1773 0 : CPLFree(pszValuesEncoded);
1774 : }
1775 :
1776 0 : CPLDebug("NGW", "SetIgnoredFields: NGW fields filter set to '%s'",
1777 : osFields.c_str());
1778 :
1779 0 : ResetReading();
1780 0 : return OGRERR_NONE;
1781 : }
1782 :
1783 : /*
1784 : * ISetSpatialFilter()
1785 : */
1786 0 : OGRErr OGRNGWLayer::ISetSpatialFilter(int iGeomField, const OGRGeometry *poGeom)
1787 : {
1788 0 : OGRLayer::ISetSpatialFilter(iGeomField, poGeom);
1789 :
1790 0 : if (nullptr == m_poFilterGeom)
1791 : {
1792 0 : CPLDebug("NGW", "Spatial filter unset");
1793 0 : osSpatialFilter.clear();
1794 : }
1795 : else
1796 : {
1797 0 : OGREnvelope sEnvelope;
1798 0 : m_poFilterGeom->getEnvelope(&sEnvelope);
1799 :
1800 0 : OGREnvelope sBigEnvelope;
1801 0 : sBigEnvelope.MinX = -40000000.0;
1802 0 : sBigEnvelope.MinY = -40000000.0;
1803 0 : sBigEnvelope.MaxX = 40000000.0;
1804 0 : sBigEnvelope.MaxY = 40000000.0;
1805 :
1806 : // Case for infinity filter
1807 0 : if (sEnvelope.Contains(sBigEnvelope) == TRUE)
1808 : {
1809 0 : CPLDebug("NGW", "Spatial filter unset as filter envelope covers "
1810 : "whole features.");
1811 0 : osSpatialFilter.clear();
1812 : }
1813 : else
1814 : {
1815 0 : if (sEnvelope.MinX == sEnvelope.MaxX &&
1816 0 : sEnvelope.MinY == sEnvelope.MaxY)
1817 : {
1818 0 : OGRPoint p(sEnvelope.MinX, sEnvelope.MinY);
1819 0 : InstallFilter(&p);
1820 : }
1821 :
1822 0 : osSpatialFilter = OGRGeometryToWKT(m_poFilterGeom);
1823 0 : CPLDebug("NGW", "Spatial filter: %s", osSpatialFilter.c_str());
1824 0 : char *pszSpatFilterEncoded = CPLEscapeString(
1825 : osSpatialFilter.c_str(),
1826 0 : static_cast<int>(osSpatialFilter.size()), CPLES_URL);
1827 0 : osSpatialFilter = pszSpatFilterEncoded;
1828 0 : CPLFree(pszSpatFilterEncoded);
1829 : }
1830 : }
1831 :
1832 0 : ResetReading();
1833 :
1834 0 : return OGRERR_NONE;
1835 : }
1836 :
1837 : /*
1838 : * SetAttributeFilter()
1839 : */
1840 0 : OGRErr OGRNGWLayer::SetAttributeFilter(const char *pszQuery)
1841 : {
1842 0 : OGRErr eResult = OGRERR_NONE;
1843 0 : if (nullptr == pszQuery)
1844 : {
1845 0 : eResult = OGRLayer::SetAttributeFilter(pszQuery);
1846 0 : osWhere.clear();
1847 0 : bClientSideAttributeFilter = false;
1848 : }
1849 0 : else if (STARTS_WITH_CI(pszQuery,
1850 : "NGW:")) // Already formatted for NGW REST API
1851 : {
1852 0 : osWhere = pszQuery + strlen("NGW:");
1853 0 : bClientSideAttributeFilter = false;
1854 : }
1855 : else
1856 : {
1857 0 : eResult = OGRLayer::SetAttributeFilter(pszQuery);
1858 0 : if (eResult == OGRERR_NONE && m_poAttrQuery != nullptr)
1859 : {
1860 : swq_expr_node *poNode =
1861 0 : reinterpret_cast<swq_expr_node *>(m_poAttrQuery->GetSWQExpr());
1862 0 : std::string osWhereIn = TranslateSQLToFilter(poNode);
1863 0 : if (osWhereIn.empty())
1864 : {
1865 0 : osWhere.clear();
1866 0 : bClientSideAttributeFilter = true;
1867 0 : CPLDebug(
1868 : "NGW",
1869 : "Attribute filter '%s' will be evaluated on client side.",
1870 : pszQuery);
1871 : }
1872 : else
1873 : {
1874 0 : bClientSideAttributeFilter = false;
1875 0 : CPLDebug("NGW", "Attribute filter: %s", osWhereIn.c_str());
1876 0 : osWhere = osWhereIn;
1877 : }
1878 : }
1879 : }
1880 :
1881 0 : ResetReading();
1882 0 : return eResult;
1883 : }
1884 :
1885 : /*
1886 : * SetSelectedFields()
1887 : */
1888 0 : OGRErr OGRNGWLayer::SetSelectedFields(const std::set<std::string> &aosFields)
1889 : {
1890 0 : CPLStringList aosIgnoreFields;
1891 0 : for (int iField = 0; iField < poFeatureDefn->GetFieldCount(); ++iField)
1892 : {
1893 0 : OGRFieldDefn *poFieldDefn = poFeatureDefn->GetFieldDefn(iField);
1894 0 : if (aosFields.find(poFieldDefn->GetNameRef()) != aosFields.end())
1895 : {
1896 0 : continue;
1897 : }
1898 0 : aosIgnoreFields.AddString(poFieldDefn->GetNameRef());
1899 : }
1900 0 : return SetIgnoredFields(aosIgnoreFields.List());
1901 : }
1902 :
1903 : /*
1904 : * Clone()
1905 : */
1906 0 : OGRNGWLayer *OGRNGWLayer::Clone() const
1907 : {
1908 0 : return new OGRNGWLayer(osResourceId, poDS, stPermissions,
1909 0 : poFeatureDefn->Clone(), nFeatureCount, stExtent);
1910 : }
1911 :
1912 : /*
1913 : * GetNewFeaturesCount()
1914 : */
1915 0 : GIntBig OGRNGWLayer::GetNewFeaturesCount() const
1916 : {
1917 0 : if (soChangedIds.empty())
1918 : {
1919 0 : return 0;
1920 : }
1921 :
1922 0 : if (*soChangedIds.begin() >= 0)
1923 : {
1924 0 : return 0;
1925 : }
1926 :
1927 : // The lowest negative identifier equal new feature count
1928 0 : return *soChangedIds.begin() * -1;
1929 : }
|