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 "ogrs101readerconstants.h"
15 : #include "ogrs101featurecatalog.h"
16 :
17 : #include "cpl_enumerate.h"
18 :
19 : #include <limits>
20 : #include <memory>
21 : #include <utility>
22 :
23 : /************************************************************************/
24 : /* OGRS101Reader() */
25 : /************************************************************************/
26 :
27 : OGRS101Reader::OGRS101Reader() = default;
28 :
29 : /************************************************************************/
30 : /* ~OGRS101Reader() */
31 : /************************************************************************/
32 :
33 : OGRS101Reader::~OGRS101Reader() = default;
34 :
35 : /************************************************************************/
36 : /* EmitErrorOrWarning() */
37 : /************************************************************************/
38 :
39 : /*static */ bool
40 579 : OGRS101Reader::EmitErrorOrWarning(const char *pszFile, const char *pszFunc,
41 : int nLine, const char *pszMsg, bool bError,
42 : bool bRecoverable)
43 : {
44 : #ifdef _WIN32
45 : const char *lastPathSep = strrchr(pszFile, '\\');
46 : #else
47 579 : const char *lastPathSep = strrchr(pszFile, '/');
48 : #endif
49 579 : if (lastPathSep)
50 579 : pszFile = lastPathSep + 1;
51 :
52 579 : if (bError)
53 : {
54 214 : if (bRecoverable)
55 : {
56 196 : CPLError(
57 : CE_Failure, CPLE_AppDefined,
58 : "at %s:%d (%s()): %s\n"
59 : "You can potentially try to overcome this error by setting "
60 : "the STRICT open option to FALSE",
61 : pszFile, nLine, pszFunc, pszMsg);
62 : }
63 : else
64 : {
65 18 : CPLError(CE_Failure, CPLE_AppDefined, "at %s:%d (%s()): %s",
66 : pszFile, nLine, pszFunc, pszMsg);
67 : }
68 : }
69 : else
70 : {
71 365 : CPLError(CE_Warning, CPLE_AppDefined, "at %s:%d (%s()): %s", pszFile,
72 : nLine, pszFunc, pszMsg);
73 : }
74 579 : return !bError;
75 : }
76 :
77 : /************************************************************************/
78 : /* Load() */
79 : /************************************************************************/
80 :
81 : /** Load a dataset.
82 : *
83 : * Ingests records in the various DDFRecordIndex members and build layer
84 : * definitions.
85 : */
86 501 : bool OGRS101Reader::Load(GDALOpenInfo *poOpenInfo)
87 : {
88 501 : if (!poOpenInfo->fpL)
89 0 : return false;
90 :
91 501 : m_bStrict = CPLTestBool(
92 501 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "STRICT", "YES"));
93 501 : VSILFILE *fp = poOpenInfo->fpL;
94 501 : poOpenInfo->fpL = nullptr;
95 :
96 501 : if (!Load(poOpenInfo->pszFilename, fp, &m_oMainModule))
97 90 : return false;
98 :
99 411 : m_aosMetadata.SetNameValue("STATUS", "VALID");
100 :
101 : // Browse and load update files
102 822 : if (poOpenInfo->IsExtensionEqualToCI("000") &&
103 411 : EQUAL(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "UPDATES",
104 : "APPLY"),
105 : "APPLY"))
106 : {
107 410 : m_bInUpdate = true;
108 481 : for (int iUpdate = 1; iUpdate <= 999; ++iUpdate)
109 : {
110 481 : DDFModule oTmpModule;
111 : const std::string osUpdateFilename = CPLResetExtensionSafe(
112 481 : poOpenInfo->pszFilename, CPLSPrintf("%03d", iUpdate));
113 : VSIStatBufL sStatBuf;
114 481 : if (VSIStatL(osUpdateFilename.c_str(), &sStatBuf) != 0)
115 356 : break;
116 125 : CPLDebug("S101", "Loading update file %s",
117 : osUpdateFilename.c_str());
118 125 : if (!Load(osUpdateFilename.c_str(), nullptr, &oTmpModule))
119 49 : return false;
120 76 : if (m_bCancelled)
121 5 : break;
122 : }
123 361 : m_bInUpdate = false;
124 : }
125 :
126 724 : return ReadFeatureCatalog() && CreateInformationTypeFeatureDefn() &&
127 353 : CreatePointFeatureDefns() && CreateMultiPointFeatureDefns() &&
128 345 : CreateCurveFeatureDefn() && CreateCompositeCurveFeatureDefn() &&
129 724 : CreateSurfaceFeatureDefn() && CreateFeatureTypeFeatureDefns();
130 : }
131 :
132 : /************************************************************************/
133 : /* Load() */
134 : /************************************************************************/
135 :
136 626 : bool OGRS101Reader::Load(const std::string &osFilename, VSILFILE *fp,
137 : DDFModule *poCurModule)
138 : {
139 626 : m_osFilename = osFilename;
140 626 : if (!poCurModule->Open(m_osFilename.c_str(), false, fp))
141 0 : return false;
142 :
143 626 : if (!poCurModule->FindFieldDefn(DSID_FIELD))
144 : {
145 0 : CPLError(CE_Failure, CPLE_AppDefined,
146 : "%s is an ISO8211 file, but not a S-101 data file.",
147 : m_osFilename.c_str());
148 0 : return false;
149 : }
150 :
151 626 : DDFRecord *poRecord = poCurModule->ReadRecord();
152 626 : if (!ReadDatasetGeneralInformationRecord(poRecord))
153 53 : return false;
154 :
155 573 : poRecord = poCurModule->ReadRecord();
156 573 : if (m_bInUpdate)
157 : {
158 120 : if (!poRecord)
159 6 : return true;
160 114 : if (poRecord->FindField(CSID_FIELD))
161 : {
162 0 : if (!EMIT_ERROR_OR_WARNING(
163 : "CSID field found in update file but not expected."))
164 0 : return false;
165 0 : poRecord = poCurModule->ReadRecord();
166 0 : if (!poRecord)
167 0 : return true;
168 : }
169 :
170 114 : return IngestUpdateRecords(poRecord, poCurModule);
171 : }
172 : else
173 : {
174 453 : if (!poRecord)
175 1 : return EMIT_ERROR("no Dataset Coordinate Reference System record.");
176 452 : bool bSkipFirstReadRecord = false;
177 452 : if (!m_bStrict && !poRecord->FindField(CSID_FIELD))
178 : {
179 1 : EMIT_ERROR_OR_WARNING("CSID field not found.");
180 1 : bSkipFirstReadRecord = true;
181 : }
182 451 : else if (!ReadCSID(poRecord))
183 29 : return false;
184 :
185 423 : return IngestInitialRecords(bSkipFirstReadRecord ? poRecord : nullptr);
186 : }
187 : }
188 :
189 : /************************************************************************/
190 : /* ReadFeatureCatalog() */
191 : /************************************************************************/
192 :
193 362 : bool OGRS101Reader::ReadFeatureCatalog()
194 : {
195 : OGRS101FeatureCatalog::LoadingStatus status;
196 362 : std::tie(status, m_poFeatureCatalog) =
197 724 : OGRS101FeatureCatalog::GetSingletonFeatureCatalog(m_bStrict);
198 362 : return status != OGRS101FeatureCatalog::LoadingStatus::ERROR;
199 : }
200 :
201 : /************************************************************************/
202 : /* CheckFieldDefinitions() */
203 : /************************************************************************/
204 :
205 : /** Check that the fields found in the record match the expectations
206 : * from the spec. That is check there are no missing required field,
207 : * no duplicate field or unexpected field.
208 : */
209 2243 : bool OGRS101Reader::CheckFieldDefinitions(
210 : const DDFRecord *poRecord, int iRecord, RecordName nRCNM, int nRCID,
211 : const std::map<RecordName, std::vector<std::vector<NameOccMinOccMax>>>
212 : &mapExpectedFields) const
213 : {
214 4486 : std::vector<std::string> fieldsInRecord;
215 8725 : for (const auto &poField : poRecord->GetFields())
216 : {
217 6482 : fieldsInRecord.push_back(poField->GetFieldDefn()->GetName());
218 : }
219 2243 : const auto oIter = mapExpectedFields.find(nRCNM);
220 2243 : if (oIter != mapExpectedFields.end())
221 : {
222 2240 : bool bMatch = false;
223 2336 : for (const auto &expectedFields : oIter->second)
224 : {
225 2320 : bMatch = true;
226 2320 : size_t iExpected = 0;
227 8966 : for (size_t i = 0; bMatch && i < fieldsInRecord.size(); ++i)
228 : {
229 8961 : for (; iExpected < expectedFields.size(); ++iExpected)
230 : {
231 8956 : if (fieldsInRecord[i] ==
232 8956 : std::get<0>(expectedFields[iExpected]))
233 : {
234 : const int nMaxOcc =
235 6569 : std::get<2>(expectedFields[iExpected]);
236 6569 : if (nMaxOcc == 1)
237 : {
238 6264 : if (i > 0 &&
239 1972 : fieldsInRecord[i - 1] == fieldsInRecord[i])
240 : {
241 0 : bMatch = false;
242 : }
243 : else
244 : {
245 4292 : ++iExpected;
246 : }
247 : }
248 2891 : else if (i + 1 == fieldsInRecord.size() ||
249 614 : fieldsInRecord[i + 1] != fieldsInRecord[i])
250 : {
251 2123 : ++iExpected;
252 : }
253 6569 : break;
254 : }
255 : else
256 : {
257 : const int nMinOcc =
258 2387 : std::get<1>(expectedFields[iExpected]);
259 2387 : if (nMinOcc == 1)
260 : {
261 74 : bMatch = false;
262 74 : break;
263 : }
264 : }
265 : }
266 :
267 : // If we have reached the end of expected fields but
268 : // there are remaining fields, then there are missing
269 : // compulsory fields.
270 8184 : if (iExpected == expectedFields.size() &&
271 1536 : i + 1 < fieldsInRecord.size())
272 : {
273 2 : bMatch = false;
274 2 : break;
275 : }
276 : }
277 :
278 : // Skip optional fields after the last one in the record
279 2320 : if (bMatch && iExpected < expectedFields.size())
280 : {
281 1795 : for (; iExpected < expectedFields.size(); ++iExpected)
282 : {
283 1105 : const int nMinOcc = std::get<1>(expectedFields[iExpected]);
284 1105 : if (nMinOcc > 0)
285 : {
286 20 : bMatch = false;
287 20 : break;
288 : }
289 : }
290 : }
291 2320 : bMatch = bMatch && iExpected == expectedFields.size();
292 2320 : if (bMatch)
293 2224 : break;
294 : }
295 2240 : if (!bMatch)
296 : {
297 16 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
298 : "Record index %d, RCNM=%d, RCID=%d: invalid "
299 : "sequence of fields.",
300 : iRecord, static_cast<int>(nRCNM), static_cast<int>(nRCID))))
301 : {
302 8 : return false;
303 : }
304 : }
305 : }
306 :
307 2235 : return true;
308 : }
309 :
310 : /************************************************************************/
311 : /* IngestInitialRecords() */
312 : /************************************************************************/
313 :
314 : /** Ingest records of .000 initial file into the various m_oXXXXXIndex members
315 : *
316 : * @param poRecordIn Already read record, or nullptr to advance to the next one
317 : */
318 423 : bool OGRS101Reader::IngestInitialRecords(const DDFRecord *poRecordIn)
319 : {
320 : // Must NOT be set as static, as it depends on "this" !
321 : /* NOT STATIC */ const struct
322 : {
323 : const char *pszFieldName;
324 : const char *pszType;
325 : RecordName nRCNM;
326 : int nExpectedCount;
327 : DDFRecordIndex &oIndex;
328 423 : } asRecordTypeDesc[] = {
329 : {IRID_FIELD, "information type", RECORD_NAME_INFORMATION_TYPE,
330 423 : m_nCountInformationRecord, m_oInformationTypeRecordIndex},
331 423 : {PRID_FIELD, "point", RECORD_NAME_POINT, m_nCountPointRecord,
332 423 : m_oPointRecordIndex},
333 : {MRID_FIELD, "multipoint", RECORD_NAME_MULTIPOINT,
334 423 : m_nCountMultiPointRecord, m_oMultiPointRecordIndex},
335 423 : {CRID_FIELD, "curve", RECORD_NAME_CURVE, m_nCountCurveRecord,
336 423 : m_oCurveRecordIndex},
337 : {CCID_FIELD, "composite curve", RECORD_NAME_COMPOSITE_CURVE,
338 423 : m_nCountCompositeCurveRecord, m_oCompositeCurveRecordIndex},
339 423 : {SRID_FIELD, "surface", RECORD_NAME_SURFACE, m_nCountSurfaceRecord,
340 423 : m_oSurfaceRecordIndex},
341 : {FRID_FIELD, "feature type", RECORD_NAME_FEATURE_TYPE,
342 423 : m_nCountFeatureTypeRecord, m_oFeatureTypeRecordIndex},
343 423 : };
344 :
345 423 : constexpr int STAR = std::numeric_limits<int>::max();
346 : const std::map<RecordName, std::vector<std::vector<NameOccMinOccMax>>>
347 : mapExpectedFields = {
348 : {
349 : RECORD_NAME_INFORMATION_TYPE,
350 : {{{IRID_FIELD, 1, 1},
351 : {ATTR_FIELD, 0, STAR},
352 : {INAS_FIELD, 0, STAR}}},
353 : },
354 : {RECORD_NAME_POINT,
355 : {
356 : {{{PRID_FIELD, 1, 1},
357 : {INAS_FIELD, 0, STAR},
358 : {C2IT_FIELD, 1, 1}}},
359 : {{{PRID_FIELD, 1, 1},
360 : {INAS_FIELD, 0, STAR},
361 : {C3IT_FIELD, 1, 1}}},
362 : }},
363 : {RECORD_NAME_MULTIPOINT,
364 : {
365 : {{{MRID_FIELD, 1, 1},
366 : {INAS_FIELD, 0, STAR},
367 : {C2IL_FIELD, 1, STAR}}},
368 : {{{MRID_FIELD, 1, 1},
369 : {INAS_FIELD, 0, STAR},
370 : {C3IL_FIELD, 1, STAR}}},
371 : }},
372 : {RECORD_NAME_CURVE,
373 : {
374 : {{{CRID_FIELD, 1, 1},
375 : {INAS_FIELD, 0, STAR},
376 : {PTAS_FIELD, 1, 1},
377 : {SEGH_FIELD, 1, 1},
378 : {C2IL_FIELD, 1, STAR}}},
379 : }},
380 : {RECORD_NAME_COMPOSITE_CURVE,
381 : {
382 : {{{CCID_FIELD, 1, 1},
383 : {INAS_FIELD, 0, STAR},
384 : {CUCO_FIELD, 1, STAR}}},
385 : }},
386 : {RECORD_NAME_SURFACE,
387 : {
388 : {{{SRID_FIELD, 1, 1},
389 : {INAS_FIELD, 0, STAR},
390 : {RIAS_FIELD, 1, STAR}}},
391 : }},
392 : {RECORD_NAME_FEATURE_TYPE,
393 : {
394 : {{{FRID_FIELD, 1, 1},
395 : {FOID_FIELD, 1, 1},
396 : {ATTR_FIELD, 0, STAR},
397 : {INAS_FIELD, 0, STAR},
398 : {SPAS_FIELD, 0, STAR},
399 : {FASC_FIELD, 0, STAR},
400 : {MASK_FIELD, 0, STAR}}},
401 : }},
402 11421 : };
403 :
404 : // Loop through all records (except first two DSID and CRID already parsed)
405 : // and dispatch them to the appropriate DDFRecordIndex member variable.
406 423 : const DDFRecord *poRecord = nullptr;
407 423 : const int iFirstRecord = poRecordIn ? 1 : 2;
408 2452 : for (int iRecord = iFirstRecord;
409 2452 : poRecordIn || (poRecord = m_oMainModule.ReadRecord()) != nullptr;
410 : ++iRecord)
411 : {
412 2043 : if (poRecordIn)
413 1 : std::swap(poRecord, poRecordIn);
414 :
415 2043 : const auto poField = poRecord->GetField(0);
416 2043 : if (!poField)
417 11 : return EMIT_ERROR(
418 : CPLSPrintf("Record index %d without field.", iRecord));
419 2043 : const char *pszFieldName = poField->GetFieldDefn()->GetName();
420 :
421 : // Record name
422 : const RecordName nRCNM =
423 2043 : poRecord->GetIntSubfield(pszFieldName, 0, RCNM_SUBFIELD, 0);
424 :
425 : // Record identifier
426 : const int nRCID =
427 2043 : poRecord->GetIntSubfield(pszFieldName, 0, RCID_SUBFIELD, 0);
428 :
429 2043 : if (!CheckFieldDefinitions(poRecord, iRecord, nRCNM, nRCID,
430 : mapExpectedFields))
431 8 : return false;
432 :
433 2035 : const auto iterRecordTypeDesc = std::find_if(
434 : std::begin(asRecordTypeDesc), std::end(asRecordTypeDesc),
435 7942 : [pszFieldName](const auto &sRecordTypeDesc)
436 : {
437 7942 : return strcmp(pszFieldName, sRecordTypeDesc.pszFieldName) == 0;
438 : });
439 2035 : if (iterRecordTypeDesc != std::end(asRecordTypeDesc))
440 : {
441 2034 : const auto &sRecordTypeDesc = *iterRecordTypeDesc;
442 :
443 2034 : if (nRCNM != sRecordTypeDesc.nRCNM &&
444 0 : !EMIT_ERROR_OR_WARNING(
445 : CPLSPrintf("Record index %d: invalid value %d for RCNM "
446 : "subfield of %s.",
447 : iRecord, static_cast<int>(nRCNM), pszFieldName)))
448 : {
449 0 : return false;
450 : }
451 :
452 2034 : if (nRCID <= 0)
453 : {
454 6 : if (!EMIT_ERROR_OR_WARNING(
455 : CPLSPrintf("Record index %d: invalid value %d for "
456 : "RCID subfield of %s.",
457 : iRecord, nRCID, pszFieldName)))
458 : {
459 3 : return false;
460 : }
461 3 : break;
462 : }
463 :
464 2028 : if (sRecordTypeDesc.oIndex.FindRecord(nRCID) &&
465 0 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
466 : "Record index %d: several %s records have RCID = %d.",
467 : iRecord, pszFieldName, nRCID)))
468 : {
469 0 : return false;
470 : }
471 2028 : sRecordTypeDesc.oIndex.AddRecord(nRCID, poRecord->Clone());
472 : }
473 1 : else if (!EMIT_ERROR_OR_WARNING(
474 : CPLSPrintf("Record index %d: unknown field name %s.",
475 : iRecord, pszFieldName)))
476 : {
477 0 : return false;
478 : }
479 : }
480 :
481 : // Check consistency between number of records of each category (information
482 : // type, point, etc.) and the number actually found.
483 3289 : for (const auto &sRecordTypeDesc : asRecordTypeDesc)
484 : {
485 2878 : if (sRecordTypeDesc.oIndex.GetCount() !=
486 2890 : sRecordTypeDesc.nExpectedCount &&
487 12 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
488 : "%d %s records mentioned in DSSI field, but %d actually found.",
489 : sRecordTypeDesc.nExpectedCount, sRecordTypeDesc.pszType,
490 : sRecordTypeDesc.oIndex.GetCount())))
491 : {
492 1 : return false;
493 : }
494 : }
495 :
496 411 : return true;
497 : }
498 :
499 : /************************************************************************/
500 : /* IngestUpdateRecords() */
501 : /************************************************************************/
502 :
503 : /** Ingest records of .00x (x>=1) update file into the various m_oXXXXXIndex members
504 : *
505 : * @param poRecordIn Already read record
506 : */
507 114 : bool OGRS101Reader::IngestUpdateRecords(const DDFRecord *poRecordIn,
508 : DDFModule *poCurModule)
509 : {
510 : // Must NOT be set as static, as it depends on "this" !
511 : /* NOT STATIC */ const struct
512 : {
513 : const char *pszFieldName;
514 : const char *pszType;
515 : RecordName nRCNM;
516 : int &nExpectedCount;
517 : DDFRecordIndex &oIndex;
518 114 : } asRecordTypeDesc[] = {
519 : {IRID_FIELD, "information type", RECORD_NAME_INFORMATION_TYPE,
520 114 : m_nCountInformationRecord, m_oInformationTypeRecordIndex},
521 114 : {PRID_FIELD, "point", RECORD_NAME_POINT, m_nCountPointRecord,
522 114 : m_oPointRecordIndex},
523 : {MRID_FIELD, "multipoint", RECORD_NAME_MULTIPOINT,
524 114 : m_nCountMultiPointRecord, m_oMultiPointRecordIndex},
525 114 : {CRID_FIELD, "curve", RECORD_NAME_CURVE, m_nCountCurveRecord,
526 114 : m_oCurveRecordIndex},
527 : {CCID_FIELD, "composite curve", RECORD_NAME_COMPOSITE_CURVE,
528 114 : m_nCountCompositeCurveRecord, m_oCompositeCurveRecordIndex},
529 114 : {SRID_FIELD, "surface", RECORD_NAME_SURFACE, m_nCountSurfaceRecord,
530 114 : m_oSurfaceRecordIndex},
531 : {FRID_FIELD, "feature type", RECORD_NAME_FEATURE_TYPE,
532 114 : m_nCountFeatureTypeRecord, m_oFeatureTypeRecordIndex},
533 114 : };
534 :
535 114 : constexpr int STAR = std::numeric_limits<int>::max();
536 : const std::map<RecordName, std::vector<std::vector<NameOccMinOccMax>>>
537 : mapExpectedFields = {
538 : {
539 : RECORD_NAME_INFORMATION_TYPE,
540 : {{{IRID_FIELD, 1, 1},
541 : {ATTR_FIELD, 0, STAR},
542 : {INAS_FIELD, 0, STAR}}},
543 : },
544 : {RECORD_NAME_POINT,
545 : {
546 : {{{PRID_FIELD, 1, 1},
547 : {INAS_FIELD, 0, STAR},
548 : {C2IT_FIELD, 0, 1}}},
549 : {{{PRID_FIELD, 1, 1},
550 : {INAS_FIELD, 0, STAR},
551 : {C3IT_FIELD, 0, 1}}},
552 : }},
553 : {RECORD_NAME_MULTIPOINT,
554 : {
555 : {{{MRID_FIELD, 1, 1},
556 : {INAS_FIELD, 0, STAR},
557 : {COCC_FIELD, 0, 1},
558 : {C2IL_FIELD, 0, STAR}}},
559 : {{{MRID_FIELD, 1, 1},
560 : {INAS_FIELD, 0, STAR},
561 : {COCC_FIELD, 0, 1},
562 : {C3IL_FIELD, 0, STAR}}},
563 : }},
564 : {RECORD_NAME_CURVE,
565 : {
566 : {{{CRID_FIELD, 1, 1},
567 : {INAS_FIELD, 0, STAR},
568 : {PTAS_FIELD, 0, 1},
569 : {SECC_FIELD, 0, 1},
570 : {SEGH_FIELD, 0, 1},
571 : {COCC_FIELD, 0, 1},
572 : {C2IL_FIELD, 0, STAR}}},
573 : }},
574 : {RECORD_NAME_COMPOSITE_CURVE,
575 : {
576 : {{{CCID_FIELD, 1, 1},
577 : {INAS_FIELD, 0, STAR},
578 : {CCOC_FIELD, 0, STAR},
579 : {CUCO_FIELD, 0, STAR}}},
580 : }},
581 : {RECORD_NAME_SURFACE,
582 : {
583 : {{{SRID_FIELD, 1, 1},
584 : {INAS_FIELD, 0, STAR},
585 : {RIAS_FIELD, 0, STAR}}},
586 : }},
587 : {RECORD_NAME_FEATURE_TYPE,
588 : {
589 : {{{FRID_FIELD, 1, 1},
590 : {FOID_FIELD, 0, 1},
591 : {ATTR_FIELD, 0, STAR},
592 : {INAS_FIELD, 0, STAR},
593 : {SPAS_FIELD, 0, STAR},
594 : {FASC_FIELD, 0, STAR},
595 : {MASK_FIELD, 0, STAR}}},
596 : }},
597 3078 : };
598 :
599 : // Loop through all records (except first DSID already parsed)
600 : // and dispatch them to the appropriate DDFRecordIndex member variable.
601 114 : const DDFRecord *poRecord = nullptr;
602 114 : const int iFirstRecord = 1;
603 269 : for (int iRecord = iFirstRecord;
604 269 : poRecordIn || (poRecord = poCurModule->ReadRecord()) != nullptr;
605 : ++iRecord)
606 : {
607 200 : if (poRecordIn)
608 114 : std::swap(poRecord, poRecordIn);
609 :
610 200 : const auto poField = poRecord->GetField(0);
611 200 : if (!poField)
612 44 : return EMIT_ERROR(
613 : CPLSPrintf("Record index %d without field.", iRecord));
614 200 : const char *pszFieldName = poField->GetFieldDefn()->GetName();
615 :
616 : // Record name
617 : const RecordName nRCNM =
618 200 : poRecord->GetIntSubfield(pszFieldName, 0, RCNM_SUBFIELD, 0);
619 :
620 : // Record identifier
621 : const int nRCID =
622 200 : poRecord->GetIntSubfield(pszFieldName, 0, RCID_SUBFIELD, 0);
623 :
624 200 : if (!CheckFieldDefinitions(poRecord, iRecord, nRCNM, nRCID,
625 : mapExpectedFields))
626 0 : return false;
627 :
628 : // Record version
629 : const int nRVER =
630 200 : poRecord->GetIntSubfield(pszFieldName, 0, RVER_SUBFIELD, 0);
631 :
632 : // Record update instruction
633 : const int nRUIN =
634 200 : poRecord->GetIntSubfield(pszFieldName, 0, RUIN_SUBFIELD, 0);
635 :
636 200 : const auto iterRecordTypeDesc = std::find_if(
637 : std::begin(asRecordTypeDesc), std::end(asRecordTypeDesc),
638 745 : [pszFieldName](const auto &sRecordTypeDesc)
639 : {
640 745 : return strcmp(pszFieldName, sRecordTypeDesc.pszFieldName) == 0;
641 : });
642 200 : if (iterRecordTypeDesc != std::end(asRecordTypeDesc))
643 : {
644 200 : const auto &sRecordTypeDesc = *iterRecordTypeDesc;
645 :
646 202 : if (nRCNM != sRecordTypeDesc.nRCNM &&
647 2 : !EMIT_ERROR_OR_WARNING(
648 : CPLSPrintf("Record index %d: invalid value %d for RCNM "
649 : "subfield of %s.",
650 : iRecord, static_cast<int>(nRCNM), pszFieldName)))
651 : {
652 1 : return false;
653 : }
654 :
655 199 : if (nRCID <= 0)
656 : {
657 2 : if (!EMIT_ERROR_OR_WARNING(
658 : CPLSPrintf("Record index %d: invalid value %d for "
659 : "RCID subfield of %s.",
660 : iRecord, nRCID, pszFieldName)))
661 : {
662 1 : return false;
663 : }
664 1 : break;
665 : }
666 :
667 : DDFRecord *poExistingRecord =
668 197 : sRecordTypeDesc.oIndex.FindRecord(nRCID);
669 197 : if (nRUIN == INSTRUCTION_UPDATE || nRUIN == INSTRUCTION_DELETE)
670 : {
671 154 : if (!poExistingRecord)
672 : {
673 2 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
674 : "Record index %d, RCNM=%d, RCID=%d: no such "
675 : "record.",
676 : iRecord, static_cast<int>(nRCNM), nRCID)))
677 : {
678 1 : return false;
679 : }
680 1 : continue;
681 : }
682 :
683 152 : const int nOldRVER = poExistingRecord->GetIntSubfield(
684 : pszFieldName, 0, RVER_SUBFIELD, 0);
685 152 : if (nRVER != nOldRVER + 1)
686 : {
687 2 : if (!EMIT_ERROR_OR_WARNING(
688 : CPLSPrintf("Record index %d, RCNM=%d, RCID=%d: got "
689 : "RVER=%d, expected %d.",
690 : iRecord, static_cast<int>(nRCNM), nRCID,
691 : nRVER, nOldRVER + 1)))
692 : {
693 1 : return false;
694 : }
695 : }
696 : }
697 :
698 194 : if (nRUIN == INSTRUCTION_INSERT)
699 : {
700 43 : if (poExistingRecord &&
701 2 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
702 : "Record index %d: several %s records have RCID = %d.",
703 : iRecord, pszFieldName, nRCID)))
704 : {
705 2 : return false;
706 : }
707 :
708 40 : auto poClone = poRecord->Clone();
709 40 : if (!UpdateCodesInRecord(poClone.get()))
710 0 : return false;
711 :
712 40 : if (!poClone->TransferTo(&m_oMainModule))
713 1 : return false;
714 :
715 39 : sRecordTypeDesc.oIndex.AddRecord(nRCID, std::move(poClone));
716 :
717 39 : sRecordTypeDesc.nExpectedCount++;
718 : }
719 153 : else if (nRUIN == INSTRUCTION_UPDATE)
720 : {
721 144 : CPLAssert(poExistingRecord);
722 :
723 144 : auto poClone = poRecord->Clone();
724 144 : if (!UpdateCodesInRecord(poClone.get()))
725 7 : return false;
726 :
727 137 : if (!ProcessUpdateRecord(poClone.get(), poExistingRecord))
728 30 : return false;
729 : }
730 9 : else if (nRUIN == INSTRUCTION_DELETE)
731 : {
732 7 : CPLAssert(poExistingRecord);
733 :
734 7 : sRecordTypeDesc.oIndex.RemoveRecord(nRCID);
735 :
736 7 : sRecordTypeDesc.nExpectedCount--;
737 : }
738 2 : else if (!EMIT_ERROR_OR_WARNING(
739 : CPLSPrintf("Record index %d, RCNM=%d, RCID=%d: wrong "
740 : "value %d for RUIN "
741 : "subfield of %s field.",
742 : iRecord, static_cast<int>(nRCNM), nRCID,
743 : nRUIN, pszFieldName)))
744 : {
745 1 : return false;
746 : }
747 : }
748 0 : else if (!EMIT_ERROR_OR_WARNING(
749 : CPLSPrintf("Record index %d: unknown field name %s.",
750 : iRecord, pszFieldName)))
751 : {
752 0 : return false;
753 : }
754 : }
755 :
756 : #ifdef DEBUG
757 : // Check consistency between number of records of each category (information
758 : // type, point, etc.) and the number actually found.
759 560 : for (const auto &sRecordTypeDesc : asRecordTypeDesc)
760 : {
761 490 : if (sRecordTypeDesc.oIndex.GetCount() !=
762 490 : sRecordTypeDesc.nExpectedCount &&
763 0 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
764 : "%d %s records mentioned in DSSI field, but %d actually found.",
765 : sRecordTypeDesc.nExpectedCount, sRecordTypeDesc.pszType,
766 : sRecordTypeDesc.oIndex.GetCount())))
767 : {
768 0 : return false;
769 : }
770 : }
771 : #endif
772 :
773 70 : return true;
774 : }
775 :
776 : /************************************************************************/
777 : /* UpdateCodesInRecord() */
778 : /************************************************************************/
779 :
780 : /** Update NITC, NFTC, NATC, NIAC, NFAC and NARC subfields from the value
781 : * used in the update file to the value of the merged dataset.
782 : */
783 184 : bool OGRS101Reader::UpdateCodesInRecord(DDFRecord *poRecord) const
784 : {
785 184 : const auto poIDField = poRecord->GetField(0);
786 184 : CPLAssert(poIDField);
787 184 : const char *pszIDFieldName = poIDField->GetFieldDefn()->GetName();
788 184 : CPLAssert(pszIDFieldName);
789 :
790 : // Record name
791 : const RecordName nRCNM =
792 184 : poRecord->GetIntSubfield(pszIDFieldName, 0, RCNM_SUBFIELD, 0);
793 :
794 : // Record identifier
795 : const int nRCID =
796 184 : poRecord->GetIntSubfield(pszIDFieldName, 0, RCID_SUBFIELD, 0);
797 :
798 184 : if (EQUAL(pszIDFieldName, IRID_FIELD))
799 : {
800 : const InfoTypeCode nNITC(
801 18 : poRecord->GetIntSubfield(poIDField, NITC_SUBFIELD, 0));
802 :
803 18 : const auto oIter = m_informationTypeCodesRemapping.find(nNITC);
804 18 : if (oIter == m_informationTypeCodesRemapping.end())
805 : {
806 2 : if (!EMIT_ERROR_OR_WARNING(
807 : CPLSPrintf("%s, RCNM=%d, RCID=%d: unknown NITC=%d",
808 : m_osFilename.c_str(), static_cast<int>(nRCNM),
809 : nRCID, static_cast<int>(nNITC))))
810 : {
811 1 : return false;
812 : }
813 : }
814 : else
815 : {
816 16 : poRecord->SetIntSubfield(pszIDFieldName, 0, NITC_SUBFIELD, 0,
817 16 : static_cast<int>(oIter->second));
818 : }
819 : }
820 166 : else if (EQUAL(pszIDFieldName, FRID_FIELD))
821 : {
822 : const FeatureTypeCode nNFTC(
823 24 : poRecord->GetIntSubfield(poIDField, NFTC_SUBFIELD, 0));
824 :
825 24 : const auto oIter = m_featureTypeCodesRemapping.find(nNFTC);
826 24 : if (oIter == m_featureTypeCodesRemapping.end())
827 : {
828 2 : if (!EMIT_ERROR_OR_WARNING(
829 : CPLSPrintf("%s, RCNM=%d, RCID=%d: unknown NFTC=%d",
830 : m_osFilename.c_str(), static_cast<int>(nRCNM),
831 : nRCID, static_cast<int>(nNFTC))))
832 : {
833 1 : return false;
834 : }
835 : }
836 : else
837 : {
838 22 : poRecord->SetIntSubfield(pszIDFieldName, 0, NFTC_SUBFIELD, 0,
839 22 : static_cast<int>(oIter->second));
840 : }
841 : }
842 :
843 723 : for (const char *pszFieldName : {ATTR_FIELD, INAS_FIELD, FASC_FIELD})
844 : {
845 543 : auto apoFields = poRecord->GetFields(pszFieldName);
846 638 : for (auto [fieldIdxSizeT, poField] : cpl::enumerate(apoFields))
847 : {
848 97 : const int fieldIdx = static_cast<int>(fieldIdxSizeT);
849 : const int nRepeatCount =
850 97 : poField->GetParts().size() == 2
851 97 : ? poField->GetParts()[1]->GetRepeatCount()
852 28 : : poField->GetRepeatCount();
853 288 : for (int iSubField = 0; iSubField < nRepeatCount; ++iSubField)
854 : {
855 : const AttrCode nNATC =
856 193 : poRecord->GetIntSubfield(poField, NATC_SUBFIELD, iSubField);
857 193 : const auto oIter = m_attributeCodesRemapping.find(nNATC);
858 193 : if (oIter == m_attributeCodesRemapping.end())
859 : {
860 4 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
861 : "%s, RCNM=%d, RCID=%d, %s[iField=%d, "
862 : "iSubField=%d]: unknown NATC=%d",
863 : m_osFilename.c_str(), static_cast<int>(nRCNM),
864 : nRCID, pszFieldName, fieldIdx, iSubField,
865 : static_cast<int>(nNATC))))
866 : {
867 2 : return false;
868 : }
869 : }
870 : else
871 : {
872 189 : poRecord->SetIntSubfield(pszFieldName, fieldIdx,
873 : NATC_SUBFIELD, iSubField,
874 189 : static_cast<int>(oIter->second));
875 : }
876 : }
877 : }
878 : }
879 :
880 536 : for (const char *pszFieldName : {INAS_FIELD, FASC_FIELD})
881 : {
882 359 : auto apoFields = poRecord->GetFields(pszFieldName);
883 423 : for (auto [fieldIdxSizeT, poField] : cpl::enumerate(apoFields))
884 : {
885 67 : const int fieldIdx = static_cast<int>(fieldIdxSizeT);
886 : const int nRepeatCount =
887 67 : poField->GetParts().size() == 2
888 67 : ? poField->GetParts()[1]->GetRepeatCount()
889 0 : : poField->GetRepeatCount();
890 115 : for (int iSubField = 0; iSubField < nRepeatCount; ++iSubField)
891 : {
892 51 : if (EQUAL(pszFieldName, INAS_FIELD))
893 : {
894 : const InfoAssocCode nNIAC = poRecord->GetIntSubfield(
895 42 : poField, NIAC_SUBFIELD, iSubField);
896 : const auto oIter =
897 42 : m_informationAssociationCodesRemapping.find(nNIAC);
898 42 : if (oIter == m_informationAssociationCodesRemapping.end())
899 : {
900 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
901 : "%s, RCNM=%d, RCID=%d, %s[iField=%d, "
902 : "iSubField=%d]: unknown NIAC=%d",
903 : m_osFilename.c_str(), static_cast<int>(nRCNM),
904 : nRCID, pszFieldName, fieldIdx, iSubField,
905 : static_cast<int>(nNIAC))))
906 : {
907 0 : return false;
908 : }
909 : }
910 : else
911 : {
912 42 : poRecord->SetIntSubfield(
913 : pszFieldName, fieldIdx, NIAC_SUBFIELD, iSubField,
914 42 : static_cast<int>(oIter->second));
915 : }
916 : }
917 : else
918 : {
919 : const FeatureAssocCode nNFAC = poRecord->GetIntSubfield(
920 9 : poField, NFAC_SUBFIELD, iSubField);
921 : const auto oIter =
922 9 : m_featureAssociationCodesRemapping.find(nNFAC);
923 9 : if (oIter == m_featureAssociationCodesRemapping.end())
924 : {
925 2 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
926 : "%s, RCNM=%d, RCID=%d, %s[iField=%d, "
927 : "iSubField=%d]: unknown NFAC=%d",
928 : m_osFilename.c_str(), static_cast<int>(nRCNM),
929 : nRCID, pszFieldName, fieldIdx, iSubField,
930 : static_cast<int>(nNFAC))))
931 : {
932 1 : return false;
933 : }
934 : }
935 : else
936 : {
937 7 : poRecord->SetIntSubfield(
938 : pszFieldName, fieldIdx, NFAC_SUBFIELD, iSubField,
939 7 : static_cast<int>(oIter->second));
940 : }
941 : }
942 :
943 : {
944 : const AssocRoleCode nNARC = poRecord->GetIntSubfield(
945 50 : poField, NARC_SUBFIELD, iSubField);
946 : const auto oIter =
947 50 : m_associationRoleCodesRemapping.find(nNARC);
948 50 : if (oIter == m_associationRoleCodesRemapping.end())
949 : {
950 4 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
951 : "%s, RCNM=%d, RCID=%d, %s[iField=%d, "
952 : "iSubField=%d]: unknown NARC=%d",
953 : m_osFilename.c_str(), static_cast<int>(nRCNM),
954 : nRCID, pszFieldName, fieldIdx, iSubField,
955 : static_cast<int>(nNARC))))
956 : {
957 2 : return false;
958 : }
959 : }
960 : else
961 : {
962 46 : poRecord->SetIntSubfield(
963 : pszFieldName, fieldIdx, NARC_SUBFIELD, iSubField,
964 46 : static_cast<int>(oIter->second));
965 : }
966 : }
967 : }
968 : }
969 : }
970 :
971 177 : return true;
972 : }
973 :
974 : /************************************************************************/
975 : /* ProcessUpdateRecord() */
976 : /************************************************************************/
977 :
978 137 : bool OGRS101Reader::ProcessUpdateRecord(const DDFRecord *poUpdateRecord,
979 : DDFRecord *poTargetRecord) const
980 : {
981 137 : const auto poIDField = poUpdateRecord->GetField(0);
982 137 : CPLAssert(poIDField);
983 137 : const char *pszIDFieldName = poIDField->GetFieldDefn()->GetName();
984 137 : CPLAssert(pszIDFieldName);
985 :
986 : // Record version
987 : const int nRVER =
988 137 : poUpdateRecord->GetIntSubfield(pszIDFieldName, 0, RVER_SUBFIELD, 0);
989 137 : poTargetRecord->SetIntSubfield(pszIDFieldName, 0, RVER_SUBFIELD, 0, nRVER);
990 :
991 137 : if (!ProcessUpdateINASOrFASC(poUpdateRecord, poTargetRecord, INAS_FIELD))
992 2 : return false;
993 :
994 135 : bool bRet = true;
995 135 : if (EQUAL(pszIDFieldName, IRID_FIELD))
996 : {
997 14 : bRet = ProcessUpdateATTR(poUpdateRecord, poTargetRecord);
998 : }
999 121 : else if (EQUAL(pszIDFieldName, PRID_FIELD))
1000 : {
1001 11 : bRet = ProcessUpdateRecordPoint(poUpdateRecord, poTargetRecord);
1002 : }
1003 110 : else if (EQUAL(pszIDFieldName, MRID_FIELD))
1004 : {
1005 33 : bRet = ProcessUpdateRecordMultiPoint(poUpdateRecord, poTargetRecord);
1006 : }
1007 77 : else if (EQUAL(pszIDFieldName, CRID_FIELD))
1008 : {
1009 15 : bRet = ProcessUpdateRecordCurve(poUpdateRecord, poTargetRecord);
1010 : }
1011 62 : else if (EQUAL(pszIDFieldName, CCID_FIELD))
1012 : {
1013 : bRet =
1014 36 : ProcessUpdateRecordCompositeCurve(poUpdateRecord, poTargetRecord);
1015 : }
1016 26 : else if (EQUAL(pszIDFieldName, SRID_FIELD))
1017 : {
1018 7 : bRet = ProcessUpdateRecordSurface(poUpdateRecord, poTargetRecord);
1019 : }
1020 19 : else if (EQUAL(pszIDFieldName, FRID_FIELD))
1021 : {
1022 38 : bRet = ProcessUpdateATTR(poUpdateRecord, poTargetRecord) &&
1023 19 : ProcessUpdateINASOrFASC(poUpdateRecord, poTargetRecord,
1024 38 : FASC_FIELD) &&
1025 19 : ProcessUpdateRecordFeatureType(poUpdateRecord, poTargetRecord);
1026 : }
1027 :
1028 135 : return bRet;
1029 : }
1030 :
1031 : /************************************************************************/
1032 : /* FillFeatureWithNonAttrAssocSubfields() */
1033 : /************************************************************************/
1034 :
1035 : /** Fill attribute fields of the provided feature with the fixed subfields
1036 : * of the INAS or FASC field.
1037 : */
1038 5155 : bool OGRS101Reader::FillFeatureWithNonAttrAssocSubfields(
1039 : const DDFRecord *poRecord, int iRecord, const char *pszFieldName,
1040 : OGRFeature &oFeature) const
1041 : {
1042 5155 : const bool bIsINAS = EQUAL(pszFieldName, INAS_FIELD);
1043 :
1044 10310 : const auto apoAssocFields = poRecord->GetFields(pszFieldName);
1045 10310 : const bool bMultipleAssocs = oFeature.GetDefnRef()->GetFieldIndex(
1046 : bIsINAS ? OGR_FIELD_NAME_REF_INFO_RID
1047 5155 : : OGR_FIELD_NAME_REF_FEAT_RID) < 0;
1048 5155 : const auto &assocRecord =
1049 : bIsINAS ? m_oInformationTypeRecordIndex : m_oFeatureTypeRecordIndex;
1050 :
1051 5990 : for (const auto &[iField, poField] : cpl::enumerate(apoAssocFields))
1052 : {
1053 : const std::string osSuffix =
1054 156 : bMultipleAssocs ? CPLSPrintf("[%d]", static_cast<int>(iField) + 1)
1055 991 : : "";
1056 :
1057 14 : const auto GetErrorContext = [poRecord, iRecord]()
1058 : {
1059 7 : const auto poIDField = poRecord->GetField(0);
1060 7 : CPLAssert(poIDField);
1061 7 : const char *pszIDFieldName = poIDField->GetFieldDefn()->GetName();
1062 7 : return CPLSPrintf("Record index=%d of %s", iRecord, pszIDFieldName);
1063 835 : };
1064 :
1065 : const RecordName nRRNM =
1066 835 : poRecord->GetIntSubfield(poField, RRNM_SUBFIELD, 0);
1067 835 : const RecordName nExpectedRRNM =
1068 835 : bIsINAS ? RECORD_NAME_INFORMATION_TYPE : RECORD_NAME_FEATURE_TYPE;
1069 835 : if (nRRNM != nExpectedRRNM)
1070 : {
1071 4 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
1072 : "%s: Invalid value for RRNM subfield of %s field: "
1073 : "got %d, expected %d.",
1074 : GetErrorContext(), pszFieldName, static_cast<int>(nRRNM),
1075 : static_cast<int>(nExpectedRRNM))))
1076 : {
1077 0 : return false;
1078 : }
1079 : }
1080 :
1081 835 : const int nRRID = poRecord->GetIntSubfield(poField, RRID_SUBFIELD, 0);
1082 835 : if (!assocRecord.FindRecord(nRRID))
1083 : {
1084 1 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
1085 : "%s: Invalid value %d for RRID subfield of %s field: "
1086 : "does not match the record identifier of an existing "
1087 : "InformationType record.",
1088 : GetErrorContext(), static_cast<int>(nRRID), pszFieldName)))
1089 : {
1090 0 : return false;
1091 : }
1092 : }
1093 :
1094 835 : if (bIsINAS)
1095 : {
1096 709 : oFeature.SetField((OGR_FIELD_NAME_REF_INFO_RID + osSuffix).c_str(),
1097 : nRRID);
1098 :
1099 : const InfoAssocCode nNIAC =
1100 709 : poRecord->GetIntSubfield(poField, NIAC_SUBFIELD, 0);
1101 709 : const auto iterIAC = m_informationAssociationCodes.find(nNIAC);
1102 709 : if (iterIAC == m_informationAssociationCodes.end())
1103 : {
1104 1 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
1105 : "%s: cannot find attribute code %d in IACS field "
1106 : "of the Dataset General Information Record.",
1107 : GetErrorContext(), static_cast<int>(nNIAC))))
1108 : {
1109 0 : return false;
1110 : }
1111 : else
1112 : {
1113 1 : oFeature.SetField((OGR_FIELD_NAME_NIAC + osSuffix).c_str(),
1114 : CPLSPrintf("informationAssociationCode%d",
1115 : static_cast<int>(nNIAC)));
1116 : }
1117 : }
1118 : else
1119 : {
1120 708 : oFeature.SetField((OGR_FIELD_NAME_NIAC + osSuffix).c_str(),
1121 708 : iterIAC->second.c_str());
1122 : }
1123 : }
1124 : else
1125 : {
1126 126 : const auto oIterFID = m_oMapFeatureTypeIdToFDefn.find(nRRID);
1127 126 : if (oIterFID != m_oMapFeatureTypeIdToFDefn.end())
1128 : {
1129 126 : oFeature.SetField(
1130 252 : (OGR_FIELD_NAME_REF_FEAT_LAYER_NAME + osSuffix).c_str(),
1131 126 : oIterFID->second->GetName());
1132 : }
1133 :
1134 126 : oFeature.SetField((OGR_FIELD_NAME_REF_FEAT_RID + osSuffix).c_str(),
1135 : nRRID);
1136 :
1137 : const FeatureAssocCode nNFAC =
1138 126 : poRecord->GetIntSubfield(poField, NFAC_SUBFIELD, 0);
1139 126 : const auto iterFAC = m_featureAssociationCodes.find(nNFAC);
1140 126 : if (iterFAC == m_featureAssociationCodes.end())
1141 : {
1142 0 : if (!EMIT_ERROR_OR_WARNING(
1143 : CPLSPrintf("%s: cannot find feature association code "
1144 : "%d in FACS field "
1145 : "of the Dataset General Information Record.",
1146 : GetErrorContext(), static_cast<int>(nNFAC))))
1147 : {
1148 0 : return false;
1149 : }
1150 : else
1151 : {
1152 0 : oFeature.SetField((OGR_FIELD_NAME_NFAC + osSuffix).c_str(),
1153 : CPLSPrintf("featureAssociationCode%d",
1154 : static_cast<int>(nNFAC)));
1155 : }
1156 : }
1157 : else
1158 : {
1159 126 : oFeature.SetField((OGR_FIELD_NAME_NFAC + osSuffix).c_str(),
1160 126 : iterFAC->second.c_str());
1161 : }
1162 : }
1163 :
1164 : const AssocRoleCode nNARC =
1165 835 : poRecord->GetIntSubfield(poField, NARC_SUBFIELD, 0);
1166 835 : const auto iterARC = m_associationRoleCodes.find(nNARC);
1167 835 : if (iterARC == m_associationRoleCodes.end())
1168 : {
1169 1 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
1170 : "%s: cannot find attribute code %d in ARCS field "
1171 : "of the Dataset General Information Record.",
1172 : GetErrorContext(), static_cast<int>(nNARC))))
1173 : {
1174 0 : return false;
1175 : }
1176 : else
1177 : {
1178 2 : oFeature.SetField(((bIsINAS ? OGR_FIELD_NAME_NARC
1179 2 : : OGR_FIELD_NAME_FEATURE_NARC) +
1180 : osSuffix)
1181 : .c_str(),
1182 : CPLSPrintf("associationRoleCode%d",
1183 : static_cast<int>(nNARC)));
1184 : }
1185 : }
1186 : else
1187 : {
1188 1668 : oFeature.SetField(
1189 1668 : ((bIsINAS ? OGR_FIELD_NAME_NARC : OGR_FIELD_NAME_FEATURE_NARC) +
1190 : osSuffix)
1191 : .c_str(),
1192 834 : iterARC->second.c_str());
1193 : }
1194 :
1195 835 : const char *pszSubFieldName = bIsINAS ? IUIN_SUBFIELD : FAUI_SUBFIELD;
1196 : const int nInstruction =
1197 835 : poRecord->GetIntSubfield(poField, pszSubFieldName, 0);
1198 835 : if (nInstruction != INSTRUCTION_INSERT)
1199 : {
1200 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf("%s: wrong value %d for %s "
1201 : "subfield of %s field.",
1202 : GetErrorContext(),
1203 : nInstruction, pszSubFieldName,
1204 : pszFieldName)))
1205 : {
1206 0 : return false;
1207 : }
1208 : }
1209 : }
1210 :
1211 5155 : return true;
1212 : }
|