Coverage Report

Created: 2025-07-11 06:33

/src/bag/api/bag_legacy_crs.cpp
Line
Count
Source (jump to first uncovered line)
1
2
#include "bag_errors.h"
3
#include "bag_exceptions.h"
4
#include "bag_legacy_crs.h"
5
6
#include <algorithm>  // std::transform
7
#include <cctype>  // std::tolower
8
#include <cmath>  // M_PI
9
#include <cstring>
10
#include <exception>  // std::exception
11
#include <fstream>  // std::ifstream
12
#include <iomanip>
13
#include <iostream>
14
#include <sstream>
15
#include <string>
16
#include <vector>
17
18
19
namespace {
20
21
//! Projection parameters WKT names.
22
const char k_latitude_of_origin[] = "latitude_of_origin";
23
const char k_central_meridian[] = "central_meridian";
24
const char k_scale_factor[] = "scale_factor";
25
const char k_false_easting[] = "false_easting";
26
const char k_false_northing[] = "false_northing";
27
const char k_standard_parallel_1[] = "standard_parallel_1";
28
const char k_standard_parallel_2[] = "standard_parallel_2";
29
const char k_latitude_of_center[] = "latitude_of_center";
30
const char k_longitude_of_center[] = "longitude_of_center";
31
32
//! Projection type WKT names.
33
const char k_albers_conic_equal_area[] = "albers_conic_equal_area";
34
const char k_azimuthal_equidistant[] = "azimuthal_equidistant";
35
const char k_bonne[] = "bonne";
36
const char k_cassini_soldner[] = "cassini_soldner";
37
const char k_cylindrical_equal_area[] = "cylindrical_equal_area";
38
const char k_eckert_iv[] = "eckert_iv";
39
const char k_eckert_vi[] = "eckert_vi";
40
const char k_equirectangular[] = "equirectangular";
41
const char k_gnomonic[] = "gnomonic";
42
const char k_lambert_conformal_conic[] = "lambert_conformal_conic_2sp";
43
const char k_mercator[] = "mercator_1sp";
44
const char k_miller_cylindrical[] = "miller_cylindrical";
45
const char k_mollweide[] = "mollweide";
46
const char k_new_zealand_map_grid[] = "new_zealand_map_grid";
47
const char k_orthographic[] = "orthographic";
48
const char k_polar_stereographic[] = "polar_stereographic";
49
const char k_polyconic[] = "polyconic";
50
const char k_sinusoidal[] = "sinusoidal";
51
const char k_oblique_stereographic[] = "oblique_stereographic";
52
const char k_transverse_mercator[] = "transverse_mercator";
53
const char k_vandergrinten[] = "vandergrinten";
54
55
//! Datum WKT names.
56
const char k_wgs84[] = "wgs_1984";
57
const char k_wgs72[] = "wgs_1972";
58
const char k_nad83[] = "north_american_datum_1983";
59
60
//! Split a string by the specified separator.
61
/*!
62
\param str
63
    The string to split.
64
\param separator
65
    The separator to split the string by.
66
    Defaults to space.
67
68
\return
69
    A list of words split by separator.
70
*/
71
std::vector<std::string> split(
72
    const std::string& str,
73
    const char separator = ' ')
74
0
{
75
0
    std::vector<std::string> tokens;
76
77
0
    const auto end = cend(str);
78
0
    auto p1 = cbegin(str);
79
0
    auto p2 = end;
80
81
0
    do
82
0
    {
83
0
        p2 = std::find(p1, end, separator);  // find next separator
84
0
        tokens.emplace_back(p1, p2);
85
86
0
        if (p2 != end)
87
0
        {   // More tokens left.
88
0
            p1 = std::find_if(p2, end, [separator](char c) -> bool {
89
0
                return c != separator;  // skip all separators
90
0
            });
91
0
        }
92
0
    } while (p2 != end);
93
94
0
    return tokens;
95
0
}
96
97
//************************************************************************
98
/*!
99
\brief Convert the given string value to double.
100
101
The string is expected to contain a decimal value in the classic locale.
102
For example something like "20.0". The classic locale uses a '.' as the
103
decimal separator.
104
105
\param value
106
    \li The string to convert.
107
\return
108
    \li The converted value.
109
*/
110
//************************************************************************
111
double toDouble(const std::string& value)
112
0
{
113
0
    std::stringstream lineStream;
114
0
115
0
    (void)lineStream.imbue(std::locale::classic());
116
0
    lineStream << value;
117
0
118
0
    double dblValue = 0.0;
119
0
    lineStream >> dblValue;
120
0
121
0
    return dblValue;
122
0
}
123
124
//************************************************************************
125
/*!
126
\brief Convert the BAG ellipsoid to WKT.
127
128
To convert the ellipsoid we need the semi-major and inverse flattening
129
ratio. We will open the ellips.dat file to retrieve this information.
130
131
\param ellipsoid
132
    \li The BAG ellipsoid type to convert.
133
\return
134
    \li The WKT representation for that ellipsoid.
135
*/
136
//************************************************************************
137
std::string ellipsoidToWkt(const char* ellipsoid)
138
0
{
139
0
    const char* onsHome = getenv("BAG_HOME");
140
0
    if (!onsHome)
141
0
        throw BAG::InvalidEllipsoidError();
142
143
    //Make the ellipsoid name all lower case so we can find it.
144
0
    std::string ellipsoidName{ellipsoid};
145
0
    std::transform(begin(ellipsoidName), end(ellipsoidName),
146
0
        begin(ellipsoidName),
147
0
        [](unsigned char c) noexcept {
148
0
            return static_cast<char>(std::tolower(c));
149
0
        });
150
151
    //Build the full path to the ellips.dat file.
152
0
    std::string ellipFile{onsHome};
153
0
    ellipFile += "/ellips.dat";
154
155
0
    std::ifstream file{ellipFile.c_str()};
156
0
    if (!file.is_open())
157
0
        throw BAG::InvalidEllipsoidError();
158
159
0
    while (file.good())
160
0
    {
161
        //Get the current line in lower case.
162
0
        std::string line;
163
0
        std::getline(file, line);
164
165
0
        std::transform(begin(line), end(line), begin(line),
166
0
            [](unsigned char c) noexcept {
167
0
                return static_cast<char>(std::tolower(c));
168
0
            });
169
170
0
        const size_t index = line.find(ellipsoidName);
171
0
        if (index != 0)
172
0
            continue;
173
174
0
        auto elements = split(line, ' ');
175
0
        const auto numItems = elements.size();
176
177
        //We MUST have at least 5 elements (name, id, a, b, if)
178
0
        if (numItems < 5)
179
0
            throw BAG::InvalidEllipsoidError();
180
181
        //The last item will be the inverse flattening.
182
0
        const double invFlat = atof(elements[numItems - 1].c_str());
183
184
        //The third to last item will be the semi-major.
185
0
        const double semiMajor = atof(elements[numItems - 3].c_str());
186
187
0
        std::stringstream wktStream;
188
0
        (void)wktStream.imbue(std::locale::classic());
189
0
        wktStream << std::fixed << std::setprecision(9) <<
190
0
            R"(SPHEROID[")" << ellipsoid << R"(",)" <<
191
0
            semiMajor << ',' << invFlat << ']';
192
193
0
        return wktStream.str();
194
0
    }
195
196
    //Couldn't find it :(
197
0
    throw BAG::InvalidEllipsoidError();
198
0
}
199
200
//************************************************************************
201
/*!
202
\brief Convert the BAG datum type to WKT.
203
204
If we are unable to convert the specified ellipsoid, we will use
205
a good default for the datum type.
206
207
\param datum
208
    \li The BAG datum type to convert.
209
\param ellipsoid
210
    \li The BAG ellipsoid to convert.
211
\return
212
    \li The WKT representation for that datum.
213
*/
214
//************************************************************************
215
std::string datumToWkt(
216
    const BAG::BagDatum datum,
217
    const char* ellipsoid)
218
0
{
219
0
    std::string ellipWkt;
220
221
    //Try to decode the ellipsoid, if we fail then we will use the 'default' ellipsoid.
222
0
    try
223
0
    {
224
        //Try to decode the ellipsoid
225
0
        ellipWkt = ellipsoidToWkt(ellipsoid);
226
0
    }
227
0
    catch (...)
228
0
    {
229
0
        switch (datum)
230
0
        {
231
0
        case BAG::BagDatum::wgs84:
232
0
            ellipWkt = R"(SPHEROID["WGS 84",6378137,298.257223563])";
233
0
            break;
234
235
0
        case BAG::BagDatum::wgs72:
236
0
            ellipWkt = R"(SPHEROID["WGS 72",6378135,298.26])";
237
0
            break;
238
239
0
        case BAG::BagDatum::nad83:
240
0
            ellipWkt = R"(SPHEROID["GRS 1980",6378137,298.257222101])";
241
0
            break;
242
243
0
        case BAG::BagDatum::unknown:
244
0
            ellipWkt = R"(SPHEROID["UNKNOWN",0,0.0])";
245
0
            break;
246
0
        }
247
0
    };
248
249
0
    std::stringstream wktStream;
250
0
    (void)wktStream.imbue(std::locale::classic());
251
252
0
    switch (datum)
253
0
    {
254
0
    case BAG::BagDatum::wgs84:
255
0
    {
256
0
        wktStream << R"(GEOGCS["WGS 84", )"
257
0
            << R"(DATUM["WGS_1984", )"
258
0
            << ellipWkt << ", "
259
0
            << "TOWGS84[0,0,0,0,0,0,0]], "
260
0
            << R"(PRIMEM["Greenwich",0], )"
261
0
            << R"(UNIT["degree",0.0174532925199433]])";
262
263
0
        return wktStream.str();
264
0
    }
265
0
    break;
266
267
0
    case BAG::BagDatum::wgs72:
268
0
    {
269
0
        wktStream << R"(GEOGCS["WGS 72", )"
270
0
            << R"(DATUM["WGS_1972", )"
271
0
            << ellipWkt << ", "
272
0
            << "TOWGS84[0,0,4.5,0,0,0.554,0.2263]], "
273
0
            << R"(PRIMEM["Greenwich",0], )"
274
0
            << R"(UNIT["degree",0.0174532925199433]])";
275
276
0
        return wktStream.str();
277
0
    }
278
0
    break;
279
280
0
    case BAG::BagDatum::nad83:
281
0
    {
282
0
        wktStream << R"(GEOGCS["NAD83", )"
283
0
            << R"(DATUM["North_American_Datum_1983", )"
284
0
            << ellipWkt << ", "
285
0
            << "TOWGS84[0,0,0,0,0,0,0]], "
286
0
            << R"(PRIMEM["Greenwich",0], )"
287
0
            << R"(UNIT["degree",0.0174532925199433]])";
288
289
0
        return wktStream.str();
290
0
    }
291
0
    break;
292
293
0
    case BAG::BagDatum::unknown:
294
0
    {
295
0
        return wktStream.str();
296
0
    }
297
0
    break;
298
299
0
    }
300
301
    //If we got here then we don't know what type of datum we have.
302
0
    throw BAG::InvalidDatumError();
303
0
}
304
305
//************************************************************************
306
/*!
307
\brief Retrieve the specified projection parameter.
308
309
\param wkt
310
    \li The wkt string containing the coordinate system definition.
311
\param paramName
312
    \li The name of the projection parameter to retrieve.
313
\return
314
    \li The specified projection parameter.
315
*/
316
//************************************************************************
317
double getProjectionParam(
318
    const std::string& wkt,
319
    const std::string& paramName)
320
0
{
321
0
    //Find the projection node in the wkt string.
322
0
    const size_t startIndex = wkt.find(paramName);
323
0
    if (startIndex == std::string::npos)
324
0
        throw BAG::CoordSysError();
325
0
326
0
    const size_t valueStartIndex = wkt.find(",", startIndex);
327
0
    if (valueStartIndex == std::string::npos)
328
0
        throw BAG::CoordSysError();
329
0
330
0
    const size_t valueEndIndex = wkt.find("]", valueStartIndex);
331
0
    if (valueEndIndex == std::string::npos)
332
0
        throw BAG::CoordSysError();
333
0
334
0
    //Extract the value
335
0
    const size_t startPos = valueStartIndex + 1;
336
0
    const size_t length = valueEndIndex - startPos;
337
0
    const std::string value = wkt.substr(startPos, length);
338
0
339
0
    return toDouble(value);
340
0
}
341
342
//************************************************************************
343
/*!
344
\brief Retrieve the BAG coordinate type from the WKT definition.
345
346
\param wkt
347
    \li The wkt string containing the coordinate system definition.
348
\return
349
    \li The BAG coordiante system type.
350
*/
351
//************************************************************************
352
BAG::CoordinateType getCoordinateType(const std::string& wkt)
353
0
{
354
0
    //Find the projection node in the wkt string.
355
0
    const size_t startIndex = wkt.find("projection[");
356
0
357
0
    //If no projection node, then we must have a Geographic system.
358
0
    if (startIndex == std::string::npos)
359
0
        return BAG::CoordinateType::Geodetic;
360
0
361
0
    const size_t endIndex = wkt.find(R"("])", startIndex);
362
0
    if (endIndex == std::string::npos)
363
0
        throw BAG::CoordSysError();
364
0
365
0
    //Extract the projection name.
366
0
    const size_t startPos = startIndex + 12;
367
0
    const size_t length = endIndex - startPos;
368
0
    const std::string projName = wkt.substr(startPos, length);
369
0
370
0
    if (projName == k_albers_conic_equal_area)
371
0
        return BAG::CoordinateType::Albers_Equal_Area_Conic;
372
0
    else if (projName == k_azimuthal_equidistant)
373
0
        return BAG::CoordinateType::Azimuthal_Equidistant;
374
0
    else if (projName == k_bonne)
375
0
        return BAG::CoordinateType::Bonne;
376
0
    else if (projName == k_cassini_soldner)
377
0
        return BAG::CoordinateType::Cassini;
378
0
    else if (projName == k_cylindrical_equal_area)
379
0
        return BAG::CoordinateType::Cylindrical_Equal_Area;
380
0
    else if (projName == k_eckert_iv)
381
0
        return BAG::CoordinateType::Eckert4;
382
0
    else if (projName == k_eckert_vi)
383
0
        return BAG::CoordinateType::Eckert6;
384
0
    else if (projName == k_equirectangular)
385
0
        return BAG::CoordinateType::Equidistant_Cylindrical;
386
0
    else if (projName == k_gnomonic)
387
0
        return BAG::CoordinateType::Gnomonic;
388
0
    else if (projName == k_lambert_conformal_conic)
389
0
        return BAG::CoordinateType::Lambert_Conformal_Conic;
390
0
    else if (projName == k_mercator)
391
0
        return BAG::CoordinateType::Mercator;
392
0
    else if (projName == k_miller_cylindrical)
393
0
        return BAG::CoordinateType::Miller_Cylindrical;
394
0
    else if (projName == k_mollweide)
395
0
        return BAG::CoordinateType::Mollweide;
396
0
    else if (projName == k_new_zealand_map_grid)
397
0
        return BAG::CoordinateType::NZMG;
398
0
    else if (projName == k_orthographic)
399
0
        return BAG::CoordinateType::Orthographic;
400
0
    else if (projName == k_polar_stereographic)
401
0
        return BAG::CoordinateType::Polar_Stereo;
402
0
    else if (projName == k_polyconic)
403
0
        return BAG::CoordinateType::Polyconic;
404
0
    else if (projName == k_sinusoidal)
405
0
        return BAG::CoordinateType::Sinusoidal;
406
0
    else if (projName == k_oblique_stereographic)
407
0
        return BAG::CoordinateType::Stereographic;
408
0
    else if (projName == k_transverse_mercator)
409
0
        return BAG::CoordinateType::Transverse_Mercator;
410
0
    else if (projName == k_vandergrinten)
411
0
        return BAG::CoordinateType::Van_der_Grinten;
412
0
413
0
    //No idea...
414
0
    throw BAG::CoordSysError();
415
0
}
416
417
//************************************************************************
418
/*!
419
\brief Retrieve the BAG datum type from the WKT definition.
420
421
\param wkt
422
    \li The wkt string containing the coordinate system definition.
423
\return
424
    \li The BAG datum type.
425
*/
426
//************************************************************************
427
BAG::BagDatum getDatumType(const std::string &wkt)
428
0
{
429
0
    //Find the horizontal datum node in the wkt string.
430
0
    const size_t startIndex = wkt.find("datum[");
431
0
    if (startIndex == std::string::npos)
432
0
        throw BAG::InvalidDatumError();
433
0
434
0
    const size_t endIndex = wkt.find(",", startIndex);
435
0
    if (endIndex == std::string::npos)
436
0
        throw BAG::InvalidDatumError();
437
0
438
0
    //Extract the horizontal datum name.
439
0
    const size_t startPos = startIndex + 7;
440
0
    const size_t length = endIndex - startPos - 1;
441
0
    const std::string hDatumName = wkt.substr(startPos, length);
442
0
443
0
    if (hDatumName == k_wgs84)
444
0
        return BAG::BagDatum::wgs84;
445
0
    else if (hDatumName == k_wgs72)
446
0
        return BAG::BagDatum::wgs72;
447
0
    else if (hDatumName == k_nad83)
448
0
        return BAG::BagDatum::nad83;
449
0
450
0
    //Unknown, so we can not convert this coordinate system.
451
0
    throw BAG::InvalidDatumError();
452
0
}
453
454
//************************************************************************
455
/*!
456
\brief Retrieve the BAG ellipsoid name from the WKT definition.
457
458
\param wkt
459
    \li The wkt string containing the coordinate system definition.
460
\return
461
    \li The ellipsoid name.
462
*/
463
//************************************************************************
464
std::string getEllipsoid(const std::string& wkt)
465
0
{
466
0
    //Find the ellipsoid node in the wkt string.
467
0
    const size_t startIndex = wkt.find("spheroid[");
468
0
    if (startIndex == std::string::npos)
469
0
        throw BAG::InvalidDatumError();
470
0
471
0
    const size_t endIndex = wkt.find(",", startIndex);
472
0
    if (endIndex == std::string::npos)
473
0
        throw BAG::InvalidDatumError();
474
0
475
0
    //Extract the ellipsoid name.
476
0
    const size_t startPos = startIndex + 10;
477
0
    const size_t length = endIndex - startPos - 1;
478
0
479
0
    return wkt.substr(startPos, length);
480
0
}
481
482
//************************************************************************
483
/*!
484
\brief Retrieve the vertical datum name from the WKT definition.
485
486
\param wkt
487
    \li The wkt string containing the vertical reference system definition.
488
\return
489
    \li The vertical datum name.
490
*/
491
//************************************************************************
492
std::string getVDatum(const std::string& wkt)
493
0
{
494
0
    //Find the vertical datum node in the wkt string.
495
0
    const size_t startIndex = wkt.find("vert_datum[");
496
0
    if (startIndex == std::string::npos)
497
0
        throw BAG::InvalidDatumError();
498
0
499
0
    const size_t endIndex = wkt.find(",", startIndex);
500
0
    if (endIndex == std::string::npos)
501
0
        throw BAG::InvalidDatumError();
502
0
503
0
    //Extract the vertical datum name.
504
0
    const size_t startPos = startIndex + 12;
505
0
    const size_t length = endIndex - startPos - 1;
506
0
507
0
    return wkt.substr(startPos, length);
508
0
}
509
510
}  // namespace
511
512
namespace BAG {
513
514
//************************************************************************
515
//      Method name:    bagLegacyToWkt()
516
//
517
//      - Initial implementation
518
//        Mike Van Duzee, 1/26/2012
519
//
520
//************************************************************************
521
/*!
522
\brief Convert a BAG coordinate system definition to wkt.
523
524
\param system
525
\li The projection parameters.
526
\param hBuffer
527
\li Modified to contain the horizontal reference system
528
in the form of a WKT string.
529
\param hBufferSize
530
\li The size of the horizontal reference system buffer.
531
\param vBuffer
532
\li Modified to contain the vertical reference system
533
in the form of a WKT string.
534
\param vBufferSize
535
\li The size of the vertical reference system buffer.
536
\return
537
\li 0 on success, else an error code.
538
*/
539
//************************************************************************
540
BagError bagLegacyToWkt(
541
    const BagLegacyReferenceSystem& system,
542
    char* hBuffer,
543
    size_t hBufferSize,
544
    char* vBuffer,
545
    size_t vBufferSize)
546
0
try
547
0
{
548
0
    std::stringstream wktStream;
549
0
    (void)wktStream.imbue(std::locale::classic());
550
551
    //If we want the vertical system then...
552
0
    if (vBuffer && vBufferSize > 0 &&
553
0
        strlen(system.geoParameters.vertical_datum) != 0)
554
0
    {
555
0
        wktStream << R"(VERT_CS[")" << system.geoParameters.vertical_datum <<
556
0
            R"(", VERT_DATUM[")" << system.geoParameters.vertical_datum <<
557
0
            R"(", 2000]])";
558
559
        //Make sure our string is not too large.
560
0
        if (wktStream.str().size() > vBufferSize)
561
0
            wktStream.str().resize(vBufferSize);
562
563
0
        strcpy(vBuffer, wktStream.str().c_str());
564
0
    }
565
566
    //If we want the horizontal system then...
567
0
    if (hBuffer && hBufferSize > 0)
568
0
    {
569
0
        switch (system.coordSys)
570
0
        {
571
0
        case CoordinateType::Geodetic:
572
0
        {
573
0
            wktStream << datumToWkt(system.geoParameters.datum,
574
0
                system.geoParameters.ellipsoid);
575
0
        }
576
0
        break;
577
578
0
        case CoordinateType::UTM:
579
0
        {
580
            //We need to figure out what hemisphere we are in.
581
0
            bool isNorth = false;
582
583
            //A false northing of 0.0 means north.
584
0
            if (system.geoParameters.false_northing == 0.0)
585
0
                isNorth = true;
586
            //A false northing of 10,000,000 means south.
587
0
            else if (system.geoParameters.false_northing == 10'000'000)
588
0
                isNorth = false;
589
            //If we don't have an appropriate false northing, then use the zone.
590
0
            else
591
0
                isNorth = (system.geoParameters.zone >= 0);
592
593
0
            wktStream << std::fixed << std::setprecision(6)
594
0
                << R"(PROJCS["UTM Zone )" << abs(system.geoParameters.zone)
595
0
                << ((isNorth) ? R"(, Northern Hemisphere")" : R"(, Southern Hemisphere")")
596
0
                << ", " << datumToWkt(system.geoParameters.datum,
597
0
                    system.geoParameters.ellipsoid)
598
0
                << R"(, PROJECTION["Transverse_Mercator"],)"
599
0
                << R"( PARAMETER["latitude_of_origin",)" << 0 << "],"
600
0
                << R"( PARAMETER["central_meridian",)" << (abs(system.geoParameters.zone) * 6 - 183) << "],"
601
0
                << R"( PARAMETER["scale_factor",)" << 0.9996 << "],"
602
0
                << R"( PARAMETER["false_easting",)" << 500'000 << "],"
603
0
                << R"( PARAMETER["false_northing",)" << ((isNorth) ? 0 : 10'000'000) << "],"
604
0
                << R"( UNIT["metre",1]])";
605
0
        }
606
0
        break;
607
608
0
        case CoordinateType::Albers_Equal_Area_Conic:
609
0
        {
610
0
            wktStream << std::fixed << std::setprecision(6)
611
0
                << R"(PROJCS["unnamed")"
612
0
                << ", " << datumToWkt(system.geoParameters.datum,
613
0
                    system.geoParameters.ellipsoid)
614
0
                << R"(, PROJECTION["Albers_Conic_Equal_Area"],)"
615
0
                << R"( PARAMETER["standard_parallel_1",)" << system.geoParameters.std_parallel_1 << "],"
616
0
                << R"( PARAMETER["standard_parallel_2",)" << system.geoParameters.std_parallel_2 << "],"
617
0
                << R"( PARAMETER["latitude_of_center",)" << system.geoParameters.latitude_of_centre << "],"
618
0
                << R"( PARAMETER["longitude_of_center",)" << system.geoParameters.longitude_of_centre << "],"
619
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
620
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
621
0
                << R"( UNIT["metre",1]])";
622
0
        }
623
0
        break;
624
625
0
        case CoordinateType::Azimuthal_Equidistant:
626
0
        {
627
0
            wktStream << std::fixed << std::setprecision(6)
628
0
                << R"(PROJCS["unnamed")"
629
0
                << ", " << datumToWkt(system.geoParameters.datum,
630
0
                    system.geoParameters.ellipsoid)
631
0
                << R"(, PROJECTION["Azimuthal_Equidistant"],)"
632
0
                << R"( PARAMETER["latitude_of_center",)" << system.geoParameters.latitude_of_centre << "],"
633
0
                << R"( PARAMETER["longitude_of_center",)" << system.geoParameters.longitude_of_centre << "],"
634
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
635
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
636
0
                << R"( UNIT["metre",1]])";
637
0
        }
638
0
        break;
639
640
0
        case CoordinateType::Bonne:
641
0
        {
642
0
            wktStream << std::fixed << std::setprecision(6)
643
0
                << R"(PROJCS["unnamed")"
644
0
                << ", " << datumToWkt(system.geoParameters.datum,
645
0
                    system.geoParameters.ellipsoid)
646
0
                << R"(, PROJECTION["Bonne"],)"
647
0
                << R"( PARAMETER["standard_parallel_1",)" << system.geoParameters.std_parallel_1 << "],"
648
0
                << R"( PARAMETER["central_meridian",)" << system.geoParameters.central_meridian << "],"
649
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
650
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
651
0
                << R"( UNIT["metre",1]])";
652
0
        }
653
0
        break;
654
655
0
        case CoordinateType::Cassini:
656
0
        {
657
0
            wktStream << std::fixed << std::setprecision(6)
658
0
                << R"(PROJCS["unnamed")"
659
0
                << ", " << datumToWkt(system.geoParameters.datum,
660
0
                    system.geoParameters.ellipsoid)
661
0
                << R"(, PROJECTION["Cassini_Soldner"],)"
662
0
                << R"( PARAMETER["latitude_of_origin",)" << system.geoParameters.origin_latitude << "],"
663
0
                << R"( PARAMETER["central_meridian",)" << system.geoParameters.central_meridian << "],"
664
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
665
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
666
0
                << R"( UNIT["metre",1]])";
667
0
        }
668
0
        break;
669
670
0
        case CoordinateType::Cylindrical_Equal_Area:
671
0
        {
672
0
            wktStream << std::fixed << std::setprecision(6)
673
0
                << R"(PROJCS["unnamed")"
674
0
                << ", " << datumToWkt(system.geoParameters.datum,
675
0
                    system.geoParameters.ellipsoid)
676
0
                << R"(, PROJECTION["Cylindrical_Equal_Area"],)"
677
0
                << R"( PARAMETER["standard_parallel_1",)" << system.geoParameters.std_parallel_1 << "],"
678
0
                << R"( PARAMETER["central_meridian",)" << system.geoParameters.central_meridian << "],"
679
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
680
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
681
0
                << R"( UNIT["metre",1]])";
682
0
        }
683
0
        break;
684
685
0
        case CoordinateType::Eckert4:
686
0
        {
687
0
            wktStream << std::fixed << std::setprecision(6)
688
0
                << R"(PROJCS["unnamed")"
689
0
                << ", " << datumToWkt(system.geoParameters.datum,
690
0
                    system.geoParameters.ellipsoid)
691
0
                << R"(, PROJECTION["Eckert_IV"],)"
692
0
                << R"( PARAMETER["central_meridian",)" << system.geoParameters.central_meridian << "],"
693
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
694
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
695
0
                << R"( UNIT["metre",1]])";
696
0
        }
697
0
        break;
698
699
0
        case CoordinateType::Eckert6:
700
0
        {
701
0
            wktStream << std::fixed << std::setprecision(6)
702
0
                << R"(PROJCS["unnamed")"
703
0
                << ", " << datumToWkt(system.geoParameters.datum,
704
0
                    system.geoParameters.ellipsoid)
705
0
                << R"(, PROJECTION["Eckert_VI"],)"
706
0
                << R"( PARAMETER["central_meridian",)" << system.geoParameters.central_meridian << "],"
707
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
708
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
709
0
                << R"( UNIT["metre",1]])";
710
0
        }
711
0
        break;
712
713
0
        case CoordinateType::Equidistant_Cylindrical:
714
0
        {
715
            //Plate Caree,  Equidistant Cylindrical, and Simple Cylindrical are all
716
            //aliases for Equirectangular.
717
718
0
            wktStream << std::fixed << std::setprecision(6)
719
0
                << R"(PROJCS["unnamed")"
720
0
                << ", " << datumToWkt(system.geoParameters.datum,
721
0
                    system.geoParameters.ellipsoid)
722
0
                << R"(, PROJECTION["Equirectangular"],)"
723
0
                << R"( PARAMETER["latitude_of_origin",)" << system.geoParameters.origin_latitude << "],"
724
0
                << R"( PARAMETER["central_meridian",)" << system.geoParameters.central_meridian << "],"
725
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
726
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
727
0
                << R"( UNIT["metre",1]])";
728
0
        }
729
0
        break;
730
731
0
        case CoordinateType::Gnomonic:
732
0
        {
733
0
            wktStream << std::fixed << std::setprecision(6)
734
0
                << R"(PROJCS["unnamed")"
735
0
                << ", " << datumToWkt(system.geoParameters.datum,
736
0
                    system.geoParameters.ellipsoid)
737
0
                << R"(, PROJECTION["Gnomonic"],)"
738
0
                << R"( PARAMETER["latitude_of_origin",)" << system.geoParameters.origin_latitude << "],"
739
0
                << R"( PARAMETER["central_meridian",)" << system.geoParameters.central_meridian << "],"
740
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
741
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
742
0
                << R"( UNIT["metre",1]])";
743
0
        }
744
0
        break;
745
746
0
        case CoordinateType::Lambert_Conformal_Conic:
747
0
        {
748
0
            wktStream << std::fixed << std::setprecision(6)
749
0
                << R"(PROJCS["unnamed")"
750
0
                << ", " << datumToWkt(system.geoParameters.datum,
751
0
                    system.geoParameters.ellipsoid)
752
0
                << R"(, PROJECTION["Lambert_Conformal_Conic_2SP"],)"
753
0
                << R"( PARAMETER["standard_parallel_1",)" << system.geoParameters.std_parallel_1 << "],"
754
0
                << R"( PARAMETER["standard_parallel_2",)" << system.geoParameters.std_parallel_2 << "],"
755
0
                << R"( PARAMETER["latitude_of_origin",)" << system.geoParameters.origin_latitude << "],"
756
0
                << R"( PARAMETER["central_meridian",)" << system.geoParameters.central_meridian << "],"
757
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
758
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
759
0
                << R"( UNIT["metre",1]])";
760
0
        }
761
0
        break;
762
763
0
        case CoordinateType::Mercator:
764
0
        {
765
0
            wktStream << std::fixed << std::setprecision(6)
766
0
                << R"(PROJCS["unnamed")"
767
0
                << ", " << datumToWkt(system.geoParameters.datum,
768
0
                    system.geoParameters.ellipsoid)
769
0
                << R"(, PROJECTION["Mercator_1SP"],)"
770
0
                << R"( PARAMETER["latitude_of_origin",)" << system.geoParameters.origin_latitude << "],"
771
0
                << R"( PARAMETER["central_meridian",)" << system.geoParameters.central_meridian << "],"
772
0
                << R"( PARAMETER["scale_factor",)" << system.geoParameters.scale_factor << "],"
773
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
774
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
775
0
                << R"( UNIT["metre",1]])";
776
0
        }
777
0
        break;
778
779
0
        case CoordinateType::Miller_Cylindrical:
780
0
        {
781
0
            wktStream << std::fixed << std::setprecision(6)
782
0
                << R"(PROJCS["unnamed")"
783
0
                << ", " << datumToWkt(system.geoParameters.datum,
784
0
                    system.geoParameters.ellipsoid)
785
0
                << R"(, PROJECTION["Miller_Cylindrical"],)"
786
0
                << R"( PARAMETER["latitude_of_center",)" << system.geoParameters.latitude_of_centre << "],"
787
0
                << R"( PARAMETER["longitude_of_center",)" << system.geoParameters.longitude_of_centre << "],"
788
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
789
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
790
0
                << R"( UNIT["metre",1]])";
791
0
        }
792
0
        break;
793
794
0
        case CoordinateType::Mollweide:
795
0
        {
796
0
            wktStream << std::fixed << std::setprecision(6)
797
0
                << R"(PROJCS["unnamed")"
798
0
                << ", " << datumToWkt(system.geoParameters.datum,
799
0
                    system.geoParameters.ellipsoid)
800
0
                << R"(, PROJECTION["Mollweide"],)"
801
0
                << R"( PARAMETER["central_meridian",)" << system.geoParameters.central_meridian << "],"
802
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
803
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
804
0
                << R"( UNIT["metre",1]])";
805
0
        }
806
0
        break;
807
808
0
        case CoordinateType::NZMG:
809
0
        {
810
0
            wktStream << std::fixed << std::setprecision(6)
811
0
                << R"(PROJCS["unnamed")"
812
0
                << ", " << datumToWkt(system.geoParameters.datum,
813
0
                    system.geoParameters.ellipsoid)
814
0
                << R"(, PROJECTION["New_Zealand_Map_Grid"],)"
815
0
                << R"( PARAMETER["latitude_of_origin",)" << system.geoParameters.origin_latitude << "],"
816
0
                << R"( PARAMETER["central_meridian",)" << system.geoParameters.central_meridian << "],"
817
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
818
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
819
0
                << R"( UNIT["metre",1]])";
820
0
        }
821
0
        break;
822
823
0
        case CoordinateType::Orthographic:
824
0
        {
825
0
            wktStream << std::fixed << std::setprecision(6)
826
0
                << R"(PROJCS["unnamed")"
827
0
                << ", " << datumToWkt(system.geoParameters.datum,
828
0
                    system.geoParameters.ellipsoid)
829
0
                << R"(, PROJECTION["Orthographic"],)"
830
0
                << R"( PARAMETER["latitude_of_origin",)" << system.geoParameters.origin_latitude << "],"
831
0
                << R"( PARAMETER["central_meridian",)" << system.geoParameters.central_meridian << "],"
832
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
833
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
834
0
                << R"( UNIT["metre",1]])";
835
0
        }
836
0
        break;
837
838
0
        case CoordinateType::Polar_Stereo:
839
0
        {
840
0
            wktStream << std::fixed << std::setprecision(6)
841
0
                << R"(PROJCS["unnamed")"
842
0
                << ", " << datumToWkt(system.geoParameters.datum,
843
0
                    system.geoParameters.ellipsoid)
844
0
                << R"(, PROJECTION["Polar_Stereographic"],)"
845
0
                << R"( PARAMETER["latitude_of_origin",)" << system.geoParameters.origin_latitude << "],"
846
0
                << R"( PARAMETER["central_meridian",)" << system.geoParameters.central_meridian << "],"
847
0
                << R"( PARAMETER["scale_factor",)" << system.geoParameters.scale_factor << "],"
848
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
849
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
850
0
                << R"( UNIT["metre",1]])";
851
0
        }
852
0
        break;
853
854
0
        case CoordinateType::Polyconic:
855
0
        {
856
0
            wktStream << std::fixed << std::setprecision(6)
857
0
                << R"(PROJCS["unnamed")"
858
0
                << ", " << datumToWkt(system.geoParameters.datum,
859
0
                    system.geoParameters.ellipsoid)
860
0
                << R"(, PROJECTION["Polyconic"],)"
861
0
                << R"( PARAMETER["latitude_of_origin",)" << system.geoParameters.origin_latitude << "],"
862
0
                << R"( PARAMETER["central_meridian",)" << system.geoParameters.central_meridian << "],"
863
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
864
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
865
0
                << R"( UNIT["metre",1]])";
866
0
        }
867
0
        break;
868
869
0
        case CoordinateType::Sinusoidal:
870
0
        {
871
0
            wktStream << std::fixed << std::setprecision(6)
872
0
                << R"(PROJCS["unnamed")"
873
0
                << ", " << datumToWkt(system.geoParameters.datum,
874
0
                    system.geoParameters.ellipsoid)
875
0
                << R"(, PROJECTION["Sinusoidal"],)"
876
0
                << R"( PARAMETER["longitude_of_center",)" << system.geoParameters.longitude_of_centre << "],"
877
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
878
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
879
0
                << R"( UNIT["metre",1]])";
880
0
        }
881
0
        break;
882
883
0
        case CoordinateType::Stereographic:
884
0
        {
885
0
            wktStream << std::fixed << std::setprecision(6)
886
0
                << R"(PROJCS["unnamed")"
887
0
                << ", " << datumToWkt(system.geoParameters.datum,
888
0
                    system.geoParameters.ellipsoid)
889
0
                << R"(, PROJECTION["Oblique_Stereographic"],)"
890
0
                << R"( PARAMETER["latitude_of_origin",)" << system.geoParameters.origin_latitude << "],"
891
0
                << R"( PARAMETER["central_meridian",)" << system.geoParameters.central_meridian << "],"
892
0
                << R"( PARAMETER["scale_factor",)" << system.geoParameters.scale_factor << "],"
893
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
894
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
895
0
                << R"( UNIT["metre",1]])";
896
0
        }
897
0
        break;
898
899
0
        case CoordinateType::Transverse_Mercator:
900
0
        {
901
0
            wktStream << std::fixed << std::setprecision(6)
902
0
                << R"(PROJCS["unnamed")"
903
0
                << ", " << datumToWkt(system.geoParameters.datum,
904
0
                    system.geoParameters.ellipsoid)
905
0
                << R"(, PROJECTION["Transverse_Mercator"],)"
906
0
                << R"( PARAMETER["latitude_of_origin",)" << system.geoParameters.origin_latitude << "],"
907
0
                << R"( PARAMETER["central_meridian",)" << system.geoParameters.central_meridian << "],"
908
0
                << R"( PARAMETER["scale_factor",)" << system.geoParameters.scale_factor << "],"
909
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
910
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
911
0
                << R"( UNIT["metre",1]])";
912
0
        }
913
0
        break;
914
915
0
        case CoordinateType::Van_der_Grinten:
916
0
        {
917
0
            wktStream << std::fixed << std::setprecision(6)
918
0
                << R"(PROJCS["unnamed")"
919
0
                << ", " << datumToWkt(system.geoParameters.datum,
920
0
                    system.geoParameters.ellipsoid)
921
0
                << R"(, PROJECTION["VanDerGrinten"],)"
922
0
                << R"( PARAMETER["central_meridian",)" << system.geoParameters.central_meridian << "],"
923
0
                << R"( PARAMETER["false_easting",)" << system.geoParameters.false_easting << "],"
924
0
                << R"( PARAMETER["false_northing",)" << system.geoParameters.false_northing << "],"
925
0
                << R"( UNIT["metre",1]])";
926
0
        }
927
0
        break;
928
929
0
        default:
930
0
        {
931
            //Currently unsupported type.
932
0
            return BAG_METADTA_INVALID_PROJECTION;
933
0
        }
934
935
0
        }
936
937
0
        if (wktStream.str().size() > hBufferSize)
938
0
            wktStream.str().resize(hBufferSize);
939
940
0
        strcpy(hBuffer, wktStream.str().c_str());
941
0
    }
942
943
0
    return 0;
944
0
}
945
0
catch (const InvalidDatumError &/*e*/)
946
0
{
947
0
    return BAG_METADTA_INVALID_DATUM;
948
0
}
949
0
catch (const std::exception &/*e*/)
950
0
{
951
    //Something bad happened.
952
0
    return BAG_METADTA_INVALID_PROJECTION;
953
0
}
954
955
956
957
958
/*  function:  bagCoordsys
959
    purpose: taken from geotrans' engine.h from their Coordinate Type
960
        Enumeration we have a matching string set.  have included all
961
        coordinate sys types here although we only are supporting
962
        0, 5, 6, 17, 18, 25, and 30 initially.
963
964
    author:  dave fabre, us naval oceanographic office, jul 2005
965
*/
966
967
static const char* COORDINATE_SYS_LIST[]=
968
{
969
    "Geodetic",
970
    "GEOREF",
971
    "Geocentric",
972
    "Local_Cartesian",
973
    "MGRS",
974
    "UTM",
975
    "UPS",
976
    "Albers_Equal_Area_Conic",
977
    "Azimuthal_Equidistant",
978
    "BNG",
979
    "Bonne",
980
    "Cassini",
981
    "Cylindrical_Equal_Area",
982
    "Eckert4",
983
    "Eckert6",
984
    "Equidistant_Cylindrical",
985
    "Gnomonic",
986
    "Lambert_Conformal_Conic",
987
    "Mercator",
988
    "Miller_Cylindrical",
989
    "Mollweide",
990
    "Neys",
991
    "NZMG",
992
    "Oblique_Mercator",
993
    "Orthographic",
994
    "Polar_Stereo",
995
    "Polyconic",
996
    "Sinusoidal",
997
    "Stereographic",
998
    "Transverse_Cylindrical_Equal_Area",
999
    "Transverse_Mercator",
1000
    "Van_der_Grinten"
1001
};
1002
1003
/******************************************************************************/
1004
CoordinateType bagCoordsys(const char* str) noexcept
1005
0
{
1006
0
    constexpr auto MAX_NCOORD_SYS = 32;
1007
0
    #define COORD_SYS_NAME(k) COORDINATE_SYS_LIST[k]
1008
1009
0
    for (size_t i = 0; i < MAX_NCOORD_SYS; i++)
1010
0
        if (strncmp(str, COORD_SYS_NAME(i), strlen(COORD_SYS_NAME(i))) == 0 )
1011
0
            return static_cast<CoordinateType>(i);
1012
1013
0
    return CoordinateType::Unknown;
1014
0
}
1015
1016
1017
1018
1019
static const char* DATUM_NAME_LIST[] =
1020
{
1021
    "WGS84",
1022
    "WGS72",
1023
    "NAD83"
1024
};
1025
1026
/******************************************************************************/
1027
BagDatum bagDatumID(const char* str) noexcept
1028
0
{
1029
0
    constexpr auto MAX_DATUMS = 3;
1030
0
    #define DATUM_NAME(k) DATUM_NAME_LIST[k]
1031
1032
0
    for (uint32_t i = 0; i < MAX_DATUMS; i++)
1033
0
        if (strncmp(str, DATUM_NAME(i), strlen(DATUM_NAME(i))) == 0)
1034
0
            return static_cast<BagDatum>(i);
1035
1036
0
    return BagDatum::unknown;
1037
0
}
1038
1039
}  // namespace BAG
1040