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 <memory>
17 :
18 : /************************************************************************/
19 : /* CreateCurveFeatureDefn() */
20 : /************************************************************************/
21 :
22 : /** Create the feature definition for the Curve layer
23 : */
24 249 : bool OGRS101Reader::CreateCurveFeatureDefn()
25 : {
26 249 : if (m_oCurveRecordIndex.GetCount() > 0)
27 : {
28 : m_poFeatureDefnCurve =
29 113 : OGRFeatureDefnRefCountedPtr::makeInstance(OGR_LAYER_NAME_CURVE);
30 113 : m_poFeatureDefnCurve->SetGeomType(wkbLineString);
31 113 : auto oSRSIter = m_oMapSRS.find(HORIZONTAL_CRS_ID);
32 113 : if (oSRSIter != m_oMapSRS.end())
33 : {
34 226 : m_poFeatureDefnCurve->GetGeomFieldDefn(0)->SetSpatialRef(
35 226 : OGRSpatialReferenceRefCountedPtr::makeClone(&oSRSIter->second)
36 113 : .get());
37 : }
38 : {
39 226 : OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_RECORD_ID, OFTInteger);
40 113 : m_poFeatureDefnCurve->AddFieldDefn(&oFieldDefn);
41 : }
42 : {
43 226 : OGRFieldDefn oFieldDefn(OGR_FIELD_NAME_RECORD_VERSION, OFTInteger);
44 113 : m_poFeatureDefnCurve->AddFieldDefn(&oFieldDefn);
45 : }
46 113 : if (!InferFeatureDefn(m_oCurveRecordIndex, CRID_FIELD, INAS_FIELD, {},
47 113 : *m_poFeatureDefnCurve, m_oMapFieldDomains))
48 : {
49 1 : return false;
50 : }
51 : }
52 :
53 248 : return true;
54 : }
55 :
56 : /************************************************************************/
57 : /* ReadCurveGeometry() */
58 : /************************************************************************/
59 :
60 : std::unique_ptr<OGRLineString>
61 2031 : OGRS101Reader::ReadCurveGeometry(const DDFRecord *poRecord, int iRecord,
62 : int nRecordID,
63 : const OGRSpatialReference *poSRS) const
64 : {
65 2031 : const DDFField *poSEGHField = poRecord->FindField(SEGH_FIELD);
66 2031 : if (poSEGHField)
67 : {
68 2031 : constexpr const char *INTP_SUBFIELD = "INTP";
69 : const int nINTP =
70 2031 : poRecord->GetIntSubfield(SEGH_FIELD, 0, INTP_SUBFIELD, 0);
71 2031 : constexpr int INTERPOLATION_LOXODROMIC = 4;
72 2039 : if (nINTP != INTERPOLATION_LOXODROMIC &&
73 8 : !EMIT_ERROR_OR_WARNING(
74 : CPLSPrintf("Record index %d of CRID: Invalid value for INTP "
75 : "subfield of SEGH field: "
76 : "got %d, expected %d.",
77 : iRecord, nINTP, INTERPOLATION_LOXODROMIC)))
78 : {
79 2 : return nullptr;
80 : }
81 : }
82 :
83 4058 : const auto apoCoordFields = poRecord->GetFields(C2IL_FIELD);
84 2029 : if (apoCoordFields.empty())
85 0 : return nullptr;
86 :
87 4058 : auto poLS = std::make_unique<OGRLineString>();
88 2029 : poLS->assignSpatialReference(poSRS);
89 2029 : int nCoordCount = 0;
90 4058 : for (const auto *poCoordField : apoCoordFields)
91 : {
92 2029 : const int nCoordFieldCount = poCoordField->GetRepeatCount();
93 2029 : nCoordCount += nCoordFieldCount;
94 : }
95 2029 : poLS->setNumPoints(nCoordCount);
96 2029 : int iPnt = 0;
97 4057 : for (const auto *poCoordField : apoCoordFields)
98 : {
99 2029 : const int nCoordFieldCount = poCoordField->GetRepeatCount();
100 10952 : for (int iPntThisField = 0; iPntThisField < nCoordFieldCount;
101 : ++iPntThisField)
102 : {
103 : auto poPoint = ReadPointGeometryInternal(
104 : poRecord, iRecord, nRecordID, iPntThisField, poSRS,
105 8924 : /* bIs3D = */ false, poCoordField, CRID_FIELD);
106 8924 : if (!poPoint)
107 1 : return nullptr;
108 8923 : poLS->setPoint(iPnt, poPoint.get());
109 8923 : ++iPnt;
110 : }
111 : }
112 :
113 2028 : const DDFField *poPTASField = poRecord->FindField(PTAS_FIELD);
114 2028 : if (poPTASField)
115 : {
116 2028 : const int nPTASMembers = poPTASField->GetRepeatCount();
117 2028 : constexpr const char *TOPI_SUBFIELD = "TOPI";
118 2028 : if (nPTASMembers == 1 || nPTASMembers == 2)
119 : {
120 4551 : for (int iPTAS = 0; iPTAS < nPTASMembers; ++iPTAS)
121 : {
122 : const auto GetIntSubfield =
123 7594 : [poRecord, poPTASField, iPTAS](const char *pszSubFieldName)
124 : {
125 7594 : return poRecord->GetIntSubfield(poPTASField,
126 7594 : pszSubFieldName, iPTAS);
127 2533 : };
128 :
129 2533 : const RecordName nRRNM = GetIntSubfield(RRNM_SUBFIELD);
130 2533 : constexpr RecordName nExpectedRRNM = RECORD_NAME_POINT;
131 2536 : if (nRRNM != nExpectedRRNM &&
132 3 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
133 : "Record index %d of CRID: Invalid value for RRNM "
134 : "subfield of %d instance of PTAS field: "
135 : "got %d, expected %d.",
136 : iRecord, iPTAS, static_cast<int>(nRRNM),
137 : static_cast<int>(nExpectedRRNM))))
138 : {
139 7 : return nullptr;
140 : }
141 :
142 2532 : const int nTOPI = GetIntSubfield(TOPI_SUBFIELD);
143 2532 : constexpr int TOPOLOGY_INDICATOR_BEGINNING_POINT = 1;
144 2532 : constexpr int TOPOLOGY_INDICATOR_END_POINT = 2;
145 2532 : constexpr int TOPOLOGY_INDICATOR_BEGINNING_AND_END_POINT = 3;
146 2532 : const int nExpectedTOPI =
147 : nPTASMembers == 1
148 2532 : ? TOPOLOGY_INDICATOR_BEGINNING_AND_END_POINT
149 : : iPTAS == 0 ? TOPOLOGY_INDICATOR_BEGINNING_POINT
150 : : TOPOLOGY_INDICATOR_END_POINT;
151 :
152 2541 : if (nTOPI != nExpectedTOPI &&
153 9 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
154 : "Record index %d of CRID: Invalid value for TOPI "
155 : "subfield of %d instance of PTAS field: "
156 : "got %d, expected %d.",
157 : iRecord, iPTAS, nTOPI, nExpectedTOPI)))
158 : {
159 3 : return nullptr;
160 : }
161 :
162 2529 : const int nRRID = GetIntSubfield(RRID_SUBFIELD);
163 : const auto poPointRecord =
164 2529 : m_oPointRecordIndex.FindRecord(nRRID);
165 2532 : if (!poPointRecord &&
166 3 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
167 : "Record index %d of CRID: No point record matching "
168 : "RRID=%d value of %d instance of PTAS field.",
169 : iRecord, nRRID, iPTAS)))
170 : {
171 1 : return nullptr;
172 : }
173 2528 : if (poPointRecord)
174 : {
175 2526 : if (nTOPI == TOPOLOGY_INDICATOR_BEGINNING_POINT ||
176 : nTOPI == TOPOLOGY_INDICATOR_BEGINNING_AND_END_POINT)
177 : {
178 2015 : if (poPointRecord->GetIntSubfield(C2IT_FIELD, 0,
179 : XCOO_SUBFIELD, 0) !=
180 2015 : poRecord->GetIntSubfield(C2IL_FIELD, 0,
181 4020 : XCOO_SUBFIELD, 0) ||
182 2005 : poPointRecord->GetIntSubfield(C2IT_FIELD, 0,
183 : YCOO_SUBFIELD, 0) !=
184 2005 : poRecord->GetIntSubfield(C2IL_FIELD, 0,
185 : YCOO_SUBFIELD, 0))
186 : {
187 15 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
188 : "Record index %d of CRID: Point record %d "
189 : "pointed by %d instance of PTAS field does "
190 : "not match first point of curve.",
191 : iRecord, nRRID, iPTAS)))
192 : {
193 1 : return nullptr;
194 : }
195 : }
196 : }
197 :
198 2525 : if (nTOPI == TOPOLOGY_INDICATOR_END_POINT ||
199 : nTOPI == TOPOLOGY_INDICATOR_BEGINNING_AND_END_POINT)
200 : {
201 2015 : if (poPointRecord->GetIntSubfield(C2IT_FIELD, 0,
202 : XCOO_SUBFIELD, 0) !=
203 2015 : poRecord->GetIntSubfield(C2IL_FIELD, 0,
204 : XCOO_SUBFIELD,
205 4021 : nCoordCount - 1) ||
206 2006 : poPointRecord->GetIntSubfield(C2IT_FIELD, 0,
207 : YCOO_SUBFIELD, 0) !=
208 2006 : poRecord->GetIntSubfield(C2IL_FIELD, 0,
209 : YCOO_SUBFIELD,
210 : nCoordCount - 1))
211 : {
212 12 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
213 : "Record index %d of CRID: Point record %d "
214 : "pointed by %d instance of PTAS field does "
215 : "not match end point of curve.",
216 : iRecord, nRRID, iPTAS)))
217 : {
218 1 : return nullptr;
219 : }
220 : }
221 : }
222 : }
223 2018 : }
224 : }
225 : else
226 : {
227 3 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
228 : "Record index %d of CRID: Invalid repeat count for "
229 : "PTAS field: got %d, expected 1 or 2.",
230 : iRecord, nPTASMembers)))
231 : {
232 1 : return nullptr;
233 : }
234 : }
235 : }
236 :
237 2020 : return poLS;
238 : }
239 :
240 : /************************************************************************/
241 : /* FillFeatureCurve() */
242 : /************************************************************************/
243 :
244 : /** Fill the content of the provided feature from the identified record
245 : * (of m_oCurveRecordIndex).
246 : */
247 539 : bool OGRS101Reader::FillFeatureCurve(const DDFRecordIndex &oIndex, int iRecord,
248 : OGRFeature &oFeature) const
249 : {
250 539 : const auto poRecord = oIndex.GetByIndex(iRecord);
251 539 : CPLAssert(poRecord);
252 :
253 : const OGRSpatialReference *poSRS =
254 539 : oFeature.GetDefnRef()->GetGeomFieldDefn(0)->GetSpatialRef();
255 : auto poLS =
256 1078 : ReadCurveGeometry(poRecord, iRecord, /* nRecordID = */ -1, poSRS);
257 539 : if (!poLS)
258 : {
259 11 : if (m_bStrict)
260 11 : return false; // error message already emitted
261 : }
262 : else
263 : {
264 528 : oFeature.SetGeometry(std::move(poLS));
265 : }
266 :
267 1056 : return FillFeatureAttributes(oIndex, iRecord, INAS_FIELD, oFeature) &&
268 528 : FillFeatureWithNonAttrAssocSubfields(poRecord, iRecord, INAS_FIELD,
269 528 : oFeature);
270 : }
|