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