1 /******************************************************************************
2  * $Id: s57classregistrar.cpp 27885 2014-10-19 22:56:48Z rouault $
3  *
4  * Project:  S-57 Translator
5  * Purpose:  Implements S57ClassRegistrar class for keeping track of
6  *           information on S57 object classes.
7  * Author:   Frank Warmerdam, warmerdam@pobox.com
8  *
9  ******************************************************************************
10  * Copyright (c) 1999, Frank Warmerdam
11  * Copyright (c) 2011-2013, Even Rouault <even dot rouault at mines-paris dot org>
12  *
13  * Permission is hereby granted, free of charge, to any person obtaining a
14  * copy of this software and associated documentation files (the "Software"),
15  * to deal in the Software without restriction, including without limitation
16  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
17  * and/or sell copies of the Software, and to permit persons to whom the
18  * Software is furnished to do so, subject to the following conditions:
19  *
20  * The above copyright notice and this permission notice shall be included
21  * in all copies or substantial portions of the Software.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29  * DEALINGS IN THE SOFTWARE.
30  ****************************************************************************/
31 
32 #include "s57.h"
33 #include "cpl_conv.h"
34 #include "cpl_string.h"
35 
36 CPL_CVSID("$Id: s57classregistrar.cpp 27885 2014-10-19 22:56:48Z rouault $");
37 
38 
39 #ifdef S57_BUILTIN_CLASSES
40 #include "s57tables.h"
41 #endif
42 
43 /************************************************************************/
44 /*                         S57ClassRegistrar()                          */
45 /************************************************************************/
46 
S57ClassRegistrar()47 S57ClassRegistrar::S57ClassRegistrar()
48 
49 {
50     papszNextLine = NULL;
51 }
52 
53 /************************************************************************/
54 /*                         ~S57ClassRegistrar()                         */
55 /************************************************************************/
56 
~S57ClassRegistrar()57 S57ClassRegistrar::~S57ClassRegistrar()
58 
59 {
60     nClasses = 0;
61     for(size_t i=0;i<aoAttrInfos.size();i++)
62         delete aoAttrInfos[i];
63     aoAttrInfos.resize(0);
64     nAttrCount = 0;
65 }
66 
67 /************************************************************************/
68 /*                        S57ClassContentExplorer()                     */
69 /************************************************************************/
70 
S57ClassContentExplorer(S57ClassRegistrar * poRegistrar)71 S57ClassContentExplorer::S57ClassContentExplorer(S57ClassRegistrar* poRegistrar):
72     poRegistrar(poRegistrar)
73 {
74 
75     iCurrentClass = -1;
76 
77     papszCurrentFields = NULL;
78     papapszClassesFields = NULL;
79     papszTempResult = NULL;
80 }
81 
82 /************************************************************************/
83 /*                        ~S57ClassContentExplorer()                    */
84 /************************************************************************/
85 
~S57ClassContentExplorer()86 S57ClassContentExplorer::~S57ClassContentExplorer()
87 {
88     CSLDestroy( papszTempResult );
89 
90     if( papapszClassesFields != NULL )
91     {
92         for( int i = 0; i < poRegistrar->nClasses; i++ )
93             CSLDestroy( papapszClassesFields[i] );
94         CPLFree( papapszClassesFields );
95     }
96 }
97 
98 /************************************************************************/
99 /*                              FindFile()                              */
100 /************************************************************************/
101 
FindFile(const char * pszTarget,const char * pszDirectory,int bReportErr,VSILFILE ** pfp)102 int S57ClassRegistrar::FindFile( const char *pszTarget,
103                                  const char *pszDirectory,
104                                  int bReportErr,
105                                  VSILFILE **pfp )
106 
107 {
108     const char *pszFilename;
109 
110     if( pszDirectory == NULL )
111     {
112         pszFilename = CPLFindFile( "s57", pszTarget );
113         if( pszFilename == NULL )
114             pszFilename = pszTarget;
115     }
116     else
117     {
118         pszFilename = CPLFormFilename( pszDirectory, pszTarget, NULL );
119     }
120 
121     *pfp = VSIFOpenL( pszFilename, "rb" );
122 
123 #ifdef S57_BUILTIN_CLASSES
124     if( *pfp == NULL )
125     {
126         if( EQUAL(pszTarget, "s57objectclasses.csv") )
127             papszNextLine = gpapszS57Classes;
128         else
129             papszNextLine = gpapszS57attributes;
130     }
131 #else
132     if( *pfp == NULL )
133     {
134         if( bReportErr )
135             CPLError( CE_Failure, CPLE_OpenFailed,
136                       "Failed to open %s.\n",
137                       pszFilename );
138         return FALSE;
139     }
140 #endif
141 
142     return TRUE;
143 }
144 
145 /************************************************************************/
146 /*                              ReadLine()                              */
147 /*                                                                      */
148 /*      Read a line from the provided file, or from the "built-in"      */
149 /*      configuration file line list if the file is NULL.               */
150 /************************************************************************/
151 
ReadLine(VSILFILE * fp)152 const char *S57ClassRegistrar::ReadLine( VSILFILE * fp )
153 
154 {
155     if( fp != NULL )
156         return CPLReadLineL( fp );
157 
158     if( papszNextLine == NULL )
159         return NULL;
160 
161     if( *papszNextLine == NULL )
162     {
163         papszNextLine = NULL;
164         return NULL;
165     }
166     else
167         return *(papszNextLine++);
168 }
169 
170 /************************************************************************/
171 /*                              LoadInfo()                              */
172 /************************************************************************/
173 
LoadInfo(const char * pszDirectory,const char * pszProfile,int bReportErr)174 int S57ClassRegistrar::LoadInfo( const char * pszDirectory,
175                                  const char * pszProfile,
176                                  int bReportErr )
177 
178 {
179     VSILFILE   *fp;
180     char        szTargetFile[1024];
181 
182     if( pszDirectory == NULL )
183         pszDirectory = CPLGetConfigOption("S57_CSV",NULL);
184 
185 /* ==================================================================== */
186 /*      Read the s57objectclasses file.                                 */
187 /* ==================================================================== */
188     if( pszProfile == NULL )
189         pszProfile = CPLGetConfigOption( "S57_PROFILE", "" );
190 
191     if( EQUAL(pszProfile, "Additional_Military_Layers") )
192     {
193        sprintf( szTargetFile, "s57objectclasses_%s.csv", "aml" );
194     }
195     else if ( EQUAL(pszProfile, "Inland_Waterways") )
196     {
197        sprintf( szTargetFile, "s57objectclasses_%s.csv", "iw" );
198     }
199     else if( strlen(pszProfile) > 0 )
200     {
201        snprintf( szTargetFile, sizeof(szTargetFile), "s57objectclasses_%s.csv", pszProfile );
202     }
203     else
204     {
205        strcpy( szTargetFile, "s57objectclasses.csv" );
206     }
207 
208     if( !FindFile( szTargetFile, pszDirectory, bReportErr, &fp ) )
209         return FALSE;
210 
211 /* -------------------------------------------------------------------- */
212 /*      Skip the line defining the column titles.                       */
213 /* -------------------------------------------------------------------- */
214     const char * pszLine = ReadLine( fp );
215 
216     if( !EQUAL(pszLine,
217                "\"Code\",\"ObjectClass\",\"Acronym\",\"Attribute_A\","
218                "\"Attribute_B\",\"Attribute_C\",\"Class\",\"Primitives\"" ) )
219     {
220         CPLError( CE_Failure, CPLE_AppDefined,
221                   "s57objectclasses columns don't match expected format!\n" );
222         if( fp != NULL )
223             VSIFCloseL( fp );
224         return FALSE;
225     }
226 
227 /* -------------------------------------------------------------------- */
228 /*      Read and form string list.                                      */
229 /* -------------------------------------------------------------------- */
230     apszClassesInfo.Clear();
231     while( (pszLine = ReadLine(fp)) != NULL )
232     {
233         apszClassesInfo.AddString(pszLine);
234     }
235 
236 /* -------------------------------------------------------------------- */
237 /*      Cleanup, and establish state.                                   */
238 /* -------------------------------------------------------------------- */
239     if( fp != NULL )
240         VSIFCloseL( fp );
241 
242     nClasses = apszClassesInfo.size();
243     if( nClasses == 0 )
244         return FALSE;
245 
246 /* ==================================================================== */
247 /*      Read the attributes list.                                       */
248 /* ==================================================================== */
249 
250     if( EQUAL(pszProfile, "Additional_Military_Layers") )
251     {
252         sprintf( szTargetFile, "s57attributes_%s.csv", "aml" );
253     }
254     else if ( EQUAL(pszProfile, "Inland_Waterways") )
255     {
256        sprintf( szTargetFile, "s57attributes_%s.csv", "iw" );
257     }
258     else if( strlen(pszProfile) > 0 )
259     {
260        snprintf( szTargetFile, sizeof(szTargetFile), "s57attributes_%s.csv", pszProfile );
261     }
262     else
263     {
264        strcpy( szTargetFile, "s57attributes.csv" );
265     }
266 
267     if( !FindFile( szTargetFile, pszDirectory, bReportErr, &fp ) )
268         return FALSE;
269 
270 /* -------------------------------------------------------------------- */
271 /*      Skip the line defining the column titles.                       */
272 /* -------------------------------------------------------------------- */
273     pszLine = ReadLine( fp );
274 
275     if( !EQUAL(pszLine,
276           "\"Code\",\"Attribute\",\"Acronym\",\"Attributetype\",\"Class\"") )
277     {
278         CPLError( CE_Failure, CPLE_AppDefined,
279                   "s57attributes columns don't match expected format!\n" );
280         if( fp != NULL )
281             VSIFCloseL( fp );
282         return FALSE;
283     }
284 
285 /* -------------------------------------------------------------------- */
286 /*      Read and form string list.                                      */
287 /* -------------------------------------------------------------------- */
288     int        iAttr;
289 
290     while( (pszLine = ReadLine(fp)) != NULL )
291     {
292         char    **papszTokens = CSLTokenizeStringComplex( pszLine, ",",
293                                                           TRUE, TRUE );
294 
295         if( CSLCount(papszTokens) < 5 )
296         {
297             CPLAssert( FALSE );
298             continue;
299         }
300 
301         iAttr = atoi(papszTokens[0]);
302         if( iAttr >= (int) aoAttrInfos.size() )
303             aoAttrInfos.resize(iAttr+1);
304 
305         if( iAttr < 0 || aoAttrInfos[iAttr] != NULL )
306         {
307             CPLDebug( "S57",
308                       "Duplicate/corrupt definition for attribute %d:%s",
309                       iAttr, papszTokens[2] );
310             continue;
311         }
312 
313         aoAttrInfos[iAttr] = new S57AttrInfo();
314         aoAttrInfos[iAttr]->osName = papszTokens[1];
315         aoAttrInfos[iAttr]->osAcronym = papszTokens[2];
316         aoAttrInfos[iAttr]->chType = papszTokens[3][0];
317         aoAttrInfos[iAttr]->chClass = papszTokens[4][0];
318         anAttrIndex.push_back(iAttr);
319         CSLDestroy( papszTokens );
320     }
321 
322     if( fp != NULL )
323         VSIFCloseL( fp );
324 
325     nAttrCount = anAttrIndex.size();
326 
327 /* -------------------------------------------------------------------- */
328 /*      Sort index by acronym.                                          */
329 /* -------------------------------------------------------------------- */
330     int         bModified;
331     do
332     {
333         bModified = FALSE;
334         for( iAttr = 0; iAttr < nAttrCount-1; iAttr++ )
335         {
336             if( strcmp(aoAttrInfos[anAttrIndex[iAttr]]->osAcronym,
337                        aoAttrInfos[anAttrIndex[iAttr+1]]->osAcronym) > 0 )
338             {
339                 int     nTemp;
340 
341                 nTemp = anAttrIndex[iAttr];
342                 anAttrIndex[iAttr] = anAttrIndex[iAttr+1];
343                 anAttrIndex[iAttr+1] = nTemp;
344                 bModified = TRUE;
345             }
346         }
347     } while( bModified );
348 
349     return TRUE;
350 }
351 
352 /************************************************************************/
353 /*                         SelectClassByIndex()                         */
354 /************************************************************************/
355 
SelectClassByIndex(int nNewIndex)356 int S57ClassContentExplorer::SelectClassByIndex( int nNewIndex )
357 
358 {
359     if( nNewIndex < 0 || nNewIndex >= poRegistrar->nClasses )
360         return FALSE;
361 
362 /* -------------------------------------------------------------------- */
363 /*      Do we have our cache of class information field lists?          */
364 /* -------------------------------------------------------------------- */
365     if( papapszClassesFields == NULL )
366     {
367         papapszClassesFields = (char ***) CPLCalloc(sizeof(void*),poRegistrar->nClasses);
368     }
369 
370 /* -------------------------------------------------------------------- */
371 /*      Has this info been parsed yet?                                  */
372 /* -------------------------------------------------------------------- */
373     if( papapszClassesFields[nNewIndex] == NULL )
374         papapszClassesFields[nNewIndex] =
375             CSLTokenizeStringComplex( poRegistrar->apszClassesInfo[nNewIndex],
376                                       ",", TRUE, TRUE );
377 
378     papszCurrentFields = papapszClassesFields[nNewIndex];
379 
380     iCurrentClass = nNewIndex;
381 
382     return TRUE;
383 }
384 
385 /************************************************************************/
386 /*                             SelectClass()                            */
387 /************************************************************************/
388 
SelectClass(int nOBJL)389 int S57ClassContentExplorer::SelectClass( int nOBJL )
390 
391 {
392     for( int i = 0; i < poRegistrar->nClasses; i++ )
393     {
394         if( atoi(poRegistrar->apszClassesInfo[i]) == nOBJL )
395             return SelectClassByIndex( i );
396     }
397 
398     return FALSE;
399 }
400 
401 /************************************************************************/
402 /*                            SelectClass()                             */
403 /************************************************************************/
404 
SelectClass(const char * pszAcronym)405 int S57ClassContentExplorer::SelectClass( const char *pszAcronym )
406 
407 {
408     for( int i = 0; i < poRegistrar->nClasses; i++ )
409     {
410         if( !SelectClassByIndex( i ) )
411             continue;
412 
413         if( strcmp(GetAcronym(),pszAcronym) == 0 )
414             return TRUE;
415     }
416 
417     return FALSE;
418 }
419 
420 /************************************************************************/
421 /*                              GetOBJL()                               */
422 /************************************************************************/
423 
GetOBJL()424 int S57ClassContentExplorer::GetOBJL()
425 
426 {
427     if( iCurrentClass >= 0 )
428         return atoi(poRegistrar->apszClassesInfo[iCurrentClass]);
429     else
430         return -1;
431 }
432 
433 /************************************************************************/
434 /*                           GetDescription()                           */
435 /************************************************************************/
436 
GetDescription()437 const char * S57ClassContentExplorer::GetDescription()
438 
439 {
440     if( iCurrentClass >= 0 && papszCurrentFields[0] != NULL )
441         return papszCurrentFields[1];
442     else
443         return NULL;
444 }
445 
446 /************************************************************************/
447 /*                             GetAcronym()                             */
448 /************************************************************************/
449 
GetAcronym()450 const char * S57ClassContentExplorer::GetAcronym()
451 
452 {
453     if( iCurrentClass >= 0
454         && papszCurrentFields[0] != NULL
455         && papszCurrentFields[1] != NULL )
456         return papszCurrentFields[2];
457     else
458         return NULL;
459 }
460 
461 /************************************************************************/
462 /*                          GetAttributeList()                          */
463 /*                                                                      */
464 /*      The passed string can be "a", "b", "c" or NULL for all.  The    */
465 /*      returned list remained owned by this object, not the caller.    */
466 /************************************************************************/
467 
GetAttributeList(const char * pszType)468 char **S57ClassContentExplorer::GetAttributeList( const char * pszType )
469 
470 {
471     if( iCurrentClass < 0 )
472         return NULL;
473 
474     CSLDestroy( papszTempResult );
475     papszTempResult = NULL;
476 
477     for( int iColumn = 3; iColumn < 6; iColumn++ )
478     {
479         if( pszType != NULL && iColumn == 3 && !EQUAL(pszType,"a") )
480             continue;
481 
482         if( pszType != NULL && iColumn == 4 && !EQUAL(pszType,"b") )
483             continue;
484 
485         if( pszType != NULL && iColumn == 5 && !EQUAL(pszType,"c") )
486             continue;
487 
488         char    **papszTokens;
489 
490         papszTokens =
491             CSLTokenizeStringComplex( papszCurrentFields[iColumn], ";",
492                                       TRUE, FALSE );
493 
494         papszTempResult = CSLInsertStrings( papszTempResult, -1,
495                                             papszTokens );
496 
497         CSLDestroy( papszTokens );
498     }
499 
500     return papszTempResult;
501 }
502 
503 /************************************************************************/
504 /*                            GetClassCode()                            */
505 /************************************************************************/
506 
GetClassCode()507 char S57ClassContentExplorer::GetClassCode()
508 
509 {
510     if( iCurrentClass >= 0
511         && papszCurrentFields[0] != NULL
512         && papszCurrentFields[1] != NULL
513         && papszCurrentFields[2] != NULL
514         && papszCurrentFields[3] != NULL
515         && papszCurrentFields[4] != NULL
516         && papszCurrentFields[5] != NULL
517         && papszCurrentFields[6] != NULL )
518         return papszCurrentFields[6][0];
519     else
520         return '\0';
521 }
522 
523 /************************************************************************/
524 /*                           GetPrimitives()                            */
525 /************************************************************************/
526 
GetPrimitives()527 char **S57ClassContentExplorer::GetPrimitives()
528 
529 {
530     if( iCurrentClass >= 0
531         && CSLCount(papszCurrentFields) > 7 )
532     {
533         CSLDestroy( papszTempResult );
534         papszTempResult =
535             CSLTokenizeStringComplex( papszCurrentFields[7], ";",
536                                       TRUE, FALSE );
537         return papszTempResult;
538     }
539     else
540         return NULL;
541 }
542 
543 /************************************************************************/
544 /*                            GetAttrInfo()                             */
545 /************************************************************************/
546 
GetAttrInfo(int iAttr)547 const S57AttrInfo *S57ClassRegistrar::GetAttrInfo(int iAttr)
548 {
549     if( iAttr < 0 || iAttr >= (int) aoAttrInfos.size() )
550         return NULL;
551     else
552         return aoAttrInfos[iAttr];
553 }
554 
555 /************************************************************************/
556 /*                         FindAttrByAcronym()                          */
557 /************************************************************************/
558 
FindAttrByAcronym(const char * pszName)559 int    S57ClassRegistrar::FindAttrByAcronym( const char * pszName )
560 
561 {
562     int         iStart, iEnd, iCandidate;
563 
564     iStart = 0;
565     iEnd = nAttrCount-1;
566 
567     while( iStart <= iEnd )
568     {
569         int     nCompareValue;
570 
571         iCandidate = (iStart + iEnd)/2;
572         nCompareValue =
573             strcmp(pszName, aoAttrInfos[anAttrIndex[iCandidate]]->osAcronym);
574 
575         if( nCompareValue < 0 )
576         {
577             iEnd = iCandidate-1;
578         }
579         else if( nCompareValue > 0 )
580         {
581             iStart = iCandidate+1;
582         }
583         else
584             return anAttrIndex[iCandidate];
585     }
586 
587     return -1;
588 }
589