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  * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
10  *
11  * Permission is hereby granted, free of charge, to any person obtaining a
12  * copy of this software and associated documentation files (the "Software"),
13  * to deal in the Software without restriction, including without limitation
14  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15  * and/or sell copies of the Software, and to permit persons to whom the
16  * Software is furnished to do so, subject to the following conditions:
17  *
18  * The above copyright notice and this permission notice shall be included
19  * in all copies or substantial portions of the Software.
20  *
21  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27  * DEALINGS IN THE SOFTWARE.
28  ****************************************************************************/
29 
30 #include "cpl_conv.h"
31 #include "cpl_string.h"
32 #include "ogr_api.h"
33 #include "s57.h"
34 
35 #include <cmath>
36 
37 #include <algorithm>
38 #include <string>
39 
40 CPL_CVSID("$Id: s57reader.cpp 6a73451ff0b40272a30aa9470d5493f6970ab096 2021-03-28 15:28:29 +0200 Even Rouault $")
41 
42 /**
43 * Recode the given string from a source encoding to UTF-8 encoding.  The source
44 * encoding is established by inspecting the AALL and NALL fields of the S57
45 * DSSI record. If first time, the DSSI is read to setup appropriate
46 * variables. Main scope of this function is to have the strings of all
47 * attributes encoded/recoded to the same codepage in the final Shapefiles .DBF.
48 *
49 * @param[in] SourceString source string to be recoded to UTF-8.
50 *     LookAtAALL-NALL: flag indicating if the string becomes from an
51 *     international attribute (e.g.  INFORM, OBJNAM) or national attribute (e.g
52 *     NINFOM, NOBJNM). The type of encoding is contained in two different
53 *     fields of the S57 DSSI record: AALL for the international attributes,
54 *     NAAL for the national ones, so depending on the type of encoding,
55 *     different fields must be checked to fetch in which way the source string
56 *     is encoded.
57 *
58 *     0: the type of endoding is for international attributes
59 *     1: the type of endoding is for national attributes
60 *
61 * @param[in] LookAtAALL_NALL to be documented
62 *
63 * @return the output string recoded to UTF-8 or left unchanged if no valid
64 *     recoding applicable. The recodinf relies on GDAL functions appropriately
65 *     called, which allocate themselves the necessary memory to hold the
66 *     recoded string.
67 * NOTE: Aall variable is currently not used.
68 *******************************************************************************/
RecodeByDSSI(const char * SourceString,bool LookAtAALL_NALL)69 char *S57Reader::RecodeByDSSI(const char *SourceString, bool LookAtAALL_NALL)
70 {
71     if(needAallNallSetup==true)
72     {
73         OGRFeature *dsidFeature=ReadDSID();
74         if( dsidFeature == nullptr )
75             return CPLStrdup(SourceString);
76         Aall=dsidFeature->GetFieldAsInteger("DSSI_AALL");
77         Nall=dsidFeature->GetFieldAsInteger("DSSI_NALL");
78         CPLDebug("S57", "DSSI_AALL = %d, DSSI_NALL = %d", Aall, Nall);
79         needAallNallSetup=false;
80         delete dsidFeature;
81     }
82 
83     char *RecodedString = nullptr;
84     if(!LookAtAALL_NALL)
85     {
86         // In case of international attributes, only ISO8859-1 code page is
87         // used (standard ascii). The result is identical to the source string
88         // if it contains 0..127 ascii code (LL0), can slightly differ if it
89         // contains diacritics 0..255 ascii codes (LL1).
90         RecodedString = CPLRecode(SourceString,CPL_ENC_ISO8859_1,CPL_ENC_UTF8);
91     }
92     else
93     {
94         if(Nall==2) //national string encoded in UCS-2
95         {
96             GByte *pabyStr = reinterpret_cast<GByte *>(
97                 const_cast<char *>( SourceString ) );
98 
99             /* Count the number of characters */
100             int i=0;
101             while( ! ((pabyStr[2 * i] == DDF_UNIT_TERMINATOR && pabyStr[2 * i + 1] == 0) ||
102                       (pabyStr[2 * i] == 0 && pabyStr[2 * i + 1] == 0)) )
103                 i++;
104 
105             wchar_t *wideString
106                 = static_cast<wchar_t*>( CPLMalloc((i+1) * sizeof(wchar_t)) );
107             i = 0;
108             bool bLittleEndian = true;
109 
110             /* Skip BOM */
111             if( pabyStr[0] == 0xFF && pabyStr[1] == 0xFE )
112                 i++;
113             else if( pabyStr[0] == 0xFE && pabyStr[1] == 0xFF )
114             {
115                 bLittleEndian = false;
116                 i++;
117             }
118 
119             int j=0;
120             while( ! ((pabyStr[2 * i] == DDF_UNIT_TERMINATOR && pabyStr[2 * i + 1] == 0) ||
121                       (pabyStr[2 * i] == 0 && pabyStr[2 * i + 1] == 0)) )
122             {
123                 if( bLittleEndian )
124                     wideString[j++] = pabyStr[i * 2] | (pabyStr[i * 2 + 1] << 8);
125                 else
126                     wideString[j++] = pabyStr[i * 2 + 1] | (pabyStr[i * 2] << 8);
127                 i++;
128             }
129             wideString[j] = 0;
130             RecodedString = CPLRecodeFromWChar(wideString,CPL_ENC_UCS2,CPL_ENC_UTF8);
131             CPLFree(wideString);
132         }
133         else
134         {
135             // National string encoded as ISO8859-1.
136             // See comment for above on LL0/LL1).
137             RecodedString = CPLRecode(SourceString,CPL_ENC_ISO8859_1,CPL_ENC_UTF8);
138         }
139     }
140 
141     if( RecodedString == nullptr )
142         RecodedString = CPLStrdup(SourceString);
143 
144     return RecodedString;
145 }
146 
147 /************************************************************************/
148 /*                             S57Reader()                              */
149 /************************************************************************/
150 
S57Reader(const char * pszFilename)151 S57Reader::S57Reader( const char * pszFilename ) :
152     poRegistrar(nullptr),
153     poClassContentExplorer(nullptr),
154     nFDefnCount(0),
155     papoFDefnList(nullptr),
156     pszModuleName(CPLStrdup( pszFilename )),
157     pszDSNM(nullptr),
158     poModule(nullptr),
159     nCOMF(1000000),
160     nSOMF(10),
161     bFileIngested(false),
162     nNextVIIndex(0),
163     nNextVCIndex(0),
164     nNextVEIndex(0),
165     nNextVFIndex(0),
166     nNextFEIndex(0),
167     nNextDSIDIndex(0),
168     poDSIDRecord(nullptr),
169     poDSPMRecord(nullptr),
170     papszOptions(nullptr),
171     nOptionFlags(S57M_UPDATES),
172     iPointOffset(0),
173     poMultiPoint(nullptr),
174     Aall(0),  // See RecodeByDSSI() function.
175     Nall(0),  // See RecodeByDSSI() function.
176     needAallNallSetup(true),  // See RecodeByDSSI() function.
177     bMissingWarningIssued(false),
178     bAttrWarningIssued(false)
179 {
180 }
181 
182 /************************************************************************/
183 /*                             ~S57Reader()                             */
184 /************************************************************************/
185 
~S57Reader()186 S57Reader::~S57Reader()
187 
188 {
189     Close();
190 
191     CPLFree( pszModuleName );
192     CSLDestroy( papszOptions );
193 
194     CPLFree( papoFDefnList );
195 }
196 
197 /************************************************************************/
198 /*                                Open()                                */
199 /************************************************************************/
200 
Open(int bTestOpen)201 int S57Reader::Open( int bTestOpen )
202 
203 {
204     if( poModule != nullptr )
205     {
206         Rewind();
207         return TRUE;
208     }
209 
210     poModule = new DDFModule();
211     if( !poModule->Open( pszModuleName ) )
212     {
213         // notdef: test bTestOpen.
214         delete poModule;
215         poModule = nullptr;
216         return FALSE;
217     }
218 
219     // note that the following won't work for catalogs.
220     if( poModule->FindFieldDefn("DSID") == nullptr )
221     {
222         if( !bTestOpen )
223         {
224             CPLError( CE_Failure, CPLE_AppDefined,
225                       "%s is an ISO8211 file, but not an S-57 data file.\n",
226                       pszModuleName );
227         }
228         delete poModule;
229         poModule = nullptr;
230         return FALSE;
231     }
232 
233     // Make sure the FSPT field is marked as repeating.
234     DDFFieldDefn *poFSPT = poModule->FindFieldDefn( "FSPT" );
235     if( poFSPT != nullptr && !poFSPT->IsRepeating() )
236     {
237         CPLDebug( "S57", "Forcing FSPT field to be repeating." );
238         poFSPT->SetRepeatingFlag( TRUE );
239     }
240 
241     nNextFEIndex = 0;
242     nNextVIIndex = 0;
243     nNextVCIndex = 0;
244     nNextVEIndex = 0;
245     nNextVFIndex = 0;
246     nNextDSIDIndex = 0;
247 
248     return TRUE;
249 }
250 
251 /************************************************************************/
252 /*                               Close()                                */
253 /************************************************************************/
254 
Close()255 void S57Reader::Close()
256 
257 {
258     if( poModule != nullptr )
259     {
260         oVI_Index.Clear();
261         oVC_Index.Clear();
262         oVE_Index.Clear();
263         oVF_Index.Clear();
264         oFE_Index.Clear();
265 
266         if( poDSIDRecord != nullptr )
267         {
268             delete poDSIDRecord;
269             poDSIDRecord = nullptr;
270         }
271         if( poDSPMRecord != nullptr )
272         {
273             delete poDSPMRecord;
274             poDSPMRecord = nullptr;
275         }
276 
277         ClearPendingMultiPoint();
278 
279         delete poModule;
280         poModule = nullptr;
281 
282         bFileIngested = false;
283 
284         CPLFree( pszDSNM );
285         pszDSNM = nullptr;
286     }
287 }
288 
289 /************************************************************************/
290 /*                       ClearPendingMultiPoint()                       */
291 /************************************************************************/
292 
ClearPendingMultiPoint()293 void S57Reader::ClearPendingMultiPoint()
294 
295 {
296     if( poMultiPoint != nullptr )
297     {
298         delete poMultiPoint;
299         poMultiPoint = nullptr;
300     }
301 }
302 
303 /************************************************************************/
304 /*                       NextPendingMultiPoint()                        */
305 /************************************************************************/
306 
NextPendingMultiPoint()307 OGRFeature *S57Reader::NextPendingMultiPoint()
308 
309 {
310     CPLAssert( poMultiPoint != nullptr );
311     CPLAssert( wkbFlatten(poMultiPoint->GetGeometryRef()->getGeometryType())
312                                                         == wkbMultiPoint );
313 
314     OGRFeatureDefn *poDefn = poMultiPoint->GetDefnRef();
315     OGRFeature  *poPoint = new OGRFeature( poDefn );
316     OGRMultiPoint *poMPGeom = poMultiPoint->GetGeometryRef()->toMultiPoint();
317 
318     poPoint->SetFID( poMultiPoint->GetFID() );
319 
320     for( int i = 0; i < poDefn->GetFieldCount(); i++ )
321     {
322         poPoint->SetField( i, poMultiPoint->GetRawFieldRef(i) );
323     }
324 
325     OGRPoint *poSrcPoint = poMPGeom->getGeometryRef( iPointOffset );
326     iPointOffset++;
327     poPoint->SetGeometry( poSrcPoint );
328 
329     if( (nOptionFlags & S57M_ADD_SOUNDG_DEPTH) )
330         poPoint->SetField( "DEPTH", poSrcPoint->getZ() );
331 
332     if( iPointOffset >= poMPGeom->getNumGeometries() )
333         ClearPendingMultiPoint();
334 
335     return poPoint;
336 }
337 
338 /************************************************************************/
339 /*                             SetOptions()                             */
340 /************************************************************************/
341 
SetOptions(char ** papszOptionsIn)342 bool S57Reader::SetOptions( char ** papszOptionsIn )
343 
344 {
345     CSLDestroy( papszOptions );
346     papszOptions = CSLDuplicate( papszOptionsIn );
347 
348     const char *pszOptionValue
349         = CSLFetchNameValue( papszOptions, S57O_SPLIT_MULTIPOINT );
350     if( pszOptionValue != nullptr && CPLTestBool(pszOptionValue) )
351         nOptionFlags |= S57M_SPLIT_MULTIPOINT;
352     else
353         nOptionFlags &= ~S57M_SPLIT_MULTIPOINT;
354 
355     pszOptionValue = CSLFetchNameValue( papszOptions, S57O_ADD_SOUNDG_DEPTH );
356     if( pszOptionValue != nullptr && CPLTestBool(pszOptionValue) )
357         nOptionFlags |= S57M_ADD_SOUNDG_DEPTH;
358     else
359         nOptionFlags &= ~S57M_ADD_SOUNDG_DEPTH;
360 
361     if( (nOptionFlags & S57M_ADD_SOUNDG_DEPTH) &&
362         !(nOptionFlags & S57M_SPLIT_MULTIPOINT) )
363     {
364         CPLError(CE_Failure, CPLE_AppDefined,
365                  "Inconsistent options : ADD_SOUNDG_DEPTH should only be "
366                  "enabled if SPLIT_MULTIPOINT is also enabled");
367         return false;
368     }
369 
370     pszOptionValue = CSLFetchNameValue( papszOptions, S57O_LNAM_REFS );
371     if( pszOptionValue != nullptr && CPLTestBool(pszOptionValue) )
372         nOptionFlags |= S57M_LNAM_REFS;
373     else
374         nOptionFlags &= ~S57M_LNAM_REFS;
375 
376     pszOptionValue = CSLFetchNameValue( papszOptions, S57O_UPDATES );
377     if( pszOptionValue == nullptr )
378         /* no change */;
379     else if( !EQUAL(pszOptionValue,"APPLY") )
380         nOptionFlags &= ~S57M_UPDATES;
381     else
382         nOptionFlags |= S57M_UPDATES;
383 
384     pszOptionValue = CSLFetchNameValue(papszOptions,
385                                        S57O_PRESERVE_EMPTY_NUMBERS);
386     if( pszOptionValue != nullptr && CPLTestBool(pszOptionValue) )
387         nOptionFlags |= S57M_PRESERVE_EMPTY_NUMBERS;
388     else
389         nOptionFlags &= ~S57M_PRESERVE_EMPTY_NUMBERS;
390 
391     pszOptionValue = CSLFetchNameValue( papszOptions, S57O_RETURN_PRIMITIVES );
392     if( pszOptionValue != nullptr && CPLTestBool(pszOptionValue) )
393         nOptionFlags |= S57M_RETURN_PRIMITIVES;
394     else
395         nOptionFlags &= ~S57M_RETURN_PRIMITIVES;
396 
397     pszOptionValue = CSLFetchNameValue( papszOptions, S57O_RETURN_LINKAGES );
398     if( pszOptionValue != nullptr && CPLTestBool(pszOptionValue) )
399         nOptionFlags |= S57M_RETURN_LINKAGES;
400     else
401         nOptionFlags &= ~S57M_RETURN_LINKAGES;
402 
403     pszOptionValue = CSLFetchNameValue( papszOptions, S57O_RETURN_DSID );
404     if( pszOptionValue == nullptr || CPLTestBool(pszOptionValue) )
405         nOptionFlags |= S57M_RETURN_DSID;
406     else
407         nOptionFlags &= ~S57M_RETURN_DSID;
408 
409     pszOptionValue = CSLFetchNameValue( papszOptions, S57O_RECODE_BY_DSSI );
410     if( pszOptionValue != nullptr && CPLTestBool(pszOptionValue) )
411         nOptionFlags |= S57M_RECODE_BY_DSSI;
412     else
413         nOptionFlags &= ~S57M_RECODE_BY_DSSI;
414 
415     pszOptionValue = CSLFetchNameValue( papszOptions, S57O_LIST_AS_STRING );
416     if( pszOptionValue != nullptr && CPLTestBool(pszOptionValue) )
417         nOptionFlags |= S57M_LIST_AS_STRING;
418     else
419         nOptionFlags &= ~S57M_LIST_AS_STRING;
420 
421     return true;
422 }
423 
424 /************************************************************************/
425 /*                           SetClassBased()                            */
426 /************************************************************************/
427 
SetClassBased(S57ClassRegistrar * poReg,S57ClassContentExplorer * poClassContentExplorerIn)428 void S57Reader::SetClassBased( S57ClassRegistrar * poReg,
429                                S57ClassContentExplorer* poClassContentExplorerIn )
430 
431 {
432     poRegistrar = poReg;
433     poClassContentExplorer = poClassContentExplorerIn;
434 }
435 
436 /************************************************************************/
437 /*                               Rewind()                               */
438 /************************************************************************/
439 
Rewind()440 void S57Reader::Rewind()
441 
442 {
443     ClearPendingMultiPoint();
444     nNextFEIndex = 0;
445     nNextVIIndex = 0;
446     nNextVCIndex = 0;
447     nNextVEIndex = 0;
448     nNextVFIndex = 0;
449     nNextDSIDIndex = 0;
450 }
451 
452 /************************************************************************/
453 /*                               Ingest()                               */
454 /*                                                                      */
455 /*      Read all the records into memory, adding to the appropriate     */
456 /*      indexes.                                                        */
457 /************************************************************************/
458 
Ingest()459 bool S57Reader::Ingest()
460 
461 {
462     if( poModule == nullptr || bFileIngested )
463         return true;
464 
465 /* -------------------------------------------------------------------- */
466 /*      Read all the records in the module, and place them in           */
467 /*      appropriate indexes.                                            */
468 /* -------------------------------------------------------------------- */
469     CPLErrorReset();
470     DDFRecord *poRecord = nullptr;
471     while( (poRecord = poModule->ReadRecord()) != nullptr )
472     {
473         DDFField *poKeyField = poRecord->GetField(1);
474         if (poKeyField == nullptr)
475             return false;
476         DDFFieldDefn* poKeyFieldDefn = poKeyField->GetFieldDefn();
477         if( poKeyFieldDefn == nullptr )
478             continue;
479         const char* pszName = poKeyFieldDefn->GetName();
480         if( pszName == nullptr )
481             continue;
482 
483         if( EQUAL(pszName,"VRID") )
484         {
485             int bSuccess = FALSE;
486             const int nRCNM = poRecord->GetIntSubfield( "VRID",0, "RCNM",0, &bSuccess);
487             if( !bSuccess && CPLGetLastErrorType() == CE_Failure )
488                 break;
489             const int nRCID = poRecord->GetIntSubfield( "VRID",0, "RCID",0, &bSuccess);
490             if( !bSuccess && CPLGetLastErrorType() == CE_Failure )
491                 break;
492 
493             switch( nRCNM )
494             {
495               case RCNM_VI:
496                 oVI_Index.AddRecord( nRCID, poRecord->Clone() );
497                 break;
498 
499               case RCNM_VC:
500                 oVC_Index.AddRecord( nRCID, poRecord->Clone() );
501                 break;
502 
503               case RCNM_VE:
504                 oVE_Index.AddRecord( nRCID, poRecord->Clone() );
505                 break;
506 
507               case RCNM_VF:
508                 oVF_Index.AddRecord( nRCID, poRecord->Clone() );
509                 break;
510 
511               default:
512                 CPLError(CE_Failure, CPLE_AppDefined,
513                          "Unhandled value for RCNM ; %d", nRCNM);
514                 break;
515             }
516         }
517 
518         else if( EQUAL(pszName,"FRID") )
519         {
520             int bSuccess = FALSE;
521             int         nRCID = poRecord->GetIntSubfield( "FRID",0, "RCID",0, &bSuccess);
522             if( !bSuccess && CPLGetLastErrorType() == CE_Failure )
523                 break;
524 
525             oFE_Index.AddRecord( nRCID, poRecord->Clone() );
526         }
527 
528         else if( EQUAL(pszName,"DSID") )
529         {
530             int bSuccess = FALSE;
531             CPLFree( pszDSNM );
532             pszDSNM =
533                 CPLStrdup(poRecord->GetStringSubfield( "DSID", 0, "DSNM", 0, &bSuccess ));
534             if( !bSuccess && CPLGetLastErrorType() == CE_Failure )
535                 break;
536 
537             if( nOptionFlags & S57M_RETURN_DSID )
538             {
539                 if( poDSIDRecord != nullptr )
540                     delete poDSIDRecord;
541 
542                 poDSIDRecord = poRecord->Clone();
543             }
544         }
545 
546         else if( EQUAL(pszName,"DSPM") )
547         {
548             int bSuccess = FALSE;
549             nCOMF = std::max(1, poRecord->GetIntSubfield( "DSPM",0, "COMF",0, &bSuccess));
550             if( !bSuccess && CPLGetLastErrorType() == CE_Failure )
551                 break;
552             nSOMF = std::max(1, poRecord->GetIntSubfield( "DSPM",0, "SOMF",0, &bSuccess));
553             if( !bSuccess && CPLGetLastErrorType() == CE_Failure )
554                 break;
555 
556             if( nOptionFlags & S57M_RETURN_DSID )
557             {
558                 if( poDSPMRecord != nullptr )
559                     delete poDSPMRecord;
560 
561                 poDSPMRecord = poRecord->Clone();
562             }
563         }
564 
565         else
566         {
567             CPLDebug( "S57",
568                       "Skipping %s record in S57Reader::Ingest().",
569                       pszName );
570         }
571     }
572 
573     if( CPLGetLastErrorType() == CE_Failure )
574         return false;
575 
576     bFileIngested = true;
577 
578 /* -------------------------------------------------------------------- */
579 /*      If update support is enabled, read and apply them.              */
580 /* -------------------------------------------------------------------- */
581     if( nOptionFlags & S57M_UPDATES )
582         return FindAndApplyUpdates();
583 
584     return true;
585 }
586 
587 /************************************************************************/
588 /*                           SetNextFEIndex()                           */
589 /************************************************************************/
590 
SetNextFEIndex(int nNewIndex,int nRCNM)591 void S57Reader::SetNextFEIndex( int nNewIndex, int nRCNM )
592 
593 {
594     if( nRCNM == RCNM_VI )
595         nNextVIIndex = nNewIndex;
596     else if( nRCNM == RCNM_VC )
597         nNextVCIndex = nNewIndex;
598     else if( nRCNM == RCNM_VE )
599         nNextVEIndex = nNewIndex;
600     else if( nRCNM == RCNM_VF )
601         nNextVFIndex = nNewIndex;
602     else if( nRCNM == RCNM_DSID )
603         nNextDSIDIndex = nNewIndex;
604     else
605     {
606         if( nNextFEIndex != nNewIndex )
607             ClearPendingMultiPoint();
608 
609         nNextFEIndex = nNewIndex;
610     }
611 }
612 
613 /************************************************************************/
614 /*                           GetNextFEIndex()                           */
615 /************************************************************************/
616 
GetNextFEIndex(int nRCNM)617 int S57Reader::GetNextFEIndex( int nRCNM )
618 
619 {
620     if( nRCNM == RCNM_VI )
621         return nNextVIIndex;
622     if( nRCNM == RCNM_VC )
623         return nNextVCIndex;
624     if( nRCNM == RCNM_VE )
625         return nNextVEIndex;
626     if( nRCNM == RCNM_VF )
627         return nNextVFIndex;
628     if( nRCNM == RCNM_DSID )
629         return nNextDSIDIndex;
630 
631     return nNextFEIndex;
632 }
633 
634 /************************************************************************/
635 /*                          ReadNextFeature()                           */
636 /************************************************************************/
637 
ReadNextFeature(OGRFeatureDefn * poTarget)638 OGRFeature * S57Reader::ReadNextFeature( OGRFeatureDefn * poTarget )
639 
640 {
641     if( !bFileIngested && !Ingest() )
642         return nullptr;
643 
644 /* -------------------------------------------------------------------- */
645 /*      Special case for "in progress" multipoints being split up.      */
646 /* -------------------------------------------------------------------- */
647     if( poMultiPoint != nullptr )
648     {
649         if( poTarget == nullptr || poTarget == poMultiPoint->GetDefnRef() )
650         {
651             return NextPendingMultiPoint();
652         }
653         else
654         {
655             ClearPendingMultiPoint();
656         }
657     }
658 
659 /* -------------------------------------------------------------------- */
660 /*      Next vector feature?                                            */
661 /* -------------------------------------------------------------------- */
662     if( (nOptionFlags & S57M_RETURN_DSID)
663         && nNextDSIDIndex == 0
664         && (poTarget == nullptr || EQUAL(poTarget->GetName(),"DSID")) )
665     {
666         return ReadDSID();
667     }
668 
669 /* -------------------------------------------------------------------- */
670 /*      Next vector feature?                                            */
671 /* -------------------------------------------------------------------- */
672     if( nOptionFlags & S57M_RETURN_PRIMITIVES )
673     {
674         int nRCNM = 0;
675         int *pnCounter = nullptr;
676 
677         if( poTarget == nullptr )
678         {
679             if( nNextVIIndex < oVI_Index.GetCount() )
680             {
681                 nRCNM = RCNM_VI;
682                 pnCounter = &nNextVIIndex;
683             }
684             else if( nNextVCIndex < oVC_Index.GetCount() )
685             {
686                 nRCNM = RCNM_VC;
687                 pnCounter = &nNextVCIndex;
688             }
689             else if( nNextVEIndex < oVE_Index.GetCount() )
690             {
691                 nRCNM = RCNM_VE;
692                 pnCounter = &nNextVEIndex;
693             }
694             else if( nNextVFIndex < oVF_Index.GetCount() )
695             {
696                 nRCNM = RCNM_VF;
697                 pnCounter = &nNextVFIndex;
698             }
699         }
700         else
701         {
702             if( EQUAL(poTarget->GetName(),OGRN_VI) )
703             {
704                 nRCNM = RCNM_VI;
705                 pnCounter = &nNextVIIndex;
706             }
707             else if( EQUAL(poTarget->GetName(),OGRN_VC) )
708             {
709                 nRCNM = RCNM_VC;
710                 pnCounter = &nNextVCIndex;
711             }
712             else if( EQUAL(poTarget->GetName(),OGRN_VE) )
713             {
714                 nRCNM = RCNM_VE;
715                 pnCounter = &nNextVEIndex;
716             }
717             else if( EQUAL(poTarget->GetName(),OGRN_VF) )
718             {
719                 nRCNM = RCNM_VF;
720                 pnCounter = &nNextVFIndex;
721             }
722         }
723 
724         if( nRCNM != 0 )
725         {
726             OGRFeature *poFeature = ReadVector( *pnCounter, nRCNM );
727             if( poFeature != nullptr )
728             {
729                 *pnCounter += 1;
730                 return poFeature;
731             }
732         }
733     }
734 
735 /* -------------------------------------------------------------------- */
736 /*      Next feature.                                                   */
737 /* -------------------------------------------------------------------- */
738     while( nNextFEIndex < oFE_Index.GetCount() )
739     {
740         OGRFeatureDefn *poFeatureDefn
741           = static_cast<OGRFeatureDefn *>( oFE_Index.GetClientInfoByIndex( nNextFEIndex ) );
742 
743         if( poFeatureDefn == nullptr )
744         {
745             poFeatureDefn = FindFDefn( oFE_Index.GetByIndex( nNextFEIndex ) );
746             oFE_Index.SetClientInfoByIndex( nNextFEIndex, poFeatureDefn );
747         }
748 
749         if( poFeatureDefn != poTarget && poTarget != nullptr )
750         {
751             nNextFEIndex++;
752             continue;
753         }
754 
755         OGRFeature *poFeature = ReadFeature( nNextFEIndex++, poTarget );
756         if( poFeature != nullptr )
757         {
758             if( (nOptionFlags & S57M_SPLIT_MULTIPOINT)
759                 && poFeature->GetGeometryRef() != nullptr
760                 && wkbFlatten(poFeature->GetGeometryRef()->getGeometryType())
761                                                         == wkbMultiPoint)
762             {
763                 poMultiPoint = poFeature;
764                 iPointOffset = 0;
765                 return NextPendingMultiPoint();
766             }
767 
768             return poFeature;
769         }
770     }
771 
772     return nullptr;
773 }
774 
775 /************************************************************************/
776 /*                            ReadFeature()                             */
777 /*                                                                      */
778 /*      Read the features who's id is provided.                         */
779 /************************************************************************/
780 
ReadFeature(int nFeatureId,OGRFeatureDefn * poTarget)781 OGRFeature *S57Reader::ReadFeature( int nFeatureId, OGRFeatureDefn *poTarget )
782 
783 {
784     if( nFeatureId < 0 || nFeatureId >= oFE_Index.GetCount() )
785         return nullptr;
786 
787     OGRFeature  *poFeature = nullptr;
788 
789     if( (nOptionFlags & S57M_RETURN_DSID)
790         && nFeatureId == 0
791         && (poTarget == nullptr || EQUAL(poTarget->GetName(),"DSID")) )
792     {
793         poFeature = ReadDSID();
794     }
795     else
796     {
797         poFeature = AssembleFeature( oFE_Index.GetByIndex(nFeatureId),
798                                     poTarget );
799     }
800     if( poFeature != nullptr )
801         poFeature->SetFID( nFeatureId );
802 
803     return poFeature;
804 }
805 
806 /************************************************************************/
807 /*                          AssembleFeature()                           */
808 /*                                                                      */
809 /*      Assemble an OGR feature based on a feature record.              */
810 /************************************************************************/
811 
AssembleFeature(DDFRecord * poRecord,OGRFeatureDefn * poTarget)812 OGRFeature *S57Reader::AssembleFeature( DDFRecord * poRecord,
813                                         OGRFeatureDefn * poTarget )
814 
815 {
816 /* -------------------------------------------------------------------- */
817 /*      Find the feature definition to use.  Currently this is based    */
818 /*      on the primitive, but eventually this should be based on the    */
819 /*      object class (FRID.OBJL) in some cases, and the primitive in    */
820 /*      others.                                                         */
821 /* -------------------------------------------------------------------- */
822     OGRFeatureDefn *poFDefn = FindFDefn( poRecord );
823     if( poFDefn == nullptr )
824         return nullptr;
825 
826 /* -------------------------------------------------------------------- */
827 /*      Does this match our target feature definition?  If not skip     */
828 /*      this feature.                                                   */
829 /* -------------------------------------------------------------------- */
830     if( poTarget != nullptr && poFDefn != poTarget )
831         return nullptr;
832 
833 /* -------------------------------------------------------------------- */
834 /*      Create the new feature object.                                  */
835 /* -------------------------------------------------------------------- */
836     OGRFeature *poFeature = new OGRFeature( poFDefn );
837 
838 /* -------------------------------------------------------------------- */
839 /*      Assign a few standard feature attributes.                        */
840 /* -------------------------------------------------------------------- */
841     int nOBJL = poRecord->GetIntSubfield( "FRID", 0, "OBJL", 0 );
842     poFeature->SetField( "OBJL", nOBJL );
843 
844     poFeature->SetField( "RCID",
845                          poRecord->GetIntSubfield( "FRID", 0, "RCID", 0 ));
846     poFeature->SetField( "PRIM",
847                          poRecord->GetIntSubfield( "FRID", 0, "PRIM", 0 ));
848     poFeature->SetField( "GRUP",
849                          poRecord->GetIntSubfield( "FRID", 0, "GRUP", 0 ));
850     poFeature->SetField( "RVER",
851                          poRecord->GetIntSubfield( "FRID", 0, "RVER", 0 ));
852     poFeature->SetField( "AGEN",
853                          poRecord->GetIntSubfield( "FOID", 0, "AGEN", 0 ));
854     poFeature->SetField( "FIDN",
855                          poRecord->GetIntSubfield( "FOID", 0, "FIDN", 0 ));
856     poFeature->SetField( "FIDS",
857                          poRecord->GetIntSubfield( "FOID", 0, "FIDS", 0 ));
858 
859 /* -------------------------------------------------------------------- */
860 /*      Generate long name, if requested.                               */
861 /* -------------------------------------------------------------------- */
862     if( nOptionFlags & S57M_LNAM_REFS )
863     {
864         GenerateLNAMAndRefs( poRecord, poFeature );
865     }
866 
867 /* -------------------------------------------------------------------- */
868 /*      Generate primitive references if requested.                     */
869 /* -------------------------------------------------------------------- */
870     if( nOptionFlags & S57M_RETURN_LINKAGES )
871         GenerateFSPTAttributes( poRecord, poFeature );
872 
873 /* -------------------------------------------------------------------- */
874 /*      Apply object class specific attributes, if supported.           */
875 /* -------------------------------------------------------------------- */
876     if( poRegistrar != nullptr )
877         ApplyObjectClassAttributes( poRecord, poFeature );
878 
879 /* -------------------------------------------------------------------- */
880 /*      Find and assign spatial component.                              */
881 /* -------------------------------------------------------------------- */
882     const int nPRIM = poRecord->GetIntSubfield( "FRID", 0, "PRIM", 0 );
883 
884     if( nPRIM == PRIM_P )
885     {
886         if( nOBJL == 129 ) /* SOUNDG */
887             AssembleSoundingGeometry( poRecord, poFeature );
888         else
889             AssemblePointGeometry( poRecord, poFeature );
890     }
891     else if( nPRIM == PRIM_L )
892     {
893         AssembleLineGeometry( poRecord, poFeature );
894     }
895     else if( nPRIM == PRIM_A )
896     {
897         AssembleAreaGeometry( poRecord, poFeature );
898     }
899 
900     return poFeature;
901 }
902 
903 /************************************************************************/
904 /*                     ApplyObjectClassAttributes()                     */
905 /************************************************************************/
906 
ApplyObjectClassAttributes(DDFRecord * poRecord,OGRFeature * poFeature)907 void S57Reader::ApplyObjectClassAttributes( DDFRecord * poRecord,
908                                             OGRFeature * poFeature )
909 
910 {
911 /* -------------------------------------------------------------------- */
912 /*      ATTF Attributes                                                 */
913 /* -------------------------------------------------------------------- */
914     DDFField    *poATTF = poRecord->FindField( "ATTF" );
915 
916     if( poATTF == nullptr )
917         return;
918 
919     int nAttrCount = poATTF->GetRepeatCount();
920     for( int iAttr = 0; iAttr < nAttrCount; iAttr++ )
921     {
922         const int nAttrId
923             = poRecord->GetIntSubfield( "ATTF", 0, "ATTL", iAttr );
924 
925         if( poRegistrar->GetAttrInfo(nAttrId) == nullptr )
926         {
927             if( !bAttrWarningIssued )
928             {
929                 bAttrWarningIssued = true;
930                 CPLError( CE_Warning, CPLE_AppDefined,
931                           "Illegal feature attribute id (ATTF:ATTL[%d]) of %d\n"
932                           "on feature FIDN=%d, FIDS=%d.\n"
933                           "Skipping attribute. "
934                           "No more warnings will be issued.",
935                           iAttr, nAttrId,
936                           poFeature->GetFieldAsInteger( "FIDN" ),
937                           poFeature->GetFieldAsInteger( "FIDS" ) );
938             }
939 
940             continue;
941         }
942 
943         /* Fetch the attribute value */
944         const char *pszValue =
945             poRecord->GetStringSubfield("ATTF",0,"ATVL",iAttr);
946         if( pszValue == nullptr )
947             return;
948 
949         //If needed, recode the string in UTF-8.
950         char* pszValueToFree = nullptr;
951         if(nOptionFlags & S57M_RECODE_BY_DSSI)
952             pszValue = pszValueToFree = RecodeByDSSI(pszValue,false);
953 
954         /* Apply to feature in an appropriate way */
955         const char *pszAcronym = poRegistrar->GetAttrAcronym(nAttrId);
956         const int iField = poFeature->GetDefnRef()->GetFieldIndex(pszAcronym);
957         if( iField < 0 )
958         {
959             if( !bMissingWarningIssued )
960             {
961                 bMissingWarningIssued = true;
962                 CPLError( CE_Warning, CPLE_AppDefined,
963                           "Attributes %s ignored, not in expected schema.\n"
964                           "No more warnings will be issued for this dataset.",
965                           pszAcronym );
966             }
967             CPLFree(pszValueToFree);
968             continue;
969         }
970 
971         OGRFieldDefn *poFldDefn
972             = poFeature->GetDefnRef()->GetFieldDefn( iField );
973         const auto eType = poFldDefn->GetType();
974         if( eType == OFTInteger
975             || eType == OFTReal )
976         {
977             if( strlen(pszValue) == 0 )
978             {
979                 if( nOptionFlags & S57M_PRESERVE_EMPTY_NUMBERS )
980                     poFeature->SetField( iField, EMPTY_NUMBER_MARKER );
981                 else
982                 {
983                     /* leave as null if value was empty string */
984                 }
985             }
986             else
987                 poFeature->SetField( iField, pszValue );
988         }
989         else if( eType == OFTStringList )
990         {
991             char** papszTokens = CSLTokenizeString2(pszValue, ",", 0);
992             poFeature->SetField( iField, papszTokens );
993             CSLDestroy(papszTokens);
994         }
995         else
996         {
997             poFeature->SetField( iField, pszValue );
998         }
999 
1000         CPLFree(pszValueToFree);
1001     }
1002 
1003 /* -------------------------------------------------------------------- */
1004 /*      NATF (national) attributes                                      */
1005 /* -------------------------------------------------------------------- */
1006     DDFField    *poNATF = poRecord->FindField( "NATF" );
1007 
1008     if( poNATF == nullptr )
1009         return;
1010 
1011     nAttrCount = poNATF->GetRepeatCount();
1012     for( int iAttr = 0; iAttr < nAttrCount; iAttr++ )
1013     {
1014         const int nAttrId = poRecord->GetIntSubfield("NATF",0,"ATTL",iAttr);
1015         const char *pszAcronym = poRegistrar->GetAttrAcronym(nAttrId);
1016 
1017         if( pszAcronym == nullptr )
1018         {
1019             if( !bAttrWarningIssued )
1020             {
1021                 bAttrWarningIssued = true;
1022                 CPLError( CE_Warning, CPLE_AppDefined,
1023                           "Illegal feature attribute id (NATF:ATTL[%d]) of %d\n"
1024                           "on feature FIDN=%d, FIDS=%d.\n"
1025                           "Skipping attribute, no more warnings will be issued.",
1026                           iAttr, nAttrId,
1027                           poFeature->GetFieldAsInteger( "FIDN" ),
1028                           poFeature->GetFieldAsInteger( "FIDS" ) );
1029             }
1030 
1031             continue;
1032         }
1033 
1034         //If needed, recode the string in UTF-8.
1035         const char *pszValue = poRecord->GetStringSubfield("NATF",0,"ATVL",iAttr);
1036         if( pszValue != nullptr )
1037         {
1038             if(nOptionFlags & S57M_RECODE_BY_DSSI)
1039             {
1040                 char* pszValueRecoded = RecodeByDSSI(pszValue,true);
1041                 poFeature->SetField(pszAcronym,pszValueRecoded);
1042                 CPLFree(pszValueRecoded);
1043             }
1044             else
1045                 poFeature->SetField(pszAcronym,pszValue);
1046         }
1047     }
1048 }
1049 
1050 /************************************************************************/
1051 /*                        GenerateLNAMAndRefs()                         */
1052 /************************************************************************/
1053 
GenerateLNAMAndRefs(DDFRecord * poRecord,OGRFeature * poFeature)1054 void S57Reader::GenerateLNAMAndRefs( DDFRecord * poRecord,
1055                                      OGRFeature * poFeature )
1056 
1057 {
1058 /* -------------------------------------------------------------------- */
1059 /*      Apply the LNAM to the object.                                   */
1060 /* -------------------------------------------------------------------- */
1061     char szLNAM[32];
1062     snprintf( szLNAM, sizeof(szLNAM), "%04X%08X%04X",
1063              poFeature->GetFieldAsInteger( "AGEN" ),
1064              poFeature->GetFieldAsInteger( "FIDN" ),
1065              poFeature->GetFieldAsInteger( "FIDS" ) );
1066     poFeature->SetField( "LNAM", szLNAM );
1067 
1068 /* -------------------------------------------------------------------- */
1069 /*      Do we have references to other features.                        */
1070 /* -------------------------------------------------------------------- */
1071     DDFField *poFFPT = poRecord->FindField( "FFPT" );
1072 
1073     if( poFFPT == nullptr )
1074         return;
1075 
1076 /* -------------------------------------------------------------------- */
1077 /*      Apply references.                                               */
1078 /* -------------------------------------------------------------------- */
1079     const int nRefCount = poFFPT->GetRepeatCount();
1080 
1081     DDFSubfieldDefn *poLNAM
1082         = poFFPT->GetFieldDefn()->FindSubfieldDefn( "LNAM" );
1083     DDFSubfieldDefn *poRIND
1084         = poFFPT->GetFieldDefn()->FindSubfieldDefn( "RIND" );
1085     if( poLNAM == nullptr || poRIND == nullptr )
1086     {
1087         return;
1088     }
1089 
1090     int *panRIND = static_cast<int *>( CPLMalloc(sizeof(int) * nRefCount) );
1091     char **papszRefs = nullptr;
1092 
1093     for( int iRef = 0; iRef < nRefCount; iRef++ )
1094     {
1095         int nMaxBytes = 0;
1096 
1097         unsigned char *pabyData = reinterpret_cast<unsigned char *>(
1098             const_cast<char *>(
1099                 poFFPT->GetSubfieldData( poLNAM, &nMaxBytes, iRef ) ) );
1100         if( pabyData == nullptr || nMaxBytes < 8 )
1101         {
1102             CSLDestroy( papszRefs );
1103             CPLFree( panRIND );
1104             return;
1105         }
1106 
1107         snprintf( szLNAM, sizeof(szLNAM), "%02X%02X%02X%02X%02X%02X%02X%02X",
1108                  pabyData[1], pabyData[0], /* AGEN */
1109                  pabyData[5], pabyData[4], pabyData[3], pabyData[2], /* FIDN */
1110                  pabyData[7], pabyData[6] );
1111 
1112         papszRefs = CSLAddString( papszRefs, szLNAM );
1113 
1114         pabyData = reinterpret_cast<unsigned char *>(const_cast<char *>(
1115             poFFPT->GetSubfieldData( poRIND, &nMaxBytes, iRef ) ) );
1116         if( pabyData == nullptr || nMaxBytes < 1 )
1117         {
1118             CSLDestroy( papszRefs );
1119             CPLFree( panRIND );
1120             return;
1121         }
1122         panRIND[iRef] = pabyData[0];
1123     }
1124 
1125     poFeature->SetField( "LNAM_REFS", papszRefs );
1126     CSLDestroy( papszRefs );
1127 
1128     poFeature->SetField( "FFPT_RIND", nRefCount, panRIND );
1129     CPLFree( panRIND );
1130 }
1131 
1132 /************************************************************************/
1133 /*                       GenerateFSPTAttributes()                       */
1134 /************************************************************************/
1135 
GenerateFSPTAttributes(DDFRecord * poRecord,OGRFeature * poFeature)1136 void S57Reader::GenerateFSPTAttributes( DDFRecord * poRecord,
1137                                         OGRFeature * poFeature )
1138 
1139 {
1140 /* -------------------------------------------------------------------- */
1141 /*      Feature the spatial record containing the point.                */
1142 /* -------------------------------------------------------------------- */
1143     DDFField *poFSPT = poRecord->FindField( "FSPT" );
1144     if( poFSPT == nullptr )
1145         return;
1146 
1147     const int nCount = poFSPT->GetRepeatCount();
1148 
1149 /* -------------------------------------------------------------------- */
1150 /*      Allocate working lists of the attributes.                       */
1151 /* -------------------------------------------------------------------- */
1152     int * const panORNT = static_cast<int *>( CPLMalloc( sizeof(int) * nCount ) );
1153     int * const panUSAG = static_cast<int *>( CPLMalloc( sizeof(int) * nCount ) );
1154     int * const panMASK = static_cast<int *>( CPLMalloc( sizeof(int) * nCount ) );
1155     int * const panRCNM = static_cast<int *>( CPLMalloc( sizeof(int) * nCount ) );
1156     int *panRCID = static_cast<int *>( CPLMalloc( sizeof(int) * nCount ) );
1157 
1158 /* -------------------------------------------------------------------- */
1159 /*      loop over all entries, decoding them.                           */
1160 /* -------------------------------------------------------------------- */
1161     for( int i = 0; i < nCount; i++ )
1162     {
1163         panRCID[i] = ParseName( poFSPT, i, panRCNM + i );
1164         panORNT[i] = poRecord->GetIntSubfield( "FSPT", 0, "ORNT",i);
1165         panUSAG[i] = poRecord->GetIntSubfield( "FSPT", 0, "USAG",i);
1166         panMASK[i] = poRecord->GetIntSubfield( "FSPT", 0, "MASK",i);
1167     }
1168 
1169 /* -------------------------------------------------------------------- */
1170 /*      Assign to feature.                                              */
1171 /* -------------------------------------------------------------------- */
1172     poFeature->SetField( "NAME_RCNM", nCount, panRCNM );
1173     poFeature->SetField( "NAME_RCID", nCount, panRCID );
1174     poFeature->SetField( "ORNT", nCount, panORNT );
1175     poFeature->SetField( "USAG", nCount, panUSAG );
1176     poFeature->SetField( "MASK", nCount, panMASK );
1177 
1178 /* -------------------------------------------------------------------- */
1179 /*      Cleanup.                                                        */
1180 /* -------------------------------------------------------------------- */
1181     CPLFree( panRCNM );
1182     CPLFree( panRCID );
1183     CPLFree( panORNT );
1184     CPLFree( panUSAG );
1185     CPLFree( panMASK );
1186 }
1187 
1188 /************************************************************************/
1189 /*                              ReadDSID()                              */
1190 /************************************************************************/
1191 
ReadDSID()1192 OGRFeature *S57Reader::ReadDSID()
1193 
1194 {
1195     if( poDSIDRecord == nullptr && poDSPMRecord == nullptr )
1196         return nullptr;
1197 
1198 /* -------------------------------------------------------------------- */
1199 /*      Find the feature definition to use.                             */
1200 /* -------------------------------------------------------------------- */
1201     OGRFeatureDefn *poFDefn = nullptr;
1202 
1203     for( int i = 0; i < nFDefnCount; i++ )
1204     {
1205         if( EQUAL(papoFDefnList[i]->GetName(),"DSID") )
1206         {
1207             poFDefn = papoFDefnList[i];
1208             break;
1209         }
1210     }
1211 
1212     if( poFDefn == nullptr )
1213     {
1214         // CPLAssert( false );
1215         return nullptr;
1216     }
1217 
1218 /* -------------------------------------------------------------------- */
1219 /*      Create feature.                                                 */
1220 /* -------------------------------------------------------------------- */
1221     OGRFeature *poFeature = new OGRFeature( poFDefn );
1222 
1223 /* -------------------------------------------------------------------- */
1224 /*      Apply DSID values.                                              */
1225 /* -------------------------------------------------------------------- */
1226     if( poDSIDRecord != nullptr )
1227     {
1228         poFeature->SetField( "DSID_EXPP",
1229                      poDSIDRecord->GetIntSubfield( "DSID", 0, "EXPP", 0 ));
1230         poFeature->SetField( "DSID_INTU",
1231                      poDSIDRecord->GetIntSubfield( "DSID", 0, "INTU", 0 ));
1232         poFeature->SetField( "DSID_DSNM",
1233                      poDSIDRecord->GetStringSubfield( "DSID", 0, "DSNM", 0 ));
1234         if( !m_osEDTNUpdate.empty() )
1235             poFeature->SetField( "DSID_EDTN", m_osEDTNUpdate.c_str() );
1236         else
1237             poFeature->SetField( "DSID_EDTN",
1238                      poDSIDRecord->GetStringSubfield( "DSID", 0, "EDTN", 0 ));
1239         if( !m_osUPDNUpdate.empty() )
1240             poFeature->SetField( "DSID_UPDN", m_osUPDNUpdate.c_str() );
1241         else
1242             poFeature->SetField( "DSID_UPDN",
1243                      poDSIDRecord->GetStringSubfield( "DSID", 0, "UPDN", 0 ));
1244 
1245         poFeature->SetField( "DSID_UADT",
1246                      poDSIDRecord->GetStringSubfield( "DSID", 0, "UADT", 0 ));
1247         if( !m_osISDTUpdate.empty() )
1248             poFeature->SetField( "DSID_ISDT", m_osISDTUpdate.c_str() );
1249         else
1250             poFeature->SetField( "DSID_ISDT",
1251                      poDSIDRecord->GetStringSubfield( "DSID", 0, "ISDT", 0 ));
1252         poFeature->SetField( "DSID_STED",
1253                      poDSIDRecord->GetFloatSubfield( "DSID", 0, "STED", 0 ));
1254         poFeature->SetField( "DSID_PRSP",
1255                      poDSIDRecord->GetIntSubfield( "DSID", 0, "PRSP", 0 ));
1256         poFeature->SetField( "DSID_PSDN",
1257                      poDSIDRecord->GetStringSubfield( "DSID", 0, "PSDN", 0 ));
1258         poFeature->SetField( "DSID_PRED",
1259                      poDSIDRecord->GetStringSubfield( "DSID", 0, "PRED", 0 ));
1260         poFeature->SetField( "DSID_PROF",
1261                      poDSIDRecord->GetIntSubfield( "DSID", 0, "PROF", 0 ));
1262         poFeature->SetField( "DSID_AGEN",
1263                      poDSIDRecord->GetIntSubfield( "DSID", 0, "AGEN", 0 ));
1264         poFeature->SetField( "DSID_COMT",
1265                      poDSIDRecord->GetStringSubfield( "DSID", 0, "COMT", 0 ));
1266 
1267 /* -------------------------------------------------------------------- */
1268 /*      Apply DSSI values.                                              */
1269 /* -------------------------------------------------------------------- */
1270         poFeature->SetField( "DSSI_DSTR",
1271                      poDSIDRecord->GetIntSubfield( "DSSI", 0, "DSTR", 0 ));
1272         poFeature->SetField( "DSSI_AALL",
1273                      poDSIDRecord->GetIntSubfield( "DSSI", 0, "AALL", 0 ));
1274         poFeature->SetField( "DSSI_NALL",
1275                      poDSIDRecord->GetIntSubfield( "DSSI", 0, "NALL", 0 ));
1276         poFeature->SetField( "DSSI_NOMR",
1277                      poDSIDRecord->GetIntSubfield( "DSSI", 0, "NOMR", 0 ));
1278         poFeature->SetField( "DSSI_NOCR",
1279                      poDSIDRecord->GetIntSubfield( "DSSI", 0, "NOCR", 0 ));
1280         poFeature->SetField( "DSSI_NOGR",
1281                      poDSIDRecord->GetIntSubfield( "DSSI", 0, "NOGR", 0 ));
1282         poFeature->SetField( "DSSI_NOLR",
1283                      poDSIDRecord->GetIntSubfield( "DSSI", 0, "NOLR", 0 ));
1284         poFeature->SetField( "DSSI_NOIN",
1285                      poDSIDRecord->GetIntSubfield( "DSSI", 0, "NOIN", 0 ));
1286         poFeature->SetField( "DSSI_NOCN",
1287                      poDSIDRecord->GetIntSubfield( "DSSI", 0, "NOCN", 0 ));
1288         poFeature->SetField( "DSSI_NOED",
1289                      poDSIDRecord->GetIntSubfield( "DSSI", 0, "NOED", 0 ));
1290         poFeature->SetField( "DSSI_NOFA",
1291                      poDSIDRecord->GetIntSubfield( "DSSI", 0, "NOFA", 0 ));
1292     }
1293 
1294 /* -------------------------------------------------------------------- */
1295 /*      Apply DSPM record.                                              */
1296 /* -------------------------------------------------------------------- */
1297     if( poDSPMRecord != nullptr )
1298     {
1299         poFeature->SetField( "DSPM_HDAT",
1300                       poDSPMRecord->GetIntSubfield( "DSPM", 0, "HDAT", 0 ));
1301         poFeature->SetField( "DSPM_VDAT",
1302                       poDSPMRecord->GetIntSubfield( "DSPM", 0, "VDAT", 0 ));
1303         poFeature->SetField( "DSPM_SDAT",
1304                       poDSPMRecord->GetIntSubfield( "DSPM", 0, "SDAT", 0 ));
1305         poFeature->SetField( "DSPM_CSCL",
1306                       poDSPMRecord->GetIntSubfield( "DSPM", 0, "CSCL", 0 ));
1307         poFeature->SetField( "DSPM_DUNI",
1308                       poDSPMRecord->GetIntSubfield( "DSPM", 0, "DUNI", 0 ));
1309         poFeature->SetField( "DSPM_HUNI",
1310                       poDSPMRecord->GetIntSubfield( "DSPM", 0, "HUNI", 0 ));
1311         poFeature->SetField( "DSPM_PUNI",
1312                       poDSPMRecord->GetIntSubfield( "DSPM", 0, "PUNI", 0 ));
1313         poFeature->SetField( "DSPM_COUN",
1314                       poDSPMRecord->GetIntSubfield( "DSPM", 0, "COUN", 0 ));
1315         poFeature->SetField( "DSPM_COMF",
1316                       poDSPMRecord->GetIntSubfield( "DSPM", 0, "COMF", 0 ));
1317         poFeature->SetField( "DSPM_SOMF",
1318                       poDSPMRecord->GetIntSubfield( "DSPM", 0, "SOMF", 0 ));
1319         poFeature->SetField( "DSPM_COMT",
1320                       poDSPMRecord->GetStringSubfield( "DSPM", 0, "COMT", 0 ));
1321     }
1322 
1323     poFeature->SetFID( nNextDSIDIndex++ );
1324 
1325     return poFeature;
1326 }
1327 
1328 /************************************************************************/
1329 /*                             ReadVector()                             */
1330 /*                                                                      */
1331 /*      Read a vector primitive objects based on the type (RCNM_)       */
1332 /*      and index within the related index.                             */
1333 /************************************************************************/
1334 
ReadVector(int nFeatureId,int nRCNM)1335 OGRFeature *S57Reader::ReadVector( int nFeatureId, int nRCNM )
1336 
1337 {
1338     DDFRecordIndex *poIndex = nullptr;
1339     const char *pszFDName = nullptr;
1340 
1341 /* -------------------------------------------------------------------- */
1342 /*      What type of vector are we fetching.                            */
1343 /* -------------------------------------------------------------------- */
1344     switch( nRCNM )
1345     {
1346       case RCNM_VI:
1347         poIndex = &oVI_Index;
1348         pszFDName = OGRN_VI;
1349         break;
1350 
1351       case RCNM_VC:
1352         poIndex = &oVC_Index;
1353         pszFDName = OGRN_VC;
1354         break;
1355 
1356       case RCNM_VE:
1357         poIndex = &oVE_Index;
1358         pszFDName = OGRN_VE;
1359         break;
1360 
1361       case RCNM_VF:
1362         poIndex = &oVF_Index;
1363         pszFDName = OGRN_VF;
1364         break;
1365 
1366       default:
1367         CPLAssert( false );
1368         return nullptr;
1369     }
1370 
1371     if( nFeatureId < 0 || nFeatureId >= poIndex->GetCount() )
1372         return nullptr;
1373 
1374     DDFRecord *poRecord = poIndex->GetByIndex( nFeatureId );
1375 
1376 /* -------------------------------------------------------------------- */
1377 /*      Find the feature definition to use.                             */
1378 /* -------------------------------------------------------------------- */
1379     OGRFeatureDefn *poFDefn = nullptr;
1380 
1381     for( int i = 0; i < nFDefnCount; i++ )
1382     {
1383         if( EQUAL(papoFDefnList[i]->GetName(),pszFDName) )
1384         {
1385             poFDefn = papoFDefnList[i];
1386             break;
1387         }
1388     }
1389 
1390     if( poFDefn == nullptr )
1391     {
1392         // CPLAssert( false );
1393         return nullptr;
1394     }
1395 
1396 /* -------------------------------------------------------------------- */
1397 /*      Create feature, and assign standard fields.                     */
1398 /* -------------------------------------------------------------------- */
1399     OGRFeature *poFeature = new OGRFeature( poFDefn );
1400 
1401     poFeature->SetFID( nFeatureId );
1402 
1403     poFeature->SetField( "RCNM",
1404                          poRecord->GetIntSubfield( "VRID", 0, "RCNM",0) );
1405     poFeature->SetField( "RCID",
1406                          poRecord->GetIntSubfield( "VRID", 0, "RCID",0) );
1407     poFeature->SetField( "RVER",
1408                          poRecord->GetIntSubfield( "VRID", 0, "RVER",0) );
1409     poFeature->SetField( "RUIN",
1410                          poRecord->GetIntSubfield( "VRID", 0, "RUIN",0) );
1411 
1412 /* -------------------------------------------------------------------- */
1413 /*      Collect point geometries.                                       */
1414 /* -------------------------------------------------------------------- */
1415     if( nRCNM == RCNM_VI || nRCNM == RCNM_VC )
1416     {
1417         if( poRecord->FindField( "SG2D" ) != nullptr )
1418         {
1419             const double dfX = poRecord->GetIntSubfield("SG2D",0,"XCOO",0) / (double)nCOMF;
1420             const double dfY = poRecord->GetIntSubfield("SG2D",0,"YCOO",0) / (double)nCOMF;
1421             poFeature->SetGeometryDirectly( new OGRPoint( dfX, dfY ) );
1422         }
1423 
1424         else if( poRecord->FindField( "SG3D" ) != nullptr ) /* presume sounding*/
1425         {
1426             const int nVCount = poRecord->FindField("SG3D")->GetRepeatCount();
1427             if( nVCount == 1 )
1428             {
1429                 const double dfX =poRecord->GetIntSubfield("SG3D",0,"XCOO",0)/(double)nCOMF;
1430                 const double dfY =poRecord->GetIntSubfield("SG3D",0,"YCOO",0)/(double)nCOMF;
1431                 const double dfZ =poRecord->GetIntSubfield("SG3D",0,"VE3D",0)/(double)nSOMF;
1432                 poFeature->SetGeometryDirectly( new OGRPoint( dfX, dfY, dfZ ));
1433             }
1434             else
1435             {
1436                 OGRMultiPoint *poMP = new OGRMultiPoint();
1437 
1438                 for( int i = 0; i < nVCount; i++ )
1439                 {
1440                     const double dfX = poRecord->GetIntSubfield("SG3D",0,"XCOO",i)
1441                         / static_cast<double>( nCOMF );
1442                     const double dfY = poRecord->GetIntSubfield("SG3D",0,"YCOO",i)
1443                         / static_cast<double>( nCOMF );
1444                     const double dfZ = poRecord->GetIntSubfield("SG3D",0,"VE3D",i)
1445                         / static_cast<double>( nSOMF );
1446 
1447                     poMP->addGeometryDirectly( new OGRPoint( dfX, dfY, dfZ ) );
1448                 }
1449 
1450                 poFeature->SetGeometryDirectly( poMP );
1451             }
1452         }
1453     }
1454 
1455 /* -------------------------------------------------------------------- */
1456 /*      Collect an edge geometry.                                       */
1457 /* -------------------------------------------------------------------- */
1458     else if( nRCNM == RCNM_VE )
1459     {
1460         int nPoints = 0;
1461         OGRLineString *poLine = new OGRLineString();
1462 
1463         for( int iField = 0; iField < poRecord->GetFieldCount(); ++iField )
1464         {
1465             DDFField *poSG2D = poRecord->GetField( iField );
1466 
1467             if( EQUAL(poSG2D->GetFieldDefn()->GetName(), "SG2D") )
1468             {
1469                 const int nVCount = poSG2D->GetRepeatCount();
1470 
1471                 poLine->setNumPoints( nPoints + nVCount );
1472 
1473                 for( int i = 0; i < nVCount; ++i )
1474                 {
1475                     poLine->setPoint
1476                         (nPoints++,
1477                         poRecord->GetIntSubfield("SG2D",0,"XCOO",i)
1478                            / static_cast<double>( nCOMF ),
1479                         poRecord->GetIntSubfield("SG2D",0,"YCOO",i)
1480                            / static_cast<double>(nCOMF ) );
1481                 }
1482             }
1483         }
1484 
1485         poFeature->SetGeometryDirectly( poLine );
1486     }
1487 
1488 /* -------------------------------------------------------------------- */
1489 /*      Special edge fields.                                            */
1490 /*      Allow either 2 VRPT fields or one VRPT field with 2 rows        */
1491 /* -------------------------------------------------------------------- */
1492     DDFField *poVRPT = nullptr;
1493 
1494     if( nRCNM == RCNM_VE
1495         && (poVRPT = poRecord->FindField( "VRPT" )) != nullptr )
1496     {
1497         poFeature->SetField( "NAME_RCNM_0", RCNM_VC );
1498         poFeature->SetField( "NAME_RCID_0", ParseName( poVRPT ) );
1499         poFeature->SetField( "ORNT_0",
1500                              poRecord->GetIntSubfield("VRPT",0,"ORNT",0) );
1501         poFeature->SetField( "USAG_0",
1502                              poRecord->GetIntSubfield("VRPT",0,"USAG",0) );
1503         poFeature->SetField( "TOPI_0",
1504                              poRecord->GetIntSubfield("VRPT",0,"TOPI",0) );
1505         poFeature->SetField( "MASK_0",
1506                              poRecord->GetIntSubfield("VRPT",0,"MASK",0) );
1507 
1508         int iField = 0;
1509         int iSubField = 1;
1510 
1511         if( poVRPT != nullptr && poVRPT->GetRepeatCount() == 1 )
1512         {
1513             // Only one row, need a second VRPT field
1514             iField = 1;
1515             iSubField = 0;
1516 
1517             if( (poVRPT = poRecord->FindField( "VRPT", iField )) == nullptr )
1518             {
1519                 CPLError( CE_Warning, CPLE_AppDefined,
1520                           "Unable to fetch last edge node.\n"
1521                           "Feature OBJL=%s, RCID=%d may have corrupt or"
1522                           " missing geometry.",
1523                           poFeature->GetDefnRef()->GetName(),
1524                           poFeature->GetFieldAsInteger( "RCID" ) );
1525 
1526                 return poFeature;
1527             }
1528         }
1529 
1530         poFeature->SetField( "NAME_RCID_1", ParseName( poVRPT, iSubField ) );
1531         poFeature->SetField( "NAME_RCNM_1", RCNM_VC );
1532         poFeature->SetField( "ORNT_1",
1533                              poRecord->GetIntSubfield("VRPT",iField,
1534                              "ORNT",iSubField) );
1535         poFeature->SetField( "USAG_1",
1536                              poRecord->GetIntSubfield("VRPT",iField,
1537                              "USAG",iSubField) );
1538         poFeature->SetField( "TOPI_1",
1539                              poRecord->GetIntSubfield("VRPT",iField,
1540                              "TOPI",iSubField) );
1541         poFeature->SetField( "MASK_1",
1542                              poRecord->GetIntSubfield("VRPT",iField,
1543                              "MASK",iSubField) );
1544     }
1545 
1546 /* -------------------------------------------------------------------- */
1547 /*      Geometric attributes                                            */
1548 /*      Retrieve POSACC and QUAPOS attributes                           */
1549 /* -------------------------------------------------------------------- */
1550 
1551     const int posaccField = poRegistrar->FindAttrByAcronym("POSACC");
1552     const int quaposField = poRegistrar->FindAttrByAcronym("QUAPOS");
1553 
1554     DDFField * poATTV = poRecord->FindField("ATTV");
1555     if( poATTV != nullptr )
1556     {
1557         for( int j = 0; j < poATTV->GetRepeatCount(); j++ )
1558         {
1559             const int subField = poRecord->GetIntSubfield("ATTV",0,"ATTL",j);
1560             // POSACC field
1561             if (subField == posaccField) {
1562                 poFeature->SetField( "POSACC",
1563                                     poRecord->GetFloatSubfield("ATTV",0,"ATVL",j) );
1564             }
1565 
1566             // QUAPOS field
1567             if (subField == quaposField) {
1568                 poFeature->SetField( "QUAPOS",
1569                                     poRecord->GetIntSubfield("ATTV",0,"ATVL",j) );
1570             }
1571         }
1572     }
1573 
1574     return poFeature;
1575 }
1576 
1577 /************************************************************************/
1578 /*                             FetchPoint()                             */
1579 /*                                                                      */
1580 /*      Fetch the location of a spatial point object.                   */
1581 /************************************************************************/
1582 
FetchPoint(int nRCNM,int nRCID,double * pdfX,double * pdfY,double * pdfZ)1583 bool S57Reader::FetchPoint( int nRCNM, int nRCID,
1584                             double *pdfX, double *pdfY, double *pdfZ )
1585 
1586 {
1587     DDFRecord *poSRecord = nullptr;
1588 
1589     if( nRCNM == RCNM_VI )
1590         poSRecord = oVI_Index.FindRecord( nRCID );
1591     else
1592         poSRecord = oVC_Index.FindRecord( nRCID );
1593 
1594     if( poSRecord == nullptr )
1595         return false;
1596 
1597     double dfX = 0.0;
1598     double dfY = 0.0;
1599     double dfZ = 0.0;
1600 
1601     if( poSRecord->FindField( "SG2D" ) != nullptr )
1602     {
1603         dfX = poSRecord->GetIntSubfield("SG2D",0,"XCOO",0)
1604             / static_cast<double>( nCOMF );
1605         dfY = poSRecord->GetIntSubfield("SG2D",0,"YCOO",0)
1606             / static_cast<double>( nCOMF );
1607     }
1608     else if( poSRecord->FindField( "SG3D" ) != nullptr )
1609     {
1610         dfX = poSRecord->GetIntSubfield("SG3D",0,"XCOO",0)
1611             / static_cast<double>( nCOMF );
1612         dfY = poSRecord->GetIntSubfield("SG3D",0,"YCOO",0)
1613             / static_cast<double>( nCOMF );
1614         dfZ = poSRecord->GetIntSubfield("SG3D",0,"VE3D",0)
1615             / static_cast<double>( nSOMF );
1616     }
1617     else
1618         return false;
1619 
1620     if( pdfX != nullptr )
1621         *pdfX = dfX;
1622     if( pdfY != nullptr )
1623         *pdfY = dfY;
1624     if( pdfZ != nullptr )
1625         *pdfZ = dfZ;
1626 
1627     return true;
1628 }
1629 
1630 /************************************************************************/
1631 /*                  S57StrokeArcToOGRGeometry_Angles()                  */
1632 /************************************************************************/
1633 
1634 static OGRLineString *
S57StrokeArcToOGRGeometry_Angles(double dfCenterX,double dfCenterY,double dfRadius,double dfStartAngle,double dfEndAngle,int nVertexCount)1635 S57StrokeArcToOGRGeometry_Angles( double dfCenterX, double dfCenterY,
1636                                   double dfRadius,
1637                                   double dfStartAngle, double dfEndAngle,
1638                                   int nVertexCount )
1639 
1640 {
1641     OGRLineString * const poLine = new OGRLineString;
1642 
1643     nVertexCount = std::max(2, nVertexCount);
1644     const double dfSlice = (dfEndAngle-dfStartAngle)/(nVertexCount-1);
1645 
1646     poLine->setNumPoints( nVertexCount );
1647 
1648     for( int iPoint=0; iPoint < nVertexCount; iPoint++ )
1649     {
1650         const double dfAngle = (dfStartAngle + iPoint * dfSlice) * M_PI / 180.0;
1651 
1652         const double dfArcX = dfCenterX + cos(dfAngle) * dfRadius;
1653         const double dfArcY = dfCenterY + sin(dfAngle) * dfRadius;
1654 
1655         poLine->setPoint( iPoint, dfArcX, dfArcY );
1656     }
1657 
1658     return poLine;
1659 }
1660 
1661 /************************************************************************/
1662 /*                  S57StrokeArcToOGRGeometry_Points()                  */
1663 /************************************************************************/
1664 
1665 static OGRLineString *
S57StrokeArcToOGRGeometry_Points(double dfStartX,double dfStartY,double dfCenterX,double dfCenterY,double dfEndX,double dfEndY,int nVertexCount)1666 S57StrokeArcToOGRGeometry_Points( double dfStartX, double dfStartY,
1667                                   double dfCenterX, double dfCenterY,
1668                                   double dfEndX, double dfEndY,
1669                                   int nVertexCount )
1670 
1671 {
1672     double dfStartAngle = 0.0;
1673     double dfEndAngle = 360.0;
1674 
1675     if( dfStartX == dfEndX && dfStartY == dfEndY )
1676     {
1677         // dfStartAngle = 0.0;
1678         // dfEndAngle = 360.0;
1679     }
1680     else
1681     {
1682         double dfDeltaX = dfStartX - dfCenterX;
1683         double dfDeltaY = dfStartY - dfCenterY;
1684         dfStartAngle = atan2(dfDeltaY, dfDeltaX) * 180.0 / M_PI;
1685 
1686         dfDeltaX = dfEndX - dfCenterX;
1687         dfDeltaY = dfEndY - dfCenterY;
1688         dfEndAngle = atan2(dfDeltaY, dfDeltaX) * 180.0 / M_PI;
1689 
1690 #ifdef notdef
1691         if( dfStartAngle > dfAlongAngle && dfAlongAngle > dfEndAngle )
1692         {
1693             // TODO: Use std::swap.
1694             const double dfTempAngle = dfStartAngle;
1695             dfStartAngle = dfEndAngle;
1696             dfEndAngle = dfTempAngle;
1697         }
1698 #endif
1699 
1700         while( dfStartAngle < dfEndAngle )
1701             dfStartAngle += 360.0;
1702 
1703 //        while( dfAlongAngle < dfStartAngle )
1704 //            dfAlongAngle += 360.0;
1705 
1706 //        while( dfEndAngle < dfAlongAngle )
1707 //            dfEndAngle += 360.0;
1708 
1709         if( dfEndAngle - dfStartAngle > 360.0 )
1710         {
1711             // TODO: Use std::swap.
1712             const double dfTempAngle = dfStartAngle;
1713             dfStartAngle = dfEndAngle;
1714             dfEndAngle = dfTempAngle;
1715 
1716             while( dfEndAngle < dfStartAngle )
1717                 dfStartAngle -= 360.0;
1718         }
1719     }
1720 
1721     const double dfRadius =
1722         sqrt( (dfCenterX - dfStartX) * (dfCenterX - dfStartX)
1723               + (dfCenterY - dfStartY) * (dfCenterY - dfStartY) );
1724 
1725     return S57StrokeArcToOGRGeometry_Angles( dfCenterX, dfCenterY,
1726                                              dfRadius,
1727                                              dfStartAngle, dfEndAngle,
1728                                              nVertexCount );
1729 }
1730 
1731 /************************************************************************/
1732 /*                             FetchLine()                              */
1733 /************************************************************************/
1734 
FetchLine(DDFRecord * poSRecord,int iStartVertex,int iDirection,OGRLineString * poLine)1735 bool S57Reader::FetchLine( DDFRecord *poSRecord,
1736                           int iStartVertex, int iDirection,
1737                           OGRLineString *poLine )
1738 
1739 {
1740     int             nPoints = 0;
1741     DDFField        *poSG2D = nullptr;
1742     DDFField        *poAR2D = nullptr;
1743     DDFSubfieldDefn *poXCOO = nullptr;
1744     DDFSubfieldDefn *poYCOO = nullptr;
1745     bool bStandardFormat = true;
1746 
1747 /* -------------------------------------------------------------------- */
1748 /*      Points may be multiple rows in one SG2D/AR2D field or           */
1749 /*      multiple SG2D/AR2D fields (or a combination of both)            */
1750 /*      Iterate over all the SG2D/AR2D fields in the record             */
1751 /* -------------------------------------------------------------------- */
1752 
1753     for( int iField = 0; iField < poSRecord->GetFieldCount(); ++iField )
1754     {
1755         poSG2D = poSRecord->GetField( iField );
1756 
1757         if( EQUAL(poSG2D->GetFieldDefn()->GetName(), "SG2D") )
1758         {
1759             poAR2D = nullptr;
1760         }
1761         else if( EQUAL(poSG2D->GetFieldDefn()->GetName(), "AR2D") )
1762         {
1763             poAR2D = poSG2D;
1764         }
1765         else
1766         {
1767             /* Other types of fields are skipped */
1768             continue;
1769         }
1770 
1771 /* -------------------------------------------------------------------- */
1772 /*      Get some basic definitions.                                     */
1773 /* -------------------------------------------------------------------- */
1774 
1775         poXCOO = poSG2D->GetFieldDefn()->FindSubfieldDefn("XCOO");
1776         poYCOO = poSG2D->GetFieldDefn()->FindSubfieldDefn("YCOO");
1777 
1778         if( poXCOO == nullptr || poYCOO == nullptr )
1779         {
1780             CPLDebug( "S57", "XCOO or YCOO are NULL" );
1781             return false;
1782         }
1783 
1784         const int nVCount = poSG2D->GetRepeatCount();
1785 
1786 /* -------------------------------------------------------------------- */
1787 /*      It is legitimate to have zero vertices for line segments        */
1788 /*      that just have the start and end node (bug 840).                */
1789 /*                                                                      */
1790 /*      This is bogus! nVCount != 0, because poXCOO != 0 here           */
1791 /*      In case of zero vertices, there will not be any SG2D fields     */
1792 /* -------------------------------------------------------------------- */
1793         if( nVCount == 0 )
1794             continue;
1795 
1796 /* -------------------------------------------------------------------- */
1797 /*      Make sure out line is long enough to hold all the vertices      */
1798 /*      we will apply.                                                  */
1799 /* -------------------------------------------------------------------- */
1800         int nVBase = 0;
1801 
1802         if( iDirection < 0 )
1803             nVBase = iStartVertex + nPoints + nVCount;
1804         else
1805             nVBase = iStartVertex + nPoints;
1806 
1807         if( poLine->getNumPoints() < iStartVertex + nPoints + nVCount )
1808             poLine->setNumPoints( iStartVertex + nPoints + nVCount );
1809 
1810         nPoints += nVCount;
1811 /* -------------------------------------------------------------------- */
1812 /*      Are the SG2D and XCOO/YCOO definitions in the form we expect?   */
1813 /* -------------------------------------------------------------------- */
1814         bStandardFormat =
1815             (poSG2D->GetFieldDefn()->GetSubfieldCount() == 2) &&
1816             EQUAL(poXCOO->GetFormat(),"b24") &&
1817             EQUAL(poYCOO->GetFormat(),"b24");
1818 
1819 /* -------------------------------------------------------------------- */
1820 /*      Collect the vertices:                                           */
1821 /*                                                                      */
1822 /*      This approach assumes that the data is LSB organized int32      */
1823 /*      binary data as per the specification.  We avoid lots of         */
1824 /*      extra calls to low level DDF methods as they are quite          */
1825 /*      expensive.                                                      */
1826 /* -------------------------------------------------------------------- */
1827         if( bStandardFormat )
1828         {
1829             int nBytesRemaining = 0;
1830 
1831             const char *pachData =
1832                 poSG2D->GetSubfieldData( poYCOO, &nBytesRemaining, 0 );
1833 
1834             for( int i = 0; i < nVCount; i++ )
1835             {
1836                 GInt32 nYCOO = 0;
1837                 memcpy( &nYCOO, pachData, 4 );
1838                 pachData += 4;
1839 
1840                 GInt32 nXCOO = 0;
1841                 memcpy( &nXCOO, pachData, 4 );
1842                 pachData += 4;
1843 
1844 #ifdef CPL_MSB
1845                 CPL_SWAP32PTR( &nXCOO );
1846                 CPL_SWAP32PTR( &nYCOO );
1847 #endif
1848                 const double dfX = nXCOO / static_cast<double>( nCOMF );
1849                 const double dfY = nYCOO / static_cast<double>( nCOMF );
1850 
1851                 poLine->setPoint( nVBase, dfX, dfY );
1852 
1853                 nVBase += iDirection;
1854             }
1855         }
1856 
1857 /* -------------------------------------------------------------------- */
1858 /*      Collect the vertices:                                           */
1859 /*                                                                      */
1860 /*      The generic case where we use low level but expensive DDF       */
1861 /*      methods to get the data.  This should work even if some         */
1862 /*      things are changed about the SG2D fields such as making them    */
1863 /*      floating point or a different byte order.                       */
1864 /* -------------------------------------------------------------------- */
1865         else
1866         {
1867             for( int i = 0; i < nVCount; i++ )
1868             {
1869                 int nBytesRemaining = 0;
1870 
1871                 const char *pachData
1872                     = poSG2D->GetSubfieldData( poXCOO, &nBytesRemaining, i );
1873 
1874                 const double dfX
1875                     = poXCOO->ExtractIntData( pachData, nBytesRemaining, nullptr )
1876                     / static_cast<double>( nCOMF );
1877 
1878                 pachData = poSG2D->GetSubfieldData(poYCOO,&nBytesRemaining,i);
1879 
1880                 const double dfY
1881                     = poXCOO->ExtractIntData( pachData, nBytesRemaining, nullptr )
1882                     / static_cast<double>( nCOMF );
1883 
1884                 poLine->setPoint( nVBase, dfX, dfY );
1885 
1886                 nVBase += iDirection;
1887             }
1888        }
1889 
1890 /* -------------------------------------------------------------------- */
1891 /*      If this is actually an arc, turn the start, end and center      */
1892 /*      of rotation into a "stroked" arc linestring.                    */
1893 /* -------------------------------------------------------------------- */
1894         if( poAR2D != nullptr && poLine->getNumPoints() >= 3 )
1895         {
1896             int iLast = poLine->getNumPoints() - 1;
1897 
1898             OGRLineString *poArc = S57StrokeArcToOGRGeometry_Points(
1899                 poLine->getX(iLast-0), poLine->getY(iLast-0),
1900                 poLine->getX(iLast-1), poLine->getY(iLast-1),
1901                 poLine->getX(iLast-2), poLine->getY(iLast-2),
1902                 30 );
1903 
1904             if( poArc != nullptr )
1905             {
1906                 for( int i = 0; i < poArc->getNumPoints(); i++ )
1907                     poLine->setPoint( iLast-2+i, poArc->getX(i),
1908                                       poArc->getY(i) );
1909 
1910                 delete poArc;
1911             }
1912         }
1913     }
1914 
1915     return true;
1916 }
1917 
1918 /************************************************************************/
1919 /*                       AssemblePointGeometry()                        */
1920 /************************************************************************/
1921 
AssemblePointGeometry(DDFRecord * poFRecord,OGRFeature * poFeature)1922 void S57Reader::AssemblePointGeometry( DDFRecord * poFRecord,
1923                                        OGRFeature * poFeature )
1924 
1925 {
1926 /* -------------------------------------------------------------------- */
1927 /*      Feature the spatial record containing the point.                */
1928 /* -------------------------------------------------------------------- */
1929     DDFField *poFSPT = poFRecord->FindField( "FSPT" );
1930     if( poFSPT == nullptr )
1931         return;
1932 
1933     if( poFSPT->GetRepeatCount() != 1 )
1934     {
1935 #ifdef DEBUG
1936         fprintf( stderr, /*ok*/
1937                  "Point features with other than one spatial linkage.\n" );
1938         poFRecord->Dump( stderr );
1939 #endif
1940         CPLDebug( "S57",
1941            "Point feature encountered with other than one spatial linkage." );
1942     }
1943 
1944     int nRCNM = 0;
1945     const int nRCID = ParseName( poFSPT, 0, &nRCNM );
1946 
1947     double dfX = 0.0;
1948     double dfY = 0.0;
1949     double dfZ = 0.0;
1950 
1951     if( nRCID == -1 || !FetchPoint( nRCNM, nRCID, &dfX, &dfY, &dfZ ) )
1952     {
1953         CPLError( CE_Warning, CPLE_AppDefined,
1954                   "Failed to fetch %d/%d point geometry for point feature.\n"
1955                   "Feature will have empty geometry.",
1956                   nRCNM, nRCID );
1957         return;
1958     }
1959 
1960     if( dfZ == 0.0 )
1961         poFeature->SetGeometryDirectly( new OGRPoint( dfX, dfY ) );
1962     else
1963         poFeature->SetGeometryDirectly( new OGRPoint( dfX, dfY, dfZ ) );
1964 }
1965 
1966 /************************************************************************/
1967 /*                      AssembleSoundingGeometry()                      */
1968 /************************************************************************/
1969 
AssembleSoundingGeometry(DDFRecord * poFRecord,OGRFeature * poFeature)1970 void S57Reader::AssembleSoundingGeometry( DDFRecord * poFRecord,
1971                                           OGRFeature * poFeature )
1972 
1973 {
1974 /* -------------------------------------------------------------------- */
1975 /*      Feature the spatial record containing the point.                */
1976 /* -------------------------------------------------------------------- */
1977     DDFField *poFSPT = poFRecord->FindField( "FSPT" );
1978     if( poFSPT == nullptr )
1979         return;
1980 
1981     if( poFSPT->GetRepeatCount() != 1 )
1982         return;
1983 
1984     int nRCNM = 0;
1985     const int nRCID = ParseName( poFSPT, 0, &nRCNM );
1986 
1987     DDFRecord *poSRecord = nRCNM == RCNM_VI
1988         ? oVI_Index.FindRecord( nRCID )
1989         : oVC_Index.FindRecord( nRCID );
1990 
1991     if( poSRecord == nullptr )
1992         return;
1993 
1994 /* -------------------------------------------------------------------- */
1995 /*      Extract vertices.                                               */
1996 /* -------------------------------------------------------------------- */
1997     OGRMultiPoint * const poMP = new OGRMultiPoint();
1998 
1999     DDFField *poField = poSRecord->FindField( "SG2D" );
2000     if( poField == nullptr )
2001         poField = poSRecord->FindField( "SG3D" );
2002     if( poField == nullptr )
2003     {
2004         delete poMP;
2005         return;
2006     }
2007 
2008     DDFSubfieldDefn *poXCOO
2009         = poField->GetFieldDefn()->FindSubfieldDefn( "XCOO" );
2010     DDFSubfieldDefn *poYCOO
2011         = poField->GetFieldDefn()->FindSubfieldDefn( "YCOO" );
2012     if( poXCOO == nullptr || poYCOO == nullptr )
2013     {
2014         CPLDebug( "S57", "XCOO or YCOO are NULL" );
2015         delete poMP;
2016         return;
2017     }
2018     DDFSubfieldDefn * const poVE3D
2019         = poField->GetFieldDefn()->FindSubfieldDefn( "VE3D" );
2020 
2021     const int nPointCount = poField->GetRepeatCount();
2022 
2023     const char *pachData = poField->GetData();
2024     int nBytesLeft = poField->GetDataSize();
2025 
2026     for( int i = 0; i < nPointCount; i++ )
2027     {
2028         int nBytesConsumed = 0;
2029 
2030         const double dfY = poYCOO->ExtractIntData( pachData, nBytesLeft,
2031                                                    &nBytesConsumed )
2032             / static_cast<double>( nCOMF );
2033         nBytesLeft -= nBytesConsumed;
2034         pachData += nBytesConsumed;
2035 
2036         const double dfX = poXCOO->ExtractIntData( pachData, nBytesLeft,
2037                                                    &nBytesConsumed )
2038             / static_cast<double>( nCOMF );
2039         nBytesLeft -= nBytesConsumed;
2040         pachData += nBytesConsumed;
2041 
2042         double dfZ = 0.0;
2043         if( poVE3D != nullptr )
2044         {
2045             dfZ = poYCOO->ExtractIntData( pachData, nBytesLeft,
2046                                           &nBytesConsumed )
2047                 / static_cast<double>( nSOMF );
2048             nBytesLeft -= nBytesConsumed;
2049             pachData += nBytesConsumed;
2050         }
2051 
2052         poMP->addGeometryDirectly( new OGRPoint( dfX, dfY, dfZ ) );
2053     }
2054 
2055     poFeature->SetGeometryDirectly( poMP );
2056 }
2057 
2058 /************************************************************************/
2059 /*                            GetIntSubfield()                          */
2060 /************************************************************************/
2061 
2062 static int
GetIntSubfield(DDFField * poField,const char * pszSubfield,int iSubfieldIndex)2063 GetIntSubfield( DDFField *poField,
2064                 const char * pszSubfield,
2065                 int iSubfieldIndex)
2066 {
2067     DDFSubfieldDefn *poSFDefn =
2068         poField->GetFieldDefn()->FindSubfieldDefn( pszSubfield );
2069 
2070     if( poSFDefn == nullptr )
2071         return 0;
2072 
2073 /* -------------------------------------------------------------------- */
2074 /*      Get a pointer to the data.                                      */
2075 /* -------------------------------------------------------------------- */
2076     int nBytesRemaining = 0;
2077 
2078     const char *pachData = poField->GetSubfieldData( poSFDefn,
2079                                 &nBytesRemaining,
2080                                 iSubfieldIndex );
2081 
2082     return poSFDefn->ExtractIntData( pachData, nBytesRemaining, nullptr );
2083 }
2084 
2085 /************************************************************************/
2086 /*                        AssembleLineGeometry()                        */
2087 /************************************************************************/
2088 
AssembleLineGeometry(DDFRecord * poFRecord,OGRFeature * poFeature)2089 void S57Reader::AssembleLineGeometry( DDFRecord * poFRecord,
2090                                       OGRFeature * poFeature )
2091 
2092 {
2093     OGRLineString *poLine = new OGRLineString();
2094     OGRMultiLineString *poMLS = new OGRMultiLineString();
2095 
2096 /* -------------------------------------------------------------------- */
2097 /*      Loop collecting edges.                                          */
2098 /*      Iterate over the FSPT fields.                                   */
2099 /* -------------------------------------------------------------------- */
2100     const int nFieldCount = poFRecord->GetFieldCount();
2101 
2102     for( int iField = 0; iField < nFieldCount; ++iField )
2103     {
2104         double dlastfX = 0.0;
2105         double dlastfY = 0.0;
2106 
2107         DDFField *poFSPT = poFRecord->GetField( iField );
2108 
2109         if( !EQUAL(poFSPT->GetFieldDefn()->GetName(), "FSPT") )
2110             continue;
2111 
2112 /* -------------------------------------------------------------------- */
2113 /*      Loop over the rows of each FSPT field                           */
2114 /* -------------------------------------------------------------------- */
2115         const int nEdgeCount = poFSPT->GetRepeatCount();
2116 
2117         for( int iEdge = 0; iEdge < nEdgeCount; ++iEdge )
2118         {
2119             const bool bReverse
2120               = ( GetIntSubfield( poFSPT, "ORNT", iEdge ) == 2 );
2121 
2122 /* -------------------------------------------------------------------- */
2123 /*      Find the spatial record for this edge.                          */
2124 /* -------------------------------------------------------------------- */
2125             const int nRCID = ParseName( poFSPT, iEdge );
2126 
2127             DDFRecord *poSRecord = oVE_Index.FindRecord( nRCID );
2128             if( poSRecord == nullptr )
2129             {
2130                 CPLError( CE_Warning, CPLE_AppDefined,
2131                           "Couldn't find spatial record %d.\n"
2132                           "Feature OBJL=%s, RCID=%d may have corrupt or"
2133                           "missing geometry.",
2134                           nRCID,
2135                           poFeature->GetDefnRef()->GetName(),
2136                           GetIntSubfield( poFSPT, "RCID", 0 ) );
2137                 continue;
2138             }
2139 
2140 /* -------------------------------------------------------------------- */
2141 /*      Get the first and last nodes                                    */
2142 /* -------------------------------------------------------------------- */
2143             DDFField *poVRPT = poSRecord->FindField( "VRPT" );
2144             if( poVRPT == nullptr )
2145             {
2146                 CPLError( CE_Warning, CPLE_AppDefined,
2147                           "Unable to fetch start node for RCID %d.\n"
2148                           "Feature OBJL=%s, RCID=%d may have corrupt or"
2149                           "missing geometry.",
2150                           nRCID,
2151                           poFeature->GetDefnRef()->GetName(),
2152                           GetIntSubfield( poFSPT, "RCID", 0 ) );
2153                 continue;
2154             }
2155 
2156             // The "VRPT" field has only one row
2157             // Get the next row from a second "VRPT" field
2158             int nVC_RCID_firstnode = 0;
2159             int nVC_RCID_lastnode = 0;
2160 
2161             if( poVRPT->GetRepeatCount() == 1 )
2162             {
2163                 nVC_RCID_firstnode = ParseName( poVRPT );
2164                 poVRPT = poSRecord->FindField( "VRPT", 1 );
2165 
2166                 if( poVRPT == nullptr )
2167                 {
2168                     CPLError( CE_Warning, CPLE_AppDefined,
2169                               "Unable to fetch end node for RCID %d.\n"
2170                               "Feature OBJL=%s, RCID=%d may have corrupt or"
2171                               "missing geometry.",
2172                               nRCID,
2173                               poFeature->GetDefnRef()->GetName(),
2174                               GetIntSubfield( poFSPT, "RCID", 0 ) );
2175                     continue;
2176                 }
2177 
2178                 nVC_RCID_lastnode = ParseName( poVRPT );
2179 
2180                 if( bReverse )
2181                 {
2182                     // TODO: std::swap.
2183                     const int tmp = nVC_RCID_lastnode;
2184                     nVC_RCID_lastnode = nVC_RCID_firstnode;
2185                     nVC_RCID_firstnode = tmp;
2186                 }
2187             }
2188             else if( bReverse )
2189             {
2190                 nVC_RCID_lastnode = ParseName( poVRPT );
2191                 nVC_RCID_firstnode = ParseName( poVRPT, 1 );
2192             }
2193             else
2194             {
2195                 nVC_RCID_firstnode = ParseName( poVRPT );
2196                 nVC_RCID_lastnode = ParseName( poVRPT, 1 );
2197             }
2198 
2199             double dfX = 0.0;
2200             double dfY = 0.0;
2201             if( nVC_RCID_firstnode == -1 ||
2202                 ! FetchPoint( RCNM_VC, nVC_RCID_firstnode, &dfX, &dfY ) )
2203             {
2204                 CPLError( CE_Warning, CPLE_AppDefined,
2205                     "Unable to fetch start node RCID=%d.\n"
2206                     "Feature OBJL=%s, RCID=%d may have corrupt or"
2207                     " missing geometry.",
2208                     nVC_RCID_firstnode,
2209                     poFeature->GetDefnRef()->GetName(),
2210                     poFRecord->GetIntSubfield( "FRID", 0,
2211                                 "RCID", 0 ) );
2212 
2213                 continue;
2214             }
2215 
2216 /* -------------------------------------------------------------------- */
2217 /*      Does the first node match the trailing node on the existing     */
2218 /*      line string?  If so, skip it, otherwise if the existing         */
2219 /*      linestring is not empty we need to push it out and start a      */
2220 /*      new one as it means things are not connected.                   */
2221 /* -------------------------------------------------------------------- */
2222             if( poLine->getNumPoints() == 0 )
2223             {
2224                 poLine->addPoint( dfX, dfY );
2225             }
2226             else if( std::abs(dlastfX - dfX) > 0.00000001 ||
2227                 std::abs(dlastfY - dfY) > 0.00000001 )
2228             {
2229                 // we need to start a new linestring.
2230                 poMLS->addGeometryDirectly( poLine );
2231                 poLine = new OGRLineString();
2232                 poLine->addPoint( dfX, dfY );
2233             }
2234             else
2235             {
2236                 /* omit point, already present */
2237             }
2238 
2239 /* -------------------------------------------------------------------- */
2240 /*      Collect the vertices.                                           */
2241 /*      Iterate over all the SG2D fields in the Spatial record          */
2242 /* -------------------------------------------------------------------- */
2243             for( int iSField = 0;
2244                  iSField < poSRecord->GetFieldCount();
2245                  ++iSField )
2246             {
2247                 DDFField *poSG2D = poSRecord->GetField( iSField );
2248 
2249                 if( EQUAL(poSG2D->GetFieldDefn()->GetName(), "SG2D") ||
2250                     EQUAL(poSG2D->GetFieldDefn()->GetName(), "AR2D") )
2251                 {
2252                     DDFSubfieldDefn *poXCOO
2253                         = poSG2D->GetFieldDefn()->FindSubfieldDefn("XCOO");
2254                     DDFSubfieldDefn *poYCOO
2255                         = poSG2D->GetFieldDefn()->FindSubfieldDefn("YCOO");
2256 
2257                     if( poXCOO == nullptr || poYCOO == nullptr )
2258                     {
2259                         CPLDebug( "S57", "XCOO or YCOO are NULL" );
2260                         delete poLine;
2261                         delete poMLS;
2262                         return;
2263                     }
2264 
2265                     const int nVCount = poSG2D->GetRepeatCount();
2266 
2267                     int nStart = 0;
2268                     int nEnd = 0;
2269                     int nInc = 0;
2270                     if( bReverse )
2271                     {
2272                         nStart = nVCount-1;
2273                         nInc = -1;
2274                     }
2275                     else
2276                     {
2277                         nEnd = nVCount-1;
2278                         nInc = 1;
2279                     }
2280 
2281                     int nVBase = poLine->getNumPoints();
2282                     poLine->setNumPoints( nVBase + nVCount );
2283 
2284                     int nBytesRemaining = 0;
2285 
2286                     for( int i = nStart; i != nEnd+nInc; i += nInc )
2287                     {
2288                         const char *pachData
2289                           = poSG2D->GetSubfieldData(
2290                               poXCOO, &nBytesRemaining, i );
2291 
2292                         dfX = poXCOO->ExtractIntData(
2293                             pachData, nBytesRemaining, nullptr )
2294                             / static_cast<double>( nCOMF );
2295 
2296                         pachData = poSG2D->GetSubfieldData(
2297                             poYCOO, &nBytesRemaining, i );
2298 
2299                         dfY = poXCOO->ExtractIntData(
2300                             pachData, nBytesRemaining, nullptr )
2301                             / static_cast<double>( nCOMF );
2302 
2303                         poLine->setPoint( nVBase++, dfX, dfY );
2304                     }
2305                 }
2306             }
2307 
2308             // remember the coordinates of the last point
2309             dlastfX = dfX;
2310             dlastfY = dfY;
2311 
2312 /* -------------------------------------------------------------------- */
2313 /*      Add the end node.                                               */
2314 /* -------------------------------------------------------------------- */
2315             if( nVC_RCID_lastnode != -1 &&
2316                 FetchPoint( RCNM_VC, nVC_RCID_lastnode, &dfX, &dfY ) )
2317             {
2318                 poLine->addPoint( dfX, dfY );
2319                 dlastfX = dfX;
2320                 dlastfY = dfY;
2321             }
2322             else
2323             {
2324                 CPLError( CE_Warning, CPLE_AppDefined,
2325                           "Unable to fetch end node RCID=%d.\n"
2326                           "Feature OBJL=%s, RCID=%d may have corrupt or"
2327                           " missing geometry.",
2328                           nVC_RCID_lastnode,
2329                           poFeature->GetDefnRef()->GetName(),
2330                           poFRecord->GetIntSubfield( "FRID", 0, "RCID", 0 ) );
2331                 continue;
2332             }
2333         }
2334     }
2335 
2336 /* -------------------------------------------------------------------- */
2337 /*      Set either the line or multilinestring as the geometry.  We     */
2338 /*      are careful to just produce a linestring if there are no        */
2339 /*      disconnections.                                                 */
2340 /* -------------------------------------------------------------------- */
2341     if( poMLS->getNumGeometries() > 0 )
2342     {
2343         poMLS->addGeometryDirectly( poLine );
2344         poFeature->SetGeometryDirectly( poMLS );
2345     }
2346     else if( poLine->getNumPoints() >= 2 )
2347     {
2348         poFeature->SetGeometryDirectly( poLine );
2349         delete poMLS;
2350     }
2351     else
2352     {
2353         delete poLine;
2354         delete poMLS;
2355     }
2356 }
2357 
2358 /************************************************************************/
2359 /*                        AssembleAreaGeometry()                        */
2360 /************************************************************************/
2361 
AssembleAreaGeometry(DDFRecord * poFRecord,OGRFeature * poFeature)2362 void S57Reader::AssembleAreaGeometry( DDFRecord * poFRecord,
2363                                       OGRFeature * poFeature )
2364 
2365 {
2366     OGRGeometryCollection * const poLines = new OGRGeometryCollection();
2367 
2368 /* -------------------------------------------------------------------- */
2369 /*      Find the FSPT fields.                                           */
2370 /* -------------------------------------------------------------------- */
2371     const int nFieldCount = poFRecord->GetFieldCount();
2372 
2373     for( int iFSPT = 0; iFSPT < nFieldCount; ++iFSPT )
2374     {
2375         DDFField *poFSPT = poFRecord->GetField(iFSPT);
2376 
2377         if ( !EQUAL(poFSPT->GetFieldDefn()->GetName(), "FSPT") )
2378             continue;
2379 
2380         const int nEdgeCount = poFSPT->GetRepeatCount();
2381 
2382 /* ==================================================================== */
2383 /*      Loop collecting edges.                                          */
2384 /* ==================================================================== */
2385         for( int iEdge = 0; iEdge < nEdgeCount; iEdge++ )
2386         {
2387 /* -------------------------------------------------------------------- */
2388 /*      Find the spatial record for this edge.                          */
2389 /* -------------------------------------------------------------------- */
2390             const int nRCID = ParseName( poFSPT, iEdge );
2391 
2392             DDFRecord *poSRecord = oVE_Index.FindRecord( nRCID );
2393             if( poSRecord == nullptr )
2394             {
2395                 CPLError( CE_Warning, CPLE_AppDefined,
2396                           "Couldn't find spatial record %d.\n"
2397                           "Feature OBJL=%s, RCID=%d may have corrupt or"
2398                           "missing geometry.",
2399                           nRCID,
2400                           poFeature->GetDefnRef()->GetName(),
2401                           GetIntSubfield( poFSPT, "RCID", 0 ) );
2402                 continue;
2403             }
2404 
2405 /* -------------------------------------------------------------------- */
2406 /*      Create the line string.                                         */
2407 /* -------------------------------------------------------------------- */
2408             OGRLineString *poLine = new OGRLineString();
2409 
2410 /* -------------------------------------------------------------------- */
2411 /*      Add the start node.                                             */
2412 /* -------------------------------------------------------------------- */
2413             DDFField *poVRPT = poSRecord->FindField( "VRPT" );
2414             if( poVRPT != nullptr )
2415             {
2416                 int nVC_RCID = ParseName( poVRPT );
2417                 double dfX = 0.0;
2418                 double dfY = 0.0;
2419 
2420                 if( nVC_RCID != -1
2421                     && FetchPoint( RCNM_VC, nVC_RCID, &dfX, &dfY ) )
2422                     poLine->addPoint( dfX, dfY );
2423             }
2424 
2425 /* -------------------------------------------------------------------- */
2426 /*      Collect the vertices.                                           */
2427 /* -------------------------------------------------------------------- */
2428             if( !FetchLine( poSRecord, poLine->getNumPoints(), 1, poLine ) )
2429             {
2430                 CPLDebug( "S57",
2431                           "FetchLine() failed in AssembleAreaGeometry()!" );
2432             }
2433 
2434 /* -------------------------------------------------------------------- */
2435 /*      Add the end node.                                               */
2436 /* -------------------------------------------------------------------- */
2437             if( poVRPT != nullptr && poVRPT->GetRepeatCount() > 1 )
2438             {
2439                 const int nVC_RCID = ParseName( poVRPT, 1 );
2440                 double dfX = 0.0;
2441                 double dfY = 0.0;
2442 
2443                 if( nVC_RCID != -1
2444                     && FetchPoint( RCNM_VC, nVC_RCID, &dfX, &dfY ) )
2445                     poLine->addPoint( dfX, dfY );
2446             }
2447             else if( (poVRPT = poSRecord->FindField( "VRPT", 1 )) != nullptr )
2448             {
2449                 const int nVC_RCID = ParseName( poVRPT );
2450                 double dfX = 0.0;
2451                 double dfY = 0.0;
2452 
2453                 if( nVC_RCID != -1
2454                     && FetchPoint( RCNM_VC, nVC_RCID, &dfX, &dfY ) )
2455                     poLine->addPoint( dfX, dfY );
2456             }
2457 
2458             poLines->addGeometryDirectly( poLine );
2459         }
2460     }
2461 
2462 /* -------------------------------------------------------------------- */
2463 /*      Build lines into a polygon.                                     */
2464 /* -------------------------------------------------------------------- */
2465     OGRErr eErr;
2466 
2467     OGRGeometry  *poPolygon = reinterpret_cast<OGRGeometry *>(
2468         OGRBuildPolygonFromEdges( reinterpret_cast<OGRGeometryH>( poLines ),
2469                                   TRUE, FALSE, 0.0, &eErr ) );
2470     if( eErr != OGRERR_NONE )
2471     {
2472         CPLError( CE_Warning, CPLE_AppDefined,
2473                   "Polygon assembly has failed for feature FIDN=%d,FIDS=%d.\n"
2474                   "Geometry may be missing or incomplete.",
2475                   poFeature->GetFieldAsInteger( "FIDN" ),
2476                   poFeature->GetFieldAsInteger( "FIDS" ) );
2477     }
2478 
2479     delete poLines;
2480 
2481     if( poPolygon != nullptr )
2482         poFeature->SetGeometryDirectly( poPolygon );
2483 }
2484 
2485 /************************************************************************/
2486 /*                             FindFDefn()                              */
2487 /*                                                                      */
2488 /*      Find the OGRFeatureDefn corresponding to the passed feature     */
2489 /*      record.  It will search based on geometry class, or object      */
2490 /*      class depending on the bClassBased setting.                     */
2491 /************************************************************************/
2492 
FindFDefn(DDFRecord * poRecord)2493 OGRFeatureDefn * S57Reader::FindFDefn( DDFRecord * poRecord )
2494 
2495 {
2496     if( poRegistrar != nullptr )
2497     {
2498         const int nOBJL = poRecord->GetIntSubfield( "FRID", 0, "OBJL", 0 );
2499 
2500         if( nOBJL < static_cast<int>( apoFDefnByOBJL.size() )
2501             && apoFDefnByOBJL[nOBJL] != nullptr )
2502             return apoFDefnByOBJL[nOBJL];
2503 
2504         if( !poClassContentExplorer->SelectClass( nOBJL ) )
2505         {
2506             for( int i = 0; i < nFDefnCount; i++ )
2507             {
2508                 if( EQUAL(papoFDefnList[i]->GetName(),"Generic") )
2509                     return papoFDefnList[i];
2510             }
2511             return nullptr;
2512         }
2513 
2514         for( int i = 0; i < nFDefnCount; i++ )
2515         {
2516             const char* pszAcronym = poClassContentExplorer->GetAcronym();
2517             if( pszAcronym != nullptr &&
2518                 EQUAL(papoFDefnList[i]->GetName(),
2519                       pszAcronym) )
2520                 return papoFDefnList[i];
2521         }
2522 
2523         return nullptr;
2524     }
2525     else
2526     {
2527         const int nPRIM = poRecord->GetIntSubfield( "FRID", 0, "PRIM", 0 );
2528         OGRwkbGeometryType eGType;
2529 
2530         if( nPRIM == PRIM_P )
2531             eGType = wkbPoint;
2532         else if( nPRIM == PRIM_L )
2533             eGType = wkbLineString;
2534         else if( nPRIM == PRIM_A )
2535             eGType = wkbPolygon;
2536         else
2537             eGType = wkbNone;
2538 
2539         for( int i = 0; i < nFDefnCount; i++ )
2540         {
2541             if( papoFDefnList[i]->GetGeomType() == eGType )
2542                 return papoFDefnList[i];
2543         }
2544     }
2545 
2546     return nullptr;
2547 }
2548 
2549 /************************************************************************/
2550 /*                             ParseName()                              */
2551 /*                                                                      */
2552 /*      Pull the RCNM and RCID values from a NAME field.  The RCID      */
2553 /*      is returned and the RCNM can be gotten via the pnRCNM argument. */
2554 /*      Note: nIndex is the index of the requested 'NAME' instance      */
2555 /************************************************************************/
2556 
ParseName(DDFField * poField,int nIndex,int * pnRCNM)2557 int S57Reader::ParseName( DDFField * poField, int nIndex, int * pnRCNM )
2558 
2559 {
2560     if( poField == nullptr )
2561     {
2562         CPLError( CE_Failure, CPLE_AppDefined,
2563                   "Missing field in ParseName()." );
2564         return -1;
2565     }
2566 
2567     DDFSubfieldDefn* poName
2568         = poField->GetFieldDefn()->FindSubfieldDefn( "NAME" );
2569     if( poName == nullptr )
2570         return -1;
2571 
2572     int nMaxBytes = 0;
2573     unsigned char *pabyData = reinterpret_cast<unsigned char *>(
2574         const_cast<char *>(
2575             poField->GetSubfieldData( poName, &nMaxBytes, nIndex ) ) );
2576     if( pabyData == nullptr || nMaxBytes < 5 )
2577         return -1;
2578 
2579     if( pnRCNM != nullptr )
2580         *pnRCNM = pabyData[0];
2581 
2582     return CPL_LSBSINT32PTR(pabyData + 1);
2583 }
2584 
2585 /************************************************************************/
2586 /*                           AddFeatureDefn()                           */
2587 /************************************************************************/
2588 
AddFeatureDefn(OGRFeatureDefn * poFDefn)2589 void S57Reader::AddFeatureDefn( OGRFeatureDefn * poFDefn )
2590 
2591 {
2592     nFDefnCount++;
2593     papoFDefnList = static_cast<OGRFeatureDefn **>(
2594         CPLRealloc(papoFDefnList, sizeof(OGRFeatureDefn*)*nFDefnCount ) );
2595 
2596     papoFDefnList[nFDefnCount-1] = poFDefn;
2597 
2598     if( poRegistrar != nullptr )
2599     {
2600         if( poClassContentExplorer->SelectClass( poFDefn->GetName() ) )
2601         {
2602             const int nOBJL = poClassContentExplorer->GetOBJL();
2603             if( nOBJL >= 0 )
2604             {
2605                 if( nOBJL >= (int) apoFDefnByOBJL.size() )
2606                     apoFDefnByOBJL.resize(nOBJL+1);
2607                 apoFDefnByOBJL[nOBJL] = poFDefn;
2608             }
2609         }
2610     }
2611 }
2612 
2613 /************************************************************************/
2614 /*                          CollectClassList()                          */
2615 /*                                                                      */
2616 /*      Establish the list of classes (unique OBJL values) that         */
2617 /*      occur in this dataset.                                          */
2618 /************************************************************************/
2619 
CollectClassList(std::vector<int> & anClassCount)2620 bool S57Reader::CollectClassList(std::vector<int> &anClassCount)
2621 
2622 {
2623     if( !bFileIngested && !Ingest() )
2624         return false;
2625 
2626     bool bSuccess = true;
2627 
2628     for( int iFEIndex = 0; iFEIndex < oFE_Index.GetCount(); iFEIndex++ )
2629     {
2630         DDFRecord *poRecord = oFE_Index.GetByIndex( iFEIndex );
2631         const int nOBJL = poRecord->GetIntSubfield( "FRID", 0, "OBJL", 0 );
2632 
2633         if( nOBJL < 0 )
2634             bSuccess = false;
2635         else
2636         {
2637             if( nOBJL >= (int) anClassCount.size() )
2638                 anClassCount.resize(nOBJL+1);
2639             anClassCount[nOBJL]++;
2640         }
2641     }
2642 
2643     return bSuccess;
2644 }
2645 
2646 /************************************************************************/
2647 /*                         ApplyRecordUpdate()                          */
2648 /*                                                                      */
2649 /*      Update one target record based on an S-57 update record         */
2650 /*      (RUIN=3).                                                       */
2651 /************************************************************************/
2652 
ApplyRecordUpdate(DDFRecord * poTarget,DDFRecord * poUpdate)2653 bool S57Reader::ApplyRecordUpdate( DDFRecord *poTarget, DDFRecord *poUpdate )
2654 
2655 {
2656     const char *pszKey = poUpdate->GetField(1)->GetFieldDefn()->GetName();
2657 
2658 /* -------------------------------------------------------------------- */
2659 /*      Validate versioning.                                            */
2660 /* -------------------------------------------------------------------- */
2661     if( poTarget->GetIntSubfield( pszKey, 0, "RVER", 0 ) + 1
2662         != poUpdate->GetIntSubfield( pszKey, 0, "RVER", 0 )  )
2663     {
2664         CPLDebug( "S57",
2665                   "Mismatched RVER value on RCNM=%d,RCID=%d.\n",
2666                   poTarget->GetIntSubfield( pszKey, 0, "RCNM", 0 ),
2667                   poTarget->GetIntSubfield( pszKey, 0, "RCID", 0 ) );
2668 
2669         // CPLAssert( false );
2670         return false;
2671     }
2672 
2673 /* -------------------------------------------------------------------- */
2674 /*      Update the target version.                                      */
2675 /* -------------------------------------------------------------------- */
2676     DDFField *poKey = poTarget->FindField( pszKey );
2677 
2678     if( poKey == nullptr )
2679     {
2680         // CPLAssert( false );
2681         return false;
2682     }
2683 
2684     DDFSubfieldDefn *poRVER_SFD
2685         = poKey->GetFieldDefn()->FindSubfieldDefn( "RVER" );
2686     if( poRVER_SFD == nullptr )
2687         return false;
2688 
2689     unsigned char *pnRVER
2690         = (unsigned char *) poKey->GetSubfieldData( poRVER_SFD, nullptr, 0 );
2691 
2692     *pnRVER += 1;
2693 
2694 /* -------------------------------------------------------------------- */
2695 /*      Check for, and apply record record to spatial record pointer    */
2696 /*      updates.                                                        */
2697 /* -------------------------------------------------------------------- */
2698     if( poUpdate->FindField( "FSPC" ) != nullptr )
2699     {
2700         const int nFSUI = poUpdate->GetIntSubfield( "FSPC", 0, "FSUI", 0 );
2701         DDFField *poSrcFSPT = poUpdate->FindField( "FSPT" );
2702         DDFField *poDstFSPT = poTarget->FindField( "FSPT" );
2703 
2704         if( (poSrcFSPT == nullptr && nFSUI != 2) || poDstFSPT == nullptr )
2705         {
2706             // CPLAssert( false );
2707             return false;
2708         }
2709 
2710         const int nFSIX = poUpdate->GetIntSubfield( "FSPC", 0, "FSIX", 0 );
2711         const int nNSPT = poUpdate->GetIntSubfield( "FSPC", 0, "NSPT", 0 );
2712 
2713         int nPtrSize = poDstFSPT->GetFieldDefn()->GetFixedWidth();
2714 
2715         if( nFSUI == 1 ) /* INSERT */
2716         {
2717             int nInsertionBytes = nPtrSize * nNSPT;
2718 
2719             if( poSrcFSPT->GetDataSize() < nInsertionBytes )
2720             {
2721                 CPLDebug( "S57", "Not enough bytes in source FSPT field. "
2722                           "Has %d, requires %d",
2723                           poSrcFSPT->GetDataSize(), nInsertionBytes );
2724                 return false;
2725             }
2726 
2727             char *pachInsertion
2728                 = static_cast<char *>( CPLMalloc(nInsertionBytes + nPtrSize) );
2729             memcpy( pachInsertion, poSrcFSPT->GetData(), nInsertionBytes );
2730 
2731             /*
2732             ** If we are inserting before an instance that already
2733             ** exists, we must add it to the end of the data being
2734             ** inserted.
2735             */
2736             if( nFSIX <= poDstFSPT->GetRepeatCount() )
2737             {
2738                 if( poDstFSPT->GetDataSize() < nPtrSize * nFSIX )
2739                 {
2740                     CPLDebug( "S57", "Not enough bytes in dest FSPT field. "
2741                               "Has %d, requires %d",
2742                               poDstFSPT->GetDataSize(), nPtrSize * nFSIX );
2743                     CPLFree( pachInsertion );
2744                     return false;
2745                 }
2746 
2747                 memcpy( pachInsertion + nInsertionBytes,
2748                         poDstFSPT->GetData() + nPtrSize * (nFSIX-1),
2749                         nPtrSize );
2750                 nInsertionBytes += nPtrSize;
2751             }
2752 
2753             poTarget->SetFieldRaw( poDstFSPT, nFSIX - 1,
2754                                    pachInsertion, nInsertionBytes );
2755             CPLFree( pachInsertion );
2756         }
2757         else if( nFSUI == 2 ) /* DELETE */
2758         {
2759             /* Wipe each deleted coordinate */
2760             for( int i = nNSPT-1; i >= 0; i-- )
2761             {
2762                 poTarget->SetFieldRaw( poDstFSPT, i + nFSIX - 1, nullptr, 0 );
2763             }
2764         }
2765         else if( nFSUI == 3 ) /* MODIFY */
2766         {
2767             /* copy over each ptr */
2768             if( poSrcFSPT->GetDataSize() < nNSPT * nPtrSize )
2769             {
2770                 CPLDebug("S57", "Not enough bytes in source FSPT field. Has %d, requires %d",
2771                          poSrcFSPT->GetDataSize(), nNSPT * nPtrSize );
2772                 return false;
2773             }
2774 
2775             for( int i = 0; i < nNSPT; i++ )
2776             {
2777                 const char *pachRawData = poSrcFSPT->GetData() + nPtrSize * i;
2778                 poTarget->SetFieldRaw( poDstFSPT, i + nFSIX - 1,
2779                                        pachRawData, nPtrSize );
2780             }
2781         }
2782     }
2783 
2784 /* -------------------------------------------------------------------- */
2785 /*      Check for, and apply vector record to vector record pointer     */
2786 /*      updates.                                                        */
2787 /* -------------------------------------------------------------------- */
2788     if( poUpdate->FindField( "VRPC" ) != nullptr )
2789     {
2790         const int nVPUI = poUpdate->GetIntSubfield( "VRPC", 0, "VPUI", 0 );
2791         DDFField *poSrcVRPT = poUpdate->FindField( "VRPT" );
2792         DDFField *poDstVRPT = poTarget->FindField( "VRPT" );
2793 
2794         if( (poSrcVRPT == nullptr && nVPUI != 2) || poDstVRPT == nullptr )
2795         {
2796             // CPLAssert( false );
2797             return false;
2798         }
2799 
2800         const int nVPIX = poUpdate->GetIntSubfield( "VRPC", 0, "VPIX", 0 );
2801         const int nNVPT = poUpdate->GetIntSubfield( "VRPC", 0, "NVPT", 0 );
2802 
2803         const int nPtrSize = poDstVRPT->GetFieldDefn()->GetFixedWidth();
2804 
2805         if( nVPUI == 1 ) /* INSERT */
2806         {
2807             int nInsertionBytes = nPtrSize * nNVPT;
2808 
2809             if( poSrcVRPT->GetDataSize() < nInsertionBytes )
2810             {
2811                 CPLDebug("S57", "Not enough bytes in source VRPT field. Has %d, requires %d",
2812                          poSrcVRPT->GetDataSize(), nInsertionBytes );
2813                 return false;
2814             }
2815 
2816             char *pachInsertion
2817                 = static_cast<char *>( CPLMalloc(nInsertionBytes + nPtrSize) );
2818             memcpy( pachInsertion, poSrcVRPT->GetData(), nInsertionBytes );
2819 
2820             /*
2821             ** If we are inserting before an instance that already
2822             ** exists, we must add it to the end of the data being
2823             ** inserted.
2824             */
2825             if( nVPIX <= poDstVRPT->GetRepeatCount() )
2826             {
2827                 if( poDstVRPT->GetDataSize() < nPtrSize * nVPIX )
2828                 {
2829                     CPLDebug("S57", "Not enough bytes in dest VRPT field. Has %d, requires %d",
2830                          poDstVRPT->GetDataSize(), nPtrSize * nVPIX );
2831                     CPLFree( pachInsertion );
2832                     return false;
2833                 }
2834 
2835                 memcpy( pachInsertion + nInsertionBytes,
2836                         poDstVRPT->GetData() + nPtrSize * (nVPIX-1),
2837                         nPtrSize );
2838                 nInsertionBytes += nPtrSize;
2839             }
2840 
2841             poTarget->SetFieldRaw( poDstVRPT, nVPIX - 1,
2842                                    pachInsertion, nInsertionBytes );
2843             CPLFree( pachInsertion );
2844         }
2845         else if( nVPUI == 2 ) /* DELETE */
2846         {
2847             /* Wipe each deleted coordinate */
2848             for( int i = nNVPT-1; i >= 0; i-- )
2849             {
2850                 poTarget->SetFieldRaw( poDstVRPT, i + nVPIX - 1, nullptr, 0 );
2851             }
2852         }
2853         else if( nVPUI == 3 ) /* MODIFY */
2854         {
2855             if( poSrcVRPT->GetDataSize() < nNVPT * nPtrSize )
2856             {
2857                 CPLDebug( "S57", "Not enough bytes in source VRPT field. "
2858                           "Has %d, requires %d",
2859                           poSrcVRPT->GetDataSize(), nNVPT * nPtrSize );
2860                 return false;
2861             }
2862 
2863             /* copy over each ptr */
2864             for( int i = 0; i < nNVPT; i++ )
2865             {
2866                 const char *pachRawData = poSrcVRPT->GetData() + nPtrSize * i;
2867 
2868                 poTarget->SetFieldRaw( poDstVRPT, i + nVPIX - 1,
2869                                        pachRawData, nPtrSize );
2870             }
2871         }
2872     }
2873 
2874 /* -------------------------------------------------------------------- */
2875 /*      Check for, and apply record update to coordinates.              */
2876 /* -------------------------------------------------------------------- */
2877     if( poUpdate->FindField( "SGCC" ) != nullptr )
2878     {
2879         DDFField *poSrcSG2D = poUpdate->FindField( "SG2D" );
2880         DDFField *poDstSG2D = poTarget->FindField( "SG2D" );
2881 
2882         const int nCCUI = poUpdate->GetIntSubfield( "SGCC", 0, "CCUI", 0 );
2883 
2884         /* If we don't have SG2D, check for SG3D */
2885         if( poDstSG2D == nullptr )
2886         {
2887             poDstSG2D = poTarget->FindField( "SG3D" );
2888             if (poDstSG2D != nullptr)
2889             {
2890                 poSrcSG2D = poUpdate->FindField("SG3D");
2891             }
2892             else
2893             {
2894                 if ( nCCUI != 1 )
2895                 {
2896                     // CPLAssert( false );
2897                     return false;
2898                 }
2899 
2900                 poTarget->AddField(poTarget->GetModule()->FindFieldDefn("SG2D"));
2901                 poDstSG2D = poTarget->FindField("SG2D");
2902                 if (poDstSG2D == nullptr) {
2903                     // CPLAssert( false );
2904                     return false;
2905                 }
2906 
2907                 // Delete null default data that was created
2908                 poTarget->SetFieldRaw( poDstSG2D, 0, nullptr, 0 );
2909             }
2910         }
2911 
2912         if( poSrcSG2D == nullptr && nCCUI != 2 )
2913         {
2914             // CPLAssert( false );
2915             return false;
2916         }
2917 
2918         int nCoordSize = poDstSG2D->GetFieldDefn()->GetFixedWidth();
2919         const int nCCIX = poUpdate->GetIntSubfield( "SGCC", 0, "CCIX", 0 );
2920         const int nCCNC = poUpdate->GetIntSubfield( "SGCC", 0, "CCNC", 0 );
2921 
2922         if( nCCUI == 1 ) /* INSERT */
2923         {
2924             int nInsertionBytes = nCoordSize * nCCNC;
2925 
2926             if( poSrcSG2D->GetDataSize() < nInsertionBytes )
2927             {
2928                 CPLDebug( "S57", "Not enough bytes in source SG2D field. "
2929                           "Has %d, requires %d",
2930                           poSrcSG2D->GetDataSize(), nInsertionBytes );
2931                 return false;
2932             }
2933 
2934             char *pachInsertion
2935                 = static_cast<char *>(
2936                     CPLMalloc(nInsertionBytes + nCoordSize) );
2937             memcpy( pachInsertion, poSrcSG2D->GetData(), nInsertionBytes );
2938 
2939             /*
2940             ** If we are inserting before an instance that already
2941             ** exists, we must add it to the end of the data being
2942             ** inserted.
2943             */
2944             if( nCCIX <= poDstSG2D->GetRepeatCount() )
2945             {
2946                 if( poDstSG2D->GetDataSize() < nCoordSize * nCCIX )
2947                 {
2948                     CPLDebug( "S57", "Not enough bytes in dest SG2D field. "
2949                               "Has %d, requires %d",
2950                               poDstSG2D->GetDataSize(), nCoordSize * nCCIX );
2951                     CPLFree( pachInsertion );
2952                     return false;
2953                 }
2954 
2955                 memcpy( pachInsertion + nInsertionBytes,
2956                         poDstSG2D->GetData() + nCoordSize * (nCCIX-1),
2957                         nCoordSize );
2958                 nInsertionBytes += nCoordSize;
2959             }
2960 
2961             poTarget->SetFieldRaw( poDstSG2D, nCCIX - 1,
2962                                    pachInsertion, nInsertionBytes );
2963             CPLFree( pachInsertion );
2964         }
2965         else if( nCCUI == 2 ) /* DELETE */
2966         {
2967             /* Wipe each deleted coordinate */
2968             for( int i = nCCNC-1; i >= 0; i-- )
2969             {
2970                 poTarget->SetFieldRaw( poDstSG2D, i + nCCIX - 1, nullptr, 0 );
2971             }
2972         }
2973         else if( nCCUI == 3 ) /* MODIFY */
2974         {
2975             if( poSrcSG2D->GetDataSize() < nCCNC * nCoordSize )
2976             {
2977                 CPLDebug( "S57", "Not enough bytes in source SG2D field. "
2978                           "Has %d, requires %d",
2979                           poSrcSG2D->GetDataSize(), nCCNC * nCoordSize );
2980                 return false;
2981             }
2982 
2983             /* copy over each ptr */
2984             for( int i = 0; i < nCCNC; i++ )
2985             {
2986                 const char *pachRawData = poSrcSG2D->GetData() + nCoordSize * i;
2987 
2988                 poTarget->SetFieldRaw( poDstSG2D, i + nCCIX - 1,
2989                                        pachRawData, nCoordSize );
2990             }
2991         }
2992     }
2993 
2994 /* -------------------------------------------------------------------- */
2995 /*      Apply updates to Feature to Feature pointer fields.  Note       */
2996 /*      INSERT and DELETE are untested.  UPDATE tested per bug #5028.   */
2997 /* -------------------------------------------------------------------- */
2998     if( poUpdate->FindField( "FFPC" ) != nullptr )
2999     {
3000         int     nFFUI = poUpdate->GetIntSubfield( "FFPC", 0, "FFUI", 0 );
3001         DDFField *poSrcFFPT = poUpdate->FindField( "FFPT" );
3002         DDFField *poDstFFPT = poTarget->FindField( "FFPT" );
3003 
3004         if( (poSrcFFPT == nullptr && nFFUI != 2)
3005             || (poDstFFPT == nullptr && nFFUI != 1) )
3006         {
3007             CPLDebug( "S57", "Missing source or target FFPT applying update.");
3008             // CPLAssert( false );
3009             return false;
3010         }
3011 
3012         // Create FFPT field on target record, if it does not yet exist.
3013         if (poDstFFPT == nullptr)
3014         {
3015             // Untested!
3016             poTarget->AddField(poTarget->GetModule()->FindFieldDefn("FFPT"));
3017             poDstFFPT = poTarget->FindField("FFPT");
3018             if (poDstFFPT == nullptr) {
3019                 // CPLAssert( false );
3020                 return false;
3021             }
3022 
3023             // Delete null default data that was created
3024             poTarget->SetFieldRaw( poDstFFPT, 0, nullptr, 0 );
3025         }
3026 
3027         // FFPT includes COMT which is variable length which would
3028         // greatly complicate updates.  But in practice COMT is always
3029         // an empty string so we will take a chance and assume that so
3030         // we have a fixed record length.  We *could* actually verify that
3031         // but I have not done so for now.
3032         const int nFFPTSize = 10;
3033         const int nFFIX = poUpdate->GetIntSubfield( "FFPC", 0, "FFIX", 0 );
3034         const int nNFPT = poUpdate->GetIntSubfield( "FFPC", 0, "NFPT", 0 );
3035 
3036         if (nFFUI == 1 ) /* INSERT */
3037         {
3038             // Untested!
3039             CPLDebug( "S57", "Using untested FFPT INSERT code!");
3040 
3041             int nInsertionBytes = nFFPTSize * nNFPT;
3042 
3043             if( poSrcFFPT->GetDataSize() < nInsertionBytes )
3044             {
3045                 CPLDebug( "S57", "Not enough bytes in source FFPT field. "
3046                           "Has %d, requires %d",
3047                           poSrcFFPT->GetDataSize(), nInsertionBytes );
3048                 return false;
3049             }
3050 
3051             char *pachInsertion
3052                 = static_cast<char *>( CPLMalloc(nInsertionBytes + nFFPTSize) );
3053             memcpy( pachInsertion, poSrcFFPT->GetData(), nInsertionBytes );
3054 
3055             /*
3056             ** If we are inserting before an instance that already
3057             ** exists, we must add it to the end of the data being
3058             ** inserted.
3059             */
3060             if( nFFIX <= poDstFFPT->GetRepeatCount() )
3061             {
3062                 if( poDstFFPT->GetDataSize() < nFFPTSize * nFFIX )
3063                 {
3064                     CPLDebug( "S57", "Not enough bytes in dest FFPT field. "
3065                               "Has %d, requires %d",
3066                               poDstFFPT->GetDataSize(), nFFPTSize * nFFIX );
3067                     CPLFree( pachInsertion );
3068                     return false;
3069                 }
3070 
3071                 memcpy( pachInsertion + nInsertionBytes,
3072                         poDstFFPT->GetData() + nFFPTSize * (nFFIX-1),
3073                         nFFPTSize );
3074                 nInsertionBytes += nFFPTSize;
3075             }
3076 
3077             poTarget->SetFieldRaw( poDstFFPT, nFFIX - 1,
3078                                    pachInsertion, nInsertionBytes );
3079             CPLFree( pachInsertion );
3080         }
3081         else if( nFFUI == 2 ) /* DELETE */
3082         {
3083             // Untested!
3084             CPLDebug( "S57", "Using untested FFPT DELETE code!");
3085 
3086             /* Wipe each deleted record */
3087             for( int i = nNFPT-1; i >= 0; i-- )
3088             {
3089                 poTarget->SetFieldRaw( poDstFFPT, i + nFFIX - 1, nullptr, 0 );
3090             }
3091         }
3092         else if( nFFUI == 3 ) /* UPDATE */
3093         {
3094             if( poSrcFFPT->GetDataSize() < nNFPT * nFFPTSize )
3095             {
3096                 CPLDebug( "S57", "Not enough bytes in source FFPT field. "
3097                           "Has %d, requires %d",
3098                           poSrcFFPT->GetDataSize(), nNFPT * nFFPTSize );
3099                 return false;
3100             }
3101 
3102             /* copy over each ptr */
3103             for( int i = 0; i < nNFPT; i++ )
3104             {
3105                 const char *pachRawData = poSrcFFPT->GetData() + nFFPTSize * i;
3106 
3107                 poTarget->SetFieldRaw( poDstFFPT, i + nFFIX - 1,
3108                                        pachRawData, nFFPTSize );
3109             }
3110         }
3111     }
3112 
3113 /* -------------------------------------------------------------------- */
3114 /*      Check for and apply changes to attribute lists.                 */
3115 /* -------------------------------------------------------------------- */
3116     if( poUpdate->FindField( "ATTF" ) != nullptr )
3117     {
3118         DDFField *poDstATTF = poTarget->FindField( "ATTF" );
3119 
3120         if( poDstATTF == nullptr )
3121         {
3122             // Create empty ATTF Field (see GDAL/OGR Bug #1648)" );
3123             poDstATTF = poTarget->AddField(poModule->FindFieldDefn( "ATTF" ));
3124         }
3125 
3126         DDFField *poSrcATTF = poUpdate->FindField( "ATTF" );
3127         const int nRepeatCount = poSrcATTF->GetRepeatCount();
3128 
3129         for( int iAtt = 0; iAtt < nRepeatCount; iAtt++ )
3130         {
3131             const int nATTL
3132                 = poUpdate->GetIntSubfield( "ATTF", 0, "ATTL", iAtt );
3133             int iTAtt = poDstATTF->GetRepeatCount() - 1;  // Used after for.
3134 
3135             for( ; iTAtt >= 0; iTAtt-- )
3136             {
3137                 if( poTarget->GetIntSubfield( "ATTF", 0, "ATTL", iTAtt )
3138                     == nATTL )
3139                     break;
3140             }
3141             if( iTAtt == -1 )
3142                 iTAtt = poDstATTF->GetRepeatCount();
3143 
3144             int nDataBytes = 0;
3145             const char *pszRawData =
3146                 poSrcATTF->GetInstanceData( iAtt, &nDataBytes );
3147             if( pszRawData[2] == 0x7f /* delete marker */ )
3148             {
3149                 poTarget->SetFieldRaw( poDstATTF, iTAtt, nullptr, 0 );
3150             }
3151             else
3152             {
3153                 poTarget->SetFieldRaw( poDstATTF, iTAtt, pszRawData,
3154                                        nDataBytes );
3155             }
3156         }
3157     }
3158 
3159     return true;
3160 }
3161 
3162 /************************************************************************/
3163 /*                            ApplyUpdates()                            */
3164 /*                                                                      */
3165 /*      Read records from an update file, and apply them to the         */
3166 /*      currently loaded index of features.                             */
3167 /************************************************************************/
3168 
ApplyUpdates(DDFModule * poUpdateModule)3169 bool S57Reader::ApplyUpdates( DDFModule *poUpdateModule )
3170 
3171 {
3172 /* -------------------------------------------------------------------- */
3173 /*      Ensure base file is loaded.                                     */
3174 /* -------------------------------------------------------------------- */
3175     if( !bFileIngested && !Ingest() )
3176         return false;
3177 
3178 /* -------------------------------------------------------------------- */
3179 /*      Read records, and apply as updates.                             */
3180 /* -------------------------------------------------------------------- */
3181     CPLErrorReset();
3182 
3183     DDFRecord *poRecord = nullptr;
3184 
3185     while( (poRecord = poUpdateModule->ReadRecord()) != nullptr )
3186     {
3187         DDFField *poKeyField = poRecord->GetField(1);
3188         if( poKeyField == nullptr )
3189             return false;
3190 
3191         const char *pszKey = poKeyField->GetFieldDefn()->GetName();
3192 
3193         if( EQUAL(pszKey,"VRID") || EQUAL(pszKey,"FRID"))
3194         {
3195             const int nRCNM = poRecord->GetIntSubfield( pszKey,0, "RCNM",0 );
3196             const int nRCID = poRecord->GetIntSubfield( pszKey,0, "RCID",0 );
3197             const int nRVER = poRecord->GetIntSubfield( pszKey,0, "RVER",0 );
3198             const int nRUIN = poRecord->GetIntSubfield( pszKey,0, "RUIN",0 );
3199             DDFRecordIndex *poIndex = nullptr;
3200 
3201             if( EQUAL(poKeyField->GetFieldDefn()->GetName(),"VRID") )
3202             {
3203                 switch( nRCNM )
3204                 {
3205                   case RCNM_VI:
3206                     poIndex = &oVI_Index;
3207                     break;
3208 
3209                   case RCNM_VC:
3210                     poIndex = &oVC_Index;
3211                     break;
3212 
3213                   case RCNM_VE:
3214                     poIndex = &oVE_Index;
3215                     break;
3216 
3217                   case RCNM_VF:
3218                     poIndex = &oVF_Index;
3219                     break;
3220 
3221                   default:
3222                     // CPLAssert( false );
3223                     return false;
3224                     break;
3225                 }
3226             }
3227             else
3228             {
3229                 poIndex = &oFE_Index;
3230             }
3231 
3232             if( poIndex != nullptr )
3233             {
3234                 if( nRUIN == 1 )  /* insert */
3235                 {
3236                     poIndex->AddRecord( nRCID, poRecord->CloneOn(poModule) );
3237                 }
3238                 else if( nRUIN == 2 ) /* delete */
3239                 {
3240                     DDFRecord *poTarget = poIndex->FindRecord( nRCID );
3241                     if( poTarget == nullptr )
3242                     {
3243                         CPLError( CE_Warning, CPLE_AppDefined,
3244                                   "Can't find RCNM=%d,RCID=%d for delete.\n",
3245                                   nRCNM, nRCID );
3246                     }
3247                     else if( poTarget->GetIntSubfield( pszKey, 0, "RVER", 0 )
3248                              != nRVER - 1 )
3249                     {
3250                         CPLError( CE_Warning, CPLE_AppDefined,
3251                                   "Mismatched RVER value on RCNM=%d,RCID=%d.\n",
3252                                   nRCNM, nRCID );
3253                     }
3254                     else
3255                     {
3256                         poIndex->RemoveRecord( nRCID );
3257                     }
3258                 }
3259 
3260                 else if( nRUIN == 3 ) /* modify in place */
3261                 {
3262                     DDFRecord *poTarget = poIndex->FindRecord( nRCID );
3263                     if( poTarget == nullptr )
3264                     {
3265                         CPLError( CE_Warning, CPLE_AppDefined,
3266                                   "Can't find RCNM=%d,RCID=%d for update.\n",
3267                                   nRCNM, nRCID );
3268                     }
3269                     else
3270                     {
3271                         if( !ApplyRecordUpdate( poTarget, poRecord ) )
3272                         {
3273                             CPLError( CE_Warning, CPLE_AppDefined,
3274                                       "An update to RCNM=%d,RCID=%d failed.\n",
3275                                       nRCNM, nRCID );
3276                         }
3277                     }
3278                 }
3279             }
3280         }
3281 
3282         else if( EQUAL(pszKey,"DSID") )
3283         {
3284             if( poDSIDRecord != nullptr )
3285             {
3286                 const char* pszEDTN
3287                     = poRecord->GetStringSubfield( "DSID", 0, "EDTN", 0 );
3288                 if( pszEDTN != nullptr )
3289                     m_osEDTNUpdate = pszEDTN;
3290                 const char* pszUPDN
3291                     = poRecord->GetStringSubfield( "DSID", 0, "UPDN", 0 );
3292                 if( pszUPDN != nullptr )
3293                     m_osUPDNUpdate = pszUPDN;
3294                 const char* pszISDT
3295                     = poRecord->GetStringSubfield( "DSID", 0, "ISDT", 0 );
3296                 if( pszISDT != nullptr)
3297                     m_osISDTUpdate = pszISDT;
3298             }
3299         }
3300 
3301         else
3302         {
3303             CPLDebug( "S57",
3304                       "Skipping %s record in S57Reader::ApplyUpdates().\n",
3305                       pszKey );
3306         }
3307     }
3308 
3309     return CPLGetLastErrorType() != CE_Failure;
3310 }
3311 
3312 /************************************************************************/
3313 /*                        FindAndApplyUpdates()                         */
3314 /*                                                                      */
3315 /*      Find all update files that would appear to apply to this        */
3316 /*      base file.                                                      */
3317 /************************************************************************/
3318 
FindAndApplyUpdates(const char * pszPath)3319 bool S57Reader::FindAndApplyUpdates( const char * pszPath )
3320 
3321 {
3322     if( pszPath == nullptr )
3323         pszPath = pszModuleName;
3324 
3325     if( !EQUAL(CPLGetExtension(pszPath),"000") )
3326     {
3327         CPLError( CE_Failure, CPLE_AppDefined,
3328                   "Can't apply updates to a base file with a different\n"
3329                   "extension than .000.\n" );
3330         return false;
3331     }
3332 
3333     bool bSuccess = true;
3334 
3335     for( int iUpdate = 1; bSuccess; iUpdate++ )
3336     {
3337         //Creaing file extension
3338         CPLString extension;
3339         CPLString dirname;
3340 
3341         if( 1 <= iUpdate &&  iUpdate < 10 )
3342         {
3343             char buf[2];
3344             CPLsnprintf( buf, sizeof(buf), "%i", iUpdate );
3345             extension.append("00");
3346             extension.append(buf);
3347             dirname.append(buf);
3348         }
3349         else if( 10 <= iUpdate && iUpdate < 100 )
3350         {
3351             char buf[3];
3352             CPLsnprintf( buf, sizeof(buf), "%i", iUpdate );
3353             extension.append("0");
3354             extension.append(buf);
3355             dirname.append(buf);
3356         }
3357         else if( 100 <= iUpdate && iUpdate < 1000 )
3358         {
3359             char buf[4];
3360             CPLsnprintf( buf, sizeof(buf), "%i", iUpdate );
3361             extension.append(buf);
3362             dirname.append(buf);
3363         }
3364 
3365         DDFModule oUpdateModule;
3366 
3367         //trying current dir first
3368         char *pszUpdateFilename =
3369             CPLStrdup(CPLResetExtension(pszPath,extension.c_str()));
3370 
3371         VSILFILE *file = VSIFOpenL( pszUpdateFilename, "r" );
3372         if( file )
3373         {
3374             VSIFCloseL( file );
3375             bSuccess = CPL_TO_BOOL(
3376                 oUpdateModule.Open( pszUpdateFilename, TRUE ) );
3377             if( bSuccess )
3378             {
3379                 CPLDebug( "S57", "Applying feature updates from %s.",
3380                           pszUpdateFilename );
3381                 if( !ApplyUpdates( &oUpdateModule ) )
3382                     return false;
3383             }
3384         }
3385         else // File is store on Primar generated CD.
3386         {
3387             char* pszBaseFileDir = CPLStrdup(CPLGetDirname(pszPath));
3388             char* pszFileDir = CPLStrdup(CPLGetDirname(pszBaseFileDir));
3389 
3390             CPLString remotefile(pszFileDir);
3391             remotefile.append( "/" );
3392             remotefile.append( dirname );
3393             remotefile.append( "/" );
3394             remotefile.append( CPLGetBasename(pszPath) );
3395             remotefile.append( "." );
3396             remotefile.append( extension );
3397             bSuccess = CPL_TO_BOOL(
3398                 oUpdateModule.Open( remotefile.c_str(), TRUE ) );
3399 
3400             if( bSuccess )
3401                 CPLDebug( "S57", "Applying feature updates from %s.",
3402                           remotefile.c_str() );
3403             CPLFree( pszBaseFileDir );
3404             CPLFree( pszFileDir );
3405             if( bSuccess )
3406             {
3407                 if( !ApplyUpdates( &oUpdateModule ) )
3408                     return false;
3409             }
3410         }//end for if-else
3411         CPLFree( pszUpdateFilename );
3412     }
3413 
3414     return true;
3415 }
3416 
3417 /************************************************************************/
3418 /*                             GetExtent()                              */
3419 /*                                                                      */
3420 /*      Scan all the cached records collecting spatial bounds as        */
3421 /*      efficiently as possible for this transfer.                      */
3422 /************************************************************************/
3423 
GetExtent(OGREnvelope * psExtent,int bForce)3424 OGRErr S57Reader::GetExtent( OGREnvelope *psExtent, int bForce )
3425 
3426 {
3427 /* -------------------------------------------------------------------- */
3428 /*      If we aren't forced to get the extent say no if we haven't      */
3429 /*      already indexed the iso8211 records.                            */
3430 /* -------------------------------------------------------------------- */
3431     if( !bForce && !bFileIngested )
3432         return OGRERR_FAILURE;
3433 
3434     if( !Ingest() )
3435         return OGRERR_FAILURE;
3436 
3437 /* -------------------------------------------------------------------- */
3438 /*      We will scan all the low level vector elements for extents      */
3439 /*      coordinates.                                                    */
3440 /* -------------------------------------------------------------------- */
3441     bool bGotExtents = false;
3442     int nXMin=0;
3443     int nXMax=0;
3444     int nYMin=0;
3445     int nYMax=0;
3446 
3447     const int INDEX_COUNT = 4;
3448     DDFRecordIndex *apoIndex[INDEX_COUNT];
3449 
3450     apoIndex[0] = &oVI_Index;
3451     apoIndex[1] = &oVC_Index;
3452     apoIndex[2] = &oVE_Index;
3453     apoIndex[3] = &oVF_Index;
3454 
3455     for( int iIndex = 0; iIndex < INDEX_COUNT; iIndex++ )
3456     {
3457         DDFRecordIndex  *poIndex = apoIndex[iIndex];
3458 
3459         for( int iVIndex = 0; iVIndex < poIndex->GetCount(); iVIndex++ )
3460         {
3461             DDFRecord *poRecord = poIndex->GetByIndex( iVIndex );
3462             DDFField *poSG3D = poRecord->FindField( "SG3D" );
3463             DDFField *poSG2D = poRecord->FindField( "SG2D" );
3464 
3465             if( poSG3D != nullptr )
3466             {
3467                 const int  nVCount = poSG3D->GetRepeatCount();
3468                 const GByte *pabyData = (const GByte*)poSG3D->GetData();
3469                 if( poSG3D->GetDataSize() <
3470                     3 * nVCount * static_cast<int>( sizeof(int) ) )
3471                     return OGRERR_FAILURE;
3472 
3473                 for( int i = 0; i < nVCount; i++ )
3474                 {
3475                     GInt32 nX = CPL_LSBSINT32PTR(pabyData + 4*(i*3+1));
3476                     GInt32 nY = CPL_LSBSINT32PTR(pabyData + 4*(i*3+0));
3477 
3478                     if( bGotExtents )
3479                     {
3480                         nXMin = std::min(nXMin, nX);
3481                         nXMax = std::max(nXMax, nX);
3482                         nYMin = std::min(nYMin, nY);
3483                         nYMax = std::max(nYMax, nY);
3484                     }
3485                     else
3486                     {
3487                         nXMin = nX;
3488                         nXMax = nX;
3489                         nYMin = nY;
3490                         nYMax = nY;
3491                         bGotExtents = true;
3492                     }
3493                 }
3494             }
3495             else if( poSG2D != nullptr )
3496             {
3497                 const int nVCount = poSG2D->GetRepeatCount();
3498 
3499                 if( poSG2D->GetDataSize() < 2 * nVCount * (int)sizeof(int) )
3500                     return OGRERR_FAILURE;
3501 
3502                 const GByte *pabyData = (const GByte*)poSG2D->GetData();
3503 
3504                 for( int i = 0; i < nVCount; i++ )
3505                 {
3506                     const GInt32 nX = CPL_LSBSINT32PTR(pabyData + 4*(i*2+1));
3507                     const GInt32 nY = CPL_LSBSINT32PTR(pabyData + 4*(i*2+0));
3508 
3509                     if( bGotExtents )
3510                     {
3511                         nXMin = std::min(nXMin, nX);
3512                         nXMax = std::max(nXMax, nX);
3513                         nYMin = std::min(nYMin, nY);
3514                         nYMax = std::max(nYMax, nY);
3515                     }
3516                     else
3517                     {
3518                         nXMin = nX;
3519                         nXMax = nX;
3520                         nYMin = nY;
3521                         nYMax = nY;
3522                         bGotExtents = true;
3523                     }
3524                 }
3525             }
3526         }
3527     }
3528 
3529     if( !bGotExtents )
3530     {
3531         return OGRERR_FAILURE;
3532     }
3533     else
3534     {
3535         psExtent->MinX = nXMin / static_cast<double>( nCOMF );
3536         psExtent->MaxX = nXMax / static_cast<double>( nCOMF );
3537         psExtent->MinY = nYMin / static_cast<double>( nCOMF );
3538         psExtent->MaxY = nYMax / static_cast<double>( nCOMF );
3539 
3540         return OGRERR_NONE;
3541     }
3542 }
3543