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