Line data Source code
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 7 : OGRAVCBinLayer::OGRAVCBinLayer(OGRAVCBinDataSource *poDSIn,
26 7 : AVCE00Section *psSectionIn)
27 : : OGRAVCLayer(psSectionIn->eType, poDSIn), m_psSection(psSectionIn),
28 : hFile(nullptr), poArcLayer(nullptr), bNeedReset(false), hTable(nullptr),
29 7 : nTableBaseField(-1), nTableAttrIndex(-1), nNextFID(1)
30 : {
31 7 : SetupFeatureDefinition(m_psSection->pszName);
32 :
33 7 : szTableName[0] = '\0';
34 7 : if (m_psSection->eType == AVCFilePAL)
35 1 : snprintf(szTableName, sizeof(szTableName), "%s.PAT",
36 1 : poDS->GetCoverageName());
37 6 : else if (m_psSection->eType == AVCFileRPL)
38 0 : snprintf(szTableName, sizeof(szTableName), "%s.PAT%s",
39 0 : poDS->GetCoverageName(), m_psSection->pszName);
40 6 : else if (m_psSection->eType == AVCFileARC)
41 2 : snprintf(szTableName, sizeof(szTableName), "%s.AAT",
42 2 : poDS->GetCoverageName());
43 4 : else if (m_psSection->eType == AVCFileLAB)
44 : {
45 : AVCE00ReadPtr psInfo =
46 3 : static_cast<OGRAVCBinDataSource *>(poDS)->GetInfo();
47 :
48 6 : snprintf(szTableName, sizeof(szTableName), "%s.PAT",
49 3 : poDS->GetCoverageName());
50 :
51 41 : for (int iSection = 0; iSection < psInfo->numSections; iSection++)
52 : {
53 38 : if (psInfo->pasSections[iSection].eType == AVCFilePAL)
54 1 : nTableAttrIndex = poFeatureDefn->GetFieldIndex("PolyId");
55 : }
56 : }
57 :
58 7 : CheckSetupTable();
59 7 : }
60 :
61 : /************************************************************************/
62 : /* ~OGRAVCBinLayer() */
63 : /************************************************************************/
64 :
65 14 : OGRAVCBinLayer::~OGRAVCBinLayer()
66 :
67 : {
68 7 : OGRAVCBinLayer::ResetReading();
69 14 : }
70 :
71 : /************************************************************************/
72 : /* ResetReading() */
73 : /************************************************************************/
74 :
75 14 : void OGRAVCBinLayer::ResetReading()
76 :
77 : {
78 14 : if (hFile != nullptr)
79 : {
80 7 : AVCBinReadClose(hFile);
81 7 : hFile = nullptr;
82 : }
83 :
84 14 : bNeedReset = false;
85 14 : nNextFID = 1;
86 14 : m_bEOF = false;
87 :
88 14 : if (hTable != nullptr)
89 : {
90 4 : AVCBinReadClose(hTable);
91 4 : hTable = nullptr;
92 : }
93 14 : }
94 :
95 : /************************************************************************/
96 : /* GetFeature() */
97 : /************************************************************************/
98 :
99 209 : OGRFeature *OGRAVCBinLayer::GetFeature(GIntBig nFID)
100 :
101 : {
102 209 : 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 209 : if (hFile == nullptr)
109 : {
110 : AVCE00ReadPtr psInfo =
111 7 : static_cast<OGRAVCBinDataSource *>(poDS)->GetInfo();
112 :
113 14 : hFile = AVCBinReadOpen(psInfo->pszCoverPath, m_psSection->pszFilename,
114 7 : psInfo->eCoverType, m_psSection->eType,
115 : psInfo->psDBCSInfo);
116 7 : if (hFile == nullptr)
117 0 : return nullptr;
118 : }
119 :
120 : /* -------------------------------------------------------------------- */
121 : /* Read the raw feature - the SERIAL_ACCESS_FID fid is a special flag
122 : */
123 : /* indicating serial access. */
124 : /* -------------------------------------------------------------------- */
125 209 : void *pFeature = nullptr;
126 :
127 209 : if (nFID == SERIAL_ACCESS_FID)
128 : {
129 357 : while ((pFeature = AVCBinReadNextObject(hFile)) != nullptr &&
130 176 : !MatchesSpatialFilter(pFeature))
131 : {
132 0 : nNextFID++;
133 : }
134 : }
135 : else
136 : {
137 28 : bNeedReset = true;
138 28 : pFeature = AVCBinReadObject(hFile, (int)nFID);
139 : }
140 :
141 209 : if (pFeature == nullptr)
142 5 : return nullptr;
143 :
144 : /* -------------------------------------------------------------------- */
145 : /* Translate the feature. */
146 : /* -------------------------------------------------------------------- */
147 204 : OGRFeature *poFeature = TranslateFeature(pFeature);
148 204 : 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 204 : if (m_psSection->eType == AVCFileLAB)
156 : {
157 160 : if (nFID == SERIAL_ACCESS_FID)
158 160 : poFeature->SetFID(nNextFID++);
159 : else
160 0 : poFeature->SetFID(nFID);
161 : }
162 :
163 : /* -------------------------------------------------------------------- */
164 : /* If this is a polygon layer, try to assemble the arcs to form */
165 : /* the whole polygon geometry. */
166 : /* -------------------------------------------------------------------- */
167 204 : if (m_psSection->eType == AVCFilePAL || m_psSection->eType == AVCFileRPL)
168 8 : FormPolygonGeometry(poFeature, (AVCPal *)pFeature);
169 :
170 : /* -------------------------------------------------------------------- */
171 : /* If we have an attribute table, append the attributes now. */
172 : /* -------------------------------------------------------------------- */
173 204 : AppendTableFields(poFeature);
174 :
175 204 : return poFeature;
176 : }
177 :
178 : /************************************************************************/
179 : /* GetNextFeature() */
180 : /************************************************************************/
181 :
182 179 : OGRFeature *OGRAVCBinLayer::GetNextFeature()
183 :
184 : {
185 179 : if (m_bEOF)
186 0 : return nullptr;
187 :
188 179 : if (bNeedReset)
189 0 : ResetReading();
190 :
191 179 : OGRFeature *poFeature = GetFeature(SERIAL_ACCESS_FID);
192 :
193 : // Skip universe polygon.
194 185 : if (poFeature != nullptr && poFeature->GetFID() == 1 &&
195 6 : m_psSection->eType == AVCFilePAL)
196 : {
197 2 : OGRFeature::DestroyFeature(poFeature);
198 2 : poFeature = GetFeature(SERIAL_ACCESS_FID);
199 : }
200 :
201 353 : while (poFeature != nullptr &&
202 174 : ((m_poAttrQuery != nullptr && !m_poAttrQuery->Evaluate(poFeature)) ||
203 174 : !FilterGeometry(poFeature->GetGeometryRef())))
204 : {
205 0 : OGRFeature::DestroyFeature(poFeature);
206 0 : poFeature = GetFeature(SERIAL_ACCESS_FID);
207 : }
208 :
209 179 : if (poFeature == nullptr)
210 5 : m_bEOF = true;
211 :
212 179 : return poFeature;
213 : }
214 :
215 : /************************************************************************/
216 : /* TestCapability() */
217 : /************************************************************************/
218 :
219 0 : int OGRAVCBinLayer::TestCapability(const char *pszCap)
220 :
221 : {
222 0 : if (eSectionType == AVCFileARC && EQUAL(pszCap, OLCRandomRead))
223 0 : return TRUE;
224 :
225 0 : return OGRAVCLayer::TestCapability(pszCap);
226 : }
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 8 : bool OGRAVCBinLayer::FormPolygonGeometry(OGRFeature *poFeature, AVCPal *psPAL)
236 :
237 : {
238 : /* -------------------------------------------------------------------- */
239 : /* Try to find the corresponding ARC layer if not already */
240 : /* recorded. */
241 : /* -------------------------------------------------------------------- */
242 8 : if (poArcLayer == nullptr)
243 : {
244 5 : for (int i = 0; i < poDS->GetLayerCount(); i++)
245 : {
246 : OGRAVCBinLayer *poLayer =
247 4 : static_cast<OGRAVCBinLayer *>(poDS->GetLayer(i));
248 :
249 4 : if (poLayer->eSectionType == AVCFileARC)
250 1 : poArcLayer = poLayer;
251 : }
252 :
253 1 : if (poArcLayer == nullptr)
254 0 : return false;
255 : }
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 16 : OGRGeometryCollection oArcs;
262 :
263 38 : for (int iArc = 0; iArc < psPAL->numArcs; iArc++)
264 : {
265 30 : if (psPAL->pasArcs[iArc].nArcId == 0 ||
266 28 : psPAL->pasArcs[iArc].nArcId == INT_MIN)
267 : {
268 2 : continue;
269 : }
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 28 : if (psPAL->pasArcs[iArc].nAdjPoly == psPAL->nPolyId)
277 0 : continue;
278 :
279 : OGRFeature *poArc =
280 28 : poArcLayer->GetFeature(std::abs(psPAL->pasArcs[iArc].nArcId));
281 :
282 28 : if (poArc == nullptr)
283 0 : return false;
284 :
285 28 : if (poArc->GetGeometryRef() == nullptr)
286 0 : return false;
287 :
288 28 : oArcs.addGeometry(poArc->GetGeometryRef());
289 28 : OGRFeature::DestroyFeature(poArc);
290 : }
291 :
292 : OGRErr eErr;
293 8 : OGRGeometry *poPolygon = OGRGeometry::FromHandle(OGRBuildPolygonFromEdges(
294 : (OGRGeometryH)&oArcs, TRUE, FALSE, 0.0, &eErr));
295 8 : if (poPolygon != nullptr)
296 : {
297 8 : poPolygon->assignSpatialReference(GetSpatialRef());
298 8 : poFeature->SetGeometryDirectly(poPolygon);
299 : }
300 :
301 8 : return eErr == OGRERR_NONE;
302 : }
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 7 : bool OGRAVCBinLayer::CheckSetupTable()
313 :
314 : {
315 7 : if (szTableName[0] == '\0')
316 1 : return false;
317 :
318 : /* -------------------------------------------------------------------- */
319 : /* Scan for the indicated section. */
320 : /* -------------------------------------------------------------------- */
321 6 : AVCE00ReadPtr psInfo = static_cast<OGRAVCBinDataSource *>(poDS)->GetInfo();
322 :
323 6 : AVCE00Section *l_psSection = nullptr;
324 86 : for (int iSection = 0; iSection < psInfo->numSections; iSection++)
325 : {
326 160 : if (EQUAL(szTableName,
327 163 : CPLString(psInfo->pasSections[iSection].pszName).Trim()) &&
328 3 : psInfo->pasSections[iSection].eType == AVCFileTABLE)
329 3 : l_psSection = psInfo->pasSections + iSection;
330 : }
331 :
332 6 : if (l_psSection == nullptr)
333 : {
334 3 : szTableName[0] = '\0';
335 3 : return false;
336 : }
337 :
338 : /* -------------------------------------------------------------------- */
339 : /* Try opening the table. */
340 : /* -------------------------------------------------------------------- */
341 3 : hTable =
342 3 : AVCBinReadOpen(psInfo->pszInfoPath, szTableName, psInfo->eCoverType,
343 : AVCFileTABLE, psInfo->psDBCSInfo);
344 :
345 3 : if (hTable == nullptr)
346 : {
347 0 : szTableName[0] = '\0';
348 0 : return false;
349 : }
350 :
351 : /* -------------------------------------------------------------------- */
352 : /* Setup attributes. */
353 : /* -------------------------------------------------------------------- */
354 3 : nTableBaseField = poFeatureDefn->GetFieldCount();
355 :
356 3 : AppendTableDefinition(hTable->hdr.psTableDef);
357 :
358 : /* -------------------------------------------------------------------- */
359 : /* Close table so we don't have to many files open at once. */
360 : /* -------------------------------------------------------------------- */
361 3 : AVCBinReadClose(hTable);
362 :
363 3 : hTable = nullptr;
364 :
365 3 : return true;
366 : }
367 :
368 : /************************************************************************/
369 : /* AppendTableFields() */
370 : /************************************************************************/
371 :
372 204 : bool OGRAVCBinLayer::AppendTableFields(OGRFeature *poFeature)
373 :
374 : {
375 204 : AVCE00ReadPtr psInfo = static_cast<OGRAVCBinDataSource *>(poDS)->GetInfo();
376 :
377 204 : if (szTableName[0] == '\0')
378 36 : return false;
379 :
380 : /* -------------------------------------------------------------------- */
381 : /* Open the table if it is currently closed. */
382 : /* -------------------------------------------------------------------- */
383 168 : if (hTable == nullptr)
384 : {
385 4 : hTable =
386 4 : AVCBinReadOpen(psInfo->pszInfoPath, szTableName, psInfo->eCoverType,
387 : AVCFileTABLE, psInfo->psDBCSInfo);
388 : }
389 :
390 168 : 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 168 : const int nRecordId = nTableAttrIndex == -1
403 168 : ? static_cast<int>(poFeature->GetFID())
404 0 : : poFeature->GetFieldAsInteger(nTableAttrIndex);
405 :
406 168 : void *hRecord = AVCBinReadObject(hTable, nRecordId);
407 168 : if (hRecord == nullptr)
408 0 : return false;
409 :
410 : /* -------------------------------------------------------------------- */
411 : /* Translate it. */
412 : /* -------------------------------------------------------------------- */
413 336 : return TranslateTableFields(poFeature, nTableBaseField,
414 168 : hTable->hdr.psTableDef, (AVCField *)hRecord);
415 : }
|