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