1 /******************************************************************************
2  *
3  * Project:  S-57 Translator
4  * Purpose:  Implements S57Reader class.
5  * Author:   Frank Warmerdam, warmerdam@pobox.com
6  *
7  ******************************************************************************
8  * Copyright (c) 1999, 2001, Frank Warmerdam
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26  * DEALINGS IN THE SOFTWARE.
27  ******************************************************************************
28  *
29  * *
30  */
31 
32 #include <assert.h>
33 #include "s57.h"
34 #include "gdal/ogr_api.h"
35 #include "gdal/cpl_conv.h"
36 #include "gdal/cpl_string.h"
37 #include "ogr_s57.h"
38 
39 /************************************************************************/
40 /*                             S57Reader()                              */
41 /************************************************************************/
42 
S57Reader(const char * pszFilename)43 S57Reader::S57Reader( const char * pszFilename )
44 
45 {
46     pszModuleName = CPLStrdup( pszFilename );
47     pszDSNM = NULL;
48 
49     poModule = NULL;
50 
51     nFDefnCount = 0;
52     papoFDefnList = NULL;
53 
54     nCOMF = 1000000;
55     nSOMF = 10;
56 
57     poRegistrar = NULL;
58     bFileIngested = FALSE;
59 
60     nNextFEIndex = 0;
61     nNextVIIndex = 0;
62     nNextVCIndex = 0;
63     nNextVEIndex = 0;
64     nNextVFIndex = 0;
65 
66     iPointOffset = 0;
67     poMultiPoint = NULL;
68 
69     papszOptions = NULL;
70 
71     nOptionFlags = S57M_UPDATES;
72 
73     bMissingWarningIssued = FALSE;
74     bAttrWarningIssued = FALSE;
75 
76     Nall = 0;
77     Aall = 0;
78 }
79 
80 /************************************************************************/
81 /*                             ~S57Reader()                             */
82 /************************************************************************/
83 
~S57Reader()84 S57Reader::~S57Reader()
85 
86 {
87     Close();
88 
89     CPLFree( pszModuleName );
90     CSLDestroy( papszOptions );
91 
92     CPLFree( papoFDefnList );
93 }
94 
95 /************************************************************************/
96 /*                                Open()                                */
97 /************************************************************************/
98 
Open(int bTestOpen)99 int S57Reader::Open( int bTestOpen )
100 
101 {
102     if( poModule != NULL )
103     {
104         Rewind();
105         return TRUE;
106     }
107 
108     poModule = new DDFModule();
109     if( !poModule->Open( pszModuleName ) )
110     {
111         // notdef: test bTestOpen.
112         delete poModule;
113         poModule = NULL;
114         return FALSE;
115     }
116 
117     // note that the following won't work for catalogs.
118     if( poModule->FindFieldDefn("DSID") == NULL )
119     {
120         if( !bTestOpen )
121         {
122             CPLError( CE_Failure, CPLE_AppDefined,
123                       "%s is an ISO8211 file, but not an S-57 data file.\n",
124                       pszModuleName );
125         }
126         delete poModule;
127         poModule = NULL;
128         return FALSE;
129     }
130 
131     // Make sure the FSPT field is marked as repeating.
132     DDFFieldDefn *poFSPT = poModule->FindFieldDefn( "FSPT" );
133     if( poFSPT != NULL && !poFSPT->IsRepeating() )
134     {
135         CPLDebug( "S57", "Forcing FSPT field to be repeating." );
136         poFSPT->SetRepeatingFlag( TRUE );
137     }
138 
139     nNextFEIndex = 0;
140     nNextVIIndex = 0;
141     nNextVCIndex = 0;
142     nNextVEIndex = 0;
143     nNextVFIndex = 0;
144 
145     return TRUE;
146 }
147 
148 /************************************************************************/
149 /*                               Close()                                */
150 /************************************************************************/
151 
Close()152 void S57Reader::Close()
153 
154 {
155     if( poModule != NULL )
156     {
157         oVI_Index.Clear();
158         oVC_Index.Clear();
159         oVE_Index.Clear();
160         oVF_Index.Clear();
161         oFE_Index.Clear();
162 
163         ClearPendingMultiPoint();
164 
165         delete poModule;
166         poModule = NULL;
167 
168         bFileIngested = FALSE;
169 
170         CPLFreeConfig();
171 
172         CPLFree( pszDSNM );
173         pszDSNM = NULL;
174     }
175 }
176 
177 /************************************************************************/
178 /*                       ClearPendingMultiPoint()                       */
179 /************************************************************************/
180 
ClearPendingMultiPoint()181 void S57Reader::ClearPendingMultiPoint()
182 
183 {
184     if( poMultiPoint != NULL )
185     {
186         delete poMultiPoint;
187         poMultiPoint = NULL;
188     }
189 }
190 
191 /************************************************************************/
192 /*                       NextPendingMultiPoint()                        */
193 /************************************************************************/
194 
NextPendingMultiPoint()195 OGRFeature *S57Reader::NextPendingMultiPoint()
196 
197 {
198     CPLAssert( poMultiPoint != NULL );
199     CPLAssert( wkbFlatten(poMultiPoint->GetGeometryRef()->getGeometryType())
200                                                         == wkbMultiPoint );
201 
202     OGRFeatureDefn *poDefn = poMultiPoint->GetDefnRef();
203     OGRFeature  *poPoint = new OGRFeature( poDefn );
204     OGRMultiPoint *poMPGeom = (OGRMultiPoint *) poMultiPoint->GetGeometryRef();
205     OGRPoint    *poSrcPoint;
206 
207     poPoint->SetFID( poMultiPoint->GetFID() );
208 
209     for( int i = 0; i < poDefn->GetFieldCount(); i++ )
210     {
211         poPoint->SetField( i, poMultiPoint->GetRawFieldRef(i) );
212     }
213 
214     poSrcPoint = (OGRPoint *) poMPGeom->getGeometryRef( iPointOffset++ );
215     poPoint->SetGeometry( poSrcPoint );
216 
217     poPoint->SetField( "DEPTH", poSrcPoint->getZ() );
218 
219     if( iPointOffset >= poMPGeom->getNumGeometries() )
220         ClearPendingMultiPoint();
221 
222     return poPoint;
223 }
224 
225 /************************************************************************/
226 /*                             SetOptions()                             */
227 /************************************************************************/
228 
SetOptions(char ** papszOptionsIn)229 void S57Reader::SetOptions( char ** papszOptionsIn )
230 
231 {
232     const char * pszOptionValue;
233 
234     CSLDestroy( papszOptions );
235     papszOptions = CSLDuplicate( papszOptionsIn );
236 
237     pszOptionValue = CSLFetchNameValue( papszOptions, S57O_SPLIT_MULTIPOINT );
238     if( pszOptionValue != NULL && !EQUAL(pszOptionValue,"OFF") )
239         nOptionFlags |= S57M_SPLIT_MULTIPOINT;
240     else
241         nOptionFlags &= ~S57M_SPLIT_MULTIPOINT;
242 
243     pszOptionValue = CSLFetchNameValue( papszOptions, S57O_ADD_SOUNDG_DEPTH );
244     if( pszOptionValue != NULL && !EQUAL(pszOptionValue,"OFF") )
245         nOptionFlags |= S57M_ADD_SOUNDG_DEPTH;
246     else
247         nOptionFlags &= ~S57M_ADD_SOUNDG_DEPTH;
248 
249     CPLAssert( ! (nOptionFlags & S57M_ADD_SOUNDG_DEPTH)
250                || (nOptionFlags & S57M_SPLIT_MULTIPOINT) );
251 
252     pszOptionValue = CSLFetchNameValue( papszOptions, S57O_LNAM_REFS );
253     if( pszOptionValue != NULL && !EQUAL(pszOptionValue,"OFF") )
254         nOptionFlags |= S57M_LNAM_REFS;
255     else
256         nOptionFlags &= ~S57M_LNAM_REFS;
257 
258     pszOptionValue = CSLFetchNameValue( papszOptions, S57O_UPDATES );
259     if( pszOptionValue != NULL && !EQUAL(pszOptionValue,"OFF") )
260         nOptionFlags |= S57M_UPDATES;
261     else
262         nOptionFlags &= ~S57M_UPDATES;
263 
264     pszOptionValue = CSLFetchNameValue(papszOptions,
265                                        S57O_PRESERVE_EMPTY_NUMBERS);
266     if( pszOptionValue != NULL && !EQUAL(pszOptionValue,"OFF") )
267         nOptionFlags |= S57M_PRESERVE_EMPTY_NUMBERS;
268     else
269         nOptionFlags &= ~S57M_PRESERVE_EMPTY_NUMBERS;
270 
271     pszOptionValue = CSLFetchNameValue( papszOptions, S57O_RETURN_PRIMITIVES );
272     if( pszOptionValue != NULL && !EQUAL(pszOptionValue,"OFF") )
273         nOptionFlags |= S57M_RETURN_PRIMITIVES;
274     else
275         nOptionFlags &= ~S57M_RETURN_PRIMITIVES;
276 
277     pszOptionValue = CSLFetchNameValue( papszOptions, S57O_RETURN_LINKAGES );
278     if( pszOptionValue != NULL && !EQUAL(pszOptionValue,"OFF") )
279         nOptionFlags |= S57M_RETURN_LINKAGES;
280     else
281         nOptionFlags &= ~S57M_RETURN_LINKAGES;
282 }
283 
284 /************************************************************************/
285 /*                           SetClassBased()                            */
286 /************************************************************************/
287 
SetClassBased(S57ClassRegistrar * poReg)288 void S57Reader::SetClassBased( S57ClassRegistrar * poReg )
289 
290 {
291     poRegistrar = poReg;
292 }
293 
294 /************************************************************************/
295 /*                               Rewind()                               */
296 /************************************************************************/
297 
Rewind()298 void S57Reader::Rewind()
299 
300 {
301     ClearPendingMultiPoint();
302     nNextFEIndex = 0;
303     nNextVIIndex = 0;
304     nNextVCIndex = 0;
305     nNextVEIndex = 0;
306     nNextVFIndex = 0;
307 }
308 
309 /************************************************************************/
310 /*                               Ingest()                               */
311 /*                                                                      */
312 /*      Read all the records into memory, adding to the appropriate     */
313 /*      indexes.                                                        */
314 /************************************************************************/
315 
Ingest(CallBackFunction pcallback)316 int S57Reader::Ingest(CallBackFunction pcallback)
317 {
318     DDFRecord   *poRecord;
319 
320     CPLSetConfigOption( "CPL_DEBUG", "S57" );
321 
322     if( poModule == NULL || bFileIngested )
323         return 0;
324 
325 /* -------------------------------------------------------------------- */
326 /*      Read all the records in the module, and place them in           */
327 /*      appropriate indexes.                                            */
328 /* -------------------------------------------------------------------- */
329     while( (poRecord = poModule->ReadRecord()) != NULL )
330     {
331           if(pcallback)
332           {
333             if (!(*pcallback)())
334               return 0;
335           }
336 
337         DDFField        *poKeyField = poRecord->GetField(1);
338         const char *pszname = poKeyField->GetFieldDefn()->GetName();
339 
340         if( EQUAL(pszname,"VRID") )
341         {
342 #if 0
343             int         nRCNM = poRecord->GetIntSubfield( "VRID",0, "RCNM",0);
344             int         nRCID = poRecord->GetIntSubfield( "VRID",0, "RCID",0);
345 #else
346             int nRCNM = 0, nRCID = 0;
347             DDFField *poField = poRecord->FindField( "VRID", 0 );
348             if( poField ) {
349                 int         nBytesRemaining;
350                 DDFSubfieldDefn     *poSFDefn;
351                 poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( "RCNM" );
352                 if( poSFDefn ) {
353                     const char *pachData = poField->GetSubfieldData(poSFDefn, &nBytesRemaining, 0);
354                     nRCNM = poSFDefn->ExtractIntData( pachData, nBytesRemaining, NULL );
355                 }
356 
357                 poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( "RCID" );
358                 if( poSFDefn ) {
359                     const char *pachData = poField->GetSubfieldData(poSFDefn, &nBytesRemaining, 0);
360                     nRCID = poSFDefn->ExtractIntData( pachData, nBytesRemaining, NULL );
361                 }
362             }
363 #endif
364 
365             switch( nRCNM )
366             {
367               case RCNM_VI:
368                 oVI_Index.AddRecord( nRCID, poRecord->Copy() );
369                 break;
370 
371               case RCNM_VC:
372                 oVC_Index.AddRecord( nRCID, poRecord->Copy() );
373                 break;
374 
375               case RCNM_VE:
376                 oVE_Index.AddRecord( nRCID, poRecord->Copy() );
377                 break;
378 
379               case RCNM_VF:
380                 oVF_Index.AddRecord( nRCID, poRecord->Copy() );
381                 break;
382 
383               default:
384                 CPLAssert( FALSE );
385                 break;
386             }
387 
388         }
389 
390         //  Feature records
391         else if( EQUAL(pszname,"FRID") )
392         {
393 
394 //              poRecord->Dump(stderr);               //  for debugging, try ./opencpn &>test.dbg
395 
396             int         nRCID = poRecord->GetIntSubfield( "FRID",0, "RCID",0);
397             oFE_Index.AddRecord( nRCID, poRecord->Copy() );
398 
399         }
400 
401 
402         //  Convenience values
403         else if( EQUAL(pszname,"DSPM") )
404         {
405             nCOMF = MAX(1,poRecord->GetIntSubfield( "DSPM",0, "COMF",0));
406             nSOMF = MAX(1,poRecord->GetIntSubfield( "DSPM",0, "SOMF",0));
407             nCSCL = MAX(1,poRecord->GetIntSubfield( "DSPM",0, "CSCL",0));
408 
409         }
410 
411         else if( EQUAL(pszname,"DSID") )
412         {
413             CPLFree( pszDSNM );
414             pszDSNM = CPLStrdup(poRecord->GetStringSubfield( "DSID", 0, "DSNM", 0 ));
415             Nall = poRecord->GetIntSubfield( "DSSI", 0, "NALL", 0 );
416             Aall = poRecord->GetIntSubfield( "DSSI", 0, "AALL", 0 );
417         }
418 
419         else
420         {
421             CPLDebug( "S57",
422                       "Skipping %s record in S57Reader::Ingest().\n",
423                       poKeyField->GetFieldDefn()->GetName() );
424         }
425 
426     }
427 
428     bFileIngested = TRUE;
429 
430 
431 /* -------------------------------------------------------------------- */
432 /*      If update support is enabled, read and apply them.              */
433 /* -------------------------------------------------------------------- */
434     int update_return = 0;
435     if( nOptionFlags & S57M_UPDATES )
436         update_return = FindAndApplyUpdates();
437 
438     return update_return;
439 }
440 
441 /************************************************************************/
442 /*                           SetNextFEIndex()                           */
443 /************************************************************************/
444 
SetNextFEIndex(int nNewIndex,int nRCNM)445 void S57Reader::SetNextFEIndex( int nNewIndex, int nRCNM )
446 
447 {
448     if( nRCNM == RCNM_VI )
449         nNextVIIndex = nNewIndex;
450     else if( nRCNM == RCNM_VC )
451         nNextVCIndex = nNewIndex;
452     else if( nRCNM == RCNM_VE )
453         nNextVEIndex = nNewIndex;
454     else if( nRCNM == RCNM_VF )
455         nNextVFIndex = nNewIndex;
456     else
457     {
458         if( nNextFEIndex != nNewIndex )
459             ClearPendingMultiPoint();
460 
461         nNextFEIndex = nNewIndex;
462     }
463 }
464 
465 /************************************************************************/
466 /*                           GetNextFEIndex()                           */
467 /************************************************************************/
468 
GetNextFEIndex(int nRCNM)469 int S57Reader::GetNextFEIndex( int nRCNM )
470 
471 {
472     if( nRCNM == RCNM_VI )
473         return nNextVIIndex;
474     else if( nRCNM == RCNM_VC )
475         return nNextVCIndex;
476     else if( nRCNM == RCNM_VE )
477         return nNextVEIndex;
478     else if( nRCNM == RCNM_VF )
479         return nNextVFIndex;
480     else
481         return nNextFEIndex;
482 }
483 
484 /************************************************************************/
485 /*                          ReadNextFeature()                           */
486 /************************************************************************/
487 
ReadNextFeature(OGRFeatureDefn * poTarget)488 OGRFeature * S57Reader::ReadNextFeature( OGRFeatureDefn * poTarget )
489 
490 {
491     if( !bFileIngested )
492         Ingest();
493 
494 /* -------------------------------------------------------------------- */
495 /*      Special case for "in progress" multipoints being split up.      */
496 /* -------------------------------------------------------------------- */
497     if( poMultiPoint != NULL )
498     {
499         if( poTarget == NULL || poTarget == poMultiPoint->GetDefnRef() )
500         {
501             return NextPendingMultiPoint();
502         }
503         else
504         {
505             ClearPendingMultiPoint();
506         }
507     }
508 
509 /* -------------------------------------------------------------------- */
510 /*      Next vector feature?                                            */
511 /* -------------------------------------------------------------------- */
512     if( nOptionFlags & S57M_RETURN_PRIMITIVES )
513     {
514         int nRCNM = 0;
515         int *pnCounter = NULL;
516 
517         if( poTarget == NULL )
518         {
519             if( nNextVIIndex < oVI_Index.GetCount() )
520             {
521                 nRCNM = RCNM_VI;
522                 pnCounter = &nNextVIIndex;
523             }
524             else if( nNextVCIndex < oVC_Index.GetCount() )
525             {
526                 nRCNM = RCNM_VC;
527                 pnCounter = &nNextVCIndex;
528             }
529             else if( nNextVEIndex < oVE_Index.GetCount() )
530             {
531                 nRCNM = RCNM_VE;
532                 pnCounter = &nNextVEIndex;
533             }
534             else if( nNextVFIndex < oVF_Index.GetCount() )
535             {
536                 nRCNM = RCNM_VF;
537                 pnCounter = &nNextVFIndex;
538             }
539         }
540         else
541         {
542             if( EQUAL(poTarget->GetName(),OGRN_VI) )
543             {
544                 nRCNM = RCNM_VI;
545                 pnCounter = &nNextVIIndex;
546             }
547             else if( EQUAL(poTarget->GetName(),OGRN_VC) )
548             {
549                 nRCNM = RCNM_VC;
550                 pnCounter = &nNextVCIndex;
551             }
552             else if( EQUAL(poTarget->GetName(),OGRN_VE) )
553             {
554                 nRCNM = RCNM_VE;
555                 pnCounter = &nNextVEIndex;
556             }
557             else if( EQUAL(poTarget->GetName(),OGRN_VF) )
558             {
559                 nRCNM = RCNM_VF;
560                 pnCounter = &nNextVFIndex;
561             }
562         }
563 
564         if( nRCNM != 0 )
565         {
566             OGRFeature *poFeature = ReadVector( *pnCounter, nRCNM );
567             if( poFeature != NULL )
568             {
569                 *pnCounter += 1;
570                 return poFeature;
571             }
572         }
573     }
574 
575 /* -------------------------------------------------------------------- */
576 /*      Next feature.                                                   */
577 /* -------------------------------------------------------------------- */
578     while( nNextFEIndex < oFE_Index.GetCount() )
579     {
580         OGRFeature      *poFeature;
581 
582         poFeature = ReadFeature( nNextFEIndex++, poTarget );
583         if( poFeature != NULL )
584         {
585             if( (nOptionFlags & S57M_SPLIT_MULTIPOINT)
586                 && poFeature->GetGeometryRef() != NULL
587                 && wkbFlatten(poFeature->GetGeometryRef()->getGeometryType())
588                                                         == wkbMultiPoint)
589             {
590                 poMultiPoint = poFeature;
591                 iPointOffset = 0;
592                 return NextPendingMultiPoint();
593             }
594 
595             return poFeature;
596         }
597     }
598 
599     return NULL;
600 }
601 
602 
603 /************************************************************************/
604 /*                            ReadFeature()                             */
605 /*                                                                      */
606 /*      Read the features who's id is provided.                         */
607 /************************************************************************/
608 /*
609 OGRFeature *S57Reader::ReadFeature( int nFeatureId, OGRFeatureDefn *poTarget )
610 
611 {
612     OGRFeature  *poFeature;
613 
614     if( nFeatureId < 0 || nFeatureId >= oFE_Index.GetCount() )
615         return NULL;
616 
617 
618     poFeature = AssembleFeature( oFE_Index.GetByIndex(nFeatureId),
619                                  poTarget );
620     if( poFeature != NULL )
621         poFeature->SetFID( nFeatureId );
622 
623     return poFeature;
624 }
625 
626 */
627 /************************************************************************/
628 /*                            ReadFeature()                             */
629 /*                                                                      */
630 /*      Read the features who's id is provided.                         */
631 /************************************************************************/
632 
ReadFeature(int nFeatureId,OGRFeatureDefn * poTarget)633 OGRFeature *S57Reader::ReadFeature( int nFeatureId, OGRFeatureDefn *poTarget )
634 
635 {
636     OGRFeature  *poFeature;
637 
638     if( nFeatureId < 0 || nFeatureId >= oFE_Index.GetCount() )
639         return NULL;
640 
641     DDFRecord *poRecord = oFE_Index.GetByIndex(nFeatureId);
642 
643     if(poTarget)
644     {
645             int nRecord_OBJL = poRecord->GetIntSubfield( "FRID", 0, "OBJL", 0 );
646             int nOBJL = poTarget->GetOBJL();
647 
648             if(nRecord_OBJL == nOBJL)              // tentative match
649             {
650                   poFeature = AssembleFeature( oFE_Index.GetByIndex(nFeatureId),
651                                  poTarget );
652 
653                   if( poFeature != NULL )
654                         poFeature->SetFID( nFeatureId );
655 
656                   return poFeature;
657             }
658             else
659                   return NULL;
660     }
661     else
662     {
663             poFeature = AssembleFeature( oFE_Index.GetByIndex(nFeatureId),
664                                  poTarget );
665             if( poFeature != NULL )
666                   poFeature->SetFID( nFeatureId );
667 
668             return poFeature;
669     }
670 
671 }
672 
673 
674 /************************************************************************/
675 /*                          AssembleFeature()                           */
676 /*                                                                      */
677 /*      Assemble an OGR feature based on a feature record.              */
678 /************************************************************************/
679 
AssembleFeature(DDFRecord * poRecord,OGRFeatureDefn * poTarget)680 OGRFeature *S57Reader::AssembleFeature( DDFRecord * poRecord,
681                                         OGRFeatureDefn * poTarget )
682 
683 {
684     int         nPRIM, nOBJL;
685     OGRFeatureDefn *poFDefn;
686 
687 /* -------------------------------------------------------------------- */
688 /*      Find the feature definition to use.  Currently this is based    */
689 /*      on the primitive, but eventually this should be based on the    */
690 /*      object class (FRID.OBJL) in some cases, and the primitive in    */
691 /*      others.                                                         */
692 /* -------------------------------------------------------------------- */
693     poFDefn = FindFDefn( poRecord );
694     if( poFDefn == NULL ){
695 
696         //  It is possible that a Feature was added by update, whose Class
697         //  was not already present in the module before updates.
698         //  So, if necessary, try to create and add an OGRFeatureDefn for this Feature.
699         int     nOBJL = poRecord->GetIntSubfield( "FRID", 0, "OBJL", 0 );
700         poFDefn = S57GenerateObjectClassDefn( poRegistrar, nOBJL,
701                                     this->GetOptionFlags() );
702         if(poFDefn)
703             AddFeatureDefn( poFDefn );
704         else
705             return NULL;
706     }
707 
708 /* -------------------------------------------------------------------- */
709 /*      Does this match our target feature definition?  If not skip     */
710 /*      this feature.                                                   */
711 /* -------------------------------------------------------------------- */
712     if( poTarget != NULL && poFDefn != poTarget )
713         return NULL;
714 
715 /* -------------------------------------------------------------------- */
716 /*      Create the new feature object.                                  */
717 /* -------------------------------------------------------------------- */
718     OGRFeature          *poFeature;
719 
720     poFeature = new OGRFeature( poFDefn );
721 
722 /* -------------------------------------------------------------------- */
723 /*      Assign a few standard feature attribues.                        */
724 /* -------------------------------------------------------------------- */
725     nOBJL = poRecord->GetIntSubfield( "FRID", 0, "OBJL", 0 );
726     poFeature->SetField( "OBJL", nOBJL );
727 
728     poFeature->SetField( "RCID",
729                          poRecord->GetIntSubfield( "FRID", 0, "RCID", 0 ));
730     poFeature->SetField( "PRIM",
731                          poRecord->GetIntSubfield( "FRID", 0, "PRIM", 0 ));
732     poFeature->SetField( "GRUP",
733                          poRecord->GetIntSubfield( "FRID", 0, "GRUP", 0 ));
734     poFeature->SetField( "RVER",
735                          poRecord->GetIntSubfield( "FRID", 0, "RVER", 0 ));
736     poFeature->SetField( "AGEN",
737                          poRecord->GetIntSubfield( "FOID", 0, "AGEN", 0 ));
738     poFeature->SetField( "FIDN",
739                          poRecord->GetIntSubfield( "FOID", 0, "FIDN", 0 ));
740     poFeature->SetField( "FIDS",
741                          poRecord->GetIntSubfield( "FOID", 0, "FIDS", 0 ));
742 
743 /* -------------------------------------------------------------------- */
744 /*      Generate long name, if requested.                               */
745 /* -------------------------------------------------------------------- */
746     if( nOptionFlags & S57M_LNAM_REFS )
747     {
748         GenerateLNAMAndRefs( poRecord, poFeature );
749     }
750 
751 /* -------------------------------------------------------------------- */
752 /*      Generate primitive references if requested.                     */
753 /* -------------------------------------------------------------------- */
754     if( nOptionFlags & S57M_RETURN_LINKAGES )
755         GenerateFSPTAttributes( poRecord, poFeature );
756 
757 /* -------------------------------------------------------------------- */
758 /*      Apply object class specific attributes, if supported.           */
759 /* -------------------------------------------------------------------- */
760     if( poRegistrar != NULL )
761         ApplyObjectClassAttributes( poRecord, poFeature );
762 
763 /* -------------------------------------------------------------------- */
764 /*      Find and assign spatial component.                              */
765 /* -------------------------------------------------------------------- */
766     nPRIM = poRecord->GetIntSubfield( "FRID", 0, "PRIM", 0 );
767 
768     if( nPRIM == PRIM_P )
769     {
770         if( nOBJL == 129 ) /* SOUNDG */
771             AssembleSoundingGeometry( poRecord, poFeature );
772         else
773             AssemblePointGeometry( poRecord, poFeature );
774     }
775     else if( nPRIM == PRIM_L )
776     {
777         AssembleLineGeometry( poRecord, poFeature );
778     }
779     else if( nPRIM == PRIM_A )
780     {
781         AssembleAreaGeometry( poRecord, poFeature );
782     }
783 
784     return poFeature;
785 }
786 
787 /************************************************************************/
788 /*                     ApplyObjectClassAttributes()                     */
789 /************************************************************************/
790 
ApplyObjectClassAttributes(DDFRecord * poRecord,OGRFeature * poFeature)791 void S57Reader::ApplyObjectClassAttributes( DDFRecord * poRecord,
792                                             OGRFeature * poFeature )
793 
794 {
795 /* -------------------------------------------------------------------- */
796 /*      ATTF Attributes                                                 */
797 /* -------------------------------------------------------------------- */
798     DDFField    *poATTF = poRecord->FindField( "ATTF" );
799     int         nAttrCount, iAttr;
800 
801     if( poATTF == NULL )
802         return;
803 
804     DDFFieldDefn *poDefn = poATTF->GetFieldDefn();
805 
806     nAttrCount = poATTF->GetRepeatCount();
807     for( iAttr = 0; iAttr < nAttrCount; iAttr++ )
808     {
809         int     nAttrId = poRecord->GetIntSubfield("ATTF",0,"ATTL",iAttr);
810         const char *pszAcronym;
811 
812         if( nAttrId < 1 || nAttrId > poRegistrar->GetMaxAttrIndex()
813             || (pszAcronym = poRegistrar->GetAttrAcronym(nAttrId)) == NULL )
814         {
815             if( !bAttrWarningIssued )
816             {
817                 bAttrWarningIssued = TRUE;
818                 CPLError( CE_Warning, CPLE_AppDefined,
819                         "Illegal feature attribute id (ATTF:ATTL[%d]) of %d\n"
820                         "on feature FIDN=%d, FIDS=%d.\n"
821                         "Skipping attribute, no more warnings will be issued.",
822                           iAttr, nAttrId,
823                           poFeature->GetFieldAsInteger( "FIDN" ),
824                           poFeature->GetFieldAsInteger( "FIDS" ) );
825             }
826 
827             continue;
828         }
829 
830         /* Fetch the attribute value */
831         const char *pszValue;
832         pszValue = poRecord->GetStringSubfield("ATTF",0,"ATVL",iAttr);
833 
834         /* Apply to feature in an appropriate way */
835         int iField;
836         OGRFieldDefn *poFldDefn;
837 
838         iField = poFeature->GetDefnRef()->GetFieldIndex(pszAcronym);
839         if( iField < 0 )
840         {
841             if( !bMissingWarningIssued )
842             {
843                 bMissingWarningIssued = TRUE;
844                 CPLError( CE_Warning, CPLE_AppDefined,
845                           "For feature \"%s\", attribute \"%s\" ignored, not in expected schema.\n",
846                           poFeature->GetDefnRef()->GetName(), pszAcronym );
847             }
848             continue;
849         }
850 
851         // Handle deleted attributes
852         // If the first char of the attribute is 0x7f, then unset this field.
853         // Any later requests for the attribute value will retrun an empty string.
854         if(pszValue[0] == 0x7f)
855         {
856             poFeature->UnsetField( iField );
857             continue;
858         }
859 
860         poFldDefn = poFeature->GetDefnRef()->GetFieldDefn( iField );
861         if( poFldDefn->GetType() == OFTInteger
862             || poFldDefn->GetType() == OFTReal )
863         {
864             if( strlen(pszValue) == 0 )
865             {
866                 if( nOptionFlags & S57M_PRESERVE_EMPTY_NUMBERS )
867                 {
868                     poFeature->SetField( iField, EMPTY_NUMBER_MARKER );
869                 }
870                 else
871                 {
872                     /* leave as null if value was empty string */;
873                 }
874             }
875             else
876             {
877                 poFeature->SetField( iField, pszValue );
878             }
879         }
880         else
881         {
882             poFeature->SetField( iField, pszValue );
883         }
884     }
885 
886 /* -------------------------------------------------------------------- */
887 /*      NATF (national) attributes                                      */
888 /* -------------------------------------------------------------------- */
889     DDFField    *poNATF = poRecord->FindField( "NATF" );
890 
891     if( poNATF == NULL )
892         return;
893 
894     nAttrCount = poNATF->GetRepeatCount();
895     for( iAttr = 0; iAttr < nAttrCount; iAttr++ )
896     {
897         int     nAttrId = poRecord->GetIntSubfield("NATF",0,"ATTL",iAttr);
898         const char *pszAcronym;
899 
900         if( nAttrId < 1 || nAttrId >= poRegistrar->GetMaxAttrIndex()
901             || (pszAcronym = poRegistrar->GetAttrAcronym(nAttrId)) == NULL )
902         {
903 //            poRecord->Dump(stdout);
904 //            int     xnAttrId = poRecord->GetIntSubfield("NATF",0,"ATTL",iAttr);
905             static int bAttrWarningIssued = FALSE;
906 
907             if( !bAttrWarningIssued )
908             {
909                 bAttrWarningIssued = TRUE;
910                 CPLError( CE_Warning, CPLE_AppDefined,
911                         "Illegal feature attribute id (NATF:ATTL[%d]) of %d\n"
912                         "on feature FIDN=%d, FIDS=%d.\n"
913                         "Skipping attribute, no more warnings will be issued.",
914                           iAttr, nAttrId,
915                           poFeature->GetFieldAsInteger( "FIDN" ),
916                           poFeature->GetFieldAsInteger( "FIDS" ) );
917             }
918 
919             continue;
920         }
921 
922         const char *pszValue = poRecord->GetStringSubfield("NATF",0,"ATVL",iAttr);
923         if( pszValue != NULL )
924         {
925 
926         //      If National Language strings are encoded as UCS-2 (a.k.a UTF-16)
927         //      then we capture and duplicate the attribute string directly,
928         //      in order to avoid truncation that would happen if it were considered a simple char *
929 
930             if(Nall==2) { //national string encoded in UCS-2, determined from DSID record
931 
932         //      Compute the data size
933                 DDFField    *poField;
934                 int nLength = 0;
935                 poField = poRecord->FindField( "NATF", 0 );
936                 if( poField ) {
937                     DDFSubfieldDefn     *poSFDefn;
938 
939                     poSFDefn = poField->GetFieldDefn()->FindSubfieldDefn( "ATVL" );
940                     if( poSFDefn ) {
941                         int max_length = 0;
942                         const char *pachData = poField->GetSubfieldData(poSFDefn, &max_length, iAttr);
943                         nLength = poSFDefn->GetDataLength( pachData, max_length, NULL);
944                     }
945                 }
946 
947                 if( nLength ) {
948                     //  Make the new length a multiple of 2, so that
949                     //  later stages will count chars correctly
950                     //  Also, be sure that the string ends with 00 00
951                     int new_len = ((nLength / 2) + 2)*2;
952                     char *aa = (char *)calloc(new_len, 1);
953                     memcpy(aa, pszValue, nLength);
954 
955                     int index = poFeature->GetFieldIndex(pszAcronym);
956                     OGRField *field = poFeature->GetRawFieldRef( index );
957                     field->String =  aa;
958                 }
959             }
960             else {      //  encoded as ISO8859_1, pass it along
961                 poFeature->SetField(pszAcronym,pszValue);
962             }
963         }
964 
965     }
966 }
967 
968 /************************************************************************/
969 /*                        generatelnamandrefs()                         */
970 /************************************************************************/
971 
GenerateLNAMAndRefs(DDFRecord * poRecord,OGRFeature * poFeature)972 void S57Reader::GenerateLNAMAndRefs( DDFRecord * poRecord,
973                                      OGRFeature * poFeature )
974 
975 {
976     char        szLNAM[32];
977 
978 /* -------------------------------------------------------------------- */
979 /*      Apply the LNAM to the object.                                   */
980 /* -------------------------------------------------------------------- */
981     sprintf( szLNAM, "%04X%08X%04X",
982              poFeature->GetFieldAsInteger( "AGEN" ),
983              poFeature->GetFieldAsInteger( "FIDN" ),
984              poFeature->GetFieldAsInteger( "FIDS" ) );
985     poFeature->SetField( "LNAM", szLNAM );
986 
987 /* -------------------------------------------------------------------- */
988 /*      Do we have references to other features.                        */
989 /* -------------------------------------------------------------------- */
990     DDFField    *poFFPT;
991 
992     poFFPT = poRecord->FindField( "FFPT" );
993 
994     if( poFFPT == NULL )
995         return;
996 
997 /* -------------------------------------------------------------------- */
998 /*      Apply references.                                               */
999 /* -------------------------------------------------------------------- */
1000     int         nRefCount = poFFPT->GetRepeatCount();
1001     DDFSubfieldDefn *poLNAM;
1002     char        **papszRefs = NULL;
1003     int         *panRIND = (int *) CPLMalloc(sizeof(int) * nRefCount);
1004 
1005     poLNAM = poFFPT->GetFieldDefn()->FindSubfieldDefn( "LNAM" );
1006     if( poLNAM == NULL )
1007         return;
1008 
1009     for( int iRef = 0; iRef < nRefCount; iRef++ )
1010     {
1011         unsigned char *pabyData;
1012 
1013         pabyData = (unsigned char *)
1014             poFFPT->GetSubfieldData( poLNAM, NULL, iRef );
1015 
1016         sprintf( szLNAM, "%02X%02X%02X%02X%02X%02X%02X%02X",
1017                  pabyData[1], pabyData[0], /* AGEN */
1018                  pabyData[5], pabyData[4], pabyData[3], pabyData[2], /* FIDN */
1019                  pabyData[7], pabyData[6] );
1020 
1021         papszRefs = CSLAddString( papszRefs, szLNAM );
1022 
1023         panRIND[iRef] = pabyData[8];
1024     }
1025 
1026     poFeature->SetField( "LNAM_REFS", papszRefs );
1027     CSLDestroy( papszRefs );
1028 
1029     poFeature->SetField( "FFPT_RIND", nRefCount, panRIND );
1030     CPLFree( panRIND );
1031 }
1032 
1033 /************************************************************************/
1034 /*                       GenerateFSPTAttributes()                       */
1035 /************************************************************************/
1036 
GenerateFSPTAttributes(DDFRecord * poRecord,OGRFeature * poFeature)1037 void S57Reader::GenerateFSPTAttributes( DDFRecord * poRecord,
1038                                         OGRFeature * poFeature )
1039 
1040 {
1041 /* -------------------------------------------------------------------- */
1042 /*      Feature the spatial record containing the point.                */
1043 /* -------------------------------------------------------------------- */
1044     DDFField    *poFSPT;
1045     int         nCount, i;
1046 
1047     poFSPT = poRecord->FindField( "FSPT" );
1048     if( poFSPT == NULL )
1049         return;
1050 
1051     nCount = poFSPT->GetRepeatCount();
1052 
1053 /* -------------------------------------------------------------------- */
1054 /*      Allocate working lists of the attributes.                       */
1055 /* -------------------------------------------------------------------- */
1056     int *panORNT, *panUSAG, *panMASK, *panRCNM, *panRCID;
1057 
1058     panORNT = (int *) CPLMalloc( sizeof(int) * nCount );
1059     panUSAG = (int *) CPLMalloc( sizeof(int) * nCount );
1060     panMASK = (int *) CPLMalloc( sizeof(int) * nCount );
1061     panRCNM = (int *) CPLMalloc( sizeof(int) * nCount );
1062     panRCID = (int *) CPLMalloc( sizeof(int) * nCount );
1063 
1064 /* -------------------------------------------------------------------- */
1065 /*      loop over all entries, decoding them.                           */
1066 /* -------------------------------------------------------------------- */
1067     for( i = 0; i < nCount; i++ )
1068     {
1069         panRCID[i] = ParseName( poFSPT, i, panRCNM + i );
1070         panORNT[i] = poRecord->GetIntSubfield( "FSPT", 0, "ORNT",i);
1071         panUSAG[i] = poRecord->GetIntSubfield( "FSPT", 0, "USAG",i);
1072         panMASK[i] = poRecord->GetIntSubfield( "FSPT", 0, "MASK",i);
1073     }
1074 
1075 /* -------------------------------------------------------------------- */
1076 /*      Assign to feature.                                              */
1077 /* -------------------------------------------------------------------- */
1078     poFeature->SetField( "NAME_RCNM", nCount, panRCNM );
1079     poFeature->SetField( "NAME_RCID", nCount, panRCID );
1080     poFeature->SetField( "ORNT", nCount, panORNT );
1081     poFeature->SetField( "USAG", nCount, panUSAG );
1082     poFeature->SetField( "MASK", nCount, panMASK );
1083 
1084 /* -------------------------------------------------------------------- */
1085 /*      Cleanup.                                                        */
1086 /* -------------------------------------------------------------------- */
1087     CPLFree( panRCNM );
1088     CPLFree( panRCID );
1089     CPLFree( panORNT );
1090     CPLFree( panUSAG );
1091     CPLFree( panMASK );
1092 }
1093 
1094 /************************************************************************/
1095 /*                             ReadVector()                             */
1096 /*                                                                      */
1097 /*      Read a vector primitive objects based on the type (RCNM_)       */
1098 /*      and index within the related index.                             */
1099 /************************************************************************/
1100 
ReadVector(int nFeatureId,int nRCNM)1101 OGRFeature *S57Reader::ReadVector( int nFeatureId, int nRCNM )
1102 
1103 {
1104     DDFRecordIndex *poIndex;
1105     const char *pszFDName = NULL;
1106 
1107 /* -------------------------------------------------------------------- */
1108 /*      What type of vector are we fetching.                            */
1109 /* -------------------------------------------------------------------- */
1110     switch( nRCNM )
1111     {
1112       case RCNM_VI:
1113         poIndex = &oVI_Index;
1114         pszFDName = OGRN_VI;
1115         break;
1116 
1117       case RCNM_VC:
1118         poIndex = &oVC_Index;
1119         pszFDName = OGRN_VC;
1120         break;
1121 
1122       case RCNM_VE:
1123         poIndex = &oVE_Index;
1124         pszFDName = OGRN_VE;
1125         break;
1126 
1127       case RCNM_VF:
1128         poIndex = &oVF_Index;
1129         pszFDName = OGRN_VF;
1130         break;
1131 
1132       default:
1133         CPLAssert( FALSE );
1134         return NULL;
1135     }
1136 
1137     if( nFeatureId < 0 || nFeatureId >= poIndex->GetCount() )
1138         return NULL;
1139 
1140     DDFRecord *poRecord = poIndex->GetByIndex( nFeatureId );
1141 
1142 /* -------------------------------------------------------------------- */
1143 /*      Find the feature definition to use.                             */
1144 /* -------------------------------------------------------------------- */
1145     OGRFeatureDefn *poFDefn = NULL;
1146 
1147     for( int i = 0; i < nFDefnCount; i++ )
1148     {
1149         if( EQUAL(papoFDefnList[i]->GetName(),pszFDName) )
1150         {
1151             poFDefn = papoFDefnList[i];
1152             break;
1153         }
1154     }
1155 
1156     if( poFDefn == NULL )
1157     {
1158         CPLAssert( FALSE );
1159         return NULL;
1160     }
1161 
1162 /* -------------------------------------------------------------------- */
1163 /*      Create feature, and assign standard fields.                     */
1164 /* -------------------------------------------------------------------- */
1165     OGRFeature *poFeature = new OGRFeature( poFDefn );
1166 
1167     poFeature->SetFID( nFeatureId );
1168 
1169     poFeature->SetField( "RCNM",
1170                          poRecord->GetIntSubfield( "VRID", 0, "RCNM",0) );
1171     poFeature->SetField( "RCID",
1172                          poRecord->GetIntSubfield( "VRID", 0, "RCID",0) );
1173     poFeature->SetField( "RVER",
1174                          poRecord->GetIntSubfield( "VRID", 0, "RVER",0) );
1175     poFeature->SetField( "RUIN",
1176                          poRecord->GetIntSubfield( "VRID", 0, "RUIN",0) );
1177 
1178 /* -------------------------------------------------------------------- */
1179 /*      Collect point geometries.                                       */
1180 /* -------------------------------------------------------------------- */
1181     if( nRCNM == RCNM_VI || nRCNM == RCNM_VC )
1182     {
1183         double dfX=0.0, dfY=0.0, dfZ=0.0;
1184 
1185         if( poRecord->FindField( "SG2D" ) != NULL )
1186         {
1187             dfX = poRecord->GetIntSubfield("SG2D",0,"XCOO",0) / (double)nCOMF;
1188             dfY = poRecord->GetIntSubfield("SG2D",0,"YCOO",0) / (double)nCOMF;
1189             poFeature->SetGeometryDirectly( new OGRPoint( dfX, dfY ) );
1190         }
1191 
1192         else if( poRecord->FindField( "SG3D" ) != NULL ) /* presume sounding*/
1193         {
1194             int i, nVCount = poRecord->FindField("SG3D")->GetRepeatCount();
1195             if( nVCount == 1 )
1196             {
1197                 dfX =poRecord->GetIntSubfield("SG3D",0,"XCOO",0)/(double)nCOMF;
1198                 dfY =poRecord->GetIntSubfield("SG3D",0,"YCOO",0)/(double)nCOMF;
1199                 dfZ =poRecord->GetIntSubfield("SG3D",0,"VE3D",0)/(double)nSOMF;
1200                 poFeature->SetGeometryDirectly( new OGRPoint( dfX, dfY, dfZ ));
1201             }
1202             else
1203             {
1204                 OGRMultiPoint *poMP = new OGRMultiPoint();
1205 
1206                 for( i = 0; i < nVCount; i++ )
1207                 {
1208                     dfX = poRecord->GetIntSubfield("SG3D",0,"XCOO",i)
1209                         / (double)nCOMF;
1210                     dfY = poRecord->GetIntSubfield("SG3D",0,"YCOO",i)
1211                         / (double)nCOMF;
1212                     dfZ = poRecord->GetIntSubfield("SG3D",0,"VE3D",i)
1213                         / (double)nSOMF;
1214 
1215                     poMP->addGeometryDirectly( new OGRPoint( dfX, dfY, dfZ ) );
1216                 }
1217 
1218                 poFeature->SetGeometryDirectly( poMP );
1219             }
1220         }
1221 
1222     }
1223 
1224 /* -------------------------------------------------------------------- */
1225 /*      Collect an edge geometry.                                       */
1226 /* -------------------------------------------------------------------- */
1227     else if( nRCNM == RCNM_VE && poRecord->FindField( "SG2D" ) != NULL )
1228     {
1229           int i, nVCount = poRecord->FindField("SG2D")->GetRepeatCount();
1230 
1231           if(nVCount == 1)
1232           {
1233 //              poRecord->Dump(stdout);
1234                 OGRLineString *poLine = new OGRLineString();
1235 
1236                 int jpt = 0;
1237                 while(poRecord->FindField( "SG2D", jpt ) != NULL)
1238                 {
1239                       poLine->setPoint(
1240                                        jpt,
1241                                        poRecord->GetIntSubfield("SG2D",jpt,"XCOO",0) / (double)nCOMF,
1242                                        poRecord->GetIntSubfield("SG2D",jpt,"YCOO",0) / (double)nCOMF );
1243                       jpt++;
1244                 }
1245 
1246                 poLine->setNumPoints( jpt );
1247 
1248                 poFeature->SetGeometryDirectly( poLine );
1249 
1250           }
1251           else
1252           {
1253 
1254                   OGRLineString *poLine = new OGRLineString();
1255 
1256 //        if(nVCount != 1)
1257 //        {
1258 //              poRecord->Dump(stdout);
1259 //              nVCount = poRecord->FindField("SG2D")->GetRepeatCount();
1260 //        }
1261 
1262                   poLine->setNumPoints( nVCount );
1263 
1264                   for( i = 0; i < nVCount; i++ )
1265                   {
1266                         poLine->setPoint(
1267                         i,
1268                         poRecord->GetIntSubfield("SG2D",0,"XCOO",i) / (double)nCOMF,
1269                         poRecord->GetIntSubfield("SG2D",0,"YCOO",i) / (double)nCOMF );
1270                   }
1271                   poFeature->SetGeometryDirectly( poLine );
1272           }
1273     }
1274 
1275 /* -------------------------------------------------------------------- */
1276 /*      Special edge fields.                                            */
1277 /* -------------------------------------------------------------------- */
1278     DDFField *poVRPT;
1279 
1280     if( nRCNM == RCNM_VE
1281         && (poVRPT = poRecord->FindField( "VRPT" )) != NULL )
1282     {
1283         poFeature->SetField( "NAME_RCNM_0", RCNM_VC );
1284         poFeature->SetField( "NAME_RCID_0", ParseName( poVRPT, 0 ) );
1285         poFeature->SetField( "ORNT_0",
1286                              poRecord->GetIntSubfield("VRPT",0,"ORNT",0) );
1287         poFeature->SetField( "USAG_0",
1288                              poRecord->GetIntSubfield("VRPT",0,"USAG",0) );
1289         poFeature->SetField( "TOPI_0",
1290                              poRecord->GetIntSubfield("VRPT",0,"TOPI",0) );
1291         poFeature->SetField( "MASK_0",
1292                              poRecord->GetIntSubfield("VRPT",0,"MASK",0) );
1293 
1294 
1295         if(poVRPT->GetRepeatCount() > 1)
1296         {
1297             poFeature->SetField( "NAME_RCNM_1", RCNM_VC );
1298             poFeature->SetField( "NAME_RCID_1", ParseName( poVRPT, 1 ) );
1299             poFeature->SetField( "ORNT_1",
1300                               poRecord->GetIntSubfield("VRPT",0,"ORNT",1) );
1301             poFeature->SetField( "USAG_1",
1302                               poRecord->GetIntSubfield("VRPT",0,"USAG",1) );
1303             poFeature->SetField( "TOPI_1",
1304                               poRecord->GetIntSubfield("VRPT",0,"TOPI",1) );
1305             poFeature->SetField( "MASK_1",
1306                               poRecord->GetIntSubfield("VRPT",0,"MASK",1) );
1307         }
1308         else
1309         {
1310               DDFField *poVRPTEnd = poRecord->FindField( "VRPT", 1 );
1311 
1312               if(poVRPTEnd)
1313               {
1314 
1315                   poFeature->SetField( "NAME_RCNM_1", RCNM_VC );
1316                   poFeature->SetField( "NAME_RCID_1", ParseName( poVRPTEnd, 0 ) );
1317                   poFeature->SetField( "ORNT_1",
1318                                     poRecord->GetIntSubfield("VRPT",1,"ORNT",0) );
1319                   poFeature->SetField( "USAG_1",
1320                                     poRecord->GetIntSubfield("VRPT",1,"USAG",0) );
1321                   poFeature->SetField( "TOPI_1",
1322                                     poRecord->GetIntSubfield("VRPT",1,"TOPI",0) );
1323                   poFeature->SetField( "MASK_1",
1324                                     poRecord->GetIntSubfield("VRPT",1,"MASK",0) );
1325               }
1326               else
1327                     CPLDebug( "S57","Vector End Point not found, edge omitted." );
1328         }
1329     }
1330 
1331     return poFeature;
1332 }
1333 
1334 /************************************************************************/
1335 /*                             FetchPoint()                             */
1336 /*                                                                      */
1337 /*      Fetch the location and quality of a spatial point object.       */
1338 /************************************************************************/
1339 
FetchPoint(int nRCNM,int nRCID,double * pdfX,double * pdfY,double * pdfZ,int * pnquality)1340 int S57Reader::FetchPoint( int nRCNM, int nRCID,
1341                            double * pdfX, double * pdfY, double * pdfZ, int * pnquality )
1342 
1343 {
1344     DDFRecord   *poSRecord;
1345 
1346     if( nRCNM == RCNM_VI )
1347         poSRecord = oVI_Index.FindRecord( nRCID );
1348     else
1349         poSRecord = oVC_Index.FindRecord( nRCID );
1350 
1351     if( poSRecord == NULL )
1352         return FALSE;
1353 
1354     //      Fetch the quality information
1355     if(NULL != pnquality)
1356     {
1357           DDFField *f;
1358           if( (f = poSRecord->FindField( "ATTV" )) != NULL )
1359           {
1360                 DDFSubfieldDefn *sfd = (f->GetFieldDefn())->FindSubfieldDefn( "ATVL");
1361                 if(NULL != sfd)
1362                 {
1363                         int nSuccess;
1364                         char *s = (char *)poSRecord->GetStringSubfield("ATTV",0,"ATVL",0, &nSuccess) ;
1365                         if(nSuccess)
1366                         {
1367                               *pnquality = atoi(s);
1368                         }
1369                 }
1370           }
1371     }
1372 
1373     double      dfX = 0.0, dfY = 0.0, dfZ = 0.0;
1374 
1375     if( poSRecord->FindField( "SG2D" ) != NULL )
1376     {
1377         dfX = poSRecord->GetIntSubfield("SG2D",0,"XCOO",0) / (double)nCOMF;
1378         dfY = poSRecord->GetIntSubfield("SG2D",0,"YCOO",0) / (double)nCOMF;
1379     }
1380     else if( poSRecord->FindField( "SG3D" ) != NULL )
1381     {
1382         dfX = poSRecord->GetIntSubfield("SG3D",0,"XCOO",0) / (double)nCOMF;
1383         dfY = poSRecord->GetIntSubfield("SG3D",0,"YCOO",0) / (double)nCOMF;
1384         dfZ = poSRecord->GetIntSubfield("SG3D",0,"VE3D",0) / (double)nSOMF;
1385     }
1386     else
1387         return FALSE;
1388 
1389     if( pdfX != NULL )
1390         *pdfX = dfX;
1391     if( pdfY != NULL )
1392         *pdfY = dfY;
1393     if( pdfZ != NULL )
1394         *pdfZ = dfZ;
1395 
1396     return TRUE;
1397 }
1398 
1399 /************************************************************************/
1400 /*                       AssemblePointGeometry()                        */
1401 /************************************************************************/
1402 
AssemblePointGeometry(DDFRecord * poFRecord,OGRFeature * poFeature)1403 void S57Reader::AssemblePointGeometry( DDFRecord * poFRecord,
1404                                        OGRFeature * poFeature )
1405 
1406 {
1407     DDFField    *poFSPT;
1408     int         nRCNM, nRCID;
1409 
1410 /* -------------------------------------------------------------------- */
1411 /*      Feature the spatial record containing the point.                */
1412 /* -------------------------------------------------------------------- */
1413     poFSPT = poFRecord->FindField( "FSPT" );
1414     if( poFSPT == NULL )
1415         return;
1416 
1417     if( poFSPT->GetRepeatCount() != 1 )
1418     {
1419 #ifdef DEBUG
1420         fprintf( stderr,
1421                  "Point features with other than one spatial linkage.\n" );
1422         poFRecord->Dump( stderr );
1423 #endif
1424         CPLDebug( "S57",
1425            "Point feature encountered with other than one spatial linkage." );
1426     }
1427 
1428     nRCID = ParseName( poFSPT, 0, &nRCNM );
1429 
1430     double      dfX = 0.0, dfY = 0.0, dfZ = 0.0;
1431 
1432     int nquality = 10;                          // default is "precisely known"
1433     if( !FetchPoint( nRCNM, nRCID, &dfX, &dfY, &dfZ, &nquality ) )
1434     {
1435         CPLAssert( FALSE );
1436         return;
1437     }
1438 
1439     poFeature->SetGeometryDirectly( new OGRPoint( dfX, dfY, dfZ ) );
1440     OGRPoint * pp = (OGRPoint *)poFeature->GetGeometryRef();
1441     pp->setnQual(nquality);
1442 }
1443 
1444 /************************************************************************/
1445 /*                      AssembleSoundingGeometry()                      */
1446 /************************************************************************/
1447 
AssembleSoundingGeometry(DDFRecord * poFRecord,OGRFeature * poFeature)1448 void S57Reader::AssembleSoundingGeometry( DDFRecord * poFRecord,
1449                                           OGRFeature * poFeature )
1450 
1451 {
1452     DDFField    *poFSPT;
1453     int         nRCNM, nRCID;
1454     DDFRecord   *poSRecord;
1455 
1456 
1457 /* -------------------------------------------------------------------- */
1458 /*      Feature the spatial record containing the point.                */
1459 /* -------------------------------------------------------------------- */
1460     poFSPT = poFRecord->FindField( "FSPT" );
1461     if( poFSPT == NULL )
1462         return;
1463 
1464     CPLAssert( poFSPT->GetRepeatCount() == 1 );
1465 
1466     nRCID = ParseName( poFSPT, 0, &nRCNM );
1467 
1468     if( nRCNM == RCNM_VI )
1469         poSRecord = oVI_Index.FindRecord( nRCID );
1470     else
1471         poSRecord = oVC_Index.FindRecord( nRCID );
1472 
1473     if( poSRecord == NULL )
1474         return;
1475 
1476 /* -------------------------------------------------------------------- */
1477 /*      Extract vertices.                                               */
1478 /* -------------------------------------------------------------------- */
1479     OGRMultiPoint       *poMP = new OGRMultiPoint();
1480     DDFField            *poField;
1481     int                 nPointCount, i, nBytesLeft;
1482     DDFSubfieldDefn    *poXCOO, *poYCOO, *poVE3D;
1483     const char         *pachData;
1484 
1485     poField = poSRecord->FindField( "SG2D" );
1486     if( poField == NULL )
1487         poField = poSRecord->FindField( "SG3D" );
1488     if( poField == NULL )
1489         return;
1490 
1491     poXCOO = poField->GetFieldDefn()->FindSubfieldDefn( "XCOO" );
1492     poYCOO = poField->GetFieldDefn()->FindSubfieldDefn( "YCOO" );
1493     poVE3D = poField->GetFieldDefn()->FindSubfieldDefn( "VE3D" );
1494 
1495     nPointCount = poField->GetRepeatCount();
1496 
1497     pachData = poField->GetData();
1498     nBytesLeft = poField->GetDataSize();
1499 
1500     for( i = 0; i < nPointCount; i++ )
1501     {
1502         double          dfX, dfY, dfZ = 0.0;
1503         int             nBytesConsumed;
1504 
1505         dfY = poYCOO->ExtractIntData( pachData, nBytesLeft,
1506                                       &nBytesConsumed ) / (double) nCOMF;
1507         nBytesLeft -= nBytesConsumed;
1508         pachData += nBytesConsumed;
1509 
1510         dfX = poXCOO->ExtractIntData( pachData, nBytesLeft,
1511                                       &nBytesConsumed ) / (double) nCOMF;
1512         nBytesLeft -= nBytesConsumed;
1513         pachData += nBytesConsumed;
1514 
1515         if( poVE3D != NULL )
1516         {
1517             dfZ = poYCOO->ExtractIntData( pachData, nBytesLeft,
1518                                           &nBytesConsumed ) / (double) nSOMF;
1519             nBytesLeft -= nBytesConsumed;
1520             pachData += nBytesConsumed;
1521         }
1522 
1523         poMP->addGeometryDirectly( new OGRPoint( dfX, dfY, dfZ ) );
1524     }
1525 
1526     poFeature->SetGeometryDirectly( poMP );
1527 }
1528 
1529 /************************************************************************/
1530 /*                        AssembleLineGeometry()                        */
1531 /************************************************************************/
1532 
AssembleLineGeometry(DDFRecord * poFRecord,OGRFeature * poFeature)1533 void S57Reader::AssembleLineGeometry( DDFRecord * poFRecord,
1534                                       OGRFeature * poFeature )
1535 
1536 {
1537     DDFField    *poFSPT;
1538     int         nEdgeCount;
1539     OGRLineString *poLine = new OGRLineString();
1540 
1541 /* -------------------------------------------------------------------- */
1542 /*      Find the FSPT field.                                            */
1543 /* -------------------------------------------------------------------- */
1544     poFSPT = poFRecord->FindField( "FSPT" );
1545     if( poFSPT == NULL )
1546         return;
1547 
1548     nEdgeCount = poFSPT->GetRepeatCount();
1549 
1550 /* ==================================================================== */
1551 /*      Loop collecting edges.                                          */
1552 /* ==================================================================== */
1553     for( int iEdge = 0; iEdge < nEdgeCount; iEdge++ )
1554     {
1555         DDFRecord       *poSRecord;
1556         int             nRCID;
1557 
1558 /* -------------------------------------------------------------------- */
1559 /*      Find the spatial record for this edge.                          */
1560 /* -------------------------------------------------------------------- */
1561         nRCID = ParseName( poFSPT, iEdge );
1562 
1563         poSRecord = oVE_Index.FindRecord( nRCID );
1564         if( poSRecord == NULL )
1565         {
1566             CPLError( CE_Warning, CPLE_AppDefined,
1567                       "Couldn't find spatial record %d.\n"
1568                       "Feature OBJL=%s, RCID=%d may have corrupt or"
1569                       "missing geometry.",
1570                       nRCID,
1571                       poFeature->GetDefnRef()->GetName(),
1572                       poFRecord->GetIntSubfield( "FRID", 0, "RCID", 0 ) );
1573             continue;
1574         }
1575 
1576 /* -------------------------------------------------------------------- */
1577 /*      Establish the number of vertices, and whether we need to        */
1578 /*      reverse or not.                                                 */
1579 /* -------------------------------------------------------------------- */
1580         int             nVCount;
1581         int             nStart, nEnd, nInc;
1582         DDFField        *poSG2D = poSRecord->FindField( "SG2D" );
1583         DDFSubfieldDefn *poXCOO=NULL, *poYCOO=NULL;
1584 
1585         if( poSG2D != NULL )
1586         {
1587             poXCOO = poSG2D->GetFieldDefn()->FindSubfieldDefn("XCOO");
1588             poYCOO = poSG2D->GetFieldDefn()->FindSubfieldDefn("YCOO");
1589 
1590             nVCount = poSG2D->GetRepeatCount();
1591         }
1592         else
1593             nVCount = 0;
1594 
1595         DDFField * poField = poSRecord->FindField( "VRPT" );
1596         int nVC_RCID0 = 0;
1597         int nVC_RCID1 = 0;
1598         int nVC_RCIDStart, nVC_RCIDEnd;
1599 
1600         if( poField == NULL )
1601         {
1602             CPLError( CE_Warning, CPLE_AppDefined,
1603                       "Couldn't find field VRPT in spatial record %d.\n"
1604                       "Feature OBJL=%s, RCID=%d may have corrupt or"
1605                       "missing geometry.",
1606                       nRCID,
1607                       poFeature->GetDefnRef()->GetName(),
1608                       poFRecord->GetIntSubfield( "FRID", 0, "RCID", 0 ) );
1609             continue;
1610         }
1611 
1612         if(poField->GetRepeatCount() > 1)
1613         {
1614               nVC_RCID0 = ParseName( poField, 0 );
1615               nVC_RCID1 = ParseName( poField, 1 );
1616         }
1617         else
1618         {
1619               nVC_RCID0 = ParseName( poField, 0 );
1620               DDFField * poFieldEnd = poSRecord->FindField( "VRPT", 1 );
1621               if(poFieldEnd)
1622                     nVC_RCID1 = ParseName( poFieldEnd, 0 );
1623         }
1624 
1625 
1626         if( poFRecord->GetIntSubfield( "FSPT", 0, "ORNT", iEdge ) == 2 )
1627         {
1628             nStart = nVCount-1;
1629             nEnd = 0;
1630             nInc = -1;
1631             nVC_RCIDStart = nVC_RCID1;          // reversed
1632             nVC_RCIDEnd =   nVC_RCID0;
1633         }
1634         else
1635         {
1636             nStart = 0;
1637             nEnd = nVCount-1;
1638             nInc = 1;
1639             nVC_RCIDStart = nVC_RCID0;
1640             nVC_RCIDEnd =   nVC_RCID1;
1641         }
1642 
1643 /* -------------------------------------------------------------------- */
1644 /*      Add the start node, if this is the first edge.                  */
1645 /* -------------------------------------------------------------------- */
1646         if( iEdge == 0 )
1647         {
1648             int         nVC_RCID = 0;
1649             double      dfX, dfY;
1650 /*
1651             if(poField)
1652             {
1653                   if( nInc == 1 )
1654                         nVC_RCID = ParseName( poField, 0 );
1655                   else
1656                         nVC_RCID = ParseName( poField, 1 );
1657             }
1658 */
1659 
1660             if( FetchPoint( RCNM_VC, nVC_RCIDStart, &dfX, &dfY ) )
1661                 poLine->addPoint( dfX, dfY );
1662             else
1663                 CPLError( CE_Warning, CPLE_AppDefined,
1664                           "Unable to fetch start node RCID%d.\n"
1665                           "Feature OBJL=%s, RCID=%d may have corrupt or"
1666                           " missing geometry.",
1667                           nVC_RCID,
1668                           poFeature->GetDefnRef()->GetName(),
1669                           poFRecord->GetIntSubfield( "FRID", 0, "RCID", 0 ) );
1670         }
1671 
1672 /* -------------------------------------------------------------------- */
1673 /*      Collect the vertices.                                           */
1674 /* -------------------------------------------------------------------- */
1675         int             nVBase = poLine->getNumPoints();
1676 
1677         if(nVCount == 1)
1678         {
1679               int jpt = 0;
1680               while(poSRecord->FindField( "SG2D", jpt ))
1681               {
1682                     poLine->addPoint(
1683                                      poSRecord->GetIntSubfield("SG2D",jpt,"XCOO",0) / (double)nCOMF,
1684                                 poSRecord->GetIntSubfield("SG2D",jpt,"YCOO",0) / (double)nCOMF );
1685                     jpt++;
1686 
1687               }
1688         }
1689         else
1690         {
1691 
1692             poLine->setNumPoints( nVCount+nVBase );
1693 
1694             for( int i = nStart; i != nEnd+nInc; i += nInc )
1695             {
1696                   double      dfX, dfY;
1697                   const char  *pachData;
1698                   int         nBytesRemaining;
1699 
1700                   pachData = poSG2D->GetSubfieldData(poXCOO,&nBytesRemaining,i);
1701 
1702                   dfX = poXCOO->ExtractIntData(pachData,nBytesRemaining,NULL)
1703                   / (double) nCOMF;
1704 
1705                   pachData = poSG2D->GetSubfieldData(poYCOO,&nBytesRemaining,i);
1706 
1707                   dfY = poXCOO->ExtractIntData(pachData,nBytesRemaining,NULL)
1708                   / (double) nCOMF;
1709 
1710                   poLine->setPoint( nVBase++, dfX, dfY );
1711             }
1712         }
1713 
1714 /* -------------------------------------------------------------------- */
1715 /*      Add the end node.                                               */
1716 /* -------------------------------------------------------------------- */
1717         {
1718             int         nVC_RCID = 0;
1719             double      dfX, dfY;
1720 /*
1721             if(poField)
1722             {
1723                   if( nInc == 1 )
1724                         nVC_RCID = ParseName( poField, 1 );
1725                   else
1726                         nVC_RCID = ParseName( poField, 0 );
1727             }
1728 */
1729             if( FetchPoint( RCNM_VC, nVC_RCIDEnd, &dfX, &dfY ) )
1730                 poLine->addPoint( dfX, dfY );
1731             else
1732                 CPLError( CE_Warning, CPLE_AppDefined,
1733                           "Unable to fetch end node RCID=%d.\n"
1734                           "Feature OBJL=%s, RCID=%d may have corrupt or"
1735                           " missing geometry.",
1736                           nVC_RCID,
1737                           poFeature->GetDefnRef()->GetName(),
1738                           poFRecord->GetIntSubfield( "FRID", 0, "RCID", 0 ) );
1739         }
1740     }
1741 
1742     if( poLine->getNumPoints() >= 2 )
1743         poFeature->SetGeometryDirectly( poLine );
1744     else
1745         delete poLine;
1746 }
1747 
1748 /************************************************************************/
1749 /*                        AssembleAreaGeometry()                        */
1750 /************************************************************************/
1751 
AssembleAreaGeometry(DDFRecord * poFRecord,OGRFeature * poFeature)1752 void S57Reader::AssembleAreaGeometry( DDFRecord * poFRecord,
1753                                          OGRFeature * poFeature )
1754 
1755 {
1756     DDFField    *poFSPT;
1757     OGRGeometryCollection * poLines = new OGRGeometryCollection();
1758 //    poFRecord->Dump(stdout);
1759 /* -------------------------------------------------------------------- */
1760 /*      Find the FSPT fields.                                           */
1761 /* -------------------------------------------------------------------- */
1762     for( int iFSPT = 0;
1763          (poFSPT = poFRecord->FindField( "FSPT", iFSPT )) != NULL;
1764          iFSPT++ )
1765     {
1766         int         nEdgeCount;
1767 
1768         nEdgeCount = poFSPT->GetRepeatCount();
1769 
1770 /* ==================================================================== */
1771 /*      Loop collecting edges.                                          */
1772 /* ==================================================================== */
1773         for( int iEdge = 0; iEdge < nEdgeCount; iEdge++ )
1774         {
1775             DDFRecord       *poSRecord;
1776             int             nRCID;
1777 
1778 /* -------------------------------------------------------------------- */
1779 /*      Find the spatial record for this edge.                          */
1780 /* -------------------------------------------------------------------- */
1781             nRCID = ParseName( poFSPT, iEdge );
1782 
1783             poSRecord = oVE_Index.FindRecord( nRCID );
1784             if( poSRecord == NULL )
1785             {
1786                 CPLError( CE_Warning, CPLE_AppDefined,
1787                           "Couldn't find spatial record %d.", nRCID );
1788                 continue;
1789             }
1790 
1791 /* -------------------------------------------------------------------- */
1792 /*      Establish the number of vertices, and whether we need to        */
1793 /*      reverse or not.                                                 */
1794 /* -------------------------------------------------------------------- */
1795 //            poSRecord->Dump(stdout);
1796 
1797             OGRLineString *poLine = new OGRLineString();
1798 
1799             int             nVCount;
1800             int             nStart, nEnd, nInc;
1801             DDFField        *poSG2D = poSRecord->FindField( "SG2D" );
1802             DDFSubfieldDefn *poXCOO=NULL, *poYCOO=NULL;
1803 
1804             if( poSG2D != NULL )
1805             {
1806                 poXCOO = poSG2D->GetFieldDefn()->FindSubfieldDefn("XCOO");
1807                 poYCOO = poSG2D->GetFieldDefn()->FindSubfieldDefn("YCOO");
1808 
1809                 nVCount = poSG2D->GetRepeatCount();
1810             }
1811             else
1812                 nVCount = 0;
1813 
1814             DDFField * poField = poSRecord->FindField( "VRPT" );
1815             int nVC_RCID0 = 0;
1816             int nVC_RCID1 = 0;
1817             int nVC_RCIDStart, nVC_RCIDEnd;
1818 
1819             if(poField && poField->GetRepeatCount() > 1)
1820             {
1821                   nVC_RCID0 = ParseName( poField, 0 );
1822                   nVC_RCID1 = ParseName( poField, 1 );
1823             }
1824             else
1825             {
1826                   if (poField)
1827                         nVC_RCID0 = ParseName( poField, 0 );
1828                   DDFField * poFieldEnd = poSRecord->FindField( "VRPT", 1 );
1829                   if(poFieldEnd)
1830                         nVC_RCID1 = ParseName( poFieldEnd, 0 );
1831             }
1832 
1833 
1834             if( poFRecord->GetIntSubfield( "FSPT", 0, "ORNT", iEdge ) == 2 )
1835             {
1836                   nStart = nVCount-1;
1837                   nEnd = 0;
1838                   nInc = -1;
1839                   nVC_RCIDStart = nVC_RCID1;          // reversed
1840                   nVC_RCIDEnd =   nVC_RCID0;
1841             }
1842             else
1843             {
1844                   nStart = 0;
1845                   nEnd = nVCount-1;
1846                   nInc = 1;
1847                   nVC_RCIDStart = nVC_RCID0;
1848                   nVC_RCIDEnd =   nVC_RCID1;
1849             }
1850 
1851 
1852 /* -------------------------------------------------------------------- */
1853 /*      Add the start node.                                             */
1854 /* -------------------------------------------------------------------- */
1855             {
1856                 double      dfX, dfY;
1857 
1858                 if( FetchPoint( RCNM_VC, nVC_RCIDStart, &dfX, &dfY ) )
1859                     poLine->addPoint( dfX, dfY );
1860             }
1861 
1862 /* -------------------------------------------------------------------- */
1863 /*      Collect the vertices.                                           */
1864 /* -------------------------------------------------------------------- */
1865             int             nVBase = poLine->getNumPoints();
1866 
1867             if(nVCount == 1)
1868             {
1869                   int jpt = 0;
1870                   while(poSRecord->FindField( "SG2D", jpt ))
1871                   {
1872                         poLine->addPoint(
1873                                           poSRecord->GetIntSubfield("SG2D",jpt,"XCOO",0) / (double)nCOMF,
1874                                           poSRecord->GetIntSubfield("SG2D",jpt,"YCOO",0) / (double)nCOMF );
1875                         jpt++;
1876 
1877                   }
1878             }
1879             else
1880             {
1881                   poLine->setNumPoints( nVCount+nVBase );
1882 
1883                   for( int i = nStart; i != nEnd+nInc; i += nInc )
1884                   {
1885                         double      dfX, dfY;
1886                         const char  *pachData;
1887                         int         nBytesRemaining;
1888 
1889                         pachData = poSG2D->GetSubfieldData(poXCOO,&nBytesRemaining,i);
1890 
1891                         dfX = poXCOO->ExtractIntData(pachData,nBytesRemaining,NULL) / (double) nCOMF;
1892 
1893                         pachData = poSG2D->GetSubfieldData(poYCOO,&nBytesRemaining,i);
1894 
1895                         dfY = poXCOO->ExtractIntData(pachData,nBytesRemaining,NULL) / (double) nCOMF;
1896 
1897                         poLine->setPoint( nVBase++, dfX, dfY );
1898                   }
1899 
1900             }
1901 /* -------------------------------------------------------------------- */
1902 /*      Add the end node.                                               */
1903 /* -------------------------------------------------------------------- */
1904             {
1905                 double      dfX, dfY;
1906 
1907 
1908                 if( FetchPoint( RCNM_VC, nVC_RCIDEnd, &dfX, &dfY ) )
1909                     poLine->addPoint( dfX, dfY );
1910             }
1911 
1912             poLines->addGeometryDirectly( poLine );
1913         }
1914     }
1915 
1916 /* -------------------------------------------------------------------- */
1917 /*      Build lines into a polygon.                                     */
1918 /* -------------------------------------------------------------------- */
1919     OGRPolygon  *poPolygon;
1920     OGRErr      eErr;
1921 
1922     poPolygon = (OGRPolygon *)
1923         OGRBuildPolygonFromEdges( (OGRGeometryH) poLines,
1924                                   TRUE, FALSE, 0.0, &eErr );
1925     if( eErr != OGRERR_NONE )
1926     {
1927         CPLError( CE_Warning, CPLE_AppDefined,
1928                   "Polygon assembly has failed for feature FIDN=%d,FIDS=%d.\n"
1929                   "Geometry may be missing or incomplete.",
1930                   poFeature->GetFieldAsInteger( "FIDN" ),
1931                   poFeature->GetFieldAsInteger( "FIDS" ) );
1932     }
1933 
1934     delete poLines;
1935 
1936     if( poPolygon != NULL )
1937         poFeature->SetGeometryDirectly( poPolygon );
1938 }
1939 
1940 /************************************************************************/
1941 /*                             FindFDefn()                              */
1942 /*                                                                      */
1943 /*      Find the OGRFeatureDefn corresponding to the passed feature     */
1944 /*      record.  It will search based on geometry class, or object      */
1945 /*      class depending on the bClassBased setting.                     */
1946 /************************************************************************/
1947 
FindFDefn(DDFRecord * poRecord)1948 OGRFeatureDefn * S57Reader::FindFDefn( DDFRecord * poRecord )
1949 
1950 {
1951     if( poRegistrar != NULL )
1952     {
1953         int     nOBJL = poRecord->GetIntSubfield( "FRID", 0, "OBJL", 0 );
1954 
1955         if( !poRegistrar->SelectClass( nOBJL ) )
1956         {
1957             for( int i = 0; i < nFDefnCount; i++ )
1958             {
1959                 if( EQUAL(papoFDefnList[i]->GetName(),"Generic") )
1960                     return papoFDefnList[i];
1961             }
1962             return NULL;
1963         }
1964 
1965         for( int i = 0; i < nFDefnCount; i++ )
1966         {
1967             if( EQUAL(papoFDefnList[i]->GetName(),
1968                       poRegistrar->GetAcronym()) )
1969                 return papoFDefnList[i];
1970         }
1971 
1972         return NULL;
1973     }
1974     else
1975     {
1976         int     nPRIM = poRecord->GetIntSubfield( "FRID", 0, "PRIM", 0 );
1977         OGRwkbGeometryType eGType;
1978 
1979         if( nPRIM == PRIM_P )
1980             eGType = wkbPoint;
1981         else if( nPRIM == PRIM_L )
1982             eGType = wkbLineString;
1983         else if( nPRIM == PRIM_A )
1984             eGType = wkbPolygon;
1985         else
1986             eGType = wkbNone;
1987 
1988         for( int i = 0; i < nFDefnCount; i++ )
1989         {
1990             if( papoFDefnList[i]->GetGeomType() == eGType )
1991                 return papoFDefnList[i];
1992         }
1993     }
1994 
1995     return NULL;
1996 }
1997 
1998 /************************************************************************/
1999 /*                             ParseName()                              */
2000 /*                                                                      */
2001 /*      Pull the RCNM and RCID values from a NAME field.  The RCID      */
2002 /*      is returned and the RCNM can be gotten via the pnRCNM argument. */
2003 /************************************************************************/
2004 
ParseName(DDFField * poField,int nIndex,int * pnRCNM)2005 int S57Reader::ParseName( DDFField * poField, int nIndex, int * pnRCNM )
2006 
2007 {
2008     unsigned char       *pabyData;
2009 
2010     pabyData = (unsigned char *)
2011         poField->GetSubfieldData(
2012             poField->GetFieldDefn()->FindSubfieldDefn( "NAME" ),
2013             NULL, nIndex );
2014 
2015     if( pnRCNM != NULL )
2016         *pnRCNM = pabyData[0];
2017 
2018     return pabyData[1]
2019          + pabyData[2] * 256
2020          + pabyData[3] * 256 * 256
2021          + pabyData[4] * 256 * 256 * 256;
2022 }
2023 
2024 /************************************************************************/
2025 /*                           AddFeatureDefn()                           */
2026 /************************************************************************/
2027 
AddFeatureDefn(OGRFeatureDefn * poFDefn)2028 void S57Reader::AddFeatureDefn( OGRFeatureDefn * poFDefn )
2029 
2030 {
2031     nFDefnCount++;
2032     papoFDefnList = (OGRFeatureDefn **)
2033         CPLRealloc(papoFDefnList, sizeof(OGRFeatureDefn*)*nFDefnCount );
2034 
2035     papoFDefnList[nFDefnCount-1] = poFDefn;
2036 }
2037 
2038 /************************************************************************/
2039 /*                          CollectClassList()                          */
2040 /*                                                                      */
2041 /*      Establish the list of classes (unique OBJL values) that         */
2042 /*      occur in this dataset.                                          */
2043 /************************************************************************/
2044 
CollectClassList(int * panClassCount,int nMaxClass)2045 int S57Reader::CollectClassList(int *panClassCount, int nMaxClass )
2046 
2047 {
2048     int         bSuccess = TRUE;
2049 
2050     if( !bFileIngested )
2051         Ingest();
2052 
2053     for( int iFEIndex = 0; iFEIndex < oFE_Index.GetCount(); iFEIndex++ )
2054     {
2055         DDFRecord *poRecord = oFE_Index.GetByIndex( iFEIndex );
2056         int     nOBJL = poRecord->GetIntSubfield( "FRID", 0, "OBJL", 0 );
2057 
2058         if( nOBJL >= nMaxClass )
2059             bSuccess = FALSE;
2060         else
2061             panClassCount[nOBJL]++;
2062 
2063     }
2064 
2065     return bSuccess;
2066 }
2067 
2068 /************************************************************************/
2069 /*                         ApplyRecordUpdate()                          */
2070 /*                                                                      */
2071 /*      Update one target record based on an S-57 update record         */
2072 /*      (RUIN=3).                                                       */
2073 /************************************************************************/
2074 
ApplyRecordUpdate(DDFRecord * poTarget,DDFRecord * poUpdate)2075 int S57Reader::ApplyRecordUpdate( DDFRecord *poTarget, DDFRecord *poUpdate )
2076 
2077 {
2078     const char *pszKey = poUpdate->GetField(1)->GetFieldDefn()->GetName();
2079 
2080 /* -------------------------------------------------------------------- */
2081 /*      Validate versioning.                                            */
2082 /* -------------------------------------------------------------------- */
2083     if( poTarget->GetIntSubfield( pszKey, 0, "RVER", 0 ) + 1
2084         != poUpdate->GetIntSubfield( pszKey, 0, "RVER", 0 )  )
2085     {
2086           CPLError( CE_Warning, CPLE_AppDefined,
2087                   "On RecordUpdate, mismatched RVER value for RCNM=%d,RCID=%d...update RVER is %d, target RVER is %d.",
2088                   poTarget->GetIntSubfield( pszKey, 0, "RCNM", 0 ),
2089                   poTarget->GetIntSubfield( pszKey, 0, "RCID", 0 ),
2090                   poUpdate->GetIntSubfield( pszKey, 0, "RVER", 0 ),
2091                   poTarget->GetIntSubfield( pszKey, 0, "RVER", 0 ) );
2092 
2093         CPLAssert( FALSE );
2094         return FALSE;
2095     }
2096 
2097     //    More checks for validity
2098     if( poUpdate->FindField( "FRID" ) != NULL )
2099     {
2100 /*
2101           int up_FIDN = poUpdate->GetIntSubfield( "FOID", 0, "FIDN", 0 );
2102           int up_FIDS = poUpdate->GetIntSubfield( "FOID", 0, "FIDS", 0 );
2103           int tar_FIDN = poTarget->GetIntSubfield( "FOID", 0, "FIDN", 0 );
2104           int tar_FIDS = poTarget->GetIntSubfield( "FOID", 0, "FIDS", 0 );
2105           if((up_FIDN != tar_FIDN) || (up_FIDS != tar_FIDS))
2106           {
2107           CPLError( CE_Warning, CPLE_AppDefined,
2108           "On RecordUpdate, mismatched FIDN/FIDS.... target FIDN=%d, target FIDS=%d   update FIDN=%d, update FIDS=%d.",
2109           tar_FIDN, tar_FIDS, up_FIDN, up_FIDS);
2110 
2111           return FALSE;
2112     }
2113 */
2114           int up_PRIM = poUpdate->GetIntSubfield( "FRID", 0, "PRIM", 0 );
2115           int tar_PRIM = poTarget->GetIntSubfield( "FRID", 0, "PRIM", 0 );
2116           if(up_PRIM != tar_PRIM)
2117           {
2118                 CPLError( CE_Warning, CPLE_AppDefined,
2119                           "On RecordUpdate, mismatched PRIM.... target PRIM=%d, update PRIM=%d",
2120                           tar_PRIM, up_PRIM);
2121                 return FALSE;
2122           }
2123     }
2124 
2125 
2126 /* -------------------------------------------------------------------- */
2127 /*      Update the target version.                                      */
2128 /* -------------------------------------------------------------------- */
2129     unsigned int       *pnRVER;
2130     DDFField    *poKey = poTarget->FindField( pszKey );
2131     DDFSubfieldDefn *poRVER_SFD;
2132 
2133     if( poKey == NULL )
2134     {
2135         CPLAssert( FALSE );
2136         return FALSE;
2137     }
2138 
2139     poRVER_SFD = poKey->GetFieldDefn()->FindSubfieldDefn( "RVER" );
2140     if( poRVER_SFD == NULL )
2141         return FALSE;
2142 
2143     pnRVER = (unsigned int *) poKey->GetSubfieldData( poRVER_SFD, NULL, 0 );
2144 
2145     *pnRVER += 1;
2146 
2147 /* -------------------------------------------------------------------- */
2148 /*      Check for, and apply record record to spatial record pointer    */
2149 /*      updates.                                                        */
2150 /* -------------------------------------------------------------------- */
2151     if( poUpdate->FindField( "FSPC" ) != NULL )
2152     {
2153         int     nFSUI = poUpdate->GetIntSubfield( "FSPC", 0, "FSUI", 0 );
2154         int     nFSIX = poUpdate->GetIntSubfield( "FSPC", 0, "FSIX", 0 );
2155         int     nNSPT = poUpdate->GetIntSubfield( "FSPC", 0, "NSPT", 0 );
2156         DDFField *poSrcFSPT = poUpdate->FindField( "FSPT" );
2157         DDFField *poDstFSPT = poTarget->FindField( "FSPT" );
2158         int     nPtrSize;
2159 
2160         if( (poSrcFSPT == NULL && nFSUI != 2) || poDstFSPT == NULL )
2161         {
2162             CPLAssert( FALSE );
2163             return FALSE;
2164         }
2165 
2166         nPtrSize = poDstFSPT->GetFieldDefn()->GetFixedWidth();
2167 
2168         if( nFSUI == 1 ) /* INSERT */
2169         {
2170             char        *pachInsertion;
2171             int         nInsertionBytes = nPtrSize * nNSPT;
2172 
2173             pachInsertion = (char *) CPLMalloc(nInsertionBytes + nPtrSize);
2174             memcpy( pachInsertion, poSrcFSPT->GetData(), nInsertionBytes );
2175 
2176             /*
2177             ** If we are inserting before an instance that already
2178             ** exists, we must add it to the end of the data being
2179             ** inserted.
2180             */
2181             if( nFSIX <= poDstFSPT->GetRepeatCount() )
2182             {
2183                 memcpy( pachInsertion + nInsertionBytes,
2184                         poDstFSPT->GetData() + nPtrSize * (nFSIX-1),
2185                         nPtrSize );
2186                 nInsertionBytes += nPtrSize;
2187             }
2188 
2189             poTarget->SetFieldRaw( poDstFSPT, nFSIX - 1,
2190                                    pachInsertion, nInsertionBytes );
2191             CPLFree( pachInsertion );
2192         }
2193         else if( nFSUI == 2 ) /* DELETE */
2194         {
2195             /* Wipe each deleted coordinate */
2196             for( int i = nNSPT-1; i >= 0; i-- )
2197             {
2198                 poTarget->SetFieldRaw( poDstFSPT, i + nFSIX - 1, NULL, 0 );
2199             }
2200         }
2201         else if( nFSUI == 3 ) /* MODIFY */
2202         {
2203             /* copy over each ptr */
2204             for( int i = 0; i < nNSPT; i++ )
2205             {
2206                 const char *pachRawData;
2207 
2208                 pachRawData = poSrcFSPT->GetData() + nPtrSize * i;
2209 
2210                 poTarget->SetFieldRaw( poDstFSPT, i + nFSIX - 1,
2211                                        pachRawData, nPtrSize );
2212             }
2213         }
2214     }
2215 
2216 /* -------------------------------------------------------------------- */
2217 /*      Check for, and apply vector record to vector record pointer     */
2218 /*      updates.                                                        */
2219 /* -------------------------------------------------------------------- */
2220     if( poUpdate->FindField( "VRPC" ) != NULL )
2221     {
2222         int     nVPUI = poUpdate->GetIntSubfield( "VRPC", 0, "VPUI", 0 );
2223         int     nVPIX = poUpdate->GetIntSubfield( "VRPC", 0, "VPIX", 0 );
2224         int     nNVPT = poUpdate->GetIntSubfield( "VRPC", 0, "NVPT", 0 );
2225         DDFField *poSrcVRPT = poUpdate->FindField( "VRPT" );
2226         DDFField *poDstVRPT = poTarget->FindField( "VRPT" );
2227         int     nPtrSize;
2228 
2229         if( (poSrcVRPT == NULL && nVPUI != 2) || poDstVRPT == NULL )
2230         {
2231             CPLAssert( FALSE );
2232             return FALSE;
2233         }
2234 
2235         nPtrSize = poDstVRPT->GetFieldDefn()->GetFixedWidth();
2236 
2237         if( nVPUI == 1 ) /* INSERT */
2238         {
2239             char        *pachInsertion;
2240             int         nInsertionBytes = nPtrSize * nNVPT;
2241 
2242             pachInsertion = (char *) CPLMalloc(nInsertionBytes + nPtrSize);
2243             memcpy( pachInsertion, poSrcVRPT->GetData(), nInsertionBytes );
2244 
2245             /*
2246             ** If we are inserting before an instance that already
2247             ** exists, we must add it to the end of the data being
2248             ** inserted.
2249             */
2250             if( nVPIX <= poDstVRPT->GetRepeatCount() )
2251             {
2252                 memcpy( pachInsertion + nInsertionBytes,
2253                         poDstVRPT->GetData() + nPtrSize * (nVPIX-1),
2254                         nPtrSize );
2255                 nInsertionBytes += nPtrSize;
2256             }
2257 
2258             poTarget->SetFieldRaw( poDstVRPT, nVPIX - 1,
2259                                    pachInsertion, nInsertionBytes );
2260             CPLFree( pachInsertion );
2261         }
2262         else if( nVPUI == 2 ) /* DELETE */
2263         {
2264             /* Wipe each deleted coordinate */
2265             for( int i = nNVPT-1; i >= 0; i-- )
2266             {
2267                 poTarget->SetFieldRaw( poDstVRPT, i + nVPIX - 1, NULL, 0 );
2268             }
2269         }
2270         else if( nVPUI == 3 ) /* MODIFY */
2271         {
2272             /* copy over each ptr */
2273             for( int i = 0; i < nNVPT; i++ )
2274             {
2275                 const char *pachRawData;
2276 
2277                 pachRawData = poSrcVRPT->GetData() + nPtrSize * i;
2278 
2279                 poTarget->SetFieldRaw( poDstVRPT, i + nVPIX - 1,
2280                                        pachRawData, nPtrSize );
2281             }
2282         }
2283     }
2284 
2285 /* -------------------------------------------------------------------- */
2286 /*      Check for, and apply record update to coordinates.              */
2287 /* -------------------------------------------------------------------- */
2288     if( poUpdate->FindField( "SGCC" ) != NULL )
2289     {
2290         int     nCCUI = poUpdate->GetIntSubfield( "SGCC", 0, "CCUI", 0 );
2291         int     nCCIX = poUpdate->GetIntSubfield( "SGCC", 0, "CCIX", 0 );
2292         int     nCCNC = poUpdate->GetIntSubfield( "SGCC", 0, "CCNC", 0 );
2293         DDFField *poSrcSG2D = poUpdate->FindField( "SG2D" );
2294         DDFField *poDstSG2D = poTarget->FindField( "SG2D" );
2295         int     nCoordSize;
2296 
2297         /* If we don't have SG2D, check for SG3D */
2298         if( poDstSG2D == NULL )
2299         {
2300             poDstSG2D = poTarget->FindField( "SG3D" );
2301             if (poDstSG2D != NULL)
2302             {
2303                 poSrcSG2D = poUpdate->FindField("SG3D");
2304             }
2305         }
2306 
2307         if(poDstSG2D == NULL && nCCUI == 2){
2308             // Trying to delete a coordinate that does not exist...
2309             // Theoretically, this is an error.
2310             //  But we have seen this from some HOs, (China/HongKong) and seems to be OK to ignore
2311             return TRUE;
2312         }
2313 
2314         if( (poSrcSG2D == NULL && nCCUI != 2)
2315             || (poDstSG2D == NULL && nCCUI != 1) )
2316         {
2317             //CPLAssert( FALSE );
2318             return FALSE;
2319         }
2320 
2321         if (poDstSG2D == NULL)
2322         {
2323             poTarget->AddField(poTarget->GetModule()->FindFieldDefn("SG2D"));
2324             poDstSG2D = poTarget->FindField("SG2D");
2325             if (poDstSG2D == NULL) {
2326                 //CPLAssert( FALSE );
2327                 return FALSE;
2328             }
2329 
2330             // Delete null default data that was created
2331             poTarget->SetFieldRaw( poDstSG2D, 0, NULL, 0 );
2332         }
2333 
2334         nCoordSize = poDstSG2D->GetFieldDefn()->GetFixedWidth();
2335 
2336         if( nCCUI == 1 ) /* INSERT */
2337         {
2338             char        *pachInsertion;
2339             int         nInsertionBytes = nCoordSize * nCCNC;
2340 
2341             pachInsertion = (char *) CPLMalloc(nInsertionBytes + nCoordSize);
2342             memcpy( pachInsertion, poSrcSG2D->GetData(), nInsertionBytes );
2343 
2344             /*
2345             ** If we are inserting before an instance that already
2346             ** exists, we must add it to the end of the data being
2347             ** inserted.
2348             */
2349             if( nCCIX <= poDstSG2D->GetRepeatCount() )
2350             {
2351                 memcpy( pachInsertion + nInsertionBytes,
2352                         poDstSG2D->GetData() + nCoordSize * (nCCIX-1),
2353                         nCoordSize );
2354                 nInsertionBytes += nCoordSize;
2355             }
2356 
2357             poTarget->SetFieldRaw( poDstSG2D, nCCIX - 1,
2358                                    pachInsertion, nInsertionBytes );
2359             CPLFree( pachInsertion );
2360 
2361         }
2362         else if( nCCUI == 2 ) /* DELETE */
2363         {
2364             /* Wipe each deleted coordinate */
2365             for( int i = nCCNC-1; i >= 0; i-- )
2366             {
2367                 poTarget->SetFieldRaw( poDstSG2D, i + nCCIX - 1, NULL, 0 );
2368             }
2369         }
2370         else if( nCCUI == 3 ) /* MODIFY */
2371         {
2372             /* copy over each ptr */
2373             for( int i = 0; i < nCCNC; i++ )
2374             {
2375                 const char *pachRawData;
2376 
2377                 pachRawData = poSrcSG2D->GetData() + nCoordSize * i;
2378 
2379                 poTarget->SetFieldRaw( poDstSG2D, i + nCCIX - 1,
2380                                        pachRawData, nCoordSize );
2381             }
2382         }
2383     }
2384 
2385 /* -------------------------------------------------------------------- */
2386 /*      We don't currently handle FFPC (feature to feature linkage)     */
2387 /*      issues, but we will at least report them when debugging.        */
2388 /* -------------------------------------------------------------------- */
2389 /*
2390     if( poUpdate->FindField( "FFPC" ) != NULL )
2391     {
2392         CPLDebug( "S57", "Found FFPC, but not applying it." );
2393     }
2394 */
2395 /* -------------------------------------------------------------------- */
2396 /*      Check for and apply changes to attribute lists.                 */
2397 /* -------------------------------------------------------------------- */
2398     if( poUpdate->FindField( "ATTF" ) != NULL )
2399     {
2400         bool b_newField = false;
2401         DDFSubfieldDefn *poSrcATVLDefn;
2402         DDFField *poSrcATTF = poUpdate->FindField( "ATTF" );
2403         DDFField *poDstATTF = poTarget->FindField( "ATTF" );
2404 
2405         if(NULL == poDstATTF)
2406         {
2407             //  This probably means that the update applies to an attribute that doesn't (yet) exist
2408             //  To fix, we need to add an attribute, then update it.
2409 
2410             DDFFieldDefn *poATTF = poTarget->GetModule()->FindFieldDefn( "ATTF" );
2411             poTarget->AddField(poATTF);
2412             poDstATTF = poTarget->FindField( "ATTF" );
2413             b_newField = true;
2414 
2415         }
2416 
2417         int     nRepeatCount = poSrcATTF->GetRepeatCount();
2418 
2419         poSrcATVLDefn = poSrcATTF->GetFieldDefn()->FindSubfieldDefn( "ATVL" );
2420 
2421         for( int iAtt = 0; iAtt < nRepeatCount; iAtt++ )
2422         {
2423             int nATTL = poUpdate->GetIntSubfield( "ATTF", 0, "ATTL", iAtt );
2424             int iTAtt, nDataBytes;
2425             const char *pszRawData;
2426 
2427             for( iTAtt = poDstATTF->GetRepeatCount()-1; iTAtt >= 0; iTAtt-- )
2428             {
2429                 if( poTarget->GetIntSubfield( "ATTF", 0, "ATTL", iTAtt )
2430                     == nATTL )
2431                     break;
2432             }
2433             if( iTAtt == -1 )
2434                 iTAtt = poDstATTF->GetRepeatCount();
2435 
2436             //  If we just added a new field above, then the first attribute will be 0.
2437             //   We should replace this one
2438             if(b_newField){
2439                 if( poTarget->GetIntSubfield( "ATTF", 0, "ATTL", 0 ) == 0){
2440                     iTAtt = 0;
2441                     b_newField = false;
2442                 }
2443             }
2444 
2445             pszRawData = poSrcATTF->GetInstanceData( iAtt, &nDataBytes );
2446             poTarget->SetFieldRaw( poDstATTF, iTAtt, pszRawData, nDataBytes );
2447         }
2448     }
2449 
2450     if( poUpdate->FindField( "NATF" ) != NULL )
2451     {
2452         bool b_newField = false;
2453         DDFSubfieldDefn *poSrcATVLDefn;
2454         DDFField *poSrcATTF = poUpdate->FindField( "NATF" );
2455         DDFField *poDstATTF = poTarget->FindField( "NATF" );
2456 
2457 //        int up_FIDN = poUpdate->GetIntSubfield( "FOID", 0, "FIDN", 0 );
2458 //        if(up_FIDN == 1103712044 /*1225530334*/){
2459 //            poTarget->Dump(stdout);
2460 //        }
2461 
2462         if(NULL == poDstATTF)
2463         {
2464             //  This probably means that the update applies to an attribute that doesn't (yet) exist
2465             //  To fix, we need to add an attribute, then update it.
2466 
2467             DDFFieldDefn *poNATF = poTarget->GetModule()->FindFieldDefn( "NATF" );
2468             poTarget->AddField(poNATF);
2469             poDstATTF = poTarget->FindField( "NATF" );
2470             b_newField = true;
2471 
2472 //            poTarget->Dump(stdout);
2473 
2474 //            CPLDebug( "S57","Could not find target ATTF field for attribute update");
2475 //           return FALSE;
2476         }
2477 
2478         int     nRepeatCount = poSrcATTF->GetRepeatCount();
2479 
2480         poSrcATVLDefn = poSrcATTF->GetFieldDefn()->FindSubfieldDefn( "ATVL" );
2481 
2482         for( int iAtt = 0; iAtt < nRepeatCount; iAtt++ )
2483         {
2484             int nATTL = poUpdate->GetIntSubfield( "NATF", 0, "ATTL", iAtt );
2485             int iTAtt, nDataBytes;
2486             const char *pszRawData;
2487 
2488             for( iTAtt = poDstATTF->GetRepeatCount()-1; iTAtt >= 0; iTAtt-- )
2489             {
2490                 if( poTarget->GetIntSubfield( "NATF", 0, "ATTL", iTAtt ) == nATTL )
2491                     break;
2492             }
2493             if( iTAtt == -1 )
2494                 iTAtt = poDstATTF->GetRepeatCount();
2495 
2496             //  If we just added a new field above, then the first attribute will be 0.
2497             //   We should replace this one
2498             if(b_newField){
2499                 if( poTarget->GetIntSubfield( "NATF", 0, "ATTL", 0 ) == 0){
2500                     iTAtt = 0;
2501                     b_newField = false;
2502                 }
2503             }
2504 
2505 
2506             pszRawData = poSrcATTF->GetInstanceData( iAtt, &nDataBytes );
2507 
2508 //            poTarget->Dump(stdout);
2509             poTarget->SetFieldRaw( poDstATTF, iTAtt, pszRawData, nDataBytes ); ///dsr
2510 //            poTarget->Dump(stdout);
2511 
2512         }
2513     }
2514 
2515     return TRUE;
2516 }
2517 
2518 
2519 /************************************************************************/
2520 /*                            ApplyUpdates()                            */
2521 /*                                                                      */
2522 /*      Read records from an update file, and apply them to the         */
2523 /*      currently loaded index of features.                             */
2524 /************************************************************************/
2525 
ApplyUpdates(DDFModule * poUpdateModule,int iUpdate)2526 int S57Reader::ApplyUpdates( DDFModule *poUpdateModule, int iUpdate )
2527 
2528 {
2529     DDFRecord   *poRecord;
2530 
2531     int ret_code = 0;
2532 
2533 /* -------------------------------------------------------------------- */
2534 /*      Ensure base file is loaded.                                     */
2535 /* -------------------------------------------------------------------- */
2536     Ingest();
2537 
2538 /* -------------------------------------------------------------------- */
2539 /*      Read records, and apply as updates.                             */
2540 /* -------------------------------------------------------------------- */
2541     while( (poRecord = poUpdateModule->ReadRecord()) != NULL )
2542     {
2543         DDFField        *poKeyField = poRecord->GetField(1);
2544         const char      *pszKey = poKeyField->GetFieldDefn()->GetName();
2545 
2546         if( EQUAL(pszKey,"VRID") || EQUAL(pszKey,"FRID"))
2547         {
2548             int         nRCNM = poRecord->GetIntSubfield( pszKey,0, "RCNM",0 );
2549             int         nRCID = poRecord->GetIntSubfield( pszKey,0, "RCID",0 );
2550             int         nRVER = poRecord->GetIntSubfield( pszKey,0, "RVER",0 );
2551             int         nRUIN = poRecord->GetIntSubfield( pszKey,0, "RUIN",0 );
2552             DDFRecordIndex *poIndex = NULL;
2553 
2554             if( EQUAL(poKeyField->GetFieldDefn()->GetName(),"VRID") )
2555             {
2556                 switch( nRCNM )
2557                 {
2558                   case RCNM_VI:
2559                     poIndex = &oVI_Index;
2560                     break;
2561 
2562                   case RCNM_VC:
2563                     poIndex = &oVC_Index;
2564                     break;
2565 
2566                   case RCNM_VE:
2567                     poIndex = &oVE_Index;
2568                     break;
2569 
2570                   case RCNM_VF:
2571                     poIndex = &oVF_Index;
2572                     break;
2573 
2574                   default:
2575                     CPLAssert( FALSE );
2576                     break;
2577                 }
2578             }
2579             else
2580             {
2581                 poIndex = &oFE_Index;
2582             }
2583 
2584             if( poIndex != NULL )
2585             {
2586                 if( nRUIN == 1 )  /* insert */
2587                 {
2588 //                      CPLDebug( "S57","Insert Record, RCID=%d", nRCID);
2589                       poIndex->AddRecord( nRCID, poRecord->CloneOn(poModule) );
2590                 }
2591                 else if( nRUIN == 2 ) /* delete */
2592                 {
2593 //                      CPLDebug( "S57","Remove Record, RCID=%d", nRCID);
2594                       DDFRecord   *poTarget;
2595 
2596                     poTarget = poIndex->FindRecord( nRCID );
2597                     if( poTarget == NULL )
2598                     {
2599                         CPLError( CE_Warning, CPLE_AppDefined,
2600                                   "While applying update %d, Can't find RCNM=%d,RCID=%d for delete.",
2601                                   iUpdate, nRCNM, nRCID );
2602                         ret_code = BAD_UPDATE;
2603                     }
2604                     else if( poTarget->GetIntSubfield( pszKey, 0, "RVER", 0 )
2605                              != nRVER - 1 )
2606                     {
2607                         CPLError( CE_Warning, CPLE_AppDefined,
2608                                   "While applying update %d, On RecordRemove, mismatched RVER value for RCNM=%d,RCID=%d...update RVER is %d, target RVER is %d.",
2609                                   iUpdate, nRCNM, nRCID, nRVER, poTarget->GetIntSubfield( pszKey, 0, "RVER", 0 ) );
2610                         CPLError( CE_Warning, CPLE_AppDefined,
2611                                   "While applying update %d, Removal of RCNM=%d,RCID=%d failed.",
2612                                   iUpdate, nRCNM, nRCID );
2613                         ret_code = BAD_UPDATE;
2614 
2615                     }
2616                     else
2617                     {
2618                           poIndex->RemoveRecord( nRCID );
2619                     }
2620                 }
2621 
2622                 else if( nRUIN == 3 ) /* modify in place */
2623                 {
2624 //                    CPLDebug( "S57","Update Record, RCID=%d", nRCID);
2625                     DDFRecord   *poTarget;
2626 
2627                     poTarget = poIndex->FindRecord( nRCID );
2628                     if( poTarget == NULL )
2629                     {
2630                         CPLError( CE_Warning, CPLE_AppDefined,
2631                                   "While applying update %d, Can't find RCNM=%d,RCID=%d for update.",
2632                                   iUpdate, nRCNM, nRCID );
2633                         ret_code = BAD_UPDATE;
2634 
2635                     }
2636                     else
2637                     {
2638                         if( !ApplyRecordUpdate( poTarget, poRecord ) )
2639                         {
2640                             CPLError( CE_Warning, CPLE_AppDefined,
2641                                       "While applying update %d, an update to RCNM=%d,RCID=%d failed.",
2642                                       iUpdate, nRCNM, nRCID );
2643                             ret_code = BAD_UPDATE;
2644                         }
2645                     }
2646                 }
2647             }
2648         }
2649 
2650         else if( EQUAL(pszKey,"DSID") )
2651         {
2652             /* ignore */;
2653         }
2654 
2655         else
2656         {
2657             CPLDebug( "S57",
2658                       "While applying update %d, Skipping %s record in S57Reader::ApplyUpdates().",
2659                       iUpdate, pszKey );
2660             ret_code = BAD_UPDATE;
2661 
2662         }
2663     }
2664 
2665     return ret_code;
2666 }
2667 
2668 /************************************************************************/
2669 /*                        FindAndApplyUpdates()                         */
2670 /*                                                                      */
2671 /*      Find all update files that would appear to apply to this        */
2672 /*      base file.                                                      */
2673 /************************************************************************/
2674 
FindAndApplyUpdates(const char * pszPath)2675 int S57Reader::FindAndApplyUpdates( const char * pszPath )
2676 
2677 {
2678     int         iUpdate;
2679     int         bSuccess = TRUE;
2680     int         ret_code = 0;
2681 
2682     if( pszPath == NULL )
2683         pszPath = pszModuleName;
2684 
2685     if( !EQUAL(CPLGetExtension(pszPath),"000") )
2686     {
2687         CPLError( CE_Failure, CPLE_AppDefined,
2688                   "Can't apply updates to a base file with a different\n"
2689                   "extension than .000." );
2690         return BAD_UPDATE;
2691     }
2692 
2693     for( iUpdate = 1; bSuccess; iUpdate++ )
2694     {
2695         char    szExtension[16];
2696         char    *pszUpdateFilename;
2697         DDFModule oUpdateModule;
2698 
2699         assert(iUpdate <= 999);
2700         sprintf( szExtension, "%03d", iUpdate );
2701 
2702         pszUpdateFilename = CPLStrdup(CPLResetExtension(pszPath,szExtension));
2703 
2704         bSuccess = oUpdateModule.Open( pszUpdateFilename, TRUE );
2705 
2706         if( bSuccess )
2707             CPLDebug( "S57", "Applying feature updates from %s.",
2708                       pszUpdateFilename );
2709         CPLFree( pszUpdateFilename );
2710 
2711         if( bSuccess )
2712         {
2713               int update_ret = ApplyUpdates( &oUpdateModule, iUpdate );
2714               if(update_ret)
2715                     ret_code = update_ret;
2716         }
2717     }
2718 
2719     return ret_code;
2720 }
2721 
2722 /************************************************************************/
2723 /*                             GetExtent()                              */
2724 /*                                                                      */
2725 /*      Scan all the cached records collecting spatial bounds as        */
2726 /*      efficiently as possible for this transfer.                      */
2727 /************************************************************************/
2728 
2729 // Android uses clang compiler.
2730 // Problem also appears on GCC, on __ARM_ARCH.
2731 //  At optimization -O3, this function has trouble with alignment of values,
2732 //  Specifically, conversion of an int32 from a buffer into double.
2733 //  Workaround: We disable optimization for this little used function.
2734 #ifdef __ARM_ARCH
2735 #if defined(__clang__)
2736 [[clang::optnone]]
2737 #elif defined(__GNUC__) || defined(__GNUG__)
2738 #pragma GCC push_options
2739 #pragma GCC optimize ("O0")
2740 
2741 #endif
2742 #endif
2743 
2744 
GetExtent(OGREnvelope * psExtent,int bForce)2745 OGRErr S57Reader::GetExtent( OGREnvelope *psExtent, int bForce )
2746 
2747 {
2748 #define INDEX_COUNT     4
2749 
2750     DDFRecordIndex      *apoIndex[INDEX_COUNT];
2751 
2752 /* -------------------------------------------------------------------- */
2753 /*      If we aren't forced to get the extent say no if we haven't      */
2754 /*      already indexed the iso8211 records.                            */
2755 /* -------------------------------------------------------------------- */
2756     if( !bForce && !bFileIngested )
2757         return OGRERR_FAILURE;
2758 
2759     Ingest();
2760 
2761 /* -------------------------------------------------------------------- */
2762 /*      We will scan all the low level vector elements for extents      */
2763 /*      coordinates.                                                    */
2764 /* -------------------------------------------------------------------- */
2765     int         bGotExtents = FALSE;
2766     double      nXMin=0, nXMax=0, nYMin=0, nYMax=0;
2767 
2768     apoIndex[0] = &oVI_Index;
2769     apoIndex[1] = &oVC_Index;
2770     apoIndex[2] = &oVE_Index;
2771     apoIndex[3] = &oVF_Index;
2772 
2773     for( int iIndex = 0; iIndex < INDEX_COUNT; iIndex++ )
2774     {
2775         DDFRecordIndex  *poIndex = apoIndex[iIndex];
2776 
2777         for( int iVIndex = 0; iVIndex < poIndex->GetCount(); iVIndex++ )
2778         {
2779             DDFRecord *poRecord = poIndex->GetByIndex( iVIndex );
2780             DDFField    *poSG3D = poRecord->FindField( "SG3D" );
2781             DDFField    *poSG2D = poRecord->FindField( "SG2D" );
2782 
2783             if( poSG3D != NULL )
2784             {
2785                 int     i, nVCount = poSG3D->GetRepeatCount();
2786                 GInt32  *panData, nX, nY;
2787 
2788                 panData = (GInt32 *) poSG3D->GetData();
2789                 for( i = 0; i < nVCount; i++ )
2790                 {
2791                     nX = CPL_LSBWORD32(panData[i*3+1]);
2792                     nY = CPL_LSBWORD32(panData[i*3+0]);
2793 
2794                     double dnX = nX / (double)nCOMF;
2795                     double dnY = nY / (double)nCOMF;
2796 
2797                     if( bGotExtents )
2798                     {
2799                         nXMin = MIN(nXMin,dnX);
2800                         nXMax = MAX(nXMax,dnX);
2801                         nYMin = MIN(nYMin,dnY);
2802                         nYMax = MAX(nYMax,dnY);
2803                     }
2804                     else
2805                     {
2806                         nXMin = nXMax = dnX;
2807                         nYMin = nYMax = dnY;
2808                         bGotExtents = TRUE;
2809                     }
2810                 }
2811             }
2812             else if( poSG2D != NULL )
2813             {
2814                 int     i, nVCount = poSG2D->GetRepeatCount();
2815                 GInt32  *panData, nX, nY;
2816 
2817                 panData = (GInt32 *) poSG2D->GetData();
2818                 for( i = 0; i < nVCount; i++ )
2819                 {
2820                     nX = CPL_LSBWORD32(panData[i*2+1]);
2821                     nY = CPL_LSBWORD32(panData[i*2+0]);
2822 
2823                     double dnX = nX / (double)nCOMF;
2824                     double dnY = nY / (double)nCOMF;
2825 
2826                     if( bGotExtents )
2827                     {
2828                         nXMin = MIN(nXMin,dnX);
2829                         nXMax = MAX(nXMax,dnX);
2830                         nYMin = MIN(nYMin,dnY);
2831                         nYMax = MAX(nYMax,dnY);
2832                     }
2833                     else
2834                     {
2835                         nXMin = nXMax = dnX;
2836                         nYMin = nYMax = dnY;
2837                         bGotExtents = TRUE;
2838                     }
2839                 }
2840             }
2841         }
2842     }
2843 
2844     if( !bGotExtents )
2845         return OGRERR_FAILURE;
2846     else
2847     {
2848         psExtent->MinX = nXMin;
2849         psExtent->MaxX = nXMax;
2850         psExtent->MinY = nYMin;
2851         psExtent->MaxY = nYMax;
2852 
2853         return OGRERR_NONE;
2854     }
2855 }
2856 
2857 #ifdef __ARM_ARCH
2858 #if defined(__GNUC__) || defined(__GNUG__)
2859 #pragma GCC pop_options
2860 #endif
2861 #endif
2862