1 /******************************************************************************
2  * $Id: ogrs57datasource.cpp 28348 2015-01-23 15:27:13Z rouault $
3  *
4  * Project:  S-57 Translator
5  * Purpose:  Implements OGRS57DataSource class
6  * Author:   Frank Warmerdam, warmerdam@pobox.com
7  *
8  ******************************************************************************
9  * Copyright (c) 1999, Frank Warmerdam
10  * Copyright (c) 2010-2013, Even Rouault <even dot rouault at mines-paris dot org>
11  *
12  * Permission is hereby granted, free of charge, to any person obtaining a
13  * copy of this software and associated documentation files (the "Software"),
14  * to deal in the Software without restriction, including without limitation
15  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16  * and/or sell copies of the Software, and to permit persons to whom the
17  * Software is furnished to do so, subject to the following conditions:
18  *
19  * The above copyright notice and this permission notice shall be included
20  * in all copies or substantial portions of the Software.
21  *
22  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
23  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
25  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28  * DEALINGS IN THE SOFTWARE.
29  ****************************************************************************/
30 
31 #include "ogr_s57.h"
32 #include "cpl_conv.h"
33 #include "cpl_string.h"
34 
35 CPL_CVSID("$Id: ogrs57datasource.cpp 28348 2015-01-23 15:27:13Z rouault $");
36 
37 /************************************************************************/
38 /*                          OGRS57DataSource()                          */
39 /************************************************************************/
40 
OGRS57DataSource(char ** papszOpenOptions)41 OGRS57DataSource::OGRS57DataSource(char** papszOpenOptions)
42 
43 {
44     nLayers = 0;
45     papoLayers = NULL;
46 
47     nModules = 0;
48     papoModules = NULL;
49     poClassContentExplorer = NULL;
50     poWriter = NULL;
51 
52     pszName = NULL;
53 
54     poSpatialRef = new OGRSpatialReference();
55     poSpatialRef->SetWellKnownGeogCS( "WGS84" );
56 
57     bExtentsSet = FALSE;
58 
59 
60 /* -------------------------------------------------------------------- */
61 /*      Allow initialization of options from the environment.           */
62 /* -------------------------------------------------------------------- */
63     if( papszOpenOptions != NULL )
64     {
65         papszOptions = CSLDuplicate(papszOpenOptions);
66     }
67     else
68     {
69         const char *pszOptString = CPLGetConfigOption( "OGR_S57_OPTIONS", NULL );
70         papszOptions = NULL;
71 
72         if ( pszOptString )
73         {
74             char    **papszCurOption;
75 
76             papszOptions =
77                 CSLTokenizeStringComplex( pszOptString, ",", FALSE, FALSE );
78 
79             if ( papszOptions && *papszOptions )
80             {
81                 CPLDebug( "S57", "The following S57 options are being set:" );
82                 papszCurOption = papszOptions;
83                 while( *papszCurOption )
84                     CPLDebug( "S57", "    %s", *papszCurOption++ );
85             }
86         }
87     }
88 }
89 
90 /************************************************************************/
91 /*                         ~OGRS57DataSource()                          */
92 /************************************************************************/
93 
~OGRS57DataSource()94 OGRS57DataSource::~OGRS57DataSource()
95 
96 {
97     int         i;
98 
99     for( i = 0; i < nLayers; i++ )
100         delete papoLayers[i];
101 
102     CPLFree( papoLayers );
103 
104     for( i = 0; i < nModules; i++ )
105         delete papoModules[i];
106     CPLFree( papoModules );
107 
108     CPLFree( pszName );
109 
110     CSLDestroy( papszOptions );
111 
112     poSpatialRef->Release();
113 
114     if( poWriter != NULL )
115     {
116         poWriter->Close();
117         delete poWriter;
118     }
119     delete poClassContentExplorer;
120 }
121 
122 /************************************************************************/
123 /*                           SetOptionList()                            */
124 /************************************************************************/
125 
SetOptionList(char ** papszNewOptions)126 void OGRS57DataSource::SetOptionList( char ** papszNewOptions )
127 
128 {
129     CSLDestroy( papszOptions );
130     papszOptions = CSLDuplicate( papszNewOptions );
131 }
132 
133 /************************************************************************/
134 /*                             GetOption()                              */
135 /************************************************************************/
136 
GetOption(const char * pszOption)137 const char *OGRS57DataSource::GetOption( const char * pszOption )
138 
139 {
140     return CSLFetchNameValue( papszOptions, pszOption );
141 }
142 
143 /************************************************************************/
144 /*                           TestCapability()                           */
145 /************************************************************************/
146 
TestCapability(const char *)147 int OGRS57DataSource::TestCapability( const char * )
148 
149 {
150     return FALSE;
151 }
152 
153 /************************************************************************/
154 /*                                Open()                                */
155 /************************************************************************/
156 
Open(const char * pszFilename)157 int OGRS57DataSource::Open( const char * pszFilename )
158 
159 {
160     int         iModule;
161 
162     pszName = CPLStrdup( pszFilename );
163 
164 /* -------------------------------------------------------------------- */
165 /*      Setup reader options.                                           */
166 /* -------------------------------------------------------------------- */
167     char **papszReaderOptions = NULL;
168     S57Reader   *poModule;
169 
170     poModule = new S57Reader( pszFilename );
171 
172     if( GetOption(S57O_LNAM_REFS) == NULL )
173         papszReaderOptions = CSLSetNameValue(papszReaderOptions,
174                                          S57O_LNAM_REFS, "ON" );
175     else
176         papszReaderOptions =
177             CSLSetNameValue( papszReaderOptions, S57O_LNAM_REFS,
178                              GetOption(S57O_LNAM_REFS));
179 
180     if( GetOption(S57O_UPDATES) != NULL )
181         papszReaderOptions =
182             CSLSetNameValue( papszReaderOptions, S57O_UPDATES,
183                              GetOption(S57O_UPDATES));
184 
185     if( GetOption(S57O_SPLIT_MULTIPOINT) != NULL )
186         papszReaderOptions =
187             CSLSetNameValue( papszReaderOptions, S57O_SPLIT_MULTIPOINT,
188                              GetOption(S57O_SPLIT_MULTIPOINT) );
189 
190     if( GetOption(S57O_ADD_SOUNDG_DEPTH) != NULL )
191         papszReaderOptions =
192             CSLSetNameValue( papszReaderOptions, S57O_ADD_SOUNDG_DEPTH,
193                              GetOption(S57O_ADD_SOUNDG_DEPTH));
194 
195     if( GetOption(S57O_PRESERVE_EMPTY_NUMBERS) != NULL )
196         papszReaderOptions =
197             CSLSetNameValue( papszReaderOptions, S57O_PRESERVE_EMPTY_NUMBERS,
198                              GetOption(S57O_PRESERVE_EMPTY_NUMBERS) );
199 
200     if( GetOption(S57O_RETURN_PRIMITIVES) != NULL )
201         papszReaderOptions =
202             CSLSetNameValue( papszReaderOptions, S57O_RETURN_PRIMITIVES,
203                              GetOption(S57O_RETURN_PRIMITIVES) );
204 
205     if( GetOption(S57O_RETURN_LINKAGES) != NULL )
206         papszReaderOptions =
207             CSLSetNameValue( papszReaderOptions, S57O_RETURN_LINKAGES,
208                              GetOption(S57O_RETURN_LINKAGES) );
209 
210     if( GetOption(S57O_RETURN_DSID) != NULL )
211         papszReaderOptions =
212             CSLSetNameValue( papszReaderOptions, S57O_RETURN_DSID,
213                              GetOption(S57O_RETURN_DSID) );
214 
215     if( GetOption(S57O_RECODE_BY_DSSI) != NULL )
216         papszReaderOptions =
217             CSLSetNameValue( papszReaderOptions, S57O_RECODE_BY_DSSI,
218                              GetOption(S57O_RECODE_BY_DSSI) );
219 
220     int bRet = poModule->SetOptions( papszReaderOptions );
221     CSLDestroy( papszReaderOptions );
222 
223     if( !bRet )
224     {
225         delete poModule;
226         return FALSE;
227     }
228 
229 /* -------------------------------------------------------------------- */
230 /*      Try opening.                                                    */
231 /*                                                                      */
232 /*      Eventually this should check for catalogs, and if found         */
233 /*      instantiate a whole series of modules.                          */
234 /* -------------------------------------------------------------------- */
235     if( !poModule->Open( TRUE ) )
236     {
237         delete poModule;
238 
239         return FALSE;
240     }
241 
242     int bSuccess = TRUE;
243 
244     nModules = 1;
245     papoModules = (S57Reader **) CPLMalloc(sizeof(void*));
246     papoModules[0] = poModule;
247 
248 /* -------------------------------------------------------------------- */
249 /*      Add the header layers if they are called for.                   */
250 /* -------------------------------------------------------------------- */
251     if( GetOption( S57O_RETURN_DSID ) == NULL
252         || CSLTestBoolean(GetOption( S57O_RETURN_DSID )) )
253     {
254         OGRFeatureDefn  *poDefn;
255 
256         poDefn = S57GenerateDSIDFeatureDefn();
257         AddLayer( new OGRS57Layer( this, poDefn ) );
258     }
259 
260 /* -------------------------------------------------------------------- */
261 /*      Add the primitive layers if they are called for.                */
262 /* -------------------------------------------------------------------- */
263     if( GetOption( S57O_RETURN_PRIMITIVES ) != NULL )
264     {
265         OGRFeatureDefn  *poDefn;
266 
267         poDefn = S57GenerateVectorPrimitiveFeatureDefn( RCNM_VI, poModule->GetOptionFlags());
268         AddLayer( new OGRS57Layer( this, poDefn ) );
269 
270         poDefn = S57GenerateVectorPrimitiveFeatureDefn( RCNM_VC, poModule->GetOptionFlags());
271         AddLayer( new OGRS57Layer( this, poDefn ) );
272 
273         poDefn = S57GenerateVectorPrimitiveFeatureDefn( RCNM_VE, poModule->GetOptionFlags());
274         AddLayer( new OGRS57Layer( this, poDefn ) );
275 
276         poDefn = S57GenerateVectorPrimitiveFeatureDefn( RCNM_VF, poModule->GetOptionFlags());
277         AddLayer( new OGRS57Layer( this, poDefn ) );
278     }
279 
280 /* -------------------------------------------------------------------- */
281 /*      Initialize a layer for each type of geometry.  Eventually       */
282 /*      we will do this by object class.                                */
283 /* -------------------------------------------------------------------- */
284     if( OGRS57Driver::GetS57Registrar() == NULL )
285     {
286         OGRFeatureDefn  *poDefn;
287 
288         poDefn = S57GenerateGeomFeatureDefn( wkbPoint,
289                                              poModule->GetOptionFlags() );
290         AddLayer( new OGRS57Layer( this, poDefn ) );
291 
292         poDefn = S57GenerateGeomFeatureDefn( wkbLineString,
293                                              poModule->GetOptionFlags() );
294         AddLayer( new OGRS57Layer( this, poDefn ) );
295 
296         poDefn = S57GenerateGeomFeatureDefn( wkbPolygon,
297                                              poModule->GetOptionFlags() );
298         AddLayer( new OGRS57Layer( this, poDefn ) );
299 
300         poDefn = S57GenerateGeomFeatureDefn( wkbNone,
301                                              poModule->GetOptionFlags() );
302         AddLayer( new OGRS57Layer( this, poDefn ) );
303     }
304 
305 /* -------------------------------------------------------------------- */
306 /*      Initialize a feature definition for each class that actually    */
307 /*      occurs in the dataset.                                          */
308 /* -------------------------------------------------------------------- */
309     else
310     {
311         OGRFeatureDefn  *poDefn;
312         std::vector<int> anClassCount;
313         int              bGeneric = FALSE;
314         unsigned int     iClass;
315 
316         poClassContentExplorer =
317             new S57ClassContentExplorer( OGRS57Driver::GetS57Registrar() );
318 
319         for( iModule = 0; iModule < nModules; iModule++ )
320             papoModules[iModule]->SetClassBased( OGRS57Driver::GetS57Registrar(),
321                                                  poClassContentExplorer );
322 
323         for( iModule = 0; iModule < nModules; iModule++ )
324         {
325             bSuccess &=
326                 papoModules[iModule]->CollectClassList(anClassCount);
327         }
328 
329         for( iClass = 0; iClass < anClassCount.size(); iClass++ )
330         {
331             if( anClassCount[iClass] > 0 )
332             {
333                 poDefn =
334                     S57GenerateObjectClassDefn( OGRS57Driver::GetS57Registrar(),
335                                                 poClassContentExplorer,
336                                                 iClass,
337                                                 poModule->GetOptionFlags() );
338 
339                 if( poDefn != NULL )
340                     AddLayer( new OGRS57Layer( this, poDefn,
341                                                anClassCount[iClass] ) );
342                 else
343                 {
344                     bGeneric = TRUE;
345                     CPLDebug( "S57",
346                               "Unable to find definition for OBJL=%d\n",
347                               iClass );
348                 }
349             }
350         }
351 
352         if( bGeneric )
353         {
354             poDefn = S57GenerateGeomFeatureDefn( wkbUnknown,
355                                                  poModule->GetOptionFlags() );
356             AddLayer( new OGRS57Layer( this, poDefn ) );
357         }
358     }
359 
360 /* -------------------------------------------------------------------- */
361 /*      Attach the layer definitions to each of the readers.            */
362 /* -------------------------------------------------------------------- */
363     for( iModule = 0; iModule < nModules; iModule++ )
364     {
365         for( int iLayer = 0; iLayer < nLayers; iLayer++ )
366         {
367             papoModules[iModule]->AddFeatureDefn(
368                 papoLayers[iLayer]->GetLayerDefn() );
369         }
370     }
371 
372     return bSuccess;
373 }
374 
375 /************************************************************************/
376 /*                              GetLayer()                              */
377 /************************************************************************/
378 
GetLayer(int iLayer)379 OGRLayer *OGRS57DataSource::GetLayer( int iLayer )
380 
381 {
382     if( iLayer < 0 || iLayer >= nLayers )
383         return NULL;
384     else
385         return papoLayers[iLayer];
386 }
387 
388 /************************************************************************/
389 /*                              AddLayer()                              */
390 /************************************************************************/
391 
AddLayer(OGRS57Layer * poNewLayer)392 void OGRS57DataSource::AddLayer( OGRS57Layer * poNewLayer )
393 
394 {
395     papoLayers = (OGRS57Layer **)
396         CPLRealloc( papoLayers, sizeof(void*) * ++nLayers );
397 
398     papoLayers[nLayers-1] = poNewLayer;
399 }
400 
401 /************************************************************************/
402 /*                             GetModule()                              */
403 /************************************************************************/
404 
GetModule(int i)405 S57Reader * OGRS57DataSource::GetModule( int i )
406 
407 {
408     if( i < 0 || i >= nModules )
409         return NULL;
410     else
411         return papoModules[i];
412 }
413 
414 /************************************************************************/
415 /*                            GetDSExtent()                             */
416 /************************************************************************/
417 
GetDSExtent(OGREnvelope * psExtent,int bForce)418 OGRErr OGRS57DataSource::GetDSExtent( OGREnvelope *psExtent, int bForce )
419 
420 {
421 /* -------------------------------------------------------------------- */
422 /*      If we have it, return it immediately.                           */
423 /* -------------------------------------------------------------------- */
424     if( bExtentsSet )
425     {
426         *psExtent = oExtents;
427         return OGRERR_NONE;
428     }
429 
430     if( nModules == 0 )
431         return OGRERR_FAILURE;
432 
433 /* -------------------------------------------------------------------- */
434 /*      Otherwise try asking each of the readers for it.                */
435 /* -------------------------------------------------------------------- */
436     for( int iModule=0; iModule < nModules; iModule++ )
437     {
438         OGREnvelope     oModuleEnvelope;
439         OGRErr          eErr;
440 
441         eErr = papoModules[iModule]->GetExtent( &oModuleEnvelope, bForce );
442         if( eErr != OGRERR_NONE )
443             return eErr;
444 
445         if( iModule == 0 )
446             oExtents = oModuleEnvelope;
447         else
448         {
449             oExtents.MinX = MIN(oExtents.MinX,oModuleEnvelope.MinX);
450             oExtents.MaxX = MAX(oExtents.MaxX,oModuleEnvelope.MaxX);
451             oExtents.MinY = MIN(oExtents.MinY,oModuleEnvelope.MinY);
452             oExtents.MaxX = MAX(oExtents.MaxY,oModuleEnvelope.MaxY);
453         }
454     }
455 
456     *psExtent = oExtents;
457     bExtentsSet = TRUE;
458 
459     return OGRERR_NONE;
460 }
461 
462 /************************************************************************/
463 /*                               Create()                               */
464 /*                                                                      */
465 /*      Create a new S57 file, and represent it as a datasource.        */
466 /************************************************************************/
467 
Create(const char * pszFilename,char ** papszOptions)468 int OGRS57DataSource::Create( const char *pszFilename,
469                               char **papszOptions )
470 {
471 /* -------------------------------------------------------------------- */
472 /*      Instantiate the class registrar if possible.                    */
473 /* -------------------------------------------------------------------- */
474     if( OGRS57Driver::GetS57Registrar() == NULL )
475     {
476         CPLError( CE_Failure, CPLE_AppDefined,
477                   "Unable to load s57objectclasses.csv, unable to continue." );
478         return FALSE;
479     }
480 
481 /* -------------------------------------------------------------------- */
482 /*      Create the S-57 file with definition record.                    */
483 /* -------------------------------------------------------------------- */
484     poWriter = new S57Writer();
485 
486     if( !poWriter->CreateS57File( pszFilename ) )
487         return FALSE;
488 
489     poClassContentExplorer =
490         new S57ClassContentExplorer( OGRS57Driver::GetS57Registrar() );
491 
492     poWriter->SetClassBased( OGRS57Driver::GetS57Registrar(),
493                              poClassContentExplorer );
494     pszName = CPLStrdup( pszFilename );
495 
496 /* -------------------------------------------------------------------- */
497 /*      Add the primitive layers if they are called for.                */
498 /* -------------------------------------------------------------------- */
499     OGRFeatureDefn  *poDefn;
500     int nOptionFlags = S57M_RETURN_LINKAGES | S57M_LNAM_REFS;
501 
502     poDefn = S57GenerateVectorPrimitiveFeatureDefn( RCNM_VI, nOptionFlags );
503     AddLayer( new OGRS57Layer( this, poDefn ) );
504 
505     poDefn = S57GenerateVectorPrimitiveFeatureDefn( RCNM_VC, nOptionFlags );
506     AddLayer( new OGRS57Layer( this, poDefn ) );
507 
508     poDefn = S57GenerateVectorPrimitiveFeatureDefn( RCNM_VE, nOptionFlags );
509     AddLayer( new OGRS57Layer( this, poDefn ) );
510 
511     poDefn = S57GenerateVectorPrimitiveFeatureDefn( RCNM_VF, nOptionFlags );
512     AddLayer( new OGRS57Layer( this, poDefn ) );
513 
514 /* -------------------------------------------------------------------- */
515 /*      Initialize a feature definition for each object class.          */
516 /* -------------------------------------------------------------------- */
517     poClassContentExplorer->Rewind();
518     while( poClassContentExplorer->NextClass() )
519     {
520         poDefn =
521             S57GenerateObjectClassDefn( OGRS57Driver::GetS57Registrar(),
522                                         poClassContentExplorer,
523                                         poClassContentExplorer->GetOBJL(),
524                                         nOptionFlags );
525 
526         AddLayer( new OGRS57Layer( this, poDefn, 0,
527                                    poClassContentExplorer->GetOBJL() ) );
528     }
529 
530 /* -------------------------------------------------------------------- */
531 /*      Write out "header" records.                                     */
532 /* -------------------------------------------------------------------- */
533     int nEXPP = 1, nINTU = 4, nAGEN = 540, nNOMR = 0, nNOGR = 0,
534         nNOLR = 0, nNOIN = 0, nNOCN = 0, nNOED = 0;
535     const char
536         *pszEXPP = CSLFetchNameValue(papszOptions, "S57_EXPP"),
537         *pszINTU = CSLFetchNameValue(papszOptions, "S57_INTU"),
538         *pszEDTN = CSLFetchNameValue(papszOptions, "S57_EDTN"),
539         *pszUPDN = CSLFetchNameValue(papszOptions, "S57_UPDN"),
540         *pszUADT = CSLFetchNameValue(papszOptions, "S57_UADT"),
541         *pszISDT = CSLFetchNameValue(papszOptions, "S57_ISDT"),
542         *pszSTED = CSLFetchNameValue(papszOptions, "S57_STED"),
543         *pszAGEN = CSLFetchNameValue(papszOptions, "S57_AGEN"),
544         *pszCOMT = CSLFetchNameValue(papszOptions, "S57_COMT"),
545         *pszNOMR = CSLFetchNameValue(papszOptions, "S57_NOMR"),
546         *pszNOGR = CSLFetchNameValue(papszOptions, "S57_NOGR"),
547         *pszNOLR = CSLFetchNameValue(papszOptions, "S57_NOLR"),
548         *pszNOIN = CSLFetchNameValue(papszOptions, "S57_NOIN"),
549         *pszNOCN = CSLFetchNameValue(papszOptions, "S57_NOCN"),
550         *pszNOED = CSLFetchNameValue(papszOptions, "S57_NOED");
551     if (pszEXPP) nEXPP = atoi(pszEXPP);
552     if (pszINTU) nINTU = atoi(pszINTU);
553     if (pszAGEN) nAGEN = atoi(pszAGEN);
554     if (pszNOMR) nNOMR = atoi(pszNOMR);
555     if (pszNOGR) nNOGR = atoi(pszNOGR);
556     if (pszNOLR) nNOLR = atoi(pszNOLR);
557     if (pszNOIN) nNOIN = atoi(pszNOIN);
558     if (pszNOCN) nNOCN = atoi(pszNOCN);
559     if (pszNOED) nNOED = atoi(pszNOED);
560     poWriter->WriteDSID( nEXPP, nINTU, CPLGetFilename( pszFilename ),
561                          pszEDTN, pszUPDN, pszUADT, pszISDT, pszSTED, nAGEN,
562                          pszCOMT, nNOMR, nNOGR, nNOLR, nNOIN, nNOCN, nNOED );
563 
564     int nHDAT = 2, nVDAT = 17, nSDAT = 23, nCSCL = 52000;
565     const char
566         *pszHDAT = CSLFetchNameValue(papszOptions, "S57_HDAT"),
567         *pszVDAT = CSLFetchNameValue(papszOptions, "S57_VDAT"),
568         *pszSDAT = CSLFetchNameValue(papszOptions, "S57_SDAT"),
569         *pszCSCL = CSLFetchNameValue(papszOptions, "S57_CSCL");
570     if (pszHDAT)
571         nHDAT = atoi(pszHDAT);
572     if (pszVDAT)
573         nVDAT = atoi(pszVDAT);
574     if (pszSDAT)
575         nSDAT = atoi(pszSDAT);
576     if (pszCSCL)
577         nCSCL = atoi(pszCSCL);
578     poWriter->WriteDSPM(nHDAT, nVDAT, nSDAT, nCSCL);
579 
580 
581     return TRUE;
582 }
583