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