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