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