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 470 : 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 470 : const char *lastPathSep = strrchr(pszFile, '/');
48 : #endif
49 470 : if (lastPathSep)
50 470 : pszFile = lastPathSep + 1;
51 :
52 470 : if (bError)
53 : {
54 164 : if (bRecoverable)
55 : {
56 147 : 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 17 : CPLError(CE_Failure, CPLE_AppDefined, "at %s:%d (%s()): %s",
66 : pszFile, nLine, pszFunc, pszMsg);
67 : }
68 : }
69 : else
70 : {
71 306 : CPLError(CE_Warning, CPLE_AppDefined, "at %s:%d (%s()): %s", pszFile,
72 : nLine, pszFunc, pszMsg);
73 : }
74 470 : 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 355 : bool OGRS101Reader::Load(GDALOpenInfo *poOpenInfo)
87 : {
88 355 : if (!poOpenInfo->fpL)
89 0 : return false;
90 :
91 355 : m_bStrict = CPLTestBool(
92 355 : CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "STRICT", "YES"));
93 355 : CPLAssert(!m_poModule);
94 355 : m_poModule = std::make_unique<DDFModule>();
95 355 : m_osFilename = poOpenInfo->pszFilename;
96 355 : VSILFILE *fp = poOpenInfo->fpL;
97 355 : poOpenInfo->fpL = nullptr;
98 355 : if (!m_poModule->Open(m_osFilename.c_str(), false, fp))
99 0 : return false;
100 :
101 355 : if (!m_poModule->FindFieldDefn(DSID_FIELD))
102 : {
103 0 : CPLError(CE_Failure, CPLE_AppDefined,
104 : "%s is an ISO8211 file, but not a S-101 data file.",
105 : m_osFilename.c_str());
106 0 : return false;
107 : }
108 :
109 355 : DDFRecord *poRecord = m_poModule->ReadRecord();
110 355 : if (!ReadDatasetGeneralInformationRecord(poRecord))
111 48 : return false;
112 :
113 307 : poRecord = m_poModule->ReadRecord();
114 307 : if (!poRecord)
115 0 : return EMIT_ERROR("no Dataset Coordinate Reference System record.");
116 307 : bool bSkipFirstReadRecord = false;
117 307 : if (!m_bStrict && !poRecord->FindField(CSID_FIELD))
118 : {
119 1 : EMIT_ERROR_OR_WARNING("CSID field not found.");
120 1 : bSkipFirstReadRecord = true;
121 : }
122 306 : else if (!ReadCSID(poRecord))
123 29 : return false;
124 :
125 278 : return IngestRecords(bSkipFirstReadRecord ? poRecord : nullptr) &&
126 266 : ReadFeatureCatalog() && CreateInformationTypeFeatureDefn() &&
127 257 : CreatePointFeatureDefns() && CreateMultiPointFeatureDefns() &&
128 249 : CreateCurveFeatureDefn() && CreateCompositeCurveFeatureDefn() &&
129 544 : CreateSurfaceFeatureDefn() && CreateFeatureTypeFeatureDefns();
130 : }
131 :
132 : /************************************************************************/
133 : /* ReadFeatureCatalog() */
134 : /************************************************************************/
135 :
136 266 : bool OGRS101Reader::ReadFeatureCatalog()
137 : {
138 : OGRS101FeatureCatalog::LoadingStatus status;
139 266 : std::tie(status, m_poFeatureCatalog) =
140 532 : OGRS101FeatureCatalog::GetSingletonFeatureCatalog(m_bStrict);
141 266 : return status != OGRS101FeatureCatalog::LoadingStatus::ERROR;
142 : }
143 :
144 : /************************************************************************/
145 : /* IngestRecords() */
146 : /************************************************************************/
147 :
148 : /** Ingest records into the various m_oXXXXXIndex members
149 : *
150 : * @param poRecordIn Already read record, or nullptr to advance to the next one
151 : */
152 278 : bool OGRS101Reader::IngestRecords(const DDFRecord *poRecordIn)
153 : {
154 : // Must NOT be set as static, as it depends on "this" !
155 : /* NOT STATIC */ const struct
156 : {
157 : const char *pszFieldName;
158 : const char *pszType;
159 : RecordName nRCNM;
160 : int nExpectedCount;
161 : DDFRecordIndex &oIndex;
162 278 : } asRecordTypeDesc[] = {
163 : {IRID_FIELD, "information type", RECORD_NAME_INFORMATION_TYPE,
164 278 : m_nCountInformationRecord, m_oInformationTypeRecordIndex},
165 278 : {PRID_FIELD, "point", RECORD_NAME_POINT, m_nCountPointRecord,
166 278 : m_oPointRecordIndex},
167 : {MRID_FIELD, "multipoint", RECORD_NAME_MULTIPOINT,
168 278 : m_nCountMultiPointRecord, m_oMultiPointRecordIndex},
169 278 : {CRID_FIELD, "curve", RECORD_NAME_CURVE, m_nCountCurveRecord,
170 278 : m_oCurveRecordIndex},
171 : {CCID_FIELD, "composite curve", RECORD_NAME_COMPOSITE_CURVE,
172 278 : m_nCountCompositeCurveRecord, m_oCompositeCurveRecordIndex},
173 278 : {SRID_FIELD, "surface", RECORD_NAME_SURFACE, m_nCountSurfaceRecord,
174 278 : m_oSurfaceRecordIndex},
175 : {FRID_FIELD, "feature type", RECORD_NAME_FEATURE_TYPE,
176 278 : m_nCountFeatureTypeRecord, m_oFeatureTypeRecordIndex},
177 278 : };
178 :
179 278 : constexpr int STAR = std::numeric_limits<int>::max();
180 : using NameOccMinOccMax = std::tuple<const char *, int, int>;
181 : const std::map<RecordName, std::vector<std::vector<NameOccMinOccMax>>>
182 : mapExpectedFields = {
183 : {
184 : RECORD_NAME_INFORMATION_TYPE,
185 : {{{IRID_FIELD, 1, 1},
186 : {ATTR_FIELD, 0, STAR},
187 : {INAS_FIELD, 0, STAR}}},
188 : },
189 : {RECORD_NAME_POINT,
190 : {
191 : {{{PRID_FIELD, 1, 1},
192 : {INAS_FIELD, 0, STAR},
193 : {C2IT_FIELD, 1, 1}}},
194 : {{{PRID_FIELD, 1, 1},
195 : {INAS_FIELD, 0, STAR},
196 : {C3IT_FIELD, 1, 1}}},
197 : }},
198 : {RECORD_NAME_MULTIPOINT,
199 : {
200 : {{{MRID_FIELD, 1, 1},
201 : {INAS_FIELD, 0, STAR},
202 : {C2IL_FIELD, 1, STAR}}},
203 : {{{MRID_FIELD, 1, 1},
204 : {INAS_FIELD, 0, STAR},
205 : {C3IL_FIELD, 1, STAR}}},
206 : }},
207 : {RECORD_NAME_CURVE,
208 : {
209 : {{{CRID_FIELD, 1, 1},
210 : {INAS_FIELD, 0, STAR},
211 : {PTAS_FIELD, 1, 1},
212 : {SEGH_FIELD, 1, 1},
213 : {C2IL_FIELD, 1, STAR}}},
214 : }},
215 : {RECORD_NAME_COMPOSITE_CURVE,
216 : {
217 : {{{CCID_FIELD, 1, 1},
218 : {INAS_FIELD, 0, STAR},
219 : {CUCO_FIELD, 1, STAR}}},
220 : }},
221 : {RECORD_NAME_SURFACE,
222 : {
223 : {{{SRID_FIELD, 1, 1},
224 : {INAS_FIELD, 0, STAR},
225 : {RIAS_FIELD, 1, STAR}}},
226 : }},
227 : {RECORD_NAME_FEATURE_TYPE,
228 : {
229 : {{{FRID_FIELD, 1, 1},
230 : {FOID_FIELD, 1, 1},
231 : {ATTR_FIELD, 0, STAR},
232 : {INAS_FIELD, 0, STAR},
233 : {SPAS_FIELD, 0, STAR},
234 : {FASC_FIELD, 0, STAR},
235 : {MASK_FIELD, 0, STAR}}},
236 : }},
237 7506 : };
238 :
239 : // Loop through all records (except first two DSID and CRID already parsed)
240 : // and dispatch them to the appropriate DDFRecordIndex member variable.
241 278 : const DDFRecord *poRecord = nullptr;
242 278 : const int iFirstRecord = poRecordIn ? 1 : 2;
243 1801 : for (int iRecord = iFirstRecord;
244 1801 : poRecordIn || (poRecord = m_poModule->ReadRecord()) != nullptr;
245 : ++iRecord)
246 : {
247 1537 : if (poRecordIn)
248 1 : std::swap(poRecord, poRecordIn);
249 :
250 1537 : const auto poField = poRecord->GetField(0);
251 1537 : if (!poField)
252 11 : return EMIT_ERROR(
253 : CPLSPrintf("Record index %d without field.", iRecord));
254 1537 : const char *pszFieldName = poField->GetFieldDefn()->GetName();
255 :
256 : // Record name
257 : const RecordName nRCNM =
258 1537 : poRecord->GetIntSubfield(pszFieldName, 0, RCNM_SUBFIELD, 0);
259 :
260 : // Record identifier
261 : const int nRCID =
262 1537 : poRecord->GetIntSubfield(pszFieldName, 0, RCID_SUBFIELD, 0);
263 :
264 : // Check that the fields found in the record match the expectations
265 : // from the spec. That is check there are no missing required field,
266 : // no duplicate field or unexpected field.
267 1537 : std::vector<std::string> fieldsInRecord;
268 5996 : for (const auto &oField : poRecord->GetFields())
269 : {
270 4459 : fieldsInRecord.push_back(oField.GetFieldDefn()->GetName());
271 : }
272 1537 : const auto oIter = mapExpectedFields.find(nRCNM);
273 1537 : if (oIter != mapExpectedFields.end())
274 : {
275 1536 : bool bMatch = false;
276 1625 : for (const auto &expectedFields : oIter->second)
277 : {
278 1609 : bMatch = true;
279 1609 : size_t iExpected = 0;
280 6222 : for (size_t i = 0; bMatch && i < fieldsInRecord.size(); ++i)
281 : {
282 6246 : for (; iExpected < expectedFields.size(); ++iExpected)
283 : {
284 6246 : if (fieldsInRecord[i] ==
285 6246 : std::get<0>(expectedFields[iExpected]))
286 : {
287 : const int nMaxOcc =
288 4548 : std::get<2>(expectedFields[iExpected]);
289 4548 : if (nMaxOcc == 1)
290 : {
291 4283 : if (i > 0 &&
292 1337 : fieldsInRecord[i - 1] == fieldsInRecord[i])
293 : {
294 0 : bMatch = false;
295 : }
296 : else
297 : {
298 2946 : ++iExpected;
299 : }
300 : }
301 1988 : else if (i + 1 == fieldsInRecord.size() ||
302 386 : fieldsInRecord[i + 1] != fieldsInRecord[i])
303 : {
304 1517 : ++iExpected;
305 : }
306 4548 : break;
307 : }
308 : else
309 : {
310 : const int nMinOcc =
311 1698 : std::get<1>(expectedFields[iExpected]);
312 1698 : if (nMinOcc == 1)
313 : {
314 67 : bMatch = false;
315 67 : break;
316 : }
317 : }
318 : }
319 :
320 : // If we have reached the end of expected fields but
321 : // there are remaining fields, then there are missing
322 : // compulsory fields.
323 5563 : if (iExpected == expectedFields.size() &&
324 948 : i + 1 < fieldsInRecord.size())
325 : {
326 2 : bMatch = false;
327 2 : break;
328 : }
329 : }
330 :
331 : // Skip optional fields after the last one in the record
332 1609 : if (bMatch && iExpected < expectedFields.size())
333 : {
334 1534 : for (; iExpected < expectedFields.size(); ++iExpected)
335 : {
336 : const int nMinOcc =
337 960 : std::get<1>(expectedFields[iExpected]);
338 960 : if (nMinOcc > 0)
339 : {
340 20 : bMatch = false;
341 20 : break;
342 : }
343 : }
344 : }
345 1609 : bMatch = bMatch && iExpected == expectedFields.size();
346 1609 : if (bMatch)
347 1520 : break;
348 : }
349 1552 : if (!bMatch &&
350 16 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
351 : "Record index %d, RCNM=%d, RCID=%d: invalid "
352 : "sequence of fields.",
353 : iRecord, static_cast<int>(nRCNM), static_cast<int>(nRCID))))
354 : {
355 8 : return false;
356 : }
357 : }
358 :
359 1529 : const auto iterRecordTypeDesc = std::find_if(
360 : std::begin(asRecordTypeDesc), std::end(asRecordTypeDesc),
361 6386 : [pszFieldName](const auto &sRecordTypeDesc)
362 : {
363 6386 : return strcmp(pszFieldName, sRecordTypeDesc.pszFieldName) == 0;
364 : });
365 1529 : if (iterRecordTypeDesc != std::end(asRecordTypeDesc))
366 : {
367 1528 : const auto &sRecordTypeDesc = *iterRecordTypeDesc;
368 :
369 1528 : if (nRCNM != sRecordTypeDesc.nRCNM &&
370 0 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
371 : "Record index %d: invalid value %d for RCNM field of %s.",
372 : iRecord, static_cast<int>(nRCNM), pszFieldName)))
373 : {
374 0 : return false;
375 : }
376 :
377 1528 : if (nRCID <= 0)
378 : {
379 6 : if (!EMIT_ERROR_OR_WARNING(
380 : CPLSPrintf("Record index %d: invalid value %d for "
381 : "RCID subfield of %s.",
382 : iRecord, nRCID, pszFieldName)))
383 : {
384 3 : return false;
385 : }
386 3 : break;
387 : }
388 :
389 1522 : if (sRecordTypeDesc.oIndex.FindRecord(nRCID) &&
390 0 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
391 : "Record index %d: several %s records have RCID = %d.",
392 : iRecord, pszFieldName, nRCID)))
393 : {
394 0 : return false;
395 : }
396 1522 : sRecordTypeDesc.oIndex.AddRecord(nRCID, poRecord->Clone());
397 : }
398 1 : else if (!EMIT_ERROR_OR_WARNING(
399 : CPLSPrintf("Record index %d: unknown field name %s.",
400 : iRecord, pszFieldName)))
401 : {
402 0 : return false;
403 : }
404 : }
405 :
406 : // Check consistency between number of records of each category (information
407 : // type, point, etc.) and the number actually found.
408 2129 : for (const auto &sRecordTypeDesc : asRecordTypeDesc)
409 : {
410 1863 : if (sRecordTypeDesc.oIndex.GetCount() !=
411 1875 : sRecordTypeDesc.nExpectedCount &&
412 12 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
413 : "%d %s records mentioned in DSSI field, but %d actually found.",
414 : sRecordTypeDesc.nExpectedCount, sRecordTypeDesc.pszType,
415 : sRecordTypeDesc.oIndex.GetCount())))
416 : {
417 1 : return false;
418 : }
419 : }
420 :
421 266 : return true;
422 : }
423 :
424 : /************************************************************************/
425 : /* FillFeatureWithNonAttrAssocSubfields() */
426 : /************************************************************************/
427 :
428 : /** Fill attribute fields of the provided feature with the fixed subfields
429 : * of the INAS or FASC field.
430 : */
431 3256 : bool OGRS101Reader::FillFeatureWithNonAttrAssocSubfields(
432 : const DDFRecord *poRecord, int iRecord, const char *pszFieldName,
433 : OGRFeature &oFeature) const
434 : {
435 3256 : const bool bIsINAS = EQUAL(pszFieldName, INAS_FIELD);
436 :
437 6512 : const auto apoAssocFields = poRecord->GetFields(pszFieldName);
438 6512 : const bool bMultipleAssocs = oFeature.GetDefnRef()->GetFieldIndex(
439 : bIsINAS ? OGR_FIELD_NAME_REF_INFO_RID
440 3256 : : OGR_FIELD_NAME_REF_FEAT_RID) < 0;
441 3256 : const auto &assocRecord =
442 : bIsINAS ? m_oInformationTypeRecordIndex : m_oFeatureTypeRecordIndex;
443 :
444 3856 : for (const auto &[iField, poField] : cpl::enumerate(apoAssocFields))
445 : {
446 : const std::string osSuffix =
447 104 : bMultipleAssocs ? CPLSPrintf("[%d]", static_cast<int>(iField) + 1)
448 704 : : "";
449 :
450 14 : const auto GetErrorContext = [poRecord, iRecord]()
451 : {
452 7 : const auto poIDField = poRecord->GetField(0);
453 7 : CPLAssert(poIDField);
454 7 : const char *pszIDFieldName = poIDField->GetFieldDefn()->GetName();
455 7 : return CPLSPrintf("Record index=%d of %s", iRecord, pszIDFieldName);
456 600 : };
457 :
458 : const RecordName nRRNM =
459 600 : poRecord->GetIntSubfield(poField, RRNM_SUBFIELD, 0);
460 600 : const RecordName nExpectedRRNM =
461 600 : bIsINAS ? RECORD_NAME_INFORMATION_TYPE : RECORD_NAME_FEATURE_TYPE;
462 600 : if (nRRNM != nExpectedRRNM)
463 : {
464 4 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
465 : "%s: Invalid value for RRNM subfield of %s field: "
466 : "got %d, expected %d.",
467 : GetErrorContext(), pszFieldName, static_cast<int>(nRRNM),
468 : static_cast<int>(nExpectedRRNM))))
469 : {
470 0 : return false;
471 : }
472 : }
473 :
474 600 : const int nRRID = poRecord->GetIntSubfield(poField, RRID_SUBFIELD, 0);
475 600 : if (!assocRecord.FindRecord(nRRID))
476 : {
477 1 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
478 : "%s: Invalid value %d for RRID subfield of %s field: "
479 : "does not match the record identifier of an existing "
480 : "InformationType record.",
481 : GetErrorContext(), static_cast<int>(nRRID), pszFieldName)))
482 : {
483 0 : return false;
484 : }
485 : }
486 :
487 600 : if (bIsINAS)
488 : {
489 500 : oFeature.SetField((OGR_FIELD_NAME_REF_INFO_RID + osSuffix).c_str(),
490 : nRRID);
491 :
492 : const InfoAssocCode nNIAC =
493 500 : poRecord->GetIntSubfield(poField, NIAC_SUBFIELD, 0);
494 500 : const auto iterIAC = m_informationAssociationCodes.find(nNIAC);
495 500 : if (iterIAC == m_informationAssociationCodes.end())
496 : {
497 1 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
498 : "%s: cannot find attribute code %d in IACS field "
499 : "of the Dataset General Information Record.",
500 : GetErrorContext(), static_cast<int>(nNIAC))))
501 : {
502 0 : return false;
503 : }
504 : else
505 : {
506 1 : oFeature.SetField((OGR_FIELD_NAME_NIAC + osSuffix).c_str(),
507 : CPLSPrintf("informationAssociationCode%d",
508 : static_cast<int>(nNIAC)));
509 : }
510 : }
511 : else
512 : {
513 499 : oFeature.SetField((OGR_FIELD_NAME_NIAC + osSuffix).c_str(),
514 499 : iterIAC->second.c_str());
515 : }
516 : }
517 : else
518 : {
519 100 : const auto oIterFID = m_oMapFeatureTypeIdToFDefn.find(nRRID);
520 100 : if (oIterFID != m_oMapFeatureTypeIdToFDefn.end())
521 : {
522 100 : oFeature.SetField(
523 200 : (OGR_FIELD_NAME_REF_FEAT_LAYER_NAME + osSuffix).c_str(),
524 100 : oIterFID->second->GetName());
525 : }
526 :
527 100 : oFeature.SetField((OGR_FIELD_NAME_REF_FEAT_RID + osSuffix).c_str(),
528 : nRRID);
529 :
530 : const FeatureAssocCode nNFAC =
531 100 : poRecord->GetIntSubfield(poField, NFAC_SUBFIELD, 0);
532 100 : const auto iterFAC = m_featureAssociationCodes.find(nNFAC);
533 100 : if (iterFAC == m_featureAssociationCodes.end())
534 : {
535 0 : if (!EMIT_ERROR_OR_WARNING(
536 : CPLSPrintf("%s: cannot find feature association code "
537 : "%d in FACS field "
538 : "of the Dataset General Information Record.",
539 : GetErrorContext(), static_cast<int>(nNFAC))))
540 : {
541 0 : return false;
542 : }
543 : else
544 : {
545 0 : oFeature.SetField((OGR_FIELD_NAME_NFAC + osSuffix).c_str(),
546 : CPLSPrintf("featureAssociationCode%d",
547 : static_cast<int>(nNFAC)));
548 : }
549 : }
550 : else
551 : {
552 100 : oFeature.SetField((OGR_FIELD_NAME_NFAC + osSuffix).c_str(),
553 100 : iterFAC->second.c_str());
554 : }
555 : }
556 :
557 : const AssocRoleCode nNARC =
558 600 : poRecord->GetIntSubfield(poField, NARC_SUBFIELD, 0);
559 600 : const auto iterARC = m_associationRoleCodes.find(nNARC);
560 600 : if (iterARC == m_associationRoleCodes.end())
561 : {
562 1 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
563 : "%s: cannot find attribute code %d in ARCS field "
564 : "of the Dataset General Information Record.",
565 : GetErrorContext(), static_cast<int>(nNARC))))
566 : {
567 0 : return false;
568 : }
569 : else
570 : {
571 2 : oFeature.SetField(((bIsINAS ? OGR_FIELD_NAME_NARC
572 2 : : OGR_FIELD_NAME_FEATURE_NARC) +
573 : osSuffix)
574 : .c_str(),
575 : CPLSPrintf("associationRoleCode%d",
576 : static_cast<int>(nNARC)));
577 : }
578 : }
579 : else
580 : {
581 1198 : oFeature.SetField(
582 1198 : ((bIsINAS ? OGR_FIELD_NAME_NARC : OGR_FIELD_NAME_FEATURE_NARC) +
583 : osSuffix)
584 : .c_str(),
585 599 : iterARC->second.c_str());
586 : }
587 :
588 600 : const char *pszSubFieldName = bIsINAS ? IUIN_SUBFIELD : FAUI_SUBFIELD;
589 : const int nInstruction =
590 600 : poRecord->GetIntSubfield(poField, pszSubFieldName, 0);
591 600 : if (nInstruction != INSTRUCTION_INSERT)
592 : {
593 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf("%s: wrong value %d for %s "
594 : "subfield of %s field.",
595 : GetErrorContext(),
596 : nInstruction, pszSubFieldName,
597 : pszFieldName)))
598 : {
599 0 : return false;
600 : }
601 : }
602 : }
603 :
604 3256 : return true;
605 : }
|