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