Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: S-101 driver
4 : * Purpose: Implements OGRS101Reader
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2026, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "ogr_s101.h"
14 : #include "ogrs101featurecatalog.h"
15 : #include "ogrs101readerconstants.h"
16 :
17 : #include <algorithm>
18 : #include <memory>
19 :
20 : /************************************************************************/
21 : /* CreateFeatureTypeFeatureDefns() */
22 : /************************************************************************/
23 :
24 : /** Create the feature definitions for the various kinds of Feature Type records
25 : */
26 342 : bool OGRS101Reader::CreateFeatureTypeFeatureDefns()
27 : {
28 342 : const int nCount = m_oFeatureTypeRecordIndex.GetCount();
29 :
30 : struct FeatureTypeDef
31 : {
32 : int nMaxMaskCount = 0;
33 : bool bMultiSpatialAssociations = false;
34 : bool bPromotedToMultiPointFromPoint = false;
35 : std::vector<int> anRecordIdx{};
36 : };
37 :
38 684 : std::map<FeatureTypeKey, FeatureTypeDef> oMapFeatureClassAndGeomType;
39 :
40 : // First pass to collect all (feature type code, geometry type, CRS Id) triples
41 762 : for (int iRecord = 0; iRecord < nCount; ++iRecord)
42 : {
43 422 : const auto poRecord = m_oFeatureTypeRecordIndex.GetByIndex(iRecord);
44 422 : FeatureTypeKey key;
45 422 : key.nFeatureTypeCode =
46 : poRecord->GetIntSubfield(FRID_FIELD, 0, NFTC_SUBFIELD, 0);
47 :
48 927 : const auto NormalizeCurve = [](RecordName name)
49 : {
50 927 : return name == RECORD_NAME_COMPOSITE_CURVE ? RECORD_NAME_CURVE
51 927 : : name;
52 : };
53 :
54 422 : bool bMultiSpatialAssociations = false;
55 : // S-101 page 19: A feature may reference multiple geometries
56 : // but must only reference geometries of a single
57 : // geometric primitive (point, pointset, curve or surface).
58 422 : const auto apoSPASFields = poRecord->GetFields(SPAS_FIELD);
59 422 : int nSPASCount = 0;
60 422 : bool bHeterogeneous = false;
61 830 : for (int iSPASField = 0;
62 830 : iSPASField < static_cast<int>(apoSPASFields.size()); ++iSPASField)
63 : {
64 408 : const auto poSPASField = apoSPASFields[iSPASField];
65 408 : if (iSPASField == 0)
66 : {
67 : key.nGeometryType = NormalizeCurve(
68 375 : poRecord->GetIntSubfield(poSPASField, RRNM_SUBFIELD, 0));
69 : }
70 :
71 408 : const int nSPASCountThisIter = poSPASField->GetRepeatCount();
72 408 : nSPASCount += nSPASCountThisIter;
73 958 : for (int iSPAS = 0; iSPAS < nSPASCountThisIter; ++iSPAS)
74 : {
75 552 : if (NormalizeCurve(poRecord->GetIntSubfield(
76 552 : poSPASField, RRNM_SUBFIELD, iSPAS)) !=
77 : key.nGeometryType)
78 : {
79 2 : bHeterogeneous = true;
80 2 : break;
81 : }
82 : }
83 : }
84 422 : if (bHeterogeneous)
85 : {
86 2 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
87 : "Record index %d of FRID: has %d "
88 : "spatial associations with at least 2 "
89 : "not being of the same geometry type%s",
90 : iRecord, nSPASCount, m_bStrict ? "" : ". Ignoring it")))
91 : {
92 1 : return false;
93 : }
94 1 : continue;
95 : }
96 420 : else if (nSPASCount > 1)
97 : {
98 173 : bMultiSpatialAssociations = true;
99 : }
100 :
101 420 : if (key.nGeometryType == RECORD_NAME_POINT)
102 : {
103 : const int nRRID =
104 98 : poRecord->GetIntSubfield(SPAS_FIELD, 0, RRID_SUBFIELD, 0);
105 98 : const auto poGeomRecord = m_oPointRecordIndex.FindRecord(nRRID);
106 98 : if (!poGeomRecord && !EMIT_ERROR_OR_WARNING(CPLSPrintf(
107 : "Record index %d of FRID: Point of id %d "
108 : "does not exist",
109 : iRecord, nRRID)))
110 : {
111 1 : return false;
112 : }
113 :
114 : const auto nCRSId =
115 96 : poGeomRecord ? GetCRSIdForPointRecord(poGeomRecord, -1, nRRID)
116 97 : : HORIZONTAL_CRS_ID;
117 97 : if (nCRSId == INVALID_CRS_ID)
118 : {
119 : // Error already emitted
120 0 : if (m_bStrict)
121 0 : return false;
122 0 : continue;
123 : }
124 97 : key.nCRSId = nCRSId;
125 : }
126 322 : else if (key.nGeometryType == RECORD_NAME_MULTIPOINT)
127 : {
128 : const int nRRID =
129 66 : poRecord->GetIntSubfield(SPAS_FIELD, 0, RRID_SUBFIELD, 0);
130 : const auto poGeomRecord =
131 66 : m_oMultiPointRecordIndex.FindRecord(nRRID);
132 66 : if (!poGeomRecord &&
133 0 : !EMIT_ERROR_OR_WARNING(
134 : CPLSPrintf("Record index %d of FRID: MultiPoint of id %d "
135 : "does not exist",
136 : iRecord, nRRID)))
137 : {
138 0 : return false;
139 : }
140 :
141 : const auto nCRSId =
142 : poGeomRecord
143 66 : ? GetCRSIdForMultiPointRecord(poGeomRecord, -1, nRRID)
144 66 : : HORIZONTAL_CRS_ID;
145 66 : if (nCRSId == INVALID_CRS_ID)
146 : {
147 : // Error already emitted
148 0 : if (m_bStrict)
149 0 : return false;
150 0 : continue;
151 : }
152 66 : key.nCRSId = nCRSId;
153 : }
154 256 : else if (!apoSPASFields.empty())
155 209 : key.nCRSId = HORIZONTAL_CRS_ID;
156 :
157 : const bool bPromotedToMultiPointFromPoint =
158 419 : (key.nGeometryType == RECORD_NAME_POINT &&
159 419 : bMultiSpatialAssociations);
160 419 : if (bPromotedToMultiPointFromPoint)
161 65 : key.nGeometryType = RECORD_NAME_MULTIPOINT;
162 :
163 419 : auto &featureTypeDef = oMapFeatureClassAndGeomType[key];
164 419 : if (bPromotedToMultiPointFromPoint)
165 : {
166 65 : featureTypeDef.bPromotedToMultiPointFromPoint = true;
167 : }
168 354 : else if (bMultiSpatialAssociations)
169 : {
170 108 : featureTypeDef.bMultiSpatialAssociations = true;
171 : }
172 :
173 419 : int nMaskCount = 0;
174 495 : for (const auto poField : poRecord->GetFields(MASK_FIELD))
175 : {
176 76 : nMaskCount += poField->GetRepeatCount();
177 : }
178 419 : featureTypeDef.nMaxMaskCount =
179 419 : std::max(featureTypeDef.nMaxMaskCount, nMaskCount);
180 :
181 419 : featureTypeDef.anRecordIdx.push_back(iRecord);
182 : }
183 :
184 640 : for (auto &[key, def] : oMapFeatureClassAndGeomType)
185 : {
186 302 : std::string osLayerCode;
187 302 : std::string osName;
188 302 : const auto oIter = m_featureTypeCodes.find(key.nFeatureTypeCode);
189 302 : if (oIter != m_featureTypeCodes.end())
190 : {
191 300 : osLayerCode = oIter->second;
192 300 : osName = osLayerCode;
193 : }
194 : else
195 : {
196 2 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
197 : "Features pointing at unknown feature type code %d",
198 : static_cast<int>(key.nFeatureTypeCode))))
199 : {
200 1 : return false;
201 : }
202 : osName = CPLSPrintf("unknownFeatureType%d",
203 1 : static_cast<int>(key.nFeatureTypeCode));
204 : }
205 :
206 301 : const auto oIterSRS = m_oMapSRS.find(key.nCRSId);
207 301 : CPLAssert(key.nCRSId == INVALID_CRS_ID || oIterSRS != m_oMapSRS.end());
208 : const OGRSpatialReference *poSRS =
209 301 : key.nCRSId != INVALID_CRS_ID ? &(oIterSRS->second) : nullptr;
210 301 : const bool bIs2D = key.nCRSId == HORIZONTAL_CRS_ID;
211 :
212 301 : OGRwkbGeometryType eGeomType = wkbNone;
213 301 : const char *pszExpectedPermittedPrimitive = nullptr;
214 301 : switch (static_cast<int>(key.nGeometryType))
215 : {
216 44 : case static_cast<int>(PSEUDO_RECORD_NAME_NO_GEOM):
217 : {
218 44 : osName += "_NoGeom";
219 44 : pszExpectedPermittedPrimitive =
220 : OGRS101FeatureCatalog::PERMITTED_PRIMITIVE_NO_GEOMETRY;
221 44 : break;
222 : }
223 :
224 30 : case static_cast<int>(RECORD_NAME_POINT):
225 : {
226 30 : CPLAssert(poSRS);
227 30 : osName += '_';
228 30 : if (def.bMultiSpatialAssociations)
229 : {
230 0 : osName += GetMultiPointLayerName(*poSRS);
231 0 : eGeomType = bIs2D ? wkbMultiPoint : wkbMultiPoint25D;
232 : }
233 : else
234 : {
235 30 : osName += GetPointLayerName(*poSRS);
236 30 : eGeomType = bIs2D ? wkbPoint : wkbPoint25D;
237 : }
238 30 : pszExpectedPermittedPrimitive =
239 : OGRS101FeatureCatalog::PERMITTED_PRIMITIVE_POINT;
240 30 : break;
241 : }
242 :
243 92 : case static_cast<int>(RECORD_NAME_MULTIPOINT):
244 : {
245 92 : CPLAssert(poSRS);
246 92 : osName += '_';
247 92 : if (def.bMultiSpatialAssociations)
248 : {
249 31 : osName += "CollectionOfMultiPoint";
250 31 : eGeomType = wkbGeometryCollection;
251 : }
252 : else
253 : {
254 61 : osName += GetMultiPointLayerName(*poSRS);
255 61 : eGeomType = bIs2D ? wkbMultiPoint : wkbMultiPoint25D;
256 : }
257 92 : pszExpectedPermittedPrimitive =
258 : OGRS101FeatureCatalog::PERMITTED_PRIMITIVE_POINTSET;
259 92 : break;
260 : }
261 :
262 62 : case static_cast<int>(RECORD_NAME_CURVE):
263 : {
264 62 : if (def.bMultiSpatialAssociations)
265 : {
266 31 : osName += "_MultiLine";
267 31 : eGeomType = wkbMultiLineString;
268 : }
269 : else
270 : {
271 31 : osName += "_Line";
272 31 : eGeomType = wkbLineString;
273 : }
274 62 : pszExpectedPermittedPrimitive =
275 : OGRS101FeatureCatalog::PERMITTED_PRIMITIVE_CURVE;
276 62 : break;
277 : }
278 :
279 71 : case static_cast<int>(RECORD_NAME_SURFACE):
280 : {
281 71 : if (def.bMultiSpatialAssociations)
282 : {
283 40 : osName += "_MultiPolygon";
284 40 : eGeomType = wkbMultiPolygon;
285 : }
286 : else
287 : {
288 31 : osName += "_Polygon";
289 31 : eGeomType = wkbPolygon;
290 : }
291 71 : pszExpectedPermittedPrimitive =
292 : OGRS101FeatureCatalog::PERMITTED_PRIMITIVE_SURFACE;
293 71 : break;
294 : }
295 :
296 2 : default:
297 : {
298 2 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
299 : "Features pointing at unknown spatial record type %d",
300 : static_cast<int>(key.nGeometryType))))
301 : {
302 1 : return false;
303 : }
304 1 : osName += "_UnknownGeomType";
305 1 : osName += std::to_string(static_cast<int>(key.nGeometryType));
306 1 : break;
307 : }
308 : }
309 :
310 300 : const OGRS101FeatureCatalog::FeatureType *psFeatureType = nullptr;
311 300 : if (m_poFeatureCatalog)
312 : {
313 : const auto oIterFT =
314 300 : m_poFeatureCatalog->GetFeatureTypes().find(osLayerCode);
315 300 : if (oIterFT != m_poFeatureCatalog->GetFeatureTypes().end())
316 : {
317 0 : psFeatureType = &(oIterFT->second);
318 : }
319 301 : else if (!cpl::starts_with(osLayerCode, "FeatureType") &&
320 1 : !EMIT_ERROR_OR_WARNING(
321 : CPLSPrintf("Feature type %s is not referenced in the "
322 : "feature catalog",
323 : osLayerCode.c_str())))
324 : {
325 0 : return false;
326 : }
327 : }
328 :
329 299 : if (pszExpectedPermittedPrimitive && psFeatureType &&
330 0 : !cpl::contains(psFeatureType->permittedPrimitives,
331 599 : pszExpectedPermittedPrimitive) &&
332 0 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
333 : "Features of %s contain records with primitive %s, whereas "
334 : "it is not allowed by the feature catalog",
335 : osLayerCode.c_str(), pszExpectedPermittedPrimitive)))
336 : {
337 0 : return false;
338 : }
339 :
340 : auto poFDefn =
341 300 : OGRFeatureDefnRefCountedPtr::makeInstance(osName.c_str());
342 300 : poFDefn->SetGeomType(eGeomType);
343 1500 : for (const char *pszOGRFieldName :
344 : {OGR_FIELD_NAME_RECORD_ID, OGR_FIELD_NAME_RECORD_VERSION,
345 1800 : OGR_FIELD_NAME_AGEN, OGR_FIELD_NAME_FIDN, OGR_FIELD_NAME_FIDS})
346 : {
347 3000 : OGRFieldDefn oFieldDefn(pszOGRFieldName, OFTInteger);
348 1500 : poFDefn->AddFieldDefn(&oFieldDefn);
349 : }
350 :
351 300 : if (eGeomType != wkbNone)
352 : {
353 255 : CPLAssert(poSRS);
354 510 : poFDefn->GetGeomFieldDefn(0)->SetSpatialRef(
355 510 : OGRSpatialReferenceRefCountedPtr::makeClone(poSRS).get());
356 255 : poFDefn->GetGeomFieldDefn(0)->SetCoordinatePrecision(
357 255 : m_coordinatePrecision);
358 408 : const bool bList = def.bMultiSpatialAssociations ||
359 153 : def.bPromotedToMultiPointFromPoint;
360 :
361 : {
362 : OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_GEOMETRY_LAYER_NAME,
363 510 : bList ? OFTStringList : OFTString);
364 255 : poFDefn->AddFieldDefn(&oFieldDefn);
365 : }
366 255 : for (const char *pszOGRFieldName :
367 510 : {OGR_FIELD_NAME_GEOMETRY_RECORD_ID})
368 : {
369 : OGRFieldDefn oFieldDefn(pszOGRFieldName,
370 510 : bList ? OFTIntegerList : OFTInteger);
371 255 : poFDefn->AddFieldDefn(&oFieldDefn);
372 : }
373 255 : if (eGeomType == wkbLineString || eGeomType == wkbMultiLineString)
374 : {
375 : OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_GEOMETRY_ORIENTATION,
376 124 : bList ? OFTStringList : OFTString);
377 62 : poFDefn->AddFieldDefn(&oFieldDefn);
378 : }
379 510 : for (const char *pszOGRFieldName :
380 765 : {OGR_FIELD_NAME_SMIN, OGR_FIELD_NAME_SMAX})
381 : {
382 : OGRFieldDefn oFieldDefn(pszOGRFieldName,
383 1020 : bList ? OFTIntegerList : OFTInteger);
384 510 : poFDefn->AddFieldDefn(&oFieldDefn);
385 : }
386 : }
387 :
388 900 : for (const char *pszAttrFieldName :
389 1200 : {ATTR_FIELD, INAS_FIELD, FASC_FIELD})
390 : {
391 1800 : if (!InferFeatureDefn(
392 900 : m_oFeatureTypeRecordIndex, FRID_FIELD, pszAttrFieldName,
393 900 : def.anRecordIdx, *poFDefn, m_oMapFieldDomains, nullptr,
394 900 : strcmp(pszAttrFieldName, ATTR_FIELD) == 0 ? psFeatureType
395 : : nullptr))
396 : {
397 0 : return false;
398 : }
399 : }
400 :
401 300 : if (def.nMaxMaskCount == 1)
402 : {
403 : {
404 : OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_MASK_LAYER_NAME,
405 12 : OFTString);
406 6 : poFDefn->AddFieldDefn(&oFieldDefn);
407 : }
408 : {
409 : OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_MASK_RECORD_ID,
410 12 : OFTInteger);
411 6 : poFDefn->AddFieldDefn(&oFieldDefn);
412 : }
413 : {
414 : OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_MASK_INDICATOR,
415 12 : OFTString);
416 6 : poFDefn->AddFieldDefn(&oFieldDefn);
417 : }
418 :
419 12 : OGRGeomFieldDefn oGeomFieldDefn("maskGeometry", wkbLineString);
420 6 : oGeomFieldDefn.SetSpatialRef(
421 6 : OGRSpatialReferenceRefCountedPtr::makeClone(
422 6 : &(m_oMapSRS[HORIZONTAL_CRS_ID]))
423 6 : .get());
424 6 : poFDefn->AddGeomFieldDefn(&oGeomFieldDefn);
425 : }
426 294 : else if (def.nMaxMaskCount > 1)
427 : {
428 : {
429 : OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_MASK_LAYER_NAME,
430 68 : OFTStringList);
431 34 : poFDefn->AddFieldDefn(&oFieldDefn);
432 : }
433 : {
434 : OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_MASK_RECORD_ID,
435 68 : OFTIntegerList);
436 34 : poFDefn->AddFieldDefn(&oFieldDefn);
437 : }
438 : {
439 : OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_MASK_INDICATOR,
440 68 : OFTStringList);
441 34 : poFDefn->AddFieldDefn(&oFieldDefn);
442 : }
443 :
444 68 : OGRGeomFieldDefn oGeomFieldDefn("maskGeometry", wkbMultiLineString);
445 34 : oGeomFieldDefn.SetSpatialRef(
446 34 : OGRSpatialReferenceRefCountedPtr::makeClone(
447 34 : &(m_oMapSRS[HORIZONTAL_CRS_ID]))
448 34 : .get());
449 34 : poFDefn->AddGeomFieldDefn(&oGeomFieldDefn);
450 : }
451 :
452 693 : for (int nRecordIdx : def.anRecordIdx)
453 : {
454 : const int nRCID =
455 : m_oFeatureTypeRecordIndex.GetByIndex(nRecordIdx)
456 393 : ->GetIntSubfield(FRID_FIELD, 0, RCID_SUBFIELD, 0);
457 393 : m_oMapFeatureTypeIdToFDefn[nRCID] = poFDefn.get();
458 : }
459 :
460 600 : LayerDef layerDef;
461 300 : layerDef.poFeatureDefn = std::move(poFDefn);
462 300 : if (psFeatureType)
463 : {
464 0 : layerDef.osName = psFeatureType->name;
465 0 : layerDef.osDefinition = psFeatureType->definition;
466 0 : layerDef.osAlias = psFeatureType->alias;
467 : }
468 300 : layerDef.anRecordIndices = std::move(def.anRecordIdx);
469 300 : m_oMapFeatureKeyToLayerDef[key] = std::move(layerDef);
470 : }
471 :
472 338 : return true;
473 : }
474 :
475 : /************************************************************************/
476 : /* ReadGeometry() */
477 : /************************************************************************/
478 :
479 : template <typename T, typename GeomReaderMethodType>
480 1313 : bool OGRS101Reader::ReadGeometry(
481 : const DDFRecordIndex &oIndex, const char *pszErrorContext,
482 : int nGeomRecordID, const char *pszGeomType, bool bReverse,
483 : OGRFeature &oFeature, std::unique_ptr<OGRGeometryCollection> &poMultiGeom,
484 : GeomReaderMethodType geomReaderMethod, int iGeomField) const
485 : {
486 1313 : const auto poGeomRecord = oIndex.FindRecord(nGeomRecordID);
487 :
488 1313 : if (!poGeomRecord && !EMIT_ERROR_OR_WARNING(CPLSPrintf(
489 : "%s: %s of ID=%d does not exist", pszErrorContext,
490 : pszGeomType, nGeomRecordID)))
491 : {
492 3 : return false;
493 : }
494 :
495 : const auto poGeomFieldDefn =
496 1310 : oFeature.GetDefnRef()->GetGeomFieldDefn(iGeomField);
497 1310 : const OGRSpatialReference *poSRS = poGeomFieldDefn->GetSpatialRef();
498 1310 : std::unique_ptr<T> poGeom;
499 1310 : if (poGeomRecord)
500 : {
501 1303 : poGeom = (this->*geomReaderMethod)(poGeomRecord, /* index = */ -1,
502 : nGeomRecordID, poSRS);
503 : }
504 1310 : if (!poGeom)
505 : {
506 7 : if (poGeomRecord && !EMIT_ERROR_OR_WARNING(CPLSPrintf(
507 : "%s: %s of ID=%d is invalid", pszErrorContext,
508 : pszGeomType, nGeomRecordID)))
509 : {
510 0 : return false;
511 : }
512 : }
513 : else if constexpr (std::is_same_v<T, OGRLineString>)
514 : {
515 525 : if (bReverse)
516 103 : poGeom->reversePoints();
517 : }
518 :
519 1310 : const auto eGeomType = poGeomFieldDefn->GetType();
520 :
521 2456 : if (wkbFlatten(eGeomType) == wkbGeometryCollection ||
522 : (!std::is_same_v<T, OGRMultiPoint> &&
523 1146 : OGR_GT_IsSubClassOf(eGeomType, wkbGeometryCollection)))
524 : {
525 936 : if (!poMultiGeom)
526 : {
527 434 : poMultiGeom = std::make_unique<typename T::MultiType>();
528 434 : poMultiGeom->assignSpatialReference(poSRS);
529 : }
530 936 : poMultiGeom->addGeometry(poGeom ? std::move(poGeom)
531 : : std::make_unique<T>());
532 : }
533 : else
534 : {
535 374 : oFeature.SetGeomField(iGeomField, std::move(poGeom));
536 : }
537 :
538 1310 : return true;
539 : }
540 :
541 : /************************************************************************/
542 : /* FillFeatureTypeGeometry() */
543 : /************************************************************************/
544 :
545 : /** Fill the geometry of the provided feature from the identified record
546 : * (of m_oFeatureTypeRecordIndex).
547 : */
548 789 : bool OGRS101Reader::FillFeatureTypeGeometry(const DDFRecord *poRecord,
549 : int iRecord,
550 : OGRFeature &oFeature) const
551 : {
552 : // Process geometry (several spatial association per feature possible)
553 1578 : CPLStringList aosLayerNames, aosOrientations;
554 1578 : std::vector<int> anRRID, anSMIN, anSMAX;
555 789 : std::unique_ptr<OGRGeometryCollection> poMultiGeom;
556 :
557 1578 : const auto apoSPASFields = poRecord->GetFields(SPAS_FIELD);
558 1532 : for (const auto &poSPASField : apoSPASFields)
559 : {
560 749 : const int nSPASCount = poSPASField->GetRepeatCount();
561 :
562 1796 : for (int iSPAS = 0; iSPAS < nSPASCount; ++iSPAS)
563 : {
564 : const auto GetIntSubfield =
565 6301 : [poRecord, poSPASField, iSPAS](const char *pszSubFieldName)
566 : {
567 6301 : return poRecord->GetIntSubfield(poSPASField, pszSubFieldName,
568 6301 : iSPAS);
569 1053 : };
570 :
571 : const std::string osErrorContext =
572 : CPLSPrintf("Feature type record index %d, SPAS instance %d",
573 1053 : iRecord, iSPAS);
574 :
575 1053 : const int nSAUI = GetIntSubfield(SAUI_SUBFIELD);
576 1053 : if (nSAUI != INSTRUCTION_INSERT)
577 : {
578 3 : if (!EMIT_ERROR_OR_WARNING(
579 : CPLSPrintf("%s: SAUI value %d is invalid",
580 : osErrorContext.c_str(), nSAUI)))
581 : {
582 1 : return false;
583 : }
584 : }
585 :
586 1052 : const RecordName nRRNM = GetIntSubfield(RRNM_SUBFIELD);
587 :
588 1052 : const int nRRID = GetIntSubfield(RRID_SUBFIELD);
589 :
590 1052 : const int nORNT = GetIntSubfield(ORNT_SUBFIELD);
591 :
592 1883 : const bool bIsLine = nRRNM == RECORD_NAME_CURVE ||
593 831 : nRRNM == RECORD_NAME_COMPOSITE_CURVE;
594 1052 : switch (nORNT)
595 : {
596 164 : case ORNT_FORWARD:
597 : {
598 164 : break;
599 : }
600 :
601 106 : case ORNT_REVERSE:
602 : {
603 106 : if (!bIsLine)
604 : {
605 3 : if (!EMIT_ERROR_OR_WARNING(
606 : CPLSPrintf("%s: "
607 : "ORNT = Reverse is invalid for "
608 : "non-curve geometry",
609 : osErrorContext.c_str())))
610 : {
611 1 : return false;
612 : }
613 : }
614 105 : break;
615 : }
616 :
617 779 : case ORNT_NULL:
618 : {
619 779 : if (bIsLine)
620 : {
621 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
622 : "%s: ORNT = Null is invalid for curve geometry",
623 : osErrorContext.c_str())))
624 : {
625 0 : return false;
626 : }
627 : }
628 779 : break;
629 : }
630 :
631 3 : default:
632 : {
633 3 : if (!EMIT_ERROR_OR_WARNING(
634 : CPLSPrintf("%s: ORNT = %d is invalid",
635 : osErrorContext.c_str(), nORNT)))
636 : {
637 1 : return false;
638 : }
639 2 : break;
640 : }
641 : }
642 :
643 1050 : bool bInvalidType = false;
644 1050 : switch (static_cast<int>(nRRNM))
645 : {
646 302 : case static_cast<int>(RECORD_NAME_POINT):
647 : {
648 604 : if (!ReadGeometry<OGRPoint>(
649 302 : m_oPointRecordIndex, osErrorContext.c_str(), nRRID,
650 : "Point", false, oFeature, poMultiGeom,
651 : &OGRS101Reader::ReadPointGeometry))
652 : {
653 0 : return false;
654 : }
655 :
656 : const auto poGeomFieldDefn =
657 302 : oFeature.GetDefnRef()->GetGeomFieldDefn(0);
658 302 : CPLAssert(poGeomFieldDefn);
659 : const OGRSpatialReference *poSRS =
660 302 : poGeomFieldDefn->GetSpatialRef();
661 302 : CPLAssert(poSRS);
662 :
663 302 : aosLayerNames.push_back(GetPointLayerName(*poSRS).c_str());
664 :
665 302 : break;
666 : }
667 :
668 164 : case static_cast<int>(RECORD_NAME_MULTIPOINT):
669 : {
670 328 : if (!ReadGeometry<OGRMultiPoint>(
671 164 : m_oMultiPointRecordIndex, osErrorContext.c_str(),
672 : nRRID, "MultiPoint", false, oFeature, poMultiGeom,
673 : &OGRS101Reader::ReadMultiPointGeometry))
674 : {
675 0 : return false;
676 : }
677 :
678 : const auto poGeomFieldDefn =
679 164 : oFeature.GetDefnRef()->GetGeomFieldDefn(0);
680 164 : CPLAssert(poGeomFieldDefn);
681 : const OGRSpatialReference *poSRS =
682 164 : poGeomFieldDefn->GetSpatialRef();
683 164 : CPLAssert(poSRS);
684 :
685 164 : aosLayerNames.push_back(
686 328 : GetMultiPointLayerName(*poSRS).c_str());
687 :
688 164 : break;
689 : }
690 :
691 267 : case static_cast<int>(RECORD_NAME_CURVE):
692 : case static_cast<int>(RECORD_NAME_COMPOSITE_CURVE):
693 : {
694 : const char *pszLayerName =
695 267 : nRRNM == RECORD_NAME_CURVE
696 : ? OGR_LAYER_NAME_CURVE
697 267 : : OGR_LAYER_NAME_COMPOSITE_CURVE;
698 :
699 : bool ret;
700 267 : if (nRRNM == RECORD_NAME_CURVE)
701 : {
702 442 : ret = ReadGeometry<OGRLineString>(
703 221 : m_oCurveRecordIndex, osErrorContext.c_str(), nRRID,
704 : pszLayerName, nORNT == ORNT_REVERSE, oFeature,
705 : poMultiGeom, &OGRS101Reader::ReadCurveGeometry);
706 : }
707 : else
708 : {
709 92 : ret = ReadGeometry<OGRLineString>(
710 46 : m_oCompositeCurveRecordIndex,
711 : osErrorContext.c_str(), nRRID, pszLayerName,
712 : nORNT == ORNT_REVERSE, oFeature, poMultiGeom,
713 : &OGRS101Reader::ReadCompositeCurveGeometry);
714 : }
715 :
716 267 : if (!ret)
717 : {
718 2 : return false;
719 : }
720 :
721 265 : aosLayerNames.push_back(pszLayerName);
722 265 : aosOrientations.push_back(
723 : nORNT == ORNT_FORWARD ? "forward" : "reverse");
724 :
725 265 : break;
726 : }
727 :
728 316 : case static_cast<int>(RECORD_NAME_SURFACE):
729 : {
730 632 : if (!ReadGeometry<OGRPolygon>(
731 316 : m_oSurfaceRecordIndex, osErrorContext.c_str(),
732 : nRRID, OGR_LAYER_NAME_SURFACE, false, oFeature,
733 : poMultiGeom, &OGRS101Reader::ReadSurfaceGeometry))
734 : {
735 1 : return false;
736 : }
737 :
738 315 : aosLayerNames.push_back(OGR_LAYER_NAME_SURFACE);
739 :
740 315 : break;
741 : }
742 :
743 1 : default:
744 1 : bInvalidType = true;
745 1 : break;
746 : }
747 :
748 1047 : if (bInvalidType)
749 : {
750 1 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf("%s: "
751 : "Invalid RRNM = %d",
752 : osErrorContext.c_str(),
753 : static_cast<int>(nRRNM))))
754 : {
755 0 : return false;
756 : }
757 : }
758 : else
759 : {
760 1046 : anRRID.push_back(nRRID);
761 :
762 1046 : anSMIN.push_back(GetIntSubfield(SMIN_SUBFIELD));
763 :
764 1046 : anSMAX.push_back(GetIntSubfield(SMAX_SUBFIELD));
765 : }
766 : }
767 : }
768 :
769 783 : if (!apoSPASFields.empty())
770 : {
771 683 : CPLAssert(anRRID.size() == anSMIN.size());
772 683 : CPLAssert(anRRID.size() == anSMAX.size());
773 683 : CPLAssert(anRRID.size() == static_cast<size_t>(aosLayerNames.size()));
774 683 : CPLAssert(aosOrientations.empty() ||
775 : anRRID.size() == static_cast<size_t>(aosOrientations.size()));
776 :
777 683 : if (poMultiGeom)
778 : {
779 364 : oFeature.SetGeometry(std::move(poMultiGeom));
780 :
781 364 : oFeature.SetField(OGR_FIELD_NAME_GEOMETRY_LAYER_NAME,
782 364 : aosLayerNames.List());
783 364 : if (!aosOrientations.empty())
784 : {
785 57 : oFeature.SetField(OGR_FIELD_NAME_GEOMETRY_ORIENTATION,
786 57 : aosOrientations.List());
787 : }
788 364 : oFeature.SetField(OGR_FIELD_NAME_GEOMETRY_RECORD_ID,
789 364 : static_cast<int>(anRRID.size()), anRRID.data());
790 364 : if (std::find_if(anSMIN.begin(), anSMIN.end(),
791 847 : [](int x) { return x > 0; }) != anSMIN.end())
792 : {
793 245 : oFeature.SetField(OGR_FIELD_NAME_SMIN,
794 245 : static_cast<int>(anSMIN.size()),
795 245 : anSMIN.data());
796 : }
797 364 : if (std::find_if(anSMAX.begin(), anSMAX.end(),
798 847 : [](int x) { return x > 0; }) != anSMAX.end())
799 : {
800 245 : oFeature.SetField(OGR_FIELD_NAME_SMAX,
801 245 : static_cast<int>(anSMAX.size()),
802 245 : anSMAX.data());
803 : }
804 : }
805 319 : else if (!aosLayerNames.empty())
806 : {
807 318 : oFeature.SetField(OGR_FIELD_NAME_GEOMETRY_LAYER_NAME,
808 318 : aosLayerNames[0]);
809 318 : oFeature.SetField(OGR_FIELD_NAME_GEOMETRY_RECORD_ID, anRRID[0]);
810 318 : if (!aosOrientations.empty())
811 : {
812 151 : oFeature.SetField(OGR_FIELD_NAME_GEOMETRY_ORIENTATION,
813 151 : aosOrientations[0]);
814 : }
815 318 : if (anSMIN[0] > 0)
816 254 : oFeature.SetField(OGR_FIELD_NAME_SMIN, anSMIN[0]);
817 318 : if (anSMAX[0] > 0)
818 254 : oFeature.SetField(OGR_FIELD_NAME_SMAX, anSMAX[0]);
819 : }
820 : }
821 :
822 783 : return true;
823 : }
824 :
825 : /************************************************************************/
826 : /* FillFeatureTypeMask() */
827 : /************************************************************************/
828 :
829 : /** Fill the mask info of the provided feature from the identified record
830 : * (of m_oFeatureTypeRecordIndex).
831 : */
832 783 : bool OGRS101Reader::FillFeatureTypeMask(const DDFRecord *poRecord, int iRecord,
833 : OGRFeature &oFeature) const
834 : {
835 783 : std::unique_ptr<OGRGeometryCollection> poMultiGeom;
836 :
837 1566 : std::vector<int> anRRID;
838 1566 : CPLStringList aosLayerNames;
839 1566 : CPLStringList aosMaskIndicators;
840 :
841 783 : if (oFeature.GetDefnRef()->GetGeomFieldCount() != 2)
842 654 : return true;
843 :
844 129 : constexpr int MASK_GEOM_FIELD_IDX = 1;
845 :
846 258 : const auto apoMASKFields = poRecord->GetFields(MASK_FIELD);
847 325 : for (const auto *poMASKField : apoMASKFields)
848 : {
849 199 : const int nMaskCount = poMASKField->GetRepeatCount();
850 :
851 465 : for (int iMASK = 0; iMASK < nMaskCount; ++iMASK)
852 : {
853 : const auto GetIntSubfield =
854 1071 : [poRecord, poMASKField, iMASK](const char *pszSubFieldName)
855 : {
856 1071 : return poRecord->GetIntSubfield(poMASKField, pszSubFieldName,
857 1071 : iMASK);
858 269 : };
859 :
860 : const std::string osErrorContext =
861 : CPLSPrintf("Feature type record index %d, MASK instance %d",
862 269 : iRecord, iMASK);
863 :
864 269 : const int nMUIN = GetIntSubfield(MUIN_SUBFIELD);
865 269 : if (nMUIN != INSTRUCTION_INSERT)
866 : {
867 3 : if (!EMIT_ERROR_OR_WARNING(
868 : CPLSPrintf("%s: MUIN value %d is invalid",
869 : osErrorContext.c_str(), nMUIN)))
870 : {
871 1 : return false;
872 : }
873 : }
874 :
875 268 : const int nMIND = GetIntSubfield(MIND_SUBFIELD);
876 268 : constexpr int MASK_INDICATOR_TRUNCATED_BY_DATA_COVERAGE_LIMIT = 1;
877 268 : constexpr int MASK_INDICATOR_SUPPRESS_PORTRAYAL = 2;
878 268 : if (nMIND == MASK_INDICATOR_TRUNCATED_BY_DATA_COVERAGE_LIMIT)
879 : {
880 195 : aosMaskIndicators.push_back("truncatedByDataCoverageLimit");
881 : }
882 73 : else if (nMIND == MASK_INDICATOR_SUPPRESS_PORTRAYAL)
883 : {
884 70 : aosMaskIndicators.push_back("suppressPortrayal");
885 : }
886 : else
887 : {
888 3 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
889 : "%s: MIND value %d is invalid. Expected %d or %d",
890 : osErrorContext.c_str(), nMIND,
891 : MASK_INDICATOR_TRUNCATED_BY_DATA_COVERAGE_LIMIT,
892 : MASK_INDICATOR_SUPPRESS_PORTRAYAL)))
893 : {
894 1 : return false;
895 : }
896 2 : aosMaskIndicators.push_back(CPLSPrintf("unknown%d", nMIND));
897 : }
898 :
899 267 : const int nRRID = GetIntSubfield(RRID_SUBFIELD);
900 267 : anRRID.push_back(nRRID);
901 :
902 267 : const RecordName nRRNM = GetIntSubfield(RRNM_SUBFIELD);
903 340 : if (nRRNM == RECORD_NAME_CURVE ||
904 73 : nRRNM == RECORD_NAME_COMPOSITE_CURVE)
905 : {
906 264 : const char *pszLayerName = nRRNM == RECORD_NAME_CURVE
907 : ? OGR_LAYER_NAME_CURVE
908 264 : : OGR_LAYER_NAME_COMPOSITE_CURVE;
909 :
910 : bool ret;
911 264 : if (nRRNM == RECORD_NAME_CURVE)
912 : {
913 388 : ret = ReadGeometry<OGRLineString>(
914 194 : m_oCurveRecordIndex, osErrorContext.c_str(), nRRID,
915 : pszLayerName, false, oFeature, poMultiGeom,
916 : &OGRS101Reader::ReadCurveGeometry, MASK_GEOM_FIELD_IDX);
917 : }
918 : else
919 : {
920 140 : ret = ReadGeometry<OGRLineString>(
921 70 : m_oCompositeCurveRecordIndex, osErrorContext.c_str(),
922 : nRRID, pszLayerName, false, oFeature, poMultiGeom,
923 : &OGRS101Reader::ReadCompositeCurveGeometry,
924 : MASK_GEOM_FIELD_IDX);
925 : }
926 :
927 264 : if (!ret)
928 : {
929 0 : return false;
930 : }
931 :
932 264 : aosLayerNames.push_back(pszLayerName);
933 : }
934 : else
935 : {
936 3 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
937 : "%s: Invalid value for RRNM subfield: "
938 : "got %d, expected %d or %d.",
939 : osErrorContext.c_str(), static_cast<int>(nRRNM),
940 : static_cast<int>(RECORD_NAME_CURVE),
941 : static_cast<int>(RECORD_NAME_COMPOSITE_CURVE))))
942 : {
943 1 : return false;
944 : }
945 :
946 2 : aosLayerNames.push_back("");
947 : }
948 : }
949 : }
950 :
951 126 : if (!aosLayerNames.empty())
952 : {
953 126 : CPLAssert(anRRID.size() == static_cast<size_t>(aosLayerNames.size()));
954 126 : CPLAssert(anRRID.size() ==
955 : static_cast<size_t>(aosMaskIndicators.size()));
956 :
957 126 : if (poMultiGeom)
958 : {
959 70 : oFeature.SetGeomField(MASK_GEOM_FIELD_IDX, std::move(poMultiGeom));
960 :
961 70 : oFeature.SetField(OGR_FIELD_NAME_MASK_LAYER_NAME,
962 70 : aosLayerNames.List());
963 :
964 70 : oFeature.SetField(OGR_FIELD_NAME_MASK_RECORD_ID,
965 70 : static_cast<int>(anRRID.size()), anRRID.data());
966 :
967 70 : oFeature.SetField(OGR_FIELD_NAME_MASK_INDICATOR,
968 70 : aosMaskIndicators.List());
969 : }
970 : else
971 : {
972 56 : oFeature.SetField(OGR_FIELD_NAME_MASK_LAYER_NAME, aosLayerNames[0]);
973 :
974 56 : oFeature.SetField(OGR_FIELD_NAME_MASK_RECORD_ID, anRRID[0]);
975 :
976 56 : oFeature.SetField(OGR_FIELD_NAME_MASK_INDICATOR,
977 56 : aosMaskIndicators[0]);
978 : }
979 : }
980 :
981 126 : return true;
982 : }
983 :
984 : /************************************************************************/
985 : /* FillFeatureFeatureType() */
986 : /************************************************************************/
987 :
988 : /** Fill the content of the provided feature from the identified record
989 : * (of m_oFeatureTypeRecordIndex).
990 : */
991 789 : bool OGRS101Reader::FillFeatureFeatureType(const DDFRecordIndex &oIndex,
992 : int iRecord,
993 : OGRFeature &oFeature) const
994 : {
995 789 : const auto poRecord = oIndex.GetByIndex(iRecord);
996 789 : CPLAssert(poRecord);
997 :
998 789 : if (const auto poFOIDField = poRecord->FindField(FOID_FIELD))
999 : {
1000 : const int nAGEN =
1001 788 : poRecord->GetIntSubfield(poFOIDField, AGEN_SUBFIELD, 0);
1002 788 : oFeature.SetField(OGR_FIELD_NAME_AGEN, nAGEN);
1003 :
1004 : const int nFIDN =
1005 788 : poRecord->GetIntSubfield(poFOIDField, FIDN_SUBFIELD, 0);
1006 788 : oFeature.SetField(OGR_FIELD_NAME_FIDN, nFIDN);
1007 :
1008 : const int nFIDS =
1009 788 : poRecord->GetIntSubfield(poFOIDField, FIDS_SUBFIELD, 0);
1010 788 : oFeature.SetField(OGR_FIELD_NAME_FIDS, nFIDS);
1011 : }
1012 1 : else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
1013 : "Feature type record index %d: no FOID field", iRecord)))
1014 : {
1015 0 : return false;
1016 : }
1017 :
1018 : // A FRID record might have a ATTR field, a INAS field (pointing to a IRID
1019 : // record), a FASC field (thus pointing to another FRID record), or any
1020 : // combination of the 3.
1021 :
1022 789 : return FillFeatureTypeGeometry(poRecord, iRecord, oFeature) &&
1023 783 : FillFeatureTypeMask(poRecord, iRecord, oFeature) &&
1024 780 : FillFeatureAttributes(oIndex, iRecord, ATTR_FIELD, oFeature) &&
1025 780 : FillFeatureAttributes(oIndex, iRecord, INAS_FIELD, oFeature) &&
1026 780 : FillFeatureAttributes(oIndex, iRecord, FASC_FIELD, oFeature) &&
1027 780 : FillFeatureWithNonAttrAssocSubfields(poRecord, iRecord, INAS_FIELD,
1028 1572 : oFeature) &&
1029 780 : FillFeatureWithNonAttrAssocSubfields(poRecord, iRecord, FASC_FIELD,
1030 789 : oFeature);
1031 : }
1032 :
1033 : /************************************************************************/
1034 : /* ProcessUpdateRecordFeatureType() */
1035 : /************************************************************************/
1036 :
1037 19 : bool OGRS101Reader::ProcessUpdateRecordFeatureType(
1038 : const DDFRecord *poUpdateRecord, DDFRecord *poTargetRecord) const
1039 : {
1040 19 : const auto poIDField = poUpdateRecord->GetField(0);
1041 19 : CPLAssert(poIDField);
1042 :
1043 : // Record name
1044 : const RecordName nRCNM =
1045 19 : poUpdateRecord->GetIntSubfield(poIDField, RCNM_SUBFIELD, 0);
1046 :
1047 : // Record identifier
1048 : const int nRCID =
1049 19 : poUpdateRecord->GetIntSubfield(poIDField, RCID_SUBFIELD, 0);
1050 :
1051 : // Deal with FOID field updates
1052 19 : if (const auto poFOIDFieldUpdate = poUpdateRecord->FindField(FOID_FIELD))
1053 : {
1054 19 : auto poFOIDFieldTarget = poTargetRecord->FindField(FOID_FIELD);
1055 19 : if (!poFOIDFieldTarget)
1056 : {
1057 0 : return EMIT_ERROR_OR_WARNING(CPLSPrintf(
1058 : "%s, RCNM=%d, RCID=%d: missing FOID field in "
1059 : "target record",
1060 : m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID));
1061 : }
1062 :
1063 57 : for (const char *pszSubFieldName :
1064 76 : {AGEN_SUBFIELD, FIDN_SUBFIELD, FIDS_SUBFIELD})
1065 : {
1066 57 : const int nVal = poUpdateRecord->GetIntSubfield(poFOIDFieldUpdate,
1067 : pszSubFieldName, 0);
1068 57 : poTargetRecord->SetIntSubfield(FOID_FIELD, 0, pszSubFieldName, 0,
1069 : nVal);
1070 : }
1071 : }
1072 :
1073 : // Deal with SPAS field updates
1074 38 : const auto apoSPASFieldUpdates = poUpdateRecord->GetFields(SPAS_FIELD);
1075 19 : if (!apoSPASFieldUpdates.empty())
1076 : {
1077 : struct SPASField
1078 : {
1079 : int RRNM = 0;
1080 : int RRID = 0;
1081 : int ORNT = 0;
1082 : int SMIN = 0;
1083 : int SMAX = 0;
1084 : int SAUI = 0;
1085 :
1086 46 : static SPASField Read(const DDFRecord *poRecord,
1087 : const DDFField *poField, int i)
1088 : {
1089 46 : SPASField f;
1090 46 : f.RRNM = poRecord->GetIntSubfield(poField, RRNM_SUBFIELD, i);
1091 46 : f.RRID = poRecord->GetIntSubfield(poField, RRID_SUBFIELD, i);
1092 46 : f.ORNT = poRecord->GetIntSubfield(poField, ORNT_SUBFIELD, i);
1093 46 : f.SMIN = poRecord->GetIntSubfield(poField, SMIN_SUBFIELD, i);
1094 46 : f.SMAX = poRecord->GetIntSubfield(poField, SMAX_SUBFIELD, i);
1095 46 : f.SAUI = poRecord->GetIntSubfield(poField, SAUI_SUBFIELD, i);
1096 46 : return f;
1097 : }
1098 : };
1099 :
1100 12 : std::vector<SPASField> asSPASFields;
1101 : // Ingest the existing/target record(s)
1102 24 : for (auto *poField : poTargetRecord->GetFields(SPAS_FIELD))
1103 : {
1104 12 : const int nRepeatCount = poField->GetRepeatCount();
1105 36 : for (int i = 0; i < nRepeatCount; ++i)
1106 : {
1107 24 : asSPASFields.push_back(
1108 48 : SPASField::Read(poTargetRecord, poField, i));
1109 : }
1110 :
1111 12 : poTargetRecord->DeleteField(poField);
1112 : }
1113 :
1114 : // Apply the update record(s)
1115 22 : for (const auto *poSPASFieldUpdate : apoSPASFieldUpdates)
1116 : {
1117 12 : const int nUpdateRepeatCount = poSPASFieldUpdate->GetRepeatCount();
1118 32 : for (int i = 0; i < nUpdateRepeatCount; ++i)
1119 : {
1120 : SPASField updateFld =
1121 22 : SPASField::Read(poUpdateRecord, poSPASFieldUpdate, i);
1122 22 : if (updateFld.SAUI == INSTRUCTION_INSERT)
1123 : {
1124 10 : asSPASFields.push_back(updateFld);
1125 : }
1126 12 : else if (updateFld.SAUI == INSTRUCTION_DELETE)
1127 : {
1128 10 : bool bMatchFound = false;
1129 14 : for (size_t j = 0; j < asSPASFields.size(); ++j)
1130 : {
1131 20 : if (asSPASFields[j].RRNM == updateFld.RRNM &&
1132 8 : asSPASFields[j].RRID == updateFld.RRID)
1133 : {
1134 8 : bMatchFound = true;
1135 8 : asSPASFields.erase(asSPASFields.begin() + j);
1136 8 : break;
1137 : }
1138 : }
1139 12 : if (!bMatchFound &&
1140 2 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
1141 : "%s, RCNM=%d, RCID=%d, SPAS iSubField=%d: update "
1142 : "field references RRNM=%d, RRID=%d which does not "
1143 : "exist in initial or previous update",
1144 : m_osFilename.c_str(), static_cast<int>(nRCNM),
1145 : nRCID, i, updateFld.RRNM, updateFld.RRID)))
1146 : {
1147 2 : return false;
1148 : }
1149 : }
1150 2 : else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
1151 : "%s, RCNM=%d, RCID=%d, SPAS iSubField=%d: invalid "
1152 : "SAUI=%d",
1153 : m_osFilename.c_str(), static_cast<int>(nRCNM),
1154 : nRCID, i, updateFld.SAUI)))
1155 : {
1156 1 : return false;
1157 : }
1158 : }
1159 : }
1160 :
1161 10 : if (!asSPASFields.empty())
1162 : {
1163 : const auto poSPASFieldDefn =
1164 10 : m_oMainModule.FindFieldDefn(SPAS_FIELD);
1165 10 : if (!poSPASFieldDefn)
1166 : {
1167 0 : return EMIT_ERROR("Cannot find SPAS field definition");
1168 : }
1169 10 : auto poSPASFieldTarget = poTargetRecord->AddField(poSPASFieldDefn);
1170 10 : CPLAssert(poSPASFieldTarget);
1171 10 : const auto *poSPASFieldUpdate = apoSPASFieldUpdates[0];
1172 10 : if (*(poSPASFieldTarget->GetFieldDefn()) !=
1173 10 : *(poSPASFieldUpdate->GetFieldDefn()))
1174 : {
1175 0 : return EMIT_ERROR("SPAS field definitions of update and target "
1176 : "records are different");
1177 : }
1178 :
1179 32 : for (int i = 0; i < static_cast<int>(asSPASFields.size()); ++i)
1180 : {
1181 22 : poTargetRecord->SetIntSubfield(SPAS_FIELD, 0, RRNM_SUBFIELD, i,
1182 22 : asSPASFields[i].RRNM);
1183 22 : poTargetRecord->SetIntSubfield(SPAS_FIELD, 0, RRID_SUBFIELD, i,
1184 22 : asSPASFields[i].RRID);
1185 22 : poTargetRecord->SetIntSubfield(SPAS_FIELD, 0, ORNT_SUBFIELD, i,
1186 22 : asSPASFields[i].ORNT);
1187 22 : poTargetRecord->SetIntSubfield(SPAS_FIELD, 0, SMIN_SUBFIELD, i,
1188 22 : asSPASFields[i].SMIN);
1189 22 : poTargetRecord->SetIntSubfield(SPAS_FIELD, 0, SMAX_SUBFIELD, i,
1190 22 : asSPASFields[i].SMAX);
1191 22 : poTargetRecord->SetIntSubfield(SPAS_FIELD, 0, SAUI_SUBFIELD, i,
1192 22 : asSPASFields[i].SAUI);
1193 : }
1194 : }
1195 : }
1196 :
1197 : // Deal with MASK field updates
1198 34 : const auto apoMASKFieldUpdates = poUpdateRecord->GetFields(MASK_FIELD);
1199 17 : if (!apoMASKFieldUpdates.empty())
1200 : {
1201 : struct MASKField
1202 : {
1203 : int RRNM = 0;
1204 : int RRID = 0;
1205 : int MIND = 0;
1206 : int MUIN = 0;
1207 :
1208 64 : static MASKField Read(const DDFRecord *poRecord,
1209 : const DDFField *poField, int i)
1210 : {
1211 64 : MASKField f;
1212 64 : f.RRNM = poRecord->GetIntSubfield(poField, RRNM_SUBFIELD, i);
1213 64 : f.RRID = poRecord->GetIntSubfield(poField, RRID_SUBFIELD, i);
1214 64 : f.MIND = poRecord->GetIntSubfield(poField, MIND_SUBFIELD, i);
1215 64 : f.MUIN = poRecord->GetIntSubfield(poField, MUIN_SUBFIELD, i);
1216 64 : return f;
1217 : }
1218 : };
1219 :
1220 10 : std::vector<MASKField> asMASKFields;
1221 : // Ingest the existing/target record(s)
1222 30 : for (auto *poField : poTargetRecord->GetFields(MASK_FIELD))
1223 : {
1224 20 : const int nRepeatCount = poField->GetRepeatCount();
1225 50 : for (int i = 0; i < nRepeatCount; ++i)
1226 : {
1227 30 : asMASKFields.push_back(
1228 60 : MASKField::Read(poTargetRecord, poField, i));
1229 : }
1230 :
1231 20 : poTargetRecord->DeleteField(poField);
1232 : }
1233 :
1234 : // Apply the update record(s)
1235 18 : for (const auto *poMASKFieldUpdate : apoMASKFieldUpdates)
1236 : {
1237 10 : const int nUpdateRepeatCount = poMASKFieldUpdate->GetRepeatCount();
1238 42 : for (int i = 0; i < nUpdateRepeatCount; ++i)
1239 : {
1240 : MASKField updateFld =
1241 34 : MASKField::Read(poUpdateRecord, poMASKFieldUpdate, i);
1242 34 : if (updateFld.MUIN == INSTRUCTION_INSERT)
1243 : {
1244 8 : asMASKFields.push_back(updateFld);
1245 : }
1246 26 : else if (updateFld.MUIN == INSTRUCTION_DELETE)
1247 : {
1248 24 : bool bMatchFound = false;
1249 34 : for (size_t j = 0; j < asMASKFields.size(); ++j)
1250 : {
1251 56 : if (asMASKFields[j].RRNM == updateFld.RRNM &&
1252 24 : asMASKFields[j].RRID == updateFld.RRID)
1253 : {
1254 22 : bMatchFound = true;
1255 22 : asMASKFields.erase(asMASKFields.begin() + j);
1256 22 : break;
1257 : }
1258 : }
1259 26 : if (!bMatchFound &&
1260 2 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
1261 : "%s, RCNM=%d, RCID=%d, MASK iSubField=%d: update "
1262 : "field references RRNM=%d, RRID=%d which does not "
1263 : "exist in initial or previous update",
1264 : m_osFilename.c_str(), static_cast<int>(nRCNM),
1265 : nRCID, i, updateFld.RRNM, updateFld.RRID)))
1266 : {
1267 2 : return false;
1268 : }
1269 : }
1270 2 : else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
1271 : "%s, RCNM=%d, RCID=%d, MASK iSubField=%d: invalid "
1272 : "MUIN=%d",
1273 : m_osFilename.c_str(), static_cast<int>(nRCNM),
1274 : nRCID, i, updateFld.MUIN)))
1275 : {
1276 1 : return false;
1277 : }
1278 : }
1279 : }
1280 :
1281 8 : if (!asMASKFields.empty())
1282 : {
1283 : const auto poMASKFieldDefn =
1284 8 : m_oMainModule.FindFieldDefn(MASK_FIELD);
1285 8 : if (!poMASKFieldDefn)
1286 : {
1287 0 : return EMIT_ERROR("Cannot find MASK field definition");
1288 : }
1289 8 : auto poMASKFieldTarget = poTargetRecord->AddField(poMASKFieldDefn);
1290 8 : CPLAssert(poMASKFieldTarget);
1291 8 : const auto *poMASKFieldUpdate = apoMASKFieldUpdates[0];
1292 8 : if (*(poMASKFieldTarget->GetFieldDefn()) !=
1293 8 : *(poMASKFieldUpdate->GetFieldDefn()))
1294 : {
1295 0 : return EMIT_ERROR("MASK field definitions of update and target "
1296 : "records are different");
1297 : }
1298 :
1299 18 : for (int i = 0; i < static_cast<int>(asMASKFields.size()); ++i)
1300 : {
1301 10 : poTargetRecord->SetIntSubfield(MASK_FIELD, 0, RRNM_SUBFIELD, i,
1302 10 : asMASKFields[i].RRNM);
1303 10 : poTargetRecord->SetIntSubfield(MASK_FIELD, 0, RRID_SUBFIELD, i,
1304 10 : asMASKFields[i].RRID);
1305 10 : poTargetRecord->SetIntSubfield(MASK_FIELD, 0, MIND_SUBFIELD, i,
1306 10 : asMASKFields[i].MIND);
1307 10 : poTargetRecord->SetIntSubfield(MASK_FIELD, 0, MUIN_SUBFIELD, i,
1308 10 : asMASKFields[i].MUIN);
1309 : }
1310 : }
1311 : }
1312 :
1313 15 : return true;
1314 : }
|