/src/gdal/ogr/ogrsf_frmts/s101/ogrs101readerattributes.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 "ogrs101featurecatalog.h" |
15 | | #include "ogrs101readerconstants.h" |
16 | | |
17 | | #include <algorithm> |
18 | | #include <charconv> |
19 | | #include <cfloat> |
20 | | #include <limits> |
21 | | #include <memory> |
22 | | #include <optional> |
23 | | #include <set> |
24 | | #include <tuple> |
25 | | #include <utility> |
26 | | |
27 | | #include "include_fast_float.h" |
28 | | |
29 | | /************************************************************************/ |
30 | | /* IngestAttributes() */ |
31 | | /************************************************************************/ |
32 | | |
33 | | /** For a given record that has a ATTR/INAS/FACS field, ingest all attributes |
34 | | * from a particular instance of that field |
35 | | */ |
36 | | bool OGRS101Reader::IngestAttributes( |
37 | | const DDFRecord *poRecord, int iRecord, const char *pszIDFieldName, |
38 | | const char *pszAttrFieldName, const DDFField *poATTRField, int iField, |
39 | | bool bMultipleFields, std::vector<S101AttrDef> &asS101AttrDefs) const |
40 | 6.00k | { |
41 | 6.00k | std::set<std::tuple<AttrCode, AttrRepeat, AttrIndex>> oSetNATC_ATIX_PAIX; |
42 | | |
43 | 6.00k | const auto GetErrorContext = [iRecord, pszIDFieldName, pszAttrFieldName, |
44 | 6.00k | iField, bMultipleFields](AttrIndex iATTR) |
45 | 6.00k | { |
46 | 413 | if (bMultipleFields) |
47 | 91 | { |
48 | 91 | return CPLSPrintf( |
49 | 91 | "Record index=%d of %s, %s[%d] field, attribute idx=%d", |
50 | 91 | iRecord, pszIDFieldName, pszAttrFieldName, iField, |
51 | 91 | static_cast<int>(iATTR)); |
52 | 91 | } |
53 | 322 | else |
54 | 322 | { |
55 | 322 | return CPLSPrintf( |
56 | 322 | "Record index=%d of %s, %s field, attribute idx=%d", iRecord, |
57 | 322 | pszIDFieldName, pszAttrFieldName, static_cast<int>(iATTR)); |
58 | 322 | } |
59 | 413 | }; |
60 | | |
61 | 6.00k | const AttrCode nLargestNATC( |
62 | 6.00k | !m_attributeCodes.empty() ? m_attributeCodes.rbegin()->first : 0); |
63 | 6.00k | const bool bAttributeCodesSequential = |
64 | 6.00k | !m_attributeCodes.empty() && m_attributeCodes.begin()->first == 1 && |
65 | 5.31k | static_cast<size_t>(static_cast<int>(nLargestNATC)) == |
66 | 5.31k | m_attributeCodes.size(); |
67 | | |
68 | 6.00k | const int nRepeatCount = poATTRField->GetRepeatCount(); |
69 | 6.00k | AttrCode nLastNATC = -1; |
70 | 6.00k | AttrRepeat nLastATIX = -1; |
71 | 6.00k | AttrIndex nLastPAIX = -1; |
72 | | |
73 | | // Find multi-valued parts of the path |
74 | 6.00k | std::map<std::pair<AttrCode, AttrIndex>, int> oMapOccurrenceCount; |
75 | 23.5k | for (AttrIndex iATTR = 0; iATTR < nRepeatCount; ++iATTR) |
76 | 17.5k | { |
77 | 17.5k | const auto GetIntSubfield = |
78 | 17.5k | [poRecord, poATTRField, iATTR](const char *pszSubFieldName) |
79 | 35.0k | { |
80 | 35.0k | return poRecord->GetIntSubfield(poATTRField, pszSubFieldName, |
81 | 35.0k | static_cast<int>(iATTR)); |
82 | 35.0k | }; |
83 | | |
84 | 17.5k | ++oMapOccurrenceCount[{GetIntSubfield(NATC_SUBFIELD), |
85 | 17.5k | GetIntSubfield(PAIX_SUBFIELD)}]; |
86 | 17.5k | } |
87 | | |
88 | 6.00k | const size_t nS101AttrDefsBaseIdx = asS101AttrDefs.size(); |
89 | | |
90 | 20.1k | for (AttrIndex iATTR = 0; iATTR < nRepeatCount; ++iATTR) |
91 | 14.5k | { |
92 | 14.5k | const auto GetIntSubfield = |
93 | 14.5k | [poRecord, poATTRField, iATTR](const char *pszSubFieldName) |
94 | 57.5k | { |
95 | 57.5k | return poRecord->GetIntSubfield(poATTRField, pszSubFieldName, |
96 | 57.5k | static_cast<int>(iATTR)); |
97 | 57.5k | }; |
98 | | |
99 | 14.5k | const int nATIN = GetIntSubfield(ATIN_SUBFIELD); |
100 | 14.5k | if (nATIN != INSTRUCTION_INSERT) |
101 | 135 | { |
102 | 135 | if (!EMIT_ERROR_OR_WARNING( |
103 | 135 | CPLSPrintf("%s: wrong value %d for ATIN subfield.", |
104 | 135 | GetErrorContext(iATTR), nATIN))) |
105 | 135 | { |
106 | 135 | return false; |
107 | 135 | } |
108 | 0 | nLastNATC = -1; |
109 | 0 | nLastATIX = -1; |
110 | 0 | nLastPAIX = -1; |
111 | 0 | asS101AttrDefs.push_back(S101AttrDef()); |
112 | 0 | continue; |
113 | 135 | } |
114 | | |
115 | 14.4k | const AttrCode nNATC(GetIntSubfield(NATC_SUBFIELD)); |
116 | 14.4k | if (!cpl::contains(m_attributeCodes, nNATC) && |
117 | 158 | !EMIT_ERROR_OR_WARNING( |
118 | 14.4k | CPLSPrintf("%s: cannot find attribute code %d in ATCS field " |
119 | 14.4k | "of the Dataset General Information Record%s.", |
120 | 14.4k | GetErrorContext(iATTR), static_cast<int>(nNATC), |
121 | 14.4k | bAttributeCodesSequential |
122 | 14.4k | ? CPLSPrintf(". Must be in [1, %d]", |
123 | 14.4k | static_cast<int>(nLargestNATC)) |
124 | 14.4k | : ""))) |
125 | 158 | { |
126 | 158 | return false; |
127 | 158 | } |
128 | | |
129 | 14.2k | const AttrRepeat nATIX(GetIntSubfield(ATIX_SUBFIELD)); |
130 | 14.2k | if (!(nATIX >= 1 && nATIX <= nRepeatCount)) |
131 | 59 | { |
132 | 59 | if (!EMIT_ERROR_OR_WARNING( |
133 | 59 | CPLSPrintf("%s: wrong value %d for ATIX subfield. " |
134 | 59 | "Must be in [1, %d].", |
135 | 59 | GetErrorContext(iATTR), static_cast<int>(nATIX), |
136 | 59 | nRepeatCount))) |
137 | 59 | { |
138 | 59 | return false; |
139 | 59 | } |
140 | 0 | nLastNATC = -1; |
141 | 0 | nLastATIX = -1; |
142 | 0 | nLastPAIX = -1; |
143 | 0 | asS101AttrDefs.push_back(S101AttrDef()); |
144 | 0 | continue; |
145 | 59 | } |
146 | | |
147 | 14.2k | const AttrIndex nPAIX = GetIntSubfield(PAIX_SUBFIELD); |
148 | | // The parent index must be lower than the current attribute index, |
149 | | // since parents are required to be listed before. |
150 | 14.2k | if (!(nPAIX >= 0 && nPAIX <= iATTR)) |
151 | 43 | { |
152 | 43 | if (!EMIT_ERROR_OR_WARNING( |
153 | 43 | CPLSPrintf("%s: wrong value %d for PAIX subfield. " |
154 | 43 | "Must be in [0, %d].", |
155 | 43 | GetErrorContext(iATTR), static_cast<int>(nPAIX), |
156 | 43 | static_cast<int>(iATTR)))) |
157 | 43 | { |
158 | 43 | return false; |
159 | 43 | } |
160 | 0 | nLastNATC = -1; |
161 | 0 | nLastATIX = -1; |
162 | 0 | nLastPAIX = -1; |
163 | 0 | asS101AttrDefs.push_back(S101AttrDef()); |
164 | 0 | continue; |
165 | 43 | } |
166 | | |
167 | 14.1k | if (nPAIX == nLastPAIX) |
168 | 1.52k | { |
169 | 1.52k | if (nNATC == nLastNATC) |
170 | 221 | { |
171 | 221 | if (nATIX != nLastATIX + 1 && |
172 | 2 | !EMIT_ERROR_OR_WARNING(CPLSPrintf( |
173 | 221 | "%s: wrong value %d for ATIX subfield. Expected %d.", |
174 | 221 | GetErrorContext(iATTR), static_cast<int>(nATIX), |
175 | 221 | static_cast<int>(nLastATIX + 1)))) |
176 | 2 | { |
177 | 2 | return false; |
178 | 2 | } |
179 | 221 | } |
180 | 1.30k | else if (nATIX != 1) |
181 | 3 | { |
182 | 3 | if (!EMIT_ERROR_OR_WARNING(CPLSPrintf( |
183 | 3 | "%s: wrong value %d for ATIX subfield. Expected %d.", |
184 | 3 | GetErrorContext(iATTR), static_cast<int>(nATIX), 1))) |
185 | 3 | { |
186 | 3 | return false; |
187 | 3 | } |
188 | 3 | } |
189 | 1.52k | } |
190 | | |
191 | | // (NATC,ATIX,PAIX) tuple should be unique within a record |
192 | 14.1k | if (!oSetNATC_ATIX_PAIX.insert({nNATC, nATIX, nPAIX}).second && |
193 | 3 | !EMIT_ERROR_OR_WARNING( |
194 | 14.1k | CPLSPrintf("%s: several instances of " |
195 | 14.1k | "(NATC,ATIX,PAIX)=(%d,%d,%d) " |
196 | 14.1k | "in field %s of the same record.", |
197 | 14.1k | GetErrorContext(iATTR), static_cast<int>(nNATC), |
198 | 14.1k | static_cast<int>(nATIX), static_cast<int>(nPAIX), |
199 | 14.1k | pszAttrFieldName))) |
200 | 3 | { |
201 | 3 | return false; |
202 | 3 | } |
203 | | |
204 | | // Does this attribute have a parent? |
205 | 14.1k | const bool bIsMultiValued = oMapOccurrenceCount[{nNATC, nPAIX}] > 1; |
206 | 14.1k | PathVector oReversedPath{{nNATC, bIsMultiValued ? nATIX : 0}}; |
207 | 14.1k | if (nPAIX > 0) |
208 | 8.65k | { |
209 | | // Assertion can't trigger because nPAIX <= iATTR < asS101AttrDefs.size() - nS101AttrDefsBaseIdx |
210 | 8.65k | CPLAssert(nS101AttrDefsBaseIdx + |
211 | 8.65k | static_cast<size_t>(static_cast<int>(nPAIX) - 1) < |
212 | 8.65k | asS101AttrDefs.size()); |
213 | 8.65k | auto &sParentAttrDef = asS101AttrDefs[nS101AttrDefsBaseIdx + |
214 | 8.65k | static_cast<int>(nPAIX) - 1]; |
215 | 8.65k | sParentAttrDef.bIsParent = true; |
216 | 8.65k | if (!sParentAttrDef.osVal.empty() && |
217 | 8 | !EMIT_ERROR_OR_WARNING(CPLSPrintf( |
218 | 8.65k | "%s: parent attribute of index PAIX=%d has " |
219 | 8.65k | "a non empty ATVL subfield.", |
220 | 8.65k | GetErrorContext(iATTR), static_cast<int>(nPAIX)))) |
221 | 8 | { |
222 | 8 | return false; |
223 | 8 | } |
224 | 8.64k | #if defined(__GNUC__) |
225 | 8.64k | #pragma GCC diagnostic push |
226 | 8.64k | #pragma GCC diagnostic ignored "-Wnull-dereference" |
227 | 8.64k | #endif |
228 | 8.64k | oReversedPath.insert(oReversedPath.end(), |
229 | 8.64k | sParentAttrDef.oReversedPath.begin(), |
230 | 8.64k | sParentAttrDef.oReversedPath.end()); |
231 | 8.64k | #if defined(__GNUC__) |
232 | 8.64k | #pragma GCC diagnostic pop |
233 | 8.64k | #endif |
234 | 8.64k | } |
235 | | |
236 | 14.1k | const char *pszATVL = poRecord->GetStringSubfield( |
237 | 14.1k | poATTRField, ATVL_SUBFIELD, static_cast<int>(iATTR)); |
238 | 14.1k | if (!pszATVL && |
239 | 2 | !EMIT_ERROR_OR_WARNING(CPLSPrintf("%s: cannot read ATVL subfield.", |
240 | 14.1k | GetErrorContext(iATTR)))) |
241 | 2 | { |
242 | 2 | return false; |
243 | 2 | } |
244 | | |
245 | 14.1k | S101AttrDef sAttrDef; |
246 | 14.1k | sAttrDef.iField = iField; |
247 | 14.1k | sAttrDef.bMultipleFields = bMultipleFields; |
248 | 14.1k | sAttrDef.oReversedPath = std::move(oReversedPath); |
249 | 14.1k | if (pszATVL) |
250 | 14.1k | sAttrDef.osVal = pszATVL; |
251 | 14.1k | asS101AttrDefs.push_back(std::move(sAttrDef)); |
252 | | |
253 | 14.1k | nLastNATC = nNATC; |
254 | 14.1k | nLastATIX = nATIX; |
255 | 14.1k | nLastPAIX = nPAIX; |
256 | 14.1k | } |
257 | | |
258 | 5.58k | return true; |
259 | 6.00k | } |
260 | | |
261 | | /************************************************************************/ |
262 | | /* IngestAttributes() */ |
263 | | /************************************************************************/ |
264 | | |
265 | | /** For a given record that has a ATTR/INAS/FACS field, ingest all attributes |
266 | | * from all instances of this field. |
267 | | */ |
268 | | bool OGRS101Reader::IngestAttributes( |
269 | | const DDFRecord *poRecord, int iRecord, const char *pszIDFieldName, |
270 | | const char *pszAttrFieldName, |
271 | | std::vector<S101AttrDef> &asS101AttrDefs) const |
272 | 22.3k | { |
273 | 22.3k | asS101AttrDefs.clear(); |
274 | | |
275 | 22.3k | const auto apoATTRFields = poRecord->GetFields(pszAttrFieldName); |
276 | 22.3k | const int nATTRFieldCount = static_cast<int>(apoATTRFields.size()); |
277 | 22.3k | bool bSuccess = true; |
278 | 22.3k | if (EQUAL(pszAttrFieldName, ATTR_FIELD)) |
279 | 6.61k | { |
280 | 10.7k | for (int iATTRField = 0; bSuccess && iATTRField < nATTRFieldCount; |
281 | 6.61k | ++iATTRField) |
282 | 4.08k | { |
283 | 4.08k | bSuccess = IngestAttributes(poRecord, iRecord, pszIDFieldName, |
284 | 4.08k | pszAttrFieldName, |
285 | 4.08k | apoATTRFields[iATTRField], iATTRField, |
286 | 4.08k | nATTRFieldCount > 1, asS101AttrDefs); |
287 | 4.08k | } |
288 | 6.61k | } |
289 | 15.7k | else |
290 | 15.7k | { |
291 | 17.6k | for (int iATTRField = 0; bSuccess && iATTRField < nATTRFieldCount; |
292 | 15.7k | ++iATTRField) |
293 | 1.91k | { |
294 | 1.91k | const auto poINASOrFASCField = apoATTRFields[iATTRField]; |
295 | 1.91k | if (poINASOrFASCField->GetParts().size() != 2) |
296 | 0 | { |
297 | 0 | if (!EMIT_ERROR_OR_WARNING( |
298 | 0 | CPLSPrintf("Record index=%d of %s: missing components " |
299 | 0 | "in %s field.", |
300 | 0 | iRecord, pszIDFieldName, pszAttrFieldName))) |
301 | 0 | { |
302 | 0 | return false; |
303 | 0 | } |
304 | 0 | return true; |
305 | 0 | } |
306 | 1.91k | const auto poATTRField = poINASOrFASCField->GetParts()[1].get(); |
307 | | |
308 | 1.91k | bSuccess = IngestAttributes( |
309 | 1.91k | poRecord, iRecord, pszIDFieldName, pszAttrFieldName, |
310 | 1.91k | poATTRField, iATTRField, nATTRFieldCount > 1, asS101AttrDefs); |
311 | 1.91k | } |
312 | 15.7k | } |
313 | | |
314 | 22.3k | for (auto &sAttrDef : asS101AttrDefs) |
315 | 14.1k | { |
316 | 14.1k | if (sAttrDef.bIsParent || sAttrDef.oReversedPath.empty()) |
317 | 5.43k | continue; |
318 | | |
319 | | // For last component, set the repetition part to 0, to be |
320 | | // actually able to detect multi-valued attributes! |
321 | 8.73k | sAttrDef.oReversedPath.front().second = 0; |
322 | 8.73k | } |
323 | | |
324 | 22.3k | return bSuccess; |
325 | 22.3k | } |
326 | | |
327 | | /************************************************************************/ |
328 | | /* BuildFieldName() */ |
329 | | /************************************************************************/ |
330 | | |
331 | | /** Returns a string with the concatenation of the parts, in reverse order. |
332 | | */ |
333 | | std::string OGRS101Reader::BuildFieldName(const PathVector &oReversedPath, |
334 | | const char *pszAttrFieldName, |
335 | | int iField, bool bMultipleFields, |
336 | | const char *pszIDFieldName) const |
337 | 7.53k | { |
338 | 7.53k | std::string osAttrName; |
339 | 23.3k | for (size_t i = oReversedPath.size(); i > 0;) |
340 | 15.8k | { |
341 | 15.8k | --i; |
342 | 15.8k | const auto &oPathComp = oReversedPath[i]; |
343 | 15.8k | if (!osAttrName.empty()) |
344 | 7.99k | osAttrName += '.'; |
345 | 15.8k | const auto nCode = oPathComp.first; |
346 | 15.8k | const auto oIterNATC = m_attributeCodes.find(nCode); |
347 | 15.8k | if (oIterNATC == m_attributeCodes.end()) |
348 | 0 | { |
349 | 0 | osAttrName += CPLSPrintf("code_%d", static_cast<int>(nCode)); |
350 | 0 | } |
351 | 15.8k | else |
352 | 15.8k | { |
353 | 15.8k | osAttrName += oIterNATC->second; |
354 | 15.8k | } |
355 | | |
356 | 15.8k | if (bMultipleFields && strcmp(pszAttrFieldName, ATTR_FIELD) == 0) |
357 | 446 | { |
358 | 446 | osAttrName += '['; |
359 | 446 | osAttrName += std::to_string(iField + 1); |
360 | 446 | osAttrName += ']'; |
361 | 446 | bMultipleFields = false; |
362 | 446 | } |
363 | | |
364 | 15.8k | const auto &nRepeat = oPathComp.second; |
365 | 15.8k | if (nRepeat > 0) |
366 | 1.03k | { |
367 | 1.03k | osAttrName += '['; |
368 | 1.03k | osAttrName += std::to_string(static_cast<int>(nRepeat)); |
369 | 1.03k | osAttrName += ']'; |
370 | 1.03k | } |
371 | 15.8k | } |
372 | | |
373 | 7.53k | if (strcmp(pszAttrFieldName, ATTR_FIELD) != 0) |
374 | 920 | { |
375 | 920 | std::string osPrefix; |
376 | 920 | if (strcmp(pszIDFieldName, IRID_FIELD) == 0) |
377 | 336 | { |
378 | 336 | osPrefix = "association"; |
379 | 336 | } |
380 | 584 | else if (strcmp(pszIDFieldName, FRID_FIELD) == 0) |
381 | 556 | { |
382 | 556 | if (strcmp(pszAttrFieldName, INAS_FIELD) == 0) |
383 | 306 | osPrefix = "infoAssociation"; |
384 | 250 | else |
385 | 250 | osPrefix = "featureAssociation"; |
386 | 556 | } |
387 | 920 | if (!osPrefix.empty()) |
388 | 892 | { |
389 | 892 | if (bMultipleFields) |
390 | 404 | { |
391 | 404 | osPrefix += '['; |
392 | 404 | osPrefix += std::to_string(iField + 1); |
393 | 404 | osPrefix += ']'; |
394 | 404 | } |
395 | 892 | osPrefix += '_'; |
396 | 892 | } |
397 | 920 | osAttrName = osPrefix + osAttrName; |
398 | 920 | } |
399 | | |
400 | 7.53k | return osAttrName; |
401 | 7.53k | } |
402 | | |
403 | | /************************************************************************/ |
404 | | /* InferFeatureDefn() */ |
405 | | /************************************************************************/ |
406 | | |
407 | | /** Infer the feature definition from the content of INAS or ATTR records |
408 | | * of the index. |
409 | | */ |
410 | | bool OGRS101Reader::InferFeatureDefn( |
411 | | const DDFRecordIndex &oIndex, const char *pszIDFieldName, |
412 | | const char *pszAttrFieldName, const std::vector<int> &anRecordIndices, |
413 | | OGRFeatureDefn &oFeatureDefn, |
414 | | std::map<std::string, std::unique_ptr<OGRFieldDomain>> &oMapFieldDomains, |
415 | | const OGRS101FeatureCatalogTypes::InformationType * /*psInformationType*/, |
416 | | const OGRS101FeatureCatalogTypes::FeatureType *psFeatureType) const |
417 | 12.9k | { |
418 | 12.9k | const bool bIsINAS = EQUAL(pszAttrFieldName, INAS_FIELD); |
419 | | |
420 | 12.9k | struct OGRAttrDef |
421 | 12.9k | { |
422 | 12.9k | std::optional<OGRFieldType> oeType{}; |
423 | 12.9k | OGRFieldSubType eSubType = OFSTNone; |
424 | 12.9k | bool bIsMultiValued = false; |
425 | 12.9k | bool bMultipleFields = false; |
426 | 12.9k | std::string osLongerName{}; |
427 | 12.9k | std::string osDefinition{}; |
428 | 12.9k | std::string osFieldDomainName{}; |
429 | 12.9k | }; |
430 | | |
431 | 12.9k | struct Key |
432 | 12.9k | { |
433 | 12.9k | PathVector path{}; |
434 | 12.9k | int iField = 0; |
435 | 12.9k | bool bMultipleFields = false; |
436 | | |
437 | 12.9k | Key(const PathVector &pathIn, int iFieldIn, bool bMultipleFieldsIn) |
438 | 12.9k | : path(pathIn), iField(iFieldIn), bMultipleFields(bMultipleFieldsIn) |
439 | 12.9k | { |
440 | 5.09k | } |
441 | | |
442 | 12.9k | bool operator<(const Key &other) const |
443 | 35.6k | { |
444 | 35.6k | return path < other.path || |
445 | 22.5k | (path == other.path && |
446 | 10.9k | (iField < other.iField || |
447 | 10.4k | (iField == other.iField && !bMultipleFields && |
448 | 9.06k | other.bMultipleFields))); |
449 | 35.6k | } |
450 | 12.9k | }; |
451 | | |
452 | 12.9k | std::map<Key, OGRAttrDef> oMapFieldTypes; |
453 | | |
454 | 12.9k | std::vector<S101AttrDef> asS101AttrDefs; |
455 | 12.9k | std::map<Key, int> mapPathToCount; |
456 | 12.9k | bool bFoundValidAssocField = false; |
457 | | |
458 | | // Iterate over the records (in the index of interest) to fill the |
459 | | // oMapFieldTypes map object that will be afterwards translated as |
460 | | // OGR feature definition |
461 | | // If anRecordIndices is not empty, it defines the subset of record |
462 | | // indices to iterate over. This is used for geometry records that are |
463 | | // dispatched to different OGR layers depending on the CRS. |
464 | 12.9k | const int nRecords = anRecordIndices.empty() |
465 | 12.9k | ? oIndex.GetCount() |
466 | 12.9k | : static_cast<int>(anRecordIndices.size()); |
467 | 12.9k | int nMaxFieldRepeat = 1; |
468 | 29.2k | for (int iter = 0; iter < nRecords; ++iter) |
469 | 17.1k | { |
470 | 17.1k | const int iRecord = |
471 | 17.1k | anRecordIndices.empty() ? iter : anRecordIndices[iter]; |
472 | | |
473 | 17.1k | const auto GetErrorContext = [pszIDFieldName, iRecord]() |
474 | 17.1k | { |
475 | 512 | return CPLSPrintf("Record index=%d of %s", iRecord, pszIDFieldName); |
476 | 512 | }; |
477 | | |
478 | 17.1k | const auto poRecord = oIndex.GetByIndex(iRecord); |
479 | 17.1k | CPLAssert(poRecord); |
480 | | |
481 | 17.1k | const int nRUIN = |
482 | 17.1k | poRecord->GetIntSubfield(pszIDFieldName, 0, RUIN_SUBFIELD, 0); |
483 | 17.1k | if (nRUIN != INSTRUCTION_INSERT) |
484 | 343 | { |
485 | 343 | if (!EMIT_ERROR_OR_WARNING(CPLSPrintf("%s: wrong value %d for RUIN " |
486 | 343 | "subfield of %s field.", |
487 | 343 | GetErrorContext(), nRUIN, |
488 | 343 | pszIDFieldName))) |
489 | 343 | { |
490 | 343 | return false; |
491 | 343 | } |
492 | 0 | continue; |
493 | 343 | } |
494 | | |
495 | 16.8k | if (!EQUAL(pszAttrFieldName, ATTR_FIELD)) |
496 | 12.0k | { |
497 | 12.0k | const auto apoFields = poRecord->GetFields(pszAttrFieldName); |
498 | 12.0k | bool bSkipRecord = false; |
499 | 12.0k | nMaxFieldRepeat = |
500 | 12.0k | std::max(nMaxFieldRepeat, static_cast<int>(apoFields.size())); |
501 | 12.0k | if (!apoFields.empty()) |
502 | 1.32k | { |
503 | 1.32k | const DDFField *poField = apoFields[0]; |
504 | | |
505 | 1.32k | const RecordName nRRNM = |
506 | 1.32k | poRecord->GetIntSubfield(poField, RRNM_SUBFIELD, 0); |
507 | 1.32k | const RecordName nExpectedRRNM = |
508 | 1.32k | bIsINAS ? RECORD_NAME_INFORMATION_TYPE |
509 | 1.32k | : RECORD_NAME_FEATURE_TYPE; |
510 | 1.32k | if (nRRNM != nExpectedRRNM) |
511 | 30 | { |
512 | 30 | if (!EMIT_ERROR_OR_WARNING(CPLSPrintf( |
513 | 30 | "%s: Invalid value for RRNM subfield of %s field: " |
514 | 30 | "got %d, expected %d.", |
515 | 30 | GetErrorContext(), pszAttrFieldName, |
516 | 30 | static_cast<int>(nRRNM), |
517 | 30 | static_cast<int>(nExpectedRRNM)))) |
518 | 30 | { |
519 | 30 | return false; |
520 | 30 | } |
521 | 30 | } |
522 | | |
523 | 1.29k | const int nRRID = |
524 | 1.29k | poRecord->GetIntSubfield(poField, RRID_SUBFIELD, 0); |
525 | 1.29k | if ((bIsINAS && |
526 | 1.10k | !m_oInformationTypeRecordIndex.FindRecord(nRRID)) || |
527 | 1.29k | (!bIsINAS && !m_oFeatureTypeRecordIndex.FindRecord(nRRID))) |
528 | 11 | { |
529 | 11 | if (!EMIT_ERROR_OR_WARNING(CPLSPrintf( |
530 | 11 | "%s: Invalid value %d for RRID subfield of %s " |
531 | 11 | "field: " |
532 | 11 | "does not match the record identifier of an " |
533 | 11 | "existing " |
534 | 11 | "%s record.", |
535 | 11 | GetErrorContext(), static_cast<int>(nRRID), |
536 | 11 | pszAttrFieldName, |
537 | 11 | bIsINAS ? "InformationType" : "FeatureType"))) |
538 | 11 | { |
539 | 11 | return false; |
540 | 11 | } |
541 | 11 | } |
542 | | |
543 | 1.28k | if (bIsINAS) |
544 | 1.09k | { |
545 | 1.09k | const InfoAssocCode nNIAC = |
546 | 1.09k | poRecord->GetIntSubfield(poField, NIAC_SUBFIELD, 0); |
547 | 1.09k | if (!cpl::contains(m_informationAssociationCodes, nNIAC) && |
548 | 42 | !EMIT_ERROR_OR_WARNING(CPLSPrintf( |
549 | 1.09k | "%s: cannot find attribute code %d in IACS field " |
550 | 1.09k | "of the Dataset General Information Record.", |
551 | 1.09k | GetErrorContext(), static_cast<int>(nNIAC)))) |
552 | 42 | { |
553 | 42 | return false; |
554 | 42 | } |
555 | 1.09k | } |
556 | 189 | else |
557 | 189 | { |
558 | 189 | const FeatureAssocCode nNFAC = |
559 | 189 | poRecord->GetIntSubfield(poField, NFAC_SUBFIELD, 0); |
560 | 189 | if (!cpl::contains(m_featureAssociationCodes, nNFAC) && |
561 | 15 | !EMIT_ERROR_OR_WARNING(CPLSPrintf( |
562 | 189 | "%s: cannot find attribute code %d in NFAC field " |
563 | 189 | "of the Dataset General Information Record.", |
564 | 189 | GetErrorContext(), static_cast<int>(nNFAC)))) |
565 | 15 | { |
566 | 15 | return false; |
567 | 15 | } |
568 | 189 | } |
569 | | |
570 | 1.22k | const AssocRoleCode nNARC = |
571 | 1.22k | poRecord->GetIntSubfield(poField, NARC_SUBFIELD, 0); |
572 | 1.22k | if (!cpl::contains(m_associationRoleCodes, nNARC) && |
573 | 37 | !EMIT_ERROR_OR_WARNING(CPLSPrintf( |
574 | 1.22k | "%s: cannot find attribute code %d in ARCS field " |
575 | 1.22k | "of the Dataset General Information Record.", |
576 | 1.22k | GetErrorContext(), static_cast<int>(nNARC)))) |
577 | 37 | { |
578 | 37 | return false; |
579 | 37 | } |
580 | | |
581 | 1.19k | const char *pszSubFieldName = |
582 | 1.19k | bIsINAS ? IUIN_SUBFIELD : FAUI_SUBFIELD; |
583 | 1.19k | int nInstruction = |
584 | 1.19k | poRecord->GetIntSubfield(poField, pszSubFieldName, 0); |
585 | 1.19k | if (nInstruction == 0) |
586 | 1 | { |
587 | | // For 101GB00GB302045.000, non conformant |
588 | 1 | nInstruction = poRecord->GetIntSubfield(poField, "APUI", 0); |
589 | 1 | } |
590 | 1.19k | if (nInstruction != INSTRUCTION_INSERT) |
591 | 3 | { |
592 | 3 | if (!EMIT_ERROR_OR_WARNING( |
593 | 3 | CPLSPrintf("%s: wrong value %d for %s " |
594 | 3 | "subfield of %s field.", |
595 | 3 | GetErrorContext(), nInstruction, |
596 | 3 | pszSubFieldName, pszAttrFieldName))) |
597 | 3 | { |
598 | 3 | return false; |
599 | 3 | } |
600 | 0 | bSkipRecord = true; |
601 | 0 | } |
602 | 1.18k | else |
603 | 1.18k | { |
604 | 1.18k | bFoundValidAssocField = true; |
605 | 1.18k | } |
606 | 1.19k | } |
607 | 11.8k | if (bSkipRecord) |
608 | 0 | continue; |
609 | 11.8k | } |
610 | | |
611 | | // First (inner) pass over attributes of the current record |
612 | | // to fill asS101AttrDefs, and do all needed sanity checks |
613 | 16.7k | if (!IngestAttributes(poRecord, iRecord, pszIDFieldName, |
614 | 16.7k | pszAttrFieldName, asS101AttrDefs)) |
615 | 413 | return false; |
616 | | |
617 | 16.3k | mapPathToCount.clear(); |
618 | | |
619 | | // Update oMapFieldTypes with attributes found in this record |
620 | 16.3k | for (const auto &sAttrDef : asS101AttrDefs) |
621 | 8.09k | { |
622 | 8.09k | if (sAttrDef.bIsParent || sAttrDef.oReversedPath.empty()) |
623 | 3.00k | continue; |
624 | | |
625 | | // Check that the top-level part of the attribute is expected for |
626 | | // that feature type (using feature catalog) |
627 | 5.09k | if (psFeatureType) |
628 | 0 | { |
629 | 0 | const auto oIterNATC = |
630 | 0 | m_attributeCodes.find(sAttrDef.oReversedPath.back().first); |
631 | 0 | if (oIterNATC != m_attributeCodes.end()) |
632 | 0 | { |
633 | 0 | const std::string &osAttrCode = oIterNATC->second; |
634 | 0 | if (!cpl::contains(psFeatureType->attributeBindings, |
635 | 0 | osAttrCode)) |
636 | 0 | { |
637 | 0 | if (!EMIT_ERROR_OR_WARNING(CPLSPrintf( |
638 | 0 | "%s: attribute code %s not expected in feature " |
639 | 0 | "type %s", |
640 | 0 | GetErrorContext(), osAttrCode.c_str(), |
641 | 0 | psFeatureType->code.c_str()))) |
642 | 0 | { |
643 | 0 | return false; |
644 | 0 | } |
645 | 0 | } |
646 | 0 | } |
647 | 0 | } |
648 | | |
649 | 5.09k | const auto key = Key(sAttrDef.oReversedPath, sAttrDef.iField, |
650 | 5.09k | sAttrDef.bMultipleFields); |
651 | 5.09k | ++mapPathToCount[key]; |
652 | | |
653 | | // Must be kept in that scope to create a OGR attribute even if |
654 | | // there is no field value |
655 | 5.09k | auto &sOGRAttrDef = oMapFieldTypes[key]; |
656 | | |
657 | 5.09k | std::string typeFromCatalog; |
658 | 5.09k | if (m_poFeatureCatalog) |
659 | 5.09k | { |
660 | 5.09k | auto oIterNATC = |
661 | 5.09k | m_attributeCodes.find(sAttrDef.oReversedPath.front().first); |
662 | 5.09k | if (oIterNATC != m_attributeCodes.end()) |
663 | 5.09k | { |
664 | 5.09k | const auto &oMap = |
665 | 5.09k | m_poFeatureCatalog->GetSimpleAttributes(); |
666 | 5.09k | const std::string &osAttrCode = oIterNATC->second; |
667 | 5.09k | const auto oIterAttr = oMap.find(osAttrCode); |
668 | 5.09k | if (oIterAttr != oMap.end()) |
669 | 2.65k | { |
670 | 2.65k | const auto &attrDef = oIterAttr->second; |
671 | 2.65k | typeFromCatalog = attrDef.type; |
672 | | |
673 | 2.65k | sOGRAttrDef.osLongerName = attrDef.name; |
674 | 2.65k | sOGRAttrDef.osDefinition = attrDef.definition; |
675 | | |
676 | 2.65k | if (typeFromCatalog == |
677 | 2.65k | OGRS101FeatureCatalog::VALUE_TYPE_ENUMERATION) |
678 | 165 | { |
679 | 165 | sOGRAttrDef.osFieldDomainName = osAttrCode; |
680 | | |
681 | 165 | if (!sAttrDef.osVal.empty()) |
682 | 151 | { |
683 | | // Checks that the coded value is an allowed code. |
684 | 151 | const int nCode = atoi(sAttrDef.osVal.c_str()); |
685 | 151 | if (!cpl::contains(attrDef.enumeratedValues, |
686 | 151 | nCode)) |
687 | 31 | { |
688 | 31 | if (!EMIT_ERROR_OR_WARNING(CPLSPrintf( |
689 | 31 | "%s: value %s does not belong to " |
690 | 31 | "enumeration of attribute code %s", |
691 | 31 | GetErrorContext(), |
692 | 31 | sAttrDef.osVal.c_str(), |
693 | 31 | osAttrCode.c_str()))) |
694 | 31 | { |
695 | 31 | return false; |
696 | 31 | } |
697 | 31 | } |
698 | 151 | } |
699 | | |
700 | | // Check if field domain exists. If not, create it. |
701 | 134 | if (!cpl::contains(oMapFieldDomains, osAttrCode)) |
702 | 83 | { |
703 | 83 | std::vector<OGRCodedValue> asValues; |
704 | 83 | for (const auto &[code, value] : |
705 | 83 | attrDef.enumeratedValues) |
706 | 1.07k | { |
707 | 1.07k | OGRCodedValue codedValue; |
708 | 1.07k | codedValue.pszCode = |
709 | 1.07k | CPLStrdup(CPLSPrintf("%d", code)); |
710 | 1.07k | codedValue.pszValue = |
711 | 1.07k | CPLStrdup(value.c_str()); |
712 | 1.07k | asValues.push_back(std::move(codedValue)); |
713 | 1.07k | } |
714 | 83 | auto poFieldDomain = |
715 | 83 | std::make_unique<OGRCodedFieldDomain>( |
716 | 83 | osAttrCode, attrDef.name, OFTString, |
717 | 83 | OFSTNone, std::move(asValues)); |
718 | 83 | oMapFieldDomains[osAttrCode] = |
719 | 83 | std::move(poFieldDomain); |
720 | 83 | } |
721 | 134 | } |
722 | 2.65k | } |
723 | 5.09k | } |
724 | | |
725 | 10.6k | for (size_t i = 1; i < sAttrDef.oReversedPath.size(); ++i) |
726 | 5.55k | { |
727 | 5.55k | oIterNATC = |
728 | 5.55k | m_attributeCodes.find(sAttrDef.oReversedPath[i].first); |
729 | 5.55k | if (oIterNATC != m_attributeCodes.end()) |
730 | 5.55k | { |
731 | 5.55k | const auto &oMap = |
732 | 5.55k | m_poFeatureCatalog->GetComplexAttributes(); |
733 | 5.55k | const std::string &osAttrCode = oIterNATC->second; |
734 | 5.55k | const auto oIterAttr = oMap.find(osAttrCode); |
735 | 5.55k | if (oIterAttr != oMap.end()) |
736 | 2.21k | { |
737 | 2.21k | const auto &attrDef = oIterAttr->second; |
738 | 2.21k | sOGRAttrDef.osDefinition += ' '; |
739 | 2.21k | sOGRAttrDef.osDefinition += oIterNATC->second; |
740 | 2.21k | sOGRAttrDef.osDefinition += '='; |
741 | 2.21k | sOGRAttrDef.osDefinition += attrDef.definition; |
742 | 2.21k | } |
743 | 5.55k | } |
744 | 5.55k | } |
745 | 5.06k | } |
746 | | |
747 | 5.06k | sOGRAttrDef.bMultipleFields = sAttrDef.bMultipleFields; |
748 | | |
749 | 5.06k | if (!sAttrDef.osVal.empty()) |
750 | 4.74k | { |
751 | 4.74k | const bool bNewAttrIsMultiValued = mapPathToCount[key] > 1; |
752 | 4.74k | if (bNewAttrIsMultiValued) |
753 | 117 | sOGRAttrDef.bIsMultiValued = true; |
754 | 4.74k | const auto eCPLType = CPLGetValueType(sAttrDef.osVal.c_str()); |
755 | 4.74k | auto eOGRType = eCPLType == CPL_VALUE_STRING ? OFTString |
756 | 4.74k | : eCPLType == CPL_VALUE_INTEGER ? OFTInteger |
757 | 1.55k | : OFTReal; |
758 | | |
759 | | // Is it YYYYMMDD date ? |
760 | 4.74k | if (eOGRType == OFTInteger && sAttrDef.osVal.size() == 8 && |
761 | 60 | sAttrDef.osVal[4] <= '1' && sAttrDef.osVal[6] <= '3') |
762 | 38 | { |
763 | 38 | const auto it = m_attributeCodes.find( |
764 | 38 | sAttrDef.oReversedPath.front().first); |
765 | 38 | if (it != m_attributeCodes.end()) |
766 | 38 | { |
767 | 38 | if (cpl::ends_with(it->second, "Date") || |
768 | 38 | cpl::ends_with(it->second, "dateStart") || |
769 | 36 | cpl::ends_with(it->second, "dateEnd")) |
770 | 10 | { |
771 | 10 | eOGRType = OFTDate; |
772 | 10 | } |
773 | 38 | } |
774 | 38 | } |
775 | | // Is it YYYY---- truncated date ? |
776 | 4.70k | else if (eOGRType == OFTString && sAttrDef.osVal.size() == 8 && |
777 | 246 | sAttrDef.osVal[4] == '-' && sAttrDef.osVal[5] == '-' && |
778 | 123 | sAttrDef.osVal[6] == '-' && sAttrDef.osVal[7] == '-') |
779 | 85 | { |
780 | 85 | const auto it = m_attributeCodes.find( |
781 | 85 | sAttrDef.oReversedPath.front().first); |
782 | 85 | if (it != m_attributeCodes.end()) |
783 | 85 | { |
784 | 85 | if (cpl::ends_with(it->second, "Date") || |
785 | 84 | cpl::ends_with(it->second, "dateStart") || |
786 | 73 | cpl::ends_with(it->second, "dateEnd")) |
787 | 15 | { |
788 | 15 | eOGRType = OFTDate; |
789 | 15 | } |
790 | 85 | } |
791 | 85 | } |
792 | | // Is it time format ? ("094500", "094500", "094500+0100") |
793 | 4.61k | else if ((eOGRType == OFTInteger || eOGRType == OFTString) && |
794 | 3.54k | sAttrDef.osVal.size() >= 6 && |
795 | 2.40k | sAttrDef.osVal.size() <= 11 && |
796 | 1.51k | std::all_of(sAttrDef.osVal.begin(), |
797 | 1.51k | sAttrDef.osVal.begin() + 6, [](char c) |
798 | 2.45k | { return c >= '0' && c <= '9'; }) && |
799 | 129 | (sAttrDef.osVal.size() == 6 || |
800 | 87 | (sAttrDef.osVal.size() == 7 && |
801 | 15 | sAttrDef.osVal[6] == 'Z') || |
802 | 87 | (sAttrDef.osVal.size() == 11 && |
803 | 10 | (sAttrDef.osVal[6] == '+' || |
804 | 8 | sAttrDef.osVal[6] == '-')))) |
805 | 45 | { |
806 | 45 | const auto it = m_attributeCodes.find( |
807 | 45 | sAttrDef.oReversedPath.front().first); |
808 | 45 | if (it != m_attributeCodes.end()) |
809 | 45 | { |
810 | 45 | if (cpl::starts_with(it->second, "time")) |
811 | 10 | { |
812 | 10 | eOGRType = OFTTime; |
813 | 10 | } |
814 | 45 | } |
815 | 45 | } |
816 | | |
817 | 4.74k | if (!sOGRAttrDef.oeType.has_value()) |
818 | 4.59k | { |
819 | 4.59k | sOGRAttrDef.oeType = eOGRType; |
820 | 4.59k | if (eOGRType == OFTInteger && |
821 | 389 | typeFromCatalog == |
822 | 389 | OGRS101FeatureCatalog::VALUE_TYPE_BOOLEAN) |
823 | 6 | { |
824 | 6 | sOGRAttrDef.eSubType = OFSTBoolean; |
825 | 6 | } |
826 | 4.59k | } |
827 | 146 | else if (eOGRType == OFTString && |
828 | 75 | *sOGRAttrDef.oeType != OFTString) |
829 | 20 | { |
830 | 20 | sOGRAttrDef.oeType = OFTString; |
831 | 20 | sOGRAttrDef.eSubType = OFSTNone; |
832 | 20 | } |
833 | 126 | else if (eOGRType == OFTReal && |
834 | 8 | *sOGRAttrDef.oeType == OFTInteger) |
835 | 3 | { |
836 | 3 | sOGRAttrDef.oeType = OFTReal; |
837 | 3 | sOGRAttrDef.eSubType = OFSTNone; |
838 | 3 | } |
839 | 4.74k | } |
840 | 5.06k | } |
841 | 16.3k | } |
842 | | |
843 | 12.0k | if (bFoundValidAssocField) |
844 | 1.07k | { |
845 | 2.32k | for (int i = 0; i < nMaxFieldRepeat; ++i) |
846 | 1.24k | { |
847 | 1.24k | const std::string osSuffix = |
848 | 1.24k | nMaxFieldRepeat > 1 ? CPLSPrintf("[%d]", i + 1) : ""; |
849 | | |
850 | 1.24k | if (!bIsINAS) |
851 | 196 | { |
852 | 196 | OGRFieldDefn oFieldDefn( |
853 | 196 | (OGR_FIELD_NAME_REF_FEAT_LAYER_NAME + osSuffix).c_str(), |
854 | 196 | OFTString); |
855 | 196 | oFeatureDefn.AddFieldDefn(&oFieldDefn); |
856 | 196 | } |
857 | | |
858 | 1.24k | { |
859 | 1.24k | OGRFieldDefn oFieldDefn( |
860 | 1.24k | ((bIsINAS ? OGR_FIELD_NAME_REF_INFO_RID |
861 | 1.24k | : OGR_FIELD_NAME_REF_FEAT_RID) + |
862 | 1.24k | osSuffix) |
863 | 1.24k | .c_str(), |
864 | 1.24k | OFTInteger); |
865 | 1.24k | oFeatureDefn.AddFieldDefn(&oFieldDefn); |
866 | 1.24k | } |
867 | 1.24k | { |
868 | 1.24k | OGRFieldDefn oFieldDefn( |
869 | 1.24k | ((bIsINAS ? OGR_FIELD_NAME_NIAC : OGR_FIELD_NAME_NFAC) + |
870 | 1.24k | osSuffix) |
871 | 1.24k | .c_str(), |
872 | 1.24k | OFTString); |
873 | 1.24k | oFeatureDefn.AddFieldDefn(&oFieldDefn); |
874 | 1.24k | } |
875 | 1.24k | { |
876 | 1.24k | OGRFieldDefn oFieldDefn( |
877 | 1.24k | ((bIsINAS ? OGR_FIELD_NAME_NARC |
878 | 1.24k | : OGR_FIELD_NAME_FEATURE_NARC) + |
879 | 1.24k | osSuffix) |
880 | 1.24k | .c_str(), |
881 | 1.24k | OFTString); |
882 | 1.24k | oFeatureDefn.AddFieldDefn(&oFieldDefn); |
883 | 1.24k | } |
884 | 1.24k | } |
885 | 1.07k | } |
886 | | |
887 | | // Final pass to transform oMapFieldTypes into OGRField instances. |
888 | 12.0k | for (const auto &[key, sOGRAttrDef] : oMapFieldTypes) |
889 | 4.69k | { |
890 | 4.69k | const auto &oReversedPath = key.path; |
891 | 4.69k | const int iField = key.iField; |
892 | | |
893 | 4.69k | const std::string osAttrName = |
894 | 4.69k | BuildFieldName(oReversedPath, pszAttrFieldName, iField, |
895 | 4.69k | sOGRAttrDef.bMultipleFields, pszIDFieldName); |
896 | | |
897 | 4.69k | OGRFieldType eType = OFTString; |
898 | 4.69k | if (sOGRAttrDef.oeType.has_value()) |
899 | 4.38k | { |
900 | 4.38k | if (sOGRAttrDef.bIsMultiValued) |
901 | 116 | { |
902 | 116 | eType = (*sOGRAttrDef.oeType) == OFTInteger ? OFTIntegerList |
903 | 116 | : (*sOGRAttrDef.oeType) == OFTReal ? OFTRealList |
904 | 61 | : OFTStringList; |
905 | 116 | } |
906 | 4.26k | else |
907 | 4.26k | { |
908 | 4.26k | eType = *sOGRAttrDef.oeType; |
909 | 4.26k | } |
910 | 4.38k | } |
911 | 306 | else if (sOGRAttrDef.bIsMultiValued) |
912 | 0 | { |
913 | 0 | eType = OFTStringList; |
914 | 0 | } |
915 | | |
916 | 4.69k | if (oFeatureDefn.GetFieldIndex(osAttrName.c_str()) >= 0) |
917 | 87 | { |
918 | 87 | if (osAttrName == OGR_FIELD_NAME_SMIN || |
919 | 87 | osAttrName == OGR_FIELD_NAME_SMAX) |
920 | 0 | { |
921 | | // 101FR00368570.000 has scaleMinimum as an ATTR, and |
922 | | // doesn't define SPAS.SMIN |
923 | 0 | } |
924 | 87 | else |
925 | 87 | { |
926 | 87 | CPLError(CE_Warning, CPLE_AppDefined, |
927 | 87 | "Layer %s: %s field already exists", |
928 | 87 | oFeatureDefn.GetName(), osAttrName.c_str()); |
929 | 87 | } |
930 | 87 | } |
931 | 4.60k | else |
932 | 4.60k | { |
933 | 4.60k | OGRFieldDefn oFieldDefn(osAttrName.c_str(), eType); |
934 | 4.60k | oFieldDefn.SetSubType(sOGRAttrDef.eSubType); |
935 | 4.60k | if (!sOGRAttrDef.osLongerName.empty() && oReversedPath.size() == 1) |
936 | 1.22k | oFieldDefn.SetAlternativeName(sOGRAttrDef.osLongerName.c_str()); |
937 | 4.60k | if (!sOGRAttrDef.osDefinition.empty()) |
938 | 2.98k | oFieldDefn.SetComment(sOGRAttrDef.osDefinition.c_str()); |
939 | 4.60k | if (!sOGRAttrDef.osFieldDomainName.empty()) |
940 | 95 | oFieldDefn.SetDomainName(sOGRAttrDef.osFieldDomainName.c_str()); |
941 | 4.60k | oFeatureDefn.AddFieldDefn(&oFieldDefn); |
942 | 4.60k | } |
943 | 4.69k | } |
944 | | |
945 | 12.0k | return true; |
946 | 12.9k | } |
947 | | |
948 | | /************************************************************************/ |
949 | | /* FillFeatureAttributes() */ |
950 | | /************************************************************************/ |
951 | | |
952 | | /** Fill attribute fields of the provided feature. |
953 | | */ |
954 | | bool OGRS101Reader::FillFeatureAttributes(const DDFRecordIndex &oIndex, |
955 | | int iRecord, |
956 | | const char *pszAttrFieldName, |
957 | | OGRFeature &oFeature) const |
958 | 5.67k | { |
959 | 5.67k | const auto poRecord = oIndex.GetByIndex(iRecord); |
960 | 5.67k | if (!poRecord) |
961 | 0 | { |
962 | 0 | return EMIT_ERROR("Invalid record number"); |
963 | 0 | } |
964 | | |
965 | 5.67k | const auto poIDField = poRecord->GetField(0); |
966 | 5.67k | CPLAssert(poIDField); |
967 | 5.67k | const char *pszIDFieldName = poIDField->GetFieldDefn()->GetName(); |
968 | | |
969 | 5.67k | const int nRCID = |
970 | 5.67k | poRecord->GetIntSubfield(pszIDFieldName, 0, RCID_SUBFIELD, 0); |
971 | 5.67k | if (nRCID < 1 && !EMIT_ERROR_OR_WARNING(CPLSPrintf( |
972 | 5.67k | "Wrong value %d for RCID subfield of %s field.", nRCID, |
973 | 5.67k | pszIDFieldName))) |
974 | 0 | { |
975 | 0 | return false; |
976 | 0 | } |
977 | 5.67k | oFeature.SetField(OGR_FIELD_NAME_RECORD_ID, nRCID); |
978 | | |
979 | 5.67k | const int nRVER = |
980 | 5.67k | poRecord->GetIntSubfield(pszIDFieldName, 0, RVER_SUBFIELD, 0); |
981 | 5.67k | oFeature.SetField(OGR_FIELD_NAME_RECORD_VERSION, nRVER); |
982 | | |
983 | 5.67k | const int nRUIN = |
984 | 5.67k | poRecord->GetIntSubfield(pszIDFieldName, 0, RUIN_SUBFIELD, 0); |
985 | 5.67k | if (nRUIN != INSTRUCTION_INSERT) |
986 | 0 | { |
987 | 0 | return EMIT_ERROR_OR_WARNING( |
988 | 0 | CPLSPrintf("Wrong value %d for RUIN subfield of %s field.", nRUIN, |
989 | 0 | pszIDFieldName)); |
990 | 0 | } |
991 | | |
992 | 5.67k | const auto poFeatureDefn = oFeature.GetDefnRef(); |
993 | | |
994 | | // First pass to detect which attribute entries correspond to parent nodes |
995 | | // that don't directly hold a field value. |
996 | 5.67k | std::vector<S101AttrDef> asS101AttrDefs; |
997 | 5.67k | if (!IngestAttributes(poRecord, iRecord, pszIDFieldName, pszAttrFieldName, |
998 | 5.67k | asS101AttrDefs)) |
999 | 0 | return false; |
1000 | | |
1001 | 5.67k | struct OGRFieldIndexTag |
1002 | 5.67k | { |
1003 | 5.67k | }; |
1004 | | |
1005 | 5.67k | using OGRFieldIndex = cpl::IntWrapper<OGRFieldIndexTag>; |
1006 | | |
1007 | 5.67k | std::map<OGRFieldIndex, CPLStringList> stringListAttrs; |
1008 | 5.67k | std::map<OGRFieldIndex, std::vector<int>> intListAttrs; |
1009 | 5.67k | std::map<OGRFieldIndex, std::vector<double>> doubleListAttrs; |
1010 | | // Second pass to set single-valued attributes, or store multi-valued |
1011 | | // attributes in the 3 above maps. |
1012 | 5.67k | for (const auto &sAttrDef : asS101AttrDefs) |
1013 | 4.65k | { |
1014 | 4.65k | if (sAttrDef.bIsParent || sAttrDef.oReversedPath.empty()) |
1015 | 1.80k | continue; |
1016 | | |
1017 | 2.84k | const std::string osAttrName = BuildFieldName( |
1018 | 2.84k | sAttrDef.oReversedPath, pszAttrFieldName, sAttrDef.iField, |
1019 | 2.84k | sAttrDef.bMultipleFields, pszIDFieldName); |
1020 | | |
1021 | 2.84k | const int iOGRFieldIdx = |
1022 | 2.84k | poFeatureDefn->GetFieldIndex(osAttrName.c_str()); |
1023 | | // Shouldn't normally happen given all preceding run logic |
1024 | 2.84k | CPLAssert(iOGRFieldIdx >= 0); |
1025 | 2.84k | const auto eType = poFeatureDefn->GetFieldDefn(iOGRFieldIdx)->GetType(); |
1026 | 2.84k | const char *const pszATVL = sAttrDef.osVal.c_str(); |
1027 | 2.84k | switch (eType) |
1028 | 2.84k | { |
1029 | 208 | case OFTInteger: |
1030 | 255 | case OFTIntegerList: |
1031 | 255 | { |
1032 | 255 | if (pszATVL[0]) |
1033 | 252 | { |
1034 | 252 | const char *const last = pszATVL + sAttrDef.osVal.size(); |
1035 | 252 | int nVal = -1; |
1036 | 252 | auto [ptr, ec] = std::from_chars(pszATVL, last, nVal); |
1037 | 252 | if (ec == std::errc() && ptr == last) |
1038 | 187 | { |
1039 | 187 | if (eType == OFTInteger) |
1040 | 145 | oFeature.SetField(iOGRFieldIdx, nVal); |
1041 | 42 | else |
1042 | 42 | intListAttrs[iOGRFieldIdx].push_back(nVal); |
1043 | 187 | } |
1044 | 65 | else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf( |
1045 | 65 | "Record index=%d of %s, attribute %s: " |
1046 | 65 | "non integer value '%s'.", |
1047 | 65 | iRecord, pszIDFieldName, osAttrName.c_str(), |
1048 | 65 | pszATVL))) |
1049 | 65 | { |
1050 | 65 | return false; |
1051 | 65 | } |
1052 | 252 | } |
1053 | 3 | else if (eType == OFTIntegerList) |
1054 | 0 | { |
1055 | 0 | intListAttrs[iOGRFieldIdx].push_back( |
1056 | 0 | std::numeric_limits<int>::min()); |
1057 | 0 | } |
1058 | 190 | break; |
1059 | 255 | } |
1060 | | |
1061 | 640 | case OFTReal: |
1062 | 657 | case OFTRealList: |
1063 | 657 | { |
1064 | 657 | if (pszATVL[0]) |
1065 | 652 | { |
1066 | 652 | const char *const last = pszATVL + sAttrDef.osVal.size(); |
1067 | 652 | double dfVal = -1; |
1068 | 652 | const fast_float::parse_options options{ |
1069 | 652 | fast_float::chars_format::general, '.'}; |
1070 | 652 | auto [ptr, ec] = fast_float::from_chars_advanced( |
1071 | 652 | pszATVL, last, dfVal, options); |
1072 | 652 | if (ec == std::errc() && ptr == last) |
1073 | 489 | { |
1074 | 489 | if (eType == OFTReal) |
1075 | 480 | oFeature.SetField(iOGRFieldIdx, dfVal); |
1076 | 9 | else |
1077 | 9 | doubleListAttrs[iOGRFieldIdx].push_back(dfVal); |
1078 | 489 | } |
1079 | 163 | else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf( |
1080 | 163 | "Record index=%d of %s, attribute %s: " |
1081 | 163 | "non double value '%s'.", |
1082 | 163 | iRecord, pszIDFieldName, osAttrName.c_str(), |
1083 | 163 | pszATVL))) |
1084 | 163 | { |
1085 | 163 | return false; |
1086 | 163 | } |
1087 | 652 | } |
1088 | 5 | else if (eType == OFTRealList) |
1089 | 4 | { |
1090 | 4 | doubleListAttrs[iOGRFieldIdx].push_back( |
1091 | 4 | std::numeric_limits<double>::quiet_NaN()); |
1092 | 4 | } |
1093 | 494 | break; |
1094 | 657 | } |
1095 | | |
1096 | 1.83k | case OFTString: |
1097 | 1.90k | case OFTStringList: |
1098 | 1.90k | { |
1099 | 1.90k | std::unique_ptr<char, VSIFreeReleaser> pszTmpStr; |
1100 | 1.90k | const char *pszStr = pszATVL; |
1101 | 1.90k | if (!CPLIsUTF8(pszATVL, |
1102 | 1.90k | static_cast<int>(sAttrDef.osVal.size()))) |
1103 | 176 | { |
1104 | | // Not supposed to happen in compliant products |
1105 | 176 | if (!EMIT_ERROR_OR_WARNING(CPLSPrintf( |
1106 | 176 | "Record index=%d of %s, attribute %s: non " |
1107 | 176 | "UTF-8 string '%s'.", |
1108 | 176 | iRecord, pszIDFieldName, osAttrName.c_str(), |
1109 | 176 | pszATVL))) |
1110 | 176 | { |
1111 | 176 | return false; |
1112 | 176 | } |
1113 | 0 | pszTmpStr.reset(CPLUTF8ForceToASCII(pszATVL, '_')); |
1114 | 0 | pszStr = pszTmpStr.get(); |
1115 | 0 | } |
1116 | 1.72k | if (eType == OFTString) |
1117 | 1.66k | oFeature.SetField(iOGRFieldIdx, pszStr); |
1118 | 60 | else |
1119 | 60 | stringListAttrs[iOGRFieldIdx].push_back(pszStr); |
1120 | 1.72k | break; |
1121 | 1.90k | } |
1122 | | |
1123 | 21 | case OFTDate: |
1124 | 21 | { |
1125 | 21 | if (sAttrDef.osVal.size() == 8 && |
1126 | 21 | std::all_of(sAttrDef.osVal.begin(), sAttrDef.osVal.end(), |
1127 | 94 | [](char c) { return c >= '0' && c <= '9'; })) |
1128 | 5 | { |
1129 | 5 | const int nYear = (sAttrDef.osVal[0] - '0') * 1000 + |
1130 | 5 | (sAttrDef.osVal[1] - '0') * 100 + |
1131 | 5 | (sAttrDef.osVal[2] - '0') * 10 + |
1132 | 5 | (sAttrDef.osVal[3] - '0'); |
1133 | 5 | const int nMonth = (sAttrDef.osVal[4] - '0') * 10 + |
1134 | 5 | (sAttrDef.osVal[5] - '0'); |
1135 | 5 | const int nDay = (sAttrDef.osVal[6] - '0') * 10 + |
1136 | 5 | (sAttrDef.osVal[7] - '0'); |
1137 | 5 | oFeature.SetField(iOGRFieldIdx, nYear, nMonth, nDay); |
1138 | 5 | } |
1139 | 16 | else if (sAttrDef.osVal.size() == 8 && |
1140 | 16 | sAttrDef.osVal[0] >= '0' && sAttrDef.osVal[0] <= '9' && |
1141 | 13 | sAttrDef.osVal[1] >= '0' && sAttrDef.osVal[1] <= '9' && |
1142 | 11 | sAttrDef.osVal[2] >= '0' && sAttrDef.osVal[2] <= '9' && |
1143 | 7 | sAttrDef.osVal[3] >= '0' && sAttrDef.osVal[3] <= '9' && |
1144 | 4 | sAttrDef.osVal[4] == '-' && sAttrDef.osVal[5] == '-' && |
1145 | 3 | sAttrDef.osVal[6] == '-' && sAttrDef.osVal[7] == '-') |
1146 | 3 | { |
1147 | 3 | const int nYear = (sAttrDef.osVal[0] - '0') * 1000 + |
1148 | 3 | (sAttrDef.osVal[1] - '0') * 100 + |
1149 | 3 | (sAttrDef.osVal[2] - '0') * 10 + |
1150 | 3 | (sAttrDef.osVal[3] - '0'); |
1151 | 3 | if (cpl::ends_with(osAttrName, "dateEnd")) |
1152 | 1 | { |
1153 | 1 | oFeature.SetField(iOGRFieldIdx, nYear, 12, 31); |
1154 | 1 | } |
1155 | 2 | else |
1156 | 2 | { |
1157 | 2 | oFeature.SetField(iOGRFieldIdx, nYear, 1, 1); |
1158 | 2 | } |
1159 | 3 | } |
1160 | 21 | break; |
1161 | 1.90k | } |
1162 | | |
1163 | 10 | case OFTTime: |
1164 | 10 | { |
1165 | 10 | if (sAttrDef.osVal.size() >= 6 && |
1166 | 10 | std::all_of(sAttrDef.osVal.begin(), |
1167 | 10 | sAttrDef.osVal.begin() + 6, |
1168 | 60 | [](char c) { return c >= '0' && c <= '9'; })) |
1169 | 10 | { |
1170 | 10 | const int nHour = (sAttrDef.osVal[0] - '0') * 10 + |
1171 | 10 | (sAttrDef.osVal[1] - '0'); |
1172 | 10 | const int nMin = (sAttrDef.osVal[2] - '0') * 10 + |
1173 | 10 | (sAttrDef.osVal[3] - '0'); |
1174 | 10 | const int nSec = (sAttrDef.osVal[4] - '0') * 10 + |
1175 | 10 | (sAttrDef.osVal[5] - '0'); |
1176 | 10 | int nTZFlag = OGR_TZFLAG_UNKNOWN; |
1177 | 10 | if (sAttrDef.osVal.size() == 7 && sAttrDef.osVal[6] == 'Z') |
1178 | 0 | nTZFlag = OGR_TZFLAG_UTC; |
1179 | 10 | else if (sAttrDef.osVal.size() == 11) |
1180 | 1 | { |
1181 | 1 | const int nTZHour = (sAttrDef.osVal[7] - '0') * 10 + |
1182 | 1 | (sAttrDef.osVal[8] - '0'); |
1183 | 1 | const int nTZMin = (sAttrDef.osVal[9] - '0') * 10 + |
1184 | 1 | (sAttrDef.osVal[10] - '0'); |
1185 | 1 | const int n15Minutes = (nTZHour * 60 + nTZMin) / 15; |
1186 | 1 | if (sAttrDef.osVal[6] == '+') |
1187 | 1 | nTZFlag = OGR_TZFLAG_UTC + n15Minutes; |
1188 | 0 | else |
1189 | 0 | nTZFlag = OGR_TZFLAG_UTC - n15Minutes; |
1190 | 1 | } |
1191 | 10 | oFeature.SetField(iOGRFieldIdx, 0, 0, 0, nHour, nMin, |
1192 | 10 | static_cast<float>(nSec), nTZFlag); |
1193 | 10 | } |
1194 | 10 | break; |
1195 | 1.90k | } |
1196 | | |
1197 | 0 | default: |
1198 | 0 | CPLAssert(false); |
1199 | 2.84k | } |
1200 | 2.84k | } |
1201 | | |
1202 | | // Set multi-valued fields. |
1203 | 5.27k | for (const auto &[iOGRFieldIdx, aosStrings] : stringListAttrs) |
1204 | 21 | oFeature.SetField(static_cast<int>(iOGRFieldIdx), aosStrings.List()); |
1205 | | |
1206 | 5.27k | for (const auto &[iOGRFieldIdx, anVals] : intListAttrs) |
1207 | 20 | oFeature.SetField(static_cast<int>(iOGRFieldIdx), |
1208 | 20 | static_cast<int>(anVals.size()), anVals.data()); |
1209 | | |
1210 | 5.27k | for (const auto &[iOGRFieldIdx, adfVals] : doubleListAttrs) |
1211 | 6 | oFeature.SetField(static_cast<int>(iOGRFieldIdx), |
1212 | 6 | static_cast<int>(adfVals.size()), adfVals.data()); |
1213 | | |
1214 | 5.27k | return true; |
1215 | 5.67k | } |
1216 | | |
1217 | | /************************************************************************/ |
1218 | | /* AttrNode */ |
1219 | | /************************************************************************/ |
1220 | | |
1221 | | namespace |
1222 | | { |
1223 | | using AttrCode = OGRS101Reader::AttrCode; |
1224 | | using AttrRepeat = OGRS101Reader::AttrRepeat; |
1225 | | using AttrIndex = OGRS101Reader::AttrIndex; |
1226 | | |
1227 | | struct AttrNode |
1228 | | { |
1229 | | AttrCode code = 0; |
1230 | | AttrRepeat indexOfSameCode = 0; |
1231 | | std::string value{}; |
1232 | | std::vector<std::shared_ptr<AttrNode>> children{}; |
1233 | | |
1234 | | std::string Encode() const |
1235 | 309 | { |
1236 | 309 | std::string s; |
1237 | 309 | AttrIndex thisIdx = 0; |
1238 | 309 | AttrIndex curIdx = 0; |
1239 | 309 | for (const auto &child : children) |
1240 | 436 | { |
1241 | 436 | child->Encode(s, thisIdx, curIdx, ""); |
1242 | 436 | } |
1243 | 309 | return s; |
1244 | 309 | } |
1245 | | |
1246 | | private: |
1247 | | void Encode(std::string &s, AttrIndex parentIdx, AttrIndex &curIdx, |
1248 | | const std::string &indent); |
1249 | | }; |
1250 | | |
1251 | | void AttrNode::Encode(std::string &s, AttrIndex parentIdx, AttrIndex &curIdx, |
1252 | | const std::string &indent) |
1253 | 1.08k | { |
1254 | 1.08k | ++curIdx; |
1255 | | if constexpr (false) |
1256 | | { |
1257 | | const int idx = static_cast<int>(curIdx); |
1258 | | CPLDebug("S101", "%s[%d].code = %d", indent.c_str(), idx, |
1259 | | static_cast<int>(code)); |
1260 | | CPLDebug("S101", "%s[%d].indexOfSameCode = %d\n", indent.c_str(), idx, |
1261 | | static_cast<int>(indexOfSameCode)); |
1262 | | CPLDebug("S101", "%s[%d].parentIdx = %d", indent.c_str(), idx, |
1263 | | static_cast<int>(parentIdx)); |
1264 | | CPLDebug("S101", "%s[%d].value = %s", indent.c_str(), idx, |
1265 | | value.c_str()); |
1266 | | } |
1267 | 1.08k | OGRS101Reader::AppendUInt16(s, |
1268 | 1.08k | static_cast<uint16_t>(static_cast<int>(code))); |
1269 | 1.08k | OGRS101Reader::AppendUInt16( |
1270 | 1.08k | s, static_cast<uint16_t>(static_cast<int>(indexOfSameCode))); |
1271 | 1.08k | OGRS101Reader::AppendUInt16( |
1272 | 1.08k | s, static_cast<uint16_t>(static_cast<int>(parentIdx))); |
1273 | 1.08k | OGRS101Reader::AppendUInt8(s, static_cast<uint8_t>(INSTRUCTION_INSERT)); |
1274 | 1.08k | s.append(value); |
1275 | 1.08k | OGRS101Reader::AppendUInt8(s, static_cast<uint8_t>(DDF_UNIT_TERMINATOR)); |
1276 | | |
1277 | 1.08k | if (!children.empty()) |
1278 | 322 | { |
1279 | 322 | const auto thisIdx = curIdx; |
1280 | 322 | const std::string newIndent = indent + " "; |
1281 | 322 | for (const auto &child : children) |
1282 | 648 | { |
1283 | 648 | child->Encode(s, thisIdx, curIdx, newIndent); |
1284 | 648 | } |
1285 | 322 | } |
1286 | 1.08k | } |
1287 | | |
1288 | | } // namespace |
1289 | | |
1290 | | /************************************************************************/ |
1291 | | /* ProcessUpdateAttributeLikeField() */ |
1292 | | /************************************************************************/ |
1293 | | |
1294 | | /** Implement update of ATTR/INAS/FASC field described in 10a-5.1.2 |
1295 | | * "Updating of the Attribute field" |
1296 | | */ |
1297 | | bool OGRS101Reader::ProcessUpdateAttributeLikeField( |
1298 | | const DDFRecord *poUpdateRecord, const DDFField *poUpdateField, |
1299 | | DDFRecord *poTargetRecord, DDFField *poTargetField, |
1300 | | int iFieldInstance) const |
1301 | 391 | { |
1302 | 391 | const char *pszAttrFieldName = poUpdateField->GetFieldDefn()->GetName(); |
1303 | 391 | CPLAssert(EQUAL(pszAttrFieldName, ATTR_FIELD) || |
1304 | 391 | EQUAL(pszAttrFieldName, INAS_FIELD) || |
1305 | 391 | EQUAL(pszAttrFieldName, FASC_FIELD)); |
1306 | | |
1307 | 391 | const auto poIDField = poUpdateRecord->GetField(0); |
1308 | 391 | CPLAssert(poIDField); |
1309 | 391 | const char *pszIDFieldName = poIDField->GetFieldDefn()->GetName(); |
1310 | 391 | CPLAssert(pszIDFieldName); |
1311 | | |
1312 | | // Record name |
1313 | 391 | const RecordName nRCNM = |
1314 | 391 | poUpdateRecord->GetIntSubfield(pszIDFieldName, 0, RCNM_SUBFIELD, 0); |
1315 | | |
1316 | | // Record identifier |
1317 | 391 | const int nRCID = |
1318 | 391 | poUpdateRecord->GetIntSubfield(pszIDFieldName, 0, RCID_SUBFIELD, 0); |
1319 | | |
1320 | 391 | AttrNode root; |
1321 | | |
1322 | 391 | auto poActualTargetField = poTargetField->GetParts().size() == 2 |
1323 | 391 | ? poTargetField->GetParts()[1].get() |
1324 | 391 | : poTargetField; |
1325 | 391 | const int nTargetRepeatCount = poActualTargetField->GetRepeatCount(); |
1326 | | |
1327 | 391 | const auto poActualUpdateField = poUpdateField->GetParts().size() == 2 |
1328 | 391 | ? poUpdateField->GetParts()[1].get() |
1329 | 391 | : poUpdateField; |
1330 | 391 | const int nUpdateRepeatCount = poActualUpdateField->GetRepeatCount(); |
1331 | | |
1332 | 391 | constexpr int PASS_TARGET = 0; |
1333 | 391 | constexpr int PASS_UPDATE = 1; |
1334 | 1.05k | for (int iPass = PASS_TARGET; iPass <= PASS_UPDATE; ++iPass) |
1335 | 746 | { |
1336 | 746 | const auto poCurRecord = |
1337 | 746 | (iPass == PASS_TARGET) ? poTargetRecord : poUpdateRecord; |
1338 | 746 | const auto poCurField = |
1339 | 746 | (iPass == PASS_TARGET) ? poTargetField : poUpdateField; |
1340 | 746 | const int nRepeatCount = |
1341 | 746 | (iPass == PASS_TARGET) ? nTargetRepeatCount : nUpdateRepeatCount; |
1342 | | |
1343 | | // mapAttributeIndexToNode and oSetNATC_ATIX_PAIX do need to be reset |
1344 | | // at each pass |
1345 | 746 | std::map<AttrIndex, std::weak_ptr<AttrNode>> mapAttributeIndexToNode; |
1346 | 746 | std::set<std::tuple<AttrCode, AttrRepeat, AttrIndex>> |
1347 | 746 | oSetNATC_ATIX_PAIX; |
1348 | | |
1349 | 2.90k | for (int i = 0; i < nRepeatCount; ++i) |
1350 | 2.24k | { |
1351 | 2.24k | const AttrIndex curIdx = i + 1; |
1352 | 2.24k | const int nInstruction = |
1353 | 2.24k | poCurRecord->GetIntSubfield(poCurField, ATIN_SUBFIELD, i); |
1354 | 2.24k | if (nInstruction != INSTRUCTION_INSERT && |
1355 | 425 | nInstruction != INSTRUCTION_UPDATE && |
1356 | 58 | nInstruction != INSTRUCTION_DELETE) |
1357 | 13 | { |
1358 | 13 | return EMIT_ERROR_OR_WARNING(CPLSPrintf( |
1359 | 13 | "%s, RCNM=%d, RCID=%d, %s field, instance %d, entry %d: " |
1360 | 13 | "invalid ATIN=%d", |
1361 | 13 | m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID, |
1362 | 13 | pszAttrFieldName, iFieldInstance, i, nInstruction)); |
1363 | 13 | } |
1364 | | |
1365 | 2.22k | auto poNode = std::make_shared<AttrNode>(); |
1366 | 2.22k | poNode->code = |
1367 | 2.22k | poCurRecord->GetIntSubfield(poCurField, NATC_SUBFIELD, i); |
1368 | 2.22k | poNode->indexOfSameCode = |
1369 | 2.22k | poCurRecord->GetIntSubfield(poCurField, ATIX_SUBFIELD, i); |
1370 | 2.22k | const AttrIndex parentIndex = |
1371 | 2.22k | poCurRecord->GetIntSubfield(poCurField, PAIX_SUBFIELD, i); |
1372 | | |
1373 | | // (NATC,ATIX,PAIX) tuple should be unique within a record |
1374 | 2.22k | if (!oSetNATC_ATIX_PAIX |
1375 | 2.22k | .insert( |
1376 | 2.22k | {poNode->code, poNode->indexOfSameCode, parentIndex}) |
1377 | 2.22k | .second) |
1378 | 1 | { |
1379 | 1 | return EMIT_ERROR_OR_WARNING( |
1380 | 1 | CPLSPrintf("%s, RCNM=%d, RCID=%d, %s field, instance %d: " |
1381 | 1 | "entry %d refers to (NATC,ATIX,PAIX)=(%d,%d,%d) " |
1382 | 1 | "already encountered.", |
1383 | 1 | m_osFilename.c_str(), static_cast<int>(nRCNM), |
1384 | 1 | nRCID, pszAttrFieldName, iFieldInstance, i, |
1385 | 1 | static_cast<int>(poNode->code), |
1386 | 1 | static_cast<int>(poNode->indexOfSameCode), |
1387 | 1 | static_cast<int>(parentIndex))); |
1388 | 1 | } |
1389 | | |
1390 | 2.22k | const char *pszATVL = |
1391 | 2.22k | poCurRecord->GetStringSubfield(poCurField, ATVL_SUBFIELD, i); |
1392 | 2.22k | if (pszATVL) |
1393 | 2.22k | poNode->value = pszATVL; |
1394 | | |
1395 | | // Find the parent node (which is the root node if no parent) |
1396 | 2.22k | std::shared_ptr<AttrNode> poParentNodeSharedPtr; |
1397 | 2.22k | AttrNode *poParentNode = &root; |
1398 | 2.22k | if (parentIndex > 0) |
1399 | 1.37k | { |
1400 | 1.37k | auto oIter = mapAttributeIndexToNode.find(parentIndex); |
1401 | 1.37k | if (oIter == mapAttributeIndexToNode.end()) |
1402 | 37 | { |
1403 | 37 | return EMIT_ERROR_OR_WARNING(CPLSPrintf( |
1404 | 37 | "%s, RCNM=%d, RCID=%d, %s field, instance %d: entry %d " |
1405 | 37 | "refers to a PAIX=%d that does not exist", |
1406 | 37 | m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID, |
1407 | 37 | pszAttrFieldName, iFieldInstance, i, |
1408 | 37 | static_cast<int>(parentIndex))); |
1409 | 37 | } |
1410 | 1.33k | poParentNodeSharedPtr = oIter->second.lock(); |
1411 | 1.33k | if (!poParentNodeSharedPtr) |
1412 | 0 | { |
1413 | | // I don't think that can happen given the |
1414 | | // (NATC,ATIX,PAIX) unicity check |
1415 | 0 | return EMIT_ERROR_OR_WARNING(CPLSPrintf( |
1416 | 0 | "%s, RCNM=%d, RCID=%d, %s field, instance %d: entry %d " |
1417 | 0 | "refers to a PAIX=%d that has been deleted", |
1418 | 0 | m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID, |
1419 | 0 | pszAttrFieldName, iFieldInstance, i, |
1420 | 0 | static_cast<int>(parentIndex))); |
1421 | 0 | } |
1422 | 1.33k | poParentNode = poParentNodeSharedPtr.get(); |
1423 | 1.33k | } |
1424 | | |
1425 | 2.18k | if (nInstruction == INSTRUCTION_INSERT) |
1426 | 1.78k | { |
1427 | | // Find a sibling of same code and whose index is just one before |
1428 | | // the one to insert. |
1429 | 1.78k | auto iterInsertion = poParentNode->children.begin(); |
1430 | 2.85k | for (; iterInsertion != poParentNode->children.end(); |
1431 | 1.78k | ++iterInsertion) |
1432 | 1.20k | { |
1433 | 1.20k | if ((*iterInsertion)->code == poNode->code && |
1434 | 237 | (*iterInsertion)->indexOfSameCode >= |
1435 | 237 | poNode->indexOfSameCode) |
1436 | 132 | { |
1437 | 132 | break; |
1438 | 132 | } |
1439 | 1.20k | } |
1440 | | |
1441 | 1.78k | if (iterInsertion != poParentNode->children.end()) |
1442 | 132 | { |
1443 | 132 | if (poCurRecord == poTargetRecord) |
1444 | 16 | { |
1445 | 16 | const auto iterNext = std::next(iterInsertion); |
1446 | 16 | if (iterNext != poParentNode->children.end() && |
1447 | 9 | (*iterNext)->code == poNode->code && |
1448 | 1 | (*iterNext)->indexOfSameCode == |
1449 | 1 | poNode->indexOfSameCode) |
1450 | 0 | { |
1451 | 0 | return EMIT_ERROR_OR_WARNING(CPLSPrintf( |
1452 | 0 | "%s, RCNM=%d, RCID=%d, %s field, instance %d: " |
1453 | 0 | "entry %d collides with another entry of same " |
1454 | 0 | "(NATC, ATIX)=(%d,%d)", |
1455 | 0 | m_osFilename.c_str(), static_cast<int>(nRCNM), |
1456 | 0 | nRCID, pszAttrFieldName, iFieldInstance, i, |
1457 | 0 | static_cast<int>(poNode->code), |
1458 | 0 | static_cast<int>(poNode->indexOfSameCode))); |
1459 | 0 | } |
1460 | 16 | } |
1461 | 116 | else |
1462 | 116 | { |
1463 | | // Renumber indexOfSameCode of children right to the inserted one |
1464 | 116 | for (auto iterChild = iterInsertion; |
1465 | 338 | iterChild != poParentNode->children.end(); |
1466 | 222 | ++iterChild) |
1467 | 222 | { |
1468 | 222 | if ((*iterChild)->code == poNode->code && |
1469 | 121 | (*iterChild)->indexOfSameCode >= |
1470 | 121 | poNode->indexOfSameCode) |
1471 | 121 | { |
1472 | 121 | ++((*iterChild)->indexOfSameCode); |
1473 | 121 | } |
1474 | 222 | } |
1475 | 116 | } |
1476 | 132 | } |
1477 | | |
1478 | 1.78k | mapAttributeIndexToNode[curIdx] = poNode; |
1479 | | |
1480 | 1.78k | poParentNode->children.insert(iterInsertion, poNode); |
1481 | 1.78k | } |
1482 | 402 | else |
1483 | 402 | { |
1484 | | // Identify child with desired (code, indexOfSameCode) |
1485 | 402 | auto iterChild = poParentNode->children.begin(); |
1486 | 535 | for (; iterChild != poParentNode->children.end(); ++iterChild) |
1487 | 504 | { |
1488 | 504 | if ((*iterChild)->code == poNode->code && |
1489 | 390 | (*iterChild)->indexOfSameCode == |
1490 | 390 | poNode->indexOfSameCode) |
1491 | 371 | { |
1492 | 371 | break; |
1493 | 371 | } |
1494 | 504 | } |
1495 | 402 | if (iterChild == poParentNode->children.end()) |
1496 | 31 | { |
1497 | 31 | return EMIT_ERROR_OR_WARNING(CPLSPrintf( |
1498 | 31 | "%s, RCNM=%d, RCID=%d, %s field, instance %d: entry %d " |
1499 | 31 | "references unexisting entry (NATC, ATIX)=(%d,%d)", |
1500 | 31 | m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID, |
1501 | 31 | pszAttrFieldName, iFieldInstance, i, |
1502 | 31 | static_cast<int>(poNode->code), |
1503 | 31 | static_cast<int>(poNode->indexOfSameCode))); |
1504 | 31 | } |
1505 | | |
1506 | 371 | if (nInstruction == INSTRUCTION_DELETE) |
1507 | 33 | { |
1508 | 33 | poParentNode->children.erase(iterChild); |
1509 | | // No need to explicitly modify mapAttributeIndexToNode |
1510 | | // As it contains weak pointers, if the removed node or |
1511 | | // one of its children was in the map, the weak pointer |
1512 | | // will be invalidated. |
1513 | 33 | } |
1514 | 338 | else |
1515 | 338 | { |
1516 | 338 | const auto &poModifiedNode = *iterChild; |
1517 | 338 | mapAttributeIndexToNode[curIdx] = poModifiedNode; |
1518 | 338 | poModifiedNode->value = poNode->value; |
1519 | 338 | } |
1520 | 371 | } |
1521 | 2.18k | } |
1522 | 746 | } |
1523 | | |
1524 | 309 | std::string s; |
1525 | 309 | if (EQUAL(pszAttrFieldName, INAS_FIELD) || |
1526 | 72 | EQUAL(pszAttrFieldName, FASC_FIELD)) |
1527 | 241 | { |
1528 | 241 | constexpr int SIZE_OF_NON_REPEATED_FIELDS = 1 + 4 + 2 + 2 + 1; |
1529 | 241 | if (poUpdateField->GetDataSize() < SIZE_OF_NON_REPEATED_FIELDS) |
1530 | 0 | { |
1531 | | // Should probably not occur given earlier checks, but... |
1532 | 0 | return EMIT_ERROR_OR_WARNING( |
1533 | 0 | CPLSPrintf("%s, RCNM=%d, RCID=%d, %s field, instance %d: " |
1534 | 0 | "invalid update field", |
1535 | 0 | m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID, |
1536 | 0 | pszAttrFieldName, iFieldInstance)); |
1537 | 0 | } |
1538 | 241 | s.append(poUpdateField->GetData(), SIZE_OF_NON_REPEATED_FIELDS - 1); |
1539 | 241 | AppendUInt8(s, INSTRUCTION_INSERT); |
1540 | 241 | } |
1541 | 309 | s += root.Encode(); |
1542 | 309 | AppendUInt8(s, DDF_FIELD_TERMINATOR); |
1543 | 309 | poTargetRecord->SetFieldRaw(poTargetField, s.data(), |
1544 | 309 | static_cast<int>(s.size())); |
1545 | | |
1546 | 309 | return true; |
1547 | 309 | } |
1548 | | |
1549 | | /************************************************************************/ |
1550 | | /* ProcessUpdateATTR() */ |
1551 | | /************************************************************************/ |
1552 | | |
1553 | | /** Update all instances of ATTR field |
1554 | | */ |
1555 | | bool OGRS101Reader::ProcessUpdateATTR(const DDFRecord *poUpdateRecord, |
1556 | | DDFRecord *poTargetRecord) const |
1557 | 220 | { |
1558 | 220 | const auto poIDField = poUpdateRecord->GetField(0); |
1559 | 220 | CPLAssert(poIDField); |
1560 | 220 | const char *pszIDFieldName = poIDField->GetFieldDefn()->GetName(); |
1561 | 220 | CPLAssert(pszIDFieldName); |
1562 | | |
1563 | | // Record name |
1564 | 220 | const RecordName nRCNM = |
1565 | 220 | poUpdateRecord->GetIntSubfield(pszIDFieldName, 0, RCNM_SUBFIELD, 0); |
1566 | | |
1567 | | // Record identifier |
1568 | 220 | const int nRCID = |
1569 | 220 | poUpdateRecord->GetIntSubfield(pszIDFieldName, 0, RCID_SUBFIELD, 0); |
1570 | | |
1571 | 220 | auto apoUpdateFields = poUpdateRecord->GetFields(ATTR_FIELD); |
1572 | 220 | if (apoUpdateFields.empty()) |
1573 | 80 | return true; |
1574 | 140 | auto apoTargetFields = poTargetRecord->GetFields(ATTR_FIELD); |
1575 | 140 | if (apoTargetFields.size() != apoUpdateFields.size()) |
1576 | 1 | { |
1577 | 1 | return EMIT_ERROR_OR_WARNING(CPLSPrintf( |
1578 | 1 | "%s, RCNM=%d, RCID=%d, %s field: target record has %d field " |
1579 | 1 | "instances, whereas update record has %d", |
1580 | 1 | m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID, ATTR_FIELD, |
1581 | 1 | static_cast<int>(apoTargetFields.size()), |
1582 | 1 | static_cast<int>(apoUpdateFields.size()))); |
1583 | 1 | } |
1584 | | |
1585 | 207 | for (size_t i = 0; i < apoUpdateFields.size(); ++i) |
1586 | 140 | { |
1587 | 140 | if (!ProcessUpdateAttributeLikeField(poUpdateRecord, apoUpdateFields[i], |
1588 | 140 | poTargetRecord, apoTargetFields[i], |
1589 | 140 | static_cast<int>(i))) |
1590 | 72 | { |
1591 | 72 | return false; |
1592 | 72 | } |
1593 | 140 | } |
1594 | | |
1595 | 67 | return true; |
1596 | 139 | } |
1597 | | |
1598 | | /************************************************************************/ |
1599 | | /* ProcessUpdateINASOrFASC() */ |
1600 | | /************************************************************************/ |
1601 | | |
1602 | | /** Update all instances of INAS or FASC field |
1603 | | */ |
1604 | | bool OGRS101Reader::ProcessUpdateINASOrFASC(const DDFRecord *poUpdateRecord, |
1605 | | DDFRecord *poTargetRecord, |
1606 | | const char *pszFieldName) const |
1607 | 539 | { |
1608 | 539 | CPLAssert(EQUAL(pszFieldName, INAS_FIELD) || |
1609 | 539 | EQUAL(pszFieldName, FASC_FIELD)); |
1610 | | |
1611 | 539 | const auto poIDField = poUpdateRecord->GetField(0); |
1612 | 539 | CPLAssert(poIDField); |
1613 | | |
1614 | | // Record name |
1615 | 539 | const RecordName nRCNM = |
1616 | 539 | poUpdateRecord->GetIntSubfield(poIDField, RCNM_SUBFIELD, 0); |
1617 | | |
1618 | | // Record identifier |
1619 | 539 | const int nRCID = |
1620 | 539 | poUpdateRecord->GetIntSubfield(poIDField, RCID_SUBFIELD, 0); |
1621 | | |
1622 | 539 | auto apoUpdateFields = poUpdateRecord->GetFields(pszFieldName); |
1623 | 539 | if (apoUpdateFields.empty()) |
1624 | 337 | return true; |
1625 | | |
1626 | 202 | auto apoTargetFields = poTargetRecord->GetFields(pszFieldName); |
1627 | | |
1628 | 649 | for (int iUpdate = 0; iUpdate < static_cast<int>(apoUpdateFields.size()); |
1629 | 447 | ++iUpdate) |
1630 | 504 | { |
1631 | 504 | const auto poUpdateField = apoUpdateFields[iUpdate]; |
1632 | 504 | const int nInstruction = poUpdateRecord->GetIntSubfield( |
1633 | 504 | poUpdateField, |
1634 | 504 | EQUAL(pszFieldName, INAS_FIELD) ? IUIN_SUBFIELD : FAUI_SUBFIELD, 0); |
1635 | 504 | if (nInstruction == INSTRUCTION_INSERT) |
1636 | 194 | { |
1637 | 194 | const auto poINASFieldDefn = |
1638 | 194 | m_oMainModule.FindFieldDefn(pszFieldName); |
1639 | 194 | if (!poINASFieldDefn) |
1640 | 2 | { |
1641 | 2 | return EMIT_ERROR(CPLSPrintf("Cannot find %s field definition", |
1642 | 2 | pszFieldName)); |
1643 | 2 | } |
1644 | 192 | auto poFieldTarget = poTargetRecord->AddField(poINASFieldDefn); |
1645 | 192 | CPLAssert(poFieldTarget); |
1646 | | |
1647 | 192 | poTargetRecord->SetFieldRaw(poFieldTarget, poUpdateField->GetData(), |
1648 | 192 | poUpdateField->GetDataSize()); |
1649 | 192 | apoTargetFields.push_back(poFieldTarget); |
1650 | 192 | } |
1651 | 310 | else if (nInstruction == INSTRUCTION_UPDATE || |
1652 | 24 | nInstruction == INSTRUCTION_DELETE) |
1653 | 303 | { |
1654 | 303 | const RecordName RRNM = |
1655 | 303 | poUpdateRecord->GetIntSubfield(poUpdateField, RRNM_SUBFIELD, 0); |
1656 | 303 | const int RRID = |
1657 | 303 | poUpdateRecord->GetIntSubfield(poUpdateField, RRID_SUBFIELD, 0); |
1658 | | |
1659 | 303 | bool bMatchFound = false; |
1660 | 517 | for (size_t iTarget = 0; iTarget < apoTargetFields.size(); |
1661 | 303 | ++iTarget) |
1662 | 479 | { |
1663 | 479 | auto poTargetField = apoTargetFields[iTarget]; |
1664 | 479 | const RecordName RRNMTarget = poTargetRecord->GetIntSubfield( |
1665 | 479 | poTargetField, RRNM_SUBFIELD, 0); |
1666 | 479 | const int RRIDTarget = poTargetRecord->GetIntSubfield( |
1667 | 479 | poTargetField, RRID_SUBFIELD, 0); |
1668 | 479 | if (RRNM == RRNMTarget && RRID == RRIDTarget) |
1669 | 265 | { |
1670 | 265 | bMatchFound = true; |
1671 | 265 | if (nInstruction == INSTRUCTION_DELETE) |
1672 | 14 | { |
1673 | 14 | poTargetRecord->DeleteField(poTargetField); |
1674 | 14 | apoTargetFields.erase(apoTargetFields.begin() + |
1675 | 14 | iTarget); |
1676 | 14 | } |
1677 | 251 | else |
1678 | 251 | { |
1679 | 251 | if (!ProcessUpdateAttributeLikeField( |
1680 | 251 | poUpdateRecord, poUpdateField, poTargetRecord, |
1681 | 251 | poTargetField, iUpdate)) |
1682 | 10 | { |
1683 | 10 | return false; |
1684 | 10 | } |
1685 | 251 | } |
1686 | 255 | break; |
1687 | 265 | } |
1688 | 479 | } |
1689 | 293 | if (!bMatchFound) |
1690 | 38 | { |
1691 | 38 | return EMIT_ERROR_OR_WARNING(CPLSPrintf( |
1692 | 38 | "%s, RCNM=%d, RCID=%d, %s field, %d instance: found no " |
1693 | 38 | "matching (RRNM,RRID)=(%d,%d) to %s", |
1694 | 38 | m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID, |
1695 | 38 | pszFieldName, iUpdate, static_cast<int>(RRNM), RRID, |
1696 | 38 | nInstruction == INSTRUCTION_UPDATE ? "update" : "delete")); |
1697 | 38 | } |
1698 | 293 | } |
1699 | 7 | else |
1700 | 7 | { |
1701 | 7 | return EMIT_ERROR_OR_WARNING( |
1702 | 7 | CPLSPrintf("%s, RCNM=%d, RCID=%d, %s field, %d instance: " |
1703 | 7 | "invalid instruction = %d", |
1704 | 7 | m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID, |
1705 | 7 | pszFieldName, iUpdate, nInstruction)); |
1706 | 7 | } |
1707 | 504 | } |
1708 | | |
1709 | 145 | return true; |
1710 | 202 | } |