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