Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: NTF Translator
4 : * Purpose: NTFFileReader class implementation.
5 : * Author: Frank Warmerdam, warmerdam@pobox.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 1999, Frank Warmerdam
9 : * Copyright (c) 2013, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include <stdarg.h>
15 : #include "ntf.h"
16 : #include "cpl_conv.h"
17 : #include "cpl_string.h"
18 : #include "ogr_api.h"
19 :
20 : #include <algorithm>
21 :
22 : #define DIGIT_ZERO '0'
23 :
24 : static int DefaultNTFRecordGrouper(NTFFileReader *, NTFRecord **, NTFRecord *);
25 :
26 : /************************************************************************/
27 : /* NTFFileReader */
28 : /************************************************************************/
29 :
30 0 : NTFFileReader::NTFFileReader(OGRNTFDataSource *poDataSource)
31 : : pszFilename(nullptr), poDS(poDataSource), fp(nullptr), nFCCount(0),
32 : papszFCNum(nullptr), papszFCName(nullptr), nAttCount(0),
33 : pasAttDesc(nullptr), pszTileName(nullptr), nCoordWidth(6), nZWidth(6),
34 : nNTFLevel(0), dfXYMult(1.0), dfZMult(1.0), dfXOrigin(0), dfYOrigin(0),
35 : dfTileXSize(0), dfTileYSize(0), dfScale(0.0), dfPaperToGround(0.0),
36 : nStartPos(0), nPreSavedPos(0), nPostSavedPos(0), poSavedRecord(nullptr),
37 : nSavedFeatureId(1), nBaseFeatureId(1), nFeatureCount(-1),
38 : pszProduct(nullptr), pszPVName(nullptr), nProduct(NPC_UNKNOWN),
39 : pfnRecordGrouper(DefaultNTFRecordGrouper), bIndexBuilt(FALSE),
40 : bIndexNeeded(FALSE), nRasterXSize(1), nRasterYSize(1), nRasterDataType(1),
41 : poRasterLayer(nullptr), panColumnOffset(nullptr), bCacheLines(TRUE),
42 0 : nLineCacheSize(0), papoLineCache(nullptr)
43 : {
44 0 : apoCGroup[0] = nullptr;
45 0 : apoCGroup[1] = nullptr;
46 0 : apoCGroup[MAX_REC_GROUP] = nullptr;
47 0 : memset(adfGeoTransform, 0, sizeof(adfGeoTransform));
48 0 : memset(apoTypeTranslation, 0, sizeof(apoTypeTranslation));
49 0 : for (int i = 0; i < 100; i++)
50 : {
51 0 : anIndexSize[i] = 0;
52 0 : apapoRecordIndex[i] = nullptr;
53 : }
54 0 : if (poDS->GetOption("CACHE_LINES") != nullptr &&
55 0 : EQUAL(poDS->GetOption("CACHE_LINES"), "OFF"))
56 0 : bCacheLines = FALSE;
57 0 : }
58 :
59 : /************************************************************************/
60 : /* ~NTFFileReader() */
61 : /************************************************************************/
62 :
63 0 : NTFFileReader::~NTFFileReader()
64 :
65 : {
66 0 : CacheClean();
67 0 : DestroyIndex();
68 0 : ClearDefs();
69 0 : CPLFree(pszFilename);
70 0 : CPLFree(panColumnOffset);
71 0 : }
72 :
73 : /************************************************************************/
74 : /* SetBaseFID() */
75 : /************************************************************************/
76 :
77 0 : void NTFFileReader::SetBaseFID(long nNewBase)
78 :
79 : {
80 0 : CPLAssert(nSavedFeatureId == 1);
81 :
82 0 : nBaseFeatureId = nNewBase;
83 0 : nSavedFeatureId = nBaseFeatureId;
84 0 : }
85 :
86 : /************************************************************************/
87 : /* ClearDefs() */
88 : /* */
89 : /* Clear attribute definitions and feature classes. All the */
90 : /* stuff that would have to be cleaned up by Open(), and the */
91 : /* destructor. */
92 : /************************************************************************/
93 :
94 0 : void NTFFileReader::ClearDefs()
95 :
96 : {
97 0 : Close();
98 :
99 0 : ClearCGroup();
100 :
101 0 : CSLDestroy(papszFCNum);
102 0 : papszFCNum = nullptr;
103 0 : CSLDestroy(papszFCName);
104 0 : papszFCName = nullptr;
105 0 : nFCCount = 0;
106 :
107 0 : for (int i = 0; i < nAttCount; i++)
108 : {
109 0 : if (pasAttDesc[i].poCodeList != nullptr)
110 0 : delete pasAttDesc[i].poCodeList;
111 : }
112 :
113 0 : CPLFree(pasAttDesc);
114 0 : nAttCount = 0;
115 0 : pasAttDesc = nullptr;
116 :
117 0 : CPLFree(pszProduct);
118 0 : pszProduct = nullptr;
119 :
120 0 : CPLFree(pszPVName);
121 0 : pszPVName = nullptr;
122 :
123 0 : CPLFree(pszTileName);
124 0 : pszTileName = nullptr;
125 0 : }
126 :
127 : /************************************************************************/
128 : /* Close() */
129 : /* */
130 : /* Close the file, but don't wipe out our knowledge about this */
131 : /* file. */
132 : /************************************************************************/
133 :
134 0 : void NTFFileReader::Close()
135 :
136 : {
137 0 : if (poSavedRecord != nullptr)
138 0 : delete poSavedRecord;
139 0 : poSavedRecord = nullptr;
140 :
141 0 : nPreSavedPos = nPostSavedPos = 0;
142 0 : nSavedFeatureId = nBaseFeatureId;
143 0 : if (fp != nullptr)
144 : {
145 0 : VSIFCloseL(fp);
146 0 : fp = nullptr;
147 : }
148 :
149 0 : CacheClean();
150 0 : }
151 :
152 : /************************************************************************/
153 : /* Open() */
154 : /************************************************************************/
155 :
156 0 : int NTFFileReader::Open(const char *pszFilenameIn)
157 :
158 : {
159 0 : if (pszFilenameIn != nullptr)
160 : {
161 0 : ClearDefs();
162 :
163 0 : CPLFree(pszFilename);
164 0 : pszFilename = CPLStrdup(pszFilenameIn);
165 : }
166 : else
167 0 : Close();
168 :
169 : /* -------------------------------------------------------------------- */
170 : /* Open the file. */
171 : /* -------------------------------------------------------------------- */
172 0 : fp = VSIFOpenL(pszFilename, "rb");
173 :
174 : // notdef: we should likely issue a proper CPL error message based
175 : // based on errno here.
176 0 : if (fp == nullptr)
177 : {
178 0 : CPLError(CE_Failure, CPLE_OpenFailed,
179 : "Unable to open file `%s' for read access.\n", pszFilename);
180 0 : return FALSE;
181 : }
182 :
183 : /* -------------------------------------------------------------------- */
184 : /* If we are just reopening an existing file we will just scan */
185 : /* past the section header ... no need to reform all the definitions.*/
186 : /* -------------------------------------------------------------------- */
187 0 : if (pszFilenameIn == nullptr)
188 : {
189 0 : NTFRecord *poRecord = nullptr;
190 :
191 0 : for (poRecord = new NTFRecord(fp);
192 0 : poRecord->GetType() != NRT_VTR && poRecord->GetType() != NRT_SHR;
193 0 : poRecord = new NTFRecord(fp))
194 : {
195 0 : delete poRecord;
196 : }
197 :
198 0 : delete poRecord;
199 :
200 0 : return TRUE;
201 : }
202 :
203 : /* -------------------------------------------------------------------- */
204 : /* Read the first record, and verify it is a proper volume header. */
205 : /* -------------------------------------------------------------------- */
206 0 : NTFRecord oVHR(fp);
207 :
208 0 : if (oVHR.GetType() != NRT_VHR)
209 : {
210 0 : CPLError(CE_Failure, CPLE_AppDefined,
211 : "File `%s' appears to not be a UK NTF file.\n", pszFilename);
212 0 : return FALSE;
213 : }
214 :
215 0 : nNTFLevel = atoi(oVHR.GetField(57, 57));
216 0 : if (!(nNTFLevel >= 1 && nNTFLevel <= 5))
217 : {
218 0 : CPLError(CE_Failure, CPLE_AppDefined, "Invalid value : nNTFLevel = %d",
219 : nNTFLevel);
220 0 : return FALSE;
221 : }
222 :
223 : /* -------------------------------------------------------------------- */
224 : /* Read records till we get the section header. */
225 : /* -------------------------------------------------------------------- */
226 0 : NTFRecord *poRecord = nullptr;
227 :
228 0 : for (poRecord = new NTFRecord(fp);
229 0 : poRecord->GetType() != NRT_VTR && poRecord->GetType() != NRT_SHR;
230 0 : poRecord = new NTFRecord(fp))
231 : {
232 : /* --------------------------------------------------------------------
233 : */
234 : /* Handle feature class name records. */
235 : /* --------------------------------------------------------------------
236 : */
237 0 : if (poRecord->GetType() == NRT_FCR && poRecord->GetLength() >= 37)
238 : {
239 0 : nFCCount++;
240 :
241 0 : papszFCNum = CSLAddString(papszFCNum, poRecord->GetField(3, 6));
242 :
243 0 : CPLString osFCName;
244 0 : const char *pszData = poRecord->GetData();
245 :
246 : // CODE_COM
247 0 : int iChar = 15;
248 0 : for (; pszData[iChar] == ' ' && iChar > 5; iChar--)
249 : {
250 : }
251 :
252 0 : if (iChar > 6)
253 0 : osFCName += poRecord->GetField(7, iChar + 1);
254 :
255 : // STCLASS
256 0 : for (iChar = 35; pszData[iChar] == ' ' && iChar > 15; iChar--)
257 : {
258 : }
259 :
260 0 : if (iChar > 15)
261 : {
262 0 : if (!osFCName.empty())
263 0 : osFCName += " : ";
264 0 : osFCName += poRecord->GetField(17, iChar + 1);
265 : }
266 :
267 : // FEATDES
268 0 : for (iChar = 36; pszData[iChar] != '\0' && pszData[iChar] != '\\';
269 : iChar++)
270 : {
271 : }
272 :
273 0 : if (iChar > 37)
274 : {
275 0 : if (!osFCName.empty())
276 0 : osFCName += " : ";
277 0 : osFCName += poRecord->GetField(37, iChar);
278 : }
279 :
280 0 : papszFCName = CSLAddString(papszFCName, osFCName);
281 : }
282 :
283 : /* --------------------------------------------------------------------
284 : */
285 : /* Handle attribute description records. */
286 : /* --------------------------------------------------------------------
287 : */
288 0 : else if (poRecord->GetType() == NRT_ADR)
289 : {
290 0 : nAttCount++;
291 :
292 0 : pasAttDesc = static_cast<NTFAttDesc *>(
293 0 : CPLRealloc(pasAttDesc, sizeof(NTFAttDesc) * nAttCount));
294 0 : memset(&pasAttDesc[nAttCount - 1], 0, sizeof(NTFAttDesc));
295 :
296 0 : if (!ProcessAttDesc(poRecord, pasAttDesc + nAttCount - 1))
297 0 : nAttCount--;
298 : }
299 :
300 : /* --------------------------------------------------------------------
301 : */
302 : /* Handle attribute description records. */
303 : /* --------------------------------------------------------------------
304 : */
305 0 : else if (poRecord->GetType() == NRT_CODELIST)
306 : {
307 0 : NTFCodeList *poCodeList = new NTFCodeList(poRecord);
308 0 : NTFAttDesc *psAttDesc = GetAttDesc(poCodeList->szValType);
309 0 : if (psAttDesc == nullptr)
310 : {
311 0 : CPLDebug("NTF", "Got CODELIST for %s without ATTDESC.",
312 0 : poCodeList->szValType);
313 0 : delete poCodeList;
314 : }
315 0 : else if (psAttDesc->poCodeList != nullptr)
316 : {
317 : // Should not happen on sane files.
318 0 : delete poCodeList;
319 : }
320 : else
321 : {
322 0 : psAttDesc->poCodeList = poCodeList;
323 : }
324 : }
325 :
326 : /* --------------------------------------------------------------------
327 : */
328 : /* Handle database header record. */
329 : /* --------------------------------------------------------------------
330 : */
331 0 : else if (poRecord->GetType() == NRT_DHR && pszProduct == nullptr)
332 : {
333 0 : pszProduct = CPLStrdup(poRecord->GetField(3, 22));
334 0 : for (int iChar = static_cast<int>(strlen(pszProduct)) - 1;
335 0 : iChar > 0 && pszProduct[iChar] == ' ';
336 0 : pszProduct[iChar--] = '\0')
337 : {
338 : }
339 :
340 0 : pszPVName = CPLStrdup(poRecord->GetField(76 + 3, 76 + 22));
341 0 : for (int iChar = static_cast<int>(strlen(pszPVName)) - 1;
342 0 : iChar > 0 && pszPVName[iChar] == ' ';
343 0 : pszPVName[iChar--] = '\0')
344 : {
345 : }
346 : }
347 :
348 0 : delete poRecord;
349 : }
350 :
351 : /* -------------------------------------------------------------------- */
352 : /* Did we fall off the end without finding what we were looking */
353 : /* for? */
354 : /* -------------------------------------------------------------------- */
355 0 : if (poRecord->GetType() == NRT_VTR)
356 : {
357 0 : delete poRecord;
358 0 : CPLError(CE_Failure, CPLE_AppDefined,
359 : "Could not find section header record in %s.\n", pszFilename);
360 0 : return FALSE;
361 : }
362 :
363 0 : if (pszProduct == nullptr)
364 : {
365 0 : delete poRecord;
366 0 : CPLError(CE_Failure, CPLE_AppDefined,
367 : "Could not find product type in %s.\n", pszFilename);
368 0 : return FALSE;
369 : }
370 :
371 : /* -------------------------------------------------------------------- */
372 : /* Classify the product type. */
373 : /* -------------------------------------------------------------------- */
374 0 : if (STARTS_WITH_CI(pszProduct, "LAND-LINE") && strlen(pszPVName) > 5 &&
375 0 : CPLAtof(pszPVName + 5) < 1.3)
376 0 : nProduct = NPC_LANDLINE;
377 0 : else if (STARTS_WITH_CI(pszProduct, "LAND-LINE"))
378 0 : nProduct = NPC_LANDLINE99;
379 0 : else if (EQUAL(pszProduct, "OS_LANDRANGER_CONT")) // Panorama
380 0 : nProduct = NPC_LANDRANGER_CONT;
381 0 : else if (EQUAL(pszProduct, "L-F_PROFILE_CON")) // Panorama
382 0 : nProduct = NPC_LANDFORM_PROFILE_CONT;
383 0 : else if (STARTS_WITH_CI(pszProduct, "Strategi"))
384 0 : nProduct = NPC_STRATEGI;
385 0 : else if (STARTS_WITH_CI(pszProduct, "Meridian_02"))
386 0 : nProduct = NPC_MERIDIAN2;
387 0 : else if (STARTS_WITH_CI(pszProduct, "Meridian_01"))
388 0 : nProduct = NPC_MERIDIAN;
389 0 : else if (EQUAL(pszProduct, NTF_BOUNDARYLINE) &&
390 0 : STARTS_WITH_CI(pszPVName, "A10N_FC"))
391 0 : nProduct = NPC_BOUNDARYLINE;
392 0 : else if (EQUAL(pszProduct, NTF_BOUNDARYLINE) &&
393 0 : STARTS_WITH_CI(pszPVName, "A20N_FC"))
394 0 : nProduct = NPC_BL2000;
395 0 : else if (STARTS_WITH_CI(pszProduct, "BaseData.GB"))
396 0 : nProduct = NPC_BASEDATA;
397 0 : else if (STARTS_WITH_CI(pszProduct, "OSCAR_ASSET"))
398 0 : nProduct = NPC_OSCAR_ASSET;
399 0 : else if (STARTS_WITH_CI(pszProduct, "OSCAR_TRAFF"))
400 0 : nProduct = NPC_OSCAR_TRAFFIC;
401 0 : else if (STARTS_WITH_CI(pszProduct, "OSCAR_ROUTE"))
402 0 : nProduct = NPC_OSCAR_ROUTE;
403 0 : else if (STARTS_WITH_CI(pszProduct, "OSCAR_NETWO"))
404 0 : nProduct = NPC_OSCAR_NETWORK;
405 0 : else if (STARTS_WITH_CI(pszProduct, "ADDRESS_POI"))
406 0 : nProduct = NPC_ADDRESS_POINT;
407 0 : else if (STARTS_WITH_CI(pszProduct, "CODE_POINT"))
408 : {
409 0 : if (GetAttDesc("RH") == nullptr)
410 0 : nProduct = NPC_CODE_POINT;
411 : else
412 0 : nProduct = NPC_CODE_POINT_PLUS;
413 : }
414 0 : else if (STARTS_WITH_CI(pszProduct, "OS_LANDRANGER_DTM"))
415 0 : nProduct = NPC_LANDRANGER_DTM;
416 0 : else if (STARTS_WITH_CI(pszProduct, "L-F_PROFILE_DTM"))
417 0 : nProduct = NPC_LANDFORM_PROFILE_DTM;
418 0 : else if (STARTS_WITH_CI(pszProduct, "NEXTMap Britain DTM"))
419 0 : nProduct = NPC_LANDFORM_PROFILE_DTM; // Treat as landform
420 :
421 0 : if (poDS->GetOption("FORCE_GENERIC") != nullptr &&
422 0 : !EQUAL(poDS->GetOption("FORCE_GENERIC"), "OFF"))
423 0 : nProduct = NPC_UNKNOWN;
424 :
425 : // No point in caching lines if there are no polygons.
426 0 : if (nProduct != NPC_BOUNDARYLINE && nProduct != NPC_BL2000)
427 0 : bCacheLines = FALSE;
428 :
429 : /* -------------------------------------------------------------------- */
430 : /* Handle the section header record. */
431 : /* -------------------------------------------------------------------- */
432 0 : nSavedFeatureId = nBaseFeatureId;
433 0 : nStartPos = VSIFTellL(fp);
434 :
435 0 : pszTileName = CPLStrdup(poRecord->GetField(3, 12)); // SECT_REF
436 0 : size_t nTileNameLen = strlen(pszTileName);
437 0 : while (nTileNameLen > 0 && pszTileName[nTileNameLen - 1] == ' ')
438 : {
439 0 : pszTileName[nTileNameLen - 1] = '\0';
440 0 : nTileNameLen--;
441 : }
442 :
443 0 : nCoordWidth = atoi(poRecord->GetField(15, 19)); // XYLEN
444 0 : if (nCoordWidth <= 0)
445 0 : nCoordWidth = 10;
446 :
447 0 : nZWidth = atoi(poRecord->GetField(31, 35)); // ZLEN
448 0 : if (nZWidth <= 0)
449 0 : nZWidth = 10;
450 :
451 0 : dfXYMult = atoi(poRecord->GetField(21, 30)) / 1000.0; // XY_MULT
452 0 : dfXOrigin = atoi(poRecord->GetField(47, 56));
453 0 : dfYOrigin = atoi(poRecord->GetField(57, 66));
454 0 : dfTileXSize = atoi(poRecord->GetField(23 + 74, 32 + 74));
455 0 : dfTileYSize = atoi(poRecord->GetField(33 + 74, 42 + 74));
456 0 : dfZMult = atoi(poRecord->GetField(37, 46)) / 1000.0;
457 :
458 : /* -------------------------------------------------------------------- */
459 : /* Setup scale and transformation factor for text height. */
460 : /* -------------------------------------------------------------------- */
461 0 : if (poRecord->GetLength() >= 187)
462 0 : dfScale = atoi(poRecord->GetField(148 + 31, 148 + 39));
463 0 : else if (nProduct == NPC_STRATEGI)
464 0 : dfScale = 250000;
465 0 : else if (nProduct == NPC_MERIDIAN || nProduct == NPC_MERIDIAN2)
466 0 : dfScale = 100000;
467 0 : else if (nProduct == NPC_LANDFORM_PROFILE_CONT)
468 0 : dfScale = 10000;
469 0 : else if (nProduct == NPC_LANDRANGER_CONT)
470 0 : dfScale = 50000;
471 0 : else if (nProduct == NPC_OSCAR_ASSET || nProduct == NPC_OSCAR_TRAFFIC ||
472 0 : nProduct == NPC_OSCAR_NETWORK || nProduct == NPC_OSCAR_ROUTE)
473 0 : dfScale = 10000;
474 0 : else if (nProduct == NPC_BASEDATA)
475 0 : dfScale = 625000;
476 : else /*if( nProduct == NPC_BOUNDARYLINE ) or default case */
477 0 : dfScale = 10000;
478 :
479 0 : if (dfScale != 0.0)
480 0 : dfPaperToGround = dfScale / 1000.0;
481 : else
482 0 : dfPaperToGround = 0.0;
483 :
484 0 : delete poRecord;
485 :
486 : /* -------------------------------------------------------------------- */
487 : /* Ensure we have appropriate layers defined. */
488 : /* -------------------------------------------------------------------- */
489 0 : CPLErrorReset();
490 :
491 0 : if (!IsRasterProduct())
492 0 : EstablishLayers();
493 : else
494 0 : EstablishRasterAccess();
495 :
496 0 : return CPLGetLastErrorType() != CE_Failure;
497 : }
498 :
499 : /************************************************************************/
500 : /* DumpReadable() */
501 : /************************************************************************/
502 :
503 0 : void NTFFileReader::DumpReadable(FILE *fpLog)
504 :
505 : {
506 0 : fprintf(fpLog, "Tile Name = %s\n", pszTileName);
507 0 : fprintf(fpLog, "Product = %s\n", pszProduct);
508 0 : fprintf(fpLog, "NTFLevel = %d\n", nNTFLevel);
509 0 : fprintf(fpLog, "XYLEN = %d\n", nCoordWidth);
510 0 : fprintf(fpLog, "XY_MULT = %g\n", dfXYMult);
511 0 : fprintf(fpLog, "X_ORIG = %g\n", dfXOrigin);
512 0 : fprintf(fpLog, "Y_ORIG = %g\n", dfYOrigin);
513 0 : fprintf(fpLog, "XMAX = %g\n", dfTileXSize);
514 0 : fprintf(fpLog, "YMAX = %g\n", dfTileYSize);
515 0 : }
516 :
517 : /************************************************************************/
518 : /* ProcessGeometry() */
519 : /* */
520 : /* Drop duplicate vertices from line strings ... they mess up */
521 : /* FME's polygon handling sometimes. */
522 : /************************************************************************/
523 :
524 0 : OGRGeometry *NTFFileReader::ProcessGeometry(NTFRecord *poRecord, int *pnGeomId)
525 :
526 : {
527 0 : if (poRecord->GetType() == NRT_GEOMETRY3D)
528 0 : return ProcessGeometry3D(poRecord, pnGeomId);
529 :
530 0 : else if (poRecord->GetType() != NRT_GEOMETRY)
531 0 : return nullptr;
532 :
533 0 : const int nGType = atoi(poRecord->GetField(9, 9)); // GTYPE
534 0 : const int nNumCoord = atoi(poRecord->GetField(10, 13)); // NUM_COORD
535 0 : if (nNumCoord < 0)
536 0 : return nullptr;
537 0 : if (pnGeomId != nullptr)
538 0 : *pnGeomId = atoi(poRecord->GetField(3, 8)); // GEOM_ID
539 :
540 : /* -------------------------------------------------------------------- */
541 : /* Point */
542 : /* -------------------------------------------------------------------- */
543 0 : OGRGeometry *poGeometry = nullptr;
544 0 : if (nGType == 1)
545 : {
546 : const double dfX =
547 0 : atoi(poRecord->GetField(14, 14 + GetXYLen() - 1)) * GetXYMult() +
548 0 : GetXOrigin();
549 : const double dfY =
550 0 : atoi(poRecord->GetField(14 + GetXYLen(), 14 + GetXYLen() * 2 - 1)) *
551 0 : GetXYMult() +
552 0 : GetYOrigin();
553 :
554 0 : poGeometry = new OGRPoint(dfX, dfY);
555 : }
556 :
557 : /* -------------------------------------------------------------------- */
558 : /* Line (or arc) */
559 : /* -------------------------------------------------------------------- */
560 0 : else if (nGType == 2 || nGType == 3 || nGType == 4)
561 : {
562 :
563 0 : if (nNumCoord > 0 && poRecord->GetLength() <
564 0 : 14 + (nNumCoord - 1) * (GetXYLen() * 2 + 1) +
565 0 : GetXYLen() * 2 - 1)
566 : {
567 0 : return nullptr;
568 : }
569 :
570 0 : OGRLineString *poLine = new OGRLineString;
571 0 : double dfXLast = 0.0;
572 0 : double dfYLast = 0.0;
573 0 : int nOutCount = 0;
574 :
575 0 : poGeometry = poLine;
576 0 : poLine->setNumPoints(nNumCoord);
577 0 : for (int iCoord = 0; iCoord < nNumCoord; iCoord++)
578 : {
579 0 : const int iStart = 14 + iCoord * (GetXYLen() * 2 + 1);
580 :
581 : const double dfX =
582 0 : atoi(poRecord->GetField(iStart + 0, iStart + GetXYLen() - 1)) *
583 0 : GetXYMult() +
584 0 : GetXOrigin();
585 : const double dfY =
586 0 : atoi(poRecord->GetField(iStart + GetXYLen(),
587 0 : iStart + GetXYLen() * 2 - 1)) *
588 0 : GetXYMult() +
589 0 : GetYOrigin();
590 :
591 0 : if (iCoord == 0)
592 : {
593 0 : dfXLast = dfX;
594 0 : dfYLast = dfY;
595 0 : poLine->setPoint(nOutCount++, dfX, dfY);
596 : }
597 0 : else if (dfXLast != dfX || dfYLast != dfY)
598 : {
599 0 : dfXLast = dfX;
600 0 : dfYLast = dfY;
601 0 : poLine->setPoint(nOutCount++, dfX, dfY);
602 : }
603 : }
604 0 : poLine->setNumPoints(nOutCount);
605 :
606 0 : CacheAddByGeomId(atoi(poRecord->GetField(3, 8)), poLine);
607 : }
608 :
609 : /* -------------------------------------------------------------------- */
610 : /* Arc defined by three points on the arc. */
611 : /* -------------------------------------------------------------------- */
612 0 : else if (nGType == 5 && nNumCoord == 3)
613 : {
614 0 : double adfX[3] = {0.0, 0.0, 0.0};
615 0 : double adfY[3] = {0.0, 0.0, 0.0};
616 :
617 0 : for (int iCoord = 0; iCoord < nNumCoord; iCoord++)
618 : {
619 0 : const int iStart = 14 + iCoord * (GetXYLen() * 2 + 1);
620 :
621 0 : adfX[iCoord] =
622 0 : atoi(poRecord->GetField(iStart + 0, iStart + GetXYLen() - 1)) *
623 0 : GetXYMult() +
624 0 : GetXOrigin();
625 0 : adfY[iCoord] =
626 0 : atoi(poRecord->GetField(iStart + GetXYLen(),
627 0 : iStart + GetXYLen() * 2 - 1)) *
628 0 : GetXYMult() +
629 0 : GetYOrigin();
630 : }
631 :
632 0 : poGeometry = NTFStrokeArcToOGRGeometry_Points(
633 0 : adfX[0], adfY[0], adfX[1], adfY[1], adfX[2], adfY[2], 72);
634 : }
635 :
636 : /* -------------------------------------------------------------------- */
637 : /* Circle */
638 : /* -------------------------------------------------------------------- */
639 0 : else if (nGType == 7)
640 : {
641 0 : const int iCenterStart = 14;
642 0 : const int iArcStart = 14 + 2 * GetXYLen() + 1;
643 :
644 : const double dfCenterX =
645 0 : atoi(poRecord->GetField(iCenterStart,
646 0 : iCenterStart + GetXYLen() - 1)) *
647 0 : GetXYMult() +
648 0 : GetXOrigin();
649 : const double dfCenterY =
650 0 : atoi(poRecord->GetField(iCenterStart + GetXYLen(),
651 0 : iCenterStart + GetXYLen() * 2 - 1)) *
652 0 : GetXYMult() +
653 0 : GetYOrigin();
654 :
655 : const double dfArcX =
656 0 : atoi(poRecord->GetField(iArcStart, iArcStart + GetXYLen() - 1)) *
657 0 : GetXYMult() +
658 0 : GetXOrigin();
659 : const double dfArcY =
660 0 : atoi(poRecord->GetField(iArcStart + GetXYLen(),
661 0 : iArcStart + GetXYLen() * 2 - 1)) *
662 0 : GetXYMult() +
663 0 : GetYOrigin();
664 :
665 : const double dfRadius =
666 0 : sqrt((dfCenterX - dfArcX) * (dfCenterX - dfArcX) +
667 0 : (dfCenterY - dfArcY) * (dfCenterY - dfArcY));
668 :
669 0 : poGeometry = NTFStrokeArcToOGRGeometry_Angles(dfCenterX, dfCenterY,
670 : dfRadius, 0.0, 360.0, 72);
671 : }
672 :
673 : else
674 : {
675 0 : CPLError(CE_Failure, CPLE_AppDefined, "Unhandled GType = %d", nGType);
676 : }
677 :
678 0 : if (poGeometry != nullptr)
679 0 : poGeometry->assignSpatialReference(poDS->DSGetSpatialRef());
680 :
681 0 : return poGeometry;
682 : }
683 :
684 : /************************************************************************/
685 : /* ProcessGeometry3D() */
686 : /************************************************************************/
687 :
688 0 : OGRGeometry *NTFFileReader::ProcessGeometry3D(NTFRecord *poRecord,
689 : int *pnGeomId)
690 :
691 : {
692 0 : OGRGeometry *poGeometry = nullptr;
693 :
694 0 : if (poRecord->GetType() != NRT_GEOMETRY3D)
695 0 : return nullptr;
696 :
697 0 : const int nGType = atoi(poRecord->GetField(9, 9)); // GTYPE
698 0 : const int nNumCoord = atoi(poRecord->GetField(10, 13)); // NUM_COORD
699 0 : if (pnGeomId != nullptr)
700 0 : *pnGeomId = atoi(poRecord->GetField(3, 8)); // GEOM_ID
701 :
702 0 : if (nGType == 1)
703 : {
704 0 : if (14 + 1 + 2 * static_cast<GIntBig>(GetXYLen()) + nZWidth - 1 >
705 : INT_MAX)
706 : {
707 0 : return nullptr;
708 : }
709 : const double dfX =
710 0 : atoi(poRecord->GetField(14, 14 + GetXYLen() - 1)) * GetXYMult() +
711 0 : GetXOrigin();
712 : const double dfY =
713 0 : atoi(poRecord->GetField(14 + GetXYLen(), 14 + GetXYLen() * 2 - 1)) *
714 0 : GetXYMult() +
715 0 : GetYOrigin();
716 : const double dfZ =
717 0 : atoi(poRecord->GetField(14 + 1 + 2 * GetXYLen(),
718 0 : 14 + 1 + 2 * GetXYLen() + nZWidth - 1)) *
719 0 : dfZMult;
720 :
721 0 : poGeometry = new OGRPoint(dfX, dfY, dfZ);
722 : }
723 :
724 0 : else if (nGType == 2)
725 : {
726 0 : if (nNumCoord < 0 || 14 +
727 0 : static_cast<GIntBig>(nNumCoord - 1) *
728 0 : (GetXYLen() * 2 + nZWidth + 2) +
729 0 : 1 + 2 * GetXYLen() + nZWidth - 1 >
730 : INT_MAX)
731 : {
732 0 : return nullptr;
733 : }
734 :
735 0 : OGRLineString *poLine = new OGRLineString;
736 0 : double dfXLast = 0.0;
737 0 : double dfYLast = 0.0;
738 0 : int nOutCount = 0;
739 :
740 0 : poGeometry = poLine;
741 0 : poLine->setNumPoints(nNumCoord);
742 0 : const GUInt32 nErrorsBefore = CPLGetErrorCounter();
743 0 : for (int iCoord = 0; iCoord < nNumCoord; iCoord++)
744 : {
745 0 : const int iStart = 14 + iCoord * (GetXYLen() * 2 + nZWidth + 2);
746 :
747 : const char *pszX =
748 0 : poRecord->GetField(iStart + 0, iStart + GetXYLen() - 1);
749 0 : bool bSpace = pszX[0] == ' ';
750 0 : const double dfX = atoi(pszX) * GetXYMult() + GetXOrigin();
751 0 : const char *pszY = poRecord->GetField(iStart + GetXYLen(),
752 0 : iStart + GetXYLen() * 2 - 1);
753 0 : bSpace |= pszY[0] == ' ';
754 0 : const double dfY = atoi(pszY) * GetXYMult() + GetYOrigin();
755 :
756 : const char *pszZ =
757 0 : poRecord->GetField(iStart + 1 + 2 * GetXYLen(),
758 0 : iStart + 1 + 2 * GetXYLen() + nZWidth - 1);
759 0 : bSpace |= pszZ[0] == ' ';
760 0 : const double dfZ = atoi(pszZ) * dfZMult;
761 0 : if (bSpace && CPLGetErrorCounter() != nErrorsBefore)
762 : {
763 0 : delete poGeometry;
764 0 : return nullptr;
765 : }
766 :
767 0 : if (iCoord == 0)
768 : {
769 0 : dfXLast = dfX;
770 0 : dfYLast = dfY;
771 0 : poLine->setPoint(nOutCount++, dfX, dfY, dfZ);
772 : }
773 0 : else if (dfXLast != dfX || dfYLast != dfY)
774 : {
775 0 : dfXLast = dfX;
776 0 : dfYLast = dfY;
777 0 : poLine->setPoint(nOutCount++, dfX, dfY, dfZ);
778 : }
779 : }
780 0 : poLine->setNumPoints(nOutCount);
781 :
782 0 : CacheAddByGeomId(atoi(poRecord->GetField(3, 8)), poLine);
783 : }
784 :
785 0 : if (poGeometry != nullptr)
786 0 : poGeometry->assignSpatialReference(poDS->DSGetSpatialRef());
787 :
788 0 : return poGeometry;
789 : }
790 :
791 : /************************************************************************/
792 : /* ProcessAttDesc() */
793 : /************************************************************************/
794 :
795 0 : int NTFFileReader::ProcessAttDesc(NTFRecord *poRecord, NTFAttDesc *psAD)
796 :
797 : {
798 0 : psAD->poCodeList = nullptr;
799 0 : if (poRecord->GetType() != NRT_ADR || poRecord->GetLength() < 13)
800 0 : return FALSE;
801 :
802 0 : snprintf(psAD->val_type, sizeof(psAD->val_type), "%s",
803 : poRecord->GetField(3, 4));
804 0 : snprintf(psAD->fwidth, sizeof(psAD->fwidth), "%s",
805 : poRecord->GetField(5, 7));
806 0 : snprintf(psAD->finter, sizeof(psAD->finter), "%s",
807 : poRecord->GetField(8, 12));
808 :
809 0 : const char *pszData = poRecord->GetData();
810 0 : int iChar = 12; // Used after for.
811 0 : for (; pszData[iChar] != '\0' && pszData[iChar] != '\\'; iChar++)
812 : {
813 : }
814 :
815 0 : snprintf(psAD->att_name, sizeof(psAD->att_name), "%s",
816 : poRecord->GetField(13, iChar));
817 :
818 0 : return TRUE;
819 : }
820 :
821 : /************************************************************************/
822 : /* ProcessAttRecGroup() */
823 : /* */
824 : /* Extract attribute values from all attribute records in a */
825 : /* record set. */
826 : /************************************************************************/
827 :
828 0 : int NTFFileReader::ProcessAttRecGroup(NTFRecord **papoRecords,
829 : char ***ppapszTypes, char ***ppapszValues)
830 :
831 : {
832 0 : *ppapszTypes = nullptr;
833 0 : *ppapszValues = nullptr;
834 :
835 0 : for (int iRec = 0; papoRecords[iRec] != nullptr; iRec++)
836 : {
837 0 : if (papoRecords[iRec]->GetType() != NRT_ATTREC)
838 0 : continue;
839 :
840 0 : char **papszTypes1 = nullptr;
841 0 : char **papszValues1 = nullptr;
842 0 : if (!ProcessAttRec(papoRecords[iRec], nullptr, &papszTypes1,
843 : &papszValues1))
844 : {
845 0 : CSLDestroy(*ppapszTypes);
846 0 : CSLDestroy(*ppapszValues);
847 0 : *ppapszTypes = nullptr;
848 0 : *ppapszValues = nullptr;
849 0 : return FALSE;
850 : }
851 :
852 0 : if (*ppapszTypes == nullptr)
853 : {
854 0 : *ppapszTypes = papszTypes1;
855 0 : *ppapszValues = papszValues1;
856 : }
857 : else
858 : {
859 0 : for (int i = 0; papszTypes1[i] != nullptr; i++)
860 : {
861 0 : *ppapszTypes = CSLAddString(*ppapszTypes, papszTypes1[i]);
862 0 : *ppapszValues = CSLAddString(*ppapszValues, papszValues1[i]);
863 : }
864 0 : CSLDestroy(papszTypes1);
865 0 : CSLDestroy(papszValues1);
866 : }
867 : }
868 :
869 0 : return TRUE;
870 : }
871 :
872 : /************************************************************************/
873 : /* ProcessAttRec() */
874 : /************************************************************************/
875 :
876 0 : int NTFFileReader::ProcessAttRec(NTFRecord *poRecord, int *pnAttId,
877 : char ***ppapszTypes, char ***ppapszValues)
878 :
879 : {
880 0 : if (pnAttId != nullptr)
881 0 : *pnAttId = 0;
882 0 : *ppapszTypes = nullptr;
883 0 : *ppapszValues = nullptr;
884 :
885 0 : if (poRecord->GetType() != NRT_ATTREC || poRecord->GetLength() < 8)
886 0 : return FALSE;
887 :
888 : /* -------------------------------------------------------------------- */
889 : /* Extract the attribute id. */
890 : /* -------------------------------------------------------------------- */
891 0 : if (pnAttId != nullptr)
892 0 : *pnAttId = atoi(poRecord->GetField(3, 8));
893 :
894 : /* ==================================================================== */
895 : /* Loop handling attribute till we get a '0' indicating the end */
896 : /* of the record. */
897 : /* ==================================================================== */
898 :
899 0 : int iOffset = 8;
900 0 : const char *pszData = poRecord->GetData();
901 0 : bool bError = false;
902 :
903 0 : while (iOffset < poRecord->GetLength() && pszData[iOffset] != DIGIT_ZERO)
904 : {
905 : /* --------------------------------------------------------------------
906 : */
907 : /* Extract the two letter code name for the attribute, and use */
908 : /* it to find the correct ATTDESC info. */
909 : /* --------------------------------------------------------------------
910 : */
911 0 : NTFAttDesc *psAttDesc = GetAttDesc(pszData + iOffset);
912 0 : if (psAttDesc == nullptr)
913 : {
914 0 : CPLDebug("NTF", "Couldn't translate attrec type `%2.2s'.",
915 0 : pszData + iOffset);
916 0 : bError = true;
917 0 : break;
918 : }
919 :
920 0 : *ppapszTypes = CSLAddString(
921 : *ppapszTypes, poRecord->GetField(iOffset + 1, iOffset + 2));
922 :
923 : /* --------------------------------------------------------------------
924 : */
925 : /* Establish the width of the value. Zero width fields are */
926 : /* terminated by a backslash. */
927 : /* --------------------------------------------------------------------
928 : */
929 0 : const int nFWidth = atoi(psAttDesc->fwidth);
930 0 : if (nFWidth < 0)
931 : {
932 0 : bError = true;
933 0 : break;
934 : }
935 0 : int nEnd = 0;
936 0 : if (nFWidth == 0)
937 : {
938 0 : const char *pszData2 = poRecord->GetData();
939 0 : if (iOffset + 2 >= poRecord->GetLength())
940 : {
941 0 : bError = true;
942 0 : break;
943 : }
944 0 : for (nEnd = iOffset + 2;
945 0 : pszData2[nEnd] != '\\' && pszData2[nEnd] != '\0'; nEnd++)
946 : {
947 : }
948 : }
949 : else
950 : {
951 0 : nEnd = iOffset + 3 + nFWidth - 1;
952 : }
953 :
954 : /* --------------------------------------------------------------------
955 : */
956 : /* Extract the value. If it is formatted as fixed point real */
957 : /* we reprocess it to insert the decimal point. */
958 : /* --------------------------------------------------------------------
959 : */
960 0 : const char *pszRawValue = poRecord->GetField(iOffset + 3, nEnd);
961 0 : *ppapszValues = CSLAddString(*ppapszValues, pszRawValue);
962 :
963 : /* --------------------------------------------------------------------
964 : */
965 : /* Establish new offset position. */
966 : /* --------------------------------------------------------------------
967 : */
968 0 : if (nFWidth == 0)
969 : {
970 0 : iOffset = nEnd;
971 0 : if (iOffset >= poRecord->GetLength())
972 : {
973 0 : bError = (iOffset > poRecord->GetLength());
974 0 : break;
975 : }
976 0 : if (pszData[iOffset] == '\\')
977 0 : iOffset++;
978 : }
979 : else
980 0 : iOffset += 2 + nFWidth;
981 : }
982 :
983 0 : if (bError)
984 : {
985 0 : CSLDestroy(*ppapszTypes);
986 0 : CSLDestroy(*ppapszValues);
987 0 : *ppapszTypes = nullptr;
988 0 : *ppapszValues = nullptr;
989 : }
990 :
991 0 : return (*ppapszTypes != nullptr);
992 : }
993 :
994 : /************************************************************************/
995 : /* GetAttDesc() */
996 : /************************************************************************/
997 :
998 0 : NTFAttDesc *NTFFileReader::GetAttDesc(const char *pszType)
999 :
1000 : {
1001 0 : for (int i = 0; i < nAttCount; i++)
1002 : {
1003 0 : if (EQUALN(pszType, pasAttDesc[i].val_type, 2))
1004 0 : return pasAttDesc + i;
1005 : }
1006 :
1007 0 : return nullptr;
1008 : }
1009 :
1010 : /************************************************************************/
1011 : /* ProcessAttValue() */
1012 : /* */
1013 : /* Take an attribute type/value pair and transform into a */
1014 : /* meaningful attribute name, and value. The source can be an */
1015 : /* ATTREC or the VAL_TYPE/VALUE pair of a POINTREC or LINEREC. */
1016 : /* The name is transformed from the two character short form to */
1017 : /* the long user name. The value will be transformed from */
1018 : /* fixed point (with the decimal implicit) to fixed point with */
1019 : /* an explicit decimal point if it has a "R" format. */
1020 : /* Note: the returned *ppszAttValue has a very short lifetime */
1021 : /* and should immediately be used. Further calls to */
1022 : /* ProcessAttValue or CPLSPrintf() will invalidate it. */
1023 : /************************************************************************/
1024 :
1025 0 : int NTFFileReader::ProcessAttValue(const char *pszValType,
1026 : const char *pszRawValue,
1027 : const char **ppszAttName,
1028 : const char **ppszAttValue,
1029 : const char **ppszCodeDesc)
1030 :
1031 : {
1032 : /* -------------------------------------------------------------------- */
1033 : /* Find the ATTDESC for this attribute, and assign return name value.*/
1034 : /* -------------------------------------------------------------------- */
1035 0 : NTFAttDesc *psAttDesc = GetAttDesc(pszValType);
1036 :
1037 0 : if (psAttDesc == nullptr)
1038 0 : return FALSE;
1039 :
1040 0 : if (ppszAttName != nullptr)
1041 0 : *ppszAttName = psAttDesc->att_name;
1042 :
1043 : /* -------------------------------------------------------------------- */
1044 : /* Extract the value. If it is formatted as fixed point real */
1045 : /* we reprocess it to insert the decimal point. */
1046 : /* -------------------------------------------------------------------- */
1047 0 : if (psAttDesc->finter[0] == 'R')
1048 : {
1049 0 : const char *pszDecimalPortion = nullptr; // Used after for.
1050 :
1051 0 : for (pszDecimalPortion = psAttDesc->finter;
1052 0 : *pszDecimalPortion != ',' && *pszDecimalPortion != '\0';
1053 : pszDecimalPortion++)
1054 : {
1055 : }
1056 0 : if (*pszDecimalPortion == '\0')
1057 : {
1058 0 : *ppszAttValue = "";
1059 : }
1060 : else
1061 : {
1062 0 : const int nWidth = static_cast<int>(strlen(pszRawValue));
1063 0 : const int nPrecision = atoi(pszDecimalPortion + 1);
1064 0 : if (nPrecision < 0 || nPrecision >= nWidth)
1065 : {
1066 0 : *ppszAttValue = "";
1067 : }
1068 : else
1069 : {
1070 0 : CPLString osResult(pszRawValue);
1071 0 : osResult.resize(nWidth - nPrecision);
1072 0 : osResult += ".";
1073 0 : osResult += pszRawValue + nWidth - nPrecision;
1074 :
1075 0 : *ppszAttValue = CPLSPrintf("%s", osResult.c_str());
1076 : }
1077 : }
1078 : }
1079 :
1080 : /* -------------------------------------------------------------------- */
1081 : /* If it is an integer, we just reformat to get rid of leading */
1082 : /* zeros. */
1083 : /* -------------------------------------------------------------------- */
1084 0 : else if (psAttDesc->finter[0] == 'I')
1085 : {
1086 0 : *ppszAttValue = CPLSPrintf("%d", atoi(pszRawValue));
1087 : }
1088 :
1089 : /* -------------------------------------------------------------------- */
1090 : /* Otherwise we take the value directly. */
1091 : /* -------------------------------------------------------------------- */
1092 : else
1093 : {
1094 0 : *ppszAttValue = pszRawValue;
1095 : }
1096 :
1097 : /* -------------------------------------------------------------------- */
1098 : /* Handle processing code values into code descriptions, if */
1099 : /* applicable. */
1100 : /* -------------------------------------------------------------------- */
1101 0 : if (ppszCodeDesc == nullptr)
1102 : {
1103 : }
1104 0 : else if (psAttDesc->poCodeList != nullptr)
1105 : {
1106 0 : *ppszCodeDesc = psAttDesc->poCodeList->Lookup(*ppszAttValue);
1107 : }
1108 : else
1109 : {
1110 0 : *ppszCodeDesc = nullptr;
1111 : }
1112 :
1113 0 : return TRUE;
1114 : }
1115 :
1116 : /************************************************************************/
1117 : /* ApplyAttributeValues() */
1118 : /* */
1119 : /* Apply a series of attribute values to a feature from generic */
1120 : /* attribute records. */
1121 : /************************************************************************/
1122 :
1123 0 : void NTFFileReader::ApplyAttributeValues(OGRFeature *poFeature,
1124 : NTFRecord **papoGroup, ...)
1125 :
1126 : {
1127 : /* -------------------------------------------------------------------- */
1128 : /* Extract attribute values from record group. */
1129 : /* -------------------------------------------------------------------- */
1130 0 : char **papszTypes = nullptr;
1131 0 : char **papszValues = nullptr;
1132 :
1133 0 : if (!ProcessAttRecGroup(papoGroup, &papszTypes, &papszValues))
1134 0 : return;
1135 :
1136 : /* -------------------------------------------------------------------- */
1137 : /* Handle attribute pairs */
1138 : /* -------------------------------------------------------------------- */
1139 : va_list hVaArgs;
1140 :
1141 0 : va_start(hVaArgs, papoGroup);
1142 :
1143 0 : const char *pszAttName = nullptr;
1144 0 : while ((pszAttName = va_arg(hVaArgs, const char *)) != nullptr)
1145 : {
1146 0 : const int iField = va_arg(hVaArgs, int);
1147 :
1148 0 : ApplyAttributeValue(poFeature, iField, pszAttName, papszTypes,
1149 : papszValues);
1150 : }
1151 :
1152 0 : va_end(hVaArgs);
1153 :
1154 : /* -------------------------------------------------------------------- */
1155 : /* Cleanup. */
1156 : /* -------------------------------------------------------------------- */
1157 0 : CSLDestroy(papszTypes);
1158 0 : CSLDestroy(papszValues);
1159 : }
1160 :
1161 : /************************************************************************/
1162 : /* ApplyAttributeValue() */
1163 : /* */
1164 : /* Apply the indicated attribute value to an OGRFeature field */
1165 : /* if it exists in the attribute value list given. */
1166 : /************************************************************************/
1167 :
1168 0 : int NTFFileReader::ApplyAttributeValue(OGRFeature *poFeature, int iField,
1169 : const char *pszAttName,
1170 : char **papszTypes, char **papszValues)
1171 :
1172 : {
1173 : /* -------------------------------------------------------------------- */
1174 : /* Find the requested attribute in the name/value pair */
1175 : /* provided. If not found that's fine, just return with */
1176 : /* notification. */
1177 : /* -------------------------------------------------------------------- */
1178 0 : const int iValue = CSLFindString(papszTypes, pszAttName);
1179 0 : if (iValue < 0)
1180 0 : return FALSE;
1181 :
1182 0 : CPLAssert(papszValues != nullptr);
1183 : /* -------------------------------------------------------------------- */
1184 : /* Process the attribute value ... this really only has a */
1185 : /* useful effect for real numbers. */
1186 : /* -------------------------------------------------------------------- */
1187 0 : const char *pszAttLongName = nullptr;
1188 0 : const char *pszAttValue = nullptr;
1189 0 : const char *pszCodeDesc = nullptr;
1190 :
1191 0 : if (!ProcessAttValue(pszAttName, papszValues[iValue], &pszAttLongName,
1192 : &pszAttValue, &pszCodeDesc))
1193 0 : return FALSE;
1194 :
1195 : /* -------------------------------------------------------------------- */
1196 : /* Apply the value to the field using the simple set string */
1197 : /* method. Leave it to the OGRFeature::SetField() method to */
1198 : /* take care of translation to other types. */
1199 : /* -------------------------------------------------------------------- */
1200 0 : poFeature->SetField(iField, pszAttValue);
1201 :
1202 : /* -------------------------------------------------------------------- */
1203 : /* Apply the code description if we found one. */
1204 : /* -------------------------------------------------------------------- */
1205 0 : if (pszCodeDesc != nullptr)
1206 : {
1207 : char szDescFieldName[256];
1208 :
1209 0 : snprintf(szDescFieldName, sizeof(szDescFieldName), "%s_DESC",
1210 0 : poFeature->GetDefnRef()->GetFieldDefn(iField)->GetNameRef());
1211 0 : poFeature->SetField(szDescFieldName, pszCodeDesc);
1212 : }
1213 :
1214 0 : return TRUE;
1215 : }
1216 :
1217 : /************************************************************************/
1218 : /* SaveRecord() */
1219 : /************************************************************************/
1220 :
1221 0 : void NTFFileReader::SaveRecord(NTFRecord *poRecord)
1222 :
1223 : {
1224 0 : CPLAssert(poSavedRecord == nullptr);
1225 0 : poSavedRecord = poRecord;
1226 0 : }
1227 :
1228 : /************************************************************************/
1229 : /* ReadRecord() */
1230 : /************************************************************************/
1231 :
1232 0 : NTFRecord *NTFFileReader::ReadRecord()
1233 :
1234 : {
1235 0 : if (poSavedRecord != nullptr)
1236 : {
1237 0 : NTFRecord *poReturn = poSavedRecord;
1238 :
1239 0 : poSavedRecord = nullptr;
1240 :
1241 0 : return poReturn;
1242 : }
1243 : else
1244 : {
1245 0 : CPLErrorReset();
1246 0 : if (fp != nullptr)
1247 0 : nPreSavedPos = VSIFTellL(fp);
1248 0 : NTFRecord *poRecord = new NTFRecord(fp);
1249 0 : if (fp != nullptr)
1250 0 : nPostSavedPos = VSIFTellL(fp);
1251 :
1252 : /* ensure termination if we fail to read a record */
1253 0 : if (CPLGetLastErrorType() == CE_Failure)
1254 : {
1255 0 : delete poRecord;
1256 0 : poRecord = nullptr;
1257 : }
1258 :
1259 0 : return poRecord;
1260 : }
1261 : }
1262 :
1263 : /************************************************************************/
1264 : /* GetFPPos() */
1265 : /* */
1266 : /* Return the current file pointer position. */
1267 : /************************************************************************/
1268 :
1269 0 : void NTFFileReader::GetFPPos(vsi_l_offset *pnPos, long *pnFID)
1270 :
1271 : {
1272 0 : if (poSavedRecord != nullptr)
1273 0 : *pnPos = nPreSavedPos;
1274 : else
1275 0 : *pnPos = nPostSavedPos;
1276 :
1277 0 : if (pnFID != nullptr)
1278 0 : *pnFID = nSavedFeatureId;
1279 0 : }
1280 :
1281 : /************************************************************************/
1282 : /* SetFPPos() */
1283 : /************************************************************************/
1284 :
1285 0 : int NTFFileReader::SetFPPos(vsi_l_offset nNewPos, long nNewFID)
1286 :
1287 : {
1288 0 : if (nNewFID == nSavedFeatureId)
1289 0 : return TRUE;
1290 :
1291 0 : if (poSavedRecord != nullptr)
1292 : {
1293 0 : delete poSavedRecord;
1294 0 : poSavedRecord = nullptr;
1295 : }
1296 :
1297 0 : if (fp != nullptr && VSIFSeekL(fp, nNewPos, SEEK_SET) == 0)
1298 : {
1299 0 : nPreSavedPos = nPostSavedPos = nNewPos;
1300 0 : nSavedFeatureId = nNewFID;
1301 0 : return TRUE;
1302 : }
1303 : else
1304 0 : return FALSE;
1305 : }
1306 :
1307 : /************************************************************************/
1308 : /* Reset() */
1309 : /* */
1310 : /* Reset reading to the first feature record. */
1311 : /************************************************************************/
1312 :
1313 0 : void NTFFileReader::Reset()
1314 :
1315 : {
1316 0 : SetFPPos(nStartPos, nBaseFeatureId);
1317 0 : ClearCGroup();
1318 0 : }
1319 :
1320 : /************************************************************************/
1321 : /* ClearCGroup() */
1322 : /* */
1323 : /* Clear the currently loaded record group. */
1324 : /************************************************************************/
1325 :
1326 0 : void NTFFileReader::ClearCGroup()
1327 :
1328 : {
1329 0 : for (int i = 0; apoCGroup[i] != nullptr; i++)
1330 0 : delete apoCGroup[i];
1331 :
1332 0 : apoCGroup[0] = nullptr;
1333 0 : apoCGroup[1] = nullptr;
1334 0 : }
1335 :
1336 : /************************************************************************/
1337 : /* DefaultNTFRecordGrouper() */
1338 : /* */
1339 : /* Default rules for figuring out if a new candidate record */
1340 : /* belongs to a group of records that together form a feature */
1341 : /* (a record group). */
1342 : /************************************************************************/
1343 :
1344 0 : int DefaultNTFRecordGrouper(NTFFileReader *, NTFRecord **papoGroup,
1345 : NTFRecord *poCandidate)
1346 :
1347 : {
1348 : /* -------------------------------------------------------------------- */
1349 : /* Is this group going to be a CPOLY set? We can recognise */
1350 : /* this because we get repeating POLY/CHAIN sets without an */
1351 : /* intermediate attribute record. This is a rather special case! */
1352 : /* -------------------------------------------------------------------- */
1353 0 : if (papoGroup[0] != nullptr && papoGroup[1] != nullptr &&
1354 0 : papoGroup[0]->GetType() == NRT_POLYGON &&
1355 0 : papoGroup[1]->GetType() == NRT_CHAIN)
1356 : {
1357 : // We keep going till we get the seed geometry.
1358 0 : int iRec, bGotCPOLY = FALSE;
1359 :
1360 0 : for (iRec = 0; papoGroup[iRec] != nullptr; iRec++)
1361 : {
1362 0 : if (papoGroup[iRec]->GetType() == NRT_CPOLY)
1363 0 : bGotCPOLY = TRUE;
1364 : }
1365 :
1366 0 : if (bGotCPOLY && poCandidate->GetType() != NRT_GEOMETRY &&
1367 0 : poCandidate->GetType() != NRT_ATTREC)
1368 0 : return FALSE;
1369 :
1370 : /*
1371 : * this logic assumes we always get a point geometry with a CPOLY
1372 : * but that isn't always true, for instance with BL2000 data. The
1373 : * preceding check will handle this case.
1374 : */
1375 0 : if (papoGroup[iRec - 1]->GetType() != NRT_GEOMETRY)
1376 0 : return TRUE;
1377 : else
1378 0 : return FALSE;
1379 : }
1380 :
1381 : /* -------------------------------------------------------------------- */
1382 : /* Is this a "feature" defining record? If so break out if it */
1383 : /* isn't the first record in the group. */
1384 : /* -------------------------------------------------------------------- */
1385 0 : if (papoGroup[0] != nullptr && (poCandidate->GetType() == NRT_NAMEREC ||
1386 0 : poCandidate->GetType() == NRT_NODEREC ||
1387 0 : poCandidate->GetType() == NRT_LINEREC ||
1388 0 : poCandidate->GetType() == NRT_POINTREC ||
1389 0 : poCandidate->GetType() == NRT_POLYGON ||
1390 0 : poCandidate->GetType() == NRT_CPOLY ||
1391 0 : poCandidate->GetType() == NRT_COLLECT ||
1392 0 : poCandidate->GetType() == NRT_TEXTREC ||
1393 0 : poCandidate->GetType() == NRT_COMMENT))
1394 : {
1395 0 : return FALSE;
1396 : }
1397 :
1398 : /* -------------------------------------------------------------------- */
1399 : /* Do we already have a record of this type? If so, it likely */
1400 : /* doesn't belong in the group. Attribute records do repeat in */
1401 : /* some products. */
1402 : /* -------------------------------------------------------------------- */
1403 0 : if (poCandidate->GetType() != NRT_ATTREC)
1404 : {
1405 0 : int iRec = 0; // Used after for.
1406 0 : for (; papoGroup[iRec] != nullptr; iRec++)
1407 : {
1408 0 : if (poCandidate->GetType() == papoGroup[iRec]->GetType())
1409 0 : break;
1410 : }
1411 :
1412 0 : if (papoGroup[iRec] != nullptr)
1413 0 : return FALSE;
1414 : }
1415 :
1416 0 : return TRUE;
1417 : }
1418 :
1419 : /************************************************************************/
1420 : /* ReadRecordGroup() */
1421 : /* */
1422 : /* Read a group of records that form a single feature. */
1423 : /************************************************************************/
1424 :
1425 0 : NTFRecord **NTFFileReader::ReadRecordGroup()
1426 :
1427 : {
1428 :
1429 0 : ClearCGroup();
1430 :
1431 : /* -------------------------------------------------------------------- */
1432 : /* Loop, reading records till we think we have a grouping. */
1433 : /* -------------------------------------------------------------------- */
1434 0 : int nRecordCount = 0;
1435 0 : NTFRecord *poRecord = nullptr;
1436 0 : while ((poRecord = ReadRecord()) != nullptr &&
1437 0 : poRecord->GetType() != NRT_VTR)
1438 : {
1439 0 : if (nRecordCount >= MAX_REC_GROUP)
1440 : {
1441 0 : CPLError(CE_Failure, CPLE_AppDefined,
1442 : "Maximum record group size (%d) exceeded.\n",
1443 : MAX_REC_GROUP);
1444 0 : break;
1445 : }
1446 :
1447 0 : if (!pfnRecordGrouper(this, apoCGroup, poRecord))
1448 0 : break;
1449 :
1450 0 : apoCGroup[nRecordCount++] = poRecord;
1451 0 : apoCGroup[nRecordCount] = nullptr;
1452 : }
1453 :
1454 : /* -------------------------------------------------------------------- */
1455 : /* Push the last record back on the input queue. */
1456 : /* -------------------------------------------------------------------- */
1457 0 : if (poRecord != nullptr)
1458 0 : SaveRecord(poRecord);
1459 :
1460 : /* -------------------------------------------------------------------- */
1461 : /* Return the list, or NULL if we didn't get any records. */
1462 : /* -------------------------------------------------------------------- */
1463 0 : if (nRecordCount == 0)
1464 0 : return nullptr;
1465 : else
1466 0 : return apoCGroup;
1467 : }
1468 :
1469 : /************************************************************************/
1470 : /* GetFeatureClass() */
1471 : /************************************************************************/
1472 :
1473 0 : int NTFFileReader::GetFeatureClass(int iFCIndex, char **ppszFCId,
1474 : char **ppszFCName)
1475 :
1476 : {
1477 0 : if (iFCIndex < 0 || iFCIndex >= nFCCount)
1478 : {
1479 0 : *ppszFCId = nullptr;
1480 0 : *ppszFCName = nullptr;
1481 0 : return FALSE;
1482 : }
1483 : else
1484 : {
1485 0 : *ppszFCId = papszFCNum[iFCIndex];
1486 0 : *ppszFCName = papszFCName[iFCIndex];
1487 0 : return TRUE;
1488 : }
1489 : }
1490 :
1491 : /************************************************************************/
1492 : /* ReadOGRFeature() */
1493 : /************************************************************************/
1494 :
1495 0 : OGRFeature *NTFFileReader::ReadOGRFeature(OGRNTFLayer *poTargetLayer)
1496 :
1497 : {
1498 : /* -------------------------------------------------------------------- */
1499 : /* If this is a raster file, use a custom method to read the */
1500 : /* feature. */
1501 : /* -------------------------------------------------------------------- */
1502 0 : if (IsRasterProduct())
1503 0 : return poRasterLayer->GetNextFeature();
1504 :
1505 : /* -------------------------------------------------------------------- */
1506 : /* Loop looking for a group we can translate, and that if */
1507 : /* needed matches our layer request. */
1508 : /* -------------------------------------------------------------------- */
1509 0 : OGRNTFLayer *poLayer = nullptr;
1510 0 : OGRFeature *poFeature = nullptr;
1511 :
1512 : while (true)
1513 : {
1514 0 : NTFRecord **papoGroup = nullptr;
1515 :
1516 0 : if (GetProductId() == NPC_UNKNOWN && nNTFLevel > 2)
1517 0 : papoGroup = GetNextIndexedRecordGroup(apoCGroup + 1);
1518 : else
1519 0 : papoGroup = ReadRecordGroup();
1520 :
1521 0 : if (papoGroup == nullptr || papoGroup[0] == nullptr)
1522 : break;
1523 :
1524 0 : int nType = papoGroup[0]->GetType();
1525 0 : if (nType < 0 || nType >= (int)(sizeof(apoTypeTranslation) /
1526 : sizeof(apoTypeTranslation[0])))
1527 0 : continue;
1528 0 : poLayer = apoTypeTranslation[nType];
1529 0 : if (poLayer == nullptr)
1530 0 : continue;
1531 :
1532 0 : if (poTargetLayer != nullptr && poTargetLayer != poLayer)
1533 : {
1534 0 : CacheLineGeometryInGroup(papoGroup);
1535 0 : nSavedFeatureId++;
1536 0 : continue;
1537 : }
1538 :
1539 0 : poFeature = poLayer->FeatureTranslate(this, papoGroup);
1540 0 : if (poFeature == nullptr)
1541 : {
1542 : // should this be a real error?
1543 0 : CPLDebug("NTF",
1544 : "FeatureTranslate() failed for a type %d record group\n"
1545 : "in a %s type file.\n",
1546 : papoGroup[0]->GetType(), GetProduct());
1547 : }
1548 : else
1549 : {
1550 0 : break;
1551 : }
1552 0 : }
1553 :
1554 : /* -------------------------------------------------------------------- */
1555 : /* If we got a feature, set the TILE_REF on it. */
1556 : /* -------------------------------------------------------------------- */
1557 0 : if (poFeature != nullptr)
1558 : {
1559 0 : int iTileRefField = poLayer->GetLayerDefn()->GetFieldCount() - 1;
1560 :
1561 0 : CPLAssert(EQUAL(
1562 : poLayer->GetLayerDefn()->GetFieldDefn(iTileRefField)->GetNameRef(),
1563 : "TILE_REF"));
1564 :
1565 0 : poFeature->SetField(iTileRefField, GetTileName());
1566 0 : poFeature->SetFID(nSavedFeatureId);
1567 :
1568 0 : nSavedFeatureId++;
1569 : }
1570 :
1571 : /* -------------------------------------------------------------------- */
1572 : /* If we got to the end we can establish our feature count for */
1573 : /* the file. */
1574 : /* -------------------------------------------------------------------- */
1575 : else
1576 : {
1577 : // This assertion was triggered by
1578 : // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=1982 it doesn't
1579 : // look critical enough to be enabled.
1580 : // CPLAssert( nFeatureCount == -1
1581 : // || nFeatureCount == nSavedFeatureId - nBaseFeatureId );
1582 0 : nFeatureCount = nSavedFeatureId - nBaseFeatureId;
1583 : }
1584 :
1585 0 : return poFeature;
1586 : }
1587 :
1588 : /************************************************************************/
1589 : /* TestForLayer() */
1590 : /* */
1591 : /* Return indicator of whether this file contains any features */
1592 : /* of the indicated layer type. */
1593 : /************************************************************************/
1594 :
1595 0 : int NTFFileReader::TestForLayer(OGRNTFLayer *poLayer)
1596 :
1597 : {
1598 0 : for (int i = 0; i < 100; i++)
1599 : {
1600 0 : if (apoTypeTranslation[i] == poLayer)
1601 0 : return TRUE;
1602 : }
1603 :
1604 0 : return FALSE;
1605 : }
1606 :
1607 : /************************************************************************/
1608 : /* FreshenIndex() */
1609 : /* */
1610 : /* Rebuild the index if it is needed, and currently missing. */
1611 : /************************************************************************/
1612 :
1613 0 : void NTFFileReader::FreshenIndex()
1614 :
1615 : {
1616 0 : if (!bIndexBuilt && bIndexNeeded)
1617 0 : IndexFile();
1618 0 : }
1619 :
1620 : /************************************************************************/
1621 : /* IndexFile() */
1622 : /* */
1623 : /* Read all records beyond the section header and build an */
1624 : /* internal index of them. */
1625 : /************************************************************************/
1626 :
1627 0 : void NTFFileReader::IndexFile()
1628 :
1629 : {
1630 0 : Reset();
1631 :
1632 0 : DestroyIndex();
1633 :
1634 0 : bIndexNeeded = TRUE;
1635 0 : bIndexBuilt = TRUE;
1636 0 : bCacheLines = FALSE;
1637 :
1638 : /* -------------------------------------------------------------------- */
1639 : /* Process all records after the section header, and before 99 */
1640 : /* to put them in the index. */
1641 : /* -------------------------------------------------------------------- */
1642 0 : NTFRecord *poRecord = nullptr;
1643 0 : while ((poRecord = ReadRecord()) != nullptr && poRecord->GetType() != 99)
1644 : {
1645 0 : const int iType = poRecord->GetType();
1646 0 : const int iId = atoi(poRecord->GetField(3, 8));
1647 :
1648 0 : if (iType < 0 || iType >= 100)
1649 : {
1650 0 : CPLError(CE_Failure, CPLE_AppDefined,
1651 : "Illegal type %d record, skipping.", iType);
1652 0 : delete poRecord;
1653 0 : continue;
1654 : }
1655 0 : if (iId < 0)
1656 : {
1657 0 : CPLError(CE_Failure, CPLE_AppDefined,
1658 : "Illegal id %d record, skipping.", iId);
1659 0 : delete poRecord;
1660 0 : continue;
1661 : }
1662 :
1663 : /* --------------------------------------------------------------------
1664 : */
1665 : /* Grow type specific subindex if needed. */
1666 : /* --------------------------------------------------------------------
1667 : */
1668 0 : if (anIndexSize[iType] <= iId)
1669 : {
1670 0 : const int nNewSize = std::max(iId + 1, anIndexSize[iType] * 2 + 10);
1671 :
1672 0 : apapoRecordIndex[iType] = static_cast<NTFRecord **>(
1673 0 : CPLRealloc(apapoRecordIndex[iType], sizeof(void *) * nNewSize));
1674 :
1675 0 : for (int i = anIndexSize[iType]; i < nNewSize; i++)
1676 0 : (apapoRecordIndex[iType])[i] = nullptr;
1677 :
1678 0 : anIndexSize[iType] = nNewSize;
1679 : }
1680 :
1681 : /* --------------------------------------------------------------------
1682 : */
1683 : /* Put record into type specific subindex based on its id as */
1684 : /* the key. */
1685 : /* --------------------------------------------------------------------
1686 : */
1687 0 : if (apapoRecordIndex[iType][iId] != nullptr)
1688 : {
1689 0 : CPLDebug("OGR_NTF",
1690 : "Duplicate record with index %d and type %d\n"
1691 : "in NTFFileReader::IndexFile().",
1692 : iId, iType);
1693 0 : delete apapoRecordIndex[iType][iId];
1694 : }
1695 0 : (apapoRecordIndex[iType])[iId] = poRecord;
1696 : }
1697 :
1698 0 : if (poRecord != nullptr)
1699 0 : delete poRecord;
1700 0 : }
1701 :
1702 : /************************************************************************/
1703 : /* DestroyIndex() */
1704 : /************************************************************************/
1705 :
1706 0 : void NTFFileReader::DestroyIndex()
1707 :
1708 : {
1709 0 : for (int i = 0; i < 100; i++)
1710 : {
1711 0 : for (int iId = 0; iId < anIndexSize[i]; iId++)
1712 : {
1713 0 : if ((apapoRecordIndex[i])[iId] != nullptr)
1714 0 : delete (apapoRecordIndex[i])[iId];
1715 : }
1716 :
1717 0 : CPLFree(apapoRecordIndex[i]);
1718 0 : apapoRecordIndex[i] = nullptr;
1719 0 : anIndexSize[i] = 0;
1720 : }
1721 :
1722 0 : bIndexBuilt = FALSE;
1723 0 : }
1724 :
1725 : /************************************************************************/
1726 : /* GetIndexedRecord() */
1727 : /************************************************************************/
1728 :
1729 0 : NTFRecord *NTFFileReader::GetIndexedRecord(int iType, int iId)
1730 :
1731 : {
1732 0 : if ((iType < 0 || iType > 99) || (iId < 0 || iId >= anIndexSize[iType]) ||
1733 0 : (apapoRecordIndex[iType])[iId] == nullptr)
1734 : {
1735 : /* If NRT_GEOMETRY3D is an acceptable alternative to 2D */
1736 0 : if (iType == NRT_GEOMETRY)
1737 0 : return GetIndexedRecord(NRT_GEOMETRY3D, iId);
1738 : else
1739 0 : return nullptr;
1740 : }
1741 :
1742 0 : return (apapoRecordIndex[iType])[iId];
1743 : }
1744 :
1745 : /************************************************************************/
1746 : /* AddToIndexGroup() */
1747 : /************************************************************************/
1748 :
1749 0 : void NTFFileReader::AddToIndexGroup(NTFRecord *poRecord)
1750 :
1751 : {
1752 0 : int i = 1; // Used after for.
1753 0 : for (; apoCGroup[i] != nullptr; i++)
1754 : {
1755 0 : if (apoCGroup[i] == poRecord)
1756 : {
1757 0 : CPLError(CE_Failure, CPLE_AppDefined,
1758 : "Record already inserted in group");
1759 0 : return;
1760 : }
1761 : }
1762 0 : if (i == MAX_REC_GROUP)
1763 : {
1764 0 : CPLError(CE_Failure, CPLE_AppDefined,
1765 : "Maximum number of records in group reached");
1766 0 : delete poRecord;
1767 0 : return;
1768 : }
1769 :
1770 0 : apoCGroup[i] = poRecord;
1771 0 : apoCGroup[i + 1] = nullptr;
1772 : }
1773 :
1774 : /************************************************************************/
1775 : /* GetNextIndexedRecordGroup() */
1776 : /************************************************************************/
1777 :
1778 0 : NTFRecord **NTFFileReader::GetNextIndexedRecordGroup(NTFRecord **papoPrevGroup)
1779 :
1780 : {
1781 : int nPrevType, nPrevId;
1782 :
1783 : /* -------------------------------------------------------------------- */
1784 : /* What was the identify of our previous anchor record? */
1785 : /* -------------------------------------------------------------------- */
1786 0 : if (papoPrevGroup == nullptr || papoPrevGroup[0] == nullptr)
1787 : {
1788 0 : nPrevType = NRT_POINTREC;
1789 0 : nPrevId = 0;
1790 0 : FreshenIndex();
1791 : }
1792 : else
1793 : {
1794 0 : nPrevType = papoPrevGroup[0]->GetType();
1795 0 : nPrevId = atoi(papoPrevGroup[0]->GetField(3, 8));
1796 0 : if (nPrevId < 0)
1797 0 : return nullptr;
1798 : }
1799 :
1800 : /* -------------------------------------------------------------------- */
1801 : /* Find the next anchor record. */
1802 : /* -------------------------------------------------------------------- */
1803 0 : NTFRecord *poAnchor = nullptr;
1804 :
1805 0 : while (nPrevType != 99 && poAnchor == nullptr)
1806 : {
1807 0 : nPrevId++;
1808 0 : if (nPrevId >= anIndexSize[nPrevType])
1809 : {
1810 0 : do
1811 : {
1812 0 : nPrevType++;
1813 0 : } while (nPrevType != NRT_VTR && nPrevType != NRT_NODEREC &&
1814 0 : nPrevType != NRT_TEXTREC && nPrevType != NRT_NAMEREC &&
1815 0 : nPrevType != NRT_COLLECT && nPrevType != NRT_POLYGON &&
1816 0 : nPrevType != NRT_CPOLY && nPrevType != NRT_POINTREC &&
1817 : nPrevType != NRT_LINEREC);
1818 :
1819 0 : nPrevId = 0;
1820 : }
1821 : else
1822 : {
1823 0 : poAnchor = (apapoRecordIndex[nPrevType])[nPrevId];
1824 : }
1825 : }
1826 :
1827 0 : if (poAnchor == nullptr)
1828 : {
1829 0 : return nullptr;
1830 : }
1831 :
1832 : /* -------------------------------------------------------------------- */
1833 : /* Build record group depending on type of anchor and what it */
1834 : /* refers to. */
1835 : /* -------------------------------------------------------------------- */
1836 0 : apoCGroup[0] = nullptr;
1837 0 : apoCGroup[1] = poAnchor;
1838 0 : apoCGroup[2] = nullptr;
1839 :
1840 : /* -------------------------------------------------------------------- */
1841 : /* Handle POINTREC/LINEREC */
1842 : /* -------------------------------------------------------------------- */
1843 0 : if (poAnchor->GetType() == NRT_POINTREC ||
1844 0 : poAnchor->GetType() == NRT_LINEREC)
1845 : {
1846 0 : int l_nAttCount = 0;
1847 :
1848 0 : AddToIndexGroup(
1849 : GetIndexedRecord(NRT_GEOMETRY, atoi(poAnchor->GetField(9, 14))));
1850 :
1851 0 : if (poAnchor->GetLength() >= 16)
1852 0 : l_nAttCount = atoi(poAnchor->GetField(15, 16));
1853 :
1854 0 : for (int iAtt = 0; iAtt < l_nAttCount; iAtt++)
1855 : {
1856 0 : AddToIndexGroup(GetIndexedRecord(
1857 : NRT_ATTREC,
1858 0 : atoi(poAnchor->GetField(17 + 6 * iAtt, 22 + 6 * iAtt))));
1859 : }
1860 : }
1861 :
1862 : /* -------------------------------------------------------------------- */
1863 : /* Handle TEXTREC */
1864 : /* -------------------------------------------------------------------- */
1865 0 : else if (poAnchor->GetType() == NRT_TEXTREC)
1866 : {
1867 0 : int l_nAttCount = 0;
1868 0 : int nSelCount = 0;
1869 :
1870 : // Add all the text position records.
1871 0 : nSelCount = atoi(poAnchor->GetField(9, 10));
1872 0 : if (nSelCount < 0)
1873 0 : return nullptr;
1874 :
1875 0 : for (int iSel = 0; iSel < nSelCount; iSel++)
1876 : {
1877 0 : int iStart = 11 + 12 * iSel + 6;
1878 :
1879 0 : AddToIndexGroup(GetIndexedRecord(
1880 : NRT_TEXTPOS, atoi(poAnchor->GetField(iStart, iStart + 5))));
1881 : }
1882 :
1883 : // Add all geometry and TEXR records pointed to by text position
1884 : // records.
1885 0 : for (int iRec = 1; apoCGroup[iRec] != nullptr; iRec++)
1886 : {
1887 0 : NTFRecord *poRecord = apoCGroup[iRec];
1888 :
1889 0 : if (poRecord->GetType() != NRT_TEXTPOS)
1890 0 : continue;
1891 :
1892 0 : const int nNumTEXR = atoi(poRecord->GetField(9, 10));
1893 0 : for (int iTEXR = 0; iTEXR < nNumTEXR; iTEXR++)
1894 : {
1895 0 : AddToIndexGroup(GetIndexedRecord(
1896 0 : NRT_TEXTREP, atoi(poRecord->GetField(11 + iTEXR * 12,
1897 0 : 16 + iTEXR * 12))));
1898 0 : AddToIndexGroup(GetIndexedRecord(
1899 0 : NRT_GEOMETRY, atoi(poRecord->GetField(17 + iTEXR * 12,
1900 0 : 22 + iTEXR * 12))));
1901 : }
1902 : }
1903 :
1904 : // Add all the attribute records.
1905 0 : if (poAnchor->GetLength() >= 10 + nSelCount * 12 + 2)
1906 0 : l_nAttCount = atoi(
1907 0 : poAnchor->GetField(11 + nSelCount * 12, 12 + nSelCount * 12));
1908 :
1909 0 : for (int iAtt = 0; iAtt < l_nAttCount; iAtt++)
1910 : {
1911 0 : int iStart = 13 + nSelCount * 12 + 6 * iAtt;
1912 :
1913 0 : AddToIndexGroup(GetIndexedRecord(
1914 : NRT_ATTREC, atoi(poAnchor->GetField(iStart, iStart + 5))));
1915 : }
1916 : }
1917 :
1918 : /* -------------------------------------------------------------------- */
1919 : /* Handle NODEREC. */
1920 : /* -------------------------------------------------------------------- */
1921 0 : else if (poAnchor->GetType() == NRT_NODEREC)
1922 : {
1923 0 : AddToIndexGroup(
1924 : GetIndexedRecord(NRT_GEOMETRY, atoi(poAnchor->GetField(9, 14))));
1925 : }
1926 :
1927 : /* -------------------------------------------------------------------- */
1928 : /* Handle COLLECT. */
1929 : /* -------------------------------------------------------------------- */
1930 0 : else if (poAnchor->GetType() == NRT_COLLECT)
1931 : {
1932 0 : const int nParts = atoi(poAnchor->GetField(9, 12));
1933 0 : if (nParts < 0)
1934 0 : return nullptr;
1935 0 : const int nAttOffset = 13 + nParts * 8;
1936 0 : int l_nAttCount = 0;
1937 :
1938 0 : if (poAnchor->GetLength() > nAttOffset + 2)
1939 0 : l_nAttCount = atoi(poAnchor->GetField(nAttOffset, nAttOffset + 1));
1940 :
1941 0 : for (int iAtt = 0; iAtt < l_nAttCount; iAtt++)
1942 : {
1943 0 : const int iStart = nAttOffset + 2 + iAtt * 6;
1944 :
1945 0 : AddToIndexGroup(GetIndexedRecord(
1946 : NRT_ATTREC, atoi(poAnchor->GetField(iStart, iStart + 5))));
1947 : }
1948 : }
1949 :
1950 : /* -------------------------------------------------------------------- */
1951 : /* Handle POLYGON */
1952 : /* -------------------------------------------------------------------- */
1953 0 : else if (poAnchor->GetType() == NRT_POLYGON)
1954 : {
1955 0 : AddToIndexGroup(
1956 : GetIndexedRecord(NRT_CHAIN, atoi(poAnchor->GetField(9, 14))));
1957 :
1958 0 : if (poAnchor->GetLength() >= 20)
1959 0 : AddToIndexGroup(GetIndexedRecord(NRT_GEOMETRY,
1960 : atoi(poAnchor->GetField(15, 20))));
1961 :
1962 : // Attributes
1963 0 : int l_nAttCount = 0;
1964 :
1965 0 : if (poAnchor->GetLength() >= 22)
1966 0 : l_nAttCount = atoi(poAnchor->GetField(21, 22));
1967 :
1968 0 : for (int iAtt = 0; iAtt < l_nAttCount; iAtt++)
1969 : {
1970 0 : AddToIndexGroup(GetIndexedRecord(
1971 : NRT_ATTREC,
1972 0 : atoi(poAnchor->GetField(23 + 6 * iAtt, 28 + 6 * iAtt))));
1973 : }
1974 : }
1975 : /* -------------------------------------------------------------------- */
1976 : /* Handle CPOLY */
1977 : /* -------------------------------------------------------------------- */
1978 0 : else if (poAnchor->GetType() == NRT_CPOLY)
1979 : {
1980 0 : int nPolyCount = atoi(poAnchor->GetField(9, 12));
1981 0 : if (nPolyCount < 0)
1982 0 : return nullptr;
1983 0 : int nPostPoly = nPolyCount * 7 + 12;
1984 :
1985 0 : if (poAnchor->GetLength() >= nPostPoly + 6)
1986 : {
1987 : int nGeomId =
1988 0 : atoi(poAnchor->GetField(nPostPoly + 1, nPostPoly + 6));
1989 :
1990 0 : AddToIndexGroup(GetIndexedRecord(NRT_GEOMETRY, nGeomId));
1991 : }
1992 :
1993 0 : if (poAnchor->GetLength() >= nPostPoly + 8)
1994 : {
1995 : int l_nAttCount =
1996 0 : atoi(poAnchor->GetField(nPostPoly + 7, nPostPoly + 8));
1997 :
1998 0 : for (int iAtt = 0; iAtt < l_nAttCount; iAtt++)
1999 : {
2000 0 : int nAttId = atoi(poAnchor->GetField(
2001 0 : nPostPoly + 9 + iAtt * 6, nPostPoly + 14 + iAtt * 6));
2002 0 : AddToIndexGroup(GetIndexedRecord(NRT_ATTREC, nAttId));
2003 : }
2004 : }
2005 : }
2006 :
2007 0 : return apoCGroup + 1;
2008 : }
2009 :
2010 : /************************************************************************/
2011 : /* OverrideTileName() */
2012 : /************************************************************************/
2013 :
2014 0 : void NTFFileReader::OverrideTileName(const char *pszNewName)
2015 :
2016 : {
2017 0 : CPLFree(pszTileName);
2018 0 : pszTileName = CPLStrdup(pszNewName);
2019 0 : }
2020 :
2021 : /************************************************************************/
2022 : /* CacheAddByGeomId() */
2023 : /* */
2024 : /* Add a geometry to the geometry cache given its GEOMID as */
2025 : /* the index. */
2026 : /************************************************************************/
2027 :
2028 0 : void NTFFileReader::CacheAddByGeomId(int nGeomId, OGRGeometry *poGeometry)
2029 :
2030 : {
2031 0 : if (!bCacheLines)
2032 0 : return;
2033 :
2034 0 : CPLAssert(nGeomId >= 0);
2035 :
2036 : /* -------------------------------------------------------------------- */
2037 : /* Grow the cache if it isn't large enough to hold the newly */
2038 : /* requested geometry id. */
2039 : /* -------------------------------------------------------------------- */
2040 0 : if (nGeomId >= nLineCacheSize)
2041 : {
2042 0 : const int nNewSize = nGeomId + 100;
2043 :
2044 0 : papoLineCache = static_cast<OGRGeometry **>(
2045 0 : CPLRealloc(papoLineCache, sizeof(void *) * nNewSize));
2046 0 : memset(papoLineCache + nLineCacheSize, 0,
2047 0 : sizeof(void *) * (nNewSize - nLineCacheSize));
2048 0 : nLineCacheSize = nNewSize;
2049 : }
2050 :
2051 : /* -------------------------------------------------------------------- */
2052 : /* Make a cloned copy of the geometry for the cache. */
2053 : /* -------------------------------------------------------------------- */
2054 0 : if (papoLineCache[nGeomId] != nullptr)
2055 0 : return;
2056 :
2057 0 : papoLineCache[nGeomId] = poGeometry->clone();
2058 : }
2059 :
2060 : /************************************************************************/
2061 : /* CacheGetByGeomId() */
2062 : /************************************************************************/
2063 :
2064 0 : OGRGeometry *NTFFileReader::CacheGetByGeomId(int nGeomId)
2065 :
2066 : {
2067 0 : if (nGeomId < 0 || nGeomId >= nLineCacheSize)
2068 0 : return nullptr;
2069 : else
2070 0 : return papoLineCache[nGeomId];
2071 : }
2072 :
2073 : /************************************************************************/
2074 : /* CacheClean() */
2075 : /************************************************************************/
2076 :
2077 0 : void NTFFileReader::CacheClean()
2078 :
2079 : {
2080 0 : for (int i = 0; i < nLineCacheSize; i++)
2081 : {
2082 0 : if (papoLineCache[i] != nullptr)
2083 0 : delete papoLineCache[i];
2084 : }
2085 0 : if (papoLineCache != nullptr)
2086 0 : CPLFree(papoLineCache);
2087 :
2088 0 : nLineCacheSize = 0;
2089 0 : papoLineCache = nullptr;
2090 0 : }
2091 :
2092 : /************************************************************************/
2093 : /* CacheLineGeometryInGroup() */
2094 : /* */
2095 : /* Run any line geometries in this group through the */
2096 : /* ProcessGeometry() call just to ensure the line geometry will */
2097 : /* be cached. */
2098 : /************************************************************************/
2099 :
2100 0 : void NTFFileReader::CacheLineGeometryInGroup(NTFRecord **papoGroup)
2101 :
2102 : {
2103 0 : if (!bCacheLines)
2104 0 : return;
2105 :
2106 0 : for (int iRec = 0; papoGroup[iRec] != nullptr; iRec++)
2107 : {
2108 0 : if (papoGroup[iRec]->GetType() == NRT_GEOMETRY ||
2109 0 : papoGroup[iRec]->GetType() == NRT_GEOMETRY3D)
2110 : {
2111 0 : OGRGeometry *poGeom = ProcessGeometry(papoGroup[iRec], nullptr);
2112 0 : if (poGeom != nullptr)
2113 0 : delete poGeom;
2114 : }
2115 : }
2116 : }
2117 :
2118 : /************************************************************************/
2119 : /* FormPolygonFromCache() */
2120 : /* */
2121 : /* This method will attempt to find the line geometries */
2122 : /* referenced by the GEOM_ID_OF_LINK ids of a feature in the */
2123 : /* line cache (if available), and if so, assemble them into a */
2124 : /* polygon. */
2125 : /************************************************************************/
2126 :
2127 0 : int NTFFileReader::FormPolygonFromCache(OGRFeature *poFeature)
2128 :
2129 : {
2130 0 : if (!bCacheLines)
2131 0 : return FALSE;
2132 :
2133 : /* -------------------------------------------------------------------- */
2134 : /* Collect all the linked lines. */
2135 : /* -------------------------------------------------------------------- */
2136 0 : int nLinkCount = 0;
2137 : const int *panLinks =
2138 0 : poFeature->GetFieldAsIntegerList("GEOM_ID_OF_LINK", &nLinkCount);
2139 :
2140 0 : if (panLinks == nullptr)
2141 0 : return FALSE;
2142 :
2143 0 : OGRGeometryCollection oLines;
2144 :
2145 0 : for (int i = 0; i < nLinkCount; i++)
2146 : {
2147 0 : OGRGeometry *poLine = CacheGetByGeomId(panLinks[i]);
2148 0 : if (poLine == nullptr)
2149 : {
2150 0 : oLines.removeGeometry(-1, FALSE);
2151 0 : return FALSE;
2152 : }
2153 :
2154 0 : oLines.addGeometryDirectly(poLine);
2155 : }
2156 :
2157 : /* -------------------------------------------------------------------- */
2158 : /* Assemble into a polygon geometry. */
2159 : /* -------------------------------------------------------------------- */
2160 0 : OGRGeometry *poGeom = OGRGeometry::FromHandle(OGRBuildPolygonFromEdges(
2161 : (OGRGeometryH)&oLines, FALSE, FALSE, 0.1, nullptr));
2162 :
2163 0 : poFeature->SetGeometryDirectly(poGeom);
2164 :
2165 0 : oLines.removeGeometry(-1, FALSE);
2166 :
2167 0 : return poGeom != nullptr;
2168 : }
|