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