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