/src/gdal/ogr/ogrsf_frmts/avc/ogravcbinlayer.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: OGR |
4 | | * Purpose: Implements OGRAVCBinLayer class. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2002, Frank Warmerdam <warmerdam@pobox.com> |
9 | | * |
10 | | * SPDX-License-Identifier: MIT |
11 | | ****************************************************************************/ |
12 | | |
13 | | #include "ogr_avc.h" |
14 | | #include "ogr_api.h" |
15 | | #include "cpl_conv.h" |
16 | | #include "cpl_string.h" |
17 | | |
18 | | #include <climits> |
19 | | #include <cstdlib> |
20 | | |
21 | | /************************************************************************/ |
22 | | /* OGRAVCBinLayer() */ |
23 | | /************************************************************************/ |
24 | | |
25 | | OGRAVCBinLayer::OGRAVCBinLayer(OGRAVCBinDataSource *poDSIn, |
26 | | AVCE00Section *psSectionIn) |
27 | 13.8k | : OGRAVCLayer(psSectionIn->eType, poDSIn), m_psSection(psSectionIn), |
28 | 13.8k | hFile(nullptr), poArcLayer(nullptr), bNeedReset(false), hTable(nullptr), |
29 | 13.8k | nTableBaseField(-1), nTableAttrIndex(-1), nNextFID(1) |
30 | 13.8k | { |
31 | 13.8k | SetupFeatureDefinition(m_psSection->pszName); |
32 | | |
33 | 13.8k | szTableName[0] = '\0'; |
34 | 13.8k | if (m_psSection->eType == AVCFilePAL) |
35 | 762 | snprintf(szTableName, sizeof(szTableName), "%s.PAT", |
36 | 762 | poDS->GetCoverageName()); |
37 | 13.0k | else if (m_psSection->eType == AVCFileRPL) |
38 | 5.31k | snprintf(szTableName, sizeof(szTableName), "%s.PAT%s", |
39 | 5.31k | poDS->GetCoverageName(), m_psSection->pszName); |
40 | 7.74k | else if (m_psSection->eType == AVCFileARC) |
41 | 708 | snprintf(szTableName, sizeof(szTableName), "%s.AAT", |
42 | 708 | poDS->GetCoverageName()); |
43 | 7.03k | else if (m_psSection->eType == AVCFileLAB) |
44 | 1.61k | { |
45 | 1.61k | AVCE00ReadPtr psInfo = |
46 | 1.61k | static_cast<OGRAVCBinDataSource *>(poDS)->GetInfo(); |
47 | | |
48 | 1.61k | snprintf(szTableName, sizeof(szTableName), "%s.PAT", |
49 | 1.61k | poDS->GetCoverageName()); |
50 | | |
51 | 28.6k | for (int iSection = 0; iSection < psInfo->numSections; iSection++) |
52 | 26.9k | { |
53 | 26.9k | if (psInfo->pasSections[iSection].eType == AVCFilePAL) |
54 | 510 | nTableAttrIndex = poFeatureDefn->GetFieldIndex("PolyId"); |
55 | 26.9k | } |
56 | 1.61k | } |
57 | | |
58 | 13.8k | CheckSetupTable(); |
59 | 13.8k | } |
60 | | |
61 | | /************************************************************************/ |
62 | | /* ~OGRAVCBinLayer() */ |
63 | | /************************************************************************/ |
64 | | |
65 | | OGRAVCBinLayer::~OGRAVCBinLayer() |
66 | | |
67 | 13.8k | { |
68 | 13.8k | OGRAVCBinLayer::ResetReading(); |
69 | 13.8k | } |
70 | | |
71 | | /************************************************************************/ |
72 | | /* ResetReading() */ |
73 | | /************************************************************************/ |
74 | | |
75 | | void OGRAVCBinLayer::ResetReading() |
76 | | |
77 | 13.8k | { |
78 | 13.8k | if (hFile != nullptr) |
79 | 7.87k | { |
80 | 7.87k | AVCBinReadClose(hFile); |
81 | 7.87k | hFile = nullptr; |
82 | 7.87k | } |
83 | | |
84 | 13.8k | bNeedReset = false; |
85 | 13.8k | nNextFID = 1; |
86 | 13.8k | m_bEOF = false; |
87 | | |
88 | 13.8k | if (hTable != nullptr) |
89 | 1.86k | { |
90 | 1.86k | AVCBinReadClose(hTable); |
91 | 1.86k | hTable = nullptr; |
92 | 1.86k | } |
93 | 13.8k | } |
94 | | |
95 | | /************************************************************************/ |
96 | | /* GetFeature() */ |
97 | | /************************************************************************/ |
98 | | |
99 | | OGRFeature *OGRAVCBinLayer::GetFeature(GIntBig nFID) |
100 | | |
101 | 211k | { |
102 | 211k | if (!CPL_INT64_FITS_ON_INT32(nFID)) |
103 | 0 | return nullptr; |
104 | | |
105 | | /* -------------------------------------------------------------------- */ |
106 | | /* If we haven't started yet, open the file now. */ |
107 | | /* -------------------------------------------------------------------- */ |
108 | 211k | if (hFile == nullptr) |
109 | 8.77k | { |
110 | 8.77k | AVCE00ReadPtr psInfo = |
111 | 8.77k | static_cast<OGRAVCBinDataSource *>(poDS)->GetInfo(); |
112 | | |
113 | 8.77k | hFile = AVCBinReadOpen(psInfo->pszCoverPath, m_psSection->pszFilename, |
114 | 8.77k | psInfo->eCoverType, m_psSection->eType, |
115 | 8.77k | psInfo->psDBCSInfo); |
116 | 8.77k | if (hFile == nullptr) |
117 | 899 | return nullptr; |
118 | 8.77k | } |
119 | | |
120 | | /* -------------------------------------------------------------------- */ |
121 | | /* Read the raw feature - the SERIAL_ACCESS_FID fid is a special flag |
122 | | */ |
123 | | /* indicating serial access. */ |
124 | | /* -------------------------------------------------------------------- */ |
125 | 210k | void *pFeature = nullptr; |
126 | | |
127 | 210k | if (nFID == SERIAL_ACCESS_FID) |
128 | 192k | { |
129 | 192k | while ((pFeature = AVCBinReadNextObject(hFile)) != nullptr && |
130 | 192k | !MatchesSpatialFilter(pFeature)) |
131 | 0 | { |
132 | 0 | nNextFID++; |
133 | 0 | } |
134 | 192k | } |
135 | 18.0k | else |
136 | 18.0k | { |
137 | 18.0k | bNeedReset = true; |
138 | 18.0k | pFeature = AVCBinReadObject(hFile, (int)nFID); |
139 | 18.0k | } |
140 | | |
141 | 210k | if (pFeature == nullptr) |
142 | 17.0k | return nullptr; |
143 | | |
144 | | /* -------------------------------------------------------------------- */ |
145 | | /* Translate the feature. */ |
146 | | /* -------------------------------------------------------------------- */ |
147 | 193k | OGRFeature *poFeature = TranslateFeature(pFeature); |
148 | 193k | if (poFeature == nullptr) |
149 | 0 | return nullptr; |
150 | | |
151 | | /* -------------------------------------------------------------------- */ |
152 | | /* LAB's we have to assign the FID to directly, since it */ |
153 | | /* doesn't seem to be stored in the file structure. */ |
154 | | /* -------------------------------------------------------------------- */ |
155 | 193k | if (m_psSection->eType == AVCFileLAB) |
156 | 130k | { |
157 | 130k | if (nFID == SERIAL_ACCESS_FID) |
158 | 130k | poFeature->SetFID(nNextFID++); |
159 | 0 | else |
160 | 0 | poFeature->SetFID(nFID); |
161 | 130k | } |
162 | | |
163 | | /* -------------------------------------------------------------------- */ |
164 | | /* If this is a polygon layer, try to assemble the arcs to form */ |
165 | | /* the whole polygon geometry. */ |
166 | | /* -------------------------------------------------------------------- */ |
167 | 193k | if (m_psSection->eType == AVCFilePAL || m_psSection->eType == AVCFileRPL) |
168 | 24.5k | FormPolygonGeometry(poFeature, (AVCPal *)pFeature); |
169 | | |
170 | | /* -------------------------------------------------------------------- */ |
171 | | /* If we have an attribute table, append the attributes now. */ |
172 | | /* -------------------------------------------------------------------- */ |
173 | 193k | AppendTableFields(poFeature); |
174 | | |
175 | 193k | return poFeature; |
176 | 193k | } |
177 | | |
178 | | /************************************************************************/ |
179 | | /* GetNextFeature() */ |
180 | | /************************************************************************/ |
181 | | |
182 | | OGRFeature *OGRAVCBinLayer::GetNextFeature() |
183 | | |
184 | 192k | { |
185 | 192k | if (m_bEOF) |
186 | 0 | return nullptr; |
187 | | |
188 | 192k | if (bNeedReset) |
189 | 0 | ResetReading(); |
190 | | |
191 | 192k | OGRFeature *poFeature = GetFeature(SERIAL_ACCESS_FID); |
192 | | |
193 | | // Skip universe polygon. |
194 | 192k | if (poFeature != nullptr && poFeature->GetFID() == 1 && |
195 | 192k | m_psSection->eType == AVCFilePAL) |
196 | 6 | { |
197 | 6 | OGRFeature::DestroyFeature(poFeature); |
198 | 6 | poFeature = GetFeature(SERIAL_ACCESS_FID); |
199 | 6 | } |
200 | | |
201 | 192k | while (poFeature != nullptr && |
202 | 192k | ((m_poAttrQuery != nullptr && !m_poAttrQuery->Evaluate(poFeature)) || |
203 | 184k | !FilterGeometry(poFeature->GetGeometryRef()))) |
204 | 0 | { |
205 | 0 | OGRFeature::DestroyFeature(poFeature); |
206 | 0 | poFeature = GetFeature(SERIAL_ACCESS_FID); |
207 | 0 | } |
208 | | |
209 | 192k | if (poFeature == nullptr) |
210 | 7.87k | m_bEOF = true; |
211 | | |
212 | 192k | return poFeature; |
213 | 192k | } |
214 | | |
215 | | /************************************************************************/ |
216 | | /* TestCapability() */ |
217 | | /************************************************************************/ |
218 | | |
219 | | int OGRAVCBinLayer::TestCapability(const char *pszCap) |
220 | | |
221 | 0 | { |
222 | 0 | if (eSectionType == AVCFileARC && EQUAL(pszCap, OLCRandomRead)) |
223 | 0 | return TRUE; |
224 | | |
225 | 0 | return OGRAVCLayer::TestCapability(pszCap); |
226 | 0 | } |
227 | | |
228 | | /************************************************************************/ |
229 | | /* FormPolygonGeometry() */ |
230 | | /* */ |
231 | | /* Collect all the arcs forming edges to this polygon and form */ |
232 | | /* them into the appropriate OGR geometry on the target feature. */ |
233 | | /************************************************************************/ |
234 | | |
235 | | bool OGRAVCBinLayer::FormPolygonGeometry(OGRFeature *poFeature, AVCPal *psPAL) |
236 | | |
237 | 24.5k | { |
238 | | /* -------------------------------------------------------------------- */ |
239 | | /* Try to find the corresponding ARC layer if not already */ |
240 | | /* recorded. */ |
241 | | /* -------------------------------------------------------------------- */ |
242 | 24.5k | if (poArcLayer == nullptr) |
243 | 3.94k | { |
244 | 32.1k | for (int i = 0; i < poDS->GetLayerCount(); i++) |
245 | 28.2k | { |
246 | 28.2k | OGRAVCBinLayer *poLayer = |
247 | 28.2k | static_cast<OGRAVCBinLayer *>(poDS->GetLayer(i)); |
248 | | |
249 | 28.2k | if (poLayer->eSectionType == AVCFileARC) |
250 | 779 | poArcLayer = poLayer; |
251 | 28.2k | } |
252 | | |
253 | 3.94k | if (poArcLayer == nullptr) |
254 | 3.17k | return false; |
255 | 3.94k | } |
256 | | |
257 | | /* -------------------------------------------------------------------- */ |
258 | | /* Read all the arcs related to this polygon, making a working */ |
259 | | /* copy of them since the one returned by AVC is temporary. */ |
260 | | /* -------------------------------------------------------------------- */ |
261 | 21.3k | OGRGeometryCollection oArcs; |
262 | | |
263 | 44.0k | for (int iArc = 0; iArc < psPAL->numArcs; iArc++) |
264 | 32.7k | { |
265 | 32.7k | if (psPAL->pasArcs[iArc].nArcId == 0 || |
266 | 32.7k | psPAL->pasArcs[iArc].nArcId == INT_MIN) |
267 | 8.79k | { |
268 | 8.79k | continue; |
269 | 8.79k | } |
270 | | |
271 | | // If the other side of the line is the same polygon then this |
272 | | // arc is a "bridge" arc and can be discarded. If we don't discard |
273 | | // it, then we should double it as bridge arcs seem to only appear |
274 | | // once. But by discarding it we ensure a multi-ring polygon will be |
275 | | // properly formed. |
276 | 23.9k | if (psPAL->pasArcs[iArc].nAdjPoly == psPAL->nPolyId) |
277 | 4.97k | continue; |
278 | | |
279 | 18.9k | OGRFeature *poArc = |
280 | 18.9k | poArcLayer->GetFeature(std::abs(psPAL->pasArcs[iArc].nArcId)); |
281 | | |
282 | 18.9k | if (poArc == nullptr) |
283 | 10.0k | return false; |
284 | | |
285 | 8.94k | if (poArc->GetGeometryRef() == nullptr) |
286 | 0 | return false; |
287 | | |
288 | 8.94k | oArcs.addGeometry(poArc->GetGeometryRef()); |
289 | 8.94k | OGRFeature::DestroyFeature(poArc); |
290 | 8.94k | } |
291 | | |
292 | 11.3k | OGRErr eErr; |
293 | 11.3k | OGRGeometry *poPolygon = OGRGeometry::FromHandle(OGRBuildPolygonFromEdges( |
294 | 11.3k | (OGRGeometryH)&oArcs, TRUE, FALSE, 0.0, &eErr)); |
295 | 11.3k | if (poPolygon != nullptr) |
296 | 11.3k | { |
297 | 11.3k | poPolygon->assignSpatialReference(GetSpatialRef()); |
298 | 11.3k | poFeature->SetGeometryDirectly(poPolygon); |
299 | 11.3k | } |
300 | | |
301 | 11.3k | return eErr == OGRERR_NONE; |
302 | 21.3k | } |
303 | | |
304 | | /************************************************************************/ |
305 | | /* CheckSetupTable() */ |
306 | | /* */ |
307 | | /* Check if the named table exists, and if so, setup access to */ |
308 | | /* it (open it), and add its fields to the feature class */ |
309 | | /* definition. */ |
310 | | /************************************************************************/ |
311 | | |
312 | | bool OGRAVCBinLayer::CheckSetupTable() |
313 | | |
314 | 13.8k | { |
315 | 13.8k | if (szTableName[0] == '\0') |
316 | 5.42k | return false; |
317 | | |
318 | | /* -------------------------------------------------------------------- */ |
319 | | /* Scan for the indicated section. */ |
320 | | /* -------------------------------------------------------------------- */ |
321 | 8.40k | AVCE00ReadPtr psInfo = static_cast<OGRAVCBinDataSource *>(poDS)->GetInfo(); |
322 | | |
323 | 8.40k | AVCE00Section *l_psSection = nullptr; |
324 | 408k | for (int iSection = 0; iSection < psInfo->numSections; iSection++) |
325 | 399k | { |
326 | 399k | if (EQUAL(szTableName, |
327 | 399k | CPLString(psInfo->pasSections[iSection].pszName).Trim()) && |
328 | 399k | psInfo->pasSections[iSection].eType == AVCFileTABLE) |
329 | 3.36k | l_psSection = psInfo->pasSections + iSection; |
330 | 399k | } |
331 | | |
332 | 8.40k | if (l_psSection == nullptr) |
333 | 5.14k | { |
334 | 5.14k | szTableName[0] = '\0'; |
335 | 5.14k | return false; |
336 | 5.14k | } |
337 | | |
338 | | /* -------------------------------------------------------------------- */ |
339 | | /* Try opening the table. */ |
340 | | /* -------------------------------------------------------------------- */ |
341 | 3.26k | hTable = |
342 | 3.26k | AVCBinReadOpen(psInfo->pszInfoPath, szTableName, psInfo->eCoverType, |
343 | 3.26k | AVCFileTABLE, psInfo->psDBCSInfo); |
344 | | |
345 | 3.26k | if (hTable == nullptr) |
346 | 431 | { |
347 | 431 | szTableName[0] = '\0'; |
348 | 431 | return false; |
349 | 431 | } |
350 | | |
351 | | /* -------------------------------------------------------------------- */ |
352 | | /* Setup attributes. */ |
353 | | /* -------------------------------------------------------------------- */ |
354 | 2.83k | nTableBaseField = poFeatureDefn->GetFieldCount(); |
355 | | |
356 | 2.83k | AppendTableDefinition(hTable->hdr.psTableDef); |
357 | | |
358 | | /* -------------------------------------------------------------------- */ |
359 | | /* Close table so we don't have to many files open at once. */ |
360 | | /* -------------------------------------------------------------------- */ |
361 | 2.83k | AVCBinReadClose(hTable); |
362 | | |
363 | 2.83k | hTable = nullptr; |
364 | | |
365 | 2.83k | return true; |
366 | 3.26k | } |
367 | | |
368 | | /************************************************************************/ |
369 | | /* AppendTableFields() */ |
370 | | /************************************************************************/ |
371 | | |
372 | | bool OGRAVCBinLayer::AppendTableFields(OGRFeature *poFeature) |
373 | | |
374 | 193k | { |
375 | 193k | AVCE00ReadPtr psInfo = static_cast<OGRAVCBinDataSource *>(poDS)->GetInfo(); |
376 | | |
377 | 193k | if (szTableName[0] == '\0') |
378 | 81.6k | return false; |
379 | | |
380 | | /* -------------------------------------------------------------------- */ |
381 | | /* Open the table if it is currently closed. */ |
382 | | /* -------------------------------------------------------------------- */ |
383 | 111k | if (hTable == nullptr) |
384 | 1.86k | { |
385 | 1.86k | hTable = |
386 | 1.86k | AVCBinReadOpen(psInfo->pszInfoPath, szTableName, psInfo->eCoverType, |
387 | 1.86k | AVCFileTABLE, psInfo->psDBCSInfo); |
388 | 1.86k | } |
389 | | |
390 | 111k | if (hTable == nullptr) |
391 | 0 | return false; |
392 | | |
393 | | /* -------------------------------------------------------------------- */ |
394 | | /* Read the info record. */ |
395 | | /* */ |
396 | | /* We usually assume the FID of the feature is the key but in a */ |
397 | | /* polygon coverage we need to use the PolyId attribute of LAB */ |
398 | | /* features to lookup the related attributes. In this case */ |
399 | | /* nTableAttrIndex will already be setup to refer to the */ |
400 | | /* PolyId field. */ |
401 | | /* -------------------------------------------------------------------- */ |
402 | 111k | const int nRecordId = nTableAttrIndex == -1 |
403 | 111k | ? static_cast<int>(poFeature->GetFID()) |
404 | 111k | : poFeature->GetFieldAsInteger(nTableAttrIndex); |
405 | | |
406 | 111k | void *hRecord = AVCBinReadObject(hTable, nRecordId); |
407 | 111k | if (hRecord == nullptr) |
408 | 44.4k | return false; |
409 | | |
410 | | /* -------------------------------------------------------------------- */ |
411 | | /* Translate it. */ |
412 | | /* -------------------------------------------------------------------- */ |
413 | 66.9k | return TranslateTableFields(poFeature, nTableBaseField, |
414 | 66.9k | hTable->hdr.psTableDef, (AVCField *)hRecord); |
415 | 111k | } |