Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: S-101 driver
4 : * Purpose: Implements OGRS101Reader
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2026, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "ogr_s101.h"
14 : #include "ogrs101readerconstants.h"
15 :
16 : #include <algorithm>
17 : #include <memory>
18 :
19 : /************************************************************************/
20 : /* CreateSurfaceFeatureDefn() */
21 : /************************************************************************/
22 :
23 : /** Create the feature definition for the Surface layer
24 : */
25 343 : bool OGRS101Reader::CreateSurfaceFeatureDefn()
26 : {
27 343 : if (m_oSurfaceRecordIndex.GetCount() > 0)
28 : {
29 : m_poFeatureDefnSurface =
30 83 : OGRFeatureDefnRefCountedPtr::makeInstance(OGR_LAYER_NAME_SURFACE);
31 83 : m_poFeatureDefnSurface->SetGeomType(wkbPolygon);
32 83 : auto oSRSIter = m_oMapSRS.find(HORIZONTAL_CRS_ID);
33 83 : if (oSRSIter != m_oMapSRS.end())
34 : {
35 166 : m_poFeatureDefnSurface->GetGeomFieldDefn(0)->SetSpatialRef(
36 166 : OGRSpatialReferenceRefCountedPtr::makeClone(&oSRSIter->second)
37 83 : .get());
38 : }
39 83 : m_poFeatureDefnSurface->GetGeomFieldDefn(0)->SetCoordinatePrecision(
40 83 : m_coordinatePrecision);
41 : {
42 166 : OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_RECORD_ID, OFTInteger);
43 83 : m_poFeatureDefnSurface->AddFieldDefn(&oFieldDefn);
44 : }
45 : {
46 166 : OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_RECORD_VERSION, OFTInteger);
47 83 : m_poFeatureDefnSurface->AddFieldDefn(&oFieldDefn);
48 : }
49 83 : if (!InferFeatureDefn(m_oSurfaceRecordIndex, SRID_FIELD, INAS_FIELD, {},
50 83 : *m_poFeatureDefnSurface, m_oMapFieldDomains))
51 : {
52 1 : return false;
53 : }
54 : }
55 :
56 342 : return true;
57 : }
58 :
59 : /************************************************************************/
60 : /* ReadSurfaceGeometry() */
61 : /************************************************************************/
62 :
63 : std::unique_ptr<OGRPolygon>
64 539 : OGRS101Reader::ReadSurfaceGeometry(const DDFRecord *poRecord, int iRecord,
65 : int nRecordID,
66 : const OGRSpatialReference *poSRS) const
67 : {
68 539 : if (nRecordID < 0)
69 226 : nRecordID = poRecord->GetIntSubfield(SRID_FIELD, 0, RCID_SUBFIELD, 0);
70 :
71 74 : const auto GetErrorContext = [iRecord, nRecordID]()
72 : {
73 37 : if (iRecord >= 0)
74 37 : return CPLSPrintf("Record index=%d of SRID", iRecord);
75 : else
76 0 : return CPLSPrintf("Record ID=%d of SRID", nRecordID);
77 539 : };
78 :
79 539 : if (!poRecord->FindField(RIAS_FIELD))
80 : {
81 1 : CPL_IGNORE_RET_VAL(EMIT_ERROR_OR_WARNING(
82 : CPLSPrintf("%s: no RIAS field", GetErrorContext())));
83 1 : return nullptr;
84 : }
85 :
86 1076 : auto poSurface = std::make_unique<OGRPolygon>();
87 538 : poSurface->assignSpatialReference(poSRS);
88 538 : std::unique_ptr<OGRLinearRing> poExteriorRing;
89 1076 : std::vector<std::unique_ptr<OGRLinearRing>> apoInteriorRings;
90 1048 : for (const auto *poRIASField : poRecord->GetFields(RIAS_FIELD))
91 : {
92 538 : const int nRings = poRIASField->GetRepeatCount();
93 1564 : for (int iRing = 0; iRing < nRings; ++iRing)
94 : {
95 : const auto GetIntSubfield =
96 4193 : [poRecord, poRIASField, iRing](const char *pszSubFieldName)
97 : {
98 4193 : return poRecord->GetIntSubfield(poRIASField, pszSubFieldName,
99 4193 : iRing);
100 1054 : };
101 :
102 1054 : const int nRAUI = GetIntSubfield(RAUI_SUBFIELD);
103 1054 : if (nRAUI != INSTRUCTION_INSERT)
104 : {
105 3 : CPL_IGNORE_RET_VAL(EMIT_ERROR_OR_WARNING(
106 : CPLSPrintf("%s: wrong value %d for RAUI "
107 : "subfield of %d instance of RIAS field.",
108 : GetErrorContext(), nRAUI, iRing)));
109 28 : return nullptr;
110 : }
111 :
112 1051 : const RecordName nRRNM = GetIntSubfield(RRNM_SUBFIELD);
113 1525 : if (nRRNM != RECORD_NAME_CURVE &&
114 474 : nRRNM != RECORD_NAME_COMPOSITE_CURVE)
115 : {
116 3 : CPL_IGNORE_RET_VAL(EMIT_ERROR_OR_WARNING(CPLSPrintf(
117 : "%s: Invalid value for RRNM "
118 : "subfield of %d instance of RIAS field: "
119 : "got %d, expected %d or %d.",
120 : GetErrorContext(), iRing, static_cast<int>(nRRNM),
121 : static_cast<int>(RECORD_NAME_CURVE),
122 : static_cast<int>(RECORD_NAME_COMPOSITE_CURVE))));
123 3 : return nullptr;
124 : }
125 :
126 1048 : const int nRRID = GetIntSubfield(RRID_SUBFIELD);
127 0 : std::unique_ptr<OGRLineString> poCurvePart;
128 1048 : if (nRRNM == RECORD_NAME_CURVE)
129 : {
130 : const auto poCurveRecord =
131 577 : m_oCurveRecordIndex.FindRecord(nRRID);
132 577 : if (!poCurveRecord)
133 : {
134 3 : CPL_IGNORE_RET_VAL(EMIT_ERROR_OR_WARNING(CPLSPrintf(
135 : "%s: Value (RRNM=%d, RRID=%d) "
136 : "of instance %d of RIAS field does not point to an "
137 : "existing record.",
138 : GetErrorContext(), static_cast<int>(nRRNM), nRRID,
139 : iRing)));
140 3 : return nullptr;
141 : }
142 :
143 1148 : poCurvePart = ReadCurveGeometry(
144 574 : poCurveRecord, /* iRecord = */ -1, nRRID, poSRS);
145 : }
146 : else
147 : {
148 471 : CPLAssert(nRRNM == RECORD_NAME_COMPOSITE_CURVE);
149 :
150 : const auto poCompositeCurveRecord =
151 471 : m_oCompositeCurveRecordIndex.FindRecord(nRRID);
152 471 : if (!poCompositeCurveRecord)
153 : {
154 3 : CPL_IGNORE_RET_VAL(EMIT_ERROR_OR_WARNING(CPLSPrintf(
155 : "%s: Value (RRNM=%d, RRID=%d) "
156 : "of instance %d of RIAS field does not point to an "
157 : "existing record.",
158 : GetErrorContext(), static_cast<int>(nRRNM), nRRID,
159 : iRing)));
160 3 : return nullptr;
161 : }
162 :
163 936 : poCurvePart = ReadCompositeCurveGeometry(
164 468 : poCompositeCurveRecord, /* iRecord = */ -1, nRRID, poSRS);
165 : }
166 :
167 1042 : if (!poCurvePart)
168 : {
169 2 : return nullptr;
170 : }
171 :
172 1040 : bool bReverse = false;
173 1040 : const int nORNT = GetIntSubfield(ORNT_SUBFIELD);
174 1040 : if (nORNT == ORNT_REVERSE)
175 : {
176 523 : bReverse = true;
177 : }
178 517 : else if (nORNT != ORNT_FORWARD)
179 : {
180 3 : CPL_IGNORE_RET_VAL(EMIT_ERROR_OR_WARNING(
181 : CPLSPrintf("%s: Invalid value for ORNT "
182 : "subfield of %d instance of RIAS field: "
183 : "got %d, expected %d or %d.",
184 : GetErrorContext(), iRing, nORNT, ORNT_FORWARD,
185 : ORNT_REVERSE)));
186 3 : return nullptr;
187 : }
188 :
189 1037 : auto poRing = std::make_unique<OGRLinearRing>();
190 1037 : if (bReverse && poCurvePart->getNumPoints() > 0)
191 1046 : poRing->addSubLineString(poCurvePart.get(),
192 523 : poCurvePart->getNumPoints() - 1, 0);
193 : else
194 514 : poRing->addSubLineString(poCurvePart.get());
195 1037 : if (poRing->getNumPoints() <= 2 || !poRing->get_IsClosed())
196 : {
197 3 : CPL_IGNORE_RET_VAL(EMIT_ERROR_OR_WARNING(
198 : CPLSPrintf("%s: Ring of index %d is not closed.",
199 : GetErrorContext(), iRing)));
200 3 : return nullptr;
201 : }
202 :
203 1034 : constexpr int USAG_EXTERIOR = 1; // Exterior ring
204 1034 : constexpr int USAG_INTERIOR = 2; // Interior ring
205 : const int nUSAG =
206 1034 : poRecord->GetIntSubfield(RIAS_FIELD, 0, USAG_SUBFIELD, iRing);
207 1034 : if (nUSAG == USAG_EXTERIOR)
208 : {
209 520 : if (poExteriorRing)
210 : {
211 3 : CPL_IGNORE_RET_VAL(EMIT_ERROR_OR_WARNING(CPLSPrintf(
212 : "%s: several rings tagged as exterior rings.",
213 : GetErrorContext())));
214 3 : return nullptr;
215 : }
216 517 : if (!poRing->isClockwise())
217 : {
218 3 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
219 : "%s: exterior ring orientation is not clockwise.",
220 : GetErrorContext())))
221 : {
222 1 : return nullptr;
223 : }
224 : }
225 516 : poExteriorRing = std::move(poRing);
226 : }
227 514 : else if (nUSAG == USAG_INTERIOR)
228 : {
229 511 : if (poRing->isClockwise())
230 : {
231 3 : if (!EMIT_ERROR_OR_WARNING(
232 : CPLSPrintf("%s: orientation of interior ring of "
233 : "index %d is not counter-clockwise.",
234 : GetErrorContext(), iRing)))
235 : {
236 1 : return nullptr;
237 : }
238 : }
239 510 : apoInteriorRings.push_back(std::move(poRing));
240 : }
241 : else
242 : {
243 3 : CPL_IGNORE_RET_VAL(EMIT_ERROR_OR_WARNING(
244 : CPLSPrintf("%s: Invalid value for USAG "
245 : "subfield of %d instance of RIAS field: "
246 : "got %d, expected %d or %d.",
247 : GetErrorContext(), iRing, nUSAG, USAG_EXTERIOR,
248 : USAG_INTERIOR)));
249 3 : return nullptr;
250 : }
251 : }
252 : }
253 :
254 510 : if (!poExteriorRing)
255 : {
256 3 : CPL_IGNORE_RET_VAL(EMIT_ERROR_OR_WARNING(CPLSPrintf(
257 : "%s: no ring tagged as exterior ring.", GetErrorContext())));
258 3 : return nullptr;
259 : }
260 :
261 507 : poSurface->addRing(std::move(poExteriorRing));
262 1014 : for (auto &poRing : apoInteriorRings)
263 507 : poSurface->addRing(std::move(poRing));
264 :
265 507 : if (OGRGeometryFactory::haveGEOS())
266 : {
267 507 : std::string osReason;
268 507 : if (!poSurface->IsValid(&osReason))
269 : {
270 3 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf("%s: surface is invalid: %s.",
271 : GetErrorContext(),
272 : osReason.c_str())))
273 : {
274 1 : return nullptr;
275 : }
276 : }
277 : }
278 :
279 506 : return poSurface;
280 : }
281 :
282 : /************************************************************************/
283 : /* FillFeatureSurface() */
284 : /************************************************************************/
285 :
286 : /** Fill the content of the provided feature from the identified record
287 : * (of m_oSurfaceRecordIndex).
288 : */
289 226 : bool OGRS101Reader::FillFeatureSurface(const DDFRecordIndex &oIndex,
290 : int iRecord, OGRFeature &oFeature) const
291 : {
292 226 : const auto poRecord = oIndex.GetByIndex(iRecord);
293 226 : CPLAssert(poRecord);
294 :
295 : const OGRSpatialReference *poSRS =
296 226 : oFeature.GetDefnRef()->GetGeomFieldDefn(0)->GetSpatialRef();
297 : auto poSurface =
298 452 : ReadSurfaceGeometry(poRecord, iRecord, /* nRecordID = */ -1, poSRS);
299 226 : if (!poSurface)
300 : {
301 33 : if (m_bStrict)
302 12 : return false; // error message already emitted
303 : }
304 : else
305 : {
306 193 : oFeature.SetGeometry(std::move(poSurface));
307 : }
308 :
309 428 : return FillFeatureAttributes(oIndex, iRecord, INAS_FIELD, oFeature) &&
310 214 : FillFeatureWithNonAttrAssocSubfields(poRecord, iRecord, INAS_FIELD,
311 214 : oFeature);
312 : }
313 :
314 : /************************************************************************/
315 : /* ProcessUpdateRecordSurface() */
316 : /************************************************************************/
317 :
318 : /** Updates the geometry part of poTargetRecord with poUpdateRecord */
319 7 : bool OGRS101Reader::ProcessUpdateRecordSurface(const DDFRecord *poUpdateRecord,
320 : DDFRecord *poTargetRecord) const
321 : {
322 7 : const auto poIDField = poUpdateRecord->GetField(0);
323 7 : CPLAssert(poIDField);
324 :
325 : // Record name
326 : const RecordName nRCNM =
327 7 : poUpdateRecord->GetIntSubfield(poIDField, RCNM_SUBFIELD, 0);
328 :
329 : // Record identifier
330 : const int nRCID =
331 7 : poUpdateRecord->GetIntSubfield(poIDField, RCID_SUBFIELD, 0);
332 :
333 : // Ring Association Field field
334 14 : const auto apoUpdateFields = poUpdateRecord->GetFields(RIAS_FIELD);
335 7 : if (apoUpdateFields.empty())
336 0 : return true;
337 :
338 : struct Ring
339 : {
340 : int RRNM = 0;
341 : int RRID = 0;
342 : int ORNT = 0;
343 : int USAG = 0;
344 : int RAUI = 0;
345 :
346 26 : static Ring Read(const DDFRecord *poRecord, const DDFField *poField,
347 : int i)
348 : {
349 26 : Ring ring;
350 26 : ring.RRNM = poRecord->GetIntSubfield(poField, RRNM_SUBFIELD, i);
351 26 : ring.RRID = poRecord->GetIntSubfield(poField, RRID_SUBFIELD, i);
352 26 : ring.ORNT = poRecord->GetIntSubfield(poField, ORNT_SUBFIELD, i);
353 26 : ring.USAG = poRecord->GetIntSubfield(poField, USAG_SUBFIELD, i);
354 26 : ring.RAUI = poRecord->GetIntSubfield(poField, RAUI_SUBFIELD, i);
355 26 : return ring;
356 : }
357 : };
358 :
359 14 : std::vector<Ring> asTarget;
360 : // Ingest the existing/target record(s)
361 14 : auto apoTargetFields = poTargetRecord->GetFields(RIAS_FIELD);
362 14 : for (auto *poTargetField : apoTargetFields)
363 : {
364 7 : const int nTargetRepeatCount = poTargetField->GetRepeatCount();
365 21 : for (int i = 0; i < nTargetRepeatCount; ++i)
366 : {
367 14 : asTarget.push_back(Ring::Read(poTargetRecord, poTargetField, i));
368 : }
369 7 : poTargetRecord->DeleteField(poTargetField);
370 : }
371 :
372 : // Apply the update record(s)
373 12 : for (auto *poUpdateField : apoUpdateFields)
374 : {
375 7 : const int nUpdateRepeatCount = poUpdateField->GetRepeatCount();
376 17 : for (int i = 0; i < nUpdateRepeatCount; ++i)
377 : {
378 12 : Ring ring = Ring::Read(poUpdateRecord, poUpdateField, i);
379 12 : if (ring.RAUI == INSTRUCTION_INSERT)
380 : {
381 5 : asTarget.push_back(std::move(ring));
382 : }
383 7 : else if (ring.RAUI == INSTRUCTION_DELETE)
384 : {
385 5 : bool bMatchFound = false;
386 12 : for (size_t j = 0; j < asTarget.size(); ++j)
387 : {
388 13 : if (asTarget[j].RRNM == ring.RRNM &&
389 3 : asTarget[j].RRID == ring.RRID)
390 : {
391 3 : bMatchFound = true;
392 3 : asTarget.erase(asTarget.begin() + j);
393 3 : break;
394 : }
395 : }
396 7 : if (!bMatchFound &&
397 2 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
398 : "%s, RCNM=%d, RCID=%d, RIAS iSubField=%d: update "
399 : "field references RRNM=%d, RRID=%d which does not "
400 : "exist in initial or previous update",
401 : m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID, i,
402 : ring.RRNM, ring.RRID)))
403 : {
404 2 : return false;
405 : }
406 : }
407 2 : else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
408 : "%s, RCNM=%d, RCID=%d, SPAS iSubField=%d: invalid "
409 : "RAUI=%d",
410 : m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID,
411 : i, ring.RAUI)))
412 : {
413 1 : return false;
414 : }
415 : }
416 : }
417 :
418 5 : if (!asTarget.empty())
419 : {
420 5 : auto poRIASFieldDefn = m_oMainModule.FindFieldDefn(RIAS_FIELD);
421 5 : if (!poRIASFieldDefn)
422 : {
423 0 : return EMIT_ERROR("Cannot find RIAS field definition");
424 : }
425 5 : auto poRIASFieldTarget = poTargetRecord->AddField(poRIASFieldDefn);
426 5 : CPLAssert(poRIASFieldTarget);
427 5 : const auto poRIASFieldUpdate = apoUpdateFields[0];
428 5 : if (*(poRIASFieldTarget->GetFieldDefn()) !=
429 5 : *(poRIASFieldUpdate->GetFieldDefn()))
430 : {
431 0 : return EMIT_ERROR("RIAS field definitions of update and target "
432 : "records are different");
433 : }
434 :
435 : // Compose raw target field
436 10 : std::string s;
437 17 : for (const auto &cc : asTarget)
438 : {
439 12 : AppendUInt8(s, static_cast<uint8_t>(cc.RRNM));
440 12 : AppendInt32(s, cc.RRID);
441 12 : AppendUInt8(s, static_cast<uint8_t>(cc.ORNT));
442 12 : AppendUInt8(s, static_cast<uint8_t>(cc.USAG));
443 12 : AppendUInt8(s, static_cast<uint8_t>(cc.RAUI));
444 : }
445 5 : AppendUInt8(s, DDF_FIELD_TERMINATOR);
446 :
447 5 : poTargetRecord->SetFieldRaw(poRIASFieldTarget, s.data(),
448 5 : static_cast<int>(s.size()));
449 : }
450 :
451 5 : return true;
452 : }
|