Coverage Report

Created: 2025-08-26 07:08

/src/PROJ/src/iso19111/c_api.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  PROJ
4
 * Purpose:  C API wrapper of C++ API
5
 * Author:   Even Rouault <even dot rouault at spatialys dot com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2018, Even Rouault <even dot rouault at spatialys dot com>
9
 *
10
 * Permission is hereby granted, free of charge, to any person obtaining a
11
 * copy of this software and associated documentation files (the "Software"),
12
 * to deal in the Software without restriction, including without limitation
13
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14
 * and/or sell copies of the Software, and to permit persons to whom the
15
 * Software is furnished to do so, subject to the following conditions:
16
 *
17
 * The above copyright notice and this permission notice shall be included
18
 * in all copies or substantial portions of the Software.
19
 *
20
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26
 * DEALINGS IN THE SOFTWARE.
27
 ****************************************************************************/
28
29
#ifndef FROM_PROJ_CPP
30
#define FROM_PROJ_CPP
31
#endif
32
33
#include <algorithm>
34
#include <cassert>
35
#include <cstdarg>
36
#include <cstring>
37
#include <limits>
38
#include <map>
39
#include <memory>
40
#include <new>
41
#include <utility>
42
#include <vector>
43
44
#include "proj/common.hpp"
45
#include "proj/coordinateoperation.hpp"
46
#include "proj/coordinates.hpp"
47
#include "proj/coordinatesystem.hpp"
48
#include "proj/crs.hpp"
49
#include "proj/datum.hpp"
50
#include "proj/io.hpp"
51
#include "proj/metadata.hpp"
52
#include "proj/util.hpp"
53
54
#include "proj/internal/datum_internal.hpp"
55
#include "proj/internal/internal.hpp"
56
#include "proj/internal/io_internal.hpp"
57
58
// PROJ include order is sensitive
59
// clang-format off
60
#include "proj.h"
61
#include "proj_internal.h"
62
#include "proj_experimental.h"
63
// clang-format on
64
#include "geodesic.h"
65
#include "proj_constants.h"
66
67
using namespace NS_PROJ::common;
68
using namespace NS_PROJ::coordinates;
69
using namespace NS_PROJ::crs;
70
using namespace NS_PROJ::cs;
71
using namespace NS_PROJ::datum;
72
using namespace NS_PROJ::io;
73
using namespace NS_PROJ::internal;
74
using namespace NS_PROJ::metadata;
75
using namespace NS_PROJ::operation;
76
using namespace NS_PROJ::util;
77
using namespace NS_PROJ;
78
79
// ---------------------------------------------------------------------------
80
81
static void PROJ_NO_INLINE proj_log_error(PJ_CONTEXT *ctx, const char *function,
82
8.47k
                                          const char *text) {
83
8.47k
    if (ctx->debug_level != PJ_LOG_NONE) {
84
8.47k
        std::string msg(function);
85
8.47k
        msg += ": ";
86
8.47k
        msg += text;
87
8.47k
        ctx->logger(ctx->logger_app_data, PJ_LOG_ERROR, msg.c_str());
88
8.47k
    }
89
8.47k
    auto previous_errno = proj_context_errno(ctx);
90
8.47k
    if (previous_errno == 0) {
91
        // only set errno if it wasn't set deeper down the call stack
92
736
        proj_context_errno_set(ctx, PROJ_ERR_OTHER);
93
736
    }
94
8.47k
}
95
96
// ---------------------------------------------------------------------------
97
98
static void PROJ_NO_INLINE proj_log_debug(PJ_CONTEXT *ctx, const char *function,
99
0
                                          const char *text) {
100
0
    std::string msg(function);
101
0
    msg += ": ";
102
0
    msg += text;
103
0
    ctx->logger(ctx->logger_app_data, PJ_LOG_DEBUG, msg.c_str());
104
0
}
105
106
// ---------------------------------------------------------------------------
107
108
//! @cond Doxygen_Suppress
109
110
// ---------------------------------------------------------------------------
111
112
0
template <class T> static PROJ_STRING_LIST to_string_list(T &&set) {
113
0
    auto ret = new char *[set.size() + 1];
114
0
    size_t i = 0;
115
0
    for (const auto &str : set) {
116
0
        try {
117
0
            ret[i] = new char[str.size() + 1];
118
0
        } catch (const std::exception &) {
119
0
            while (--i > 0) {
120
0
                delete[] ret[i];
121
0
            }
122
0
            delete[] ret;
123
0
            throw;
124
0
        }
125
0
        std::memcpy(ret[i], str.c_str(), str.size() + 1);
126
0
        i++;
127
0
    }
128
0
    ret[i] = nullptr;
129
0
    return ret;
130
0
}
Unexecuted instantiation: c_api.cpp:char** to_string_list<std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >(std::__1::vector<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&&)
Unexecuted instantiation: c_api.cpp:char** to_string_list<std::__1::list<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&>(std::__1::list<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&)
Unexecuted instantiation: c_api.cpp:char** to_string_list<std::__1::set<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >(std::__1::set<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::less<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&&)
Unexecuted instantiation: c_api.cpp:char** to_string_list<std::__1::list<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > > >(std::__1::list<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> >, std::__1::allocator<std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > > >&&)
131
132
// ---------------------------------------------------------------------------
133
134
37.5k
void proj_context_delete_cpp_context(struct projCppContext *cppContext) {
135
37.5k
    delete cppContext;
136
37.5k
}
137
// ---------------------------------------------------------------------------
138
139
projCppContext::projCppContext(PJ_CONTEXT *ctx, const char *dbPath,
140
                               const std::vector<std::string> &auxDbPaths)
141
37.5k
    : ctx_(ctx), dbPath_(dbPath ? dbPath : std::string()),
142
37.5k
      auxDbPaths_(auxDbPaths) {}
143
144
// ---------------------------------------------------------------------------
145
146
std::vector<std::string>
147
0
projCppContext::toVector(const char *const *auxDbPaths) {
148
0
    std::vector<std::string> res;
149
0
    for (auto iter = auxDbPaths; iter && *iter; ++iter) {
150
0
        res.emplace_back(std::string(*iter));
151
0
    }
152
0
    return res;
153
0
}
154
155
// ---------------------------------------------------------------------------
156
157
37.5k
projCppContext *projCppContext::clone(PJ_CONTEXT *ctx) const {
158
37.5k
    projCppContext *newContext =
159
37.5k
        new projCppContext(ctx, getDbPath().c_str(), getAuxDbPaths());
160
37.5k
    return newContext;
161
37.5k
}
162
163
// ---------------------------------------------------------------------------
164
165
137k
NS_PROJ::io::DatabaseContextNNPtr projCppContext::getDatabaseContext() {
166
137k
    if (databaseContext_) {
167
125k
        return NN_NO_CHECK(databaseContext_);
168
125k
    }
169
11.9k
    auto dbContext =
170
11.9k
        NS_PROJ::io::DatabaseContext::create(dbPath_, auxDbPaths_, ctx_);
171
11.9k
    databaseContext_ = dbContext;
172
11.9k
    return dbContext;
173
137k
}
174
175
// ---------------------------------------------------------------------------
176
177
64.2k
static PROJ_NO_INLINE DatabaseContextNNPtr getDBcontext(PJ_CONTEXT *ctx) {
178
64.2k
    return ctx->get_cpp_context()->getDatabaseContext();
179
64.2k
}
180
181
// ---------------------------------------------------------------------------
182
183
static PROJ_NO_INLINE DatabaseContextPtr
184
64.2k
getDBcontextNoException(PJ_CONTEXT *ctx, const char *function) {
185
64.2k
    try {
186
64.2k
        return getDBcontext(ctx).as_nullable();
187
64.2k
    } catch (const std::exception &e) {
188
0
        proj_log_debug(ctx, function, e.what());
189
0
        return nullptr;
190
0
    }
191
64.2k
}
192
// ---------------------------------------------------------------------------
193
194
115k
PJ *pj_obj_create(PJ_CONTEXT *ctx, const BaseObjectNNPtr &objIn) {
195
115k
    auto coordop = dynamic_cast<const CoordinateOperation *>(objIn.get());
196
115k
    if (coordop) {
197
39.7k
        auto singleOp = dynamic_cast<const SingleOperation *>(coordop);
198
39.7k
        bool bTryToExportToProj = true;
199
39.7k
        if (singleOp && singleOp->method()->nameStr() == "unnamed") {
200
            // Can happen for example when the GDAL GeoTIFF SRS builder
201
            // creates a dummy conversion when building the SRS, before setting
202
            // the final map projection. This avoids exportToPROJString() from
203
            // throwing an exception.
204
0
            bTryToExportToProj = false;
205
0
        }
206
39.7k
        if (bTryToExportToProj) {
207
39.7k
            auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
208
39.7k
            try {
209
39.7k
                auto formatter = PROJStringFormatter::create(
210
39.7k
                    PROJStringFormatter::Convention::PROJ_5,
211
39.7k
                    std::move(dbContext));
212
39.7k
                auto projString = coordop->exportToPROJString(formatter.get());
213
39.7k
                const bool defer_grid_opening_backup = ctx->defer_grid_opening;
214
39.7k
                if (!defer_grid_opening_backup &&
215
39.7k
                    proj_context_is_network_enabled(ctx)) {
216
0
                    ctx->defer_grid_opening = true;
217
0
                }
218
39.7k
                auto pj = pj_create_internal(ctx, projString.c_str());
219
39.7k
                ctx->defer_grid_opening = defer_grid_opening_backup;
220
39.7k
                if (pj) {
221
14.9k
                    pj->iso_obj = objIn;
222
14.9k
                    pj->iso_obj_is_coordinate_operation = true;
223
14.9k
                    auto sourceEpoch = coordop->sourceCoordinateEpoch();
224
14.9k
                    auto targetEpoch = coordop->targetCoordinateEpoch();
225
14.9k
                    if (sourceEpoch.has_value()) {
226
24
                        if (!targetEpoch.has_value()) {
227
24
                            pj->hasCoordinateEpoch = true;
228
24
                            pj->coordinateEpoch =
229
24
                                sourceEpoch->coordinateEpoch().convertToUnit(
230
24
                                    common::UnitOfMeasure::YEAR);
231
24
                        }
232
14.9k
                    } else {
233
14.9k
                        if (targetEpoch.has_value()) {
234
25
                            pj->hasCoordinateEpoch = true;
235
25
                            pj->coordinateEpoch =
236
25
                                targetEpoch->coordinateEpoch().convertToUnit(
237
25
                                    common::UnitOfMeasure::YEAR);
238
25
                        }
239
14.9k
                    }
240
14.9k
                    return pj;
241
14.9k
                }
242
39.7k
            } catch (const std::exception &) {
243
                // Silence, since we may not always be able to export as a
244
                // PROJ string.
245
423
            }
246
39.7k
        }
247
39.7k
    }
248
100k
    auto pj = pj_new();
249
100k
    if (pj) {
250
100k
        pj->ctx = ctx;
251
100k
        pj->descr = "ISO-19111 object";
252
100k
        pj->iso_obj = objIn;
253
100k
        pj->iso_obj_is_coordinate_operation = coordop != nullptr;
254
100k
        try {
255
100k
            auto crs = dynamic_cast<const CRS *>(objIn.get());
256
100k
            if (crs) {
257
63.8k
                auto geodCRS = crs->extractGeodeticCRS();
258
63.8k
                if (geodCRS) {
259
63.4k
                    const auto &ellps = geodCRS->ellipsoid();
260
63.4k
                    const double a = ellps->semiMajorAxis().getSIValue();
261
63.4k
                    const double es = ellps->squaredEccentricity();
262
63.4k
                    if (!(a > 0 && es >= 0 && es < 1)) {
263
29
                        proj_log_error(pj, _("Invalid ellipsoid parameters"));
264
29
                        proj_errno_set(pj,
265
29
                                       PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
266
29
                        proj_destroy(pj);
267
29
                        return nullptr;
268
29
                    }
269
63.3k
                    pj_calc_ellipsoid_params(pj, a, es);
270
63.3k
                    assert(pj->geod == nullptr);
271
63.3k
                    pj->geod = static_cast<struct geod_geodesic *>(
272
63.3k
                        calloc(1, sizeof(struct geod_geodesic)));
273
63.3k
                    if (pj->geod) {
274
63.3k
                        geod_init(pj->geod, pj->a,
275
63.3k
                                  pj->es / (1 + sqrt(pj->one_es)));
276
63.3k
                    }
277
63.3k
                }
278
63.8k
            }
279
100k
        } catch (const std::exception &) {
280
0
        }
281
100k
    }
282
100k
    return pj;
283
100k
}
284
//! @endcond
285
286
// ---------------------------------------------------------------------------
287
288
/** \brief Opaque object representing a set of operation results. */
289
struct PJ_OBJ_LIST {
290
    //! @cond Doxygen_Suppress
291
    std::vector<IdentifiedObjectNNPtr> objects;
292
293
    explicit PJ_OBJ_LIST(std::vector<IdentifiedObjectNNPtr> &&objectsIn)
294
7.98k
        : objects(std::move(objectsIn)) {}
295
    virtual ~PJ_OBJ_LIST();
296
297
    PJ_OBJ_LIST(const PJ_OBJ_LIST &) = delete;
298
    PJ_OBJ_LIST &operator=(const PJ_OBJ_LIST &) = delete;
299
    //! @endcond
300
};
301
302
//! @cond Doxygen_Suppress
303
7.98k
PJ_OBJ_LIST::~PJ_OBJ_LIST() = default;
304
//! @endcond
305
306
// ---------------------------------------------------------------------------
307
308
//! @cond Doxygen_Suppress
309
310
#define SANITIZE_CTX(ctx)                                                      \
311
188k
    do {                                                                       \
312
188k
        if (ctx == nullptr) {                                                  \
313
0
            ctx = pj_get_default_ctx();                                        \
314
0
        }                                                                      \
315
188k
    } while (0)
316
317
//! @endcond
318
319
// ---------------------------------------------------------------------------
320
321
/** \brief Starting with PROJ 8.1, this function does nothing.
322
 *
323
 * If you want to take into account changes to the PROJ database, you need to
324
 * re-create a new context.
325
 *
326
 * @param ctx Ignored
327
 * @param autoclose Ignored
328
 * @since 6.2
329
 * deprecated Since 8.1
330
 */
331
0
void proj_context_set_autoclose_database(PJ_CONTEXT *ctx, int autoclose) {
332
0
    (void)ctx;
333
0
    (void)autoclose;
334
0
}
335
336
// ---------------------------------------------------------------------------
337
338
/** \brief Explicitly point to the main PROJ CRS and coordinate operation
339
 * definition database ("proj.db"), and potentially auxiliary databases with
340
 * same structure.
341
 *
342
 * Starting with PROJ 8.1, if the auxDbPaths parameter is an empty array,
343
 * the PROJ_AUX_DB environment variable will be used, if set.
344
 * It must contain one or several paths. If several paths are
345
 * provided, they must be separated by the colon (:) character on Unix, and
346
 * on Windows, by the semi-colon (;) character.
347
 *
348
 * @param ctx PROJ context, or NULL for default context
349
 * @param dbPath Path to main database, or NULL for default.
350
 * @param auxDbPaths NULL-terminated list of auxiliary database filenames, or
351
 * NULL.
352
 * @param options should be set to NULL for now
353
 * @return TRUE in case of success
354
 */
355
int proj_context_set_database_path(PJ_CONTEXT *ctx, const char *dbPath,
356
                                   const char *const *auxDbPaths,
357
0
                                   const char *const *options) {
358
0
    SANITIZE_CTX(ctx);
359
0
    (void)options;
360
0
    std::string osPrevDbPath;
361
0
    std::vector<std::string> osPrevAuxDbPaths;
362
0
    if (ctx->cpp_context) {
363
0
        osPrevDbPath = ctx->cpp_context->getDbPath();
364
0
        osPrevAuxDbPaths = ctx->cpp_context->getAuxDbPaths();
365
0
    }
366
0
    delete ctx->cpp_context;
367
0
    ctx->cpp_context = nullptr;
368
0
    try {
369
0
        ctx->cpp_context = new projCppContext(
370
0
            ctx, dbPath, projCppContext::toVector(auxDbPaths));
371
0
        ctx->cpp_context->getDatabaseContext();
372
0
        return true;
373
0
    } catch (const std::exception &e) {
374
0
        proj_log_error(ctx, __FUNCTION__, e.what());
375
0
        delete ctx->cpp_context;
376
0
        ctx->cpp_context =
377
0
            new projCppContext(ctx, osPrevDbPath.c_str(), osPrevAuxDbPaths);
378
0
        return false;
379
0
    }
380
0
}
381
382
// ---------------------------------------------------------------------------
383
384
/** \brief Returns the path to the database.
385
 *
386
 * The returned pointer remains valid while ctx is valid, and until
387
 * proj_context_set_database_path() is called.
388
 *
389
 * @param ctx PROJ context, or NULL for default context
390
 * @return path, or nullptr
391
 */
392
0
const char *proj_context_get_database_path(PJ_CONTEXT *ctx) {
393
0
    SANITIZE_CTX(ctx);
394
0
    try {
395
        // temporary variable must be used as getDBcontext() might create
396
        // ctx->cpp_context
397
0
        const std::string osPath(getDBcontext(ctx)->getPath());
398
0
        ctx->get_cpp_context()->lastDbPath_ = osPath;
399
0
        return ctx->cpp_context->lastDbPath_.c_str();
400
0
    } catch (const std::exception &e) {
401
0
        proj_log_error(ctx, __FUNCTION__, e.what());
402
0
        return nullptr;
403
0
    }
404
0
}
405
406
// ---------------------------------------------------------------------------
407
408
/** \brief Return a metadata from the database.
409
 *
410
 * The returned pointer remains valid while ctx is valid, and until
411
 * proj_context_get_database_metadata() is called.
412
 *
413
 * Available keys:
414
 *
415
 * - DATABASE.LAYOUT.VERSION.MAJOR
416
 * - DATABASE.LAYOUT.VERSION.MINOR
417
 * - EPSG.VERSION
418
 * - EPSG.DATE
419
 * - ESRI.VERSION
420
 * - ESRI.DATE
421
 * - IGNF.SOURCE
422
 * - IGNF.VERSION
423
 * - IGNF.DATE
424
 * - NKG.SOURCE
425
 * - NKG.VERSION
426
 * - NKG.DATE
427
 * - PROJ.VERSION
428
 * - PROJ_DATA.VERSION : PROJ-data version most compatible with this database.
429
 *
430
 *
431
 * @param ctx PROJ context, or NULL for default context
432
 * @param key Metadata key. Must not be NULL
433
 * @return value, or nullptr
434
 */
435
const char *proj_context_get_database_metadata(PJ_CONTEXT *ctx,
436
0
                                               const char *key) {
437
0
    SANITIZE_CTX(ctx);
438
0
    if (!key) {
439
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
440
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
441
0
        return nullptr;
442
0
    }
443
0
    try {
444
        // temporary variable must be used as getDBcontext() might create
445
        // ctx->cpp_context
446
0
        auto osVal(getDBcontext(ctx)->getMetadata(key));
447
0
        if (osVal == nullptr) {
448
0
            return nullptr;
449
0
        }
450
0
        ctx->get_cpp_context()->lastDbMetadataItem_ = osVal;
451
0
        return ctx->cpp_context->lastDbMetadataItem_.c_str();
452
0
    } catch (const std::exception &e) {
453
0
        proj_log_error(ctx, __FUNCTION__, e.what());
454
0
        return nullptr;
455
0
    }
456
0
}
457
458
// ---------------------------------------------------------------------------
459
460
/** \brief Return the database structure
461
 *
462
 * Return SQL statements to run to initiate a new valid auxiliary empty
463
 * database. It contains definitions of tables, views and triggers, as well
464
 * as metadata for the version of the layout of the database.
465
 *
466
 * @param ctx PROJ context, or NULL for default context
467
 * @param options null-terminated list of options, or NULL. None currently.
468
 * @return list of SQL statements (to be freed with proj_string_list_destroy()),
469
 *         or NULL in case of error.
470
 * @since 8.1
471
 */
472
PROJ_STRING_LIST
473
proj_context_get_database_structure(PJ_CONTEXT *ctx,
474
0
                                    const char *const *options) {
475
0
    SANITIZE_CTX(ctx);
476
0
    (void)options;
477
0
    try {
478
0
        auto ret = to_string_list(getDBcontext(ctx)->getDatabaseStructure());
479
0
        return ret;
480
0
    } catch (const std::exception &e) {
481
0
        proj_log_error(ctx, __FUNCTION__, e.what());
482
0
        return nullptr;
483
0
    }
484
0
}
485
486
// ---------------------------------------------------------------------------
487
488
/** \brief Guess the "dialect" of the WKT string.
489
 *
490
 * @param ctx PROJ context, or NULL for default context
491
 * @param wkt String (must not be NULL)
492
 */
493
PJ_GUESSED_WKT_DIALECT proj_context_guess_wkt_dialect(PJ_CONTEXT *ctx,
494
0
                                                      const char *wkt) {
495
0
    (void)ctx;
496
0
    if (!wkt) {
497
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
498
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
499
0
        return PJ_GUESSED_NOT_WKT;
500
0
    }
501
0
    switch (WKTParser().guessDialect(wkt)) {
502
0
    case WKTParser::WKTGuessedDialect::WKT2_2019:
503
0
        return PJ_GUESSED_WKT2_2019;
504
0
    case WKTParser::WKTGuessedDialect::WKT2_2015:
505
0
        return PJ_GUESSED_WKT2_2015;
506
0
    case WKTParser::WKTGuessedDialect::WKT1_GDAL:
507
0
        return PJ_GUESSED_WKT1_GDAL;
508
0
    case WKTParser::WKTGuessedDialect::WKT1_ESRI:
509
0
        return PJ_GUESSED_WKT1_ESRI;
510
0
    case WKTParser::WKTGuessedDialect::NOT_WKT:
511
0
        break;
512
0
    }
513
0
    return PJ_GUESSED_NOT_WKT;
514
0
}
515
516
// ---------------------------------------------------------------------------
517
518
//! @cond Doxygen_Suppress
519
static const char *getOptionValue(const char *option,
520
0
                                  const char *keyWithEqual) noexcept {
521
0
    if (ci_starts_with(option, keyWithEqual)) {
522
0
        return option + strlen(keyWithEqual);
523
0
    }
524
0
    return nullptr;
525
0
}
526
//! @endcond
527
528
// ---------------------------------------------------------------------------
529
530
/** \brief "Clone" an object.
531
 *
532
 * The object might be used independently of the original object, provided that
533
 * the use of context is compatible. In particular if you intend to use a
534
 * clone in a different thread than the original object, you should pass a
535
 * context that is different from the one of the original object (or later
536
 * assign a different context with proj_assign_context()).
537
 *
538
 * The returned object must be unreferenced with proj_destroy() after use.
539
 * It should be used by at most one thread at a time.
540
 *
541
 * @param ctx PROJ context, or NULL for default context
542
 * @param obj Object to clone. Must not be NULL.
543
 * @return Object that must be unreferenced with proj_destroy(), or NULL in
544
 * case of error.
545
 */
546
20.0k
PJ *proj_clone(PJ_CONTEXT *ctx, const PJ *obj) {
547
20.0k
    SANITIZE_CTX(ctx);
548
20.0k
    if (!obj) {
549
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
550
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
551
0
        return nullptr;
552
0
    }
553
20.0k
    if (!obj->iso_obj) {
554
0
        if (!obj->alternativeCoordinateOperations.empty()) {
555
0
            auto newPj = pj_new();
556
0
            if (newPj) {
557
0
                newPj->descr = "Set of coordinate operations";
558
0
                newPj->ctx = ctx;
559
0
                newPj->copyStateFrom(*obj);
560
0
                const int old_debug_level = ctx->debug_level;
561
0
                ctx->debug_level = PJ_LOG_NONE;
562
0
                for (const auto &altOp : obj->alternativeCoordinateOperations) {
563
0
                    newPj->alternativeCoordinateOperations.emplace_back(
564
0
                        PJCoordOperation(ctx, altOp));
565
0
                }
566
0
                ctx->debug_level = old_debug_level;
567
0
            }
568
0
            return newPj;
569
0
        }
570
0
        return nullptr;
571
0
    }
572
20.0k
    try {
573
20.0k
        PJ *newPj = pj_obj_create(ctx, NN_NO_CHECK(obj->iso_obj));
574
20.0k
        if (newPj) {
575
20.0k
            newPj->copyStateFrom(*obj);
576
20.0k
        }
577
20.0k
        return newPj;
578
20.0k
    } catch (const std::exception &e) {
579
0
        proj_log_error(ctx, __FUNCTION__, e.what());
580
0
    }
581
0
    return nullptr;
582
20.0k
}
583
584
// ---------------------------------------------------------------------------
585
586
/** \brief Instantiate an object from a WKT string, PROJ string, object code
587
 * (like "EPSG:4326", "urn:ogc:def:crs:EPSG::4326",
588
 * "urn:ogc:def:coordinateOperation:EPSG::1671"), a PROJJSON string, an object
589
 * name (e.g "WGS 84") of a compound CRS build from object names
590
 * (e.g "WGS 84 + EGM96 height")
591
 *
592
 * This function calls osgeo::proj::io::createFromUserInput()
593
 *
594
 * The returned object must be unreferenced with proj_destroy() after use.
595
 * It should be used by at most one thread at a time.
596
 *
597
 * @param ctx PROJ context, or NULL for default context
598
 * @param text String (must not be NULL)
599
 * @return Object that must be unreferenced with proj_destroy(), or NULL in
600
 * case of error.
601
 */
602
23.2k
PJ *proj_create(PJ_CONTEXT *ctx, const char *text) {
603
23.2k
    SANITIZE_CTX(ctx);
604
23.2k
    if (!text) {
605
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
606
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
607
0
        return nullptr;
608
0
    }
609
610
    // Only connect to proj.db if needed
611
23.2k
    if (strstr(text, "proj=") == nullptr || strstr(text, "init=") != nullptr) {
612
8.40k
        getDBcontextNoException(ctx, __FUNCTION__);
613
8.40k
    }
614
23.2k
    try {
615
23.2k
        auto obj =
616
23.2k
            nn_dynamic_pointer_cast<BaseObject>(createFromUserInput(text, ctx));
617
23.2k
        if (obj) {
618
15.5k
            return pj_obj_create(ctx, NN_NO_CHECK(obj));
619
15.5k
        }
620
23.2k
    } catch (const io::ParsingException &e) {
621
7.39k
        if (proj_context_errno(ctx) == 0) {
622
1.76k
            proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_WRONG_SYNTAX);
623
1.76k
        }
624
7.39k
        proj_log_error(ctx, __FUNCTION__, e.what());
625
7.39k
    } catch (const NoSuchAuthorityCodeException &e) {
626
100
        proj_log_error(ctx, __FUNCTION__,
627
100
                       std::string(e.what())
628
100
                           .append(": ")
629
100
                           .append(e.getAuthority())
630
100
                           .append(":")
631
100
                           .append(e.getAuthorityCode())
632
100
                           .c_str());
633
176
    } catch (const std::exception &e) {
634
176
        proj_log_error(ctx, __FUNCTION__, e.what());
635
176
    }
636
7.66k
    return nullptr;
637
23.2k
}
638
639
// ---------------------------------------------------------------------------
640
641
/** \brief Instantiate an object from a WKT string.
642
 *
643
 * This function calls osgeo::proj::io::WKTParser::createFromWKT()
644
 *
645
 * The returned object must be unreferenced with proj_destroy() after use.
646
 * It should be used by at most one thread at a time.
647
 *
648
 * The distinction between warnings and grammar errors is somewhat artificial
649
 * and does not tell much about the real criticity of the non-compliance.
650
 * Some warnings may be more concerning than some grammar errors. Human
651
 * expertise (or, by the time this comment will be read, specialized AI) is
652
 * generally needed to perform that assessment.
653
 *
654
 * @param ctx PROJ context, or NULL for default context
655
 * @param wkt WKT string (must not be NULL)
656
 * @param options null-terminated list of options, or NULL. Currently
657
 * supported options are:
658
 * <ul>
659
 * <li>STRICT=YES/NO. Defaults to NO. When set to YES, strict validation will
660
 * be enabled.</li>
661
 * <li>UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF=YES/NO. Defaults to YES.
662
 *     When set to YES, object identifiers are unset when there is
663
 *     a contradiction between the definition from WKT and the one from
664
 *     the database./<li>
665
 * </ul>
666
 * @param out_warnings Pointer to a PROJ_STRING_LIST object, or NULL.
667
 * If provided, *out_warnings will contain a list of warnings, typically for
668
 * non recognized projection method or parameters, or other issues found during
669
 * WKT analys. It must be freed with proj_string_list_destroy().
670
 * @param out_grammar_errors Pointer to a PROJ_STRING_LIST object, or NULL.
671
 * If provided, *out_grammar_errors will contain a list of errors regarding the
672
 * WKT grammar. It must be freed with proj_string_list_destroy().
673
 * @return Object that must be unreferenced with proj_destroy(), or NULL in
674
 * case of error.
675
 */
676
PJ *proj_create_from_wkt(PJ_CONTEXT *ctx, const char *wkt,
677
                         const char *const *options,
678
                         PROJ_STRING_LIST *out_warnings,
679
0
                         PROJ_STRING_LIST *out_grammar_errors) {
680
0
    SANITIZE_CTX(ctx);
681
0
    if (!wkt) {
682
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
683
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
684
0
        return nullptr;
685
0
    }
686
687
0
    if (out_warnings) {
688
0
        *out_warnings = nullptr;
689
0
    }
690
0
    if (out_grammar_errors) {
691
0
        *out_grammar_errors = nullptr;
692
0
    }
693
694
0
    try {
695
0
        WKTParser parser;
696
0
        auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
697
0
        if (dbContext) {
698
0
            parser.attachDatabaseContext(NN_NO_CHECK(dbContext));
699
0
        }
700
0
        parser.setStrict(false);
701
0
        for (auto iter = options; iter && iter[0]; ++iter) {
702
0
            const char *value;
703
0
            if ((value = getOptionValue(*iter, "STRICT="))) {
704
0
                parser.setStrict(ci_equal(value, "YES"));
705
0
            } else if ((value = getOptionValue(
706
0
                            *iter, "UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF="))) {
707
0
                parser.setUnsetIdentifiersIfIncompatibleDef(
708
0
                    ci_equal(value, "YES"));
709
0
            } else {
710
0
                std::string msg("Unknown option :");
711
0
                msg += *iter;
712
0
                proj_log_error(ctx, __FUNCTION__, msg.c_str());
713
0
                return nullptr;
714
0
            }
715
0
        }
716
0
        auto obj = parser.createFromWKT(wkt);
717
718
0
        if (out_grammar_errors) {
719
0
            auto grammarErrors = parser.grammarErrorList();
720
0
            if (!grammarErrors.empty()) {
721
0
                *out_grammar_errors = to_string_list(grammarErrors);
722
0
            }
723
0
        }
724
725
0
        if (out_warnings) {
726
0
            auto warnings = parser.warningList();
727
0
            auto derivedCRS = dynamic_cast<const crs::DerivedCRS *>(obj.get());
728
0
            if (derivedCRS) {
729
0
                auto extraWarnings =
730
0
                    derivedCRS->derivingConversionRef()->validateParameters();
731
0
                warnings.insert(warnings.end(), extraWarnings.begin(),
732
0
                                extraWarnings.end());
733
0
            } else {
734
0
                auto singleOp =
735
0
                    dynamic_cast<const operation::SingleOperation *>(obj.get());
736
0
                if (singleOp) {
737
0
                    auto extraWarnings = singleOp->validateParameters();
738
0
                    warnings.insert(warnings.end(), extraWarnings.begin(),
739
0
                                    extraWarnings.end());
740
0
                }
741
0
            }
742
0
            if (!warnings.empty()) {
743
0
                *out_warnings = to_string_list(warnings);
744
0
            }
745
0
        }
746
747
0
        return pj_obj_create(ctx, NN_NO_CHECK(obj));
748
0
    } catch (const std::exception &e) {
749
0
        if (out_grammar_errors) {
750
0
            std::list<std::string> exc{e.what()};
751
0
            try {
752
0
                *out_grammar_errors = to_string_list(exc);
753
0
            } catch (const std::exception &) {
754
0
                proj_log_error(ctx, __FUNCTION__, e.what());
755
0
            }
756
0
        } else {
757
0
            proj_log_error(ctx, __FUNCTION__, e.what());
758
0
        }
759
0
    }
760
0
    return nullptr;
761
0
}
762
763
// ---------------------------------------------------------------------------
764
765
/** \brief Instantiate an object from a database lookup.
766
 *
767
 * The returned object must be unreferenced with proj_destroy() after use.
768
 * It should be used by at most one thread at a time.
769
 *
770
 * @param ctx Context, or NULL for default context.
771
 * @param auth_name Authority name (must not be NULL)
772
 * @param code Object code (must not be NULL)
773
 * @param category Object category
774
 * @param usePROJAlternativeGridNames Whether PROJ alternative grid names
775
 * should be substituted to the official grid names. Only used on
776
 * transformations
777
 * @param options should be set to NULL for now
778
 * @return Object that must be unreferenced with proj_destroy(), or NULL in
779
 * case of error.
780
 */
781
PJ *proj_create_from_database(PJ_CONTEXT *ctx, const char *auth_name,
782
                              const char *code, PJ_CATEGORY category,
783
                              int usePROJAlternativeGridNames,
784
0
                              const char *const *options) {
785
0
    SANITIZE_CTX(ctx);
786
0
    if (!auth_name || !code) {
787
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
788
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
789
0
        return nullptr;
790
0
    }
791
0
    (void)options;
792
0
    try {
793
0
        const std::string codeStr(code);
794
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx), auth_name);
795
0
        IdentifiedObjectPtr obj;
796
0
        switch (category) {
797
0
        case PJ_CATEGORY_ELLIPSOID:
798
0
            obj = factory->createEllipsoid(codeStr).as_nullable();
799
0
            break;
800
0
        case PJ_CATEGORY_PRIME_MERIDIAN:
801
0
            obj = factory->createPrimeMeridian(codeStr).as_nullable();
802
0
            break;
803
0
        case PJ_CATEGORY_DATUM:
804
0
            obj = factory->createDatum(codeStr).as_nullable();
805
0
            break;
806
0
        case PJ_CATEGORY_CRS:
807
0
            obj =
808
0
                factory->createCoordinateReferenceSystem(codeStr).as_nullable();
809
0
            break;
810
0
        case PJ_CATEGORY_COORDINATE_OPERATION:
811
0
            obj = factory
812
0
                      ->createCoordinateOperation(
813
0
                          codeStr, usePROJAlternativeGridNames != 0)
814
0
                      .as_nullable();
815
0
            break;
816
0
        case PJ_CATEGORY_DATUM_ENSEMBLE:
817
0
            obj = factory->createDatumEnsemble(codeStr).as_nullable();
818
0
            break;
819
0
        }
820
0
        return pj_obj_create(ctx, NN_NO_CHECK(obj));
821
0
    } catch (const NoSuchAuthorityCodeException &e) {
822
0
        proj_log_error(ctx, __FUNCTION__,
823
0
                       std::string(e.what())
824
0
                           .append(": ")
825
0
                           .append(e.getAuthority())
826
0
                           .append(":")
827
0
                           .append(e.getAuthorityCode())
828
0
                           .c_str());
829
0
    } catch (const std::exception &e) {
830
0
        proj_log_error(ctx, __FUNCTION__, e.what());
831
0
    }
832
0
    return nullptr;
833
0
}
834
835
// ---------------------------------------------------------------------------
836
837
//! @cond Doxygen_Suppress
838
static const char *get_unit_category(const std::string &unit_name,
839
0
                                     UnitOfMeasure::Type type) {
840
0
    const char *ret = nullptr;
841
0
    switch (type) {
842
0
    case UnitOfMeasure::Type::UNKNOWN:
843
0
        ret = "unknown";
844
0
        break;
845
0
    case UnitOfMeasure::Type::NONE:
846
0
        ret = "none";
847
0
        break;
848
0
    case UnitOfMeasure::Type::ANGULAR:
849
0
        ret = unit_name.find(" per ") != std::string::npos ? "angular_per_time"
850
0
                                                           : "angular";
851
0
        break;
852
0
    case UnitOfMeasure::Type::LINEAR:
853
0
        ret = unit_name.find(" per ") != std::string::npos ? "linear_per_time"
854
0
                                                           : "linear";
855
0
        break;
856
0
    case UnitOfMeasure::Type::SCALE:
857
0
        ret = unit_name.find(" per year") != std::string::npos ||
858
0
                      unit_name.find(" per second") != std::string::npos
859
0
                  ? "scale_per_time"
860
0
                  : "scale";
861
0
        break;
862
0
    case UnitOfMeasure::Type::TIME:
863
0
        ret = "time";
864
0
        break;
865
0
    case UnitOfMeasure::Type::PARAMETRIC:
866
0
        ret = unit_name.find(" per ") != std::string::npos
867
0
                  ? "parametric_per_time"
868
0
                  : "parametric";
869
0
        break;
870
0
    }
871
0
    return ret;
872
0
}
873
//! @endcond
874
875
// ---------------------------------------------------------------------------
876
877
/** \brief Get information for a unit of measure from a database lookup.
878
 *
879
 * @param ctx Context, or NULL for default context.
880
 * @param auth_name Authority name (must not be NULL)
881
 * @param code Unit of measure code (must not be NULL)
882
 * @param out_name Pointer to a string value to store the parameter name. or
883
 * NULL. This value remains valid until the next call to
884
 * proj_uom_get_info_from_database() or the context destruction.
885
 * @param out_conv_factor Pointer to a value to store the conversion
886
 * factor of the prime meridian longitude unit to radian. or NULL
887
 * @param out_category Pointer to a string value to store the parameter name. or
888
 * NULL. This value might be "unknown", "none", "linear", "linear_per_time",
889
 * "angular", "angular_per_time", "scale", "scale_per_time", "time",
890
 * "parametric" or "parametric_per_time"
891
 * @return TRUE in case of success
892
 */
893
int proj_uom_get_info_from_database(PJ_CONTEXT *ctx, const char *auth_name,
894
                                    const char *code, const char **out_name,
895
                                    double *out_conv_factor,
896
0
                                    const char **out_category) {
897
898
0
    SANITIZE_CTX(ctx);
899
0
    if (!auth_name || !code) {
900
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
901
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
902
0
        return false;
903
0
    }
904
0
    try {
905
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx), auth_name);
906
0
        auto obj = factory->createUnitOfMeasure(code);
907
0
        if (out_name) {
908
0
            ctx->get_cpp_context()->lastUOMName_ = obj->name();
909
0
            *out_name = ctx->cpp_context->lastUOMName_.c_str();
910
0
        }
911
0
        if (out_conv_factor) {
912
0
            *out_conv_factor = obj->conversionToSI();
913
0
        }
914
0
        if (out_category) {
915
0
            *out_category = get_unit_category(obj->name(), obj->type());
916
0
        }
917
0
        return true;
918
0
    } catch (const std::exception &e) {
919
0
        proj_log_error(ctx, __FUNCTION__, e.what());
920
0
    }
921
0
    return false;
922
0
}
923
924
// ---------------------------------------------------------------------------
925
926
/** \brief Get information for a grid from a database lookup.
927
 *
928
 * @param ctx Context, or NULL for default context.
929
 * @param grid_name Grid name (must not be NULL)
930
 * @param out_full_name Pointer to a string value to store the grid full
931
 * filename. or NULL
932
 * @param out_package_name Pointer to a string value to store the package name
933
 * where
934
 * the grid might be found. or NULL
935
 * @param out_url Pointer to a string value to store the grid URL or the
936
 * package URL where the grid might be found. or NULL
937
 * @param out_direct_download Pointer to a int (boolean) value to store whether
938
 * *out_url can be downloaded directly. or NULL
939
 * @param out_open_license Pointer to a int (boolean) value to store whether
940
 * the grid is released with an open license. or NULL
941
 * @param out_available Pointer to a int (boolean) value to store whether the
942
 * grid is available at runtime. or NULL
943
 * @return TRUE in case of success.
944
 */
945
int proj_grid_get_info_from_database(
946
    PJ_CONTEXT *ctx, const char *grid_name, const char **out_full_name,
947
    const char **out_package_name, const char **out_url,
948
0
    int *out_direct_download, int *out_open_license, int *out_available) {
949
0
    SANITIZE_CTX(ctx);
950
0
    if (!grid_name) {
951
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
952
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
953
0
        return false;
954
0
    }
955
0
    try {
956
0
        auto db_context = getDBcontext(ctx);
957
0
        bool direct_download;
958
0
        bool open_license;
959
0
        bool available;
960
0
        if (!db_context->lookForGridInfo(
961
0
                grid_name, false, ctx->get_cpp_context()->lastGridFullName_,
962
0
                ctx->get_cpp_context()->lastGridPackageName_,
963
0
                ctx->get_cpp_context()->lastGridUrl_, direct_download,
964
0
                open_license, available)) {
965
0
            return false;
966
0
        }
967
968
0
        if (out_full_name)
969
0
            *out_full_name = ctx->get_cpp_context()->lastGridFullName_.c_str();
970
0
        if (out_package_name)
971
0
            *out_package_name =
972
0
                ctx->get_cpp_context()->lastGridPackageName_.c_str();
973
0
        if (out_url)
974
0
            *out_url = ctx->get_cpp_context()->lastGridUrl_.c_str();
975
0
        if (out_direct_download)
976
0
            *out_direct_download = direct_download ? 1 : 0;
977
0
        if (out_open_license)
978
0
            *out_open_license = open_license ? 1 : 0;
979
0
        if (out_available)
980
0
            *out_available = available ? 1 : 0;
981
982
0
        return true;
983
0
    } catch (const std::exception &e) {
984
0
        proj_log_error(ctx, __FUNCTION__, e.what());
985
0
    }
986
0
    return false;
987
0
}
988
989
// ---------------------------------------------------------------------------
990
991
/** \brief Return GeodeticCRS that use the specified datum.
992
 *
993
 * @param ctx Context, or NULL for default context.
994
 * @param crs_auth_name CRS authority name, or NULL.
995
 * @param datum_auth_name Datum authority name (must not be NULL)
996
 * @param datum_code Datum code (must not be NULL)
997
 * @param crs_type "geographic 2D", "geographic 3D", "geocentric" or NULL
998
 * @return a result set that must be unreferenced with
999
 * proj_list_destroy(), or NULL in case of error.
1000
 */
1001
PJ_OBJ_LIST *proj_query_geodetic_crs_from_datum(PJ_CONTEXT *ctx,
1002
                                                const char *crs_auth_name,
1003
                                                const char *datum_auth_name,
1004
                                                const char *datum_code,
1005
0
                                                const char *crs_type) {
1006
0
    SANITIZE_CTX(ctx);
1007
0
    if (!datum_auth_name || !datum_code) {
1008
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
1009
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
1010
0
        return nullptr;
1011
0
    }
1012
0
    try {
1013
0
        auto factory = AuthorityFactory::create(
1014
0
            getDBcontext(ctx), crs_auth_name ? crs_auth_name : "");
1015
0
        auto res = factory->createGeodeticCRSFromDatum(
1016
0
            datum_auth_name, datum_code, crs_type ? crs_type : "");
1017
0
        std::vector<IdentifiedObjectNNPtr> objects;
1018
0
        for (const auto &obj : res) {
1019
0
            objects.push_back(obj);
1020
0
        }
1021
0
        return new PJ_OBJ_LIST(std::move(objects));
1022
0
    } catch (const std::exception &e) {
1023
0
        proj_log_error(ctx, __FUNCTION__, e.what());
1024
0
    }
1025
0
    return nullptr;
1026
0
}
1027
1028
// ---------------------------------------------------------------------------
1029
1030
//! @cond Doxygen_Suppress
1031
static AuthorityFactory::ObjectType
1032
0
convertPJObjectTypeToObjectType(PJ_TYPE type, bool &valid) {
1033
0
    valid = true;
1034
0
    AuthorityFactory::ObjectType cppType = AuthorityFactory::ObjectType::CRS;
1035
0
    switch (type) {
1036
0
    case PJ_TYPE_ELLIPSOID:
1037
0
        cppType = AuthorityFactory::ObjectType::ELLIPSOID;
1038
0
        break;
1039
1040
0
    case PJ_TYPE_PRIME_MERIDIAN:
1041
0
        cppType = AuthorityFactory::ObjectType::PRIME_MERIDIAN;
1042
0
        break;
1043
1044
0
    case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
1045
0
        cppType = AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME;
1046
0
        break;
1047
1048
0
    case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
1049
0
        cppType =
1050
0
            AuthorityFactory::ObjectType::DYNAMIC_GEODETIC_REFERENCE_FRAME;
1051
0
        break;
1052
1053
0
    case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
1054
0
        cppType = AuthorityFactory::ObjectType::VERTICAL_REFERENCE_FRAME;
1055
0
        break;
1056
1057
0
    case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
1058
0
        cppType =
1059
0
            AuthorityFactory::ObjectType::DYNAMIC_VERTICAL_REFERENCE_FRAME;
1060
0
        break;
1061
1062
0
    case PJ_TYPE_DATUM_ENSEMBLE:
1063
0
        cppType = AuthorityFactory::ObjectType::DATUM_ENSEMBLE;
1064
0
        break;
1065
1066
0
    case PJ_TYPE_TEMPORAL_DATUM:
1067
0
        valid = false;
1068
0
        break;
1069
1070
0
    case PJ_TYPE_ENGINEERING_DATUM:
1071
0
        cppType = AuthorityFactory::ObjectType::ENGINEERING_DATUM;
1072
0
        break;
1073
1074
0
    case PJ_TYPE_PARAMETRIC_DATUM:
1075
0
        valid = false;
1076
0
        break;
1077
1078
0
    case PJ_TYPE_CRS:
1079
0
        cppType = AuthorityFactory::ObjectType::CRS;
1080
0
        break;
1081
1082
0
    case PJ_TYPE_GEODETIC_CRS:
1083
0
        cppType = AuthorityFactory::ObjectType::GEODETIC_CRS;
1084
0
        break;
1085
1086
0
    case PJ_TYPE_GEOCENTRIC_CRS:
1087
0
        cppType = AuthorityFactory::ObjectType::GEOCENTRIC_CRS;
1088
0
        break;
1089
1090
0
    case PJ_TYPE_GEOGRAPHIC_CRS:
1091
0
        cppType = AuthorityFactory::ObjectType::GEOGRAPHIC_CRS;
1092
0
        break;
1093
1094
0
    case PJ_TYPE_GEOGRAPHIC_2D_CRS:
1095
0
        cppType = AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS;
1096
0
        break;
1097
1098
0
    case PJ_TYPE_GEOGRAPHIC_3D_CRS:
1099
0
        cppType = AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS;
1100
0
        break;
1101
1102
0
    case PJ_TYPE_VERTICAL_CRS:
1103
0
        cppType = AuthorityFactory::ObjectType::VERTICAL_CRS;
1104
0
        break;
1105
1106
0
    case PJ_TYPE_PROJECTED_CRS:
1107
0
        cppType = AuthorityFactory::ObjectType::PROJECTED_CRS;
1108
0
        break;
1109
1110
0
    case PJ_TYPE_DERIVED_PROJECTED_CRS:
1111
0
        valid = false;
1112
0
        break;
1113
1114
0
    case PJ_TYPE_COMPOUND_CRS:
1115
0
        cppType = AuthorityFactory::ObjectType::COMPOUND_CRS;
1116
0
        break;
1117
1118
0
    case PJ_TYPE_ENGINEERING_CRS:
1119
0
        cppType = AuthorityFactory::ObjectType::ENGINEERING_CRS;
1120
0
        break;
1121
1122
0
    case PJ_TYPE_TEMPORAL_CRS:
1123
0
        valid = false;
1124
0
        break;
1125
1126
0
    case PJ_TYPE_BOUND_CRS:
1127
0
        valid = false;
1128
0
        break;
1129
1130
0
    case PJ_TYPE_OTHER_CRS:
1131
0
        cppType = AuthorityFactory::ObjectType::CRS;
1132
0
        break;
1133
1134
0
    case PJ_TYPE_CONVERSION:
1135
0
        cppType = AuthorityFactory::ObjectType::CONVERSION;
1136
0
        break;
1137
1138
0
    case PJ_TYPE_TRANSFORMATION:
1139
0
        cppType = AuthorityFactory::ObjectType::TRANSFORMATION;
1140
0
        break;
1141
1142
0
    case PJ_TYPE_CONCATENATED_OPERATION:
1143
0
        cppType = AuthorityFactory::ObjectType::CONCATENATED_OPERATION;
1144
0
        break;
1145
1146
0
    case PJ_TYPE_OTHER_COORDINATE_OPERATION:
1147
0
        cppType = AuthorityFactory::ObjectType::COORDINATE_OPERATION;
1148
0
        break;
1149
1150
0
    case PJ_TYPE_UNKNOWN:
1151
0
        valid = false;
1152
0
        break;
1153
1154
0
    case PJ_TYPE_COORDINATE_METADATA:
1155
0
        valid = false;
1156
0
        break;
1157
0
    }
1158
0
    return cppType;
1159
0
}
1160
//! @endcond
1161
1162
// ---------------------------------------------------------------------------
1163
1164
/** \brief Return a list of objects by their name.
1165
 *
1166
 * @param ctx Context, or NULL for default context.
1167
 * @param auth_name Authority name, used to restrict the search.
1168
 * Or NULL for all authorities.
1169
 * @param searchedName Searched name. Must be at least 2 character long.
1170
 * @param types List of object types into which to search. If
1171
 * NULL, all object types will be searched.
1172
 * @param typesCount Number of elements in types, or 0 if types is NULL
1173
 * @param approximateMatch Whether approximate name identification is allowed.
1174
 * @param limitResultCount Maximum number of results to return.
1175
 * Or 0 for unlimited.
1176
 * @param options should be set to NULL for now
1177
 * @return a result set that must be unreferenced with
1178
 * proj_list_destroy(), or NULL in case of error.
1179
 */
1180
PJ_OBJ_LIST *proj_create_from_name(PJ_CONTEXT *ctx, const char *auth_name,
1181
                                   const char *searchedName,
1182
                                   const PJ_TYPE *types, size_t typesCount,
1183
                                   int approximateMatch,
1184
                                   size_t limitResultCount,
1185
0
                                   const char *const *options) {
1186
0
    SANITIZE_CTX(ctx);
1187
0
    if (!searchedName || (types != nullptr && typesCount == 0) ||
1188
0
        (types == nullptr && typesCount > 0)) {
1189
0
        proj_log_error(ctx, __FUNCTION__, "invalid input");
1190
0
        return nullptr;
1191
0
    }
1192
0
    (void)options;
1193
0
    try {
1194
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx),
1195
0
                                                auth_name ? auth_name : "");
1196
0
        std::vector<AuthorityFactory::ObjectType> allowedTypes;
1197
0
        for (size_t i = 0; i < typesCount; ++i) {
1198
0
            bool valid = false;
1199
0
            auto type = convertPJObjectTypeToObjectType(types[i], valid);
1200
0
            if (valid) {
1201
0
                allowedTypes.push_back(type);
1202
0
            }
1203
0
        }
1204
0
        auto res = factory->createObjectsFromName(searchedName, allowedTypes,
1205
0
                                                  approximateMatch != 0,
1206
0
                                                  limitResultCount);
1207
0
        std::vector<IdentifiedObjectNNPtr> objects;
1208
0
        for (const auto &obj : res) {
1209
0
            objects.push_back(obj);
1210
0
        }
1211
0
        return new PJ_OBJ_LIST(std::move(objects));
1212
0
    } catch (const std::exception &e) {
1213
0
        proj_log_error(ctx, __FUNCTION__, e.what());
1214
0
    }
1215
0
    return nullptr;
1216
0
}
1217
1218
// ---------------------------------------------------------------------------
1219
1220
/** \brief Return the type of an object.
1221
 *
1222
 * @param obj Object (must not be NULL)
1223
 * @return its type.
1224
 */
1225
37.3k
PJ_TYPE proj_get_type(const PJ *obj) {
1226
37.3k
    if (!obj || !obj->iso_obj) {
1227
0
        return PJ_TYPE_UNKNOWN;
1228
0
    }
1229
37.3k
    if (obj->type != PJ_TYPE_UNKNOWN)
1230
2.32k
        return obj->type;
1231
1232
34.9k
    const auto getType = [&obj]() {
1233
34.9k
        auto ptr = obj->iso_obj.get();
1234
34.9k
        if (dynamic_cast<Ellipsoid *>(ptr)) {
1235
0
            return PJ_TYPE_ELLIPSOID;
1236
0
        }
1237
1238
34.9k
        if (dynamic_cast<PrimeMeridian *>(ptr)) {
1239
0
            return PJ_TYPE_PRIME_MERIDIAN;
1240
0
        }
1241
1242
34.9k
        if (dynamic_cast<DynamicGeodeticReferenceFrame *>(ptr)) {
1243
0
            return PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME;
1244
0
        }
1245
34.9k
        if (dynamic_cast<GeodeticReferenceFrame *>(ptr)) {
1246
0
            return PJ_TYPE_GEODETIC_REFERENCE_FRAME;
1247
0
        }
1248
34.9k
        if (dynamic_cast<DynamicVerticalReferenceFrame *>(ptr)) {
1249
0
            return PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
1250
0
        }
1251
34.9k
        if (dynamic_cast<VerticalReferenceFrame *>(ptr)) {
1252
0
            return PJ_TYPE_VERTICAL_REFERENCE_FRAME;
1253
0
        }
1254
34.9k
        if (dynamic_cast<DatumEnsemble *>(ptr)) {
1255
0
            return PJ_TYPE_DATUM_ENSEMBLE;
1256
0
        }
1257
34.9k
        if (dynamic_cast<TemporalDatum *>(ptr)) {
1258
0
            return PJ_TYPE_TEMPORAL_DATUM;
1259
0
        }
1260
34.9k
        if (dynamic_cast<EngineeringDatum *>(ptr)) {
1261
0
            return PJ_TYPE_ENGINEERING_DATUM;
1262
0
        }
1263
34.9k
        if (dynamic_cast<ParametricDatum *>(ptr)) {
1264
0
            return PJ_TYPE_PARAMETRIC_DATUM;
1265
0
        }
1266
1267
34.9k
        if (auto crs = dynamic_cast<GeographicCRS *>(ptr)) {
1268
8.58k
            if (crs->coordinateSystem()->axisList().size() == 2) {
1269
7.69k
                return PJ_TYPE_GEOGRAPHIC_2D_CRS;
1270
7.69k
            } else {
1271
897
                return PJ_TYPE_GEOGRAPHIC_3D_CRS;
1272
897
            }
1273
8.58k
        }
1274
1275
26.4k
        if (auto crs = dynamic_cast<GeodeticCRS *>(ptr)) {
1276
4.39k
            if (crs->isGeocentric()) {
1277
4.35k
                return PJ_TYPE_GEOCENTRIC_CRS;
1278
4.35k
            } else {
1279
39
                return PJ_TYPE_GEODETIC_CRS;
1280
39
            }
1281
4.39k
        }
1282
1283
22.0k
        if (dynamic_cast<VerticalCRS *>(ptr)) {
1284
0
            return PJ_TYPE_VERTICAL_CRS;
1285
0
        }
1286
22.0k
        if (dynamic_cast<ProjectedCRS *>(ptr)) {
1287
8.20k
            return PJ_TYPE_PROJECTED_CRS;
1288
8.20k
        }
1289
13.8k
        if (dynamic_cast<DerivedProjectedCRS *>(ptr)) {
1290
0
            return PJ_TYPE_DERIVED_PROJECTED_CRS;
1291
0
        }
1292
13.8k
        if (dynamic_cast<CompoundCRS *>(ptr)) {
1293
12.7k
            return PJ_TYPE_COMPOUND_CRS;
1294
12.7k
        }
1295
1.09k
        if (dynamic_cast<TemporalCRS *>(ptr)) {
1296
0
            return PJ_TYPE_TEMPORAL_CRS;
1297
0
        }
1298
1.09k
        if (dynamic_cast<EngineeringCRS *>(ptr)) {
1299
0
            return PJ_TYPE_ENGINEERING_CRS;
1300
0
        }
1301
1.09k
        if (dynamic_cast<BoundCRS *>(ptr)) {
1302
1.08k
            return PJ_TYPE_BOUND_CRS;
1303
1.08k
        }
1304
8
        if (dynamic_cast<CRS *>(ptr)) {
1305
0
            return PJ_TYPE_OTHER_CRS;
1306
0
        }
1307
1308
8
        if (dynamic_cast<Conversion *>(ptr)) {
1309
0
            return PJ_TYPE_CONVERSION;
1310
0
        }
1311
8
        if (dynamic_cast<Transformation *>(ptr)) {
1312
0
            return PJ_TYPE_TRANSFORMATION;
1313
0
        }
1314
8
        if (dynamic_cast<ConcatenatedOperation *>(ptr)) {
1315
0
            return PJ_TYPE_CONCATENATED_OPERATION;
1316
0
        }
1317
8
        if (dynamic_cast<CoordinateOperation *>(ptr)) {
1318
0
            return PJ_TYPE_OTHER_COORDINATE_OPERATION;
1319
0
        }
1320
1321
8
        if (dynamic_cast<CoordinateMetadata *>(ptr)) {
1322
8
            return PJ_TYPE_COORDINATE_METADATA;
1323
8
        }
1324
1325
0
        return PJ_TYPE_UNKNOWN;
1326
8
    };
1327
1328
34.9k
    obj->type = getType();
1329
34.9k
    return obj->type;
1330
37.3k
}
1331
1332
// ---------------------------------------------------------------------------
1333
1334
/** \brief Return whether an object is deprecated.
1335
 *
1336
 * @param obj Object (must not be NULL)
1337
 * @return TRUE if it is deprecated, FALSE otherwise
1338
 */
1339
0
int proj_is_deprecated(const PJ *obj) {
1340
0
    if (!obj) {
1341
0
        return false;
1342
0
    }
1343
0
    auto identifiedObj = dynamic_cast<IdentifiedObject *>(obj->iso_obj.get());
1344
0
    if (!identifiedObj) {
1345
0
        return false;
1346
0
    }
1347
0
    return identifiedObj->isDeprecated();
1348
0
}
1349
1350
// ---------------------------------------------------------------------------
1351
1352
/** \brief Return a list of non-deprecated objects related to the passed one
1353
 *
1354
 * @param ctx Context, or NULL for default context.
1355
 * @param obj Object (of type CRS for now) for which non-deprecated objects
1356
 * must be searched. Must not be NULL
1357
 * @return a result set that must be unreferenced with
1358
 * proj_list_destroy(), or NULL in case of error.
1359
 */
1360
0
PJ_OBJ_LIST *proj_get_non_deprecated(PJ_CONTEXT *ctx, const PJ *obj) {
1361
0
    SANITIZE_CTX(ctx);
1362
0
    if (!obj) {
1363
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
1364
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
1365
0
        return nullptr;
1366
0
    }
1367
0
    auto crs = dynamic_cast<const CRS *>(obj->iso_obj.get());
1368
0
    if (!crs) {
1369
0
        return nullptr;
1370
0
    }
1371
0
    try {
1372
0
        std::vector<IdentifiedObjectNNPtr> objects;
1373
0
        auto res = crs->getNonDeprecated(getDBcontext(ctx));
1374
0
        for (const auto &resObj : res) {
1375
0
            objects.push_back(resObj);
1376
0
        }
1377
0
        return new PJ_OBJ_LIST(std::move(objects));
1378
0
    } catch (const std::exception &e) {
1379
0
        proj_log_error(ctx, __FUNCTION__, e.what());
1380
0
    }
1381
0
    return nullptr;
1382
0
}
1383
1384
// ---------------------------------------------------------------------------
1385
1386
static int proj_is_equivalent_to_internal(PJ_CONTEXT *ctx, const PJ *obj,
1387
                                          const PJ *other,
1388
0
                                          PJ_COMPARISON_CRITERION criterion) {
1389
1390
0
    if (!obj || !other) {
1391
0
        if (ctx) {
1392
0
            proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
1393
0
            proj_log_error(ctx, __FUNCTION__, "missing required input");
1394
0
        }
1395
0
        return false;
1396
0
    }
1397
1398
0
    if (obj->iso_obj == nullptr && other->iso_obj == nullptr &&
1399
0
        !obj->alternativeCoordinateOperations.empty() &&
1400
0
        obj->alternativeCoordinateOperations.size() ==
1401
0
            other->alternativeCoordinateOperations.size()) {
1402
0
        for (size_t i = 0; i < obj->alternativeCoordinateOperations.size();
1403
0
             ++i) {
1404
0
            if (obj->alternativeCoordinateOperations[i] !=
1405
0
                other->alternativeCoordinateOperations[i]) {
1406
0
                return false;
1407
0
            }
1408
0
        }
1409
0
        return true;
1410
0
    }
1411
1412
0
    auto identifiedObj = dynamic_cast<IdentifiedObject *>(obj->iso_obj.get());
1413
0
    if (!identifiedObj) {
1414
0
        return false;
1415
0
    }
1416
0
    auto otherIdentifiedObj =
1417
0
        dynamic_cast<IdentifiedObject *>(other->iso_obj.get());
1418
0
    if (!otherIdentifiedObj) {
1419
0
        return false;
1420
0
    }
1421
0
    const auto cppCriterion = ([](PJ_COMPARISON_CRITERION l_criterion) {
1422
0
        switch (l_criterion) {
1423
0
        case PJ_COMP_STRICT:
1424
0
            return IComparable::Criterion::STRICT;
1425
0
        case PJ_COMP_EQUIVALENT:
1426
0
            return IComparable::Criterion::EQUIVALENT;
1427
0
        case PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS:
1428
0
            break;
1429
0
        }
1430
0
        return IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
1431
0
    })(criterion);
1432
1433
0
    int res = identifiedObj->isEquivalentTo(
1434
0
        otherIdentifiedObj, cppCriterion,
1435
0
        ctx ? getDBcontextNoException(ctx, "proj_is_equivalent_to_with_ctx")
1436
0
            : nullptr);
1437
0
    return res;
1438
0
}
1439
1440
// ---------------------------------------------------------------------------
1441
1442
/** \brief Return whether two objects are equivalent.
1443
 *
1444
 * Use proj_is_equivalent_to_with_ctx() to be able to use database information.
1445
 *
1446
 * @param obj Object (must not be NULL)
1447
 * @param other Other object (must not be NULL)
1448
 * @param criterion Comparison criterion
1449
 * @return TRUE if they are equivalent
1450
 */
1451
int proj_is_equivalent_to(const PJ *obj, const PJ *other,
1452
0
                          PJ_COMPARISON_CRITERION criterion) {
1453
0
    return proj_is_equivalent_to_internal(nullptr, obj, other, criterion);
1454
0
}
1455
1456
// ---------------------------------------------------------------------------
1457
1458
/** \brief Return whether two objects are equivalent
1459
 *
1460
 * Possibly using database to check for name aliases.
1461
 *
1462
 * @param ctx PROJ context, or NULL for default context
1463
 * @param obj Object (must not be NULL)
1464
 * @param other Other object (must not be NULL)
1465
 * @param criterion Comparison criterion
1466
 * @return TRUE if they are equivalent
1467
 * @since 6.3
1468
 */
1469
int proj_is_equivalent_to_with_ctx(PJ_CONTEXT *ctx, const PJ *obj,
1470
                                   const PJ *other,
1471
0
                                   PJ_COMPARISON_CRITERION criterion) {
1472
0
    SANITIZE_CTX(ctx);
1473
0
    return proj_is_equivalent_to_internal(ctx, obj, other, criterion);
1474
0
}
1475
1476
// ---------------------------------------------------------------------------
1477
1478
/** \brief Return whether an object is a CRS
1479
 *
1480
 * @param obj Object (must not be NULL)
1481
 */
1482
0
int proj_is_crs(const PJ *obj) {
1483
0
    if (!obj) {
1484
0
        return false;
1485
0
    }
1486
0
    return dynamic_cast<CRS *>(obj->iso_obj.get()) != nullptr;
1487
0
}
1488
1489
// ---------------------------------------------------------------------------
1490
1491
/** \brief Get the name of an object.
1492
 *
1493
 * The lifetime of the returned string is the same as the input obj parameter.
1494
 *
1495
 * @param obj Object (must not be NULL)
1496
 * @return a string, or NULL in case of error or missing name.
1497
 */
1498
16.5k
const char *proj_get_name(const PJ *obj) {
1499
16.5k
    if (!obj) {
1500
0
        return nullptr;
1501
0
    }
1502
16.5k
    auto identifiedObj = dynamic_cast<IdentifiedObject *>(obj->iso_obj.get());
1503
16.5k
    if (!identifiedObj) {
1504
0
        return nullptr;
1505
0
    }
1506
16.5k
    const auto &desc = identifiedObj->name()->description();
1507
16.5k
    if (!desc.has_value()) {
1508
0
        return nullptr;
1509
0
    }
1510
    // The object will still be alive after the function call.
1511
    // cppcheck-suppress stlcstr
1512
16.5k
    return desc->c_str();
1513
16.5k
}
1514
1515
// ---------------------------------------------------------------------------
1516
1517
/** \brief Get the remarks of an object.
1518
 *
1519
 * The lifetime of the returned string is the same as the input obj parameter.
1520
 *
1521
 * @param obj Object (must not be NULL)
1522
 * @return a string, or NULL in case of error.
1523
 */
1524
0
const char *proj_get_remarks(const PJ *obj) {
1525
0
    if (!obj) {
1526
0
        return nullptr;
1527
0
    }
1528
0
    auto identifiedObj = dynamic_cast<IdentifiedObject *>(obj->iso_obj.get());
1529
0
    if (!identifiedObj) {
1530
0
        return nullptr;
1531
0
    }
1532
    // The object will still be alive after the function call.
1533
    // cppcheck-suppress stlcstr
1534
0
    return identifiedObj->remarks().c_str();
1535
0
}
1536
1537
// ---------------------------------------------------------------------------
1538
1539
/** \brief Get the authority name / codespace of an identifier of an object.
1540
 *
1541
 * The lifetime of the returned string is the same as the input obj parameter.
1542
 *
1543
 * @param obj Object (must not be NULL)
1544
 * @param index Index of the identifier. 0 = first identifier
1545
 * @return a string, or NULL in case of error or missing name.
1546
 */
1547
0
const char *proj_get_id_auth_name(const PJ *obj, int index) {
1548
0
    if (!obj) {
1549
0
        return nullptr;
1550
0
    }
1551
0
    auto identifiedObj = dynamic_cast<IdentifiedObject *>(obj->iso_obj.get());
1552
0
    if (!identifiedObj) {
1553
0
        return nullptr;
1554
0
    }
1555
0
    const auto &ids = identifiedObj->identifiers();
1556
0
    if (static_cast<size_t>(index) >= ids.size()) {
1557
0
        return nullptr;
1558
0
    }
1559
0
    const auto &codeSpace = ids[index]->codeSpace();
1560
0
    if (!codeSpace.has_value()) {
1561
0
        return nullptr;
1562
0
    }
1563
    // The object will still be alive after the function call.
1564
    // cppcheck-suppress stlcstr
1565
0
    return codeSpace->c_str();
1566
0
}
1567
1568
// ---------------------------------------------------------------------------
1569
1570
/** \brief Get the code of an identifier of an object.
1571
 *
1572
 * The lifetime of the returned string is the same as the input obj parameter.
1573
 *
1574
 * @param obj Object (must not be NULL)
1575
 * @param index Index of the identifier. 0 = first identifier
1576
 * @return a string, or NULL in case of error or missing name.
1577
 */
1578
0
const char *proj_get_id_code(const PJ *obj, int index) {
1579
0
    if (!obj) {
1580
0
        return nullptr;
1581
0
    }
1582
0
    auto identifiedObj = dynamic_cast<IdentifiedObject *>(obj->iso_obj.get());
1583
0
    if (!identifiedObj) {
1584
0
        return nullptr;
1585
0
    }
1586
0
    const auto &ids = identifiedObj->identifiers();
1587
0
    if (static_cast<size_t>(index) >= ids.size()) {
1588
0
        return nullptr;
1589
0
    }
1590
0
    return ids[index]->code().c_str();
1591
0
}
1592
1593
// ---------------------------------------------------------------------------
1594
1595
/** \brief Get a WKT representation of an object.
1596
 *
1597
 * The returned string is valid while the input obj parameter is valid,
1598
 * and until a next call to proj_as_wkt() with the same input object.
1599
 *
1600
 * This function calls osgeo::proj::io::IWKTExportable::exportToWKT().
1601
 *
1602
 * This function may return NULL if the object is not compatible with an
1603
 * export to the requested type.
1604
 *
1605
 * @param ctx PROJ context, or NULL for default context
1606
 * @param obj Object (must not be NULL)
1607
 * @param type WKT version.
1608
 * @param options null-terminated list of options, or NULL. Currently
1609
 * supported options are:
1610
 * <ul>
1611
 * <li>MULTILINE=YES/NO. Defaults to YES, except for WKT1_ESRI</li>
1612
 * <li>INDENTATION_WIDTH=number. Defaults to 4 (when multiline output is
1613
 * on).</li>
1614
 * <li>OUTPUT_AXIS=AUTO/YES/NO. In AUTO mode, axis will be output for WKT2
1615
 * variants, for WKT1_GDAL for ProjectedCRS with easting/northing ordering
1616
 * (otherwise stripped), but not for WKT1_ESRI. Setting to YES will output
1617
 * them unconditionally, and to NO will omit them unconditionally.</li>
1618
 * <li>STRICT=YES/NO. Default is YES. If NO, a Geographic 3D CRS can be for
1619
 * example exported as WKT1_GDAL with 3 axes, whereas this is normally not
1620
 * allowed.</li>
1621
 * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1622
 * to YES and type == PJ_WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS
1623
 * will be exported as a compound CRS whose vertical part represents an
1624
 * ellipsoidal height (for example for use with LAS 1.4 WKT1).</li>
1625
 * <li>ALLOW_LINUNIT_NODE=YES/NO. Default is YES starting with PROJ 9.1.
1626
 * Only taken into account with type == PJ_WKT1_ESRI on a Geographic 3D
1627
 * CRS.</li>
1628
 * </ul>
1629
 * @return a string, or NULL in case of error.
1630
 */
1631
const char *proj_as_wkt(PJ_CONTEXT *ctx, const PJ *obj, PJ_WKT_TYPE type,
1632
0
                        const char *const *options) {
1633
0
    SANITIZE_CTX(ctx);
1634
0
    if (!obj) {
1635
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
1636
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
1637
0
        return nullptr;
1638
0
    }
1639
0
    auto iWKTExportable = dynamic_cast<IWKTExportable *>(obj->iso_obj.get());
1640
0
    if (!iWKTExportable) {
1641
0
        return nullptr;
1642
0
    }
1643
1644
0
    const auto convention = ([](PJ_WKT_TYPE l_type) {
1645
0
        switch (l_type) {
1646
0
        case PJ_WKT2_2015:
1647
0
            return WKTFormatter::Convention::WKT2_2015;
1648
0
        case PJ_WKT2_2015_SIMPLIFIED:
1649
0
            return WKTFormatter::Convention::WKT2_2015_SIMPLIFIED;
1650
0
        case PJ_WKT2_2019:
1651
0
            return WKTFormatter::Convention::WKT2_2019;
1652
0
        case PJ_WKT2_2019_SIMPLIFIED:
1653
0
            return WKTFormatter::Convention::WKT2_2019_SIMPLIFIED;
1654
0
        case PJ_WKT1_GDAL:
1655
0
            return WKTFormatter::Convention::WKT1_GDAL;
1656
0
        case PJ_WKT1_ESRI:
1657
0
            break;
1658
0
        }
1659
0
        return WKTFormatter::Convention::WKT1_ESRI;
1660
0
    })(type);
1661
1662
0
    try {
1663
0
        auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
1664
0
        auto formatter = WKTFormatter::create(convention, std::move(dbContext));
1665
0
        for (auto iter = options; iter && iter[0]; ++iter) {
1666
0
            const char *value;
1667
0
            if ((value = getOptionValue(*iter, "MULTILINE="))) {
1668
0
                formatter->setMultiLine(ci_equal(value, "YES"));
1669
0
            } else if ((value = getOptionValue(*iter, "INDENTATION_WIDTH="))) {
1670
0
                formatter->setIndentationWidth(std::atoi(value));
1671
0
            } else if ((value = getOptionValue(*iter, "OUTPUT_AXIS="))) {
1672
0
                if (!ci_equal(value, "AUTO")) {
1673
0
                    formatter->setOutputAxis(
1674
0
                        ci_equal(value, "YES")
1675
0
                            ? WKTFormatter::OutputAxisRule::YES
1676
0
                            : WKTFormatter::OutputAxisRule::NO);
1677
0
                }
1678
0
            } else if ((value = getOptionValue(*iter, "STRICT="))) {
1679
0
                formatter->setStrict(ci_equal(value, "YES"));
1680
0
            } else if ((value = getOptionValue(
1681
0
                            *iter,
1682
0
                            "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS="))) {
1683
0
                formatter->setAllowEllipsoidalHeightAsVerticalCRS(
1684
0
                    ci_equal(value, "YES"));
1685
0
            } else if ((value = getOptionValue(*iter, "ALLOW_LINUNIT_NODE="))) {
1686
0
                formatter->setAllowLINUNITNode(ci_equal(value, "YES"));
1687
0
            } else {
1688
0
                std::string msg("Unknown option :");
1689
0
                msg += *iter;
1690
0
                proj_log_error(ctx, __FUNCTION__, msg.c_str());
1691
0
                return nullptr;
1692
0
            }
1693
0
        }
1694
0
        obj->lastWKT = iWKTExportable->exportToWKT(formatter.get());
1695
0
        return obj->lastWKT.c_str();
1696
0
    } catch (const std::exception &e) {
1697
0
        proj_log_error(ctx, __FUNCTION__, e.what());
1698
0
        return nullptr;
1699
0
    }
1700
0
}
1701
1702
// ---------------------------------------------------------------------------
1703
1704
/** \brief Get a PROJ string representation of an object.
1705
 *
1706
 * The returned string is valid while the input obj parameter is valid,
1707
 * and until a next call to proj_as_proj_string() with the same input
1708
 * object.
1709
 *
1710
 * \warning If a CRS object was not created from a PROJ string,
1711
 *          exporting to a PROJ string will in most cases
1712
 *          cause a loss of information. This can potentially lead to
1713
 *          erroneous transformations.
1714
 *
1715
 * This function calls
1716
 * osgeo::proj::io::IPROJStringExportable::exportToPROJString().
1717
 *
1718
 * This function may return NULL if the object is not compatible with an
1719
 * export to the requested type.
1720
 *
1721
 * @param ctx PROJ context, or NULL for default context
1722
 * @param obj Object (must not be NULL)
1723
 * @param type PROJ String version.
1724
 * @param options NULL-terminated list of strings with "KEY=VALUE" format. or
1725
 * NULL. Currently supported options are:
1726
 * <ul>
1727
 * <li>USE_APPROX_TMERC=YES to add the +approx flag to +proj=tmerc or
1728
 * +proj=utm.</li>
1729
 * <li>MULTILINE=YES/NO. Defaults to NO</li>
1730
 * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
1731
 * on).</li>
1732
 * <li>MAX_LINE_LENGTH=number. Defaults to 80 (when multiline output is
1733
 * on).</li>
1734
 * </ul>
1735
 * @return a string, or NULL in case of error.
1736
 */
1737
const char *proj_as_proj_string(PJ_CONTEXT *ctx, const PJ *obj,
1738
                                PJ_PROJ_STRING_TYPE type,
1739
0
                                const char *const *options) {
1740
0
    SANITIZE_CTX(ctx);
1741
0
    if (!obj) {
1742
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
1743
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
1744
0
        return nullptr;
1745
0
    }
1746
0
    auto exportable =
1747
0
        dynamic_cast<const IPROJStringExportable *>(obj->iso_obj.get());
1748
0
    if (!exportable) {
1749
0
        proj_log_error(ctx, __FUNCTION__, "Object type not exportable to PROJ");
1750
0
        return nullptr;
1751
0
    }
1752
    // Make sure that the C and C++ enumeration match
1753
0
    static_assert(static_cast<int>(PJ_PROJ_5) ==
1754
0
                      static_cast<int>(PROJStringFormatter::Convention::PROJ_5),
1755
0
                  "");
1756
0
    static_assert(static_cast<int>(PJ_PROJ_4) ==
1757
0
                      static_cast<int>(PROJStringFormatter::Convention::PROJ_4),
1758
0
                  "");
1759
    // Make sure we enumerate all values. If adding a new value, as we
1760
    // don't have a default clause, the compiler will warn.
1761
0
    switch (type) {
1762
0
    case PJ_PROJ_5:
1763
0
    case PJ_PROJ_4:
1764
0
        break;
1765
0
    }
1766
0
    const PROJStringFormatter::Convention convention =
1767
0
        static_cast<PROJStringFormatter::Convention>(type);
1768
0
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
1769
0
    try {
1770
0
        auto formatter =
1771
0
            PROJStringFormatter::create(convention, std::move(dbContext));
1772
0
        for (auto iter = options; iter && iter[0]; ++iter) {
1773
0
            const char *value;
1774
0
            if ((value = getOptionValue(*iter, "MULTILINE="))) {
1775
0
                formatter->setMultiLine(ci_equal(value, "YES"));
1776
0
            } else if ((value = getOptionValue(*iter, "INDENTATION_WIDTH="))) {
1777
0
                formatter->setIndentationWidth(std::atoi(value));
1778
0
            } else if ((value = getOptionValue(*iter, "MAX_LINE_LENGTH="))) {
1779
0
                formatter->setMaxLineLength(std::atoi(value));
1780
0
            } else if ((value = getOptionValue(*iter, "USE_APPROX_TMERC="))) {
1781
0
                formatter->setUseApproxTMerc(ci_equal(value, "YES"));
1782
0
            } else {
1783
0
                std::string msg("Unknown option :");
1784
0
                msg += *iter;
1785
0
                proj_log_error(ctx, __FUNCTION__, msg.c_str());
1786
0
                return nullptr;
1787
0
            }
1788
0
        }
1789
0
        obj->lastPROJString = exportable->exportToPROJString(formatter.get());
1790
0
        return obj->lastPROJString.c_str();
1791
0
    } catch (const std::exception &e) {
1792
0
        proj_log_error(ctx, __FUNCTION__, e.what());
1793
0
        return nullptr;
1794
0
    }
1795
0
}
1796
1797
// ---------------------------------------------------------------------------
1798
1799
/** \brief Get a PROJJSON string representation of an object.
1800
 *
1801
 * The returned string is valid while the input obj parameter is valid,
1802
 * and until a next call to proj_as_proj_string() with the same input
1803
 * object.
1804
 *
1805
 * This function calls
1806
 * osgeo::proj::io::IJSONExportable::exportToJSON().
1807
 *
1808
 * This function may return NULL if the object is not compatible with an
1809
 * export to the requested type.
1810
 *
1811
 * @param ctx PROJ context, or NULL for default context
1812
 * @param obj Object (must not be NULL)
1813
 * @param options NULL-terminated list of strings with "KEY=VALUE" format. or
1814
 * NULL. Currently
1815
 * supported options are:
1816
 * <ul>
1817
 * <li>MULTILINE=YES/NO. Defaults to YES</li>
1818
 * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
1819
 * on).</li>
1820
 * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
1821
 * disable it.</li>
1822
 * </ul>
1823
 * @return a string, or NULL in case of error.
1824
 *
1825
 * @since 6.2
1826
 */
1827
const char *proj_as_projjson(PJ_CONTEXT *ctx, const PJ *obj,
1828
0
                             const char *const *options) {
1829
0
    SANITIZE_CTX(ctx);
1830
0
    if (!obj) {
1831
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
1832
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
1833
0
        return nullptr;
1834
0
    }
1835
0
    auto exportable = dynamic_cast<const IJSONExportable *>(obj->iso_obj.get());
1836
0
    if (!exportable) {
1837
0
        proj_log_error(ctx, __FUNCTION__, "Object type not exportable to JSON");
1838
0
        return nullptr;
1839
0
    }
1840
1841
0
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
1842
0
    try {
1843
0
        auto formatter = JSONFormatter::create(std::move(dbContext));
1844
0
        for (auto iter = options; iter && iter[0]; ++iter) {
1845
0
            const char *value;
1846
0
            if ((value = getOptionValue(*iter, "MULTILINE="))) {
1847
0
                formatter->setMultiLine(ci_equal(value, "YES"));
1848
0
            } else if ((value = getOptionValue(*iter, "INDENTATION_WIDTH="))) {
1849
0
                formatter->setIndentationWidth(std::atoi(value));
1850
0
            } else if ((value = getOptionValue(*iter, "SCHEMA="))) {
1851
0
                formatter->setSchema(value);
1852
0
            } else {
1853
0
                std::string msg("Unknown option :");
1854
0
                msg += *iter;
1855
0
                proj_log_error(ctx, __FUNCTION__, msg.c_str());
1856
0
                return nullptr;
1857
0
            }
1858
0
        }
1859
0
        obj->lastJSONString = exportable->exportToJSON(formatter.get());
1860
0
        return obj->lastJSONString.c_str();
1861
0
    } catch (const std::exception &e) {
1862
0
        proj_log_error(ctx, __FUNCTION__, e.what());
1863
0
        return nullptr;
1864
0
    }
1865
0
}
1866
1867
// ---------------------------------------------------------------------------
1868
1869
/** \brief Get the number of domains/usages for a given object.
1870
 *
1871
 * Most objects have a single domain/usage, but for some of them, there might
1872
 * be multiple.
1873
 *
1874
 * @param obj Object (must not be NULL)
1875
 * @return the number of domains, or 0 in case of error.
1876
 * @since 9.2
1877
 */
1878
0
int proj_get_domain_count(const PJ *obj) {
1879
0
    if (!obj || !obj->iso_obj) {
1880
0
        return 0;
1881
0
    }
1882
0
    auto objectUsage = dynamic_cast<const ObjectUsage *>(obj->iso_obj.get());
1883
0
    if (!objectUsage) {
1884
0
        return 0;
1885
0
    }
1886
0
    const auto &domains = objectUsage->domains();
1887
0
    return static_cast<int>(domains.size());
1888
0
}
1889
1890
// ---------------------------------------------------------------------------
1891
1892
/** \brief Get the scope of an object.
1893
 *
1894
 * In case of multiple usages, this will be the one of first usage.
1895
 *
1896
 * The lifetime of the returned string is the same as the input obj parameter.
1897
 *
1898
 * @param obj Object (must not be NULL)
1899
 * @return a string, or NULL in case of error or missing scope.
1900
 */
1901
0
const char *proj_get_scope(const PJ *obj) { return proj_get_scope_ex(obj, 0); }
1902
1903
// ---------------------------------------------------------------------------
1904
1905
/** \brief Get the scope of an object.
1906
 *
1907
 * The lifetime of the returned string is the same as the input obj parameter.
1908
 *
1909
 * @param obj Object (must not be NULL)
1910
 * @param domainIdx Index of the domain/usage. In [0,proj_get_domain_count(obj)[
1911
 * @return a string, or NULL in case of error or missing scope.
1912
 * @since 9.2
1913
 */
1914
0
const char *proj_get_scope_ex(const PJ *obj, int domainIdx) {
1915
0
    if (!obj || !obj->iso_obj) {
1916
0
        return nullptr;
1917
0
    }
1918
0
    auto objectUsage = dynamic_cast<const ObjectUsage *>(obj->iso_obj.get());
1919
0
    if (!objectUsage) {
1920
0
        return nullptr;
1921
0
    }
1922
0
    const auto &domains = objectUsage->domains();
1923
0
    if (domainIdx < 0 || static_cast<size_t>(domainIdx) >= domains.size()) {
1924
0
        return nullptr;
1925
0
    }
1926
0
    const auto &scope = domains[domainIdx]->scope();
1927
0
    if (!scope.has_value()) {
1928
0
        return nullptr;
1929
0
    }
1930
    // The object will still be alive after the function call.
1931
    // cppcheck-suppress stlcstr
1932
0
    return scope->c_str();
1933
0
}
1934
1935
// ---------------------------------------------------------------------------
1936
1937
/** \brief Return the area of use of an object.
1938
 *
1939
 * In case of multiple usages, this will be the one of first usage.
1940
 *
1941
 * @param ctx PROJ context, or NULL for default context
1942
 * @param obj Object (must not be NULL)
1943
 * @param out_west_lon_degree Pointer to a double to receive the west longitude
1944
 * (in degrees). Or NULL. If the returned value is -1000, the bounding box is
1945
 * unknown.
1946
 * @param out_south_lat_degree Pointer to a double to receive the south latitude
1947
 * (in degrees). Or NULL. If the returned value is -1000, the bounding box is
1948
 * unknown.
1949
 * @param out_east_lon_degree Pointer to a double to receive the east longitude
1950
 * (in degrees). Or NULL. If the returned value is -1000, the bounding box is
1951
 * unknown.
1952
 * @param out_north_lat_degree Pointer to a double to receive the north latitude
1953
 * (in degrees). Or NULL. If the returned value is -1000, the bounding box is
1954
 * unknown.
1955
 * @param out_area_name Pointer to a string to receive the name of the area of
1956
 * use. Or NULL. *p_area_name is valid while obj is valid itself.
1957
 * @return TRUE in case of success, FALSE in case of error or if the area
1958
 * of use is unknown.
1959
 */
1960
int proj_get_area_of_use(PJ_CONTEXT *ctx, const PJ *obj,
1961
                         double *out_west_lon_degree,
1962
                         double *out_south_lat_degree,
1963
                         double *out_east_lon_degree,
1964
                         double *out_north_lat_degree,
1965
25.6k
                         const char **out_area_name) {
1966
25.6k
    return proj_get_area_of_use_ex(ctx, obj, 0, out_west_lon_degree,
1967
25.6k
                                   out_south_lat_degree, out_east_lon_degree,
1968
25.6k
                                   out_north_lat_degree, out_area_name);
1969
25.6k
}
1970
1971
// ---------------------------------------------------------------------------
1972
1973
/** \brief Return the area of use of an object.
1974
 *
1975
 * @param ctx PROJ context, or NULL for default context
1976
 * @param obj Object (must not be NULL)
1977
 * @param domainIdx Index of the domain/usage. In [0,proj_get_domain_count(obj)[
1978
 * @param out_west_lon_degree Pointer to a double to receive the west longitude
1979
 * (in degrees). Or NULL. If the returned value is -1000, the bounding box is
1980
 * unknown.
1981
 * @param out_south_lat_degree Pointer to a double to receive the south latitude
1982
 * (in degrees). Or NULL. If the returned value is -1000, the bounding box is
1983
 * unknown.
1984
 * @param out_east_lon_degree Pointer to a double to receive the east longitude
1985
 * (in degrees). Or NULL. If the returned value is -1000, the bounding box is
1986
 * unknown.
1987
 * @param out_north_lat_degree Pointer to a double to receive the north latitude
1988
 * (in degrees). Or NULL. If the returned value is -1000, the bounding box is
1989
 * unknown.
1990
 * @param out_area_name Pointer to a string to receive the name of the area of
1991
 * use. Or NULL. *p_area_name is valid while obj is valid itself.
1992
 * @return TRUE in case of success, FALSE in case of error or if the area
1993
 * of use is unknown.
1994
 */
1995
int proj_get_area_of_use_ex(PJ_CONTEXT *ctx, const PJ *obj, int domainIdx,
1996
                            double *out_west_lon_degree,
1997
                            double *out_south_lat_degree,
1998
                            double *out_east_lon_degree,
1999
                            double *out_north_lat_degree,
2000
25.6k
                            const char **out_area_name) {
2001
25.6k
    (void)ctx;
2002
25.6k
    if (out_area_name) {
2003
25.6k
        *out_area_name = nullptr;
2004
25.6k
    }
2005
25.6k
    auto objectUsage = dynamic_cast<const ObjectUsage *>(obj->iso_obj.get());
2006
25.6k
    if (!objectUsage) {
2007
0
        return false;
2008
0
    }
2009
25.6k
    const auto &domains = objectUsage->domains();
2010
25.6k
    if (domainIdx < 0 || static_cast<size_t>(domainIdx) >= domains.size()) {
2011
0
        return false;
2012
0
    }
2013
25.6k
    const auto &extent = domains[domainIdx]->domainOfValidity();
2014
25.6k
    if (!extent) {
2015
0
        return false;
2016
0
    }
2017
25.6k
    const auto &desc = extent->description();
2018
25.6k
    if (desc.has_value() && out_area_name) {
2019
21.2k
        *out_area_name = desc->c_str();
2020
21.2k
    }
2021
2022
25.6k
    const auto &geogElements = extent->geographicElements();
2023
25.6k
    if (!geogElements.empty()) {
2024
25.6k
        auto bbox =
2025
25.6k
            dynamic_cast<const GeographicBoundingBox *>(geogElements[0].get());
2026
25.6k
        if (bbox) {
2027
25.6k
            if (out_west_lon_degree) {
2028
25.6k
                *out_west_lon_degree = bbox->westBoundLongitude();
2029
25.6k
            }
2030
25.6k
            if (out_south_lat_degree) {
2031
25.6k
                *out_south_lat_degree = bbox->southBoundLatitude();
2032
25.6k
            }
2033
25.6k
            if (out_east_lon_degree) {
2034
25.6k
                *out_east_lon_degree = bbox->eastBoundLongitude();
2035
25.6k
            }
2036
25.6k
            if (out_north_lat_degree) {
2037
25.6k
                *out_north_lat_degree = bbox->northBoundLatitude();
2038
25.6k
            }
2039
25.6k
            return true;
2040
25.6k
        }
2041
25.6k
    }
2042
0
    if (out_west_lon_degree) {
2043
0
        *out_west_lon_degree = -1000;
2044
0
    }
2045
0
    if (out_south_lat_degree) {
2046
0
        *out_south_lat_degree = -1000;
2047
0
    }
2048
0
    if (out_east_lon_degree) {
2049
0
        *out_east_lon_degree = -1000;
2050
0
    }
2051
0
    if (out_north_lat_degree) {
2052
0
        *out_north_lat_degree = -1000;
2053
0
    }
2054
0
    return true;
2055
25.6k
}
2056
2057
// ---------------------------------------------------------------------------
2058
2059
static const GeodeticCRS *extractGeodeticCRS(PJ_CONTEXT *ctx, const PJ *crs,
2060
2.32k
                                             const char *fname) {
2061
2.32k
    if (!crs) {
2062
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2063
0
        proj_log_error(ctx, fname, "missing required input");
2064
0
        return nullptr;
2065
0
    }
2066
2.32k
    auto l_crs = dynamic_cast<const CRS *>(crs->iso_obj.get());
2067
2.32k
    if (!l_crs) {
2068
0
        proj_log_error(ctx, fname, "Object is not a CRS");
2069
0
        return nullptr;
2070
0
    }
2071
2.32k
    auto geodCRS = l_crs->extractGeodeticCRSRaw();
2072
2.32k
    if (!geodCRS) {
2073
0
        proj_log_error(ctx, fname, "CRS has no geodetic CRS");
2074
0
    }
2075
2.32k
    return geodCRS;
2076
2.32k
}
2077
2078
// ---------------------------------------------------------------------------
2079
2080
/** \brief Get the geodeticCRS / geographicCRS from a CRS
2081
 *
2082
 * The returned object must be unreferenced with proj_destroy() after
2083
 * use.
2084
 * It should be used by at most one thread at a time.
2085
 *
2086
 * @param ctx PROJ context, or NULL for default context
2087
 * @param crs Object of type CRS (must not be NULL)
2088
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2089
 * in case of error.
2090
 */
2091
2.32k
PJ *proj_crs_get_geodetic_crs(PJ_CONTEXT *ctx, const PJ *crs) {
2092
2.32k
    SANITIZE_CTX(ctx);
2093
2.32k
    auto geodCRS = extractGeodeticCRS(ctx, crs, __FUNCTION__);
2094
2.32k
    if (!geodCRS) {
2095
0
        return nullptr;
2096
0
    }
2097
2.32k
    return pj_obj_create(ctx,
2098
2.32k
                         NN_NO_CHECK(nn_dynamic_pointer_cast<IdentifiedObject>(
2099
2.32k
                             geodCRS->shared_from_this())));
2100
2.32k
}
2101
2102
// ---------------------------------------------------------------------------
2103
2104
/** \brief Returns whether a CRS is a derived CRS.
2105
 *
2106
 * @param ctx PROJ context, or NULL for default context
2107
 * @param crs Object of type CRS (must not be NULL)
2108
 * @return TRUE if the CRS is a derived CRS.
2109
 * @since 8.0
2110
 */
2111
0
int proj_crs_is_derived(PJ_CONTEXT *ctx, const PJ *crs) {
2112
0
    if (!crs) {
2113
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2114
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2115
0
        return false;
2116
0
    }
2117
0
    auto l_crs = dynamic_cast<const CRS *>(crs->iso_obj.get());
2118
0
    if (!l_crs) {
2119
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CRS");
2120
0
        return false;
2121
0
    }
2122
0
    return dynamic_cast<const DerivedCRS *>(l_crs) != nullptr;
2123
0
}
2124
2125
// ---------------------------------------------------------------------------
2126
2127
/** \brief Get a CRS component from a CompoundCRS
2128
 *
2129
 * The returned object must be unreferenced with proj_destroy() after
2130
 * use.
2131
 * It should be used by at most one thread at a time.
2132
 *
2133
 * @param ctx PROJ context, or NULL for default context
2134
 * @param crs Object of type CRS (must not be NULL)
2135
 * @param index Index of the CRS component (typically 0 = horizontal, 1 =
2136
 * vertical)
2137
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2138
 * in case of error.
2139
 */
2140
0
PJ *proj_crs_get_sub_crs(PJ_CONTEXT *ctx, const PJ *crs, int index) {
2141
0
    SANITIZE_CTX(ctx);
2142
0
    if (!crs) {
2143
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2144
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2145
0
        return nullptr;
2146
0
    }
2147
0
    auto l_crs = dynamic_cast<CompoundCRS *>(crs->iso_obj.get());
2148
0
    if (!l_crs) {
2149
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CompoundCRS");
2150
0
        return nullptr;
2151
0
    }
2152
0
    const auto &components = l_crs->componentReferenceSystems();
2153
0
    if (static_cast<size_t>(index) >= components.size()) {
2154
0
        return nullptr;
2155
0
    }
2156
0
    return pj_obj_create(ctx, components[index]);
2157
0
}
2158
2159
// ---------------------------------------------------------------------------
2160
2161
/** \brief Returns a BoundCRS
2162
 *
2163
 * The returned object must be unreferenced with proj_destroy() after
2164
 * use.
2165
 * It should be used by at most one thread at a time.
2166
 *
2167
 * @param ctx PROJ context, or NULL for default context
2168
 * @param base_crs Base CRS (must not be NULL)
2169
 * @param hub_crs Hub CRS (must not be NULL)
2170
 * @param transformation Transformation (must not be NULL)
2171
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2172
 * in case of error.
2173
 */
2174
PJ *proj_crs_create_bound_crs(PJ_CONTEXT *ctx, const PJ *base_crs,
2175
0
                              const PJ *hub_crs, const PJ *transformation) {
2176
0
    SANITIZE_CTX(ctx);
2177
0
    if (!base_crs || !hub_crs || !transformation) {
2178
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2179
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2180
0
        return nullptr;
2181
0
    }
2182
0
    auto l_base_crs = std::dynamic_pointer_cast<CRS>(base_crs->iso_obj);
2183
0
    if (!l_base_crs) {
2184
0
        proj_log_error(ctx, __FUNCTION__, "base_crs is not a CRS");
2185
0
        return nullptr;
2186
0
    }
2187
0
    auto l_hub_crs = std::dynamic_pointer_cast<CRS>(hub_crs->iso_obj);
2188
0
    if (!l_hub_crs) {
2189
0
        proj_log_error(ctx, __FUNCTION__, "hub_crs is not a CRS");
2190
0
        return nullptr;
2191
0
    }
2192
0
    auto l_transformation =
2193
0
        std::dynamic_pointer_cast<Transformation>(transformation->iso_obj);
2194
0
    if (!l_transformation) {
2195
0
        proj_log_error(ctx, __FUNCTION__, "transformation is not a CRS");
2196
0
        return nullptr;
2197
0
    }
2198
0
    try {
2199
0
        return pj_obj_create(ctx,
2200
0
                             BoundCRS::create(NN_NO_CHECK(l_base_crs),
2201
0
                                              NN_NO_CHECK(l_hub_crs),
2202
0
                                              NN_NO_CHECK(l_transformation)));
2203
0
    } catch (const std::exception &e) {
2204
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2205
0
        return nullptr;
2206
0
    }
2207
0
}
2208
2209
// ---------------------------------------------------------------------------
2210
2211
/** \brief Returns potentially
2212
 * a BoundCRS, with a transformation to EPSG:4326, wrapping this CRS
2213
 *
2214
 * The returned object must be unreferenced with proj_destroy() after
2215
 * use.
2216
 * It should be used by at most one thread at a time.
2217
 *
2218
 * This is the same as method
2219
 * osgeo::proj::crs::CRS::createBoundCRSToWGS84IfPossible()
2220
 *
2221
 * @param ctx PROJ context, or NULL for default context
2222
 * @param crs Object of type CRS (must not be NULL)
2223
 * @param options null-terminated list of options, or NULL. Currently
2224
 * supported options are:
2225
 * <ul>
2226
 * <li>ALLOW_INTERMEDIATE_CRS=ALWAYS/IF_NO_DIRECT_TRANSFORMATION/NEVER. Defaults
2227
 * to NEVER. When set to ALWAYS/IF_NO_DIRECT_TRANSFORMATION,
2228
 * intermediate CRS may be considered when computing the possible
2229
 * transformations. Slower.</li>
2230
 * </ul>
2231
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2232
 * in case of error.
2233
 */
2234
PJ *proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, const PJ *crs,
2235
0
                                       const char *const *options) {
2236
0
    SANITIZE_CTX(ctx);
2237
0
    if (!crs) {
2238
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2239
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2240
0
        return nullptr;
2241
0
    }
2242
0
    auto l_crs = dynamic_cast<const CRS *>(crs->iso_obj.get());
2243
0
    if (!l_crs) {
2244
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CRS");
2245
0
        return nullptr;
2246
0
    }
2247
0
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
2248
0
    try {
2249
0
        CoordinateOperationContext::IntermediateCRSUse allowIntermediateCRS =
2250
0
            CoordinateOperationContext::IntermediateCRSUse::NEVER;
2251
0
        for (auto iter = options; iter && iter[0]; ++iter) {
2252
0
            const char *value;
2253
0
            if ((value = getOptionValue(*iter, "ALLOW_INTERMEDIATE_CRS="))) {
2254
0
                if (ci_equal(value, "YES") || ci_equal(value, "ALWAYS")) {
2255
0
                    allowIntermediateCRS =
2256
0
                        CoordinateOperationContext::IntermediateCRSUse::ALWAYS;
2257
0
                } else if (ci_equal(value, "IF_NO_DIRECT_TRANSFORMATION")) {
2258
0
                    allowIntermediateCRS = CoordinateOperationContext::
2259
0
                        IntermediateCRSUse::IF_NO_DIRECT_TRANSFORMATION;
2260
0
                }
2261
0
            } else {
2262
0
                std::string msg("Unknown option :");
2263
0
                msg += *iter;
2264
0
                proj_log_error(ctx, __FUNCTION__, msg.c_str());
2265
0
                return nullptr;
2266
0
            }
2267
0
        }
2268
0
        return pj_obj_create(ctx, l_crs->createBoundCRSToWGS84IfPossible(
2269
0
                                      dbContext, allowIntermediateCRS));
2270
0
    } catch (const std::exception &e) {
2271
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2272
0
        return nullptr;
2273
0
    }
2274
0
}
2275
2276
// ---------------------------------------------------------------------------
2277
2278
/** \brief Returns a BoundCRS, with a transformation to a hub geographic 3D crs
2279
 * (use EPSG:4979 for WGS84 for example), using a grid.
2280
 *
2281
 * The returned object must be unreferenced with proj_destroy() after
2282
 * use.
2283
 * It should be used by at most one thread at a time.
2284
 *
2285
 * @param ctx PROJ context, or NULL for default context
2286
 * @param vert_crs Object of type VerticalCRS (must not be NULL)
2287
 * @param hub_geographic_3D_crs Object of type Geographic 3D CRS (must not be
2288
 * NULL)
2289
 * @param grid_name Grid name (typically a .gtx file)
2290
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2291
 * in case of error.
2292
 * @since 6.3
2293
 */
2294
PJ *proj_crs_create_bound_vertical_crs(PJ_CONTEXT *ctx, const PJ *vert_crs,
2295
                                       const PJ *hub_geographic_3D_crs,
2296
0
                                       const char *grid_name) {
2297
0
    SANITIZE_CTX(ctx);
2298
0
    if (!vert_crs || !hub_geographic_3D_crs || !grid_name) {
2299
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2300
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2301
0
        return nullptr;
2302
0
    }
2303
0
    auto l_crs = std::dynamic_pointer_cast<VerticalCRS>(vert_crs->iso_obj);
2304
0
    if (!l_crs) {
2305
0
        proj_log_error(ctx, __FUNCTION__, "vert_crs is not a VerticalCRS");
2306
0
        return nullptr;
2307
0
    }
2308
0
    auto hub_crs =
2309
0
        std::dynamic_pointer_cast<CRS>(hub_geographic_3D_crs->iso_obj);
2310
0
    if (!hub_crs) {
2311
0
        proj_log_error(ctx, __FUNCTION__, "hub_geographic_3D_crs is not a CRS");
2312
0
        return nullptr;
2313
0
    }
2314
0
    try {
2315
0
        auto nnCRS = NN_NO_CHECK(l_crs);
2316
0
        auto nnHubCRS = NN_NO_CHECK(hub_crs);
2317
0
        auto transformation =
2318
0
            Transformation::createGravityRelatedHeightToGeographic3D(
2319
0
                PropertyMap().set(IdentifiedObject::NAME_KEY,
2320
0
                                  "unknown to " + hub_crs->nameStr() +
2321
0
                                      " ellipsoidal height"),
2322
0
                nnCRS, nnHubCRS, nullptr, std::string(grid_name),
2323
0
                std::vector<PositionalAccuracyNNPtr>());
2324
0
        return pj_obj_create(ctx,
2325
0
                             BoundCRS::create(nnCRS, nnHubCRS, transformation));
2326
0
    } catch (const std::exception &e) {
2327
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2328
0
        return nullptr;
2329
0
    }
2330
0
}
2331
2332
// ---------------------------------------------------------------------------
2333
2334
/** \brief Get the ellipsoid from a CRS or a GeodeticReferenceFrame.
2335
 *
2336
 * The returned object must be unreferenced with proj_destroy() after
2337
 * use.
2338
 * It should be used by at most one thread at a time.
2339
 *
2340
 * @param ctx PROJ context, or NULL for default context
2341
 * @param obj Object of type CRS or GeodeticReferenceFrame (must not be NULL)
2342
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2343
 * in case of error.
2344
 */
2345
2.74k
PJ *proj_get_ellipsoid(PJ_CONTEXT *ctx, const PJ *obj) {
2346
2.74k
    SANITIZE_CTX(ctx);
2347
2.74k
    auto ptr = obj->iso_obj.get();
2348
2.74k
    if (dynamic_cast<const CRS *>(ptr)) {
2349
0
        auto geodCRS = extractGeodeticCRS(ctx, obj, __FUNCTION__);
2350
0
        if (geodCRS) {
2351
0
            return pj_obj_create(ctx, geodCRS->ellipsoid());
2352
0
        }
2353
2.74k
    } else {
2354
2.74k
        auto datum = dynamic_cast<const GeodeticReferenceFrame *>(ptr);
2355
2.74k
        if (datum) {
2356
2.74k
            return pj_obj_create(ctx, datum->ellipsoid());
2357
2.74k
        }
2358
2.74k
    }
2359
0
    proj_log_error(ctx, __FUNCTION__,
2360
0
                   "Object is not a CRS or GeodeticReferenceFrame");
2361
0
    return nullptr;
2362
2.74k
}
2363
2364
// ---------------------------------------------------------------------------
2365
2366
/** \brief Get the name of the celestial body of this object.
2367
 *
2368
 * Object should be a CRS, Datum or Ellipsoid.
2369
 *
2370
 * @param ctx PROJ context, or NULL for default context
2371
 * @param obj Object of type CRS, Datum or Ellipsoid.(must not be NULL)
2372
 * @return the name of the celestial body, or NULL.
2373
 * @since 8.1
2374
 */
2375
0
const char *proj_get_celestial_body_name(PJ_CONTEXT *ctx, const PJ *obj) {
2376
0
    SANITIZE_CTX(ctx);
2377
0
    const BaseObject *ptr = obj->iso_obj.get();
2378
0
    if (dynamic_cast<const CRS *>(ptr)) {
2379
0
        const auto geodCRS = extractGeodeticCRS(ctx, obj, __FUNCTION__);
2380
0
        if (!geodCRS) {
2381
            // FIXME when vertical CRS can be non-EARTH...
2382
0
            return datum::Ellipsoid::EARTH.c_str();
2383
0
        }
2384
0
        return geodCRS->ellipsoid()->celestialBody().c_str();
2385
0
    }
2386
0
    const auto ensemble = dynamic_cast<const DatumEnsemble *>(ptr);
2387
0
    if (ensemble) {
2388
0
        ptr = ensemble->datums().front().get();
2389
        // Go on
2390
0
    }
2391
0
    const auto geodetic_datum =
2392
0
        dynamic_cast<const GeodeticReferenceFrame *>(ptr);
2393
0
    if (geodetic_datum) {
2394
0
        return geodetic_datum->ellipsoid()->celestialBody().c_str();
2395
0
    }
2396
0
    const auto vertical_datum =
2397
0
        dynamic_cast<const VerticalReferenceFrame *>(ptr);
2398
0
    if (vertical_datum) {
2399
        // FIXME when vertical CRS can be non-EARTH...
2400
0
        return datum::Ellipsoid::EARTH.c_str();
2401
0
    }
2402
0
    const auto ellipsoid = dynamic_cast<const Ellipsoid *>(ptr);
2403
0
    if (ellipsoid) {
2404
0
        return ellipsoid->celestialBody().c_str();
2405
0
    }
2406
0
    proj_log_error(ctx, __FUNCTION__,
2407
0
                   "Object is not a CRS, Datum or Ellipsoid");
2408
0
    return nullptr;
2409
0
}
2410
2411
// ---------------------------------------------------------------------------
2412
2413
/** \brief Get the horizontal datum from a CRS
2414
 *
2415
 * This function may return a Datum or DatumEnsemble object.
2416
 *
2417
 * The returned object must be unreferenced with proj_destroy() after
2418
 * use.
2419
 * It should be used by at most one thread at a time.
2420
 *
2421
 * @param ctx PROJ context, or NULL for default context
2422
 * @param crs Object of type CRS (must not be NULL)
2423
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2424
 * in case of error.
2425
 */
2426
0
PJ *proj_crs_get_horizontal_datum(PJ_CONTEXT *ctx, const PJ *crs) {
2427
0
    SANITIZE_CTX(ctx);
2428
0
    auto geodCRS = extractGeodeticCRS(ctx, crs, __FUNCTION__);
2429
0
    if (!geodCRS) {
2430
0
        return nullptr;
2431
0
    }
2432
0
    const auto &datum = geodCRS->datum();
2433
0
    if (datum) {
2434
0
        return pj_obj_create(ctx, NN_NO_CHECK(datum));
2435
0
    }
2436
2437
0
    const auto &datumEnsemble = geodCRS->datumEnsemble();
2438
0
    if (datumEnsemble) {
2439
0
        return pj_obj_create(ctx, NN_NO_CHECK(datumEnsemble));
2440
0
    }
2441
0
    proj_log_error(ctx, __FUNCTION__, "CRS has no datum");
2442
0
    return nullptr;
2443
0
}
2444
2445
// ---------------------------------------------------------------------------
2446
2447
/** \brief Return ellipsoid parameters.
2448
 *
2449
 * @param ctx PROJ context, or NULL for default context
2450
 * @param ellipsoid Object of type Ellipsoid (must not be NULL)
2451
 * @param out_semi_major_metre Pointer to a value to store the semi-major axis
2452
 * in
2453
 * metre. or NULL
2454
 * @param out_semi_minor_metre Pointer to a value to store the semi-minor axis
2455
 * in
2456
 * metre. or NULL
2457
 * @param out_is_semi_minor_computed Pointer to a boolean value to indicate if
2458
 * the
2459
 * semi-minor value was computed. If FALSE, its value comes from the
2460
 * definition. or NULL
2461
 * @param out_inv_flattening Pointer to a value to store the inverse
2462
 * flattening. or NULL
2463
 * @return TRUE in case of success.
2464
 */
2465
int proj_ellipsoid_get_parameters(PJ_CONTEXT *ctx, const PJ *ellipsoid,
2466
                                  double *out_semi_major_metre,
2467
                                  double *out_semi_minor_metre,
2468
                                  int *out_is_semi_minor_computed,
2469
2.74k
                                  double *out_inv_flattening) {
2470
2.74k
    SANITIZE_CTX(ctx);
2471
2.74k
    if (!ellipsoid) {
2472
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2473
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2474
0
        return FALSE;
2475
0
    }
2476
2.74k
    auto l_ellipsoid =
2477
2.74k
        dynamic_cast<const Ellipsoid *>(ellipsoid->iso_obj.get());
2478
2.74k
    if (!l_ellipsoid) {
2479
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a Ellipsoid");
2480
0
        return FALSE;
2481
0
    }
2482
2483
2.74k
    if (out_semi_major_metre) {
2484
2.74k
        *out_semi_major_metre = l_ellipsoid->semiMajorAxis().getSIValue();
2485
2.74k
    }
2486
2.74k
    if (out_semi_minor_metre) {
2487
0
        *out_semi_minor_metre =
2488
0
            l_ellipsoid->computeSemiMinorAxis().getSIValue();
2489
0
    }
2490
2.74k
    if (out_is_semi_minor_computed) {
2491
0
        *out_is_semi_minor_computed =
2492
0
            !(l_ellipsoid->semiMinorAxis().has_value());
2493
0
    }
2494
2.74k
    if (out_inv_flattening) {
2495
2.74k
        *out_inv_flattening = l_ellipsoid->computedInverseFlattening();
2496
2.74k
    }
2497
2.74k
    return TRUE;
2498
2.74k
}
2499
2500
// ---------------------------------------------------------------------------
2501
2502
/** \brief Get the prime meridian of a CRS or a GeodeticReferenceFrame.
2503
 *
2504
 * The returned object must be unreferenced with proj_destroy() after
2505
 * use.
2506
 * It should be used by at most one thread at a time.
2507
 *
2508
 * @param ctx PROJ context, or NULL for default context
2509
 * @param obj Object of type CRS or GeodeticReferenceFrame (must not be NULL)
2510
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2511
 * in case of error.
2512
 */
2513
2514
0
PJ *proj_get_prime_meridian(PJ_CONTEXT *ctx, const PJ *obj) {
2515
0
    SANITIZE_CTX(ctx);
2516
0
    auto ptr = obj->iso_obj.get();
2517
0
    if (dynamic_cast<CRS *>(ptr)) {
2518
0
        auto geodCRS = extractGeodeticCRS(ctx, obj, __FUNCTION__);
2519
0
        if (geodCRS) {
2520
0
            return pj_obj_create(ctx, geodCRS->primeMeridian());
2521
0
        }
2522
0
    } else {
2523
0
        auto datum = dynamic_cast<const GeodeticReferenceFrame *>(ptr);
2524
0
        if (datum) {
2525
0
            return pj_obj_create(ctx, datum->primeMeridian());
2526
0
        }
2527
0
    }
2528
0
    proj_log_error(ctx, __FUNCTION__,
2529
0
                   "Object is not a CRS or GeodeticReferenceFrame");
2530
0
    return nullptr;
2531
0
}
2532
2533
// ---------------------------------------------------------------------------
2534
2535
/** \brief Return prime meridian parameters.
2536
 *
2537
 * @param ctx PROJ context, or NULL for default context
2538
 * @param prime_meridian Object of type PrimeMeridian (must not be NULL)
2539
 * @param out_longitude Pointer to a value to store the longitude of the prime
2540
 * meridian, in its native unit. or NULL
2541
 * @param out_unit_conv_factor Pointer to a value to store the conversion
2542
 * factor of the prime meridian longitude unit to radian. or NULL
2543
 * @param out_unit_name Pointer to a string value to store the unit name.
2544
 * or NULL
2545
 * @return TRUE in case of success.
2546
 */
2547
int proj_prime_meridian_get_parameters(PJ_CONTEXT *ctx,
2548
                                       const PJ *prime_meridian,
2549
                                       double *out_longitude,
2550
                                       double *out_unit_conv_factor,
2551
0
                                       const char **out_unit_name) {
2552
0
    SANITIZE_CTX(ctx);
2553
0
    if (!prime_meridian) {
2554
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2555
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2556
0
        return false;
2557
0
    }
2558
0
    auto l_pm =
2559
0
        dynamic_cast<const PrimeMeridian *>(prime_meridian->iso_obj.get());
2560
0
    if (!l_pm) {
2561
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a PrimeMeridian");
2562
0
        return false;
2563
0
    }
2564
0
    const auto &longitude = l_pm->longitude();
2565
0
    if (out_longitude) {
2566
0
        *out_longitude = longitude.value();
2567
0
    }
2568
0
    const auto &unit = longitude.unit();
2569
0
    if (out_unit_conv_factor) {
2570
0
        *out_unit_conv_factor = unit.conversionToSI();
2571
0
    }
2572
0
    if (out_unit_name) {
2573
0
        *out_unit_name = unit.name().c_str();
2574
0
    }
2575
0
    return true;
2576
0
}
2577
2578
// ---------------------------------------------------------------------------
2579
2580
/** \brief Return the base CRS of a BoundCRS or a DerivedCRS/ProjectedCRS, or
2581
 * the source CRS of a CoordinateOperation, or the CRS of a CoordinateMetadata.
2582
 *
2583
 * The returned object must be unreferenced with proj_destroy() after
2584
 * use.
2585
 * It should be used by at most one thread at a time.
2586
 *
2587
 * @param ctx PROJ context, or NULL for default context
2588
 * @param obj Object of type BoundCRS or CoordinateOperation (must not be NULL)
2589
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2590
 * in case of error, or missing source CRS.
2591
 */
2592
13.8k
PJ *proj_get_source_crs(PJ_CONTEXT *ctx, const PJ *obj) {
2593
13.8k
    SANITIZE_CTX(ctx);
2594
13.8k
    if (!obj) {
2595
0
        return nullptr;
2596
0
    }
2597
13.8k
    auto ptr = obj->iso_obj.get();
2598
13.8k
    auto boundCRS = dynamic_cast<const BoundCRS *>(ptr);
2599
13.8k
    if (boundCRS) {
2600
0
        return pj_obj_create(ctx, boundCRS->baseCRS());
2601
0
    }
2602
13.8k
    auto derivedCRS = dynamic_cast<const DerivedCRS *>(ptr);
2603
13.8k
    if (derivedCRS) {
2604
0
        return pj_obj_create(ctx, derivedCRS->baseCRS());
2605
0
    }
2606
13.8k
    auto co = dynamic_cast<const CoordinateOperation *>(ptr);
2607
13.8k
    if (co) {
2608
13.7k
        auto sourceCRS = co->sourceCRS();
2609
13.7k
        if (sourceCRS) {
2610
13.7k
            return pj_obj_create(ctx, NN_NO_CHECK(sourceCRS));
2611
13.7k
        }
2612
0
        return nullptr;
2613
13.7k
    }
2614
8
    if (!obj->alternativeCoordinateOperations.empty()) {
2615
0
        return proj_get_source_crs(ctx,
2616
0
                                   obj->alternativeCoordinateOperations[0].pj);
2617
0
    }
2618
8
    auto coordinateMetadata = dynamic_cast<const CoordinateMetadata *>(ptr);
2619
8
    if (coordinateMetadata) {
2620
8
        return pj_obj_create(ctx, coordinateMetadata->crs());
2621
8
    }
2622
2623
0
    proj_log_error(ctx, __FUNCTION__,
2624
0
                   "Object is not a BoundCRS, a CoordinateOperation or a "
2625
0
                   "CoordinateMetadata");
2626
0
    return nullptr;
2627
8
}
2628
2629
// ---------------------------------------------------------------------------
2630
2631
/** \brief Return the hub CRS of a BoundCRS or the target CRS of a
2632
 * CoordinateOperation.
2633
 *
2634
 * The returned object must be unreferenced with proj_destroy() after
2635
 * use.
2636
 * It should be used by at most one thread at a time.
2637
 *
2638
 * @param ctx PROJ context, or NULL for default context
2639
 * @param obj Object of type BoundCRS or CoordinateOperation (must not be NULL)
2640
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2641
 * in case of error, or missing target CRS.
2642
 */
2643
13.7k
PJ *proj_get_target_crs(PJ_CONTEXT *ctx, const PJ *obj) {
2644
13.7k
    SANITIZE_CTX(ctx);
2645
13.7k
    if (!obj) {
2646
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2647
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2648
0
        return nullptr;
2649
0
    }
2650
13.7k
    auto ptr = obj->iso_obj.get();
2651
13.7k
    auto boundCRS = dynamic_cast<const BoundCRS *>(ptr);
2652
13.7k
    if (boundCRS) {
2653
0
        return pj_obj_create(ctx, boundCRS->hubCRS());
2654
0
    }
2655
13.7k
    auto co = dynamic_cast<const CoordinateOperation *>(ptr);
2656
13.7k
    if (co) {
2657
13.7k
        auto targetCRS = co->targetCRS();
2658
13.7k
        if (targetCRS) {
2659
13.7k
            return pj_obj_create(ctx, NN_NO_CHECK(targetCRS));
2660
13.7k
        }
2661
0
        return nullptr;
2662
13.7k
    }
2663
0
    if (!obj->alternativeCoordinateOperations.empty()) {
2664
0
        return proj_get_target_crs(ctx,
2665
0
                                   obj->alternativeCoordinateOperations[0].pj);
2666
0
    }
2667
0
    proj_log_error(ctx, __FUNCTION__,
2668
0
                   "Object is not a BoundCRS or a CoordinateOperation");
2669
0
    return nullptr;
2670
0
}
2671
2672
// ---------------------------------------------------------------------------
2673
2674
/** \brief Identify the CRS with reference CRSs.
2675
 *
2676
 * The candidate CRSs are either hard-coded, or looked in the database when
2677
 * it is available.
2678
 *
2679
 * Note that the implementation uses a set of heuristics to have a good
2680
 * compromise of successful identifications over execution time. It might miss
2681
 * legitimate matches in some circumstances.
2682
 *
2683
 * The method returns a list of matching reference CRS, and the percentage
2684
 * (0-100) of confidence in the match. The list is sorted by decreasing
2685
 * confidence.
2686
 * <ul>
2687
 * <li>100% means that the name of the reference entry
2688
 * perfectly matches the CRS name, and both are equivalent. In which case a
2689
 * single result is returned.
2690
 * Note: in the case of a GeographicCRS whose axis
2691
 * order is implicit in the input definition (for example ESRI WKT), then axis
2692
 * order is ignored for the purpose of identification. That is the CRS built
2693
 * from
2694
 * GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],
2695
 * PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]
2696
 * will be identified to EPSG:4326, but will not pass a
2697
 * isEquivalentTo(EPSG_4326, util::IComparable::Criterion::EQUIVALENT) test,
2698
 * but rather isEquivalentTo(EPSG_4326,
2699
 * util::IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS)
2700
 * </li>
2701
 * <li>90% means that CRS are equivalent, but the names are not exactly the
2702
 * same.</li>
2703
 * <li>70% means that CRS are equivalent, but the names are not equivalent.
2704
 * </li>
2705
 * <li>25% means that the CRS are not equivalent, but there is some similarity
2706
 * in
2707
 * the names.</li>
2708
 * </ul>
2709
 * Other confidence values may be returned by some specialized implementations.
2710
 *
2711
 * This is implemented for GeodeticCRS, ProjectedCRS, VerticalCRS and
2712
 * CompoundCRS.
2713
 *
2714
 * @param ctx PROJ context, or NULL for default context
2715
 * @param obj Object of type CRS. Must not be NULL
2716
 * @param auth_name Authority name, or NULL for all authorities
2717
 * @param options Placeholder for future options. Should be set to NULL.
2718
 * @param out_confidence Output parameter. Pointer to an array of integers that
2719
 * will be allocated by the function and filled with the confidence values
2720
 * (0-100). There are as many elements in this array as
2721
 * proj_list_get_count()
2722
 * returns on the return value of this function. *confidence should be
2723
 * released with proj_int_list_destroy().
2724
 * @return a list of matching reference CRS, or nullptr in case of error.
2725
 */
2726
PJ_OBJ_LIST *proj_identify(PJ_CONTEXT *ctx, const PJ *obj,
2727
                           const char *auth_name, const char *const *options,
2728
0
                           int **out_confidence) {
2729
0
    SANITIZE_CTX(ctx);
2730
0
    if (!obj) {
2731
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2732
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2733
0
        return nullptr;
2734
0
    }
2735
0
    (void)options;
2736
0
    if (out_confidence) {
2737
0
        *out_confidence = nullptr;
2738
0
    }
2739
0
    auto ptr = obj->iso_obj.get();
2740
0
    auto crs = dynamic_cast<const CRS *>(ptr);
2741
0
    if (!crs) {
2742
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CRS");
2743
0
    } else {
2744
0
        int *confidenceTemp = nullptr;
2745
0
        try {
2746
0
            auto factory = AuthorityFactory::create(getDBcontext(ctx),
2747
0
                                                    auth_name ? auth_name : "");
2748
0
            auto res = crs->identify(factory);
2749
0
            std::vector<IdentifiedObjectNNPtr> objects;
2750
0
            confidenceTemp = out_confidence ? new int[res.size()] : nullptr;
2751
0
            size_t i = 0;
2752
0
            for (const auto &pair : res) {
2753
0
                objects.push_back(pair.first);
2754
0
                if (confidenceTemp) {
2755
0
                    confidenceTemp[i] = pair.second;
2756
0
                    ++i;
2757
0
                }
2758
0
            }
2759
0
            auto ret = std::make_unique<PJ_OBJ_LIST>(std::move(objects));
2760
0
            if (out_confidence) {
2761
0
                *out_confidence = confidenceTemp;
2762
0
                confidenceTemp = nullptr;
2763
0
            }
2764
0
            return ret.release();
2765
0
        } catch (const std::exception &e) {
2766
0
            delete[] confidenceTemp;
2767
0
            proj_log_error(ctx, __FUNCTION__, e.what());
2768
0
        }
2769
0
    }
2770
0
    return nullptr;
2771
0
}
2772
2773
// ---------------------------------------------------------------------------
2774
2775
/** \brief Free an array of integer. */
2776
0
void proj_int_list_destroy(int *list) { delete[] list; }
2777
2778
// ---------------------------------------------------------------------------
2779
2780
/** \brief Return the list of authorities used in the database.
2781
 *
2782
 * The returned list is NULL terminated and must be freed with
2783
 * proj_string_list_destroy().
2784
 *
2785
 * @param ctx PROJ context, or NULL for default context
2786
 *
2787
 * @return a NULL terminated list of NUL-terminated strings that must be
2788
 * freed with proj_string_list_destroy(), or NULL in case of error.
2789
 */
2790
0
PROJ_STRING_LIST proj_get_authorities_from_database(PJ_CONTEXT *ctx) {
2791
0
    SANITIZE_CTX(ctx);
2792
0
    try {
2793
0
        auto ret = to_string_list(getDBcontext(ctx)->getAuthorities());
2794
0
        return ret;
2795
0
    } catch (const std::exception &e) {
2796
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2797
0
    }
2798
0
    return nullptr;
2799
0
}
2800
2801
// ---------------------------------------------------------------------------
2802
2803
/** \brief Returns the set of authority codes of the given object type.
2804
 *
2805
 * The returned list is NULL terminated and must be freed with
2806
 * proj_string_list_destroy().
2807
 *
2808
 * @param ctx PROJ context, or NULL for default context.
2809
 * @param auth_name Authority name (must not be NULL)
2810
 * @param type Object type.
2811
 * @param allow_deprecated whether we should return deprecated objects as well.
2812
 *
2813
 * @return a NULL terminated list of NUL-terminated strings that must be
2814
 * freed with proj_string_list_destroy(), or NULL in case of error.
2815
 *
2816
 * @see proj_get_crs_info_list_from_database()
2817
 */
2818
PROJ_STRING_LIST proj_get_codes_from_database(PJ_CONTEXT *ctx,
2819
                                              const char *auth_name,
2820
                                              PJ_TYPE type,
2821
0
                                              int allow_deprecated) {
2822
0
    SANITIZE_CTX(ctx);
2823
0
    if (!auth_name) {
2824
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2825
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2826
0
        return nullptr;
2827
0
    }
2828
0
    try {
2829
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx), auth_name);
2830
0
        bool valid = false;
2831
0
        auto typeInternal = convertPJObjectTypeToObjectType(type, valid);
2832
0
        if (!valid) {
2833
0
            return nullptr;
2834
0
        }
2835
0
        auto ret = to_string_list(
2836
0
            factory->getAuthorityCodes(typeInternal, allow_deprecated != 0));
2837
0
        return ret;
2838
2839
0
    } catch (const std::exception &e) {
2840
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2841
0
    }
2842
0
    return nullptr;
2843
0
}
2844
2845
// ---------------------------------------------------------------------------
2846
2847
/** \brief Enumerate celestial bodies from the database.
2848
 *
2849
 * The returned object is an array of PROJ_CELESTIAL_BODY_INFO* pointers, whose
2850
 * last entry is NULL. This array should be freed with
2851
 * proj_celestial_body_list_destroy()
2852
 *
2853
 * @param ctx PROJ context, or NULL for default context
2854
 * @param auth_name Authority name, used to restrict the search.
2855
 * Or NULL for all authorities.
2856
 * @param out_result_count Output parameter pointing to an integer to receive
2857
 * the size of the result list. Might be NULL
2858
 * @return an array of PROJ_CELESTIAL_BODY_INFO* pointers to be freed with
2859
 * proj_celestial_body_list_destroy(), or NULL in case of error.
2860
 * @since 8.1
2861
 */
2862
PROJ_CELESTIAL_BODY_INFO **proj_get_celestial_body_list_from_database(
2863
0
    PJ_CONTEXT *ctx, const char *auth_name, int *out_result_count) {
2864
0
    SANITIZE_CTX(ctx);
2865
0
    PROJ_CELESTIAL_BODY_INFO **ret = nullptr;
2866
0
    int i = 0;
2867
0
    try {
2868
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx),
2869
0
                                                auth_name ? auth_name : "");
2870
0
        auto list = factory->getCelestialBodyList();
2871
0
        ret = new PROJ_CELESTIAL_BODY_INFO *[list.size() + 1];
2872
0
        for (const auto &info : list) {
2873
0
            ret[i] = new PROJ_CELESTIAL_BODY_INFO;
2874
0
            ret[i]->auth_name = pj_strdup(info.authName.c_str());
2875
0
            ret[i]->name = pj_strdup(info.name.c_str());
2876
0
            i++;
2877
0
        }
2878
0
        ret[i] = nullptr;
2879
0
        if (out_result_count)
2880
0
            *out_result_count = i;
2881
0
        return ret;
2882
0
    } catch (const std::exception &e) {
2883
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2884
0
        if (ret) {
2885
0
            ret[i + 1] = nullptr;
2886
0
            proj_celestial_body_list_destroy(ret);
2887
0
        }
2888
0
        if (out_result_count)
2889
0
            *out_result_count = 0;
2890
0
    }
2891
0
    return nullptr;
2892
0
}
2893
2894
// ---------------------------------------------------------------------------
2895
2896
/** \brief Destroy the result returned by
2897
 * proj_get_celestial_body_list_from_database().
2898
 *
2899
 * @since 8.1
2900
 */
2901
0
void proj_celestial_body_list_destroy(PROJ_CELESTIAL_BODY_INFO **list) {
2902
0
    if (list) {
2903
0
        for (int i = 0; list[i] != nullptr; i++) {
2904
0
            free(list[i]->auth_name);
2905
0
            free(list[i]->name);
2906
0
            delete list[i];
2907
0
        }
2908
0
        delete[] list;
2909
0
    }
2910
0
}
2911
2912
// ---------------------------------------------------------------------------
2913
2914
/** Free a list of NULL terminated strings. */
2915
0
void proj_string_list_destroy(PROJ_STRING_LIST list) {
2916
0
    if (list) {
2917
0
        for (size_t i = 0; list[i] != nullptr; i++) {
2918
0
            delete[] list[i];
2919
0
        }
2920
0
        delete[] list;
2921
0
    }
2922
0
}
2923
2924
// ---------------------------------------------------------------------------
2925
2926
/** \brief Instantiate a default set of parameters to be used by
2927
 * proj_get_crs_list().
2928
 *
2929
 * @return a new object to free with proj_get_crs_list_parameters_destroy() */
2930
0
PROJ_CRS_LIST_PARAMETERS *proj_get_crs_list_parameters_create() {
2931
0
    auto ret = new (std::nothrow) PROJ_CRS_LIST_PARAMETERS();
2932
0
    if (ret) {
2933
0
        ret->types = nullptr;
2934
0
        ret->typesCount = 0;
2935
0
        ret->crs_area_of_use_contains_bbox = TRUE;
2936
0
        ret->bbox_valid = FALSE;
2937
0
        ret->west_lon_degree = 0.0;
2938
0
        ret->south_lat_degree = 0.0;
2939
0
        ret->east_lon_degree = 0.0;
2940
0
        ret->north_lat_degree = 0.0;
2941
0
        ret->allow_deprecated = FALSE;
2942
0
        ret->celestial_body_name = nullptr;
2943
0
    }
2944
0
    return ret;
2945
0
}
2946
2947
// ---------------------------------------------------------------------------
2948
2949
/** \brief Destroy an object returned by proj_get_crs_list_parameters_create()
2950
 */
2951
0
void proj_get_crs_list_parameters_destroy(PROJ_CRS_LIST_PARAMETERS *params) {
2952
0
    delete params;
2953
0
}
2954
2955
// ---------------------------------------------------------------------------
2956
2957
/** \brief Enumerate CRS objects from the database, taking into account various
2958
 * criteria.
2959
 *
2960
 * The returned object is an array of PROJ_CRS_INFO* pointers, whose last
2961
 * entry is NULL. This array should be freed with proj_crs_info_list_destroy()
2962
 *
2963
 * When no filter parameters are set, this is functionally equivalent to
2964
 * proj_get_codes_from_database(), instantiating a PJ* object for each
2965
 * of the codes with proj_create_from_database() and retrieving information
2966
 * with the various getters. However this function will be much faster.
2967
 *
2968
 * @param ctx PROJ context, or NULL for default context
2969
 * @param auth_name Authority name, used to restrict the search.
2970
 * Or NULL for all authorities.
2971
 * @param params Additional criteria, or NULL. If not-NULL, params SHOULD
2972
 * have been allocated by proj_get_crs_list_parameters_create(), as the
2973
 * PROJ_CRS_LIST_PARAMETERS structure might grow over time.
2974
 * @param out_result_count Output parameter pointing to an integer to receive
2975
 * the size of the result list. Might be NULL
2976
 * @return an array of PROJ_CRS_INFO* pointers to be freed with
2977
 * proj_crs_info_list_destroy(), or NULL in case of error.
2978
 */
2979
PROJ_CRS_INFO **
2980
proj_get_crs_info_list_from_database(PJ_CONTEXT *ctx, const char *auth_name,
2981
                                     const PROJ_CRS_LIST_PARAMETERS *params,
2982
0
                                     int *out_result_count) {
2983
0
    SANITIZE_CTX(ctx);
2984
0
    PROJ_CRS_INFO **ret = nullptr;
2985
0
    int i = 0;
2986
0
    try {
2987
0
        auto dbContext = getDBcontext(ctx);
2988
0
        std::string authName = auth_name ? auth_name : "";
2989
0
        auto actualAuthNames =
2990
0
            dbContext->getVersionedAuthoritiesFromName(authName);
2991
0
        if (actualAuthNames.empty())
2992
0
            actualAuthNames.push_back(std::move(authName));
2993
0
        std::list<AuthorityFactory::CRSInfo> concatList;
2994
0
        for (const auto &actualAuthName : actualAuthNames) {
2995
0
            auto factory = AuthorityFactory::create(dbContext, actualAuthName);
2996
0
            auto list = factory->getCRSInfoList();
2997
0
            concatList.splice(concatList.end(), std::move(list));
2998
0
        }
2999
0
        ret = new PROJ_CRS_INFO *[concatList.size() + 1];
3000
0
        GeographicBoundingBoxPtr bbox;
3001
0
        if (params && params->bbox_valid) {
3002
0
            bbox = GeographicBoundingBox::create(
3003
0
                       params->west_lon_degree, params->south_lat_degree,
3004
0
                       params->east_lon_degree, params->north_lat_degree)
3005
0
                       .as_nullable();
3006
0
        }
3007
0
        for (const auto &info : concatList) {
3008
0
            auto type = PJ_TYPE_CRS;
3009
0
            if (info.type == AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS) {
3010
0
                type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
3011
0
            } else if (info.type ==
3012
0
                       AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS) {
3013
0
                type = PJ_TYPE_GEOGRAPHIC_3D_CRS;
3014
0
            } else if (info.type ==
3015
0
                       AuthorityFactory::ObjectType::GEOCENTRIC_CRS) {
3016
0
                type = PJ_TYPE_GEOCENTRIC_CRS;
3017
0
            } else if (info.type ==
3018
0
                       AuthorityFactory::ObjectType::GEODETIC_CRS) {
3019
0
                type = PJ_TYPE_GEODETIC_CRS;
3020
0
            } else if (info.type ==
3021
0
                       AuthorityFactory::ObjectType::PROJECTED_CRS) {
3022
0
                type = PJ_TYPE_PROJECTED_CRS;
3023
0
            } else if (info.type ==
3024
0
                       AuthorityFactory::ObjectType::VERTICAL_CRS) {
3025
0
                type = PJ_TYPE_VERTICAL_CRS;
3026
0
            } else if (info.type ==
3027
0
                       AuthorityFactory::ObjectType::COMPOUND_CRS) {
3028
0
                type = PJ_TYPE_COMPOUND_CRS;
3029
0
            }
3030
0
            if (params && params->typesCount) {
3031
0
                bool typeValid = false;
3032
0
                for (size_t j = 0; j < params->typesCount; j++) {
3033
0
                    if (params->types[j] == type) {
3034
0
                        typeValid = true;
3035
0
                        break;
3036
0
                    } else if (params->types[j] == PJ_TYPE_GEOGRAPHIC_CRS &&
3037
0
                               (type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3038
0
                                type == PJ_TYPE_GEOGRAPHIC_3D_CRS)) {
3039
0
                        typeValid = true;
3040
0
                        break;
3041
0
                    } else if (params->types[j] == PJ_TYPE_GEODETIC_CRS &&
3042
0
                               (type == PJ_TYPE_GEOCENTRIC_CRS ||
3043
0
                                type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3044
0
                                type == PJ_TYPE_GEOGRAPHIC_3D_CRS)) {
3045
0
                        typeValid = true;
3046
0
                        break;
3047
0
                    }
3048
0
                }
3049
0
                if (!typeValid) {
3050
0
                    continue;
3051
0
                }
3052
0
            }
3053
0
            if (params && !params->allow_deprecated && info.deprecated) {
3054
0
                continue;
3055
0
            }
3056
0
            if (params && params->bbox_valid) {
3057
0
                if (!info.bbox_valid) {
3058
0
                    continue;
3059
0
                }
3060
0
                if (info.west_lon_degree <= info.east_lon_degree &&
3061
0
                    params->west_lon_degree <= params->east_lon_degree) {
3062
0
                    if (params->crs_area_of_use_contains_bbox) {
3063
0
                        if (params->west_lon_degree < info.west_lon_degree ||
3064
0
                            params->east_lon_degree > info.east_lon_degree ||
3065
0
                            params->south_lat_degree < info.south_lat_degree ||
3066
0
                            params->north_lat_degree > info.north_lat_degree) {
3067
0
                            continue;
3068
0
                        }
3069
0
                    } else {
3070
0
                        if (info.east_lon_degree < params->west_lon_degree ||
3071
0
                            info.west_lon_degree > params->east_lon_degree ||
3072
0
                            info.north_lat_degree < params->south_lat_degree ||
3073
0
                            info.south_lat_degree > params->north_lat_degree) {
3074
0
                            continue;
3075
0
                        }
3076
0
                    }
3077
0
                } else {
3078
0
                    auto crsExtent = GeographicBoundingBox::create(
3079
0
                        info.west_lon_degree, info.south_lat_degree,
3080
0
                        info.east_lon_degree, info.north_lat_degree);
3081
0
                    if (params->crs_area_of_use_contains_bbox) {
3082
0
                        if (!crsExtent->contains(NN_NO_CHECK(bbox))) {
3083
0
                            continue;
3084
0
                        }
3085
0
                    } else {
3086
0
                        if (!bbox->intersects(crsExtent)) {
3087
0
                            continue;
3088
0
                        }
3089
0
                    }
3090
0
                }
3091
0
            }
3092
0
            if (params && params->celestial_body_name &&
3093
0
                params->celestial_body_name != info.celestialBodyName) {
3094
0
                continue;
3095
0
            }
3096
3097
0
            ret[i] = new PROJ_CRS_INFO;
3098
0
            ret[i]->auth_name = pj_strdup(info.authName.c_str());
3099
0
            ret[i]->code = pj_strdup(info.code.c_str());
3100
0
            ret[i]->name = pj_strdup(info.name.c_str());
3101
0
            ret[i]->type = type;
3102
0
            ret[i]->deprecated = info.deprecated;
3103
0
            ret[i]->bbox_valid = info.bbox_valid;
3104
0
            ret[i]->west_lon_degree = info.west_lon_degree;
3105
0
            ret[i]->south_lat_degree = info.south_lat_degree;
3106
0
            ret[i]->east_lon_degree = info.east_lon_degree;
3107
0
            ret[i]->north_lat_degree = info.north_lat_degree;
3108
0
            ret[i]->area_name = pj_strdup(info.areaName.c_str());
3109
0
            ret[i]->projection_method_name =
3110
0
                info.projectionMethodName.empty()
3111
0
                    ? nullptr
3112
0
                    : pj_strdup(info.projectionMethodName.c_str());
3113
0
            ret[i]->celestial_body_name =
3114
0
                pj_strdup(info.celestialBodyName.c_str());
3115
0
            i++;
3116
0
        }
3117
0
        ret[i] = nullptr;
3118
0
        if (out_result_count)
3119
0
            *out_result_count = i;
3120
0
        return ret;
3121
0
    } catch (const std::exception &e) {
3122
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3123
0
        if (ret) {
3124
0
            ret[i + 1] = nullptr;
3125
0
            proj_crs_info_list_destroy(ret);
3126
0
        }
3127
0
        if (out_result_count)
3128
0
            *out_result_count = 0;
3129
0
    }
3130
0
    return nullptr;
3131
0
}
3132
3133
// ---------------------------------------------------------------------------
3134
3135
/** \brief Destroy the result returned by
3136
 * proj_get_crs_info_list_from_database().
3137
 */
3138
0
void proj_crs_info_list_destroy(PROJ_CRS_INFO **list) {
3139
0
    if (list) {
3140
0
        for (int i = 0; list[i] != nullptr; i++) {
3141
0
            free(list[i]->auth_name);
3142
0
            free(list[i]->code);
3143
0
            free(list[i]->name);
3144
0
            free(list[i]->area_name);
3145
0
            free(list[i]->projection_method_name);
3146
0
            free(list[i]->celestial_body_name);
3147
0
            delete list[i];
3148
0
        }
3149
0
        delete[] list;
3150
0
    }
3151
0
}
3152
3153
// ---------------------------------------------------------------------------
3154
3155
/** \brief Enumerate units from the database, taking into account various
3156
 * criteria.
3157
 *
3158
 * The returned object is an array of PROJ_UNIT_INFO* pointers, whose last
3159
 * entry is NULL. This array should be freed with proj_unit_list_destroy()
3160
 *
3161
 * @param ctx PROJ context, or NULL for default context
3162
 * @param auth_name Authority name, used to restrict the search.
3163
 * Or NULL for all authorities.
3164
 * @param category Filter by category, if this parameter is not NULL. Category
3165
 * is one of "linear", "linear_per_time", "angular", "angular_per_time",
3166
 * "scale", "scale_per_time" or "time"
3167
 * @param allow_deprecated whether we should return deprecated objects as well.
3168
 * @param out_result_count Output parameter pointing to an integer to receive
3169
 * the size of the result list. Might be NULL
3170
 * @return an array of PROJ_UNIT_INFO* pointers to be freed with
3171
 * proj_unit_list_destroy(), or NULL in case of error.
3172
 *
3173
 * @since 7.1
3174
 */
3175
PROJ_UNIT_INFO **proj_get_units_from_database(PJ_CONTEXT *ctx,
3176
                                              const char *auth_name,
3177
                                              const char *category,
3178
                                              int allow_deprecated,
3179
0
                                              int *out_result_count) {
3180
0
    SANITIZE_CTX(ctx);
3181
0
    PROJ_UNIT_INFO **ret = nullptr;
3182
0
    int i = 0;
3183
0
    try {
3184
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx),
3185
0
                                                auth_name ? auth_name : "");
3186
0
        auto list = factory->getUnitList();
3187
0
        ret = new PROJ_UNIT_INFO *[list.size() + 1];
3188
0
        for (const auto &info : list) {
3189
0
            if (category && info.category != category) {
3190
0
                continue;
3191
0
            }
3192
0
            if (!allow_deprecated && info.deprecated) {
3193
0
                continue;
3194
0
            }
3195
0
            ret[i] = new PROJ_UNIT_INFO;
3196
0
            ret[i]->auth_name = pj_strdup(info.authName.c_str());
3197
0
            ret[i]->code = pj_strdup(info.code.c_str());
3198
0
            ret[i]->name = pj_strdup(info.name.c_str());
3199
0
            ret[i]->category = pj_strdup(info.category.c_str());
3200
0
            ret[i]->conv_factor = info.convFactor;
3201
0
            ret[i]->proj_short_name =
3202
0
                info.projShortName.empty()
3203
0
                    ? nullptr
3204
0
                    : pj_strdup(info.projShortName.c_str());
3205
0
            ret[i]->deprecated = info.deprecated;
3206
0
            i++;
3207
0
        }
3208
0
        ret[i] = nullptr;
3209
0
        if (out_result_count)
3210
0
            *out_result_count = i;
3211
0
        return ret;
3212
0
    } catch (const std::exception &e) {
3213
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3214
0
        if (ret) {
3215
0
            ret[i + 1] = nullptr;
3216
0
            proj_unit_list_destroy(ret);
3217
0
        }
3218
0
        if (out_result_count)
3219
0
            *out_result_count = 0;
3220
0
    }
3221
0
    return nullptr;
3222
0
}
3223
3224
// ---------------------------------------------------------------------------
3225
3226
/** \brief Destroy the result returned by
3227
 * proj_get_units_from_database().
3228
 *
3229
 * @since 7.1
3230
 */
3231
0
void proj_unit_list_destroy(PROJ_UNIT_INFO **list) {
3232
0
    if (list) {
3233
0
        for (int i = 0; list[i] != nullptr; i++) {
3234
0
            free(list[i]->auth_name);
3235
0
            free(list[i]->code);
3236
0
            free(list[i]->name);
3237
0
            free(list[i]->category);
3238
0
            free(list[i]->proj_short_name);
3239
0
            delete list[i];
3240
0
        }
3241
0
        delete[] list;
3242
0
    }
3243
0
}
3244
3245
// ---------------------------------------------------------------------------
3246
3247
/** \brief Return the Conversion of a DerivedCRS (such as a ProjectedCRS),
3248
 * or the Transformation from the baseCRS to the hubCRS of a BoundCRS
3249
 *
3250
 * The returned object must be unreferenced with proj_destroy() after
3251
 * use.
3252
 * It should be used by at most one thread at a time.
3253
 *
3254
 * @param ctx PROJ context, or NULL for default context
3255
 * @param crs Object of type DerivedCRS or BoundCRSs (must not be NULL)
3256
 * @return Object of type SingleOperation that must be unreferenced with
3257
 * proj_destroy(), or NULL in case of error.
3258
 */
3259
0
PJ *proj_crs_get_coordoperation(PJ_CONTEXT *ctx, const PJ *crs) {
3260
0
    SANITIZE_CTX(ctx);
3261
0
    if (!crs) {
3262
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
3263
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
3264
0
        return nullptr;
3265
0
    }
3266
0
    SingleOperationPtr co;
3267
3268
0
    auto derivedCRS = dynamic_cast<const DerivedCRS *>(crs->iso_obj.get());
3269
0
    if (derivedCRS) {
3270
0
        co = derivedCRS->derivingConversion().as_nullable();
3271
0
    } else {
3272
0
        auto boundCRS = dynamic_cast<const BoundCRS *>(crs->iso_obj.get());
3273
0
        if (boundCRS) {
3274
0
            co = boundCRS->transformation().as_nullable();
3275
0
        } else {
3276
0
            proj_log_error(ctx, __FUNCTION__,
3277
0
                           "Object is not a DerivedCRS or BoundCRS");
3278
0
            return nullptr;
3279
0
        }
3280
0
    }
3281
3282
0
    return pj_obj_create(ctx, NN_NO_CHECK(co));
3283
0
}
3284
3285
// ---------------------------------------------------------------------------
3286
3287
/** \brief Return information on the operation method of the SingleOperation.
3288
 *
3289
 * @param ctx PROJ context, or NULL for default context
3290
 * @param coordoperation Object of type SingleOperation (typically a Conversion
3291
 * or Transformation) (must not be NULL)
3292
 * @param out_method_name Pointer to a string value to store the method
3293
 * (projection) name. or NULL
3294
 * @param out_method_auth_name Pointer to a string value to store the method
3295
 * authority name. or NULL
3296
 * @param out_method_code Pointer to a string value to store the method
3297
 * code. or NULL
3298
 * @return TRUE in case of success.
3299
 */
3300
int proj_coordoperation_get_method_info(PJ_CONTEXT *ctx,
3301
                                        const PJ *coordoperation,
3302
                                        const char **out_method_name,
3303
                                        const char **out_method_auth_name,
3304
0
                                        const char **out_method_code) {
3305
0
    SANITIZE_CTX(ctx);
3306
0
    if (!coordoperation) {
3307
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
3308
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
3309
0
        return false;
3310
0
    }
3311
0
    auto singleOp =
3312
0
        dynamic_cast<const SingleOperation *>(coordoperation->iso_obj.get());
3313
0
    if (!singleOp) {
3314
0
        proj_log_error(ctx, __FUNCTION__,
3315
0
                       "Object is not a DerivedCRS or BoundCRS");
3316
0
        return false;
3317
0
    }
3318
3319
0
    const auto &method = singleOp->method();
3320
0
    const auto &method_ids = method->identifiers();
3321
0
    if (out_method_name) {
3322
0
        *out_method_name = method->name()->description()->c_str();
3323
0
    }
3324
0
    if (out_method_auth_name) {
3325
0
        if (!method_ids.empty()) {
3326
0
            *out_method_auth_name = method_ids[0]->codeSpace()->c_str();
3327
0
        } else {
3328
0
            *out_method_auth_name = nullptr;
3329
0
        }
3330
0
    }
3331
0
    if (out_method_code) {
3332
0
        if (!method_ids.empty()) {
3333
0
            *out_method_code = method_ids[0]->code().c_str();
3334
0
        } else {
3335
0
            *out_method_code = nullptr;
3336
0
        }
3337
0
    }
3338
0
    return true;
3339
0
}
3340
3341
// ---------------------------------------------------------------------------
3342
3343
//! @cond Doxygen_Suppress
3344
static PropertyMap createPropertyMapName(const char *c_name,
3345
                                         const char *auth_name = nullptr,
3346
8.22k
                                         const char *code = nullptr) {
3347
8.22k
    std::string name(c_name ? c_name : "unnamed");
3348
8.22k
    PropertyMap properties;
3349
8.22k
    if (ends_with(name, " (deprecated)")) {
3350
0
        name.resize(name.size() - strlen(" (deprecated)"));
3351
0
        properties.set(common::IdentifiedObject::DEPRECATED_KEY, true);
3352
0
    }
3353
8.22k
    if (auth_name && code) {
3354
0
        properties.set(metadata::Identifier::CODESPACE_KEY, auth_name);
3355
0
        properties.set(metadata::Identifier::CODE_KEY, code);
3356
0
    }
3357
8.22k
    return properties.set(common::IdentifiedObject::NAME_KEY, name);
3358
8.22k
}
3359
3360
// ---------------------------------------------------------------------------
3361
3362
static UnitOfMeasure createLinearUnit(const char *name, double convFactor,
3363
                                      const char *unit_auth_name = nullptr,
3364
0
                                      const char *unit_code = nullptr) {
3365
0
    return name == nullptr
3366
0
               ? UnitOfMeasure::METRE
3367
0
               : UnitOfMeasure(name, convFactor, UnitOfMeasure::Type::LINEAR,
3368
0
                               unit_auth_name ? unit_auth_name : "",
3369
0
                               unit_code ? unit_code : "");
3370
0
}
3371
3372
// ---------------------------------------------------------------------------
3373
3374
static UnitOfMeasure createAngularUnit(const char *name, double convFactor,
3375
                                       const char *unit_auth_name = nullptr,
3376
5.48k
                                       const char *unit_code = nullptr) {
3377
5.48k
    return name ? (ci_equal(name, "degree") ? UnitOfMeasure::DEGREE
3378
0
                   : ci_equal(name, "grad")
3379
0
                       ? UnitOfMeasure::GRAD
3380
0
                       : UnitOfMeasure(name, convFactor,
3381
0
                                       UnitOfMeasure::Type::ANGULAR,
3382
0
                                       unit_auth_name ? unit_auth_name : "",
3383
0
                                       unit_code ? unit_code : ""))
3384
5.48k
                : UnitOfMeasure::DEGREE;
3385
5.48k
}
3386
3387
// ---------------------------------------------------------------------------
3388
3389
static GeodeticReferenceFrameNNPtr createGeodeticReferenceFrame(
3390
    PJ_CONTEXT *ctx, const char *datum_name, const char *ellps_name,
3391
    double semi_major_metre, double inv_flattening,
3392
    const char *prime_meridian_name, double prime_meridian_offset,
3393
2.74k
    const char *angular_units, double angular_units_conv) {
3394
2.74k
    const UnitOfMeasure angUnit(
3395
2.74k
        createAngularUnit(angular_units, angular_units_conv));
3396
2.74k
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
3397
2.74k
    auto body = Ellipsoid::guessBodyName(dbContext, semi_major_metre);
3398
2.74k
    auto ellpsName = createPropertyMapName(ellps_name);
3399
2.74k
    auto ellps = inv_flattening != 0.0
3400
2.74k
                     ? Ellipsoid::createFlattenedSphere(
3401
2.70k
                           ellpsName, Length(semi_major_metre),
3402
2.70k
                           Scale(inv_flattening), body)
3403
2.74k
                     : Ellipsoid::createSphere(ellpsName,
3404
35
                                               Length(semi_major_metre), body);
3405
2.74k
    auto pm = PrimeMeridian::create(
3406
2.74k
        PropertyMap().set(
3407
2.74k
            common::IdentifiedObject::NAME_KEY,
3408
2.74k
            prime_meridian_name ? prime_meridian_name
3409
2.74k
            : prime_meridian_offset == 0.0
3410
0
                ? (ellps->celestialBody() == Ellipsoid::EARTH
3411
0
                       ? PrimeMeridian::GREENWICH->nameStr().c_str()
3412
0
                       : PrimeMeridian::REFERENCE_MERIDIAN->nameStr().c_str())
3413
0
                : "unnamed"),
3414
2.74k
        Angle(prime_meridian_offset, angUnit));
3415
3416
2.74k
    std::string datumName(datum_name ? datum_name : "unnamed");
3417
2.74k
    if (datumName == "WGS_1984") {
3418
0
        datumName = GeodeticReferenceFrame::EPSG_6326->nameStr();
3419
2.74k
    } else if (datumName.find('_') != std::string::npos) {
3420
        // Likely coming from WKT1
3421
0
        if (dbContext) {
3422
0
            auto authFactory =
3423
0
                AuthorityFactory::create(NN_NO_CHECK(dbContext), std::string());
3424
0
            auto res = authFactory->createObjectsFromName(
3425
0
                datumName,
3426
0
                {AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME}, true,
3427
0
                1);
3428
0
            if (!res.empty()) {
3429
0
                const auto &refDatum = res.front();
3430
0
                if (metadata::Identifier::isEquivalentName(
3431
0
                        datumName.c_str(), refDatum->nameStr().c_str())) {
3432
0
                    datumName = refDatum->nameStr();
3433
0
                } else if (refDatum->identifiers().size() == 1) {
3434
0
                    const auto &id = refDatum->identifiers()[0];
3435
0
                    const auto aliases =
3436
0
                        authFactory->databaseContext()->getAliases(
3437
0
                            *id->codeSpace(), id->code(), refDatum->nameStr(),
3438
0
                            "geodetic_datum", std::string());
3439
0
                    for (const auto &alias : aliases) {
3440
0
                        if (metadata::Identifier::isEquivalentName(
3441
0
                                datumName.c_str(), alias.c_str())) {
3442
0
                            datumName = refDatum->nameStr();
3443
0
                            break;
3444
0
                        }
3445
0
                    }
3446
0
                }
3447
0
            }
3448
0
        }
3449
0
    }
3450
3451
2.74k
    return GeodeticReferenceFrame::create(
3452
2.74k
        createPropertyMapName(datumName.c_str()), ellps,
3453
2.74k
        util::optional<std::string>(), pm);
3454
2.74k
}
3455
3456
//! @endcond
3457
3458
// ---------------------------------------------------------------------------
3459
3460
/** \brief Create a GeographicCRS.
3461
 *
3462
 * The returned object must be unreferenced with proj_destroy() after
3463
 * use.
3464
 * It should be used by at most one thread at a time.
3465
 *
3466
 * @param ctx PROJ context, or NULL for default context
3467
 * @param crs_name Name of the GeographicCRS. Or NULL
3468
 * @param datum_name Name of the GeodeticReferenceFrame. Or NULL
3469
 * @param ellps_name Name of the Ellipsoid. Or NULL
3470
 * @param semi_major_metre Ellipsoid semi-major axis, in metres.
3471
 * @param inv_flattening Ellipsoid inverse flattening. Or 0 for a sphere.
3472
 * @param prime_meridian_name Name of the PrimeMeridian. Or NULL
3473
 * @param prime_meridian_offset Offset of the prime meridian, expressed in the
3474
 * specified angular units.
3475
 * @param pm_angular_units Name of the angular units. Or NULL for Degree
3476
 * @param pm_angular_units_conv Conversion factor from the angular unit to
3477
 * radian.
3478
 * Or
3479
 * 0 for Degree if pm_angular_units == NULL. Otherwise should be not NULL
3480
 * @param ellipsoidal_cs Coordinate system. Must not be NULL.
3481
 *
3482
 * @return Object of type GeographicCRS that must be unreferenced with
3483
 * proj_destroy(), or NULL in case of error.
3484
 */
3485
PJ *proj_create_geographic_crs(PJ_CONTEXT *ctx, const char *crs_name,
3486
                               const char *datum_name, const char *ellps_name,
3487
                               double semi_major_metre, double inv_flattening,
3488
                               const char *prime_meridian_name,
3489
                               double prime_meridian_offset,
3490
                               const char *pm_angular_units,
3491
                               double pm_angular_units_conv,
3492
2.74k
                               const PJ *ellipsoidal_cs) {
3493
3494
2.74k
    SANITIZE_CTX(ctx);
3495
2.74k
    auto cs = std::dynamic_pointer_cast<EllipsoidalCS>(ellipsoidal_cs->iso_obj);
3496
2.74k
    if (!cs) {
3497
0
        return nullptr;
3498
0
    }
3499
2.74k
    try {
3500
2.74k
        auto datum = createGeodeticReferenceFrame(
3501
2.74k
            ctx, datum_name, ellps_name, semi_major_metre, inv_flattening,
3502
2.74k
            prime_meridian_name, prime_meridian_offset, pm_angular_units,
3503
2.74k
            pm_angular_units_conv);
3504
2.74k
        auto geogCRS = GeographicCRS::create(createPropertyMapName(crs_name),
3505
2.74k
                                             datum, NN_NO_CHECK(cs));
3506
2.74k
        return pj_obj_create(ctx, geogCRS);
3507
2.74k
    } catch (const std::exception &e) {
3508
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3509
0
    }
3510
0
    return nullptr;
3511
2.74k
}
3512
3513
// ---------------------------------------------------------------------------
3514
3515
/** \brief Create a GeographicCRS.
3516
 *
3517
 * The returned object must be unreferenced with proj_destroy() after
3518
 * use.
3519
 * It should be used by at most one thread at a time.
3520
 *
3521
 * @param ctx PROJ context, or NULL for default context
3522
 * @param crs_name Name of the GeographicCRS. Or NULL
3523
 * @param datum_or_datum_ensemble Datum or DatumEnsemble (DatumEnsemble possible
3524
 * since 7.2). Must not be NULL.
3525
 * @param ellipsoidal_cs Coordinate system. Must not be NULL.
3526
 *
3527
 * @return Object of type GeographicCRS that must be unreferenced with
3528
 * proj_destroy(), or NULL in case of error.
3529
 */
3530
PJ *proj_create_geographic_crs_from_datum(PJ_CONTEXT *ctx, const char *crs_name,
3531
                                          const PJ *datum_or_datum_ensemble,
3532
0
                                          const PJ *ellipsoidal_cs) {
3533
3534
0
    SANITIZE_CTX(ctx);
3535
0
    if (datum_or_datum_ensemble == nullptr) {
3536
0
        proj_log_error(ctx, __FUNCTION__,
3537
0
                       "Missing input datum_or_datum_ensemble");
3538
0
        return nullptr;
3539
0
    }
3540
0
    auto l_datum = std::dynamic_pointer_cast<GeodeticReferenceFrame>(
3541
0
        datum_or_datum_ensemble->iso_obj);
3542
0
    auto l_datum_ensemble = std::dynamic_pointer_cast<DatumEnsemble>(
3543
0
        datum_or_datum_ensemble->iso_obj);
3544
0
    auto cs = std::dynamic_pointer_cast<EllipsoidalCS>(ellipsoidal_cs->iso_obj);
3545
0
    if (!cs) {
3546
0
        return nullptr;
3547
0
    }
3548
0
    try {
3549
0
        auto geogCRS =
3550
0
            GeographicCRS::create(createPropertyMapName(crs_name), l_datum,
3551
0
                                  l_datum_ensemble, NN_NO_CHECK(cs));
3552
0
        return pj_obj_create(ctx, geogCRS);
3553
0
    } catch (const std::exception &e) {
3554
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3555
0
    }
3556
0
    return nullptr;
3557
0
}
3558
3559
// ---------------------------------------------------------------------------
3560
3561
/** \brief Create a GeodeticCRS of geocentric type.
3562
 *
3563
 * The returned object must be unreferenced with proj_destroy() after
3564
 * use.
3565
 * It should be used by at most one thread at a time.
3566
 *
3567
 * @param ctx PROJ context, or NULL for default context
3568
 * @param crs_name Name of the GeographicCRS. Or NULL
3569
 * @param datum_name Name of the GeodeticReferenceFrame. Or NULL
3570
 * @param ellps_name Name of the Ellipsoid. Or NULL
3571
 * @param semi_major_metre Ellipsoid semi-major axis, in metres.
3572
 * @param inv_flattening Ellipsoid inverse flattening. Or 0 for a sphere.
3573
 * @param prime_meridian_name Name of the PrimeMeridian. Or NULL
3574
 * @param prime_meridian_offset Offset of the prime meridian, expressed in the
3575
 * specified angular units.
3576
 * @param angular_units Name of the angular units. Or NULL for Degree
3577
 * @param angular_units_conv Conversion factor from the angular unit to radian.
3578
 * Or
3579
 * 0 for Degree if angular_units == NULL. Otherwise should be not NULL
3580
 * @param linear_units Name of the linear units. Or NULL for Metre
3581
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
3582
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
3583
 *
3584
 * @return Object of type GeodeticCRS that must be unreferenced with
3585
 * proj_destroy(), or NULL in case of error.
3586
 */
3587
PJ *proj_create_geocentric_crs(
3588
    PJ_CONTEXT *ctx, const char *crs_name, const char *datum_name,
3589
    const char *ellps_name, double semi_major_metre, double inv_flattening,
3590
    const char *prime_meridian_name, double prime_meridian_offset,
3591
    const char *angular_units, double angular_units_conv,
3592
0
    const char *linear_units, double linear_units_conv) {
3593
3594
0
    SANITIZE_CTX(ctx);
3595
0
    try {
3596
0
        const UnitOfMeasure linearUnit(
3597
0
            createLinearUnit(linear_units, linear_units_conv));
3598
0
        auto datum = createGeodeticReferenceFrame(
3599
0
            ctx, datum_name, ellps_name, semi_major_metre, inv_flattening,
3600
0
            prime_meridian_name, prime_meridian_offset, angular_units,
3601
0
            angular_units_conv);
3602
3603
0
        auto geodCRS =
3604
0
            GeodeticCRS::create(createPropertyMapName(crs_name), datum,
3605
0
                                cs::CartesianCS::createGeocentric(linearUnit));
3606
0
        return pj_obj_create(ctx, geodCRS);
3607
0
    } catch (const std::exception &e) {
3608
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3609
0
    }
3610
0
    return nullptr;
3611
0
}
3612
3613
// ---------------------------------------------------------------------------
3614
3615
/** \brief Create a GeodeticCRS of geocentric type.
3616
 *
3617
 * The returned object must be unreferenced with proj_destroy() after
3618
 * use.
3619
 * It should be used by at most one thread at a time.
3620
 *
3621
 * @param ctx PROJ context, or NULL for default context
3622
 * @param crs_name Name of the GeographicCRS. Or NULL
3623
 * @param datum_or_datum_ensemble Datum or DatumEnsemble (DatumEnsemble possible
3624
 * since 7.2). Must not be NULL.
3625
 * @param linear_units Name of the linear units. Or NULL for Metre
3626
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
3627
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
3628
 *
3629
 * @return Object of type GeodeticCRS that must be unreferenced with
3630
 * proj_destroy(), or NULL in case of error.
3631
 */
3632
PJ *proj_create_geocentric_crs_from_datum(PJ_CONTEXT *ctx, const char *crs_name,
3633
                                          const PJ *datum_or_datum_ensemble,
3634
                                          const char *linear_units,
3635
0
                                          double linear_units_conv) {
3636
0
    SANITIZE_CTX(ctx);
3637
0
    if (datum_or_datum_ensemble == nullptr) {
3638
0
        proj_log_error(ctx, __FUNCTION__,
3639
0
                       "Missing input datum_or_datum_ensemble");
3640
0
        return nullptr;
3641
0
    }
3642
0
    auto l_datum = std::dynamic_pointer_cast<GeodeticReferenceFrame>(
3643
0
        datum_or_datum_ensemble->iso_obj);
3644
0
    auto l_datum_ensemble = std::dynamic_pointer_cast<DatumEnsemble>(
3645
0
        datum_or_datum_ensemble->iso_obj);
3646
0
    try {
3647
0
        const UnitOfMeasure linearUnit(
3648
0
            createLinearUnit(linear_units, linear_units_conv));
3649
0
        auto geodCRS = GeodeticCRS::create(
3650
0
            createPropertyMapName(crs_name), l_datum, l_datum_ensemble,
3651
0
            cs::CartesianCS::createGeocentric(linearUnit));
3652
0
        return pj_obj_create(ctx, geodCRS);
3653
0
    } catch (const std::exception &e) {
3654
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3655
0
    }
3656
0
    return nullptr;
3657
0
}
3658
3659
// ---------------------------------------------------------------------------
3660
3661
/** \brief Create a DerivedGeograhicCRS.
3662
 *
3663
 * The returned object must be unreferenced with proj_destroy() after
3664
 * use.
3665
 * It should be used by at most one thread at a time.
3666
 *
3667
 * @param ctx PROJ context, or NULL for default context
3668
 * @param crs_name Name of the GeographicCRS. Or NULL
3669
 * @param base_geographic_crs Base Geographic CRS. Must not be NULL.
3670
 * @param conversion Conversion from the base Geographic to the
3671
 * DerivedGeograhicCRS. Must not be NULL.
3672
 * @param ellipsoidal_cs Coordinate system. Must not be NULL.
3673
 *
3674
 * @return Object of type GeodeticCRS that must be unreferenced with
3675
 * proj_destroy(), or NULL in case of error.
3676
 *
3677
 * @since 7.0
3678
 */
3679
PJ *proj_create_derived_geographic_crs(PJ_CONTEXT *ctx, const char *crs_name,
3680
                                       const PJ *base_geographic_crs,
3681
                                       const PJ *conversion,
3682
0
                                       const PJ *ellipsoidal_cs) {
3683
0
    SANITIZE_CTX(ctx);
3684
0
    auto base_crs =
3685
0
        std::dynamic_pointer_cast<GeographicCRS>(base_geographic_crs->iso_obj);
3686
0
    auto conversion_cpp =
3687
0
        std::dynamic_pointer_cast<Conversion>(conversion->iso_obj);
3688
0
    auto cs = std::dynamic_pointer_cast<EllipsoidalCS>(ellipsoidal_cs->iso_obj);
3689
0
    if (!base_crs || !conversion_cpp || !cs) {
3690
0
        return nullptr;
3691
0
    }
3692
0
    try {
3693
0
        auto derivedCRS = DerivedGeographicCRS::create(
3694
0
            createPropertyMapName(crs_name), NN_NO_CHECK(base_crs),
3695
0
            NN_NO_CHECK(conversion_cpp), NN_NO_CHECK(cs));
3696
0
        return pj_obj_create(ctx, derivedCRS);
3697
0
    } catch (const std::exception &e) {
3698
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3699
0
    }
3700
0
    return nullptr;
3701
0
}
3702
3703
// ---------------------------------------------------------------------------
3704
3705
/** \brief Return whether a CRS is a Derived CRS.
3706
 *
3707
 * @param ctx PROJ context, or NULL for default context
3708
 * @param crs CRS. Must not be NULL.
3709
 *
3710
 * @return whether a CRS is a Derived CRS.
3711
 *
3712
 * @since 7.0
3713
 */
3714
0
int proj_is_derived_crs(PJ_CONTEXT *ctx, const PJ *crs) {
3715
0
    SANITIZE_CTX(ctx);
3716
0
    return dynamic_cast<DerivedCRS *>(crs->iso_obj.get()) != nullptr;
3717
0
}
3718
3719
// ---------------------------------------------------------------------------
3720
3721
/** \brief Create a VerticalCRS
3722
 *
3723
 * The returned object must be unreferenced with proj_destroy() after
3724
 * use.
3725
 * It should be used by at most one thread at a time.
3726
 *
3727
 * @param ctx PROJ context, or NULL for default context
3728
 * @param crs_name Name of the GeographicCRS. Or NULL
3729
 * @param datum_name Name of the VerticalReferenceFrame. Or NULL
3730
 * @param linear_units Name of the linear units. Or NULL for Metre
3731
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
3732
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
3733
 *
3734
 * @return Object of type VerticalCRS that must be unreferenced with
3735
 * proj_destroy(), or NULL in case of error.
3736
 */
3737
PJ *proj_create_vertical_crs(PJ_CONTEXT *ctx, const char *crs_name,
3738
                             const char *datum_name, const char *linear_units,
3739
0
                             double linear_units_conv) {
3740
3741
0
    return proj_create_vertical_crs_ex(
3742
0
        ctx, crs_name, datum_name, nullptr, nullptr, linear_units,
3743
0
        linear_units_conv, nullptr, nullptr, nullptr, nullptr, nullptr);
3744
0
}
3745
3746
// ---------------------------------------------------------------------------
3747
3748
/** \brief Create a VerticalCRS
3749
 *
3750
 * The returned object must be unreferenced with proj_destroy() after
3751
 * use.
3752
 * It should be used by at most one thread at a time.
3753
 *
3754
 * This is an extended (_ex) version of proj_create_vertical_crs() that adds
3755
 * the capability of defining a geoid model.
3756
 *
3757
 * @param ctx PROJ context, or NULL for default context
3758
 * @param crs_name Name of the GeographicCRS. Or NULL
3759
 * @param datum_name Name of the VerticalReferenceFrame. Or NULL
3760
 * @param datum_auth_name Authority name of the VerticalReferenceFrame. Or NULL
3761
 * @param datum_code Code of the VerticalReferenceFrame. Or NULL
3762
 * @param linear_units Name of the linear units. Or NULL for Metre
3763
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
3764
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
3765
 * @param geoid_model_name Geoid model name, or NULL. Can be a name from the
3766
 * geoid_model name or a string "PROJ foo.gtx"
3767
 * @param geoid_model_auth_name Authority name of the transformation for
3768
 * the geoid model. or NULL
3769
 * @param geoid_model_code Code of the transformation for
3770
 * the geoid model. or NULL
3771
 * @param geoid_geog_crs Geographic CRS for the geoid transformation, or NULL.
3772
 * @param options NULL-terminated list of strings with "KEY=VALUE" format. or
3773
 * NULL.
3774
 * The currently recognized option is ACCURACY=value, where value is in metre.
3775
 * @return Object of type VerticalCRS that must be unreferenced with
3776
 * proj_destroy(), or NULL in case of error.
3777
 */
3778
PJ *proj_create_vertical_crs_ex(
3779
    PJ_CONTEXT *ctx, const char *crs_name, const char *datum_name,
3780
    const char *datum_auth_name, const char *datum_code,
3781
    const char *linear_units, double linear_units_conv,
3782
    const char *geoid_model_name, const char *geoid_model_auth_name,
3783
    const char *geoid_model_code, const PJ *geoid_geog_crs,
3784
0
    const char *const *options) {
3785
0
    SANITIZE_CTX(ctx);
3786
0
    try {
3787
0
        const UnitOfMeasure linearUnit(
3788
0
            createLinearUnit(linear_units, linear_units_conv));
3789
0
        auto datum = VerticalReferenceFrame::create(
3790
0
            createPropertyMapName(datum_name, datum_auth_name, datum_code));
3791
0
        auto props = createPropertyMapName(crs_name);
3792
0
        auto cs = cs::VerticalCS::createGravityRelatedHeight(linearUnit);
3793
0
        if (geoid_model_name) {
3794
0
            auto propsModel = createPropertyMapName(
3795
0
                geoid_model_name, geoid_model_auth_name, geoid_model_code);
3796
0
            const auto vertCRSWithoutGeoid =
3797
0
                VerticalCRS::create(props, datum, cs);
3798
0
            const auto interpCRS =
3799
0
                geoid_geog_crs && std::dynamic_pointer_cast<GeographicCRS>(
3800
0
                                      geoid_geog_crs->iso_obj)
3801
0
                    ? std::dynamic_pointer_cast<CRS>(geoid_geog_crs->iso_obj)
3802
0
                    : nullptr;
3803
3804
0
            std::vector<metadata::PositionalAccuracyNNPtr> accuracies;
3805
0
            for (auto iter = options; iter && iter[0]; ++iter) {
3806
0
                const char *value;
3807
0
                if ((value = getOptionValue(*iter, "ACCURACY="))) {
3808
0
                    accuracies.emplace_back(
3809
0
                        metadata::PositionalAccuracy::create(value));
3810
0
                }
3811
0
            }
3812
0
            const auto model(Transformation::create(
3813
0
                propsModel, vertCRSWithoutGeoid,
3814
0
                GeographicCRS::EPSG_4979, // arbitrarily chosen. Ignored
3815
0
                interpCRS,
3816
0
                OperationMethod::create(PropertyMap(),
3817
0
                                        std::vector<OperationParameterNNPtr>()),
3818
0
                {}, accuracies));
3819
0
            props.set("GEOID_MODEL", model);
3820
0
        }
3821
0
        auto vertCRS = VerticalCRS::create(props, datum, cs);
3822
0
        return pj_obj_create(ctx, vertCRS);
3823
0
    } catch (const std::exception &e) {
3824
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3825
0
    }
3826
0
    return nullptr;
3827
0
}
3828
3829
// ---------------------------------------------------------------------------
3830
3831
/** \brief Create a CompoundCRS
3832
 *
3833
 * The returned object must be unreferenced with proj_destroy() after
3834
 * use.
3835
 * It should be used by at most one thread at a time.
3836
 *
3837
 * @param ctx PROJ context, or NULL for default context
3838
 * @param crs_name Name of the GeographicCRS. Or NULL
3839
 * @param horiz_crs Horizontal CRS. must not be NULL.
3840
 * @param vert_crs Vertical CRS. must not be NULL.
3841
 *
3842
 * @return Object of type CompoundCRS that must be unreferenced with
3843
 * proj_destroy(), or NULL in case of error.
3844
 */
3845
PJ *proj_create_compound_crs(PJ_CONTEXT *ctx, const char *crs_name,
3846
0
                             const PJ *horiz_crs, const PJ *vert_crs) {
3847
3848
0
    SANITIZE_CTX(ctx);
3849
0
    if (!horiz_crs || !vert_crs) {
3850
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
3851
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
3852
0
        return nullptr;
3853
0
    }
3854
0
    auto l_horiz_crs = std::dynamic_pointer_cast<CRS>(horiz_crs->iso_obj);
3855
0
    if (!l_horiz_crs) {
3856
0
        return nullptr;
3857
0
    }
3858
0
    auto l_vert_crs = std::dynamic_pointer_cast<CRS>(vert_crs->iso_obj);
3859
0
    if (!l_vert_crs) {
3860
0
        return nullptr;
3861
0
    }
3862
0
    try {
3863
0
        auto compoundCRS = CompoundCRS::create(
3864
0
            createPropertyMapName(crs_name),
3865
0
            {NN_NO_CHECK(l_horiz_crs), NN_NO_CHECK(l_vert_crs)});
3866
0
        return pj_obj_create(ctx, compoundCRS);
3867
0
    } catch (const std::exception &e) {
3868
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3869
0
    }
3870
0
    return nullptr;
3871
0
}
3872
3873
// ---------------------------------------------------------------------------
3874
3875
/** \brief Return a copy of the object with its name changed
3876
 *
3877
 * Currently, only implemented on CRS objects.
3878
 *
3879
 * The returned object must be unreferenced with proj_destroy() after
3880
 * use.
3881
 * It should be used by at most one thread at a time.
3882
 *
3883
 * @param ctx PROJ context, or NULL for default context
3884
 * @param obj Object of type CRS. Must not be NULL
3885
 * @param name New name. Must not be NULL
3886
 *
3887
 * @return Object that must be unreferenced with
3888
 * proj_destroy(), or NULL in case of error.
3889
 */
3890
0
PJ *proj_alter_name(PJ_CONTEXT *ctx, const PJ *obj, const char *name) {
3891
0
    SANITIZE_CTX(ctx);
3892
0
    if (!obj || !name) {
3893
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
3894
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
3895
0
        return nullptr;
3896
0
    }
3897
0
    auto crs = dynamic_cast<const CRS *>(obj->iso_obj.get());
3898
0
    if (!crs) {
3899
0
        return nullptr;
3900
0
    }
3901
0
    try {
3902
0
        return pj_obj_create(ctx, crs->alterName(name));
3903
0
    } catch (const std::exception &e) {
3904
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3905
0
    }
3906
0
    return nullptr;
3907
0
}
3908
3909
// ---------------------------------------------------------------------------
3910
3911
/** \brief Return a copy of the object with its identifier changed/set
3912
 *
3913
 * Currently, only implemented on CRS objects.
3914
 *
3915
 * The returned object must be unreferenced with proj_destroy() after
3916
 * use.
3917
 * It should be used by at most one thread at a time.
3918
 *
3919
 * @param ctx PROJ context, or NULL for default context
3920
 * @param obj Object of type CRS. Must not be NULL
3921
 * @param auth_name Authority name. Must not be NULL
3922
 * @param code Code. Must not be NULL
3923
 *
3924
 * @return Object that must be unreferenced with
3925
 * proj_destroy(), or NULL in case of error.
3926
 */
3927
PJ *proj_alter_id(PJ_CONTEXT *ctx, const PJ *obj, const char *auth_name,
3928
0
                  const char *code) {
3929
0
    SANITIZE_CTX(ctx);
3930
0
    if (!obj || !auth_name || !code) {
3931
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
3932
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
3933
0
        return nullptr;
3934
0
    }
3935
0
    auto crs = dynamic_cast<const CRS *>(obj->iso_obj.get());
3936
0
    if (!crs) {
3937
0
        return nullptr;
3938
0
    }
3939
0
    try {
3940
0
        return pj_obj_create(ctx, crs->alterId(auth_name, code));
3941
0
    } catch (const std::exception &e) {
3942
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3943
0
    }
3944
0
    return nullptr;
3945
0
}
3946
3947
// ---------------------------------------------------------------------------
3948
3949
/** \brief Return a copy of the CRS with its geodetic CRS changed
3950
 *
3951
 * Currently, when obj is a GeodeticCRS, it returns a clone of new_geod_crs
3952
 * When obj is a ProjectedCRS, it replaces its base CRS with new_geod_crs.
3953
 * When obj is a CompoundCRS, it replaces the GeodeticCRS part of the horizontal
3954
 * CRS with new_geod_crs.
3955
 * In other cases, it returns a clone of obj.
3956
 *
3957
 * The returned object must be unreferenced with proj_destroy() after
3958
 * use.
3959
 * It should be used by at most one thread at a time.
3960
 *
3961
 * @param ctx PROJ context, or NULL for default context
3962
 * @param obj Object of type CRS. Must not be NULL
3963
 * @param new_geod_crs Object of type GeodeticCRS. Must not be NULL
3964
 *
3965
 * @return Object that must be unreferenced with
3966
 * proj_destroy(), or NULL in case of error.
3967
 */
3968
PJ *proj_crs_alter_geodetic_crs(PJ_CONTEXT *ctx, const PJ *obj,
3969
0
                                const PJ *new_geod_crs) {
3970
0
    SANITIZE_CTX(ctx);
3971
0
    if (!obj || !new_geod_crs) {
3972
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
3973
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
3974
0
        return nullptr;
3975
0
    }
3976
0
    auto l_new_geod_crs =
3977
0
        std::dynamic_pointer_cast<GeodeticCRS>(new_geod_crs->iso_obj);
3978
0
    if (!l_new_geod_crs) {
3979
0
        proj_log_error(ctx, __FUNCTION__, "new_geod_crs is not a GeodeticCRS");
3980
0
        return nullptr;
3981
0
    }
3982
3983
0
    auto crs = dynamic_cast<const CRS *>(obj->iso_obj.get());
3984
0
    if (!crs) {
3985
0
        proj_log_error(ctx, __FUNCTION__, "obj is not a CRS");
3986
0
        return nullptr;
3987
0
    }
3988
3989
0
    try {
3990
0
        return pj_obj_create(
3991
0
            ctx, crs->alterGeodeticCRS(NN_NO_CHECK(l_new_geod_crs)));
3992
0
    } catch (const std::exception &e) {
3993
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3994
0
        return nullptr;
3995
0
    }
3996
0
}
3997
3998
// ---------------------------------------------------------------------------
3999
4000
/** \brief Return a copy of the CRS with its angular units changed
4001
 *
4002
 * The CRS must be or contain a GeographicCRS.
4003
 *
4004
 * The returned object must be unreferenced with proj_destroy() after
4005
 * use.
4006
 * It should be used by at most one thread at a time.
4007
 *
4008
 * @param ctx PROJ context, or NULL for default context
4009
 * @param obj Object of type CRS. Must not be NULL
4010
 * @param angular_units Name of the angular units. Or NULL for Degree
4011
 * @param angular_units_conv Conversion factor from the angular unit to radian.
4012
 * Or 0 for Degree if angular_units == NULL. Otherwise should be not NULL
4013
 * @param unit_auth_name Unit authority name. Or NULL.
4014
 * @param unit_code Unit code. Or NULL.
4015
 *
4016
 * @return Object that must be unreferenced with
4017
 * proj_destroy(), or NULL in case of error.
4018
 */
4019
PJ *proj_crs_alter_cs_angular_unit(PJ_CONTEXT *ctx, const PJ *obj,
4020
                                   const char *angular_units,
4021
                                   double angular_units_conv,
4022
                                   const char *unit_auth_name,
4023
0
                                   const char *unit_code) {
4024
4025
0
    SANITIZE_CTX(ctx);
4026
0
    auto geodCRS = proj_crs_get_geodetic_crs(ctx, obj);
4027
0
    if (!geodCRS) {
4028
0
        return nullptr;
4029
0
    }
4030
0
    auto geogCRS = dynamic_cast<const GeographicCRS *>(geodCRS->iso_obj.get());
4031
0
    if (!geogCRS) {
4032
0
        proj_destroy(geodCRS);
4033
0
        return nullptr;
4034
0
    }
4035
4036
0
    PJ *geogCRSAltered = nullptr;
4037
0
    try {
4038
0
        const UnitOfMeasure angUnit(createAngularUnit(
4039
0
            angular_units, angular_units_conv, unit_auth_name, unit_code));
4040
0
        geogCRSAltered = pj_obj_create(
4041
0
            ctx, GeographicCRS::create(
4042
0
                     createPropertyMapName(proj_get_name(geodCRS)),
4043
0
                     geogCRS->datum(), geogCRS->datumEnsemble(),
4044
0
                     geogCRS->coordinateSystem()->alterAngularUnit(angUnit)));
4045
0
        proj_destroy(geodCRS);
4046
0
    } catch (const std::exception &e) {
4047
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4048
0
        proj_destroy(geodCRS);
4049
0
        return nullptr;
4050
0
    }
4051
4052
0
    auto ret = proj_crs_alter_geodetic_crs(ctx, obj, geogCRSAltered);
4053
0
    proj_destroy(geogCRSAltered);
4054
0
    return ret;
4055
0
}
4056
4057
// ---------------------------------------------------------------------------
4058
4059
/** \brief Return a copy of the CRS with the linear units of its coordinate
4060
 * system changed
4061
 *
4062
 * The CRS must be or contain a ProjectedCRS, VerticalCRS or a GeocentricCRS.
4063
 *
4064
 * The returned object must be unreferenced with proj_destroy() after
4065
 * use.
4066
 * It should be used by at most one thread at a time.
4067
 *
4068
 * @param ctx PROJ context, or NULL for default context
4069
 * @param obj Object of type CRS. Must not be NULL
4070
 * @param linear_units Name of the linear units. Or NULL for Metre
4071
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
4072
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
4073
 * @param unit_auth_name Unit authority name. Or NULL.
4074
 * @param unit_code Unit code. Or NULL.
4075
 *
4076
 * @return Object that must be unreferenced with
4077
 * proj_destroy(), or NULL in case of error.
4078
 */
4079
PJ *proj_crs_alter_cs_linear_unit(PJ_CONTEXT *ctx, const PJ *obj,
4080
                                  const char *linear_units,
4081
                                  double linear_units_conv,
4082
                                  const char *unit_auth_name,
4083
0
                                  const char *unit_code) {
4084
0
    SANITIZE_CTX(ctx);
4085
0
    if (!obj) {
4086
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4087
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4088
0
        return nullptr;
4089
0
    }
4090
0
    auto crs = dynamic_cast<const CRS *>(obj->iso_obj.get());
4091
0
    if (!crs) {
4092
0
        return nullptr;
4093
0
    }
4094
4095
0
    try {
4096
0
        const UnitOfMeasure linearUnit(createLinearUnit(
4097
0
            linear_units, linear_units_conv, unit_auth_name, unit_code));
4098
0
        return pj_obj_create(ctx, crs->alterCSLinearUnit(linearUnit));
4099
0
    } catch (const std::exception &e) {
4100
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4101
0
        return nullptr;
4102
0
    }
4103
0
}
4104
4105
// ---------------------------------------------------------------------------
4106
4107
/** \brief Return a copy of the CRS with the linear units of the parameters
4108
 * of its conversion modified.
4109
 *
4110
 * The CRS must be or contain a ProjectedCRS, VerticalCRS or a GeocentricCRS.
4111
 *
4112
 * The returned object must be unreferenced with proj_destroy() after
4113
 * use.
4114
 * It should be used by at most one thread at a time.
4115
 *
4116
 * @param ctx PROJ context, or NULL for default context
4117
 * @param obj Object of type ProjectedCRS. Must not be NULL
4118
 * @param linear_units Name of the linear units. Or NULL for Metre
4119
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
4120
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
4121
 * @param unit_auth_name Unit authority name. Or NULL.
4122
 * @param unit_code Unit code. Or NULL.
4123
 * @param convert_to_new_unit TRUE if existing values should be converted from
4124
 * their current unit to the new unit. If FALSE, their value will be left
4125
 * unchanged and the unit overridden (so the resulting CRS will not be
4126
 * equivalent to the original one for reprojection purposes).
4127
 *
4128
 * @return Object that must be unreferenced with
4129
 * proj_destroy(), or NULL in case of error.
4130
 */
4131
PJ *proj_crs_alter_parameters_linear_unit(PJ_CONTEXT *ctx, const PJ *obj,
4132
                                          const char *linear_units,
4133
                                          double linear_units_conv,
4134
                                          const char *unit_auth_name,
4135
                                          const char *unit_code,
4136
0
                                          int convert_to_new_unit) {
4137
0
    SANITIZE_CTX(ctx);
4138
0
    if (!obj) {
4139
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4140
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4141
0
        return nullptr;
4142
0
    }
4143
0
    auto crs = dynamic_cast<const ProjectedCRS *>(obj->iso_obj.get());
4144
0
    if (!crs) {
4145
0
        return nullptr;
4146
0
    }
4147
4148
0
    try {
4149
0
        const UnitOfMeasure linearUnit(createLinearUnit(
4150
0
            linear_units, linear_units_conv, unit_auth_name, unit_code));
4151
0
        return pj_obj_create(ctx, crs->alterParametersLinearUnit(
4152
0
                                      linearUnit, convert_to_new_unit == TRUE));
4153
0
    } catch (const std::exception &e) {
4154
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4155
0
        return nullptr;
4156
0
    }
4157
0
}
4158
4159
// ---------------------------------------------------------------------------
4160
4161
/** \brief Create a 3D CRS from an existing 2D CRS.
4162
 *
4163
 * The new axis will be ellipsoidal height, oriented upwards, and with metre
4164
 * units.
4165
 *
4166
 * See osgeo::proj::crs::CRS::promoteTo3D().
4167
 *
4168
 * The returned object must be unreferenced with proj_destroy() after
4169
 * use.
4170
 * It should be used by at most one thread at a time.
4171
 *
4172
 * @param ctx PROJ context, or NULL for default context
4173
 * @param crs_3D_name CRS name. Or NULL (in which case the name of crs_2D
4174
 * will be used)
4175
 * @param crs_2D 2D CRS to be "promoted" to 3D. Must not be NULL.
4176
 *
4177
 * @return Object that must be unreferenced with
4178
 * proj_destroy(), or NULL in case of error.
4179
 * @since 6.3
4180
 */
4181
PJ *proj_crs_promote_to_3D(PJ_CONTEXT *ctx, const char *crs_3D_name,
4182
0
                           const PJ *crs_2D) {
4183
0
    SANITIZE_CTX(ctx);
4184
0
    if (!crs_2D) {
4185
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4186
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4187
0
        return nullptr;
4188
0
    }
4189
0
    auto cpp_2D_crs = dynamic_cast<const CRS *>(crs_2D->iso_obj.get());
4190
0
    if (!cpp_2D_crs) {
4191
0
        auto coordinateMetadata =
4192
0
            dynamic_cast<const CoordinateMetadata *>(crs_2D->iso_obj.get());
4193
0
        if (!coordinateMetadata) {
4194
0
            proj_log_error(ctx, __FUNCTION__,
4195
0
                           "crs_2D is not a CRS or a CoordinateMetadata");
4196
0
            return nullptr;
4197
0
        }
4198
4199
0
        try {
4200
0
            auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
4201
0
            auto crs = coordinateMetadata->crs();
4202
0
            auto crs_3D = crs->promoteTo3D(
4203
0
                crs_3D_name ? std::string(crs_3D_name) : crs->nameStr(),
4204
0
                dbContext);
4205
0
            if (coordinateMetadata->coordinateEpoch().has_value()) {
4206
0
                return pj_obj_create(
4207
0
                    ctx, CoordinateMetadata::create(
4208
0
                             crs_3D,
4209
0
                             coordinateMetadata->coordinateEpochAsDecimalYear(),
4210
0
                             dbContext));
4211
0
            } else {
4212
0
                return pj_obj_create(ctx, CoordinateMetadata::create(crs_3D));
4213
0
            }
4214
0
        } catch (const std::exception &e) {
4215
0
            proj_log_error(ctx, __FUNCTION__, e.what());
4216
0
            return nullptr;
4217
0
        }
4218
0
    } else {
4219
0
        try {
4220
0
            auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
4221
0
            return pj_obj_create(ctx, cpp_2D_crs->promoteTo3D(
4222
0
                                          crs_3D_name ? std::string(crs_3D_name)
4223
0
                                                      : cpp_2D_crs->nameStr(),
4224
0
                                          dbContext));
4225
0
        } catch (const std::exception &e) {
4226
0
            proj_log_error(ctx, __FUNCTION__, e.what());
4227
0
            return nullptr;
4228
0
        }
4229
0
    }
4230
0
}
4231
4232
// ---------------------------------------------------------------------------
4233
4234
/** \brief Create a projected 3D CRS from an existing projected 2D CRS.
4235
 *
4236
 * The passed projected_2D_crs is used so that its name is replaced by
4237
 * crs_name and its base geographic CRS is replaced by geog_3D_crs. The vertical
4238
 * axis of geog_3D_crs (ellipsoidal height) will be added as the 3rd axis of
4239
 * the resulting projected 3D CRS.
4240
 * Normally, the passed geog_3D_crs should be the 3D counterpart of the original
4241
 * 2D base geographic CRS of projected_2D_crs, but such no check is done.
4242
 *
4243
 * It is also possible to invoke this function with a NULL geog_3D_crs. In which
4244
 * case, the existing base geographic 2D CRS of projected_2D_crs will be
4245
 * automatically promoted to 3D by assuming a 3rd axis being an ellipsoidal
4246
 * height, oriented upwards, and with metre units. This is equivalent to using
4247
 * proj_crs_promote_to_3D().
4248
 *
4249
 * The returned object must be unreferenced with proj_destroy() after
4250
 * use.
4251
 * It should be used by at most one thread at a time.
4252
 *
4253
 * @param ctx PROJ context, or NULL for default context
4254
 * @param crs_name CRS name. Or NULL (in which case the name of projected_2D_crs
4255
 * will be used)
4256
 * @param projected_2D_crs Projected 2D CRS to be "promoted" to 3D. Must not be
4257
 * NULL.
4258
 * @param geog_3D_crs Base geographic 3D CRS for the new CRS. May be NULL.
4259
 *
4260
 * @return Object that must be unreferenced with
4261
 * proj_destroy(), or NULL in case of error.
4262
 * @since 6.3
4263
 */
4264
PJ *proj_crs_create_projected_3D_crs_from_2D(PJ_CONTEXT *ctx,
4265
                                             const char *crs_name,
4266
                                             const PJ *projected_2D_crs,
4267
0
                                             const PJ *geog_3D_crs) {
4268
0
    SANITIZE_CTX(ctx);
4269
0
    if (!projected_2D_crs) {
4270
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4271
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4272
0
        return nullptr;
4273
0
    }
4274
0
    auto cpp_projected_2D_crs =
4275
0
        dynamic_cast<const ProjectedCRS *>(projected_2D_crs->iso_obj.get());
4276
0
    if (!cpp_projected_2D_crs) {
4277
0
        proj_log_error(ctx, __FUNCTION__,
4278
0
                       "projected_2D_crs is not a Projected CRS");
4279
0
        return nullptr;
4280
0
    }
4281
0
    const auto &oldCS = cpp_projected_2D_crs->coordinateSystem();
4282
0
    const auto &oldCSAxisList = oldCS->axisList();
4283
4284
0
    if (geog_3D_crs && geog_3D_crs->iso_obj) {
4285
0
        auto cpp_geog_3D_CRS =
4286
0
            std::dynamic_pointer_cast<GeographicCRS>(geog_3D_crs->iso_obj);
4287
0
        if (!cpp_geog_3D_CRS) {
4288
0
            proj_log_error(ctx, __FUNCTION__,
4289
0
                           "geog_3D_crs is not a Geographic CRS");
4290
0
            return nullptr;
4291
0
        }
4292
4293
0
        const auto &geogCS = cpp_geog_3D_CRS->coordinateSystem();
4294
0
        const auto &geogCSAxisList = geogCS->axisList();
4295
0
        if (geogCSAxisList.size() != 3) {
4296
0
            proj_log_error(ctx, __FUNCTION__,
4297
0
                           "geog_3D_crs is not a Geographic 3D CRS");
4298
0
            return nullptr;
4299
0
        }
4300
0
        try {
4301
0
            auto newCS =
4302
0
                cs::CartesianCS::create(PropertyMap(), oldCSAxisList[0],
4303
0
                                        oldCSAxisList[1], geogCSAxisList[2]);
4304
0
            return pj_obj_create(
4305
0
                ctx,
4306
0
                ProjectedCRS::create(
4307
0
                    createPropertyMapName(
4308
0
                        crs_name ? crs_name
4309
0
                                 : cpp_projected_2D_crs->nameStr().c_str()),
4310
0
                    NN_NO_CHECK(cpp_geog_3D_CRS),
4311
0
                    cpp_projected_2D_crs->derivingConversion(), newCS));
4312
0
        } catch (const std::exception &e) {
4313
0
            proj_log_error(ctx, __FUNCTION__, e.what());
4314
0
            return nullptr;
4315
0
        }
4316
0
    } else {
4317
0
        try {
4318
0
            auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
4319
0
            return pj_obj_create(ctx,
4320
0
                                 cpp_projected_2D_crs->promoteTo3D(
4321
0
                                     crs_name ? std::string(crs_name)
4322
0
                                              : cpp_projected_2D_crs->nameStr(),
4323
0
                                     dbContext));
4324
0
        } catch (const std::exception &e) {
4325
0
            proj_log_error(ctx, __FUNCTION__, e.what());
4326
0
            return nullptr;
4327
0
        }
4328
0
    }
4329
0
}
4330
4331
// ---------------------------------------------------------------------------
4332
4333
/** \brief Create a 2D CRS from an existing 3D CRS.
4334
 *
4335
 * See osgeo::proj::crs::CRS::demoteTo2D().
4336
 *
4337
 * The returned object must be unreferenced with proj_destroy() after
4338
 * use.
4339
 * It should be used by at most one thread at a time.
4340
 *
4341
 * @param ctx PROJ context, or NULL for default context
4342
 * @param crs_2D_name CRS name. Or NULL (in which case the name of crs_3D
4343
 * will be used)
4344
 * @param crs_3D 3D CRS to be "demoted" to 2D. Must not be NULL.
4345
 *
4346
 * @return Object that must be unreferenced with
4347
 * proj_destroy(), or NULL in case of error.
4348
 * @since 6.3
4349
 */
4350
PJ *proj_crs_demote_to_2D(PJ_CONTEXT *ctx, const char *crs_2D_name,
4351
2.29k
                          const PJ *crs_3D) {
4352
2.29k
    SANITIZE_CTX(ctx);
4353
2.29k
    if (!crs_3D) {
4354
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4355
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4356
0
        return nullptr;
4357
0
    }
4358
2.29k
    auto cpp_3D_crs = dynamic_cast<const CRS *>(crs_3D->iso_obj.get());
4359
2.29k
    if (!cpp_3D_crs) {
4360
0
        proj_log_error(ctx, __FUNCTION__, "crs_3D is not a CRS");
4361
0
        return nullptr;
4362
0
    }
4363
2.29k
    try {
4364
2.29k
        auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
4365
2.29k
        return pj_obj_create(
4366
2.29k
            ctx, cpp_3D_crs->demoteTo2D(crs_2D_name ? std::string(crs_2D_name)
4367
2.29k
                                                    : cpp_3D_crs->nameStr(),
4368
2.29k
                                        dbContext));
4369
2.29k
    } catch (const std::exception &e) {
4370
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4371
0
        return nullptr;
4372
0
    }
4373
2.29k
}
4374
4375
// ---------------------------------------------------------------------------
4376
4377
/** \brief Instantiate a EngineeringCRS with just a name
4378
 *
4379
 * The returned object must be unreferenced with proj_destroy() after
4380
 * use.
4381
 * It should be used by at most one thread at a time.
4382
 *
4383
 * @param ctx PROJ context, or NULL for default context
4384
 * @param crs_name CRS name. Or NULL.
4385
 *
4386
 * @return Object that must be unreferenced with
4387
 * proj_destroy(), or NULL in case of error.
4388
 */
4389
0
PJ *proj_create_engineering_crs(PJ_CONTEXT *ctx, const char *crs_name) {
4390
0
    SANITIZE_CTX(ctx);
4391
0
    try {
4392
0
        return pj_obj_create(
4393
0
            ctx, EngineeringCRS::create(
4394
0
                     createPropertyMapName(crs_name),
4395
0
                     EngineeringDatum::create(
4396
0
                         createPropertyMapName(UNKNOWN_ENGINEERING_DATUM)),
4397
0
                     CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)));
4398
0
    } catch (const std::exception &e) {
4399
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4400
0
        return nullptr;
4401
0
    }
4402
0
}
4403
4404
// ---------------------------------------------------------------------------
4405
4406
//! @cond Doxygen_Suppress
4407
4408
static void setSingleOperationElements(
4409
    const char *name, const char *auth_name, const char *code,
4410
    const char *method_name, const char *method_auth_name,
4411
    const char *method_code, int param_count,
4412
    const PJ_PARAM_DESCRIPTION *params, PropertyMap &propSingleOp,
4413
    PropertyMap &propMethod, std::vector<OperationParameterNNPtr> &parameters,
4414
0
    std::vector<ParameterValueNNPtr> &values) {
4415
0
    propSingleOp.set(common::IdentifiedObject::NAME_KEY,
4416
0
                     name ? name : "unnamed");
4417
0
    if (auth_name && code) {
4418
0
        propSingleOp.set(metadata::Identifier::CODESPACE_KEY, auth_name)
4419
0
            .set(metadata::Identifier::CODE_KEY, code);
4420
0
    }
4421
4422
0
    propMethod.set(common::IdentifiedObject::NAME_KEY,
4423
0
                   method_name ? method_name : "unnamed");
4424
0
    if (method_auth_name && method_code) {
4425
0
        propMethod.set(metadata::Identifier::CODESPACE_KEY, method_auth_name)
4426
0
            .set(metadata::Identifier::CODE_KEY, method_code);
4427
0
    }
4428
4429
0
    for (int i = 0; i < param_count; i++) {
4430
0
        PropertyMap propParam;
4431
0
        propParam.set(common::IdentifiedObject::NAME_KEY,
4432
0
                      params[i].name ? params[i].name : "unnamed");
4433
0
        if (params[i].auth_name && params[i].code) {
4434
0
            propParam
4435
0
                .set(metadata::Identifier::CODESPACE_KEY, params[i].auth_name)
4436
0
                .set(metadata::Identifier::CODE_KEY, params[i].code);
4437
0
        }
4438
0
        parameters.emplace_back(OperationParameter::create(propParam));
4439
0
        auto unit_type = UnitOfMeasure::Type::UNKNOWN;
4440
0
        switch (params[i].unit_type) {
4441
0
        case PJ_UT_ANGULAR:
4442
0
            unit_type = UnitOfMeasure::Type::ANGULAR;
4443
0
            break;
4444
0
        case PJ_UT_LINEAR:
4445
0
            unit_type = UnitOfMeasure::Type::LINEAR;
4446
0
            break;
4447
0
        case PJ_UT_SCALE:
4448
0
            unit_type = UnitOfMeasure::Type::SCALE;
4449
0
            break;
4450
0
        case PJ_UT_TIME:
4451
0
            unit_type = UnitOfMeasure::Type::TIME;
4452
0
            break;
4453
0
        case PJ_UT_PARAMETRIC:
4454
0
            unit_type = UnitOfMeasure::Type::PARAMETRIC;
4455
0
            break;
4456
0
        }
4457
4458
0
        Measure measure(
4459
0
            params[i].value,
4460
0
            params[i].unit_type == PJ_UT_ANGULAR
4461
0
                ? createAngularUnit(params[i].unit_name,
4462
0
                                    params[i].unit_conv_factor)
4463
0
            : params[i].unit_type == PJ_UT_LINEAR
4464
0
                ? createLinearUnit(params[i].unit_name,
4465
0
                                   params[i].unit_conv_factor)
4466
0
                : UnitOfMeasure(params[i].unit_name ? params[i].unit_name
4467
0
                                                    : "unnamed",
4468
0
                                params[i].unit_conv_factor, unit_type));
4469
0
        values.emplace_back(ParameterValue::create(measure));
4470
0
    }
4471
0
}
4472
4473
//! @endcond
4474
4475
// ---------------------------------------------------------------------------
4476
4477
/** \brief Instantiate a Conversion
4478
 *
4479
 * The returned object must be unreferenced with proj_destroy() after
4480
 * use.
4481
 * It should be used by at most one thread at a time.
4482
 *
4483
 * @param ctx PROJ context, or NULL for default context
4484
 * @param name Conversion name. Or NULL.
4485
 * @param auth_name Conversion authority name. Or NULL.
4486
 * @param code Conversion code. Or NULL.
4487
 * @param method_name Method name. Or NULL.
4488
 * @param method_auth_name Method authority name. Or NULL.
4489
 * @param method_code Method code. Or NULL.
4490
 * @param param_count Number of parameters (size of params argument)
4491
 * @param params Parameter descriptions (array of size param_count)
4492
 *
4493
 * @return Object that must be unreferenced with
4494
 * proj_destroy(), or NULL in case of error.
4495
 */
4496
4497
PJ *proj_create_conversion(PJ_CONTEXT *ctx, const char *name,
4498
                           const char *auth_name, const char *code,
4499
                           const char *method_name,
4500
                           const char *method_auth_name,
4501
                           const char *method_code, int param_count,
4502
0
                           const PJ_PARAM_DESCRIPTION *params) {
4503
0
    SANITIZE_CTX(ctx);
4504
0
    try {
4505
0
        PropertyMap propSingleOp;
4506
0
        PropertyMap propMethod;
4507
0
        std::vector<OperationParameterNNPtr> parameters;
4508
0
        std::vector<ParameterValueNNPtr> values;
4509
4510
0
        setSingleOperationElements(
4511
0
            name, auth_name, code, method_name, method_auth_name, method_code,
4512
0
            param_count, params, propSingleOp, propMethod, parameters, values);
4513
4514
0
        return pj_obj_create(ctx, Conversion::create(propSingleOp, propMethod,
4515
0
                                                     parameters, values));
4516
0
    } catch (const std::exception &e) {
4517
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4518
0
        return nullptr;
4519
0
    }
4520
0
}
4521
4522
// ---------------------------------------------------------------------------
4523
4524
/** \brief Instantiate a Transformation
4525
 *
4526
 * The returned object must be unreferenced with proj_destroy() after
4527
 * use.
4528
 * It should be used by at most one thread at a time.
4529
 *
4530
 * @param ctx PROJ context, or NULL for default context
4531
 * @param name Transformation name. Or NULL.
4532
 * @param auth_name Transformation authority name. Or NULL.
4533
 * @param code Transformation code. Or NULL.
4534
 * @param source_crs Object of type CRS representing the source CRS.
4535
 * Must not be NULL.
4536
 * @param target_crs Object of type CRS representing the target CRS.
4537
 * Must not be NULL.
4538
 * @param interpolation_crs Object of type CRS representing the interpolation
4539
 * CRS. Or NULL.
4540
 * @param method_name Method name. Or NULL.
4541
 * @param method_auth_name Method authority name. Or NULL.
4542
 * @param method_code Method code. Or NULL.
4543
 * @param param_count Number of parameters (size of params argument)
4544
 * @param params Parameter descriptions (array of size param_count)
4545
 * @param accuracy Accuracy of the transformation in meters. A negative
4546
 * values means unknown.
4547
 *
4548
 * @return Object that must be unreferenced with
4549
 * proj_destroy(), or NULL in case of error.
4550
 */
4551
4552
PJ *proj_create_transformation(
4553
    PJ_CONTEXT *ctx, const char *name, const char *auth_name, const char *code,
4554
    const PJ *source_crs, const PJ *target_crs, const PJ *interpolation_crs,
4555
    const char *method_name, const char *method_auth_name,
4556
    const char *method_code, int param_count,
4557
0
    const PJ_PARAM_DESCRIPTION *params, double accuracy) {
4558
0
    SANITIZE_CTX(ctx);
4559
0
    if (!source_crs || !target_crs) {
4560
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4561
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4562
0
        return nullptr;
4563
0
    }
4564
4565
0
    auto l_sourceCRS = std::dynamic_pointer_cast<CRS>(source_crs->iso_obj);
4566
0
    if (!l_sourceCRS) {
4567
0
        proj_log_error(ctx, __FUNCTION__, "source_crs is not a CRS");
4568
0
        return nullptr;
4569
0
    }
4570
4571
0
    auto l_targetCRS = std::dynamic_pointer_cast<CRS>(target_crs->iso_obj);
4572
0
    if (!l_targetCRS) {
4573
0
        proj_log_error(ctx, __FUNCTION__, "target_crs is not a CRS");
4574
0
        return nullptr;
4575
0
    }
4576
4577
0
    CRSPtr l_interpolationCRS;
4578
0
    if (interpolation_crs) {
4579
0
        l_interpolationCRS =
4580
0
            std::dynamic_pointer_cast<CRS>(interpolation_crs->iso_obj);
4581
0
        if (!l_interpolationCRS) {
4582
0
            proj_log_error(ctx, __FUNCTION__, "interpolation_crs is not a CRS");
4583
0
            return nullptr;
4584
0
        }
4585
0
    }
4586
4587
0
    try {
4588
0
        PropertyMap propSingleOp;
4589
0
        PropertyMap propMethod;
4590
0
        std::vector<OperationParameterNNPtr> parameters;
4591
0
        std::vector<ParameterValueNNPtr> values;
4592
4593
0
        setSingleOperationElements(
4594
0
            name, auth_name, code, method_name, method_auth_name, method_code,
4595
0
            param_count, params, propSingleOp, propMethod, parameters, values);
4596
4597
0
        std::vector<metadata::PositionalAccuracyNNPtr> accuracies;
4598
0
        if (accuracy >= 0.0) {
4599
0
            accuracies.emplace_back(
4600
0
                PositionalAccuracy::create(toString(accuracy)));
4601
0
        }
4602
4603
0
        return pj_obj_create(
4604
0
            ctx,
4605
0
            Transformation::create(propSingleOp, NN_NO_CHECK(l_sourceCRS),
4606
0
                                   NN_NO_CHECK(l_targetCRS), l_interpolationCRS,
4607
0
                                   propMethod, parameters, values, accuracies));
4608
0
    } catch (const std::exception &e) {
4609
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4610
0
        return nullptr;
4611
0
    }
4612
0
}
4613
4614
// ---------------------------------------------------------------------------
4615
4616
/**
4617
 * \brief Return an equivalent projection.
4618
 *
4619
 * Currently implemented:
4620
 * <ul>
4621
 * <li>EPSG_CODE_METHOD_MERCATOR_VARIANT_A (1SP) to
4622
 * EPSG_CODE_METHOD_MERCATOR_VARIANT_B (2SP)</li>
4623
 * <li>EPSG_CODE_METHOD_MERCATOR_VARIANT_B (2SP) to
4624
 * EPSG_CODE_METHOD_MERCATOR_VARIANT_A (1SP)</li>
4625
 * <li>EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP to
4626
 * EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP</li>
4627
 * <li>EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP to
4628
 * EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP</li>
4629
 * </ul>
4630
 *
4631
 * @param ctx PROJ context, or NULL for default context
4632
 * @param conversion Object of type Conversion. Must not be NULL.
4633
 * @param new_method_epsg_code EPSG code of the target method. Or 0 (in which
4634
 * case new_method_name must be specified).
4635
 * @param new_method_name EPSG or PROJ target method name. Or nullptr  (in which
4636
 * case new_method_epsg_code must be specified).
4637
 * @return new conversion that must be unreferenced with
4638
 * proj_destroy(), or NULL in case of error.
4639
 */
4640
PJ *proj_convert_conversion_to_other_method(PJ_CONTEXT *ctx,
4641
                                            const PJ *conversion,
4642
                                            int new_method_epsg_code,
4643
0
                                            const char *new_method_name) {
4644
0
    SANITIZE_CTX(ctx);
4645
0
    if (!conversion) {
4646
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4647
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4648
0
        return nullptr;
4649
0
    }
4650
0
    auto conv = dynamic_cast<const Conversion *>(conversion->iso_obj.get());
4651
0
    if (!conv) {
4652
0
        proj_log_error(ctx, __FUNCTION__, "not a Conversion");
4653
0
        return nullptr;
4654
0
    }
4655
0
    if (new_method_epsg_code == 0) {
4656
0
        if (!new_method_name) {
4657
0
            return nullptr;
4658
0
        }
4659
0
        if (metadata::Identifier::isEquivalentName(
4660
0
                new_method_name, EPSG_NAME_METHOD_MERCATOR_VARIANT_A)) {
4661
0
            new_method_epsg_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
4662
0
        } else if (metadata::Identifier::isEquivalentName(
4663
0
                       new_method_name, EPSG_NAME_METHOD_MERCATOR_VARIANT_B)) {
4664
0
            new_method_epsg_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
4665
0
        } else if (metadata::Identifier::isEquivalentName(
4666
0
                       new_method_name,
4667
0
                       EPSG_NAME_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)) {
4668
0
            new_method_epsg_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
4669
0
        } else if (metadata::Identifier::isEquivalentName(
4670
0
                       new_method_name,
4671
0
                       EPSG_NAME_METHOD_LAMBERT_CONIC_CONFORMAL_2SP)) {
4672
0
            new_method_epsg_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
4673
0
        }
4674
0
    }
4675
0
    try {
4676
0
        auto new_conv = conv->convertToOtherMethod(new_method_epsg_code);
4677
0
        if (!new_conv)
4678
0
            return nullptr;
4679
0
        return pj_obj_create(ctx, NN_NO_CHECK(new_conv));
4680
0
    } catch (const std::exception &e) {
4681
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4682
0
        return nullptr;
4683
0
    }
4684
0
}
4685
4686
// ---------------------------------------------------------------------------
4687
4688
//! @cond Doxygen_Suppress
4689
4690
0
static CoordinateSystemAxisNNPtr createAxis(const PJ_AXIS_DESCRIPTION &axis) {
4691
0
    const auto dir =
4692
0
        axis.direction ? AxisDirection::valueOf(axis.direction) : nullptr;
4693
0
    if (dir == nullptr)
4694
0
        throw Exception("invalid value for axis direction");
4695
0
    auto unit_type = UnitOfMeasure::Type::UNKNOWN;
4696
0
    switch (axis.unit_type) {
4697
0
    case PJ_UT_ANGULAR:
4698
0
        unit_type = UnitOfMeasure::Type::ANGULAR;
4699
0
        break;
4700
0
    case PJ_UT_LINEAR:
4701
0
        unit_type = UnitOfMeasure::Type::LINEAR;
4702
0
        break;
4703
0
    case PJ_UT_SCALE:
4704
0
        unit_type = UnitOfMeasure::Type::SCALE;
4705
0
        break;
4706
0
    case PJ_UT_TIME:
4707
0
        unit_type = UnitOfMeasure::Type::TIME;
4708
0
        break;
4709
0
    case PJ_UT_PARAMETRIC:
4710
0
        unit_type = UnitOfMeasure::Type::PARAMETRIC;
4711
0
        break;
4712
0
    }
4713
0
    const common::UnitOfMeasure unit(
4714
0
        axis.unit_type == PJ_UT_ANGULAR
4715
0
            ? createAngularUnit(axis.unit_name, axis.unit_conv_factor)
4716
0
        : axis.unit_type == PJ_UT_LINEAR
4717
0
            ? createLinearUnit(axis.unit_name, axis.unit_conv_factor)
4718
0
            : UnitOfMeasure(axis.unit_name ? axis.unit_name : "unnamed",
4719
0
                            axis.unit_conv_factor, unit_type));
4720
4721
0
    return CoordinateSystemAxis::create(
4722
0
        createPropertyMapName(axis.name),
4723
0
        axis.abbreviation ? axis.abbreviation : std::string(), *dir, unit);
4724
0
}
4725
4726
//! @endcond
4727
4728
// ---------------------------------------------------------------------------
4729
4730
/** \brief Instantiate a CoordinateSystem.
4731
 *
4732
 * The returned object must be unreferenced with proj_destroy() after
4733
 * use.
4734
 * It should be used by at most one thread at a time.
4735
 *
4736
 * @param ctx PROJ context, or NULL for default context
4737
 * @param type Coordinate system type.
4738
 * @param axis_count Number of axis
4739
 * @param axis Axis description (array of size axis_count)
4740
 *
4741
 * @return Object that must be unreferenced with
4742
 * proj_destroy(), or NULL in case of error.
4743
 */
4744
4745
PJ *proj_create_cs(PJ_CONTEXT *ctx, PJ_COORDINATE_SYSTEM_TYPE type,
4746
0
                   int axis_count, const PJ_AXIS_DESCRIPTION *axis) {
4747
0
    SANITIZE_CTX(ctx);
4748
0
    try {
4749
0
        switch (type) {
4750
0
        case PJ_CS_TYPE_UNKNOWN:
4751
0
            return nullptr;
4752
4753
0
        case PJ_CS_TYPE_CARTESIAN: {
4754
0
            if (axis_count == 2) {
4755
0
                return pj_obj_create(
4756
0
                    ctx, CartesianCS::create(PropertyMap(), createAxis(axis[0]),
4757
0
                                             createAxis(axis[1])));
4758
0
            } else if (axis_count == 3) {
4759
0
                return pj_obj_create(
4760
0
                    ctx, CartesianCS::create(PropertyMap(), createAxis(axis[0]),
4761
0
                                             createAxis(axis[1]),
4762
0
                                             createAxis(axis[2])));
4763
0
            }
4764
0
            break;
4765
0
        }
4766
4767
0
        case PJ_CS_TYPE_ELLIPSOIDAL: {
4768
0
            if (axis_count == 2) {
4769
0
                return pj_obj_create(
4770
0
                    ctx,
4771
0
                    EllipsoidalCS::create(PropertyMap(), createAxis(axis[0]),
4772
0
                                          createAxis(axis[1])));
4773
0
            } else if (axis_count == 3) {
4774
0
                return pj_obj_create(
4775
0
                    ctx, EllipsoidalCS::create(
4776
0
                             PropertyMap(), createAxis(axis[0]),
4777
0
                             createAxis(axis[1]), createAxis(axis[2])));
4778
0
            }
4779
0
            break;
4780
0
        }
4781
4782
0
        case PJ_CS_TYPE_VERTICAL: {
4783
0
            if (axis_count == 1) {
4784
0
                return pj_obj_create(
4785
0
                    ctx,
4786
0
                    VerticalCS::create(PropertyMap(), createAxis(axis[0])));
4787
0
            }
4788
0
            break;
4789
0
        }
4790
4791
0
        case PJ_CS_TYPE_SPHERICAL: {
4792
0
            if (axis_count == 3) {
4793
0
                return pj_obj_create(
4794
0
                    ctx, EllipsoidalCS::create(
4795
0
                             PropertyMap(), createAxis(axis[0]),
4796
0
                             createAxis(axis[1]), createAxis(axis[2])));
4797
0
            }
4798
0
            break;
4799
0
        }
4800
4801
0
        case PJ_CS_TYPE_PARAMETRIC: {
4802
0
            if (axis_count == 1) {
4803
0
                return pj_obj_create(
4804
0
                    ctx,
4805
0
                    ParametricCS::create(PropertyMap(), createAxis(axis[0])));
4806
0
            }
4807
0
            break;
4808
0
        }
4809
4810
0
        case PJ_CS_TYPE_ORDINAL: {
4811
0
            std::vector<CoordinateSystemAxisNNPtr> axisVector;
4812
0
            for (int i = 0; i < axis_count; i++) {
4813
0
                axisVector.emplace_back(createAxis(axis[i]));
4814
0
            }
4815
4816
0
            return pj_obj_create(ctx,
4817
0
                                 OrdinalCS::create(PropertyMap(), axisVector));
4818
0
        }
4819
4820
0
        case PJ_CS_TYPE_DATETIMETEMPORAL: {
4821
0
            if (axis_count == 1) {
4822
0
                return pj_obj_create(
4823
0
                    ctx, DateTimeTemporalCS::create(PropertyMap(),
4824
0
                                                    createAxis(axis[0])));
4825
0
            }
4826
0
            break;
4827
0
        }
4828
4829
0
        case PJ_CS_TYPE_TEMPORALCOUNT: {
4830
0
            if (axis_count == 1) {
4831
0
                return pj_obj_create(
4832
0
                    ctx, TemporalCountCS::create(PropertyMap(),
4833
0
                                                 createAxis(axis[0])));
4834
0
            }
4835
0
            break;
4836
0
        }
4837
4838
0
        case PJ_CS_TYPE_TEMPORALMEASURE: {
4839
0
            if (axis_count == 1) {
4840
0
                return pj_obj_create(
4841
0
                    ctx, TemporalMeasureCS::create(PropertyMap(),
4842
0
                                                   createAxis(axis[0])));
4843
0
            }
4844
0
            break;
4845
0
        }
4846
0
        }
4847
4848
0
    } catch (const std::exception &e) {
4849
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4850
0
        return nullptr;
4851
0
    }
4852
0
    proj_log_error(ctx, __FUNCTION__, "Wrong value for axis_count");
4853
0
    return nullptr;
4854
0
}
4855
4856
// ---------------------------------------------------------------------------
4857
4858
/** \brief Instantiate a CartesiansCS 2D
4859
 *
4860
 * The returned object must be unreferenced with proj_destroy() after
4861
 * use.
4862
 * It should be used by at most one thread at a time.
4863
 *
4864
 * @param ctx PROJ context, or NULL for default context
4865
 * @param type Coordinate system type.
4866
 * @param unit_name Unit name.
4867
 * @param unit_conv_factor Unit conversion factor to SI.
4868
 *
4869
 * @return Object that must be unreferenced with
4870
 * proj_destroy(), or NULL in case of error.
4871
 */
4872
4873
PJ *proj_create_cartesian_2D_cs(PJ_CONTEXT *ctx, PJ_CARTESIAN_CS_2D_TYPE type,
4874
                                const char *unit_name,
4875
0
                                double unit_conv_factor) {
4876
0
    SANITIZE_CTX(ctx);
4877
0
    try {
4878
0
        switch (type) {
4879
0
        case PJ_CART2D_EASTING_NORTHING:
4880
0
            return pj_obj_create(
4881
0
                ctx, CartesianCS::createEastingNorthing(
4882
0
                         createLinearUnit(unit_name, unit_conv_factor)));
4883
4884
0
        case PJ_CART2D_NORTHING_EASTING:
4885
0
            return pj_obj_create(
4886
0
                ctx, CartesianCS::createNorthingEasting(
4887
0
                         createLinearUnit(unit_name, unit_conv_factor)));
4888
4889
0
        case PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH:
4890
0
            return pj_obj_create(
4891
0
                ctx, CartesianCS::createNorthPoleEastingSouthNorthingSouth(
4892
0
                         createLinearUnit(unit_name, unit_conv_factor)));
4893
4894
0
        case PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH:
4895
0
            return pj_obj_create(
4896
0
                ctx, CartesianCS::createSouthPoleEastingNorthNorthingNorth(
4897
0
                         createLinearUnit(unit_name, unit_conv_factor)));
4898
4899
0
        case PJ_CART2D_WESTING_SOUTHING:
4900
0
            return pj_obj_create(
4901
0
                ctx, CartesianCS::createWestingSouthing(
4902
0
                         createLinearUnit(unit_name, unit_conv_factor)));
4903
0
        }
4904
0
    } catch (const std::exception &e) {
4905
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4906
0
    }
4907
0
    return nullptr;
4908
0
}
4909
4910
// ---------------------------------------------------------------------------
4911
4912
/** \brief Instantiate a Ellipsoidal 2D
4913
 *
4914
 * The returned object must be unreferenced with proj_destroy() after
4915
 * use.
4916
 * It should be used by at most one thread at a time.
4917
 *
4918
 * @param ctx PROJ context, or NULL for default context
4919
 * @param type Coordinate system type.
4920
 * @param unit_name Name of the angular units. Or NULL for Degree
4921
 * @param unit_conv_factor Conversion factor from the angular unit to radian.
4922
 * Or 0 for Degree if unit_name == NULL. Otherwise should be not NULL
4923
 *
4924
 * @return Object that must be unreferenced with
4925
 * proj_destroy(), or NULL in case of error.
4926
 */
4927
4928
PJ *proj_create_ellipsoidal_2D_cs(PJ_CONTEXT *ctx,
4929
                                  PJ_ELLIPSOIDAL_CS_2D_TYPE type,
4930
                                  const char *unit_name,
4931
2.74k
                                  double unit_conv_factor) {
4932
2.74k
    SANITIZE_CTX(ctx);
4933
2.74k
    try {
4934
2.74k
        switch (type) {
4935
2.74k
        case PJ_ELLPS2D_LONGITUDE_LATITUDE:
4936
2.74k
            return pj_obj_create(
4937
2.74k
                ctx, EllipsoidalCS::createLongitudeLatitude(
4938
2.74k
                         createAngularUnit(unit_name, unit_conv_factor)));
4939
4940
0
        case PJ_ELLPS2D_LATITUDE_LONGITUDE:
4941
0
            return pj_obj_create(
4942
0
                ctx, EllipsoidalCS::createLatitudeLongitude(
4943
0
                         createAngularUnit(unit_name, unit_conv_factor)));
4944
2.74k
        }
4945
2.74k
    } catch (const std::exception &e) {
4946
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4947
0
    }
4948
0
    return nullptr;
4949
2.74k
}
4950
4951
// ---------------------------------------------------------------------------
4952
4953
/** \brief Instantiate a Ellipsoidal 3D
4954
 *
4955
 * The returned object must be unreferenced with proj_destroy() after
4956
 * use.
4957
 * It should be used by at most one thread at a time.
4958
 *
4959
 * @param ctx PROJ context, or NULL for default context
4960
 * @param type Coordinate system type.
4961
 * @param horizontal_angular_unit_name Name of the angular units. Or NULL for
4962
 * Degree.
4963
 * @param horizontal_angular_unit_conv_factor Conversion factor from the angular
4964
 * unit to radian. Or 0 for Degree if horizontal_angular_unit_name == NULL.
4965
 * Otherwise should be not NULL
4966
 * @param vertical_linear_unit_name Vertical linear unit name. Or NULL for
4967
 * Metre.
4968
 * @param vertical_linear_unit_conv_factor Vertical linear unit conversion
4969
 * factor to metre. Or 0 for Metre if vertical_linear_unit_name == NULL.
4970
 * Otherwise should be not NULL
4971
4972
 * @return Object that must be unreferenced with
4973
 * proj_destroy(), or NULL in case of error.
4974
 * @since 6.3
4975
 */
4976
4977
PJ *proj_create_ellipsoidal_3D_cs(PJ_CONTEXT *ctx,
4978
                                  PJ_ELLIPSOIDAL_CS_3D_TYPE type,
4979
                                  const char *horizontal_angular_unit_name,
4980
                                  double horizontal_angular_unit_conv_factor,
4981
                                  const char *vertical_linear_unit_name,
4982
0
                                  double vertical_linear_unit_conv_factor) {
4983
0
    SANITIZE_CTX(ctx);
4984
0
    try {
4985
0
        switch (type) {
4986
0
        case PJ_ELLPS3D_LONGITUDE_LATITUDE_HEIGHT:
4987
0
            return pj_obj_create(
4988
0
                ctx, EllipsoidalCS::createLongitudeLatitudeEllipsoidalHeight(
4989
0
                         createAngularUnit(horizontal_angular_unit_name,
4990
0
                                           horizontal_angular_unit_conv_factor),
4991
0
                         createLinearUnit(vertical_linear_unit_name,
4992
0
                                          vertical_linear_unit_conv_factor)));
4993
4994
0
        case PJ_ELLPS3D_LATITUDE_LONGITUDE_HEIGHT:
4995
0
            return pj_obj_create(
4996
0
                ctx, EllipsoidalCS::createLatitudeLongitudeEllipsoidalHeight(
4997
0
                         createAngularUnit(horizontal_angular_unit_name,
4998
0
                                           horizontal_angular_unit_conv_factor),
4999
0
                         createLinearUnit(vertical_linear_unit_name,
5000
0
                                          vertical_linear_unit_conv_factor)));
5001
0
        }
5002
0
    } catch (const std::exception &e) {
5003
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5004
0
    }
5005
0
    return nullptr;
5006
0
}
5007
5008
// ---------------------------------------------------------------------------
5009
5010
/** \brief Instantiate a ProjectedCRS
5011
 *
5012
 * The returned object must be unreferenced with proj_destroy() after
5013
 * use.
5014
 * It should be used by at most one thread at a time.
5015
 *
5016
 * @param ctx PROJ context, or NULL for default context
5017
 * @param crs_name CRS name. Or NULL
5018
 * @param geodetic_crs Base GeodeticCRS. Must not be NULL.
5019
 * @param conversion Conversion. Must not be NULL.
5020
 * @param coordinate_system Cartesian coordinate system. Must not be NULL.
5021
 *
5022
 * @return Object that must be unreferenced with
5023
 * proj_destroy(), or NULL in case of error.
5024
 */
5025
5026
PJ *proj_create_projected_crs(PJ_CONTEXT *ctx, const char *crs_name,
5027
                              const PJ *geodetic_crs, const PJ *conversion,
5028
0
                              const PJ *coordinate_system) {
5029
0
    SANITIZE_CTX(ctx);
5030
0
    if (!geodetic_crs || !conversion || !coordinate_system) {
5031
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
5032
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
5033
0
        return nullptr;
5034
0
    }
5035
0
    auto geodCRS =
5036
0
        std::dynamic_pointer_cast<GeodeticCRS>(geodetic_crs->iso_obj);
5037
0
    if (!geodCRS) {
5038
0
        return nullptr;
5039
0
    }
5040
0
    auto conv = std::dynamic_pointer_cast<Conversion>(conversion->iso_obj);
5041
0
    if (!conv) {
5042
0
        return nullptr;
5043
0
    }
5044
0
    auto cs =
5045
0
        std::dynamic_pointer_cast<CartesianCS>(coordinate_system->iso_obj);
5046
0
    if (!cs) {
5047
0
        return nullptr;
5048
0
    }
5049
0
    try {
5050
0
        return pj_obj_create(
5051
0
            ctx, ProjectedCRS::create(createPropertyMapName(crs_name),
5052
0
                                      NN_NO_CHECK(geodCRS), NN_NO_CHECK(conv),
5053
0
                                      NN_NO_CHECK(cs)));
5054
0
    } catch (const std::exception &e) {
5055
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5056
0
    }
5057
0
    return nullptr;
5058
0
}
5059
5060
// ---------------------------------------------------------------------------
5061
5062
//! @cond Doxygen_Suppress
5063
5064
static PJ *proj_create_conversion(PJ_CONTEXT *ctx,
5065
0
                                  const ConversionNNPtr &conv) {
5066
0
    return pj_obj_create(ctx, conv);
5067
0
}
5068
5069
//! @endcond
5070
5071
/* BEGIN: Generated by scripts/create_c_api_projections.py*/
5072
5073
// ---------------------------------------------------------------------------
5074
5075
/** \brief Instantiate a ProjectedCRS with a Universal Transverse Mercator
5076
 * conversion.
5077
 *
5078
 * See osgeo::proj::operation::Conversion::createUTM().
5079
 *
5080
 * Linear parameters are expressed in (linear_unit_name,
5081
 * linear_unit_conv_factor).
5082
 */
5083
0
PJ *proj_create_conversion_utm(PJ_CONTEXT *ctx, int zone, int north) {
5084
0
    SANITIZE_CTX(ctx);
5085
0
    try {
5086
0
        auto conv = Conversion::createUTM(PropertyMap(), zone, north != 0);
5087
0
        return proj_create_conversion(ctx, conv);
5088
0
    } catch (const std::exception &e) {
5089
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5090
0
    }
5091
0
    return nullptr;
5092
0
}
5093
// ---------------------------------------------------------------------------
5094
5095
/** \brief Instantiate a ProjectedCRS with a conversion based on the Transverse
5096
 * Mercator projection method.
5097
 *
5098
 * See osgeo::proj::operation::Conversion::createTransverseMercator().
5099
 *
5100
 * Linear parameters are expressed in (linear_unit_name,
5101
 * linear_unit_conv_factor).
5102
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5103
 */
5104
PJ *proj_create_conversion_transverse_mercator(
5105
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
5106
    double false_easting, double false_northing, const char *ang_unit_name,
5107
    double ang_unit_conv_factor, const char *linear_unit_name,
5108
0
    double linear_unit_conv_factor) {
5109
0
    SANITIZE_CTX(ctx);
5110
0
    try {
5111
0
        UnitOfMeasure linearUnit(
5112
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5113
0
        UnitOfMeasure angUnit(
5114
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5115
0
        auto conv = Conversion::createTransverseMercator(
5116
0
            PropertyMap(), Angle(center_lat, angUnit),
5117
0
            Angle(center_long, angUnit), Scale(scale),
5118
0
            Length(false_easting, linearUnit),
5119
0
            Length(false_northing, linearUnit));
5120
0
        return proj_create_conversion(ctx, conv);
5121
0
    } catch (const std::exception &e) {
5122
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5123
0
    }
5124
0
    return nullptr;
5125
0
}
5126
// ---------------------------------------------------------------------------
5127
5128
/** \brief Instantiate a ProjectedCRS with a conversion based on the Gauss
5129
 * Schreiber Transverse Mercator projection method.
5130
 *
5131
 * See
5132
 * osgeo::proj::operation::Conversion::createGaussSchreiberTransverseMercator().
5133
 *
5134
 * Linear parameters are expressed in (linear_unit_name,
5135
 * linear_unit_conv_factor).
5136
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5137
 */
5138
PJ *proj_create_conversion_gauss_schreiber_transverse_mercator(
5139
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
5140
    double false_easting, double false_northing, const char *ang_unit_name,
5141
    double ang_unit_conv_factor, const char *linear_unit_name,
5142
0
    double linear_unit_conv_factor) {
5143
0
    SANITIZE_CTX(ctx);
5144
0
    try {
5145
0
        UnitOfMeasure linearUnit(
5146
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5147
0
        UnitOfMeasure angUnit(
5148
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5149
0
        auto conv = Conversion::createGaussSchreiberTransverseMercator(
5150
0
            PropertyMap(), Angle(center_lat, angUnit),
5151
0
            Angle(center_long, angUnit), Scale(scale),
5152
0
            Length(false_easting, linearUnit),
5153
0
            Length(false_northing, linearUnit));
5154
0
        return proj_create_conversion(ctx, conv);
5155
0
    } catch (const std::exception &e) {
5156
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5157
0
    }
5158
0
    return nullptr;
5159
0
}
5160
// ---------------------------------------------------------------------------
5161
5162
/** \brief Instantiate a ProjectedCRS with a conversion based on the Transverse
5163
 * Mercator South Orientated projection method.
5164
 *
5165
 * See
5166
 * osgeo::proj::operation::Conversion::createTransverseMercatorSouthOriented().
5167
 *
5168
 * Linear parameters are expressed in (linear_unit_name,
5169
 * linear_unit_conv_factor).
5170
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5171
 */
5172
PJ *proj_create_conversion_transverse_mercator_south_oriented(
5173
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
5174
    double false_easting, double false_northing, const char *ang_unit_name,
5175
    double ang_unit_conv_factor, const char *linear_unit_name,
5176
0
    double linear_unit_conv_factor) {
5177
0
    SANITIZE_CTX(ctx);
5178
0
    try {
5179
0
        UnitOfMeasure linearUnit(
5180
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5181
0
        UnitOfMeasure angUnit(
5182
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5183
0
        auto conv = Conversion::createTransverseMercatorSouthOriented(
5184
0
            PropertyMap(), Angle(center_lat, angUnit),
5185
0
            Angle(center_long, angUnit), Scale(scale),
5186
0
            Length(false_easting, linearUnit),
5187
0
            Length(false_northing, linearUnit));
5188
0
        return proj_create_conversion(ctx, conv);
5189
0
    } catch (const std::exception &e) {
5190
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5191
0
    }
5192
0
    return nullptr;
5193
0
}
5194
// ---------------------------------------------------------------------------
5195
5196
/** \brief Instantiate a ProjectedCRS with a conversion based on the Two Point
5197
 * Equidistant projection method.
5198
 *
5199
 * See osgeo::proj::operation::Conversion::createTwoPointEquidistant().
5200
 *
5201
 * Linear parameters are expressed in (linear_unit_name,
5202
 * linear_unit_conv_factor).
5203
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5204
 */
5205
PJ *proj_create_conversion_two_point_equidistant(
5206
    PJ_CONTEXT *ctx, double latitude_first_point, double longitude_first_point,
5207
    double latitude_second_point, double longitude_secon_point,
5208
    double false_easting, double false_northing, const char *ang_unit_name,
5209
    double ang_unit_conv_factor, const char *linear_unit_name,
5210
0
    double linear_unit_conv_factor) {
5211
0
    SANITIZE_CTX(ctx);
5212
0
    try {
5213
0
        UnitOfMeasure linearUnit(
5214
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5215
0
        UnitOfMeasure angUnit(
5216
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5217
0
        auto conv = Conversion::createTwoPointEquidistant(
5218
0
            PropertyMap(), Angle(latitude_first_point, angUnit),
5219
0
            Angle(longitude_first_point, angUnit),
5220
0
            Angle(latitude_second_point, angUnit),
5221
0
            Angle(longitude_secon_point, angUnit),
5222
0
            Length(false_easting, linearUnit),
5223
0
            Length(false_northing, linearUnit));
5224
0
        return proj_create_conversion(ctx, conv);
5225
0
    } catch (const std::exception &e) {
5226
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5227
0
    }
5228
0
    return nullptr;
5229
0
}
5230
// ---------------------------------------------------------------------------
5231
5232
/** \brief Instantiate a ProjectedCRS with a conversion based on the Tunisia
5233
 * Mining Grid projection method.
5234
 *
5235
 * See osgeo::proj::operation::Conversion::createTunisiaMiningGrid().
5236
 *
5237
 * Linear parameters are expressed in (linear_unit_name,
5238
 * linear_unit_conv_factor).
5239
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5240
 *
5241
 * @since 9.2
5242
 */
5243
PJ *proj_create_conversion_tunisia_mining_grid(
5244
    PJ_CONTEXT *ctx, double center_lat, double center_long,
5245
    double false_easting, double false_northing, const char *ang_unit_name,
5246
    double ang_unit_conv_factor, const char *linear_unit_name,
5247
0
    double linear_unit_conv_factor) {
5248
0
    SANITIZE_CTX(ctx);
5249
0
    try {
5250
0
        UnitOfMeasure linearUnit(
5251
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5252
0
        UnitOfMeasure angUnit(
5253
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5254
0
        auto conv = Conversion::createTunisiaMiningGrid(
5255
0
            PropertyMap(), Angle(center_lat, angUnit),
5256
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
5257
0
            Length(false_northing, linearUnit));
5258
0
        return proj_create_conversion(ctx, conv);
5259
0
    } catch (const std::exception &e) {
5260
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5261
0
    }
5262
0
    return nullptr;
5263
0
}
5264
// ---------------------------------------------------------------------------
5265
5266
/** \brief Instantiate a ProjectedCRS with a conversion based on the Tunisia
5267
 * Mining Grid projection method.
5268
 *
5269
 * See osgeo::proj::operation::Conversion::createTunisiaMiningGrid().
5270
 *
5271
 * Linear parameters are expressed in (linear_unit_name,
5272
 * linear_unit_conv_factor).
5273
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5274
 *
5275
 * @deprecated Replaced by proj_create_conversion_tunisia_mining_grid
5276
 */
5277
PJ *proj_create_conversion_tunisia_mapping_grid(
5278
    PJ_CONTEXT *ctx, double center_lat, double center_long,
5279
    double false_easting, double false_northing, const char *ang_unit_name,
5280
    double ang_unit_conv_factor, const char *linear_unit_name,
5281
0
    double linear_unit_conv_factor) {
5282
0
    SANITIZE_CTX(ctx);
5283
0
    try {
5284
0
        UnitOfMeasure linearUnit(
5285
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5286
0
        UnitOfMeasure angUnit(
5287
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5288
0
        auto conv = Conversion::createTunisiaMiningGrid(
5289
0
            PropertyMap(), Angle(center_lat, angUnit),
5290
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
5291
0
            Length(false_northing, linearUnit));
5292
0
        return proj_create_conversion(ctx, conv);
5293
0
    } catch (const std::exception &e) {
5294
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5295
0
    }
5296
0
    return nullptr;
5297
0
}
5298
// ---------------------------------------------------------------------------
5299
5300
/** \brief Instantiate a ProjectedCRS with a conversion based on the Albers
5301
 * Conic Equal Area projection method.
5302
 *
5303
 * See osgeo::proj::operation::Conversion::createAlbersEqualArea().
5304
 *
5305
 * Linear parameters are expressed in (linear_unit_name,
5306
 * linear_unit_conv_factor).
5307
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5308
 */
5309
PJ *proj_create_conversion_albers_equal_area(
5310
    PJ_CONTEXT *ctx, double latitude_false_origin,
5311
    double longitude_false_origin, double latitude_first_parallel,
5312
    double latitude_second_parallel, double easting_false_origin,
5313
    double northing_false_origin, const char *ang_unit_name,
5314
    double ang_unit_conv_factor, const char *linear_unit_name,
5315
0
    double linear_unit_conv_factor) {
5316
0
    SANITIZE_CTX(ctx);
5317
0
    try {
5318
0
        UnitOfMeasure linearUnit(
5319
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5320
0
        UnitOfMeasure angUnit(
5321
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5322
0
        auto conv = Conversion::createAlbersEqualArea(
5323
0
            PropertyMap(), Angle(latitude_false_origin, angUnit),
5324
0
            Angle(longitude_false_origin, angUnit),
5325
0
            Angle(latitude_first_parallel, angUnit),
5326
0
            Angle(latitude_second_parallel, angUnit),
5327
0
            Length(easting_false_origin, linearUnit),
5328
0
            Length(northing_false_origin, linearUnit));
5329
0
        return proj_create_conversion(ctx, conv);
5330
0
    } catch (const std::exception &e) {
5331
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5332
0
    }
5333
0
    return nullptr;
5334
0
}
5335
// ---------------------------------------------------------------------------
5336
5337
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5338
 * Conic Conformal 1SP projection method.
5339
 *
5340
 * See osgeo::proj::operation::Conversion::createLambertConicConformal_1SP().
5341
 *
5342
 * Linear parameters are expressed in (linear_unit_name,
5343
 * linear_unit_conv_factor).
5344
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5345
 */
5346
PJ *proj_create_conversion_lambert_conic_conformal_1sp(
5347
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
5348
    double false_easting, double false_northing, const char *ang_unit_name,
5349
    double ang_unit_conv_factor, const char *linear_unit_name,
5350
0
    double linear_unit_conv_factor) {
5351
0
    SANITIZE_CTX(ctx);
5352
0
    try {
5353
0
        UnitOfMeasure linearUnit(
5354
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5355
0
        UnitOfMeasure angUnit(
5356
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5357
0
        auto conv = Conversion::createLambertConicConformal_1SP(
5358
0
            PropertyMap(), Angle(center_lat, angUnit),
5359
0
            Angle(center_long, angUnit), Scale(scale),
5360
0
            Length(false_easting, linearUnit),
5361
0
            Length(false_northing, linearUnit));
5362
0
        return proj_create_conversion(ctx, conv);
5363
0
    } catch (const std::exception &e) {
5364
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5365
0
    }
5366
0
    return nullptr;
5367
0
}
5368
// ---------------------------------------------------------------------------
5369
5370
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5371
 * Conic Conformal (1SP Variant B) projection method.
5372
 *
5373
 * See
5374
 * osgeo::proj::operation::Conversion::createLambertConicConformal_1SP_VariantB().
5375
 *
5376
 * Linear parameters are expressed in (linear_unit_name,
5377
 * linear_unit_conv_factor).
5378
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5379
 * @since 9.2.1
5380
 */
5381
PJ *proj_create_conversion_lambert_conic_conformal_1sp_variant_b(
5382
    PJ_CONTEXT *ctx, double latitude_nat_origin, double scale,
5383
    double latitude_false_origin, double longitude_false_origin,
5384
    double easting_false_origin, double northing_false_origin,
5385
    const char *ang_unit_name, double ang_unit_conv_factor,
5386
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
5387
0
    SANITIZE_CTX(ctx);
5388
0
    try {
5389
0
        UnitOfMeasure linearUnit(
5390
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5391
0
        UnitOfMeasure angUnit(
5392
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5393
0
        auto conv = Conversion::createLambertConicConformal_1SP_VariantB(
5394
0
            PropertyMap(), Angle(latitude_nat_origin, angUnit), Scale(scale),
5395
0
            Angle(latitude_false_origin, angUnit),
5396
0
            Angle(longitude_false_origin, angUnit),
5397
0
            Length(easting_false_origin, linearUnit),
5398
0
            Length(northing_false_origin, linearUnit));
5399
0
        return proj_create_conversion(ctx, conv);
5400
0
    } catch (const std::exception &e) {
5401
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5402
0
    }
5403
0
    return nullptr;
5404
0
}
5405
// ---------------------------------------------------------------------------
5406
5407
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5408
 * Conic Conformal (2SP) projection method.
5409
 *
5410
 * See osgeo::proj::operation::Conversion::createLambertConicConformal_2SP().
5411
 *
5412
 * Linear parameters are expressed in (linear_unit_name,
5413
 * linear_unit_conv_factor).
5414
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5415
 */
5416
PJ *proj_create_conversion_lambert_conic_conformal_2sp(
5417
    PJ_CONTEXT *ctx, double latitude_false_origin,
5418
    double longitude_false_origin, double latitude_first_parallel,
5419
    double latitude_second_parallel, double easting_false_origin,
5420
    double northing_false_origin, const char *ang_unit_name,
5421
    double ang_unit_conv_factor, const char *linear_unit_name,
5422
0
    double linear_unit_conv_factor) {
5423
0
    SANITIZE_CTX(ctx);
5424
0
    try {
5425
0
        UnitOfMeasure linearUnit(
5426
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5427
0
        UnitOfMeasure angUnit(
5428
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5429
0
        auto conv = Conversion::createLambertConicConformal_2SP(
5430
0
            PropertyMap(), Angle(latitude_false_origin, angUnit),
5431
0
            Angle(longitude_false_origin, angUnit),
5432
0
            Angle(latitude_first_parallel, angUnit),
5433
0
            Angle(latitude_second_parallel, angUnit),
5434
0
            Length(easting_false_origin, linearUnit),
5435
0
            Length(northing_false_origin, linearUnit));
5436
0
        return proj_create_conversion(ctx, conv);
5437
0
    } catch (const std::exception &e) {
5438
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5439
0
    }
5440
0
    return nullptr;
5441
0
}
5442
// ---------------------------------------------------------------------------
5443
5444
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5445
 * Conic Conformal (2SP Michigan) projection method.
5446
 *
5447
 * See
5448
 * osgeo::proj::operation::Conversion::createLambertConicConformal_2SP_Michigan().
5449
 *
5450
 * Linear parameters are expressed in (linear_unit_name,
5451
 * linear_unit_conv_factor).
5452
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5453
 */
5454
PJ *proj_create_conversion_lambert_conic_conformal_2sp_michigan(
5455
    PJ_CONTEXT *ctx, double latitude_false_origin,
5456
    double longitude_false_origin, double latitude_first_parallel,
5457
    double latitude_second_parallel, double easting_false_origin,
5458
    double northing_false_origin, double ellipsoid_scaling_factor,
5459
    const char *ang_unit_name, double ang_unit_conv_factor,
5460
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
5461
0
    SANITIZE_CTX(ctx);
5462
0
    try {
5463
0
        UnitOfMeasure linearUnit(
5464
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5465
0
        UnitOfMeasure angUnit(
5466
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5467
0
        auto conv = Conversion::createLambertConicConformal_2SP_Michigan(
5468
0
            PropertyMap(), Angle(latitude_false_origin, angUnit),
5469
0
            Angle(longitude_false_origin, angUnit),
5470
0
            Angle(latitude_first_parallel, angUnit),
5471
0
            Angle(latitude_second_parallel, angUnit),
5472
0
            Length(easting_false_origin, linearUnit),
5473
0
            Length(northing_false_origin, linearUnit),
5474
0
            Scale(ellipsoid_scaling_factor));
5475
0
        return proj_create_conversion(ctx, conv);
5476
0
    } catch (const std::exception &e) {
5477
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5478
0
    }
5479
0
    return nullptr;
5480
0
}
5481
// ---------------------------------------------------------------------------
5482
5483
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5484
 * Conic Conformal (2SP Belgium) projection method.
5485
 *
5486
 * See
5487
 * osgeo::proj::operation::Conversion::createLambertConicConformal_2SP_Belgium().
5488
 *
5489
 * Linear parameters are expressed in (linear_unit_name,
5490
 * linear_unit_conv_factor).
5491
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5492
 */
5493
PJ *proj_create_conversion_lambert_conic_conformal_2sp_belgium(
5494
    PJ_CONTEXT *ctx, double latitude_false_origin,
5495
    double longitude_false_origin, double latitude_first_parallel,
5496
    double latitude_second_parallel, double easting_false_origin,
5497
    double northing_false_origin, const char *ang_unit_name,
5498
    double ang_unit_conv_factor, const char *linear_unit_name,
5499
0
    double linear_unit_conv_factor) {
5500
0
    SANITIZE_CTX(ctx);
5501
0
    try {
5502
0
        UnitOfMeasure linearUnit(
5503
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5504
0
        UnitOfMeasure angUnit(
5505
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5506
0
        auto conv = Conversion::createLambertConicConformal_2SP_Belgium(
5507
0
            PropertyMap(), Angle(latitude_false_origin, angUnit),
5508
0
            Angle(longitude_false_origin, angUnit),
5509
0
            Angle(latitude_first_parallel, angUnit),
5510
0
            Angle(latitude_second_parallel, angUnit),
5511
0
            Length(easting_false_origin, linearUnit),
5512
0
            Length(northing_false_origin, linearUnit));
5513
0
        return proj_create_conversion(ctx, conv);
5514
0
    } catch (const std::exception &e) {
5515
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5516
0
    }
5517
0
    return nullptr;
5518
0
}
5519
// ---------------------------------------------------------------------------
5520
5521
/** \brief Instantiate a ProjectedCRS with a conversion based on the Modified
5522
 * Azimuthal Equidistant projection method.
5523
 *
5524
 * See osgeo::proj::operation::Conversion::createAzimuthalEquidistant().
5525
 *
5526
 * Linear parameters are expressed in (linear_unit_name,
5527
 * linear_unit_conv_factor).
5528
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5529
 */
5530
PJ *proj_create_conversion_azimuthal_equidistant(
5531
    PJ_CONTEXT *ctx, double latitude_nat_origin, double longitude_nat_origin,
5532
    double false_easting, double false_northing, const char *ang_unit_name,
5533
    double ang_unit_conv_factor, const char *linear_unit_name,
5534
0
    double linear_unit_conv_factor) {
5535
0
    SANITIZE_CTX(ctx);
5536
0
    try {
5537
0
        UnitOfMeasure linearUnit(
5538
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5539
0
        UnitOfMeasure angUnit(
5540
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5541
0
        auto conv = Conversion::createAzimuthalEquidistant(
5542
0
            PropertyMap(), Angle(latitude_nat_origin, angUnit),
5543
0
            Angle(longitude_nat_origin, angUnit),
5544
0
            Length(false_easting, linearUnit),
5545
0
            Length(false_northing, linearUnit));
5546
0
        return proj_create_conversion(ctx, conv);
5547
0
    } catch (const std::exception &e) {
5548
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5549
0
    }
5550
0
    return nullptr;
5551
0
}
5552
// ---------------------------------------------------------------------------
5553
5554
/** \brief Instantiate a ProjectedCRS with a conversion based on the Guam
5555
 * Projection projection method.
5556
 *
5557
 * See osgeo::proj::operation::Conversion::createGuamProjection().
5558
 *
5559
 * Linear parameters are expressed in (linear_unit_name,
5560
 * linear_unit_conv_factor).
5561
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5562
 */
5563
PJ *proj_create_conversion_guam_projection(
5564
    PJ_CONTEXT *ctx, double latitude_nat_origin, double longitude_nat_origin,
5565
    double false_easting, double false_northing, const char *ang_unit_name,
5566
    double ang_unit_conv_factor, const char *linear_unit_name,
5567
0
    double linear_unit_conv_factor) {
5568
0
    SANITIZE_CTX(ctx);
5569
0
    try {
5570
0
        UnitOfMeasure linearUnit(
5571
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5572
0
        UnitOfMeasure angUnit(
5573
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5574
0
        auto conv = Conversion::createGuamProjection(
5575
0
            PropertyMap(), Angle(latitude_nat_origin, angUnit),
5576
0
            Angle(longitude_nat_origin, angUnit),
5577
0
            Length(false_easting, linearUnit),
5578
0
            Length(false_northing, linearUnit));
5579
0
        return proj_create_conversion(ctx, conv);
5580
0
    } catch (const std::exception &e) {
5581
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5582
0
    }
5583
0
    return nullptr;
5584
0
}
5585
// ---------------------------------------------------------------------------
5586
5587
/** \brief Instantiate a ProjectedCRS with a conversion based on the Bonne
5588
 * projection method.
5589
 *
5590
 * See osgeo::proj::operation::Conversion::createBonne().
5591
 *
5592
 * Linear parameters are expressed in (linear_unit_name,
5593
 * linear_unit_conv_factor).
5594
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5595
 */
5596
PJ *proj_create_conversion_bonne(PJ_CONTEXT *ctx, double latitude_nat_origin,
5597
                                 double longitude_nat_origin,
5598
                                 double false_easting, double false_northing,
5599
                                 const char *ang_unit_name,
5600
                                 double ang_unit_conv_factor,
5601
                                 const char *linear_unit_name,
5602
0
                                 double linear_unit_conv_factor) {
5603
0
    SANITIZE_CTX(ctx);
5604
0
    try {
5605
0
        UnitOfMeasure linearUnit(
5606
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5607
0
        UnitOfMeasure angUnit(
5608
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5609
0
        auto conv = Conversion::createBonne(
5610
0
            PropertyMap(), Angle(latitude_nat_origin, angUnit),
5611
0
            Angle(longitude_nat_origin, angUnit),
5612
0
            Length(false_easting, linearUnit),
5613
0
            Length(false_northing, linearUnit));
5614
0
        return proj_create_conversion(ctx, conv);
5615
0
    } catch (const std::exception &e) {
5616
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5617
0
    }
5618
0
    return nullptr;
5619
0
}
5620
// ---------------------------------------------------------------------------
5621
5622
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5623
 * Cylindrical Equal Area (Spherical) projection method.
5624
 *
5625
 * See
5626
 * osgeo::proj::operation::Conversion::createLambertCylindricalEqualAreaSpherical().
5627
 *
5628
 * Linear parameters are expressed in (linear_unit_name,
5629
 * linear_unit_conv_factor).
5630
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5631
 */
5632
PJ *proj_create_conversion_lambert_cylindrical_equal_area_spherical(
5633
    PJ_CONTEXT *ctx, double latitude_first_parallel,
5634
    double longitude_nat_origin, double false_easting, double false_northing,
5635
    const char *ang_unit_name, double ang_unit_conv_factor,
5636
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
5637
0
    SANITIZE_CTX(ctx);
5638
0
    try {
5639
0
        UnitOfMeasure linearUnit(
5640
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5641
0
        UnitOfMeasure angUnit(
5642
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5643
0
        auto conv = Conversion::createLambertCylindricalEqualAreaSpherical(
5644
0
            PropertyMap(), Angle(latitude_first_parallel, angUnit),
5645
0
            Angle(longitude_nat_origin, angUnit),
5646
0
            Length(false_easting, linearUnit),
5647
0
            Length(false_northing, linearUnit));
5648
0
        return proj_create_conversion(ctx, conv);
5649
0
    } catch (const std::exception &e) {
5650
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5651
0
    }
5652
0
    return nullptr;
5653
0
}
5654
// ---------------------------------------------------------------------------
5655
5656
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5657
 * Cylindrical Equal Area (ellipsoidal form) projection method.
5658
 *
5659
 * See osgeo::proj::operation::Conversion::createLambertCylindricalEqualArea().
5660
 *
5661
 * Linear parameters are expressed in (linear_unit_name,
5662
 * linear_unit_conv_factor).
5663
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5664
 */
5665
PJ *proj_create_conversion_lambert_cylindrical_equal_area(
5666
    PJ_CONTEXT *ctx, double latitude_first_parallel,
5667
    double longitude_nat_origin, double false_easting, double false_northing,
5668
    const char *ang_unit_name, double ang_unit_conv_factor,
5669
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
5670
0
    SANITIZE_CTX(ctx);
5671
0
    try {
5672
0
        UnitOfMeasure linearUnit(
5673
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5674
0
        UnitOfMeasure angUnit(
5675
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5676
0
        auto conv = Conversion::createLambertCylindricalEqualArea(
5677
0
            PropertyMap(), Angle(latitude_first_parallel, angUnit),
5678
0
            Angle(longitude_nat_origin, angUnit),
5679
0
            Length(false_easting, linearUnit),
5680
0
            Length(false_northing, linearUnit));
5681
0
        return proj_create_conversion(ctx, conv);
5682
0
    } catch (const std::exception &e) {
5683
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5684
0
    }
5685
0
    return nullptr;
5686
0
}
5687
// ---------------------------------------------------------------------------
5688
5689
/** \brief Instantiate a ProjectedCRS with a conversion based on the
5690
 * Cassini-Soldner projection method.
5691
 *
5692
 * See osgeo::proj::operation::Conversion::createCassiniSoldner().
5693
 *
5694
 * Linear parameters are expressed in (linear_unit_name,
5695
 * linear_unit_conv_factor).
5696
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5697
 */
5698
PJ *proj_create_conversion_cassini_soldner(
5699
    PJ_CONTEXT *ctx, double center_lat, double center_long,
5700
    double false_easting, double false_northing, const char *ang_unit_name,
5701
    double ang_unit_conv_factor, const char *linear_unit_name,
5702
0
    double linear_unit_conv_factor) {
5703
0
    SANITIZE_CTX(ctx);
5704
0
    try {
5705
0
        UnitOfMeasure linearUnit(
5706
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5707
0
        UnitOfMeasure angUnit(
5708
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5709
0
        auto conv = Conversion::createCassiniSoldner(
5710
0
            PropertyMap(), Angle(center_lat, angUnit),
5711
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
5712
0
            Length(false_northing, linearUnit));
5713
0
        return proj_create_conversion(ctx, conv);
5714
0
    } catch (const std::exception &e) {
5715
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5716
0
    }
5717
0
    return nullptr;
5718
0
}
5719
// ---------------------------------------------------------------------------
5720
5721
/** \brief Instantiate a ProjectedCRS with a conversion based on the Equidistant
5722
 * Conic projection method.
5723
 *
5724
 * See osgeo::proj::operation::Conversion::createEquidistantConic().
5725
 *
5726
 * Linear parameters are expressed in (linear_unit_name,
5727
 * linear_unit_conv_factor).
5728
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5729
 */
5730
PJ *proj_create_conversion_equidistant_conic(
5731
    PJ_CONTEXT *ctx, double center_lat, double center_long,
5732
    double latitude_first_parallel, double latitude_second_parallel,
5733
    double false_easting, double false_northing, const char *ang_unit_name,
5734
    double ang_unit_conv_factor, const char *linear_unit_name,
5735
0
    double linear_unit_conv_factor) {
5736
0
    SANITIZE_CTX(ctx);
5737
0
    try {
5738
0
        UnitOfMeasure linearUnit(
5739
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5740
0
        UnitOfMeasure angUnit(
5741
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5742
0
        auto conv = Conversion::createEquidistantConic(
5743
0
            PropertyMap(), Angle(center_lat, angUnit),
5744
0
            Angle(center_long, angUnit),
5745
0
            Angle(latitude_first_parallel, angUnit),
5746
0
            Angle(latitude_second_parallel, angUnit),
5747
0
            Length(false_easting, linearUnit),
5748
0
            Length(false_northing, linearUnit));
5749
0
        return proj_create_conversion(ctx, conv);
5750
0
    } catch (const std::exception &e) {
5751
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5752
0
    }
5753
0
    return nullptr;
5754
0
}
5755
// ---------------------------------------------------------------------------
5756
5757
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert I
5758
 * projection method.
5759
 *
5760
 * See osgeo::proj::operation::Conversion::createEckertI().
5761
 *
5762
 * Linear parameters are expressed in (linear_unit_name,
5763
 * linear_unit_conv_factor).
5764
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5765
 */
5766
PJ *proj_create_conversion_eckert_i(PJ_CONTEXT *ctx, double center_long,
5767
                                    double false_easting, double false_northing,
5768
                                    const char *ang_unit_name,
5769
                                    double ang_unit_conv_factor,
5770
                                    const char *linear_unit_name,
5771
0
                                    double linear_unit_conv_factor) {
5772
0
    SANITIZE_CTX(ctx);
5773
0
    try {
5774
0
        UnitOfMeasure linearUnit(
5775
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5776
0
        UnitOfMeasure angUnit(
5777
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5778
0
        auto conv = Conversion::createEckertI(
5779
0
            PropertyMap(), Angle(center_long, angUnit),
5780
0
            Length(false_easting, linearUnit),
5781
0
            Length(false_northing, linearUnit));
5782
0
        return proj_create_conversion(ctx, conv);
5783
0
    } catch (const std::exception &e) {
5784
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5785
0
    }
5786
0
    return nullptr;
5787
0
}
5788
// ---------------------------------------------------------------------------
5789
5790
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert II
5791
 * projection method.
5792
 *
5793
 * See osgeo::proj::operation::Conversion::createEckertII().
5794
 *
5795
 * Linear parameters are expressed in (linear_unit_name,
5796
 * linear_unit_conv_factor).
5797
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5798
 */
5799
PJ *proj_create_conversion_eckert_ii(PJ_CONTEXT *ctx, double center_long,
5800
                                     double false_easting,
5801
                                     double false_northing,
5802
                                     const char *ang_unit_name,
5803
                                     double ang_unit_conv_factor,
5804
                                     const char *linear_unit_name,
5805
0
                                     double linear_unit_conv_factor) {
5806
0
    SANITIZE_CTX(ctx);
5807
0
    try {
5808
0
        UnitOfMeasure linearUnit(
5809
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5810
0
        UnitOfMeasure angUnit(
5811
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5812
0
        auto conv = Conversion::createEckertII(
5813
0
            PropertyMap(), Angle(center_long, angUnit),
5814
0
            Length(false_easting, linearUnit),
5815
0
            Length(false_northing, linearUnit));
5816
0
        return proj_create_conversion(ctx, conv);
5817
0
    } catch (const std::exception &e) {
5818
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5819
0
    }
5820
0
    return nullptr;
5821
0
}
5822
// ---------------------------------------------------------------------------
5823
5824
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert III
5825
 * projection method.
5826
 *
5827
 * See osgeo::proj::operation::Conversion::createEckertIII().
5828
 *
5829
 * Linear parameters are expressed in (linear_unit_name,
5830
 * linear_unit_conv_factor).
5831
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5832
 */
5833
PJ *proj_create_conversion_eckert_iii(PJ_CONTEXT *ctx, double center_long,
5834
                                      double false_easting,
5835
                                      double false_northing,
5836
                                      const char *ang_unit_name,
5837
                                      double ang_unit_conv_factor,
5838
                                      const char *linear_unit_name,
5839
0
                                      double linear_unit_conv_factor) {
5840
0
    SANITIZE_CTX(ctx);
5841
0
    try {
5842
0
        UnitOfMeasure linearUnit(
5843
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5844
0
        UnitOfMeasure angUnit(
5845
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5846
0
        auto conv = Conversion::createEckertIII(
5847
0
            PropertyMap(), Angle(center_long, angUnit),
5848
0
            Length(false_easting, linearUnit),
5849
0
            Length(false_northing, linearUnit));
5850
0
        return proj_create_conversion(ctx, conv);
5851
0
    } catch (const std::exception &e) {
5852
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5853
0
    }
5854
0
    return nullptr;
5855
0
}
5856
// ---------------------------------------------------------------------------
5857
5858
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert IV
5859
 * projection method.
5860
 *
5861
 * See osgeo::proj::operation::Conversion::createEckertIV().
5862
 *
5863
 * Linear parameters are expressed in (linear_unit_name,
5864
 * linear_unit_conv_factor).
5865
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5866
 */
5867
PJ *proj_create_conversion_eckert_iv(PJ_CONTEXT *ctx, double center_long,
5868
                                     double false_easting,
5869
                                     double false_northing,
5870
                                     const char *ang_unit_name,
5871
                                     double ang_unit_conv_factor,
5872
                                     const char *linear_unit_name,
5873
0
                                     double linear_unit_conv_factor) {
5874
0
    SANITIZE_CTX(ctx);
5875
0
    try {
5876
0
        UnitOfMeasure linearUnit(
5877
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5878
0
        UnitOfMeasure angUnit(
5879
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5880
0
        auto conv = Conversion::createEckertIV(
5881
0
            PropertyMap(), Angle(center_long, angUnit),
5882
0
            Length(false_easting, linearUnit),
5883
0
            Length(false_northing, linearUnit));
5884
0
        return proj_create_conversion(ctx, conv);
5885
0
    } catch (const std::exception &e) {
5886
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5887
0
    }
5888
0
    return nullptr;
5889
0
}
5890
// ---------------------------------------------------------------------------
5891
5892
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert V
5893
 * projection method.
5894
 *
5895
 * See osgeo::proj::operation::Conversion::createEckertV().
5896
 *
5897
 * Linear parameters are expressed in (linear_unit_name,
5898
 * linear_unit_conv_factor).
5899
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5900
 */
5901
PJ *proj_create_conversion_eckert_v(PJ_CONTEXT *ctx, double center_long,
5902
                                    double false_easting, double false_northing,
5903
                                    const char *ang_unit_name,
5904
                                    double ang_unit_conv_factor,
5905
                                    const char *linear_unit_name,
5906
0
                                    double linear_unit_conv_factor) {
5907
0
    SANITIZE_CTX(ctx);
5908
0
    try {
5909
0
        UnitOfMeasure linearUnit(
5910
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5911
0
        UnitOfMeasure angUnit(
5912
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5913
0
        auto conv = Conversion::createEckertV(
5914
0
            PropertyMap(), Angle(center_long, angUnit),
5915
0
            Length(false_easting, linearUnit),
5916
0
            Length(false_northing, linearUnit));
5917
0
        return proj_create_conversion(ctx, conv);
5918
0
    } catch (const std::exception &e) {
5919
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5920
0
    }
5921
0
    return nullptr;
5922
0
}
5923
// ---------------------------------------------------------------------------
5924
5925
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert VI
5926
 * projection method.
5927
 *
5928
 * See osgeo::proj::operation::Conversion::createEckertVI().
5929
 *
5930
 * Linear parameters are expressed in (linear_unit_name,
5931
 * linear_unit_conv_factor).
5932
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5933
 */
5934
PJ *proj_create_conversion_eckert_vi(PJ_CONTEXT *ctx, double center_long,
5935
                                     double false_easting,
5936
                                     double false_northing,
5937
                                     const char *ang_unit_name,
5938
                                     double ang_unit_conv_factor,
5939
                                     const char *linear_unit_name,
5940
0
                                     double linear_unit_conv_factor) {
5941
0
    SANITIZE_CTX(ctx);
5942
0
    try {
5943
0
        UnitOfMeasure linearUnit(
5944
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5945
0
        UnitOfMeasure angUnit(
5946
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5947
0
        auto conv = Conversion::createEckertVI(
5948
0
            PropertyMap(), Angle(center_long, angUnit),
5949
0
            Length(false_easting, linearUnit),
5950
0
            Length(false_northing, linearUnit));
5951
0
        return proj_create_conversion(ctx, conv);
5952
0
    } catch (const std::exception &e) {
5953
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5954
0
    }
5955
0
    return nullptr;
5956
0
}
5957
// ---------------------------------------------------------------------------
5958
5959
/** \brief Instantiate a ProjectedCRS with a conversion based on the Equidistant
5960
 * Cylindrical projection method.
5961
 *
5962
 * See osgeo::proj::operation::Conversion::createEquidistantCylindrical().
5963
 *
5964
 * Linear parameters are expressed in (linear_unit_name,
5965
 * linear_unit_conv_factor).
5966
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5967
 */
5968
PJ *proj_create_conversion_equidistant_cylindrical(
5969
    PJ_CONTEXT *ctx, double latitude_first_parallel,
5970
    double longitude_nat_origin, double false_easting, double false_northing,
5971
    const char *ang_unit_name, double ang_unit_conv_factor,
5972
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
5973
0
    SANITIZE_CTX(ctx);
5974
0
    try {
5975
0
        UnitOfMeasure linearUnit(
5976
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5977
0
        UnitOfMeasure angUnit(
5978
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5979
0
        auto conv = Conversion::createEquidistantCylindrical(
5980
0
            PropertyMap(), Angle(latitude_first_parallel, angUnit),
5981
0
            Angle(longitude_nat_origin, angUnit),
5982
0
            Length(false_easting, linearUnit),
5983
0
            Length(false_northing, linearUnit));
5984
0
        return proj_create_conversion(ctx, conv);
5985
0
    } catch (const std::exception &e) {
5986
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5987
0
    }
5988
0
    return nullptr;
5989
0
}
5990
// ---------------------------------------------------------------------------
5991
5992
/** \brief Instantiate a ProjectedCRS with a conversion based on the Equidistant
5993
 * Cylindrical (Spherical) projection method.
5994
 *
5995
 * See
5996
 * osgeo::proj::operation::Conversion::createEquidistantCylindricalSpherical().
5997
 *
5998
 * Linear parameters are expressed in (linear_unit_name,
5999
 * linear_unit_conv_factor).
6000
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6001
 */
6002
PJ *proj_create_conversion_equidistant_cylindrical_spherical(
6003
    PJ_CONTEXT *ctx, double latitude_first_parallel,
6004
    double longitude_nat_origin, double false_easting, double false_northing,
6005
    const char *ang_unit_name, double ang_unit_conv_factor,
6006
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
6007
0
    SANITIZE_CTX(ctx);
6008
0
    try {
6009
0
        UnitOfMeasure linearUnit(
6010
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6011
0
        UnitOfMeasure angUnit(
6012
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6013
0
        auto conv = Conversion::createEquidistantCylindricalSpherical(
6014
0
            PropertyMap(), Angle(latitude_first_parallel, angUnit),
6015
0
            Angle(longitude_nat_origin, angUnit),
6016
0
            Length(false_easting, linearUnit),
6017
0
            Length(false_northing, linearUnit));
6018
0
        return proj_create_conversion(ctx, conv);
6019
0
    } catch (const std::exception &e) {
6020
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6021
0
    }
6022
0
    return nullptr;
6023
0
}
6024
// ---------------------------------------------------------------------------
6025
6026
/** \brief Instantiate a ProjectedCRS with a conversion based on the Gall
6027
 * (Stereographic) projection method.
6028
 *
6029
 * See osgeo::proj::operation::Conversion::createGall().
6030
 *
6031
 * Linear parameters are expressed in (linear_unit_name,
6032
 * linear_unit_conv_factor).
6033
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6034
 */
6035
PJ *proj_create_conversion_gall(PJ_CONTEXT *ctx, double center_long,
6036
                                double false_easting, double false_northing,
6037
                                const char *ang_unit_name,
6038
                                double ang_unit_conv_factor,
6039
                                const char *linear_unit_name,
6040
0
                                double linear_unit_conv_factor) {
6041
0
    SANITIZE_CTX(ctx);
6042
0
    try {
6043
0
        UnitOfMeasure linearUnit(
6044
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6045
0
        UnitOfMeasure angUnit(
6046
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6047
0
        auto conv =
6048
0
            Conversion::createGall(PropertyMap(), Angle(center_long, angUnit),
6049
0
                                   Length(false_easting, linearUnit),
6050
0
                                   Length(false_northing, linearUnit));
6051
0
        return proj_create_conversion(ctx, conv);
6052
0
    } catch (const std::exception &e) {
6053
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6054
0
    }
6055
0
    return nullptr;
6056
0
}
6057
// ---------------------------------------------------------------------------
6058
6059
/** \brief Instantiate a ProjectedCRS with a conversion based on the Goode
6060
 * Homolosine projection method.
6061
 *
6062
 * See osgeo::proj::operation::Conversion::createGoodeHomolosine().
6063
 *
6064
 * Linear parameters are expressed in (linear_unit_name,
6065
 * linear_unit_conv_factor).
6066
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6067
 */
6068
PJ *proj_create_conversion_goode_homolosine(PJ_CONTEXT *ctx, double center_long,
6069
                                            double false_easting,
6070
                                            double false_northing,
6071
                                            const char *ang_unit_name,
6072
                                            double ang_unit_conv_factor,
6073
                                            const char *linear_unit_name,
6074
0
                                            double linear_unit_conv_factor) {
6075
0
    SANITIZE_CTX(ctx);
6076
0
    try {
6077
0
        UnitOfMeasure linearUnit(
6078
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6079
0
        UnitOfMeasure angUnit(
6080
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6081
0
        auto conv = Conversion::createGoodeHomolosine(
6082
0
            PropertyMap(), Angle(center_long, angUnit),
6083
0
            Length(false_easting, linearUnit),
6084
0
            Length(false_northing, linearUnit));
6085
0
        return proj_create_conversion(ctx, conv);
6086
0
    } catch (const std::exception &e) {
6087
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6088
0
    }
6089
0
    return nullptr;
6090
0
}
6091
// ---------------------------------------------------------------------------
6092
6093
/** \brief Instantiate a ProjectedCRS with a conversion based on the Interrupted
6094
 * Goode Homolosine projection method.
6095
 *
6096
 * See osgeo::proj::operation::Conversion::createInterruptedGoodeHomolosine().
6097
 *
6098
 * Linear parameters are expressed in (linear_unit_name,
6099
 * linear_unit_conv_factor).
6100
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6101
 */
6102
PJ *proj_create_conversion_interrupted_goode_homolosine(
6103
    PJ_CONTEXT *ctx, double center_long, double false_easting,
6104
    double false_northing, const char *ang_unit_name,
6105
    double ang_unit_conv_factor, const char *linear_unit_name,
6106
0
    double linear_unit_conv_factor) {
6107
0
    SANITIZE_CTX(ctx);
6108
0
    try {
6109
0
        UnitOfMeasure linearUnit(
6110
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6111
0
        UnitOfMeasure angUnit(
6112
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6113
0
        auto conv = Conversion::createInterruptedGoodeHomolosine(
6114
0
            PropertyMap(), Angle(center_long, angUnit),
6115
0
            Length(false_easting, linearUnit),
6116
0
            Length(false_northing, linearUnit));
6117
0
        return proj_create_conversion(ctx, conv);
6118
0
    } catch (const std::exception &e) {
6119
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6120
0
    }
6121
0
    return nullptr;
6122
0
}
6123
// ---------------------------------------------------------------------------
6124
6125
/** \brief Instantiate a ProjectedCRS with a conversion based on the
6126
 * Geostationary Satellite View projection method, with the sweep angle axis of
6127
 * the viewing instrument being x.
6128
 *
6129
 * See osgeo::proj::operation::Conversion::createGeostationarySatelliteSweepX().
6130
 *
6131
 * Linear parameters are expressed in (linear_unit_name,
6132
 * linear_unit_conv_factor).
6133
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6134
 */
6135
PJ *proj_create_conversion_geostationary_satellite_sweep_x(
6136
    PJ_CONTEXT *ctx, double center_long, double height, double false_easting,
6137
    double false_northing, const char *ang_unit_name,
6138
    double ang_unit_conv_factor, const char *linear_unit_name,
6139
0
    double linear_unit_conv_factor) {
6140
0
    SANITIZE_CTX(ctx);
6141
0
    try {
6142
0
        UnitOfMeasure linearUnit(
6143
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6144
0
        UnitOfMeasure angUnit(
6145
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6146
0
        auto conv = Conversion::createGeostationarySatelliteSweepX(
6147
0
            PropertyMap(), Angle(center_long, angUnit),
6148
0
            Length(height, linearUnit), Length(false_easting, linearUnit),
6149
0
            Length(false_northing, linearUnit));
6150
0
        return proj_create_conversion(ctx, conv);
6151
0
    } catch (const std::exception &e) {
6152
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6153
0
    }
6154
0
    return nullptr;
6155
0
}
6156
// ---------------------------------------------------------------------------
6157
6158
/** \brief Instantiate a ProjectedCRS with a conversion based on the
6159
 * Geostationary Satellite View projection method, with the sweep angle axis of
6160
 * the viewing instrument being y.
6161
 *
6162
 * See osgeo::proj::operation::Conversion::createGeostationarySatelliteSweepY().
6163
 *
6164
 * Linear parameters are expressed in (linear_unit_name,
6165
 * linear_unit_conv_factor).
6166
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6167
 */
6168
PJ *proj_create_conversion_geostationary_satellite_sweep_y(
6169
    PJ_CONTEXT *ctx, double center_long, double height, double false_easting,
6170
    double false_northing, const char *ang_unit_name,
6171
    double ang_unit_conv_factor, const char *linear_unit_name,
6172
0
    double linear_unit_conv_factor) {
6173
0
    SANITIZE_CTX(ctx);
6174
0
    try {
6175
0
        UnitOfMeasure linearUnit(
6176
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6177
0
        UnitOfMeasure angUnit(
6178
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6179
0
        auto conv = Conversion::createGeostationarySatelliteSweepY(
6180
0
            PropertyMap(), Angle(center_long, angUnit),
6181
0
            Length(height, linearUnit), Length(false_easting, linearUnit),
6182
0
            Length(false_northing, linearUnit));
6183
0
        return proj_create_conversion(ctx, conv);
6184
0
    } catch (const std::exception &e) {
6185
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6186
0
    }
6187
0
    return nullptr;
6188
0
}
6189
// ---------------------------------------------------------------------------
6190
6191
/** \brief Instantiate a ProjectedCRS with a conversion based on the Gnomonic
6192
 * projection method.
6193
 *
6194
 * See osgeo::proj::operation::Conversion::createGnomonic().
6195
 *
6196
 * Linear parameters are expressed in (linear_unit_name,
6197
 * linear_unit_conv_factor).
6198
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6199
 */
6200
PJ *proj_create_conversion_gnomonic(PJ_CONTEXT *ctx, double center_lat,
6201
                                    double center_long, double false_easting,
6202
                                    double false_northing,
6203
                                    const char *ang_unit_name,
6204
                                    double ang_unit_conv_factor,
6205
                                    const char *linear_unit_name,
6206
0
                                    double linear_unit_conv_factor) {
6207
0
    SANITIZE_CTX(ctx);
6208
0
    try {
6209
0
        UnitOfMeasure linearUnit(
6210
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6211
0
        UnitOfMeasure angUnit(
6212
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6213
0
        auto conv = Conversion::createGnomonic(
6214
0
            PropertyMap(), Angle(center_lat, angUnit),
6215
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
6216
0
            Length(false_northing, linearUnit));
6217
0
        return proj_create_conversion(ctx, conv);
6218
0
    } catch (const std::exception &e) {
6219
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6220
0
    }
6221
0
    return nullptr;
6222
0
}
6223
// ---------------------------------------------------------------------------
6224
6225
/** \brief Instantiate a ProjectedCRS with a conversion based on the Hotine
6226
 * Oblique Mercator (Variant A) projection method.
6227
 *
6228
 * See
6229
 * osgeo::proj::operation::Conversion::createHotineObliqueMercatorVariantA().
6230
 *
6231
 * Linear parameters are expressed in (linear_unit_name,
6232
 * linear_unit_conv_factor).
6233
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6234
 */
6235
PJ *proj_create_conversion_hotine_oblique_mercator_variant_a(
6236
    PJ_CONTEXT *ctx, double latitude_projection_centre,
6237
    double longitude_projection_centre, double azimuth_initial_line,
6238
    double angle_from_rectified_to_skrew_grid, double scale,
6239
    double false_easting, double false_northing, const char *ang_unit_name,
6240
    double ang_unit_conv_factor, const char *linear_unit_name,
6241
0
    double linear_unit_conv_factor) {
6242
0
    SANITIZE_CTX(ctx);
6243
0
    try {
6244
0
        UnitOfMeasure linearUnit(
6245
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6246
0
        UnitOfMeasure angUnit(
6247
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6248
0
        auto conv = Conversion::createHotineObliqueMercatorVariantA(
6249
0
            PropertyMap(), Angle(latitude_projection_centre, angUnit),
6250
0
            Angle(longitude_projection_centre, angUnit),
6251
0
            Angle(azimuth_initial_line, angUnit),
6252
0
            Angle(angle_from_rectified_to_skrew_grid, angUnit), Scale(scale),
6253
0
            Length(false_easting, linearUnit),
6254
0
            Length(false_northing, linearUnit));
6255
0
        return proj_create_conversion(ctx, conv);
6256
0
    } catch (const std::exception &e) {
6257
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6258
0
    }
6259
0
    return nullptr;
6260
0
}
6261
// ---------------------------------------------------------------------------
6262
6263
/** \brief Instantiate a ProjectedCRS with a conversion based on the Hotine
6264
 * Oblique Mercator (Variant B) projection method.
6265
 *
6266
 * See
6267
 * osgeo::proj::operation::Conversion::createHotineObliqueMercatorVariantB().
6268
 *
6269
 * Linear parameters are expressed in (linear_unit_name,
6270
 * linear_unit_conv_factor).
6271
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6272
 */
6273
PJ *proj_create_conversion_hotine_oblique_mercator_variant_b(
6274
    PJ_CONTEXT *ctx, double latitude_projection_centre,
6275
    double longitude_projection_centre, double azimuth_initial_line,
6276
    double angle_from_rectified_to_skrew_grid, double scale,
6277
    double easting_projection_centre, double northing_projection_centre,
6278
    const char *ang_unit_name, double ang_unit_conv_factor,
6279
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
6280
0
    SANITIZE_CTX(ctx);
6281
0
    try {
6282
0
        UnitOfMeasure linearUnit(
6283
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6284
0
        UnitOfMeasure angUnit(
6285
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6286
0
        auto conv = Conversion::createHotineObliqueMercatorVariantB(
6287
0
            PropertyMap(), Angle(latitude_projection_centre, angUnit),
6288
0
            Angle(longitude_projection_centre, angUnit),
6289
0
            Angle(azimuth_initial_line, angUnit),
6290
0
            Angle(angle_from_rectified_to_skrew_grid, angUnit), Scale(scale),
6291
0
            Length(easting_projection_centre, linearUnit),
6292
0
            Length(northing_projection_centre, linearUnit));
6293
0
        return proj_create_conversion(ctx, conv);
6294
0
    } catch (const std::exception &e) {
6295
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6296
0
    }
6297
0
    return nullptr;
6298
0
}
6299
// ---------------------------------------------------------------------------
6300
6301
/** \brief Instantiate a ProjectedCRS with a conversion based on the Hotine
6302
 * Oblique Mercator Two Point Natural Origin projection method.
6303
 *
6304
 * See
6305
 * osgeo::proj::operation::Conversion::createHotineObliqueMercatorTwoPointNaturalOrigin().
6306
 *
6307
 * Linear parameters are expressed in (linear_unit_name,
6308
 * linear_unit_conv_factor).
6309
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6310
 */
6311
PJ *proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
6312
    PJ_CONTEXT *ctx, double latitude_projection_centre, double latitude_point1,
6313
    double longitude_point1, double latitude_point2, double longitude_point2,
6314
    double scale, double easting_projection_centre,
6315
    double northing_projection_centre, const char *ang_unit_name,
6316
    double ang_unit_conv_factor, const char *linear_unit_name,
6317
0
    double linear_unit_conv_factor) {
6318
0
    SANITIZE_CTX(ctx);
6319
0
    try {
6320
0
        UnitOfMeasure linearUnit(
6321
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6322
0
        UnitOfMeasure angUnit(
6323
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6324
0
        auto conv =
6325
0
            Conversion::createHotineObliqueMercatorTwoPointNaturalOrigin(
6326
0
                PropertyMap(), Angle(latitude_projection_centre, angUnit),
6327
0
                Angle(latitude_point1, angUnit),
6328
0
                Angle(longitude_point1, angUnit),
6329
0
                Angle(latitude_point2, angUnit),
6330
0
                Angle(longitude_point2, angUnit), Scale(scale),
6331
0
                Length(easting_projection_centre, linearUnit),
6332
0
                Length(northing_projection_centre, linearUnit));
6333
0
        return proj_create_conversion(ctx, conv);
6334
0
    } catch (const std::exception &e) {
6335
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6336
0
    }
6337
0
    return nullptr;
6338
0
}
6339
// ---------------------------------------------------------------------------
6340
6341
/** \brief Instantiate a ProjectedCRS with a conversion based on the Laborde
6342
 * Oblique Mercator projection method.
6343
 *
6344
 * See
6345
 * osgeo::proj::operation::Conversion::createLabordeObliqueMercator().
6346
 *
6347
 * Linear parameters are expressed in (linear_unit_name,
6348
 * linear_unit_conv_factor).
6349
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6350
 */
6351
PJ *proj_create_conversion_laborde_oblique_mercator(
6352
    PJ_CONTEXT *ctx, double latitude_projection_centre,
6353
    double longitude_projection_centre, double azimuth_initial_line,
6354
    double scale, double false_easting, double false_northing,
6355
    const char *ang_unit_name, double ang_unit_conv_factor,
6356
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
6357
0
    SANITIZE_CTX(ctx);
6358
0
    try {
6359
0
        UnitOfMeasure linearUnit(
6360
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6361
0
        UnitOfMeasure angUnit(
6362
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6363
0
        auto conv = Conversion::createLabordeObliqueMercator(
6364
0
            PropertyMap(), Angle(latitude_projection_centre, angUnit),
6365
0
            Angle(longitude_projection_centre, angUnit),
6366
0
            Angle(azimuth_initial_line, angUnit), Scale(scale),
6367
0
            Length(false_easting, linearUnit),
6368
0
            Length(false_northing, linearUnit));
6369
0
        return proj_create_conversion(ctx, conv);
6370
0
    } catch (const std::exception &e) {
6371
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6372
0
    }
6373
0
    return nullptr;
6374
0
}
6375
// ---------------------------------------------------------------------------
6376
6377
/** \brief Instantiate a ProjectedCRS with a conversion based on the
6378
 * International Map of the World Polyconic projection method.
6379
 *
6380
 * See
6381
 * osgeo::proj::operation::Conversion::createInternationalMapWorldPolyconic().
6382
 *
6383
 * Linear parameters are expressed in (linear_unit_name,
6384
 * linear_unit_conv_factor).
6385
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6386
 */
6387
PJ *proj_create_conversion_international_map_world_polyconic(
6388
    PJ_CONTEXT *ctx, double center_long, double latitude_first_parallel,
6389
    double latitude_second_parallel, double false_easting,
6390
    double false_northing, const char *ang_unit_name,
6391
    double ang_unit_conv_factor, const char *linear_unit_name,
6392
0
    double linear_unit_conv_factor) {
6393
0
    SANITIZE_CTX(ctx);
6394
0
    try {
6395
0
        UnitOfMeasure linearUnit(
6396
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6397
0
        UnitOfMeasure angUnit(
6398
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6399
0
        auto conv = Conversion::createInternationalMapWorldPolyconic(
6400
0
            PropertyMap(), Angle(center_long, angUnit),
6401
0
            Angle(latitude_first_parallel, angUnit),
6402
0
            Angle(latitude_second_parallel, angUnit),
6403
0
            Length(false_easting, linearUnit),
6404
0
            Length(false_northing, linearUnit));
6405
0
        return proj_create_conversion(ctx, conv);
6406
0
    } catch (const std::exception &e) {
6407
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6408
0
    }
6409
0
    return nullptr;
6410
0
}
6411
// ---------------------------------------------------------------------------
6412
6413
/** \brief Instantiate a ProjectedCRS with a conversion based on the Krovak
6414
 * (north oriented) projection method.
6415
 *
6416
 * See osgeo::proj::operation::Conversion::createKrovakNorthOriented().
6417
 *
6418
 * Linear parameters are expressed in (linear_unit_name,
6419
 * linear_unit_conv_factor).
6420
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6421
 */
6422
PJ *proj_create_conversion_krovak_north_oriented(
6423
    PJ_CONTEXT *ctx, double latitude_projection_centre,
6424
    double longitude_of_origin, double colatitude_cone_axis,
6425
    double latitude_pseudo_standard_parallel,
6426
    double scale_factor_pseudo_standard_parallel, double false_easting,
6427
    double false_northing, const char *ang_unit_name,
6428
    double ang_unit_conv_factor, const char *linear_unit_name,
6429
0
    double linear_unit_conv_factor) {
6430
0
    SANITIZE_CTX(ctx);
6431
0
    try {
6432
0
        UnitOfMeasure linearUnit(
6433
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6434
0
        UnitOfMeasure angUnit(
6435
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6436
0
        auto conv = Conversion::createKrovakNorthOriented(
6437
0
            PropertyMap(), Angle(latitude_projection_centre, angUnit),
6438
0
            Angle(longitude_of_origin, angUnit),
6439
0
            Angle(colatitude_cone_axis, angUnit),
6440
0
            Angle(latitude_pseudo_standard_parallel, angUnit),
6441
0
            Scale(scale_factor_pseudo_standard_parallel),
6442
0
            Length(false_easting, linearUnit),
6443
0
            Length(false_northing, linearUnit));
6444
0
        return proj_create_conversion(ctx, conv);
6445
0
    } catch (const std::exception &e) {
6446
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6447
0
    }
6448
0
    return nullptr;
6449
0
}
6450
// ---------------------------------------------------------------------------
6451
6452
/** \brief Instantiate a ProjectedCRS with a conversion based on the Krovak
6453
 * projection method.
6454
 *
6455
 * See osgeo::proj::operation::Conversion::createKrovak().
6456
 *
6457
 * Linear parameters are expressed in (linear_unit_name,
6458
 * linear_unit_conv_factor).
6459
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6460
 */
6461
PJ *proj_create_conversion_krovak(
6462
    PJ_CONTEXT *ctx, double latitude_projection_centre,
6463
    double longitude_of_origin, double colatitude_cone_axis,
6464
    double latitude_pseudo_standard_parallel,
6465
    double scale_factor_pseudo_standard_parallel, double false_easting,
6466
    double false_northing, const char *ang_unit_name,
6467
    double ang_unit_conv_factor, const char *linear_unit_name,
6468
0
    double linear_unit_conv_factor) {
6469
0
    SANITIZE_CTX(ctx);
6470
0
    try {
6471
0
        UnitOfMeasure linearUnit(
6472
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6473
0
        UnitOfMeasure angUnit(
6474
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6475
0
        auto conv = Conversion::createKrovak(
6476
0
            PropertyMap(), Angle(latitude_projection_centre, angUnit),
6477
0
            Angle(longitude_of_origin, angUnit),
6478
0
            Angle(colatitude_cone_axis, angUnit),
6479
0
            Angle(latitude_pseudo_standard_parallel, angUnit),
6480
0
            Scale(scale_factor_pseudo_standard_parallel),
6481
0
            Length(false_easting, linearUnit),
6482
0
            Length(false_northing, linearUnit));
6483
0
        return proj_create_conversion(ctx, conv);
6484
0
    } catch (const std::exception &e) {
6485
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6486
0
    }
6487
0
    return nullptr;
6488
0
}
6489
// ---------------------------------------------------------------------------
6490
6491
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
6492
 * Azimuthal Equal Area projection method.
6493
 *
6494
 * See osgeo::proj::operation::Conversion::createLambertAzimuthalEqualArea().
6495
 *
6496
 * Linear parameters are expressed in (linear_unit_name,
6497
 * linear_unit_conv_factor).
6498
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6499
 */
6500
PJ *proj_create_conversion_lambert_azimuthal_equal_area(
6501
    PJ_CONTEXT *ctx, double latitude_nat_origin, double longitude_nat_origin,
6502
    double false_easting, double false_northing, const char *ang_unit_name,
6503
    double ang_unit_conv_factor, const char *linear_unit_name,
6504
0
    double linear_unit_conv_factor) {
6505
0
    SANITIZE_CTX(ctx);
6506
0
    try {
6507
0
        UnitOfMeasure linearUnit(
6508
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6509
0
        UnitOfMeasure angUnit(
6510
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6511
0
        auto conv = Conversion::createLambertAzimuthalEqualArea(
6512
0
            PropertyMap(), Angle(latitude_nat_origin, angUnit),
6513
0
            Angle(longitude_nat_origin, angUnit),
6514
0
            Length(false_easting, linearUnit),
6515
0
            Length(false_northing, linearUnit));
6516
0
        return proj_create_conversion(ctx, conv);
6517
0
    } catch (const std::exception &e) {
6518
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6519
0
    }
6520
0
    return nullptr;
6521
0
}
6522
// ---------------------------------------------------------------------------
6523
6524
/** \brief Instantiate a ProjectedCRS with a conversion based on the Miller
6525
 * Cylindrical projection method.
6526
 *
6527
 * See osgeo::proj::operation::Conversion::createMillerCylindrical().
6528
 *
6529
 * Linear parameters are expressed in (linear_unit_name,
6530
 * linear_unit_conv_factor).
6531
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6532
 */
6533
PJ *proj_create_conversion_miller_cylindrical(
6534
    PJ_CONTEXT *ctx, double center_long, double false_easting,
6535
    double false_northing, const char *ang_unit_name,
6536
    double ang_unit_conv_factor, const char *linear_unit_name,
6537
0
    double linear_unit_conv_factor) {
6538
0
    SANITIZE_CTX(ctx);
6539
0
    try {
6540
0
        UnitOfMeasure linearUnit(
6541
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6542
0
        UnitOfMeasure angUnit(
6543
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6544
0
        auto conv = Conversion::createMillerCylindrical(
6545
0
            PropertyMap(), Angle(center_long, angUnit),
6546
0
            Length(false_easting, linearUnit),
6547
0
            Length(false_northing, linearUnit));
6548
0
        return proj_create_conversion(ctx, conv);
6549
0
    } catch (const std::exception &e) {
6550
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6551
0
    }
6552
0
    return nullptr;
6553
0
}
6554
// ---------------------------------------------------------------------------
6555
6556
/** \brief Instantiate a ProjectedCRS with a conversion based on the Mercator
6557
 * projection method.
6558
 *
6559
 * See osgeo::proj::operation::Conversion::createMercatorVariantA().
6560
 *
6561
 * Linear parameters are expressed in (linear_unit_name,
6562
 * linear_unit_conv_factor).
6563
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6564
 */
6565
PJ *proj_create_conversion_mercator_variant_a(
6566
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
6567
    double false_easting, double false_northing, const char *ang_unit_name,
6568
    double ang_unit_conv_factor, const char *linear_unit_name,
6569
0
    double linear_unit_conv_factor) {
6570
0
    SANITIZE_CTX(ctx);
6571
0
    try {
6572
0
        UnitOfMeasure linearUnit(
6573
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6574
0
        UnitOfMeasure angUnit(
6575
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6576
0
        auto conv = Conversion::createMercatorVariantA(
6577
0
            PropertyMap(), Angle(center_lat, angUnit),
6578
0
            Angle(center_long, angUnit), Scale(scale),
6579
0
            Length(false_easting, linearUnit),
6580
0
            Length(false_northing, linearUnit));
6581
0
        return proj_create_conversion(ctx, conv);
6582
0
    } catch (const std::exception &e) {
6583
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6584
0
    }
6585
0
    return nullptr;
6586
0
}
6587
// ---------------------------------------------------------------------------
6588
6589
/** \brief Instantiate a ProjectedCRS with a conversion based on the Mercator
6590
 * projection method.
6591
 *
6592
 * See osgeo::proj::operation::Conversion::createMercatorVariantB().
6593
 *
6594
 * Linear parameters are expressed in (linear_unit_name,
6595
 * linear_unit_conv_factor).
6596
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6597
 */
6598
PJ *proj_create_conversion_mercator_variant_b(
6599
    PJ_CONTEXT *ctx, double latitude_first_parallel, double center_long,
6600
    double false_easting, double false_northing, const char *ang_unit_name,
6601
    double ang_unit_conv_factor, const char *linear_unit_name,
6602
0
    double linear_unit_conv_factor) {
6603
0
    SANITIZE_CTX(ctx);
6604
0
    try {
6605
0
        UnitOfMeasure linearUnit(
6606
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6607
0
        UnitOfMeasure angUnit(
6608
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6609
0
        auto conv = Conversion::createMercatorVariantB(
6610
0
            PropertyMap(), Angle(latitude_first_parallel, angUnit),
6611
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
6612
0
            Length(false_northing, linearUnit));
6613
0
        return proj_create_conversion(ctx, conv);
6614
0
    } catch (const std::exception &e) {
6615
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6616
0
    }
6617
0
    return nullptr;
6618
0
}
6619
// ---------------------------------------------------------------------------
6620
6621
/** \brief Instantiate a ProjectedCRS with a conversion based on the Popular
6622
 * Visualisation Pseudo Mercator projection method.
6623
 *
6624
 * See
6625
 * osgeo::proj::operation::Conversion::createPopularVisualisationPseudoMercator().
6626
 *
6627
 * Linear parameters are expressed in (linear_unit_name,
6628
 * linear_unit_conv_factor).
6629
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6630
 */
6631
PJ *proj_create_conversion_popular_visualisation_pseudo_mercator(
6632
    PJ_CONTEXT *ctx, double center_lat, double center_long,
6633
    double false_easting, double false_northing, const char *ang_unit_name,
6634
    double ang_unit_conv_factor, const char *linear_unit_name,
6635
0
    double linear_unit_conv_factor) {
6636
0
    SANITIZE_CTX(ctx);
6637
0
    try {
6638
0
        UnitOfMeasure linearUnit(
6639
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6640
0
        UnitOfMeasure angUnit(
6641
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6642
0
        auto conv = Conversion::createPopularVisualisationPseudoMercator(
6643
0
            PropertyMap(), Angle(center_lat, angUnit),
6644
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
6645
0
            Length(false_northing, linearUnit));
6646
0
        return proj_create_conversion(ctx, conv);
6647
0
    } catch (const std::exception &e) {
6648
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6649
0
    }
6650
0
    return nullptr;
6651
0
}
6652
// ---------------------------------------------------------------------------
6653
6654
/** \brief Instantiate a ProjectedCRS with a conversion based on the Mollweide
6655
 * projection method.
6656
 *
6657
 * See osgeo::proj::operation::Conversion::createMollweide().
6658
 *
6659
 * Linear parameters are expressed in (linear_unit_name,
6660
 * linear_unit_conv_factor).
6661
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6662
 */
6663
PJ *proj_create_conversion_mollweide(PJ_CONTEXT *ctx, double center_long,
6664
                                     double false_easting,
6665
                                     double false_northing,
6666
                                     const char *ang_unit_name,
6667
                                     double ang_unit_conv_factor,
6668
                                     const char *linear_unit_name,
6669
0
                                     double linear_unit_conv_factor) {
6670
0
    SANITIZE_CTX(ctx);
6671
0
    try {
6672
0
        UnitOfMeasure linearUnit(
6673
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6674
0
        UnitOfMeasure angUnit(
6675
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6676
0
        auto conv = Conversion::createMollweide(
6677
0
            PropertyMap(), Angle(center_long, angUnit),
6678
0
            Length(false_easting, linearUnit),
6679
0
            Length(false_northing, linearUnit));
6680
0
        return proj_create_conversion(ctx, conv);
6681
0
    } catch (const std::exception &e) {
6682
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6683
0
    }
6684
0
    return nullptr;
6685
0
}
6686
// ---------------------------------------------------------------------------
6687
6688
/** \brief Instantiate a ProjectedCRS with a conversion based on the New Zealand
6689
 * Map Grid projection method.
6690
 *
6691
 * See osgeo::proj::operation::Conversion::createNewZealandMappingGrid().
6692
 *
6693
 * Linear parameters are expressed in (linear_unit_name,
6694
 * linear_unit_conv_factor).
6695
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6696
 */
6697
PJ *proj_create_conversion_new_zealand_mapping_grid(
6698
    PJ_CONTEXT *ctx, double center_lat, double center_long,
6699
    double false_easting, double false_northing, const char *ang_unit_name,
6700
    double ang_unit_conv_factor, const char *linear_unit_name,
6701
0
    double linear_unit_conv_factor) {
6702
0
    SANITIZE_CTX(ctx);
6703
0
    try {
6704
0
        UnitOfMeasure linearUnit(
6705
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6706
0
        UnitOfMeasure angUnit(
6707
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6708
0
        auto conv = Conversion::createNewZealandMappingGrid(
6709
0
            PropertyMap(), Angle(center_lat, angUnit),
6710
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
6711
0
            Length(false_northing, linearUnit));
6712
0
        return proj_create_conversion(ctx, conv);
6713
0
    } catch (const std::exception &e) {
6714
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6715
0
    }
6716
0
    return nullptr;
6717
0
}
6718
// ---------------------------------------------------------------------------
6719
6720
/** \brief Instantiate a ProjectedCRS with a conversion based on the Oblique
6721
 * Stereographic (Alternative) projection method.
6722
 *
6723
 * See osgeo::proj::operation::Conversion::createObliqueStereographic().
6724
 *
6725
 * Linear parameters are expressed in (linear_unit_name,
6726
 * linear_unit_conv_factor).
6727
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6728
 */
6729
PJ *proj_create_conversion_oblique_stereographic(
6730
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
6731
    double false_easting, double false_northing, const char *ang_unit_name,
6732
    double ang_unit_conv_factor, const char *linear_unit_name,
6733
0
    double linear_unit_conv_factor) {
6734
0
    SANITIZE_CTX(ctx);
6735
0
    try {
6736
0
        UnitOfMeasure linearUnit(
6737
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6738
0
        UnitOfMeasure angUnit(
6739
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6740
0
        auto conv = Conversion::createObliqueStereographic(
6741
0
            PropertyMap(), Angle(center_lat, angUnit),
6742
0
            Angle(center_long, angUnit), Scale(scale),
6743
0
            Length(false_easting, linearUnit),
6744
0
            Length(false_northing, linearUnit));
6745
0
        return proj_create_conversion(ctx, conv);
6746
0
    } catch (const std::exception &e) {
6747
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6748
0
    }
6749
0
    return nullptr;
6750
0
}
6751
// ---------------------------------------------------------------------------
6752
6753
/** \brief Instantiate a ProjectedCRS with a conversion based on the
6754
 * Orthographic projection method.
6755
 *
6756
 * See osgeo::proj::operation::Conversion::createOrthographic().
6757
 *
6758
 * Linear parameters are expressed in (linear_unit_name,
6759
 * linear_unit_conv_factor).
6760
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6761
 */
6762
PJ *proj_create_conversion_orthographic(
6763
    PJ_CONTEXT *ctx, double center_lat, double center_long,
6764
    double false_easting, double false_northing, const char *ang_unit_name,
6765
    double ang_unit_conv_factor, const char *linear_unit_name,
6766
0
    double linear_unit_conv_factor) {
6767
0
    SANITIZE_CTX(ctx);
6768
0
    try {
6769
0
        UnitOfMeasure linearUnit(
6770
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6771
0
        UnitOfMeasure angUnit(
6772
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6773
0
        auto conv = Conversion::createOrthographic(
6774
0
            PropertyMap(), Angle(center_lat, angUnit),
6775
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
6776
0
            Length(false_northing, linearUnit));
6777
0
        return proj_create_conversion(ctx, conv);
6778
0
    } catch (const std::exception &e) {
6779
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6780
0
    }
6781
0
    return nullptr;
6782
0
}
6783
// ---------------------------------------------------------------------------
6784
6785
/** \brief Instantiate a ProjectedCRS with a conversion based on the Local
6786
 * Orthographic projection method.
6787
 *
6788
 * See osgeo::proj::operation::Conversion::createLocalOrthographic().
6789
 *
6790
 * Linear parameters are expressed in (linear_unit_name,
6791
 * linear_unit_conv_factor).
6792
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6793
 */
6794
PJ *proj_create_conversion_local_orthographic(
6795
    PJ_CONTEXT *ctx, double center_lat, double center_long, double azimuth,
6796
    double scale, double false_easting, double false_northing,
6797
    const char *ang_unit_name, double ang_unit_conv_factor,
6798
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
6799
0
    SANITIZE_CTX(ctx);
6800
0
    try {
6801
0
        UnitOfMeasure linearUnit(
6802
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6803
0
        UnitOfMeasure angUnit(
6804
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6805
0
        auto conv = Conversion::createLocalOrthographic(
6806
0
            PropertyMap(), Angle(center_lat, angUnit),
6807
0
            Angle(center_long, angUnit), Angle(azimuth, angUnit), Scale(scale),
6808
0
            Length(false_easting, linearUnit),
6809
0
            Length(false_northing, linearUnit));
6810
0
        return proj_create_conversion(ctx, conv);
6811
0
    } catch (const std::exception &e) {
6812
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6813
0
    }
6814
0
    return nullptr;
6815
0
}
6816
// ---------------------------------------------------------------------------
6817
6818
/** \brief Instantiate a ProjectedCRS with a conversion based on the American
6819
 * Polyconic projection method.
6820
 *
6821
 * See osgeo::proj::operation::Conversion::createAmericanPolyconic().
6822
 *
6823
 * Linear parameters are expressed in (linear_unit_name,
6824
 * linear_unit_conv_factor).
6825
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6826
 */
6827
PJ *proj_create_conversion_american_polyconic(
6828
    PJ_CONTEXT *ctx, double center_lat, double center_long,
6829
    double false_easting, double false_northing, const char *ang_unit_name,
6830
    double ang_unit_conv_factor, const char *linear_unit_name,
6831
0
    double linear_unit_conv_factor) {
6832
0
    SANITIZE_CTX(ctx);
6833
0
    try {
6834
0
        UnitOfMeasure linearUnit(
6835
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6836
0
        UnitOfMeasure angUnit(
6837
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6838
0
        auto conv = Conversion::createAmericanPolyconic(
6839
0
            PropertyMap(), Angle(center_lat, angUnit),
6840
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
6841
0
            Length(false_northing, linearUnit));
6842
0
        return proj_create_conversion(ctx, conv);
6843
0
    } catch (const std::exception &e) {
6844
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6845
0
    }
6846
0
    return nullptr;
6847
0
}
6848
// ---------------------------------------------------------------------------
6849
6850
/** \brief Instantiate a ProjectedCRS with a conversion based on the Polar
6851
 * Stereographic (Variant A) projection method.
6852
 *
6853
 * See osgeo::proj::operation::Conversion::createPolarStereographicVariantA().
6854
 *
6855
 * Linear parameters are expressed in (linear_unit_name,
6856
 * linear_unit_conv_factor).
6857
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6858
 */
6859
PJ *proj_create_conversion_polar_stereographic_variant_a(
6860
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
6861
    double false_easting, double false_northing, const char *ang_unit_name,
6862
    double ang_unit_conv_factor, const char *linear_unit_name,
6863
0
    double linear_unit_conv_factor) {
6864
0
    SANITIZE_CTX(ctx);
6865
0
    try {
6866
0
        UnitOfMeasure linearUnit(
6867
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6868
0
        UnitOfMeasure angUnit(
6869
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6870
0
        auto conv = Conversion::createPolarStereographicVariantA(
6871
0
            PropertyMap(), Angle(center_lat, angUnit),
6872
0
            Angle(center_long, angUnit), Scale(scale),
6873
0
            Length(false_easting, linearUnit),
6874
0
            Length(false_northing, linearUnit));
6875
0
        return proj_create_conversion(ctx, conv);
6876
0
    } catch (const std::exception &e) {
6877
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6878
0
    }
6879
0
    return nullptr;
6880
0
}
6881
// ---------------------------------------------------------------------------
6882
6883
/** \brief Instantiate a ProjectedCRS with a conversion based on the Polar
6884
 * Stereographic (Variant B) projection method.
6885
 *
6886
 * See osgeo::proj::operation::Conversion::createPolarStereographicVariantB().
6887
 *
6888
 * Linear parameters are expressed in (linear_unit_name,
6889
 * linear_unit_conv_factor).
6890
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6891
 */
6892
PJ *proj_create_conversion_polar_stereographic_variant_b(
6893
    PJ_CONTEXT *ctx, double latitude_standard_parallel,
6894
    double longitude_of_origin, double false_easting, double false_northing,
6895
    const char *ang_unit_name, double ang_unit_conv_factor,
6896
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
6897
0
    SANITIZE_CTX(ctx);
6898
0
    try {
6899
0
        UnitOfMeasure linearUnit(
6900
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6901
0
        UnitOfMeasure angUnit(
6902
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6903
0
        auto conv = Conversion::createPolarStereographicVariantB(
6904
0
            PropertyMap(), Angle(latitude_standard_parallel, angUnit),
6905
0
            Angle(longitude_of_origin, angUnit),
6906
0
            Length(false_easting, linearUnit),
6907
0
            Length(false_northing, linearUnit));
6908
0
        return proj_create_conversion(ctx, conv);
6909
0
    } catch (const std::exception &e) {
6910
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6911
0
    }
6912
0
    return nullptr;
6913
0
}
6914
// ---------------------------------------------------------------------------
6915
6916
/** \brief Instantiate a ProjectedCRS with a conversion based on the Robinson
6917
 * projection method.
6918
 *
6919
 * See osgeo::proj::operation::Conversion::createRobinson().
6920
 *
6921
 * Linear parameters are expressed in (linear_unit_name,
6922
 * linear_unit_conv_factor).
6923
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6924
 */
6925
PJ *proj_create_conversion_robinson(PJ_CONTEXT *ctx, double center_long,
6926
                                    double false_easting, double false_northing,
6927
                                    const char *ang_unit_name,
6928
                                    double ang_unit_conv_factor,
6929
                                    const char *linear_unit_name,
6930
0
                                    double linear_unit_conv_factor) {
6931
0
    SANITIZE_CTX(ctx);
6932
0
    try {
6933
0
        UnitOfMeasure linearUnit(
6934
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6935
0
        UnitOfMeasure angUnit(
6936
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6937
0
        auto conv = Conversion::createRobinson(
6938
0
            PropertyMap(), Angle(center_long, angUnit),
6939
0
            Length(false_easting, linearUnit),
6940
0
            Length(false_northing, linearUnit));
6941
0
        return proj_create_conversion(ctx, conv);
6942
0
    } catch (const std::exception &e) {
6943
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6944
0
    }
6945
0
    return nullptr;
6946
0
}
6947
// ---------------------------------------------------------------------------
6948
6949
/** \brief Instantiate a ProjectedCRS with a conversion based on the Sinusoidal
6950
 * projection method.
6951
 *
6952
 * See osgeo::proj::operation::Conversion::createSinusoidal().
6953
 *
6954
 * Linear parameters are expressed in (linear_unit_name,
6955
 * linear_unit_conv_factor).
6956
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6957
 */
6958
PJ *proj_create_conversion_sinusoidal(PJ_CONTEXT *ctx, double center_long,
6959
                                      double false_easting,
6960
                                      double false_northing,
6961
                                      const char *ang_unit_name,
6962
                                      double ang_unit_conv_factor,
6963
                                      const char *linear_unit_name,
6964
0
                                      double linear_unit_conv_factor) {
6965
0
    SANITIZE_CTX(ctx);
6966
0
    try {
6967
0
        UnitOfMeasure linearUnit(
6968
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6969
0
        UnitOfMeasure angUnit(
6970
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6971
0
        auto conv = Conversion::createSinusoidal(
6972
0
            PropertyMap(), Angle(center_long, angUnit),
6973
0
            Length(false_easting, linearUnit),
6974
0
            Length(false_northing, linearUnit));
6975
0
        return proj_create_conversion(ctx, conv);
6976
0
    } catch (const std::exception &e) {
6977
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6978
0
    }
6979
0
    return nullptr;
6980
0
}
6981
// ---------------------------------------------------------------------------
6982
6983
/** \brief Instantiate a ProjectedCRS with a conversion based on the
6984
 * Stereographic projection method.
6985
 *
6986
 * See osgeo::proj::operation::Conversion::createStereographic().
6987
 *
6988
 * Linear parameters are expressed in (linear_unit_name,
6989
 * linear_unit_conv_factor).
6990
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6991
 */
6992
PJ *proj_create_conversion_stereographic(
6993
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
6994
    double false_easting, double false_northing, const char *ang_unit_name,
6995
    double ang_unit_conv_factor, const char *linear_unit_name,
6996
0
    double linear_unit_conv_factor) {
6997
0
    SANITIZE_CTX(ctx);
6998
0
    try {
6999
0
        UnitOfMeasure linearUnit(
7000
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7001
0
        UnitOfMeasure angUnit(
7002
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7003
0
        auto conv = Conversion::createStereographic(
7004
0
            PropertyMap(), Angle(center_lat, angUnit),
7005
0
            Angle(center_long, angUnit), Scale(scale),
7006
0
            Length(false_easting, linearUnit),
7007
0
            Length(false_northing, linearUnit));
7008
0
        return proj_create_conversion(ctx, conv);
7009
0
    } catch (const std::exception &e) {
7010
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7011
0
    }
7012
0
    return nullptr;
7013
0
}
7014
// ---------------------------------------------------------------------------
7015
7016
/** \brief Instantiate a ProjectedCRS with a conversion based on the Van der
7017
 * Grinten projection method.
7018
 *
7019
 * See osgeo::proj::operation::Conversion::createVanDerGrinten().
7020
 *
7021
 * Linear parameters are expressed in (linear_unit_name,
7022
 * linear_unit_conv_factor).
7023
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7024
 */
7025
PJ *proj_create_conversion_van_der_grinten(PJ_CONTEXT *ctx, double center_long,
7026
                                           double false_easting,
7027
                                           double false_northing,
7028
                                           const char *ang_unit_name,
7029
                                           double ang_unit_conv_factor,
7030
                                           const char *linear_unit_name,
7031
0
                                           double linear_unit_conv_factor) {
7032
0
    SANITIZE_CTX(ctx);
7033
0
    try {
7034
0
        UnitOfMeasure linearUnit(
7035
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7036
0
        UnitOfMeasure angUnit(
7037
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7038
0
        auto conv = Conversion::createVanDerGrinten(
7039
0
            PropertyMap(), Angle(center_long, angUnit),
7040
0
            Length(false_easting, linearUnit),
7041
0
            Length(false_northing, linearUnit));
7042
0
        return proj_create_conversion(ctx, conv);
7043
0
    } catch (const std::exception &e) {
7044
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7045
0
    }
7046
0
    return nullptr;
7047
0
}
7048
// ---------------------------------------------------------------------------
7049
7050
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner I
7051
 * projection method.
7052
 *
7053
 * See osgeo::proj::operation::Conversion::createWagnerI().
7054
 *
7055
 * Linear parameters are expressed in (linear_unit_name,
7056
 * linear_unit_conv_factor).
7057
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7058
 */
7059
PJ *proj_create_conversion_wagner_i(PJ_CONTEXT *ctx, double center_long,
7060
                                    double false_easting, double false_northing,
7061
                                    const char *ang_unit_name,
7062
                                    double ang_unit_conv_factor,
7063
                                    const char *linear_unit_name,
7064
0
                                    double linear_unit_conv_factor) {
7065
0
    SANITIZE_CTX(ctx);
7066
0
    try {
7067
0
        UnitOfMeasure linearUnit(
7068
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7069
0
        UnitOfMeasure angUnit(
7070
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7071
0
        auto conv = Conversion::createWagnerI(
7072
0
            PropertyMap(), Angle(center_long, angUnit),
7073
0
            Length(false_easting, linearUnit),
7074
0
            Length(false_northing, linearUnit));
7075
0
        return proj_create_conversion(ctx, conv);
7076
0
    } catch (const std::exception &e) {
7077
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7078
0
    }
7079
0
    return nullptr;
7080
0
}
7081
// ---------------------------------------------------------------------------
7082
7083
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner II
7084
 * projection method.
7085
 *
7086
 * See osgeo::proj::operation::Conversion::createWagnerII().
7087
 *
7088
 * Linear parameters are expressed in (linear_unit_name,
7089
 * linear_unit_conv_factor).
7090
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7091
 */
7092
PJ *proj_create_conversion_wagner_ii(PJ_CONTEXT *ctx, double center_long,
7093
                                     double false_easting,
7094
                                     double false_northing,
7095
                                     const char *ang_unit_name,
7096
                                     double ang_unit_conv_factor,
7097
                                     const char *linear_unit_name,
7098
0
                                     double linear_unit_conv_factor) {
7099
0
    SANITIZE_CTX(ctx);
7100
0
    try {
7101
0
        UnitOfMeasure linearUnit(
7102
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7103
0
        UnitOfMeasure angUnit(
7104
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7105
0
        auto conv = Conversion::createWagnerII(
7106
0
            PropertyMap(), Angle(center_long, angUnit),
7107
0
            Length(false_easting, linearUnit),
7108
0
            Length(false_northing, linearUnit));
7109
0
        return proj_create_conversion(ctx, conv);
7110
0
    } catch (const std::exception &e) {
7111
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7112
0
    }
7113
0
    return nullptr;
7114
0
}
7115
// ---------------------------------------------------------------------------
7116
7117
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner III
7118
 * projection method.
7119
 *
7120
 * See osgeo::proj::operation::Conversion::createWagnerIII().
7121
 *
7122
 * Linear parameters are expressed in (linear_unit_name,
7123
 * linear_unit_conv_factor).
7124
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7125
 */
7126
PJ *proj_create_conversion_wagner_iii(
7127
    PJ_CONTEXT *ctx, double latitude_true_scale, double center_long,
7128
    double false_easting, double false_northing, const char *ang_unit_name,
7129
    double ang_unit_conv_factor, const char *linear_unit_name,
7130
0
    double linear_unit_conv_factor) {
7131
0
    SANITIZE_CTX(ctx);
7132
0
    try {
7133
0
        UnitOfMeasure linearUnit(
7134
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7135
0
        UnitOfMeasure angUnit(
7136
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7137
0
        auto conv = Conversion::createWagnerIII(
7138
0
            PropertyMap(), Angle(latitude_true_scale, angUnit),
7139
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
7140
0
            Length(false_northing, linearUnit));
7141
0
        return proj_create_conversion(ctx, conv);
7142
0
    } catch (const std::exception &e) {
7143
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7144
0
    }
7145
0
    return nullptr;
7146
0
}
7147
// ---------------------------------------------------------------------------
7148
7149
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner IV
7150
 * projection method.
7151
 *
7152
 * See osgeo::proj::operation::Conversion::createWagnerIV().
7153
 *
7154
 * Linear parameters are expressed in (linear_unit_name,
7155
 * linear_unit_conv_factor).
7156
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7157
 */
7158
PJ *proj_create_conversion_wagner_iv(PJ_CONTEXT *ctx, double center_long,
7159
                                     double false_easting,
7160
                                     double false_northing,
7161
                                     const char *ang_unit_name,
7162
                                     double ang_unit_conv_factor,
7163
                                     const char *linear_unit_name,
7164
0
                                     double linear_unit_conv_factor) {
7165
0
    SANITIZE_CTX(ctx);
7166
0
    try {
7167
0
        UnitOfMeasure linearUnit(
7168
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7169
0
        UnitOfMeasure angUnit(
7170
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7171
0
        auto conv = Conversion::createWagnerIV(
7172
0
            PropertyMap(), Angle(center_long, angUnit),
7173
0
            Length(false_easting, linearUnit),
7174
0
            Length(false_northing, linearUnit));
7175
0
        return proj_create_conversion(ctx, conv);
7176
0
    } catch (const std::exception &e) {
7177
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7178
0
    }
7179
0
    return nullptr;
7180
0
}
7181
// ---------------------------------------------------------------------------
7182
7183
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner V
7184
 * projection method.
7185
 *
7186
 * See osgeo::proj::operation::Conversion::createWagnerV().
7187
 *
7188
 * Linear parameters are expressed in (linear_unit_name,
7189
 * linear_unit_conv_factor).
7190
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7191
 */
7192
PJ *proj_create_conversion_wagner_v(PJ_CONTEXT *ctx, double center_long,
7193
                                    double false_easting, double false_northing,
7194
                                    const char *ang_unit_name,
7195
                                    double ang_unit_conv_factor,
7196
                                    const char *linear_unit_name,
7197
0
                                    double linear_unit_conv_factor) {
7198
0
    SANITIZE_CTX(ctx);
7199
0
    try {
7200
0
        UnitOfMeasure linearUnit(
7201
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7202
0
        UnitOfMeasure angUnit(
7203
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7204
0
        auto conv = Conversion::createWagnerV(
7205
0
            PropertyMap(), Angle(center_long, angUnit),
7206
0
            Length(false_easting, linearUnit),
7207
0
            Length(false_northing, linearUnit));
7208
0
        return proj_create_conversion(ctx, conv);
7209
0
    } catch (const std::exception &e) {
7210
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7211
0
    }
7212
0
    return nullptr;
7213
0
}
7214
// ---------------------------------------------------------------------------
7215
7216
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner VI
7217
 * projection method.
7218
 *
7219
 * See osgeo::proj::operation::Conversion::createWagnerVI().
7220
 *
7221
 * Linear parameters are expressed in (linear_unit_name,
7222
 * linear_unit_conv_factor).
7223
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7224
 */
7225
PJ *proj_create_conversion_wagner_vi(PJ_CONTEXT *ctx, double center_long,
7226
                                     double false_easting,
7227
                                     double false_northing,
7228
                                     const char *ang_unit_name,
7229
                                     double ang_unit_conv_factor,
7230
                                     const char *linear_unit_name,
7231
0
                                     double linear_unit_conv_factor) {
7232
0
    SANITIZE_CTX(ctx);
7233
0
    try {
7234
0
        UnitOfMeasure linearUnit(
7235
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7236
0
        UnitOfMeasure angUnit(
7237
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7238
0
        auto conv = Conversion::createWagnerVI(
7239
0
            PropertyMap(), Angle(center_long, angUnit),
7240
0
            Length(false_easting, linearUnit),
7241
0
            Length(false_northing, linearUnit));
7242
0
        return proj_create_conversion(ctx, conv);
7243
0
    } catch (const std::exception &e) {
7244
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7245
0
    }
7246
0
    return nullptr;
7247
0
}
7248
// ---------------------------------------------------------------------------
7249
7250
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner VII
7251
 * projection method.
7252
 *
7253
 * See osgeo::proj::operation::Conversion::createWagnerVII().
7254
 *
7255
 * Linear parameters are expressed in (linear_unit_name,
7256
 * linear_unit_conv_factor).
7257
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7258
 */
7259
PJ *proj_create_conversion_wagner_vii(PJ_CONTEXT *ctx, double center_long,
7260
                                      double false_easting,
7261
                                      double false_northing,
7262
                                      const char *ang_unit_name,
7263
                                      double ang_unit_conv_factor,
7264
                                      const char *linear_unit_name,
7265
0
                                      double linear_unit_conv_factor) {
7266
0
    SANITIZE_CTX(ctx);
7267
0
    try {
7268
0
        UnitOfMeasure linearUnit(
7269
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7270
0
        UnitOfMeasure angUnit(
7271
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7272
0
        auto conv = Conversion::createWagnerVII(
7273
0
            PropertyMap(), Angle(center_long, angUnit),
7274
0
            Length(false_easting, linearUnit),
7275
0
            Length(false_northing, linearUnit));
7276
0
        return proj_create_conversion(ctx, conv);
7277
0
    } catch (const std::exception &e) {
7278
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7279
0
    }
7280
0
    return nullptr;
7281
0
}
7282
// ---------------------------------------------------------------------------
7283
7284
/** \brief Instantiate a ProjectedCRS with a conversion based on the
7285
 * Quadrilateralized Spherical Cube projection method.
7286
 *
7287
 * See
7288
 * osgeo::proj::operation::Conversion::createQuadrilateralizedSphericalCube().
7289
 *
7290
 * Linear parameters are expressed in (linear_unit_name,
7291
 * linear_unit_conv_factor).
7292
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7293
 */
7294
PJ *proj_create_conversion_quadrilateralized_spherical_cube(
7295
    PJ_CONTEXT *ctx, double center_lat, double center_long,
7296
    double false_easting, double false_northing, const char *ang_unit_name,
7297
    double ang_unit_conv_factor, const char *linear_unit_name,
7298
0
    double linear_unit_conv_factor) {
7299
0
    SANITIZE_CTX(ctx);
7300
0
    try {
7301
0
        UnitOfMeasure linearUnit(
7302
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7303
0
        UnitOfMeasure angUnit(
7304
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7305
0
        auto conv = Conversion::createQuadrilateralizedSphericalCube(
7306
0
            PropertyMap(), Angle(center_lat, angUnit),
7307
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
7308
0
            Length(false_northing, linearUnit));
7309
0
        return proj_create_conversion(ctx, conv);
7310
0
    } catch (const std::exception &e) {
7311
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7312
0
    }
7313
0
    return nullptr;
7314
0
}
7315
// ---------------------------------------------------------------------------
7316
7317
/** \brief Instantiate a ProjectedCRS with a conversion based on the Spherical
7318
 * Cross-Track Height projection method.
7319
 *
7320
 * See osgeo::proj::operation::Conversion::createSphericalCrossTrackHeight().
7321
 *
7322
 * Linear parameters are expressed in (linear_unit_name,
7323
 * linear_unit_conv_factor).
7324
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7325
 */
7326
PJ *proj_create_conversion_spherical_cross_track_height(
7327
    PJ_CONTEXT *ctx, double peg_point_lat, double peg_point_long,
7328
    double peg_point_heading, double peg_point_height,
7329
    const char *ang_unit_name, double ang_unit_conv_factor,
7330
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
7331
0
    SANITIZE_CTX(ctx);
7332
0
    try {
7333
0
        UnitOfMeasure linearUnit(
7334
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7335
0
        UnitOfMeasure angUnit(
7336
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7337
0
        auto conv = Conversion::createSphericalCrossTrackHeight(
7338
0
            PropertyMap(), Angle(peg_point_lat, angUnit),
7339
0
            Angle(peg_point_long, angUnit), Angle(peg_point_heading, angUnit),
7340
0
            Length(peg_point_height, linearUnit));
7341
0
        return proj_create_conversion(ctx, conv);
7342
0
    } catch (const std::exception &e) {
7343
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7344
0
    }
7345
0
    return nullptr;
7346
0
}
7347
// ---------------------------------------------------------------------------
7348
7349
/** \brief Instantiate a ProjectedCRS with a conversion based on the Equal Earth
7350
 * projection method.
7351
 *
7352
 * See osgeo::proj::operation::Conversion::createEqualEarth().
7353
 *
7354
 * Linear parameters are expressed in (linear_unit_name,
7355
 * linear_unit_conv_factor).
7356
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7357
 */
7358
PJ *proj_create_conversion_equal_earth(PJ_CONTEXT *ctx, double center_long,
7359
                                       double false_easting,
7360
                                       double false_northing,
7361
                                       const char *ang_unit_name,
7362
                                       double ang_unit_conv_factor,
7363
                                       const char *linear_unit_name,
7364
0
                                       double linear_unit_conv_factor) {
7365
0
    SANITIZE_CTX(ctx);
7366
0
    try {
7367
0
        UnitOfMeasure linearUnit(
7368
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7369
0
        UnitOfMeasure angUnit(
7370
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7371
0
        auto conv = Conversion::createEqualEarth(
7372
0
            PropertyMap(), Angle(center_long, angUnit),
7373
0
            Length(false_easting, linearUnit),
7374
0
            Length(false_northing, linearUnit));
7375
0
        return proj_create_conversion(ctx, conv);
7376
0
    } catch (const std::exception &e) {
7377
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7378
0
    }
7379
0
    return nullptr;
7380
0
}
7381
7382
// ---------------------------------------------------------------------------
7383
7384
/** \brief Instantiate a conversion based on the Vertical Perspective projection
7385
 * method.
7386
 *
7387
 * See osgeo::proj::operation::Conversion::createVerticalPerspective().
7388
 *
7389
 * Linear parameters are expressed in (linear_unit_name,
7390
 * linear_unit_conv_factor).
7391
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7392
 *
7393
 * @since 6.3
7394
 */
7395
PJ *proj_create_conversion_vertical_perspective(
7396
    PJ_CONTEXT *ctx, double topo_origin_lat, double topo_origin_long,
7397
    double topo_origin_height, double view_point_height, double false_easting,
7398
    double false_northing, const char *ang_unit_name,
7399
    double ang_unit_conv_factor, const char *linear_unit_name,
7400
0
    double linear_unit_conv_factor) {
7401
0
    SANITIZE_CTX(ctx);
7402
0
    try {
7403
0
        UnitOfMeasure linearUnit(
7404
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7405
0
        UnitOfMeasure angUnit(
7406
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7407
0
        auto conv = Conversion::createVerticalPerspective(
7408
0
            PropertyMap(), Angle(topo_origin_lat, angUnit),
7409
0
            Angle(topo_origin_long, angUnit),
7410
0
            Length(topo_origin_height, linearUnit),
7411
0
            Length(view_point_height, linearUnit),
7412
0
            Length(false_easting, linearUnit),
7413
0
            Length(false_northing, linearUnit));
7414
0
        return proj_create_conversion(ctx, conv);
7415
0
    } catch (const std::exception &e) {
7416
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7417
0
    }
7418
0
    return nullptr;
7419
0
}
7420
7421
// ---------------------------------------------------------------------------
7422
7423
/** \brief Instantiate a conversion based on the Pole Rotation method, using the
7424
 * conventions of the GRIB 1 and GRIB 2 data formats.
7425
 *
7426
 * See osgeo::proj::operation::Conversion::createPoleRotationGRIBConvention().
7427
 *
7428
 * Linear parameters are expressed in (linear_unit_name,
7429
 * linear_unit_conv_factor).
7430
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7431
 */
7432
PJ *proj_create_conversion_pole_rotation_grib_convention(
7433
    PJ_CONTEXT *ctx, double south_pole_lat_in_unrotated_crs,
7434
    double south_pole_long_in_unrotated_crs, double axis_rotation,
7435
0
    const char *ang_unit_name, double ang_unit_conv_factor) {
7436
0
    SANITIZE_CTX(ctx);
7437
0
    try {
7438
0
        UnitOfMeasure angUnit(
7439
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7440
0
        auto conv = Conversion::createPoleRotationGRIBConvention(
7441
0
            PropertyMap(), Angle(south_pole_lat_in_unrotated_crs, angUnit),
7442
0
            Angle(south_pole_long_in_unrotated_crs, angUnit),
7443
0
            Angle(axis_rotation, angUnit));
7444
0
        return proj_create_conversion(ctx, conv);
7445
0
    } catch (const std::exception &e) {
7446
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7447
0
    }
7448
0
    return nullptr;
7449
0
}
7450
7451
// ---------------------------------------------------------------------------
7452
7453
/** \brief Instantiate a conversion based on the Pole Rotation method, using
7454
 * the conventions of the netCDF CF convention for the netCDF format.
7455
 *
7456
 * See
7457
 * osgeo::proj::operation::Conversion::createPoleRotationNetCDFCFConvention().
7458
 *
7459
 * Linear parameters are expressed in (linear_unit_name,
7460
 * linear_unit_conv_factor).
7461
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7462
 */
7463
PJ *proj_create_conversion_pole_rotation_netcdf_cf_convention(
7464
    PJ_CONTEXT *ctx, double grid_north_pole_latitude,
7465
    double grid_north_pole_longitude, double north_pole_grid_longitude,
7466
0
    const char *ang_unit_name, double ang_unit_conv_factor) {
7467
0
    SANITIZE_CTX(ctx);
7468
0
    try {
7469
0
        UnitOfMeasure angUnit(
7470
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7471
0
        auto conv = Conversion::createPoleRotationNetCDFCFConvention(
7472
0
            PropertyMap(), Angle(grid_north_pole_latitude, angUnit),
7473
0
            Angle(grid_north_pole_longitude, angUnit),
7474
0
            Angle(north_pole_grid_longitude, angUnit));
7475
0
        return proj_create_conversion(ctx, conv);
7476
0
    } catch (const std::exception &e) {
7477
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7478
0
    }
7479
0
    return nullptr;
7480
0
}
7481
7482
/* END: Generated by scripts/create_c_api_projections.py*/
7483
7484
// ---------------------------------------------------------------------------
7485
7486
/** \brief Return whether a coordinate operation can be instantiated as
7487
 * a PROJ pipeline, checking in particular that referenced grids are
7488
 * available.
7489
 *
7490
 * @param ctx PROJ context, or NULL for default context
7491
 * @param coordoperation Object of type CoordinateOperation or derived classes
7492
 * (must not be NULL)
7493
 * @return TRUE or FALSE.
7494
 */
7495
7496
int proj_coordoperation_is_instantiable(PJ_CONTEXT *ctx,
7497
0
                                        const PJ *coordoperation) {
7498
0
    SANITIZE_CTX(ctx);
7499
0
    if (!coordoperation) {
7500
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7501
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7502
0
        return false;
7503
0
    }
7504
0
    auto op = dynamic_cast<const CoordinateOperation *>(
7505
0
        coordoperation->iso_obj.get());
7506
0
    if (!op) {
7507
0
        proj_log_error(ctx, __FUNCTION__,
7508
0
                       "Object is not a CoordinateOperation");
7509
0
        return 0;
7510
0
    }
7511
0
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
7512
0
    try {
7513
0
        auto ret = op->isPROJInstantiable(
7514
0
                       dbContext, proj_context_is_network_enabled(ctx) != FALSE)
7515
0
                       ? 1
7516
0
                       : 0;
7517
0
        return ret;
7518
0
    } catch (const std::exception &) {
7519
0
        return 0;
7520
0
    }
7521
0
}
7522
7523
// ---------------------------------------------------------------------------
7524
7525
/** \brief Return whether a coordinate operation has a "ballpark"
7526
 * transformation,
7527
 * that is a very approximate one, due to lack of more accurate transformations.
7528
 *
7529
 * Typically a null geographic offset between two horizontal datum, or a
7530
 * null vertical offset (or limited to unit changes) between two vertical
7531
 * datum. Errors of several tens to one hundred meters might be expected,
7532
 * compared to more accurate transformations.
7533
 *
7534
 * @param ctx PROJ context, or NULL for default context
7535
 * @param coordoperation Object of type CoordinateOperation or derived classes
7536
 * (must not be NULL)
7537
 * @return TRUE or FALSE.
7538
 */
7539
7540
int proj_coordoperation_has_ballpark_transformation(PJ_CONTEXT *ctx,
7541
0
                                                    const PJ *coordoperation) {
7542
0
    SANITIZE_CTX(ctx);
7543
0
    if (!coordoperation) {
7544
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7545
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7546
0
        return false;
7547
0
    }
7548
0
    auto op = dynamic_cast<const CoordinateOperation *>(
7549
0
        coordoperation->iso_obj.get());
7550
0
    if (!op) {
7551
0
        proj_log_error(ctx, __FUNCTION__,
7552
0
                       "Object is not a CoordinateOperation");
7553
0
        return 0;
7554
0
    }
7555
0
    return op->hasBallparkTransformation();
7556
0
}
7557
7558
// ---------------------------------------------------------------------------
7559
7560
/** \brief Return whether a coordinate operation requires coordinate tuples
7561
 * to have a valid input time for the coordinate transformation to succeed.
7562
 * (this applies for the forward direction)
7563
 *
7564
 * Note: in the case of a time-dependent Helmert transformation, this function
7565
 * will return true, but when executing proj_trans(), execution will still
7566
 * succeed if the time information is missing, due to the transformation central
7567
 * epoch being used as a fallback.
7568
 *
7569
 * @param ctx PROJ context, or NULL for default context
7570
 * @param coordoperation Object of type CoordinateOperation or derived classes
7571
 * (must not be NULL)
7572
 * @return TRUE or FALSE.
7573
 * @since 9.5
7574
 */
7575
7576
int proj_coordoperation_requires_per_coordinate_input_time(
7577
0
    PJ_CONTEXT *ctx, const PJ *coordoperation) {
7578
0
    SANITIZE_CTX(ctx);
7579
0
    if (!coordoperation) {
7580
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7581
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7582
0
        return false;
7583
0
    }
7584
0
    auto op = dynamic_cast<const CoordinateOperation *>(
7585
0
        coordoperation->iso_obj.get());
7586
0
    if (!op) {
7587
0
        proj_log_error(ctx, __FUNCTION__,
7588
0
                       "Object is not a CoordinateOperation");
7589
0
        return false;
7590
0
    }
7591
0
    return op->requiresPerCoordinateInputTime();
7592
0
}
7593
7594
// ---------------------------------------------------------------------------
7595
7596
/** \brief Return the number of parameters of a SingleOperation
7597
 *
7598
 * @param ctx PROJ context, or NULL for default context
7599
 * @param coordoperation Object of type SingleOperation or derived classes
7600
 * (must not be NULL)
7601
 */
7602
7603
int proj_coordoperation_get_param_count(PJ_CONTEXT *ctx,
7604
0
                                        const PJ *coordoperation) {
7605
0
    SANITIZE_CTX(ctx);
7606
0
    if (!coordoperation) {
7607
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7608
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7609
0
        return false;
7610
0
    }
7611
0
    auto op =
7612
0
        dynamic_cast<const SingleOperation *>(coordoperation->iso_obj.get());
7613
0
    if (!op) {
7614
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleOperation");
7615
0
        return 0;
7616
0
    }
7617
0
    return static_cast<int>(op->parameterValues().size());
7618
0
}
7619
7620
// ---------------------------------------------------------------------------
7621
7622
/** \brief Return the index of a parameter of a SingleOperation
7623
 *
7624
 * @param ctx PROJ context, or NULL for default context
7625
 * @param coordoperation Object of type SingleOperation or derived classes
7626
 * (must not be NULL)
7627
 * @param name Parameter name. Must not be NULL
7628
 * @return index (>=0), or -1 in case of error.
7629
 */
7630
7631
int proj_coordoperation_get_param_index(PJ_CONTEXT *ctx,
7632
                                        const PJ *coordoperation,
7633
0
                                        const char *name) {
7634
0
    SANITIZE_CTX(ctx);
7635
0
    if (!coordoperation || !name) {
7636
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7637
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7638
0
        return -1;
7639
0
    }
7640
0
    auto op =
7641
0
        dynamic_cast<const SingleOperation *>(coordoperation->iso_obj.get());
7642
0
    if (!op) {
7643
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleOperation");
7644
0
        return -1;
7645
0
    }
7646
0
    int index = 0;
7647
0
    for (const auto &genParam : op->method()->parameters()) {
7648
0
        if (Identifier::isEquivalentName(genParam->nameStr().c_str(), name)) {
7649
0
            return index;
7650
0
        }
7651
0
        index++;
7652
0
    }
7653
0
    return -1;
7654
0
}
7655
7656
// ---------------------------------------------------------------------------
7657
7658
/** \brief Return a parameter of a SingleOperation
7659
 *
7660
 * @param ctx PROJ context, or NULL for default context
7661
 * @param coordoperation Object of type SingleOperation or derived classes
7662
 * (must not be NULL)
7663
 * @param index Parameter index.
7664
 * @param out_name Pointer to a string value to store the parameter name. or
7665
 * NULL
7666
 * @param out_auth_name Pointer to a string value to store the parameter
7667
 * authority name. or NULL
7668
 * @param out_code Pointer to a string value to store the parameter
7669
 * code. or NULL
7670
 * @param out_value Pointer to a double value to store the parameter
7671
 * value (if numeric). or NULL
7672
 * @param out_value_string Pointer to a string value to store the parameter
7673
 * value (if of type string). or NULL
7674
 * @param out_unit_conv_factor Pointer to a double value to store the parameter
7675
 * unit conversion factor. or NULL
7676
 * @param out_unit_name Pointer to a string value to store the parameter
7677
 * unit name. or NULL
7678
 * @param out_unit_auth_name Pointer to a string value to store the
7679
 * unit authority name. or NULL
7680
 * @param out_unit_code Pointer to a string value to store the
7681
 * unit code. or NULL
7682
 * @param out_unit_category Pointer to a string value to store the parameter
7683
 * name. or
7684
 * NULL. This value might be "unknown", "none", "linear", "linear_per_time",
7685
 * "angular", "angular_per_time", "scale", "scale_per_time", "time",
7686
 * "parametric" or "parametric_per_time"
7687
 * @return TRUE in case of success.
7688
 */
7689
7690
int proj_coordoperation_get_param(
7691
    PJ_CONTEXT *ctx, const PJ *coordoperation, int index, const char **out_name,
7692
    const char **out_auth_name, const char **out_code, double *out_value,
7693
    const char **out_value_string, double *out_unit_conv_factor,
7694
    const char **out_unit_name, const char **out_unit_auth_name,
7695
0
    const char **out_unit_code, const char **out_unit_category) {
7696
0
    SANITIZE_CTX(ctx);
7697
0
    if (!coordoperation) {
7698
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7699
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7700
0
        return false;
7701
0
    }
7702
0
    auto op =
7703
0
        dynamic_cast<const SingleOperation *>(coordoperation->iso_obj.get());
7704
0
    if (!op) {
7705
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleOperation");
7706
0
        return false;
7707
0
    }
7708
0
    const auto &parameters = op->method()->parameters();
7709
0
    const auto &values = op->parameterValues();
7710
0
    if (static_cast<size_t>(index) >= parameters.size() ||
7711
0
        static_cast<size_t>(index) >= values.size()) {
7712
0
        proj_log_error(ctx, __FUNCTION__, "Invalid index");
7713
0
        return false;
7714
0
    }
7715
7716
0
    const auto &param = parameters[index];
7717
0
    const auto &param_ids = param->identifiers();
7718
0
    if (out_name) {
7719
0
        *out_name = param->name()->description()->c_str();
7720
0
    }
7721
0
    if (out_auth_name) {
7722
0
        if (!param_ids.empty()) {
7723
0
            *out_auth_name = param_ids[0]->codeSpace()->c_str();
7724
0
        } else {
7725
0
            *out_auth_name = nullptr;
7726
0
        }
7727
0
    }
7728
0
    if (out_code) {
7729
0
        if (!param_ids.empty()) {
7730
0
            *out_code = param_ids[0]->code().c_str();
7731
0
        } else {
7732
0
            *out_code = nullptr;
7733
0
        }
7734
0
    }
7735
7736
0
    const auto &value = values[index];
7737
0
    ParameterValuePtr paramValue = nullptr;
7738
0
    auto opParamValue =
7739
0
        dynamic_cast<const OperationParameterValue *>(value.get());
7740
0
    if (opParamValue) {
7741
0
        paramValue = opParamValue->parameterValue().as_nullable();
7742
0
    }
7743
0
    if (out_value) {
7744
0
        *out_value = 0;
7745
0
        if (paramValue) {
7746
0
            if (paramValue->type() == ParameterValue::Type::MEASURE) {
7747
0
                *out_value = paramValue->value().value();
7748
0
            }
7749
0
        }
7750
0
    }
7751
0
    if (out_value_string) {
7752
0
        *out_value_string = nullptr;
7753
0
        if (paramValue) {
7754
0
            if (paramValue->type() == ParameterValue::Type::FILENAME) {
7755
0
                *out_value_string = paramValue->valueFile().c_str();
7756
0
            } else if (paramValue->type() == ParameterValue::Type::STRING) {
7757
0
                *out_value_string = paramValue->stringValue().c_str();
7758
0
            }
7759
0
        }
7760
0
    }
7761
0
    if (out_unit_conv_factor) {
7762
0
        *out_unit_conv_factor = 0;
7763
0
    }
7764
0
    if (out_unit_name) {
7765
0
        *out_unit_name = nullptr;
7766
0
    }
7767
0
    if (out_unit_auth_name) {
7768
0
        *out_unit_auth_name = nullptr;
7769
0
    }
7770
0
    if (out_unit_code) {
7771
0
        *out_unit_code = nullptr;
7772
0
    }
7773
0
    if (out_unit_category) {
7774
0
        *out_unit_category = nullptr;
7775
0
    }
7776
0
    if (paramValue) {
7777
0
        if (paramValue->type() == ParameterValue::Type::MEASURE) {
7778
0
            const auto &unit = paramValue->value().unit();
7779
0
            if (out_unit_conv_factor) {
7780
0
                *out_unit_conv_factor = unit.conversionToSI();
7781
0
            }
7782
0
            if (out_unit_name) {
7783
0
                *out_unit_name = unit.name().c_str();
7784
0
            }
7785
0
            if (out_unit_auth_name) {
7786
0
                *out_unit_auth_name = unit.codeSpace().c_str();
7787
0
            }
7788
0
            if (out_unit_code) {
7789
0
                *out_unit_code = unit.code().c_str();
7790
0
            }
7791
0
            if (out_unit_category) {
7792
0
                *out_unit_category =
7793
0
                    get_unit_category(unit.name(), unit.type());
7794
0
            }
7795
0
        }
7796
0
    }
7797
7798
0
    return true;
7799
0
}
7800
7801
// ---------------------------------------------------------------------------
7802
7803
/** \brief Return the parameters of a Helmert transformation as WKT1 TOWGS84
7804
 * values.
7805
 *
7806
 * @param ctx PROJ context, or NULL for default context
7807
 * @param coordoperation Object of type Transformation, that can be represented
7808
 * as a WKT1 TOWGS84 node (must not be NULL)
7809
 * @param out_values Pointer to an array of value_count double values.
7810
 * @param value_count Size of out_values array. The suggested size is 7 to get
7811
 * translation, rotation and scale difference parameters. Rotation and scale
7812
 * difference terms might be zero if the transformation only includes
7813
 * translation
7814
 * parameters. In that case, value_count could be set to 3.
7815
 * @param emit_error_if_incompatible Boolean to indicate if an error must be
7816
 * logged if coordoperation is not compatible with a WKT1 TOWGS84
7817
 * representation.
7818
 * @return TRUE in case of success, or FALSE if coordoperation is not
7819
 * compatible with a WKT1 TOWGS84 representation.
7820
 */
7821
7822
int proj_coordoperation_get_towgs84_values(PJ_CONTEXT *ctx,
7823
                                           const PJ *coordoperation,
7824
                                           double *out_values, int value_count,
7825
0
                                           int emit_error_if_incompatible) {
7826
0
    SANITIZE_CTX(ctx);
7827
0
    if (!coordoperation) {
7828
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7829
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7830
0
        return false;
7831
0
    }
7832
0
    auto transf =
7833
0
        dynamic_cast<const Transformation *>(coordoperation->iso_obj.get());
7834
0
    if (!transf) {
7835
0
        if (emit_error_if_incompatible) {
7836
0
            proj_log_error(ctx, __FUNCTION__, "Object is not a Transformation");
7837
0
        }
7838
0
        return FALSE;
7839
0
    }
7840
7841
0
    const auto values = transf->getTOWGS84Parameters(false);
7842
0
    if (!values.empty()) {
7843
0
        for (int i = 0;
7844
0
             i < value_count && static_cast<size_t>(i) < values.size(); i++) {
7845
0
            out_values[i] = values[i];
7846
0
        }
7847
0
        return TRUE;
7848
0
    } else {
7849
0
        if (emit_error_if_incompatible) {
7850
0
            proj_log_error(ctx, __FUNCTION__,
7851
0
                           "Transformation cannot be formatted as WKT1 TOWGS84 "
7852
0
                           "parameters");
7853
0
        }
7854
0
        return FALSE;
7855
0
    }
7856
0
}
7857
7858
// ---------------------------------------------------------------------------
7859
7860
/** \brief Return the number of grids used by a CoordinateOperation
7861
 *
7862
 * @param ctx PROJ context, or NULL for default context
7863
 * @param coordoperation Object of type CoordinateOperation or derived classes
7864
 * (must not be NULL)
7865
 */
7866
7867
int proj_coordoperation_get_grid_used_count(PJ_CONTEXT *ctx,
7868
2.28k
                                            const PJ *coordoperation) {
7869
2.28k
    SANITIZE_CTX(ctx);
7870
2.28k
    if (!coordoperation) {
7871
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7872
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7873
0
        return false;
7874
0
    }
7875
2.28k
    auto co = dynamic_cast<const CoordinateOperation *>(
7876
2.28k
        coordoperation->iso_obj.get());
7877
2.28k
    if (!co) {
7878
0
        proj_log_error(ctx, __FUNCTION__,
7879
0
                       "Object is not a CoordinateOperation");
7880
0
        return 0;
7881
0
    }
7882
2.28k
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
7883
2.28k
    try {
7884
2.28k
        if (!coordoperation->gridsNeededAsked) {
7885
2.28k
            coordoperation->gridsNeededAsked = true;
7886
2.28k
            const auto gridsNeeded = co->gridsNeeded(
7887
2.28k
                dbContext, proj_context_is_network_enabled(ctx) != FALSE);
7888
2.28k
            for (const auto &gridDesc : gridsNeeded) {
7889
371
                coordoperation->gridsNeeded.emplace_back(gridDesc);
7890
371
            }
7891
2.28k
        }
7892
2.28k
        return static_cast<int>(coordoperation->gridsNeeded.size());
7893
2.28k
    } catch (const std::exception &e) {
7894
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7895
0
        return 0;
7896
0
    }
7897
2.28k
}
7898
7899
// ---------------------------------------------------------------------------
7900
7901
/** \brief Return a parameter of a SingleOperation
7902
 *
7903
 * @param ctx PROJ context, or NULL for default context
7904
 * @param coordoperation Object of type SingleOperation or derived classes
7905
 * (must not be NULL)
7906
 * @param index Parameter index.
7907
 * @param out_short_name Pointer to a string value to store the grid short name.
7908
 * or NULL
7909
 * @param out_full_name Pointer to a string value to store the grid full
7910
 * filename. or NULL
7911
 * @param out_package_name Pointer to a string value to store the package name
7912
 * where
7913
 * the grid might be found. or NULL
7914
 * @param out_url Pointer to a string value to store the grid URL or the
7915
 * package URL where the grid might be found. or NULL
7916
 * @param out_direct_download Pointer to a int (boolean) value to store whether
7917
 * *out_url can be downloaded directly. or NULL
7918
 * @param out_open_license Pointer to a int (boolean) value to store whether
7919
 * the grid is released with an open license. or NULL
7920
 * @param out_available Pointer to a int (boolean) value to store whether the
7921
 * grid is available at runtime. or NULL
7922
 * @return TRUE in case of success.
7923
 */
7924
7925
int proj_coordoperation_get_grid_used(
7926
    PJ_CONTEXT *ctx, const PJ *coordoperation, int index,
7927
    const char **out_short_name, const char **out_full_name,
7928
    const char **out_package_name, const char **out_url,
7929
0
    int *out_direct_download, int *out_open_license, int *out_available) {
7930
0
    SANITIZE_CTX(ctx);
7931
0
    const int count =
7932
0
        proj_coordoperation_get_grid_used_count(ctx, coordoperation);
7933
0
    if (index < 0 || index >= count) {
7934
0
        proj_log_error(ctx, __FUNCTION__, "Invalid index");
7935
0
        return false;
7936
0
    }
7937
7938
0
    const auto &gridDesc = coordoperation->gridsNeeded[index];
7939
0
    if (out_short_name) {
7940
0
        *out_short_name = gridDesc.shortName.c_str();
7941
0
    }
7942
7943
0
    if (out_full_name) {
7944
0
        *out_full_name = gridDesc.fullName.c_str();
7945
0
    }
7946
7947
0
    if (out_package_name) {
7948
0
        *out_package_name = gridDesc.packageName.c_str();
7949
0
    }
7950
7951
0
    if (out_url) {
7952
0
        *out_url = gridDesc.url.c_str();
7953
0
    }
7954
7955
0
    if (out_direct_download) {
7956
0
        *out_direct_download = gridDesc.directDownload;
7957
0
    }
7958
7959
0
    if (out_open_license) {
7960
0
        *out_open_license = gridDesc.openLicense;
7961
0
    }
7962
7963
0
    if (out_available) {
7964
0
        *out_available = gridDesc.available;
7965
0
    }
7966
7967
0
    return true;
7968
0
}
7969
7970
// ---------------------------------------------------------------------------
7971
7972
/** \brief Opaque object representing an operation factory context. */
7973
struct PJ_OPERATION_FACTORY_CONTEXT {
7974
    //! @cond Doxygen_Suppress
7975
    CoordinateOperationContextNNPtr operationContext;
7976
7977
    explicit PJ_OPERATION_FACTORY_CONTEXT(
7978
        CoordinateOperationContextNNPtr &&operationContextIn)
7979
8.79k
        : operationContext(std::move(operationContextIn)) {}
7980
7981
    PJ_OPERATION_FACTORY_CONTEXT(const PJ_OPERATION_FACTORY_CONTEXT &) = delete;
7982
    PJ_OPERATION_FACTORY_CONTEXT &
7983
    operator=(const PJ_OPERATION_FACTORY_CONTEXT &) = delete;
7984
    //! @endcond
7985
};
7986
7987
// ---------------------------------------------------------------------------
7988
7989
/** \brief Instantiate a context for building coordinate operations between
7990
 * two CRS.
7991
 *
7992
 * The returned object must be unreferenced with
7993
 * proj_operation_factory_context_destroy() after use.
7994
 *
7995
 * If authority is NULL or the empty string, then coordinate
7996
 * operations from any authority will be searched, with the restrictions set
7997
 * in the authority_to_authority_preference database table.
7998
 * If authority is set to "any", then coordinate
7999
 * operations from any authority will be searched
8000
 * If authority is a non-empty string different of "any",
8001
 * then coordinate operations will be searched only in that authority namespace.
8002
 *
8003
 * @param ctx Context, or NULL for default context.
8004
 * @param authority Name of authority to which to restrict the search of
8005
 *                  candidate operations.
8006
 * @return Object that must be unreferenced with
8007
 * proj_operation_factory_context_destroy(), or NULL in
8008
 * case of error.
8009
 */
8010
PJ_OPERATION_FACTORY_CONTEXT *
8011
8.79k
proj_create_operation_factory_context(PJ_CONTEXT *ctx, const char *authority) {
8012
8.79k
    SANITIZE_CTX(ctx);
8013
8.79k
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
8014
8.79k
    try {
8015
8.79k
        if (dbContext) {
8016
8.79k
            auto factory = CoordinateOperationFactory::create();
8017
8.79k
            auto authFactory = AuthorityFactory::create(
8018
8.79k
                NN_NO_CHECK(dbContext),
8019
8.79k
                std::string(authority ? authority : ""));
8020
8.79k
            auto operationContext =
8021
8.79k
                CoordinateOperationContext::create(authFactory, nullptr, 0.0);
8022
8.79k
            return new PJ_OPERATION_FACTORY_CONTEXT(
8023
8.79k
                std::move(operationContext));
8024
8.79k
        } else {
8025
0
            auto operationContext =
8026
0
                CoordinateOperationContext::create(nullptr, nullptr, 0.0);
8027
0
            return new PJ_OPERATION_FACTORY_CONTEXT(
8028
0
                std::move(operationContext));
8029
0
        }
8030
8.79k
    } catch (const std::exception &e) {
8031
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8032
0
    }
8033
0
    return nullptr;
8034
8.79k
}
8035
8036
// ---------------------------------------------------------------------------
8037
8038
/** \brief Drops a reference on an object.
8039
 *
8040
 * This method should be called one and exactly one for each function
8041
 * returning a PJ_OPERATION_FACTORY_CONTEXT*
8042
 *
8043
 * @param ctx Object, or NULL.
8044
 */
8045
8.79k
void proj_operation_factory_context_destroy(PJ_OPERATION_FACTORY_CONTEXT *ctx) {
8046
8.79k
    delete ctx;
8047
8.79k
}
8048
8049
// ---------------------------------------------------------------------------
8050
8051
/** \brief Set the desired accuracy of the resulting coordinate transformations.
8052
 * @param ctx PROJ context, or NULL for default context
8053
 * @param factory_ctx Operation factory context. must not be NULL
8054
 * @param accuracy Accuracy in meter (or 0 to disable the filter).
8055
 */
8056
void proj_operation_factory_context_set_desired_accuracy(
8057
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8058
0
    double accuracy) {
8059
0
    SANITIZE_CTX(ctx);
8060
0
    if (!factory_ctx) {
8061
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8062
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8063
0
        return;
8064
0
    }
8065
0
    try {
8066
0
        factory_ctx->operationContext->setDesiredAccuracy(accuracy);
8067
0
    } catch (const std::exception &e) {
8068
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8069
0
    }
8070
0
}
8071
8072
// ---------------------------------------------------------------------------
8073
8074
/** \brief Set the desired area of interest for the resulting coordinate
8075
 * transformations.
8076
 *
8077
 * For an area of interest crossing the anti-meridian, west_lon_degree will be
8078
 * greater than east_lon_degree.
8079
 *
8080
 * @param ctx PROJ context, or NULL for default context
8081
 * @param factory_ctx Operation factory context. must not be NULL
8082
 * @param west_lon_degree West longitude (in degrees).
8083
 * @param south_lat_degree South latitude (in degrees).
8084
 * @param east_lon_degree East longitude (in degrees).
8085
 * @param north_lat_degree North latitude (in degrees).
8086
 */
8087
void proj_operation_factory_context_set_area_of_interest(
8088
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8089
    double west_lon_degree, double south_lat_degree, double east_lon_degree,
8090
0
    double north_lat_degree) {
8091
0
    SANITIZE_CTX(ctx);
8092
0
    if (!factory_ctx) {
8093
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8094
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8095
0
        return;
8096
0
    }
8097
0
    try {
8098
0
        factory_ctx->operationContext->setAreaOfInterest(
8099
0
            Extent::createFromBBOX(west_lon_degree, south_lat_degree,
8100
0
                                   east_lon_degree, north_lat_degree));
8101
0
    } catch (const std::exception &e) {
8102
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8103
0
    }
8104
0
}
8105
8106
// ---------------------------------------------------------------------------
8107
8108
/** \brief Set the name of the desired area of interest for the resulting
8109
 * coordinate transformations.
8110
 *
8111
 * @param ctx PROJ context, or NULL for default context
8112
 * @param factory_ctx Operation factory context. must not be NULL
8113
 * @param area_name Area name. Must be known of the database.
8114
 */
8115
void proj_operation_factory_context_set_area_of_interest_name(
8116
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8117
0
    const char *area_name) {
8118
0
    SANITIZE_CTX(ctx);
8119
0
    if (!factory_ctx || !area_name) {
8120
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8121
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8122
0
        return;
8123
0
    }
8124
0
    try {
8125
0
        auto extent = factory_ctx->operationContext->getAreaOfInterest();
8126
0
        if (extent == nullptr) {
8127
0
            auto dbContext = getDBcontext(ctx);
8128
0
            auto factory = AuthorityFactory::create(dbContext, std::string());
8129
0
            auto res = factory->listAreaOfUseFromName(area_name, false);
8130
0
            if (res.size() == 1) {
8131
0
                factory_ctx->operationContext->setAreaOfInterest(
8132
0
                    AuthorityFactory::create(dbContext, res.front().first)
8133
0
                        ->createExtent(res.front().second)
8134
0
                        .as_nullable());
8135
0
            } else {
8136
0
                proj_log_error(ctx, __FUNCTION__, "cannot find area");
8137
0
                return;
8138
0
            }
8139
0
        } else {
8140
0
            factory_ctx->operationContext->setAreaOfInterest(
8141
0
                metadata::Extent::create(util::optional<std::string>(area_name),
8142
0
                                         extent->geographicElements(),
8143
0
                                         extent->verticalElements(),
8144
0
                                         extent->temporalElements())
8145
0
                    .as_nullable());
8146
0
        }
8147
0
    } catch (const std::exception &e) {
8148
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8149
0
    }
8150
0
}
8151
8152
// ---------------------------------------------------------------------------
8153
8154
/** \brief Set how source and target CRS extent should be used
8155
 * when considering if a transformation can be used (only takes effect if
8156
 * no area of interest is explicitly defined).
8157
 *
8158
 * The default is PJ_CRS_EXTENT_SMALLEST.
8159
 *
8160
 * @param ctx PROJ context, or NULL for default context
8161
 * @param factory_ctx Operation factory context. must not be NULL
8162
 * @param use How source and target CRS extent should be used.
8163
 */
8164
void proj_operation_factory_context_set_crs_extent_use(
8165
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8166
0
    PROJ_CRS_EXTENT_USE use) {
8167
0
    SANITIZE_CTX(ctx);
8168
0
    if (!factory_ctx) {
8169
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8170
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8171
0
        return;
8172
0
    }
8173
0
    try {
8174
0
        switch (use) {
8175
0
        case PJ_CRS_EXTENT_NONE:
8176
0
            factory_ctx->operationContext->setSourceAndTargetCRSExtentUse(
8177
0
                CoordinateOperationContext::SourceTargetCRSExtentUse::NONE);
8178
0
            break;
8179
8180
0
        case PJ_CRS_EXTENT_BOTH:
8181
0
            factory_ctx->operationContext->setSourceAndTargetCRSExtentUse(
8182
0
                CoordinateOperationContext::SourceTargetCRSExtentUse::BOTH);
8183
0
            break;
8184
8185
0
        case PJ_CRS_EXTENT_INTERSECTION:
8186
0
            factory_ctx->operationContext->setSourceAndTargetCRSExtentUse(
8187
0
                CoordinateOperationContext::SourceTargetCRSExtentUse::
8188
0
                    INTERSECTION);
8189
0
            break;
8190
8191
0
        case PJ_CRS_EXTENT_SMALLEST:
8192
0
            factory_ctx->operationContext->setSourceAndTargetCRSExtentUse(
8193
0
                CoordinateOperationContext::SourceTargetCRSExtentUse::SMALLEST);
8194
0
            break;
8195
0
        }
8196
0
    } catch (const std::exception &e) {
8197
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8198
0
    }
8199
0
}
8200
8201
// ---------------------------------------------------------------------------
8202
8203
/** \brief Set the spatial criterion to use when comparing the area of
8204
 * validity of coordinate operations with the area of interest / area of
8205
 * validity of
8206
 * source and target CRS.
8207
 *
8208
 * The default is PROJ_SPATIAL_CRITERION_STRICT_CONTAINMENT.
8209
 *
8210
 * @param ctx PROJ context, or NULL for default context
8211
 * @param factory_ctx Operation factory context. must not be NULL
8212
 * @param criterion spatial criterion to use
8213
 */
8214
void proj_operation_factory_context_set_spatial_criterion(
8215
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8216
8.79k
    PROJ_SPATIAL_CRITERION criterion) {
8217
8.79k
    SANITIZE_CTX(ctx);
8218
8.79k
    if (!factory_ctx) {
8219
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8220
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8221
0
        return;
8222
0
    }
8223
8.79k
    try {
8224
8.79k
        switch (criterion) {
8225
0
        case PROJ_SPATIAL_CRITERION_STRICT_CONTAINMENT:
8226
0
            factory_ctx->operationContext->setSpatialCriterion(
8227
0
                CoordinateOperationContext::SpatialCriterion::
8228
0
                    STRICT_CONTAINMENT);
8229
0
            break;
8230
8231
8.79k
        case PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION:
8232
8.79k
            factory_ctx->operationContext->setSpatialCriterion(
8233
8.79k
                CoordinateOperationContext::SpatialCriterion::
8234
8.79k
                    PARTIAL_INTERSECTION);
8235
8.79k
            break;
8236
8.79k
        }
8237
8.79k
    } catch (const std::exception &e) {
8238
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8239
0
    }
8240
8.79k
}
8241
8242
// ---------------------------------------------------------------------------
8243
8244
/** \brief Set how grid availability is used.
8245
 *
8246
 * The default is USE_FOR_SORTING.
8247
 *
8248
 * @param ctx PROJ context, or NULL for default context
8249
 * @param factory_ctx Operation factory context. must not be NULL
8250
 * @param use how grid availability is used.
8251
 */
8252
void proj_operation_factory_context_set_grid_availability_use(
8253
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8254
8.79k
    PROJ_GRID_AVAILABILITY_USE use) {
8255
8.79k
    SANITIZE_CTX(ctx);
8256
8.79k
    if (!factory_ctx) {
8257
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8258
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8259
0
        return;
8260
0
    }
8261
8.79k
    try {
8262
8.79k
        switch (use) {
8263
0
        case PROJ_GRID_AVAILABILITY_USED_FOR_SORTING:
8264
0
            factory_ctx->operationContext->setGridAvailabilityUse(
8265
0
                CoordinateOperationContext::GridAvailabilityUse::
8266
0
                    USE_FOR_SORTING);
8267
0
            break;
8268
8269
8.79k
        case PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID:
8270
8.79k
            factory_ctx->operationContext->setGridAvailabilityUse(
8271
8.79k
                CoordinateOperationContext::GridAvailabilityUse::
8272
8.79k
                    DISCARD_OPERATION_IF_MISSING_GRID);
8273
8.79k
            break;
8274
8275
0
        case PROJ_GRID_AVAILABILITY_IGNORED:
8276
0
            factory_ctx->operationContext->setGridAvailabilityUse(
8277
0
                CoordinateOperationContext::GridAvailabilityUse::
8278
0
                    IGNORE_GRID_AVAILABILITY);
8279
0
            break;
8280
8281
0
        case PROJ_GRID_AVAILABILITY_KNOWN_AVAILABLE:
8282
0
            factory_ctx->operationContext->setGridAvailabilityUse(
8283
0
                CoordinateOperationContext::GridAvailabilityUse::
8284
0
                    KNOWN_AVAILABLE);
8285
0
            break;
8286
8.79k
        }
8287
8.79k
    } catch (const std::exception &e) {
8288
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8289
0
    }
8290
8.79k
}
8291
8292
// ---------------------------------------------------------------------------
8293
8294
/** \brief Set whether PROJ alternative grid names should be substituted to
8295
 * the official authority names.
8296
 *
8297
 * The default is true.
8298
 *
8299
 * @param ctx PROJ context, or NULL for default context
8300
 * @param factory_ctx Operation factory context. must not be NULL
8301
 * @param usePROJNames whether PROJ alternative grid names should be used
8302
 */
8303
void proj_operation_factory_context_set_use_proj_alternative_grid_names(
8304
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8305
0
    int usePROJNames) {
8306
0
    SANITIZE_CTX(ctx);
8307
0
    if (!factory_ctx) {
8308
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8309
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8310
0
        return;
8311
0
    }
8312
0
    try {
8313
0
        factory_ctx->operationContext->setUsePROJAlternativeGridNames(
8314
0
            usePROJNames != 0);
8315
0
    } catch (const std::exception &e) {
8316
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8317
0
    }
8318
0
}
8319
8320
// ---------------------------------------------------------------------------
8321
8322
/** \brief Set whether an intermediate pivot CRS can be used for researching
8323
 * coordinate operations between a source and target CRS.
8324
 *
8325
 * Concretely if in the database there is an operation from A to C
8326
 * (or C to A), and another one from C to B (or B to C), but no direct
8327
 * operation between A and B, setting this parameter to true, allow
8328
 * chaining both operations.
8329
 *
8330
 * The current implementation is limited to researching one intermediate
8331
 * step.
8332
 *
8333
 * By default, with the IF_NO_DIRECT_TRANSFORMATION strategy, all potential
8334
 * C candidates will be used if there is no direct transformation.
8335
 *
8336
 * @param ctx PROJ context, or NULL for default context
8337
 * @param factory_ctx Operation factory context. must not be NULL
8338
 * @param use whether and how intermediate CRS may be used.
8339
 */
8340
void proj_operation_factory_context_set_allow_use_intermediate_crs(
8341
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8342
0
    PROJ_INTERMEDIATE_CRS_USE use) {
8343
0
    SANITIZE_CTX(ctx);
8344
0
    if (!factory_ctx) {
8345
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8346
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8347
0
        return;
8348
0
    }
8349
0
    try {
8350
0
        switch (use) {
8351
0
        case PROJ_INTERMEDIATE_CRS_USE_ALWAYS:
8352
0
            factory_ctx->operationContext->setAllowUseIntermediateCRS(
8353
0
                CoordinateOperationContext::IntermediateCRSUse::ALWAYS);
8354
0
            break;
8355
8356
0
        case PROJ_INTERMEDIATE_CRS_USE_IF_NO_DIRECT_TRANSFORMATION:
8357
0
            factory_ctx->operationContext->setAllowUseIntermediateCRS(
8358
0
                CoordinateOperationContext::IntermediateCRSUse::
8359
0
                    IF_NO_DIRECT_TRANSFORMATION);
8360
0
            break;
8361
8362
0
        case PROJ_INTERMEDIATE_CRS_USE_NEVER:
8363
0
            factory_ctx->operationContext->setAllowUseIntermediateCRS(
8364
0
                CoordinateOperationContext::IntermediateCRSUse::NEVER);
8365
0
            break;
8366
0
        }
8367
0
    } catch (const std::exception &e) {
8368
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8369
0
    }
8370
0
}
8371
8372
// ---------------------------------------------------------------------------
8373
8374
/** \brief Restrict the potential pivot CRSs that can be used when trying to
8375
 * build a coordinate operation between two CRS that have no direct operation.
8376
 *
8377
 * @param ctx PROJ context, or NULL for default context
8378
 * @param factory_ctx Operation factory context. must not be NULL
8379
 * @param list_of_auth_name_codes an array of strings NLL terminated,
8380
 * with the format { "auth_name1", "code1", "auth_name2", "code2", ... NULL }
8381
 */
8382
void proj_operation_factory_context_set_allowed_intermediate_crs(
8383
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8384
0
    const char *const *list_of_auth_name_codes) {
8385
0
    SANITIZE_CTX(ctx);
8386
0
    if (!factory_ctx) {
8387
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8388
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8389
0
        return;
8390
0
    }
8391
0
    try {
8392
0
        std::vector<std::pair<std::string, std::string>> pivots;
8393
0
        for (auto iter = list_of_auth_name_codes; iter && iter[0] && iter[1];
8394
0
             iter += 2) {
8395
0
            pivots.emplace_back(std::pair<std::string, std::string>(
8396
0
                std::string(iter[0]), std::string(iter[1])));
8397
0
        }
8398
0
        factory_ctx->operationContext->setIntermediateCRS(pivots);
8399
0
    } catch (const std::exception &e) {
8400
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8401
0
    }
8402
0
}
8403
8404
// ---------------------------------------------------------------------------
8405
8406
/** \brief Set whether transformations that are superseded (but not deprecated)
8407
 * should be discarded.
8408
 *
8409
 * @param ctx PROJ context, or NULL for default context
8410
 * @param factory_ctx Operation factory context. must not be NULL
8411
 * @param discard superseded crs or not
8412
 */
8413
void proj_operation_factory_context_set_discard_superseded(
8414
0
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, int discard) {
8415
0
    SANITIZE_CTX(ctx);
8416
0
    if (!factory_ctx) {
8417
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8418
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8419
0
        return;
8420
0
    }
8421
0
    try {
8422
0
        factory_ctx->operationContext->setDiscardSuperseded(discard != 0);
8423
0
    } catch (const std::exception &e) {
8424
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8425
0
    }
8426
0
}
8427
8428
// ---------------------------------------------------------------------------
8429
8430
/** \brief Set whether ballpark transformations are allowed.
8431
 *
8432
 * @param ctx PROJ context, or NULL for default context
8433
 * @param factory_ctx Operation factory context. must not be NULL
8434
 * @param allow set to TRUE to allow ballpark transformations.
8435
 * @since 7.1
8436
 */
8437
void proj_operation_factory_context_set_allow_ballpark_transformations(
8438
6.05k
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, int allow) {
8439
6.05k
    SANITIZE_CTX(ctx);
8440
6.05k
    if (!factory_ctx) {
8441
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8442
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8443
0
        return;
8444
0
    }
8445
6.05k
    try {
8446
6.05k
        factory_ctx->operationContext->setAllowBallparkTransformations(allow !=
8447
6.05k
                                                                       0);
8448
6.05k
    } catch (const std::exception &e) {
8449
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8450
0
    }
8451
6.05k
}
8452
8453
// ---------------------------------------------------------------------------
8454
8455
//! @cond Doxygen_Suppress
8456
/** \brief Opaque object representing a set of operation results. */
8457
struct PJ_OPERATION_LIST : PJ_OBJ_LIST {
8458
8459
    PJ *source_crs;
8460
    PJ *target_crs;
8461
    bool hasPreparedOperation = false;
8462
    std::vector<PJCoordOperation> preparedOperations{};
8463
8464
    explicit PJ_OPERATION_LIST(PJ_CONTEXT *ctx, const PJ *source_crsIn,
8465
                               const PJ *target_crsIn,
8466
                               std::vector<IdentifiedObjectNNPtr> &&objectsIn);
8467
    ~PJ_OPERATION_LIST() override;
8468
8469
    PJ_OPERATION_LIST(const PJ_OPERATION_LIST &) = delete;
8470
    PJ_OPERATION_LIST &operator=(const PJ_OPERATION_LIST &) = delete;
8471
8472
    const std::vector<PJCoordOperation> &getPreparedOperations(PJ_CONTEXT *ctx);
8473
};
8474
8475
// ---------------------------------------------------------------------------
8476
8477
PJ_OPERATION_LIST::PJ_OPERATION_LIST(
8478
    PJ_CONTEXT *ctx, const PJ *source_crsIn, const PJ *target_crsIn,
8479
    std::vector<IdentifiedObjectNNPtr> &&objectsIn)
8480
7.98k
    : PJ_OBJ_LIST(std::move(objectsIn)),
8481
7.98k
      source_crs(proj_clone(ctx, source_crsIn)),
8482
7.98k
      target_crs(proj_clone(ctx, target_crsIn)) {}
8483
8484
// ---------------------------------------------------------------------------
8485
8486
7.98k
PJ_OPERATION_LIST::~PJ_OPERATION_LIST() {
8487
7.98k
    auto tmpCtxt = proj_context_create();
8488
7.98k
    proj_assign_context(source_crs, tmpCtxt);
8489
7.98k
    proj_assign_context(target_crs, tmpCtxt);
8490
7.98k
    proj_destroy(source_crs);
8491
7.98k
    proj_destroy(target_crs);
8492
7.98k
    proj_context_destroy(tmpCtxt);
8493
7.98k
}
8494
8495
// ---------------------------------------------------------------------------
8496
8497
const std::vector<PJCoordOperation> &
8498
0
PJ_OPERATION_LIST::getPreparedOperations(PJ_CONTEXT *ctx) {
8499
0
    if (!hasPreparedOperation) {
8500
0
        hasPreparedOperation = true;
8501
0
        preparedOperations =
8502
0
            pj_create_prepared_operations(ctx, source_crs, target_crs, this);
8503
0
    }
8504
0
    return preparedOperations;
8505
0
}
8506
8507
//! @endcond
8508
8509
// ---------------------------------------------------------------------------
8510
8511
/** \brief Find a list of CoordinateOperation from source_crs to target_crs.
8512
 *
8513
 * The operations are sorted with the most relevant ones first: by
8514
 * descending
8515
 * area (intersection of the transformation area with the area of interest,
8516
 * or intersection of the transformation with the area of use of the CRS),
8517
 * and
8518
 * by increasing accuracy. Operations with unknown accuracy are sorted last,
8519
 * whatever their area.
8520
 *
8521
 * Starting with PROJ 9.1, vertical transformations are only done if both
8522
 * source CRS and target CRS are 3D CRS or Compound CRS with a vertical
8523
 * component. You may need to use proj_crs_promote_to_3D().
8524
 *
8525
 * @param ctx PROJ context, or NULL for default context
8526
 * @param source_crs source CRS. Must not be NULL.
8527
 * @param target_crs source CRS. Must not be NULL.
8528
 * @param operationContext Search context. Must not be NULL.
8529
 * @return a result set that must be unreferenced with
8530
 * proj_list_destroy(), or NULL in case of error.
8531
 */
8532
PJ_OBJ_LIST *
8533
proj_create_operations(PJ_CONTEXT *ctx, const PJ *source_crs,
8534
                       const PJ *target_crs,
8535
8.79k
                       const PJ_OPERATION_FACTORY_CONTEXT *operationContext) {
8536
8.79k
    SANITIZE_CTX(ctx);
8537
8.79k
    if (!source_crs || !target_crs || !operationContext) {
8538
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8539
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8540
0
        return nullptr;
8541
0
    }
8542
8.79k
    auto sourceCRS = std::dynamic_pointer_cast<CRS>(source_crs->iso_obj);
8543
8.79k
    CoordinateMetadataPtr sourceCoordinateMetadata;
8544
8.79k
    if (!sourceCRS) {
8545
295
        sourceCoordinateMetadata =
8546
295
            std::dynamic_pointer_cast<CoordinateMetadata>(source_crs->iso_obj);
8547
295
        if (!sourceCoordinateMetadata) {
8548
267
            proj_log_error(ctx, __FUNCTION__,
8549
267
                           "source_crs is not a CRS or a CoordinateMetadata");
8550
267
            return nullptr;
8551
267
        }
8552
28
        if (!sourceCoordinateMetadata->coordinateEpoch().has_value()) {
8553
0
            sourceCRS = sourceCoordinateMetadata->crs().as_nullable();
8554
0
            sourceCoordinateMetadata.reset();
8555
0
        }
8556
28
    }
8557
8.52k
    auto targetCRS = std::dynamic_pointer_cast<CRS>(target_crs->iso_obj);
8558
8.52k
    CoordinateMetadataPtr targetCoordinateMetadata;
8559
8.52k
    if (!targetCRS) {
8560
395
        targetCoordinateMetadata =
8561
395
            std::dynamic_pointer_cast<CoordinateMetadata>(target_crs->iso_obj);
8562
395
        if (!targetCoordinateMetadata) {
8563
374
            proj_log_error(ctx, __FUNCTION__,
8564
374
                           "target_crs is not a CRS or a CoordinateMetadata");
8565
374
            return nullptr;
8566
374
        }
8567
21
        if (!targetCoordinateMetadata->coordinateEpoch().has_value()) {
8568
3
            targetCRS = targetCoordinateMetadata->crs().as_nullable();
8569
3
            targetCoordinateMetadata.reset();
8570
3
        }
8571
21
    }
8572
8573
8.15k
    try {
8574
8.15k
        auto factory = CoordinateOperationFactory::create();
8575
8.15k
        std::vector<IdentifiedObjectNNPtr> objects;
8576
8.15k
        auto ops = sourceCoordinateMetadata != nullptr
8577
8.15k
                       ? (targetCoordinateMetadata != nullptr
8578
25
                              ? factory->createOperations(
8579
0
                                    NN_NO_CHECK(sourceCoordinateMetadata),
8580
0
                                    NN_NO_CHECK(targetCoordinateMetadata),
8581
0
                                    operationContext->operationContext)
8582
25
                              : factory->createOperations(
8583
25
                                    NN_NO_CHECK(sourceCoordinateMetadata),
8584
25
                                    NN_NO_CHECK(targetCRS),
8585
25
                                    operationContext->operationContext))
8586
8.15k
                   : targetCoordinateMetadata != nullptr
8587
8.12k
                       ? factory->createOperations(
8588
18
                             NN_NO_CHECK(sourceCRS),
8589
18
                             NN_NO_CHECK(targetCoordinateMetadata),
8590
18
                             operationContext->operationContext)
8591
8.12k
                       : factory->createOperations(
8592
8.10k
                             NN_NO_CHECK(sourceCRS), NN_NO_CHECK(targetCRS),
8593
8.10k
                             operationContext->operationContext);
8594
32.5k
        for (const auto &op : ops) {
8595
32.5k
            objects.emplace_back(op);
8596
32.5k
        }
8597
8.15k
        return new PJ_OPERATION_LIST(ctx, source_crs, target_crs,
8598
8.15k
                                     std::move(objects));
8599
8.15k
    } catch (const std::exception &e) {
8600
167
        proj_log_error(ctx, __FUNCTION__, e.what());
8601
167
        return nullptr;
8602
167
    }
8603
8.15k
}
8604
8605
// ---------------------------------------------------------------------------
8606
8607
/** Return the index of the operation that would be the most appropriate to
8608
 * transform the specified coordinates.
8609
 *
8610
 * This operation may use resources that are not locally available, depending
8611
 * on the search criteria used by proj_create_operations().
8612
 *
8613
 * This could be done by using proj_create_operations() with a punctual bounding
8614
 * box, but this function is faster when one needs to evaluate on many points
8615
 * with the same (source_crs, target_crs) tuple.
8616
 *
8617
 * @param ctx PROJ context, or NULL for default context
8618
 * @param operations List of operations returned by proj_create_operations()
8619
 * @param direction Direction into which to transform the point.
8620
 * @param coord Coordinate to transform
8621
 * @return the index in operations that would be used to transform coord. Or -1
8622
 * in case of error, or no match.
8623
 *
8624
 * @since 7.1
8625
 */
8626
int proj_get_suggested_operation(PJ_CONTEXT *ctx, PJ_OBJ_LIST *operations,
8627
                                 // cppcheck-suppress passedByValue
8628
0
                                 PJ_DIRECTION direction, PJ_COORD coord) {
8629
0
    SANITIZE_CTX(ctx);
8630
0
    auto opList = dynamic_cast<PJ_OPERATION_LIST *>(operations);
8631
0
    if (opList == nullptr) {
8632
0
        proj_log_error(ctx, __FUNCTION__,
8633
0
                       "operations is not a list of operations");
8634
0
        return -1;
8635
0
    }
8636
8637
    // Special case:
8638
    // proj_create_crs_to_crs_from_pj() always use the unique operation
8639
    // if there's a single one
8640
0
    if (opList->objects.size() == 1) {
8641
0
        return 0;
8642
0
    }
8643
8644
0
    int iExcluded[2] = {-1, -1};
8645
0
    const auto &preparedOps = opList->getPreparedOperations(ctx);
8646
0
    int idx = pj_get_suggested_operation(ctx, preparedOps, iExcluded,
8647
0
                                         /* skipNonInstantiable= */ false,
8648
0
                                         direction, coord);
8649
0
    if (idx >= 0) {
8650
0
        idx = preparedOps[idx].idxInOriginalList;
8651
0
    }
8652
0
    return idx;
8653
0
}
8654
8655
// ---------------------------------------------------------------------------
8656
8657
/** \brief Return the number of objects in the result set
8658
 *
8659
 * @param result Object of type PJ_OBJ_LIST (must not be NULL)
8660
 */
8661
42.8k
int proj_list_get_count(const PJ_OBJ_LIST *result) {
8662
42.8k
    if (!result) {
8663
0
        return 0;
8664
0
    }
8665
42.8k
    return static_cast<int>(result->objects.size());
8666
42.8k
}
8667
8668
// ---------------------------------------------------------------------------
8669
8670
/** \brief Return an object from the result set
8671
 *
8672
 * The returned object must be unreferenced with proj_destroy() after
8673
 * use.
8674
 * It should be used by at most one thread at a time.
8675
 *
8676
 * @param ctx PROJ context, or NULL for default context
8677
 * @param result Object of type PJ_OBJ_LIST (must not be NULL)
8678
 * @param index Index
8679
 * @return a new object that must be unreferenced with proj_destroy(),
8680
 * or nullptr in case of error.
8681
 */
8682
8683
33.5k
PJ *proj_list_get(PJ_CONTEXT *ctx, const PJ_OBJ_LIST *result, int index) {
8684
33.5k
    SANITIZE_CTX(ctx);
8685
33.5k
    if (!result) {
8686
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8687
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8688
0
        return nullptr;
8689
0
    }
8690
33.5k
    if (index < 0 || index >= proj_list_get_count(result)) {
8691
0
        proj_log_error(ctx, __FUNCTION__, "Invalid index");
8692
0
        return nullptr;
8693
0
    }
8694
33.5k
    return pj_obj_create(ctx, result->objects[index]);
8695
33.5k
}
8696
8697
// ---------------------------------------------------------------------------
8698
8699
/** \brief Drops a reference on the result set.
8700
 *
8701
 * This method should be called one and exactly one for each function
8702
 * returning a PJ_OBJ_LIST*
8703
 *
8704
 * @param result Object, or NULL.
8705
 */
8706
7.99k
void proj_list_destroy(PJ_OBJ_LIST *result) { delete result; }
8707
8708
// ---------------------------------------------------------------------------
8709
8710
/** \brief Return the accuracy (in metre) of a coordinate operation.
8711
 *
8712
 * @param ctx PROJ context, or NULL for default context
8713
 * @param coordoperation Coordinate operation. Must not be NULL.
8714
 * @return the accuracy, or a negative value if unknown or in case of error.
8715
 */
8716
double proj_coordoperation_get_accuracy(PJ_CONTEXT *ctx,
8717
13.7k
                                        const PJ *coordoperation) {
8718
13.7k
    SANITIZE_CTX(ctx);
8719
13.7k
    if (!coordoperation) {
8720
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8721
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8722
0
        return -1;
8723
0
    }
8724
13.7k
    auto co = dynamic_cast<const CoordinateOperation *>(
8725
13.7k
        coordoperation->iso_obj.get());
8726
13.7k
    if (!co) {
8727
0
        proj_log_error(ctx, __FUNCTION__,
8728
0
                       "Object is not a CoordinateOperation");
8729
0
        return -1;
8730
0
    }
8731
13.7k
    const auto &accuracies = co->coordinateOperationAccuracies();
8732
13.7k
    if (accuracies.empty()) {
8733
10.0k
        return -1;
8734
10.0k
    }
8735
3.71k
    try {
8736
3.71k
        return c_locale_stod(accuracies[0]->value());
8737
3.71k
    } catch (const std::exception &) {
8738
0
    }
8739
0
    return -1;
8740
3.71k
}
8741
8742
// ---------------------------------------------------------------------------
8743
8744
/** \brief Returns the datum of a SingleCRS.
8745
 *
8746
 * If that function returns NULL, @see proj_crs_get_datum_ensemble() to
8747
 * potentially get a DatumEnsemble instead.
8748
 *
8749
 * The returned object must be unreferenced with proj_destroy() after
8750
 * use.
8751
 * It should be used by at most one thread at a time.
8752
 *
8753
 * @param ctx PROJ context, or NULL for default context
8754
 * @param crs Object of type SingleCRS (must not be NULL)
8755
 * @return Object that must be unreferenced with proj_destroy(), or NULL
8756
 * in case of error (or if there is no datum)
8757
 */
8758
0
PJ *proj_crs_get_datum(PJ_CONTEXT *ctx, const PJ *crs) {
8759
0
    SANITIZE_CTX(ctx);
8760
0
    if (!crs) {
8761
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8762
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8763
0
        return nullptr;
8764
0
    }
8765
0
    auto l_crs = dynamic_cast<const SingleCRS *>(crs->iso_obj.get());
8766
0
    if (!l_crs) {
8767
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleCRS");
8768
0
        return nullptr;
8769
0
    }
8770
0
    const auto &datum = l_crs->datum();
8771
0
    if (!datum) {
8772
0
        return nullptr;
8773
0
    }
8774
0
    return pj_obj_create(ctx, NN_NO_CHECK(datum));
8775
0
}
8776
8777
// ---------------------------------------------------------------------------
8778
8779
/** \brief Returns the datum ensemble of a SingleCRS.
8780
 *
8781
 * If that function returns NULL, @see proj_crs_get_datum() to
8782
 * potentially get a Datum instead.
8783
 *
8784
 * The returned object must be unreferenced with proj_destroy() after
8785
 * use.
8786
 * It should be used by at most one thread at a time.
8787
 *
8788
 * @param ctx PROJ context, or NULL for default context
8789
 * @param crs Object of type SingleCRS (must not be NULL)
8790
 * @return Object that must be unreferenced with proj_destroy(), or NULL
8791
 * in case of error (or if there is no datum ensemble)
8792
 *
8793
 * @since 7.2
8794
 */
8795
0
PJ *proj_crs_get_datum_ensemble(PJ_CONTEXT *ctx, const PJ *crs) {
8796
0
    SANITIZE_CTX(ctx);
8797
0
    if (!crs) {
8798
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8799
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8800
0
        return nullptr;
8801
0
    }
8802
0
    auto l_crs = dynamic_cast<const SingleCRS *>(crs->iso_obj.get());
8803
0
    if (!l_crs) {
8804
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleCRS");
8805
0
        return nullptr;
8806
0
    }
8807
0
    const auto &datumEnsemble = l_crs->datumEnsemble();
8808
0
    if (!datumEnsemble) {
8809
0
        return nullptr;
8810
0
    }
8811
0
    return pj_obj_create(ctx, NN_NO_CHECK(datumEnsemble));
8812
0
}
8813
8814
// ---------------------------------------------------------------------------
8815
8816
/** \brief Returns the number of members of a datum ensemble.
8817
 *
8818
 * @param ctx PROJ context, or NULL for default context
8819
 * @param datum_ensemble Object of type DatumEnsemble (must not be NULL)
8820
 *
8821
 * @since 7.2
8822
 */
8823
int proj_datum_ensemble_get_member_count(PJ_CONTEXT *ctx,
8824
0
                                         const PJ *datum_ensemble) {
8825
0
    SANITIZE_CTX(ctx);
8826
0
    if (!datum_ensemble) {
8827
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8828
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8829
0
        return 0;
8830
0
    }
8831
0
    auto l_datum_ensemble =
8832
0
        dynamic_cast<const DatumEnsemble *>(datum_ensemble->iso_obj.get());
8833
0
    if (!l_datum_ensemble) {
8834
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a DatumEnsemble");
8835
0
        return 0;
8836
0
    }
8837
0
    return static_cast<int>(l_datum_ensemble->datums().size());
8838
0
}
8839
8840
// ---------------------------------------------------------------------------
8841
8842
/** \brief Returns the positional accuracy of the datum ensemble.
8843
 *
8844
 * @param ctx PROJ context, or NULL for default context
8845
 * @param datum_ensemble Object of type DatumEnsemble (must not be NULL)
8846
 * @return the accuracy, or -1 in case of error.
8847
 *
8848
 * @since 7.2
8849
 */
8850
double proj_datum_ensemble_get_accuracy(PJ_CONTEXT *ctx,
8851
0
                                        const PJ *datum_ensemble) {
8852
0
    SANITIZE_CTX(ctx);
8853
0
    if (!datum_ensemble) {
8854
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8855
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8856
0
        return -1;
8857
0
    }
8858
0
    auto l_datum_ensemble =
8859
0
        dynamic_cast<const DatumEnsemble *>(datum_ensemble->iso_obj.get());
8860
0
    if (!l_datum_ensemble) {
8861
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a DatumEnsemble");
8862
0
        return -1;
8863
0
    }
8864
0
    const auto &accuracy = l_datum_ensemble->positionalAccuracy();
8865
0
    try {
8866
0
        return c_locale_stod(accuracy->value());
8867
0
    } catch (const std::exception &) {
8868
0
    }
8869
0
    return -1;
8870
0
}
8871
8872
// ---------------------------------------------------------------------------
8873
8874
/** \brief Returns a member from a datum ensemble.
8875
 *
8876
 * The returned object must be unreferenced with proj_destroy() after
8877
 * use.
8878
 * It should be used by at most one thread at a time.
8879
 *
8880
 * @param ctx PROJ context, or NULL for default context
8881
 * @param datum_ensemble Object of type DatumEnsemble (must not be NULL)
8882
 * @param member_index Index of the datum member to extract (between 0 and
8883
 * proj_datum_ensemble_get_member_count()-1)
8884
 * @return Object that must be unreferenced with proj_destroy(), or NULL
8885
 * in case of error (or if there is no datum ensemble)
8886
 *
8887
 * @since 7.2
8888
 */
8889
PJ *proj_datum_ensemble_get_member(PJ_CONTEXT *ctx, const PJ *datum_ensemble,
8890
0
                                   int member_index) {
8891
0
    SANITIZE_CTX(ctx);
8892
0
    if (!datum_ensemble) {
8893
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8894
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8895
0
        return nullptr;
8896
0
    }
8897
0
    auto l_datum_ensemble =
8898
0
        dynamic_cast<const DatumEnsemble *>(datum_ensemble->iso_obj.get());
8899
0
    if (!l_datum_ensemble) {
8900
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a DatumEnsemble");
8901
0
        return nullptr;
8902
0
    }
8903
0
    if (member_index < 0 ||
8904
0
        member_index >= static_cast<int>(l_datum_ensemble->datums().size())) {
8905
0
        proj_log_error(ctx, __FUNCTION__, "Invalid member_index");
8906
0
        return nullptr;
8907
0
    }
8908
0
    return pj_obj_create(ctx, l_datum_ensemble->datums()[member_index]);
8909
0
}
8910
8911
// ---------------------------------------------------------------------------
8912
8913
/** \brief Returns a datum for a SingleCRS.
8914
 *
8915
 * If the SingleCRS has a datum, then this datum is returned.
8916
 * Otherwise, the SingleCRS has a datum ensemble, and this datum ensemble is
8917
 * returned as a regular datum instead of a datum ensemble.
8918
 *
8919
 * The returned object must be unreferenced with proj_destroy() after
8920
 * use.
8921
 * It should be used by at most one thread at a time.
8922
 *
8923
 * @param ctx PROJ context, or NULL for default context
8924
 * @param crs Object of type SingleCRS (must not be NULL)
8925
 * @return Object that must be unreferenced with proj_destroy(), or NULL
8926
 * in case of error (or if there is no datum)
8927
 *
8928
 * @since 7.2
8929
 */
8930
2.74k
PJ *proj_crs_get_datum_forced(PJ_CONTEXT *ctx, const PJ *crs) {
8931
2.74k
    SANITIZE_CTX(ctx);
8932
2.74k
    if (!crs) {
8933
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8934
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8935
0
        return nullptr;
8936
0
    }
8937
2.74k
    auto l_crs = dynamic_cast<const SingleCRS *>(crs->iso_obj.get());
8938
2.74k
    if (!l_crs) {
8939
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleCRS");
8940
0
        return nullptr;
8941
0
    }
8942
2.74k
    const auto &datum = l_crs->datum();
8943
2.74k
    if (datum) {
8944
2.74k
        return pj_obj_create(ctx, NN_NO_CHECK(datum));
8945
2.74k
    }
8946
1
    const auto &datumEnsemble = l_crs->datumEnsemble();
8947
1
    assert(datumEnsemble);
8948
1
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
8949
1
    try {
8950
1
        return pj_obj_create(ctx, datumEnsemble->asDatum(dbContext));
8951
1
    } catch (const std::exception &e) {
8952
0
        proj_log_debug(ctx, __FUNCTION__, e.what());
8953
0
        return nullptr;
8954
0
    }
8955
1
}
8956
8957
// ---------------------------------------------------------------------------
8958
8959
/** \brief Returns the frame reference epoch of a dynamic geodetic or vertical
8960
 * reference frame.
8961
 *
8962
 * @param ctx PROJ context, or NULL for default context
8963
 * @param datum Object of type DynamicGeodeticReferenceFrame or
8964
 * DynamicVerticalReferenceFrame (must not be NULL)
8965
 * @return the frame reference epoch as decimal year, or -1 in case of error.
8966
 *
8967
 * @since 7.2
8968
 */
8969
double proj_dynamic_datum_get_frame_reference_epoch(PJ_CONTEXT *ctx,
8970
0
                                                    const PJ *datum) {
8971
0
    SANITIZE_CTX(ctx);
8972
0
    if (!datum) {
8973
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8974
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8975
0
        return -1;
8976
0
    }
8977
0
    auto dgrf = dynamic_cast<const DynamicGeodeticReferenceFrame *>(
8978
0
        datum->iso_obj.get());
8979
0
    auto dvrf = dynamic_cast<const DynamicVerticalReferenceFrame *>(
8980
0
        datum->iso_obj.get());
8981
0
    if (!dgrf && !dvrf) {
8982
0
        proj_log_error(ctx, __FUNCTION__,
8983
0
                       "Object is not a "
8984
0
                       "DynamicGeodeticReferenceFrame or "
8985
0
                       "DynamicVerticalReferenceFrame");
8986
0
        return -1;
8987
0
    }
8988
0
    const auto &frameReferenceEpoch =
8989
0
        dgrf ? dgrf->frameReferenceEpoch() : dvrf->frameReferenceEpoch();
8990
0
    return frameReferenceEpoch.value();
8991
0
}
8992
8993
// ---------------------------------------------------------------------------
8994
8995
/** \brief Returns the coordinate system of a SingleCRS.
8996
 *
8997
 * The returned object must be unreferenced with proj_destroy() after
8998
 * use.
8999
 * It should be used by at most one thread at a time.
9000
 *
9001
 * @param ctx PROJ context, or NULL for default context
9002
 * @param crs Object of type SingleCRS (must not be NULL)
9003
 * @return Object that must be unreferenced with proj_destroy(), or NULL
9004
 * in case of error.
9005
 */
9006
3.56k
PJ *proj_crs_get_coordinate_system(PJ_CONTEXT *ctx, const PJ *crs) {
9007
3.56k
    SANITIZE_CTX(ctx);
9008
3.56k
    if (!crs) {
9009
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9010
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9011
0
        return nullptr;
9012
0
    }
9013
3.56k
    auto l_crs = dynamic_cast<const SingleCRS *>(crs->iso_obj.get());
9014
3.56k
    if (!l_crs) {
9015
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleCRS");
9016
0
        return nullptr;
9017
0
    }
9018
3.56k
    return pj_obj_create(ctx, l_crs->coordinateSystem());
9019
3.56k
}
9020
9021
// ---------------------------------------------------------------------------
9022
9023
/** \brief Returns the type of the coordinate system.
9024
 *
9025
 * @param ctx PROJ context, or NULL for default context
9026
 * @param cs Object of type CoordinateSystem (must not be NULL)
9027
 * @return type, or PJ_CS_TYPE_UNKNOWN in case of error.
9028
 */
9029
0
PJ_COORDINATE_SYSTEM_TYPE proj_cs_get_type(PJ_CONTEXT *ctx, const PJ *cs) {
9030
0
    SANITIZE_CTX(ctx);
9031
0
    if (!cs) {
9032
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9033
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9034
0
        return PJ_CS_TYPE_UNKNOWN;
9035
0
    }
9036
0
    auto l_cs = dynamic_cast<const CoordinateSystem *>(cs->iso_obj.get());
9037
0
    if (!l_cs) {
9038
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateSystem");
9039
0
        return PJ_CS_TYPE_UNKNOWN;
9040
0
    }
9041
0
    if (dynamic_cast<const CartesianCS *>(l_cs)) {
9042
0
        return PJ_CS_TYPE_CARTESIAN;
9043
0
    }
9044
0
    if (dynamic_cast<const EllipsoidalCS *>(l_cs)) {
9045
0
        return PJ_CS_TYPE_ELLIPSOIDAL;
9046
0
    }
9047
0
    if (dynamic_cast<const VerticalCS *>(l_cs)) {
9048
0
        return PJ_CS_TYPE_VERTICAL;
9049
0
    }
9050
0
    if (dynamic_cast<const SphericalCS *>(l_cs)) {
9051
0
        return PJ_CS_TYPE_SPHERICAL;
9052
0
    }
9053
0
    if (dynamic_cast<const OrdinalCS *>(l_cs)) {
9054
0
        return PJ_CS_TYPE_ORDINAL;
9055
0
    }
9056
0
    if (dynamic_cast<const ParametricCS *>(l_cs)) {
9057
0
        return PJ_CS_TYPE_PARAMETRIC;
9058
0
    }
9059
0
    if (dynamic_cast<const DateTimeTemporalCS *>(l_cs)) {
9060
0
        return PJ_CS_TYPE_DATETIMETEMPORAL;
9061
0
    }
9062
0
    if (dynamic_cast<const TemporalCountCS *>(l_cs)) {
9063
0
        return PJ_CS_TYPE_TEMPORALCOUNT;
9064
0
    }
9065
0
    if (dynamic_cast<const TemporalMeasureCS *>(l_cs)) {
9066
0
        return PJ_CS_TYPE_TEMPORALMEASURE;
9067
0
    }
9068
0
    return PJ_CS_TYPE_UNKNOWN;
9069
0
}
9070
9071
// ---------------------------------------------------------------------------
9072
9073
/** \brief Returns the number of axis of the coordinate system.
9074
 *
9075
 * @param ctx PROJ context, or NULL for default context
9076
 * @param cs Object of type CoordinateSystem (must not be NULL)
9077
 * @return number of axis, or -1 in case of error.
9078
 */
9079
0
int proj_cs_get_axis_count(PJ_CONTEXT *ctx, const PJ *cs) {
9080
0
    SANITIZE_CTX(ctx);
9081
0
    if (!cs) {
9082
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9083
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9084
0
        return -1;
9085
0
    }
9086
0
    auto l_cs = dynamic_cast<const CoordinateSystem *>(cs->iso_obj.get());
9087
0
    if (!l_cs) {
9088
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateSystem");
9089
0
        return -1;
9090
0
    }
9091
0
    return static_cast<int>(l_cs->axisList().size());
9092
0
}
9093
9094
// ---------------------------------------------------------------------------
9095
9096
/** \brief Returns information on an axis
9097
 *
9098
 * @param ctx PROJ context, or NULL for default context
9099
 * @param cs Object of type CoordinateSystem (must not be NULL)
9100
 * @param index Index of the coordinate system (between 0 and
9101
 * proj_cs_get_axis_count() - 1)
9102
 * @param out_name Pointer to a string value to store the axis name. or NULL
9103
 * @param out_abbrev Pointer to a string value to store the axis abbreviation.
9104
 * or NULL
9105
 * @param out_direction Pointer to a string value to store the axis direction.
9106
 * or NULL
9107
 * @param out_unit_conv_factor Pointer to a double value to store the axis
9108
 * unit conversion factor. or NULL
9109
 * @param out_unit_name Pointer to a string value to store the axis
9110
 * unit name. or NULL
9111
 * @param out_unit_auth_name Pointer to a string value to store the axis
9112
 * unit authority name. or NULL
9113
 * @param out_unit_code Pointer to a string value to store the axis
9114
 * unit code. or NULL
9115
 * @return TRUE in case of success
9116
 */
9117
int proj_cs_get_axis_info(PJ_CONTEXT *ctx, const PJ *cs, int index,
9118
                          const char **out_name, const char **out_abbrev,
9119
                          const char **out_direction,
9120
                          double *out_unit_conv_factor,
9121
                          const char **out_unit_name,
9122
                          const char **out_unit_auth_name,
9123
4.60k
                          const char **out_unit_code) {
9124
4.60k
    SANITIZE_CTX(ctx);
9125
4.60k
    if (!cs) {
9126
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9127
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9128
0
        return false;
9129
0
    }
9130
4.60k
    auto l_cs = dynamic_cast<const CoordinateSystem *>(cs->iso_obj.get());
9131
4.60k
    if (!l_cs) {
9132
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateSystem");
9133
0
        return false;
9134
0
    }
9135
4.60k
    const auto &axisList = l_cs->axisList();
9136
4.60k
    if (index < 0 || static_cast<size_t>(index) >= axisList.size()) {
9137
0
        proj_log_error(ctx, __FUNCTION__, "Invalid index");
9138
0
        return false;
9139
0
    }
9140
4.60k
    const auto &axis = axisList[index];
9141
4.60k
    if (out_name) {
9142
0
        *out_name = axis->nameStr().c_str();
9143
0
    }
9144
4.60k
    if (out_abbrev) {
9145
0
        *out_abbrev = axis->abbreviation().c_str();
9146
0
    }
9147
4.60k
    if (out_direction) {
9148
4.60k
        *out_direction = axis->direction().toString().c_str();
9149
4.60k
    }
9150
4.60k
    if (out_unit_conv_factor) {
9151
4.60k
        *out_unit_conv_factor = axis->unit().conversionToSI();
9152
4.60k
    }
9153
4.60k
    if (out_unit_name) {
9154
0
        *out_unit_name = axis->unit().name().c_str();
9155
0
    }
9156
4.60k
    if (out_unit_auth_name) {
9157
0
        *out_unit_auth_name = axis->unit().codeSpace().c_str();
9158
0
    }
9159
4.60k
    if (out_unit_code) {
9160
0
        *out_unit_code = axis->unit().code().c_str();
9161
0
    }
9162
4.60k
    return true;
9163
4.60k
}
9164
9165
// ---------------------------------------------------------------------------
9166
9167
/** \brief Returns a PJ* object whose axis order is the one expected for
9168
 * visualization purposes.
9169
 *
9170
 * The input object must be either:
9171
 * <ul>
9172
 * <li>a coordinate operation, that has been created with
9173
 *     proj_create_crs_to_crs(). If the axis order of its source or target CRS
9174
 *     is northing,easting, then an axis swap operation will be inserted.</li>
9175
 * <li>or a CRS. The axis order of geographic CRS will be longitude, latitude
9176
 *     [,height], and the one of projected CRS will be easting, northing
9177
 *     [, height]</li>
9178
 * </ul>
9179
 *
9180
 * @param ctx PROJ context, or NULL for default context
9181
 * @param obj Object of type CRS, or CoordinateOperation created with
9182
 * proj_create_crs_to_crs() (must not be NULL)
9183
 * @return a new PJ* object to free with proj_destroy() in case of success, or
9184
 * nullptr in case of error
9185
 */
9186
0
PJ *proj_normalize_for_visualization(PJ_CONTEXT *ctx, const PJ *obj) {
9187
9188
0
    SANITIZE_CTX(ctx);
9189
0
    if (!obj->alternativeCoordinateOperations.empty()) {
9190
0
        try {
9191
0
            auto pjNew = std::unique_ptr<PJ>(pj_new());
9192
0
            if (!pjNew)
9193
0
                return nullptr;
9194
0
            pjNew->ctx = ctx;
9195
0
            pjNew->descr = "Set of coordinate operations";
9196
0
            pjNew->left = obj->left;
9197
0
            pjNew->right = obj->right;
9198
0
            pjNew->copyStateFrom(*obj);
9199
9200
0
            for (const auto &alt : obj->alternativeCoordinateOperations) {
9201
0
                auto co = dynamic_cast<const CoordinateOperation *>(
9202
0
                    alt.pj->iso_obj.get());
9203
0
                if (co) {
9204
0
                    double minxSrc = alt.minxSrc;
9205
0
                    double minySrc = alt.minySrc;
9206
0
                    double maxxSrc = alt.maxxSrc;
9207
0
                    double maxySrc = alt.maxySrc;
9208
0
                    double minxDst = alt.minxDst;
9209
0
                    double minyDst = alt.minyDst;
9210
0
                    double maxxDst = alt.maxxDst;
9211
0
                    double maxyDst = alt.maxyDst;
9212
9213
0
                    auto l_sourceCRS = co->sourceCRS();
9214
0
                    auto l_targetCRS = co->targetCRS();
9215
0
                    if (l_sourceCRS && l_targetCRS) {
9216
0
                        const bool swapSource =
9217
0
                            l_sourceCRS
9218
0
                                ->mustAxisOrderBeSwitchedForVisualization();
9219
0
                        if (swapSource) {
9220
0
                            std::swap(minxSrc, minySrc);
9221
0
                            std::swap(maxxSrc, maxySrc);
9222
0
                        }
9223
0
                        const bool swapTarget =
9224
0
                            l_targetCRS
9225
0
                                ->mustAxisOrderBeSwitchedForVisualization();
9226
0
                        if (swapTarget) {
9227
0
                            std::swap(minxDst, minyDst);
9228
0
                            std::swap(maxxDst, maxyDst);
9229
0
                        }
9230
0
                    }
9231
0
                    ctx->forceOver = alt.pj->over != 0;
9232
0
                    auto pjNormalized =
9233
0
                        pj_obj_create(ctx, co->normalizeForVisualization());
9234
0
                    ctx->forceOver = false;
9235
9236
0
                    pjNormalized->copyStateFrom(*(alt.pj));
9237
9238
0
                    pjNew->alternativeCoordinateOperations.emplace_back(
9239
0
                        alt.idxInOriginalList, minxSrc, minySrc, maxxSrc,
9240
0
                        maxySrc, minxDst, minyDst, maxxDst, maxyDst,
9241
0
                        pjNormalized, co->nameStr(), alt.accuracy,
9242
0
                        alt.pseudoArea, alt.areaName.c_str(),
9243
0
                        alt.pjSrcGeocentricToLonLat,
9244
0
                        alt.pjDstGeocentricToLonLat);
9245
0
                }
9246
0
            }
9247
0
            return pjNew.release();
9248
0
        } catch (const std::exception &e) {
9249
0
            ctx->forceOver = false;
9250
0
            proj_log_debug(ctx, __FUNCTION__, e.what());
9251
0
            return nullptr;
9252
0
        }
9253
0
    }
9254
9255
0
    auto crs = dynamic_cast<const CRS *>(obj->iso_obj.get());
9256
0
    if (crs) {
9257
0
        try {
9258
0
            return pj_obj_create(ctx, crs->normalizeForVisualization());
9259
0
        } catch (const std::exception &e) {
9260
0
            proj_log_debug(ctx, __FUNCTION__, e.what());
9261
0
            return nullptr;
9262
0
        }
9263
0
    }
9264
9265
0
    auto co = dynamic_cast<const CoordinateOperation *>(obj->iso_obj.get());
9266
0
    if (!co) {
9267
0
        proj_log_error(ctx, __FUNCTION__,
9268
0
                       "Object is not a CoordinateOperation "
9269
0
                       "created with "
9270
0
                       "proj_create_crs_to_crs");
9271
0
        return nullptr;
9272
0
    }
9273
0
    try {
9274
0
        ctx->forceOver = obj->over != 0;
9275
0
        auto pjNormalized = pj_obj_create(ctx, co->normalizeForVisualization());
9276
0
        pjNormalized->over = obj->over;
9277
0
        ctx->forceOver = false;
9278
0
        return pjNormalized;
9279
0
    } catch (const std::exception &e) {
9280
0
        ctx->forceOver = false;
9281
0
        proj_log_debug(ctx, __FUNCTION__, e.what());
9282
0
        return nullptr;
9283
0
    }
9284
0
}
9285
9286
// ---------------------------------------------------------------------------
9287
9288
/** \brief Returns a PJ* coordinate operation object which represents the
9289
 * inverse operation of the specified coordinate operation.
9290
 *
9291
 * @param ctx PROJ context, or NULL for default context
9292
 * @param obj Object of type CoordinateOperation (must not be NULL)
9293
 * @return a new PJ* object to free with proj_destroy() in case of success, or
9294
 * nullptr in case of error
9295
 * @since 6.3
9296
 */
9297
0
PJ *proj_coordoperation_create_inverse(PJ_CONTEXT *ctx, const PJ *obj) {
9298
9299
0
    SANITIZE_CTX(ctx);
9300
0
    if (!obj) {
9301
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9302
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9303
0
        return nullptr;
9304
0
    }
9305
0
    auto co = dynamic_cast<const CoordinateOperation *>(obj->iso_obj.get());
9306
0
    if (!co) {
9307
0
        proj_log_error(ctx, __FUNCTION__,
9308
0
                       "Object is not a CoordinateOperation");
9309
0
        return nullptr;
9310
0
    }
9311
0
    try {
9312
0
        return pj_obj_create(ctx, co->inverse());
9313
0
    } catch (const std::exception &e) {
9314
0
        proj_log_debug(ctx, __FUNCTION__, e.what());
9315
0
        return nullptr;
9316
0
    }
9317
0
}
9318
9319
// ---------------------------------------------------------------------------
9320
9321
/** \brief Returns the number of steps of a concatenated operation.
9322
 *
9323
 * The input object must be a concatenated operation.
9324
 *
9325
 * @param ctx PROJ context, or NULL for default context
9326
 * @param concatoperation Concatenated operation (must not be NULL)
9327
 * @return the number of steps, or 0 in case of error.
9328
 */
9329
int proj_concatoperation_get_step_count(PJ_CONTEXT *ctx,
9330
0
                                        const PJ *concatoperation) {
9331
0
    SANITIZE_CTX(ctx);
9332
0
    if (!concatoperation) {
9333
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9334
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9335
0
        return false;
9336
0
    }
9337
0
    auto l_co = dynamic_cast<const ConcatenatedOperation *>(
9338
0
        concatoperation->iso_obj.get());
9339
0
    if (!l_co) {
9340
0
        proj_log_error(ctx, __FUNCTION__,
9341
0
                       "Object is not a ConcatenatedOperation");
9342
0
        return false;
9343
0
    }
9344
0
    return static_cast<int>(l_co->operations().size());
9345
0
}
9346
// ---------------------------------------------------------------------------
9347
9348
/** \brief Returns a step of a concatenated operation.
9349
 *
9350
 * The input object must be a concatenated operation.
9351
 *
9352
 * The returned object must be unreferenced with proj_destroy() after
9353
 * use.
9354
 * It should be used by at most one thread at a time.
9355
 *
9356
 * @param ctx PROJ context, or NULL for default context
9357
 * @param concatoperation Concatenated operation (must not be NULL)
9358
 * @param i_step Index of the step to extract. Between 0 and
9359
 *               proj_concatoperation_get_step_count()-1
9360
 * @return Object that must be unreferenced with proj_destroy(), or NULL
9361
 * in case of error.
9362
 */
9363
PJ *proj_concatoperation_get_step(PJ_CONTEXT *ctx, const PJ *concatoperation,
9364
0
                                  int i_step) {
9365
0
    SANITIZE_CTX(ctx);
9366
0
    if (!concatoperation) {
9367
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9368
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9369
0
        return nullptr;
9370
0
    }
9371
0
    auto l_co = dynamic_cast<const ConcatenatedOperation *>(
9372
0
        concatoperation->iso_obj.get());
9373
0
    if (!l_co) {
9374
0
        proj_log_error(ctx, __FUNCTION__,
9375
0
                       "Object is not a ConcatenatedOperation");
9376
0
        return nullptr;
9377
0
    }
9378
0
    const auto &steps = l_co->operations();
9379
0
    if (i_step < 0 || static_cast<size_t>(i_step) >= steps.size()) {
9380
0
        proj_log_error(ctx, __FUNCTION__, "Invalid step index");
9381
0
        return nullptr;
9382
0
    }
9383
0
    return pj_obj_create(ctx, steps[i_step]);
9384
0
}
9385
// ---------------------------------------------------------------------------
9386
9387
/** \brief Opaque object representing an insertion session. */
9388
struct PJ_INSERT_SESSION {
9389
    //! @cond Doxygen_Suppress
9390
    PJ_CONTEXT *ctx = nullptr;
9391
    //!  @endcond
9392
};
9393
9394
// ---------------------------------------------------------------------------
9395
9396
/** \brief Starts a session for proj_get_insert_statements()
9397
 *
9398
 * Starts a new session for one or several calls to
9399
 * proj_get_insert_statements().
9400
 *
9401
 * An insertion session guarantees that the inserted objects will not create
9402
 * conflicting intermediate objects.
9403
 *
9404
 * The session must be stopped with proj_insert_object_session_destroy().
9405
 *
9406
 * Only one session may be active at a time for a given context.
9407
 *
9408
 * @param ctx PROJ context, or NULL for default context
9409
 * @return the session, or NULL in case of error.
9410
 *
9411
 * @since 8.1
9412
 */
9413
0
PJ_INSERT_SESSION *proj_insert_object_session_create(PJ_CONTEXT *ctx) {
9414
0
    SANITIZE_CTX(ctx);
9415
0
    try {
9416
0
        auto dbContext = getDBcontext(ctx);
9417
0
        dbContext->startInsertStatementsSession();
9418
0
        PJ_INSERT_SESSION *session = new PJ_INSERT_SESSION;
9419
0
        session->ctx = ctx;
9420
0
        return session;
9421
0
    } catch (const std::exception &e) {
9422
0
        proj_log_error(ctx, __FUNCTION__, e.what());
9423
0
        return nullptr;
9424
0
    }
9425
0
}
9426
9427
// ---------------------------------------------------------------------------
9428
9429
/** \brief Stops an insertion session started with
9430
 * proj_insert_object_session_create()
9431
 *
9432
 * @param ctx PROJ context, or NULL for default context
9433
 * @param session The insertion session.
9434
 * @since 8.1
9435
 */
9436
void proj_insert_object_session_destroy(PJ_CONTEXT *ctx,
9437
0
                                        PJ_INSERT_SESSION *session) {
9438
0
    SANITIZE_CTX(ctx);
9439
0
    if (session) {
9440
0
        try {
9441
0
            if (session->ctx != ctx) {
9442
0
                proj_log_error(ctx, __FUNCTION__,
9443
0
                               "proj_insert_object_session_destroy() called "
9444
0
                               "with a context different from the one of "
9445
0
                               "proj_insert_object_session_create()");
9446
0
            } else {
9447
0
                auto dbContext = getDBcontext(ctx);
9448
0
                dbContext->stopInsertStatementsSession();
9449
0
            }
9450
0
        } catch (const std::exception &e) {
9451
0
            proj_log_error(ctx, __FUNCTION__, e.what());
9452
0
        }
9453
0
        delete session;
9454
0
    }
9455
0
}
9456
9457
// ---------------------------------------------------------------------------
9458
9459
/** \brief Suggests a database code for the passed object.
9460
 *
9461
 * Supported type of objects are PrimeMeridian, Ellipsoid, Datum, DatumEnsemble,
9462
 * GeodeticCRS, ProjectedCRS, VerticalCRS, CompoundCRS, BoundCRS, Conversion.
9463
 *
9464
 * @param ctx PROJ context, or NULL for default context
9465
 * @param object Object for which to suggest a code.
9466
 * @param authority Authority name into which the object will be inserted.
9467
 * @param numeric_code Whether the code should be numeric, or derived from the
9468
 * object name.
9469
 * @param options NULL terminated list of options, or NULL.
9470
 *                No options are supported currently.
9471
 * @return the suggested code, that is guaranteed to not conflict with an
9472
 * existing one (to be freed with proj_string_destroy),
9473
 * or nullptr in case of error.
9474
 *
9475
 * @since 8.1
9476
 */
9477
char *proj_suggests_code_for(PJ_CONTEXT *ctx, const PJ *object,
9478
                             const char *authority, int numeric_code,
9479
0
                             const char *const *options) {
9480
0
    SANITIZE_CTX(ctx);
9481
0
    (void)options;
9482
9483
0
    if (!object || !authority) {
9484
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9485
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9486
0
        return nullptr;
9487
0
    }
9488
0
    auto identifiedObject =
9489
0
        std::dynamic_pointer_cast<IdentifiedObject>(object->iso_obj);
9490
0
    if (!identifiedObject) {
9491
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9492
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a IdentifiedObject");
9493
0
        return nullptr;
9494
0
    }
9495
9496
0
    try {
9497
0
        auto dbContext = getDBcontext(ctx);
9498
0
        return pj_strdup(dbContext
9499
0
                             ->suggestsCodeFor(NN_NO_CHECK(identifiedObject),
9500
0
                                               std::string(authority),
9501
0
                                               numeric_code != FALSE)
9502
0
                             .c_str());
9503
0
    } catch (const std::exception &e) {
9504
0
        proj_log_error(ctx, __FUNCTION__, e.what());
9505
0
    }
9506
0
    return nullptr;
9507
0
}
9508
9509
// ---------------------------------------------------------------------------
9510
9511
/** \brief Free a string.
9512
 *
9513
 * Only to be used with functions that document using this function.
9514
 *
9515
 * @param str String to free.
9516
 *
9517
 * @since 8.1
9518
 */
9519
0
void proj_string_destroy(char *str) { free(str); }
9520
9521
// ---------------------------------------------------------------------------
9522
9523
/** \brief Returns SQL statements needed to insert the passed object into the
9524
 * database.
9525
 *
9526
 * proj_insert_object_session_create() may have been called previously.
9527
 *
9528
 * It is strongly recommended that new objects should not be added in common
9529
 * registries, such as "EPSG", "ESRI", "IAU", etc. Users should use a custom
9530
 * authority name instead. If a new object should be
9531
 * added to the official EPSG registry, users are invited to follow the
9532
 * procedure explained at https://epsg.org/dataset-change-requests.html.
9533
 *
9534
 * Combined with proj_context_get_database_structure(), users can create
9535
 * auxiliary databases, instead of directly modifying the main proj.db database.
9536
 * Those auxiliary databases can be specified through
9537
 * proj_context_set_database_path() or the PROJ_AUX_DB environment variable.
9538
 *
9539
 * @param ctx PROJ context, or NULL for default context
9540
 * @param session The insertion session. May be NULL if a single object must be
9541
 *                inserted.
9542
 * @param object The object to insert into the database. Currently only
9543
 *               PrimeMeridian, Ellipsoid, Datum, GeodeticCRS, ProjectedCRS,
9544
 *               VerticalCRS, CompoundCRS or BoundCRS are supported.
9545
 * @param authority Authority name into which the object will be inserted.
9546
 *                  Must not be NULL.
9547
 * @param code Code with which the object will be inserted.Must not be NULL.
9548
 * @param numeric_codes Whether intermediate objects that can be created should
9549
 *                      use numeric codes (true), or may be alphanumeric (false)
9550
 * @param allowed_authorities NULL terminated list of authority names, or NULL.
9551
 *                            Authorities to which intermediate objects are
9552
 *                            allowed to refer to. "authority" will be
9553
 *                            implicitly added to it. Note that unit,
9554
 *                            coordinate systems, projection methods and
9555
 *                            parameters will in any case be allowed to refer
9556
 *                            to EPSG.
9557
 *                            If NULL, allowed_authorities defaults to
9558
 *                            {"EPSG", "PROJ", nullptr}
9559
 * @param options NULL terminated list of options, or NULL.
9560
 *                No options are supported currently.
9561
 *
9562
 * @return a list of insert statements (to be freed with
9563
 *         proj_string_list_destroy()), or NULL in case of error.
9564
 * @since 8.1
9565
 */
9566
PROJ_STRING_LIST proj_get_insert_statements(
9567
    PJ_CONTEXT *ctx, PJ_INSERT_SESSION *session, const PJ *object,
9568
    const char *authority, const char *code, int numeric_codes,
9569
0
    const char *const *allowed_authorities, const char *const *options) {
9570
0
    SANITIZE_CTX(ctx);
9571
0
    (void)options;
9572
9573
0
    struct TempSessionHolder {
9574
0
      private:
9575
0
        PJ_CONTEXT *m_ctx;
9576
0
        PJ_INSERT_SESSION *m_tempSession;
9577
0
        TempSessionHolder(const TempSessionHolder &) = delete;
9578
0
        TempSessionHolder &operator=(const TempSessionHolder &) = delete;
9579
9580
0
      public:
9581
0
        TempSessionHolder(PJ_CONTEXT *ctx, PJ_INSERT_SESSION *session)
9582
0
            : m_ctx(ctx),
9583
0
              m_tempSession(session ? nullptr
9584
0
                                    : proj_insert_object_session_create(ctx)) {}
9585
9586
0
        ~TempSessionHolder() {
9587
0
            if (m_tempSession) {
9588
0
                proj_insert_object_session_destroy(m_ctx, m_tempSession);
9589
0
            }
9590
0
        }
9591
9592
0
        inline PJ_INSERT_SESSION *GetTempSession() const {
9593
0
            return m_tempSession;
9594
0
        }
9595
0
    };
9596
9597
0
    try {
9598
0
        TempSessionHolder oHolder(ctx, session);
9599
0
        if (!session) {
9600
0
            session = oHolder.GetTempSession();
9601
0
            if (!session) {
9602
0
                return nullptr;
9603
0
            }
9604
0
        }
9605
9606
0
        if (!object || !authority || !code) {
9607
0
            proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9608
0
            proj_log_error(ctx, __FUNCTION__, "missing required input");
9609
0
            return nullptr;
9610
0
        }
9611
0
        auto identifiedObject =
9612
0
            std::dynamic_pointer_cast<IdentifiedObject>(object->iso_obj);
9613
0
        if (!identifiedObject) {
9614
0
            proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9615
0
            proj_log_error(ctx, __FUNCTION__,
9616
0
                           "Object is not a IdentifiedObject");
9617
0
            return nullptr;
9618
0
        }
9619
9620
0
        auto dbContext = getDBcontext(ctx);
9621
0
        std::vector<std::string> allowedAuthorities{"EPSG", "PROJ"};
9622
0
        if (allowed_authorities) {
9623
0
            allowedAuthorities.clear();
9624
0
            for (auto iter = allowed_authorities; *iter; ++iter) {
9625
0
                allowedAuthorities.emplace_back(*iter);
9626
0
            }
9627
0
        }
9628
0
        auto statements = dbContext->getInsertStatementsFor(
9629
0
            NN_NO_CHECK(identifiedObject), authority, code,
9630
0
            numeric_codes != FALSE, allowedAuthorities);
9631
0
        return to_string_list(std::move(statements));
9632
0
    } catch (const std::exception &e) {
9633
0
        proj_log_error(ctx, __FUNCTION__, e.what());
9634
0
    }
9635
0
    return nullptr;
9636
0
}
9637
9638
// ---------------------------------------------------------------------------
9639
9640
/** \brief Returns a list of geoid models available for that crs
9641
 *
9642
 * The list includes the geoid models connected directly with the crs,
9643
 * or via "Height Depth Reversal" or "Change of Vertical Unit" transformations.
9644
 * The returned list is NULL terminated and must be freed with
9645
 * proj_string_list_destroy().
9646
 *
9647
 * @param ctx Context, or NULL for default context.
9648
 * @param auth_name Authority name (must not be NULL)
9649
 * @param code Object code (must not be NULL)
9650
 * @param options should be set to NULL for now
9651
 * @return list of geoid models names (to be freed with
9652
 * proj_string_list_destroy()), or NULL in case of error.
9653
 * @since 8.1
9654
 */
9655
PROJ_STRING_LIST
9656
proj_get_geoid_models_from_database(PJ_CONTEXT *ctx, const char *auth_name,
9657
                                    const char *code,
9658
0
                                    const char *const *options) {
9659
0
    SANITIZE_CTX(ctx);
9660
0
    if (!auth_name || !code) {
9661
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9662
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9663
0
        return nullptr;
9664
0
    }
9665
0
    (void)options;
9666
0
    try {
9667
0
        const std::string codeStr(code);
9668
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx), auth_name);
9669
0
        auto geoidModels = factory->getGeoidModels(codeStr);
9670
0
        return to_string_list(std::move(geoidModels));
9671
0
    } catch (const std::exception &e) {
9672
0
        proj_log_error(ctx, __FUNCTION__, e.what());
9673
0
    }
9674
0
    return nullptr;
9675
0
}
9676
9677
// ---------------------------------------------------------------------------
9678
9679
/** \brief Instantiate a CoordinateMetadata object
9680
 *
9681
 * @since 9.4
9682
 */
9683
9684
PJ *proj_coordinate_metadata_create(PJ_CONTEXT *ctx, const PJ *crs,
9685
0
                                    double epoch) {
9686
0
    SANITIZE_CTX(ctx);
9687
0
    if (!crs) {
9688
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9689
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9690
0
        return nullptr;
9691
0
    }
9692
0
    auto crsCast = std::dynamic_pointer_cast<CRS>(crs->iso_obj);
9693
0
    if (!crsCast) {
9694
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CRS");
9695
0
        return nullptr;
9696
0
    }
9697
0
    try {
9698
0
        auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
9699
0
        return pj_obj_create(ctx, CoordinateMetadata::create(
9700
0
                                      NN_NO_CHECK(crsCast), epoch, dbContext));
9701
0
    } catch (const std::exception &e) {
9702
0
        proj_log_debug(ctx, __FUNCTION__, e.what());
9703
0
        return nullptr;
9704
0
    }
9705
0
}
9706
9707
// ---------------------------------------------------------------------------
9708
9709
/** \brief Return the coordinate epoch associated with a CoordinateMetadata.
9710
 *
9711
 * It may return a NaN value if there is no associated coordinate epoch.
9712
 *
9713
 * @since 9.2
9714
 */
9715
0
double proj_coordinate_metadata_get_epoch(PJ_CONTEXT *ctx, const PJ *obj) {
9716
0
    SANITIZE_CTX(ctx);
9717
0
    if (!obj) {
9718
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9719
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9720
0
        return std::numeric_limits<double>::quiet_NaN();
9721
0
    }
9722
0
    auto ptr = obj->iso_obj.get();
9723
0
    auto coordinateMetadata = dynamic_cast<const CoordinateMetadata *>(ptr);
9724
0
    if (coordinateMetadata) {
9725
0
        if (coordinateMetadata->coordinateEpoch().has_value()) {
9726
0
            return coordinateMetadata->coordinateEpochAsDecimalYear();
9727
0
        }
9728
0
        return std::numeric_limits<double>::quiet_NaN();
9729
0
    }
9730
0
    proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateMetadata");
9731
0
    return std::numeric_limits<double>::quiet_NaN();
9732
0
}
9733
9734
// ---------------------------------------------------------------------------
9735
9736
/** \brief Return whether a CRS has an associated PointMotionOperation
9737
 *
9738
 * @since 9.4
9739
 */
9740
0
int proj_crs_has_point_motion_operation(PJ_CONTEXT *ctx, const PJ *crs) {
9741
0
    SANITIZE_CTX(ctx);
9742
0
    if (!crs) {
9743
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9744
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9745
0
        return false;
9746
0
    }
9747
0
    auto l_crs = dynamic_cast<const CRS *>(crs->iso_obj.get());
9748
0
    if (!l_crs) {
9749
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CRS");
9750
0
        return false;
9751
0
    }
9752
0
    auto geodeticCRS = l_crs->extractGeodeticCRS();
9753
0
    if (!geodeticCRS)
9754
0
        return false;
9755
0
    try {
9756
0
        auto factory =
9757
0
            AuthorityFactory::create(getDBcontext(ctx), std::string());
9758
0
        return !factory
9759
0
                    ->getPointMotionOperationsFor(NN_NO_CHECK(geodeticCRS),
9760
0
                                                  false)
9761
0
                    .empty();
9762
0
    } catch (const std::exception &e) {
9763
0
        proj_log_error(ctx, __FUNCTION__, e.what());
9764
0
    }
9765
0
    return false;
9766
0
}