Coverage Report

Created: 2026-07-25 06:50

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/proj/src/iso19111/c_api.cpp
Line
Count
Source
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
0
                                          const char *text) {
83
0
    if (ctx->debug_level != PJ_LOG_NONE) {
84
0
        std::string msg(function);
85
0
        msg += ": ";
86
0
        msg += text;
87
0
        ctx->logger(ctx->logger_app_data, PJ_LOG_ERROR, msg.c_str());
88
0
    }
89
0
    auto previous_errno = proj_context_errno(ctx);
90
0
    if (previous_errno == 0) {
91
        // only set errno if it wasn't set deeper down the call stack
92
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER);
93
0
    }
94
0
}
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
0
void proj_context_delete_cpp_context(struct projCppContext *cppContext) {
135
0
    delete cppContext;
136
0
}
137
// ---------------------------------------------------------------------------
138
139
projCppContext::projCppContext(PJ_CONTEXT *ctx, const char *dbPath,
140
                               const std::vector<std::string> &auxDbPaths)
141
0
    : ctx_(ctx), dbPath_(dbPath ? dbPath : std::string()),
142
0
      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
0
projCppContext *projCppContext::clone(PJ_CONTEXT *ctx) const {
158
0
    projCppContext *newContext =
159
0
        new projCppContext(ctx, getDbPath().c_str(), getAuxDbPaths());
160
0
    return newContext;
161
0
}
162
163
// ---------------------------------------------------------------------------
164
165
0
NS_PROJ::io::DatabaseContextNNPtr projCppContext::getDatabaseContext() {
166
0
    if (databaseContext_) {
167
0
        return NN_NO_CHECK(databaseContext_);
168
0
    }
169
0
    auto dbContext =
170
0
        NS_PROJ::io::DatabaseContext::create(dbPath_, auxDbPaths_, ctx_);
171
0
    databaseContext_ = dbContext;
172
0
    return dbContext;
173
0
}
174
175
// ---------------------------------------------------------------------------
176
177
0
static PROJ_NO_INLINE DatabaseContextNNPtr getDBcontext(PJ_CONTEXT *ctx) {
178
0
    return ctx->get_cpp_context()->getDatabaseContext();
179
0
}
180
181
// ---------------------------------------------------------------------------
182
183
static PROJ_NO_INLINE DatabaseContextPtr
184
0
getDBcontextNoException(PJ_CONTEXT *ctx, const char *function) {
185
0
    try {
186
0
        return getDBcontext(ctx).as_nullable();
187
0
    } catch (const std::exception &e) {
188
0
        proj_log_debug(ctx, function, e.what());
189
0
        return nullptr;
190
0
    }
191
0
}
192
// ---------------------------------------------------------------------------
193
194
0
PJ *pj_obj_create(PJ_CONTEXT *ctx, const BaseObjectNNPtr &objIn) {
195
0
    auto coordop = dynamic_cast<const CoordinateOperation *>(objIn.get());
196
0
    if (coordop) {
197
0
        auto singleOp = dynamic_cast<const SingleOperation *>(coordop);
198
0
        bool bTryToExportToProj = true;
199
0
        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
0
        if (bTryToExportToProj) {
207
0
            try {
208
                // Use the database context if already open (e.g. when
209
                // coming from proj_create_from_database), so that
210
                // substitutePROJAlternativeGridNames() can resolve
211
                // grid names via the grid_alternatives table.
212
                // Do NOT open the database here — callers such as
213
                // proj_create() with a plain pipeline string may run
214
                // without proj.db (see commit 63c491eda3).
215
0
                auto dbContext =
216
0
                    ctx->cpp_context
217
0
                        ? ctx->get_cpp_context()->getDatabaseContextIfOpen()
218
0
                        : nullptr;
219
0
                auto formatter = PROJStringFormatter::create(
220
0
                    PROJStringFormatter::Convention::PROJ_5,
221
0
                    std::move(dbContext));
222
0
                auto projString = coordop->exportToPROJString(formatter.get());
223
0
                const bool defer_grid_opening_backup = ctx->defer_grid_opening;
224
0
                if (!defer_grid_opening_backup &&
225
0
                    proj_context_is_network_enabled(ctx)) {
226
0
                    ctx->defer_grid_opening = true;
227
0
                }
228
0
                auto pj = pj_create_internal(ctx, projString.c_str());
229
0
                ctx->defer_grid_opening = defer_grid_opening_backup;
230
0
                if (pj) {
231
0
                    pj->iso_obj = objIn;
232
0
                    pj->iso_obj_is_coordinate_operation = true;
233
0
                    auto sourceEpoch = coordop->sourceCoordinateEpoch();
234
0
                    auto targetEpoch = coordop->targetCoordinateEpoch();
235
0
                    if (sourceEpoch.has_value()) {
236
0
                        if (!targetEpoch.has_value()) {
237
0
                            pj->hasCoordinateEpoch = true;
238
0
                            pj->coordinateEpoch =
239
0
                                sourceEpoch->coordinateEpoch().convertToUnit(
240
0
                                    common::UnitOfMeasure::YEAR);
241
0
                        }
242
0
                    } else {
243
0
                        if (targetEpoch.has_value()) {
244
0
                            pj->hasCoordinateEpoch = true;
245
0
                            pj->coordinateEpoch =
246
0
                                targetEpoch->coordinateEpoch().convertToUnit(
247
0
                                    common::UnitOfMeasure::YEAR);
248
0
                        }
249
0
                    }
250
0
                    return pj;
251
0
                }
252
0
            } catch (const std::exception &) {
253
                // Silence, since we may not always be able to export as a
254
                // PROJ string.
255
0
            }
256
0
        }
257
0
    }
258
0
    auto pj = pj_new();
259
0
    if (pj) {
260
0
        pj->ctx = ctx;
261
0
        pj->descr = "ISO-19111 object";
262
0
        pj->iso_obj = objIn;
263
0
        pj->iso_obj_is_coordinate_operation = coordop != nullptr;
264
0
        try {
265
0
            auto crs = dynamic_cast<const CRS *>(objIn.get());
266
0
            if (crs) {
267
0
                auto geodCRS = crs->extractGeodeticCRS();
268
0
                if (geodCRS) {
269
0
                    const auto &ellps = geodCRS->ellipsoid();
270
0
                    const double a = ellps->semiMajorAxis().getSIValue();
271
0
                    const double es = ellps->squaredEccentricity();
272
0
                    if (!(a > 0 && es >= 0 && es < 1)) {
273
0
                        proj_log_error(pj, _("Invalid ellipsoid parameters"));
274
0
                        proj_errno_set(pj,
275
0
                                       PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
276
0
                        proj_destroy(pj);
277
0
                        return nullptr;
278
0
                    }
279
0
                    pj_calc_ellipsoid_params(pj, a, es);
280
0
                    assert(pj->geod == nullptr);
281
0
                    pj->geod = static_cast<struct geod_geodesic *>(
282
0
                        calloc(1, sizeof(struct geod_geodesic)));
283
0
                    if (pj->geod) {
284
0
                        geod_init(pj->geod, pj->a,
285
0
                                  pj->es / (1 + sqrt(pj->one_es)));
286
0
                    }
287
0
                }
288
0
            }
289
0
        } catch (const std::exception &) {
290
0
        }
291
0
    }
292
0
    return pj;
293
0
}
294
//! @endcond
295
296
// ---------------------------------------------------------------------------
297
298
/** \brief Opaque object representing a set of operation results. */
299
struct PJ_OBJ_LIST {
300
    //! @cond Doxygen_Suppress
301
    std::vector<IdentifiedObjectNNPtr> objects;
302
303
    explicit PJ_OBJ_LIST(std::vector<IdentifiedObjectNNPtr> &&objectsIn)
304
0
        : objects(std::move(objectsIn)) {}
305
    virtual ~PJ_OBJ_LIST();
306
307
    PJ_OBJ_LIST(const PJ_OBJ_LIST &) = delete;
308
    PJ_OBJ_LIST &operator=(const PJ_OBJ_LIST &) = delete;
309
    //! @endcond
310
};
311
312
//! @cond Doxygen_Suppress
313
0
PJ_OBJ_LIST::~PJ_OBJ_LIST() = default;
314
//! @endcond
315
316
// ---------------------------------------------------------------------------
317
318
//! @cond Doxygen_Suppress
319
320
#define SANITIZE_CTX(ctx)                                                      \
321
0
    do {                                                                       \
322
0
        if (ctx == nullptr) {                                                  \
323
0
            ctx = pj_get_default_ctx();                                        \
324
0
        }                                                                      \
325
0
    } while (0)
326
327
//! @endcond
328
329
// ---------------------------------------------------------------------------
330
331
/** \brief Starting with PROJ 8.1, this function does nothing.
332
 *
333
 * If you want to take into account changes to the PROJ database, you need to
334
 * re-create a new context.
335
 *
336
 * @param ctx Ignored
337
 * @param autoclose Ignored
338
 * @since 6.2
339
 * deprecated Since 8.1
340
 */
341
0
void proj_context_set_autoclose_database(PJ_CONTEXT *ctx, int autoclose) {
342
0
    (void)ctx;
343
0
    (void)autoclose;
344
0
}
345
346
// ---------------------------------------------------------------------------
347
348
/** \brief Explicitly point to the main PROJ CRS and coordinate operation
349
 * definition database ("proj.db"), and potentially auxiliary databases with
350
 * same structure.
351
 *
352
 * Starting with PROJ 8.1, if the auxDbPaths parameter is an empty array,
353
 * the PROJ_AUX_DB environment variable will be used, if set.
354
 * It must contain one or several paths. If several paths are
355
 * provided, they must be separated by the colon (:) character on Unix, and
356
 * on Windows, by the semi-colon (;) character.
357
 *
358
 * @param ctx PROJ context, or NULL for default context
359
 * @param dbPath Path to main database, or NULL for default.
360
 * @param auxDbPaths NULL-terminated list of auxiliary database filenames, or
361
 * NULL.
362
 * @param options should be set to NULL for now
363
 * @return TRUE in case of success
364
 */
365
int proj_context_set_database_path(PJ_CONTEXT *ctx, const char *dbPath,
366
                                   const char *const *auxDbPaths,
367
0
                                   const char *const *options) {
368
0
    SANITIZE_CTX(ctx);
369
0
    (void)options;
370
0
    std::string osPrevDbPath;
371
0
    std::vector<std::string> osPrevAuxDbPaths;
372
0
    if (ctx->cpp_context) {
373
0
        osPrevDbPath = ctx->cpp_context->getDbPath();
374
0
        osPrevAuxDbPaths = ctx->cpp_context->getAuxDbPaths();
375
0
    }
376
0
    delete ctx->cpp_context;
377
0
    ctx->cpp_context = nullptr;
378
0
    try {
379
0
        ctx->cpp_context = new projCppContext(
380
0
            ctx, dbPath, projCppContext::toVector(auxDbPaths));
381
0
        ctx->cpp_context->getDatabaseContext();
382
0
        return true;
383
0
    } catch (const std::exception &e) {
384
0
        proj_log_error(ctx, __FUNCTION__, e.what());
385
0
        delete ctx->cpp_context;
386
0
        ctx->cpp_context =
387
0
            new projCppContext(ctx, osPrevDbPath.c_str(), osPrevAuxDbPaths);
388
0
        return false;
389
0
    }
390
0
}
391
392
// ---------------------------------------------------------------------------
393
394
/** \brief Returns the path to the database.
395
 *
396
 * The returned pointer remains valid while ctx is valid, and until
397
 * proj_context_set_database_path() is called.
398
 *
399
 * @param ctx PROJ context, or NULL for default context
400
 * @return path, or nullptr
401
 */
402
0
const char *proj_context_get_database_path(PJ_CONTEXT *ctx) {
403
0
    SANITIZE_CTX(ctx);
404
0
    try {
405
        // temporary variable must be used as getDBcontext() might create
406
        // ctx->cpp_context
407
0
        const std::string osPath(getDBcontext(ctx)->getPath());
408
0
        ctx->get_cpp_context()->lastDbPath_ = osPath;
409
0
        return ctx->cpp_context->lastDbPath_.c_str();
410
0
    } catch (const std::exception &e) {
411
0
        proj_log_error(ctx, __FUNCTION__, e.what());
412
0
        return nullptr;
413
0
    }
414
0
}
415
416
// ---------------------------------------------------------------------------
417
418
/** \brief Return a metadata from the database.
419
 *
420
 * The returned pointer remains valid while ctx is valid, and until
421
 * proj_context_get_database_metadata() is called.
422
 *
423
 * Available keys:
424
 *
425
 * - DATABASE.LAYOUT.VERSION.MAJOR
426
 * - DATABASE.LAYOUT.VERSION.MINOR
427
 * - EPSG.VERSION
428
 * - EPSG.DATE
429
 * - ESRI.VERSION
430
 * - ESRI.DATE
431
 * - IGNF.SOURCE
432
 * - IGNF.VERSION
433
 * - IGNF.DATE
434
 * - NKG.SOURCE
435
 * - NKG.VERSION
436
 * - NKG.DATE
437
 * - PROJ.VERSION
438
 * - PROJ_DATA.VERSION : PROJ-data version most compatible with this database.
439
 *
440
 *
441
 * @param ctx PROJ context, or NULL for default context
442
 * @param key Metadata key. Must not be NULL
443
 * @return value, or nullptr
444
 */
445
const char *proj_context_get_database_metadata(PJ_CONTEXT *ctx,
446
0
                                               const char *key) {
447
0
    SANITIZE_CTX(ctx);
448
0
    if (!key) {
449
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
450
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
451
0
        return nullptr;
452
0
    }
453
0
    try {
454
        // temporary variable must be used as getDBcontext() might create
455
        // ctx->cpp_context
456
0
        auto osVal(getDBcontext(ctx)->getMetadata(key));
457
0
        if (osVal == nullptr) {
458
0
            return nullptr;
459
0
        }
460
0
        ctx->get_cpp_context()->lastDbMetadataItem_ = osVal;
461
0
        return ctx->cpp_context->lastDbMetadataItem_.c_str();
462
0
    } catch (const std::exception &e) {
463
0
        proj_log_error(ctx, __FUNCTION__, e.what());
464
0
        return nullptr;
465
0
    }
466
0
}
467
468
// ---------------------------------------------------------------------------
469
470
/** \brief Return the database structure
471
 *
472
 * Return SQL statements to run to initiate a new valid auxiliary empty
473
 * database. It contains definitions of tables, views and triggers, as well
474
 * as metadata for the version of the layout of the database.
475
 *
476
 * @param ctx PROJ context, or NULL for default context
477
 * @param options null-terminated list of options, or NULL. None currently.
478
 * @return list of SQL statements (to be freed with proj_string_list_destroy()),
479
 *         or NULL in case of error.
480
 * @since 8.1
481
 */
482
PROJ_STRING_LIST
483
proj_context_get_database_structure(PJ_CONTEXT *ctx,
484
0
                                    const char *const *options) {
485
0
    SANITIZE_CTX(ctx);
486
0
    (void)options;
487
0
    try {
488
0
        auto ret = to_string_list(getDBcontext(ctx)->getDatabaseStructure());
489
0
        return ret;
490
0
    } catch (const std::exception &e) {
491
0
        proj_log_error(ctx, __FUNCTION__, e.what());
492
0
        return nullptr;
493
0
    }
494
0
}
495
496
// ---------------------------------------------------------------------------
497
498
/** \brief Guess the "dialect" of the WKT string.
499
 *
500
 * @param ctx PROJ context, or NULL for default context
501
 * @param wkt String (must not be NULL)
502
 */
503
PJ_GUESSED_WKT_DIALECT proj_context_guess_wkt_dialect(PJ_CONTEXT *ctx,
504
0
                                                      const char *wkt) {
505
0
    (void)ctx;
506
0
    if (!wkt) {
507
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
508
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
509
0
        return PJ_GUESSED_NOT_WKT;
510
0
    }
511
0
    switch (WKTParser().guessDialect(wkt)) {
512
0
    case WKTParser::WKTGuessedDialect::WKT2_2019:
513
0
        return PJ_GUESSED_WKT2_2019;
514
0
    case WKTParser::WKTGuessedDialect::WKT2_2015:
515
0
        return PJ_GUESSED_WKT2_2015;
516
0
    case WKTParser::WKTGuessedDialect::WKT1_GDAL:
517
0
        return PJ_GUESSED_WKT1_GDAL;
518
0
    case WKTParser::WKTGuessedDialect::WKT1_ESRI:
519
0
        return PJ_GUESSED_WKT1_ESRI;
520
0
    case WKTParser::WKTGuessedDialect::NOT_WKT:
521
0
        break;
522
0
    }
523
0
    return PJ_GUESSED_NOT_WKT;
524
0
}
525
526
// ---------------------------------------------------------------------------
527
528
//! @cond Doxygen_Suppress
529
static const char *getOptionValue(const char *option,
530
0
                                  const char *keyWithEqual) noexcept {
531
0
    if (ci_starts_with(option, keyWithEqual)) {
532
0
        return option + strlen(keyWithEqual);
533
0
    }
534
0
    return nullptr;
535
0
}
536
//! @endcond
537
538
// ---------------------------------------------------------------------------
539
540
/** \brief "Clone" an object.
541
 *
542
 * The object might be used independently of the original object, provided that
543
 * the use of context is compatible. In particular if you intend to use a
544
 * clone in a different thread than the original object, you should pass a
545
 * context that is different from the one of the original object (or later
546
 * assign a different context with proj_assign_context()).
547
 *
548
 * The returned object must be unreferenced with proj_destroy() after use.
549
 * It should be used by at most one thread at a time.
550
 *
551
 * @param ctx PROJ context, or NULL for default context
552
 * @param obj Object to clone. Must not be NULL.
553
 * @return Object that must be unreferenced with proj_destroy(), or NULL in
554
 * case of error.
555
 */
556
0
PJ *proj_clone(PJ_CONTEXT *ctx, const PJ *obj) {
557
0
    SANITIZE_CTX(ctx);
558
0
    if (!obj) {
559
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
560
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
561
0
        return nullptr;
562
0
    }
563
0
    if (!obj->iso_obj) {
564
0
        if (!obj->alternativeCoordinateOperations.empty()) {
565
0
            auto newPj = pj_new();
566
0
            if (newPj) {
567
0
                newPj->descr = "Set of coordinate operations";
568
0
                newPj->ctx = ctx;
569
0
                newPj->copyStateFrom(*obj);
570
0
                ctx->forceOver = obj->over != 0;
571
0
                const int old_debug_level = ctx->debug_level;
572
0
                ctx->debug_level = PJ_LOG_NONE;
573
0
                for (const auto &altOp : obj->alternativeCoordinateOperations) {
574
0
                    newPj->alternativeCoordinateOperations.emplace_back(
575
0
                        PJCoordOperation(ctx, altOp));
576
0
                }
577
0
                ctx->forceOver = false;
578
0
                ctx->debug_level = old_debug_level;
579
0
            }
580
0
            return newPj;
581
0
        }
582
0
        return nullptr;
583
0
    }
584
0
    try {
585
0
        ctx->forceOver = obj->over != 0;
586
0
        PJ *newPj = pj_obj_create(ctx, NN_NO_CHECK(obj->iso_obj));
587
0
        ctx->forceOver = false;
588
0
        if (newPj) {
589
0
            newPj->copyStateFrom(*obj);
590
0
        }
591
0
        return newPj;
592
0
    } catch (const std::exception &e) {
593
0
        proj_log_error(ctx, __FUNCTION__, e.what());
594
0
    }
595
0
    return nullptr;
596
0
}
597
598
// ---------------------------------------------------------------------------
599
600
/** \brief Instantiate an object from a WKT string, PROJ string, object code
601
 * (like "EPSG:4326", "urn:ogc:def:crs:EPSG::4326",
602
 * "urn:ogc:def:coordinateOperation:EPSG::1671"), a PROJJSON string, an object
603
 * name (e.g "WGS 84") of a compound CRS build from object names
604
 * (e.g "WGS 84 + EGM96 height")
605
 *
606
 * This function calls osgeo::proj::io::createFromUserInput()
607
 *
608
 * The returned object must be unreferenced with proj_destroy() after use.
609
 * It should be used by at most one thread at a time.
610
 *
611
 * @param ctx PROJ context, or NULL for default context
612
 * @param text String (must not be NULL)
613
 * @return Object that must be unreferenced with proj_destroy(), or NULL in
614
 * case of error.
615
 */
616
0
PJ *proj_create(PJ_CONTEXT *ctx, const char *text) {
617
0
    SANITIZE_CTX(ctx);
618
0
    if (!text) {
619
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
620
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
621
0
        return nullptr;
622
0
    }
623
624
    // Only connect to proj.db if needed
625
0
    if (strstr(text, "proj=") == nullptr || strstr(text, "init=") != nullptr) {
626
0
        getDBcontextNoException(ctx, __FUNCTION__);
627
0
    }
628
0
    try {
629
0
        auto obj =
630
0
            nn_dynamic_pointer_cast<BaseObject>(createFromUserInput(text, ctx));
631
0
        if (obj) {
632
0
            return pj_obj_create(ctx, NN_NO_CHECK(obj));
633
0
        }
634
0
    } catch (const io::ParsingException &e) {
635
0
        if (proj_context_errno(ctx) == 0) {
636
0
            proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_WRONG_SYNTAX);
637
0
        }
638
0
        proj_log_error(ctx, __FUNCTION__, e.what());
639
0
    } catch (const NoSuchAuthorityCodeException &e) {
640
0
        proj_log_error(ctx, __FUNCTION__,
641
0
                       std::string(e.what())
642
0
                           .append(": ")
643
0
                           .append(e.getAuthority())
644
0
                           .append(":")
645
0
                           .append(e.getAuthorityCode())
646
0
                           .c_str());
647
0
    } catch (const std::exception &e) {
648
0
        proj_log_error(ctx, __FUNCTION__, e.what());
649
0
    }
650
0
    return nullptr;
651
0
}
652
653
// ---------------------------------------------------------------------------
654
655
/** \brief Instantiate an object from a WKT string.
656
 *
657
 * This function calls osgeo::proj::io::WKTParser::createFromWKT()
658
 *
659
 * The returned object must be unreferenced with proj_destroy() after use.
660
 * It should be used by at most one thread at a time.
661
 *
662
 * The distinction between warnings and grammar errors is somewhat artificial
663
 * and does not tell much about the real criticity of the non-compliance.
664
 * Some warnings may be more concerning than some grammar errors. Human
665
 * expertise (or, by the time this comment will be read, specialized AI) is
666
 * generally needed to perform that assessment.
667
 *
668
 * @param ctx PROJ context, or NULL for default context
669
 * @param wkt WKT string (must not be NULL)
670
 * @param options null-terminated list of options, or NULL. Currently
671
 * supported options are:
672
 * <ul>
673
 * <li>STRICT=YES/NO. Defaults to NO. When set to YES, strict validation will
674
 * be enabled.</li>
675
 * <li>UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF=YES/NO. Defaults to YES.
676
 *     When set to YES, object identifiers are unset when there is
677
 *     a contradiction between the definition from WKT and the one from
678
 *     the database./<li>
679
 * </ul>
680
 * @param out_warnings Pointer to a PROJ_STRING_LIST object, or NULL.
681
 * If provided, *out_warnings will contain a list of warnings, typically for
682
 * non recognized projection method or parameters, or other issues found during
683
 * WKT analys. It must be freed with proj_string_list_destroy().
684
 * @param out_grammar_errors Pointer to a PROJ_STRING_LIST object, or NULL.
685
 * If provided, *out_grammar_errors will contain a list of errors regarding the
686
 * WKT grammar. It must be freed with proj_string_list_destroy().
687
 * @return Object that must be unreferenced with proj_destroy(), or NULL in
688
 * case of error.
689
 */
690
PJ *proj_create_from_wkt(PJ_CONTEXT *ctx, const char *wkt,
691
                         const char *const *options,
692
                         PROJ_STRING_LIST *out_warnings,
693
0
                         PROJ_STRING_LIST *out_grammar_errors) {
694
0
    SANITIZE_CTX(ctx);
695
0
    if (!wkt) {
696
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
697
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
698
0
        return nullptr;
699
0
    }
700
701
0
    if (out_warnings) {
702
0
        *out_warnings = nullptr;
703
0
    }
704
0
    if (out_grammar_errors) {
705
0
        *out_grammar_errors = nullptr;
706
0
    }
707
708
0
    try {
709
0
        WKTParser parser;
710
0
        auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
711
0
        if (dbContext) {
712
0
            parser.attachDatabaseContext(NN_NO_CHECK(dbContext));
713
0
        }
714
0
        parser.setStrict(false);
715
0
        for (auto iter = options; iter && iter[0]; ++iter) {
716
0
            const char *value;
717
0
            if ((value = getOptionValue(*iter, "STRICT="))) {
718
0
                parser.setStrict(ci_equal(value, "YES"));
719
0
            } else if ((value = getOptionValue(
720
0
                            *iter, "UNSET_IDENTIFIERS_IF_INCOMPATIBLE_DEF="))) {
721
0
                parser.setUnsetIdentifiersIfIncompatibleDef(
722
0
                    ci_equal(value, "YES"));
723
0
            } else {
724
0
                std::string msg("Unknown option :");
725
0
                msg += *iter;
726
0
                proj_log_error(ctx, __FUNCTION__, msg.c_str());
727
0
                return nullptr;
728
0
            }
729
0
        }
730
0
        auto obj = parser.createFromWKT(wkt);
731
732
0
        if (out_grammar_errors) {
733
0
            auto grammarErrors = parser.grammarErrorList();
734
0
            if (!grammarErrors.empty()) {
735
0
                *out_grammar_errors = to_string_list(grammarErrors);
736
0
            }
737
0
        }
738
739
0
        if (out_warnings) {
740
0
            auto warnings = parser.warningList();
741
0
            auto derivedCRS = dynamic_cast<const crs::DerivedCRS *>(obj.get());
742
0
            if (derivedCRS) {
743
0
                auto extraWarnings =
744
0
                    derivedCRS->derivingConversionRef()->validateParameters();
745
0
                warnings.insert(warnings.end(), extraWarnings.begin(),
746
0
                                extraWarnings.end());
747
0
            } else {
748
0
                auto singleOp =
749
0
                    dynamic_cast<const operation::SingleOperation *>(obj.get());
750
0
                if (singleOp) {
751
0
                    auto extraWarnings = singleOp->validateParameters();
752
0
                    warnings.insert(warnings.end(), extraWarnings.begin(),
753
0
                                    extraWarnings.end());
754
0
                }
755
0
            }
756
0
            if (!warnings.empty()) {
757
0
                *out_warnings = to_string_list(warnings);
758
0
            }
759
0
        }
760
761
0
        return pj_obj_create(ctx, NN_NO_CHECK(obj));
762
0
    } catch (const std::exception &e) {
763
0
        if (out_grammar_errors) {
764
0
            std::list<std::string> exc{e.what()};
765
0
            try {
766
0
                *out_grammar_errors = to_string_list(exc);
767
0
            } catch (const std::exception &) {
768
0
                proj_log_error(ctx, __FUNCTION__, e.what());
769
0
            }
770
0
        } else {
771
0
            proj_log_error(ctx, __FUNCTION__, e.what());
772
0
        }
773
0
    }
774
0
    return nullptr;
775
0
}
776
777
// ---------------------------------------------------------------------------
778
779
/** \brief Instantiate an object from a database lookup.
780
 *
781
 * The returned object must be unreferenced with proj_destroy() after use.
782
 * It should be used by at most one thread at a time.
783
 *
784
 * @param ctx Context, or NULL for default context.
785
 * @param auth_name Authority name (must not be NULL)
786
 * @param code Object code (must not be NULL)
787
 * @param category Object category
788
 * @param usePROJAlternativeGridNames Whether PROJ alternative grid names
789
 * should be substituted to the official grid names. Only used on
790
 * transformations
791
 * @param options should be set to NULL for now
792
 * @return Object that must be unreferenced with proj_destroy(), or NULL in
793
 * case of error.
794
 */
795
PJ *proj_create_from_database(PJ_CONTEXT *ctx, const char *auth_name,
796
                              const char *code, PJ_CATEGORY category,
797
                              int usePROJAlternativeGridNames,
798
0
                              const char *const *options) {
799
0
    SANITIZE_CTX(ctx);
800
0
    if (!auth_name || !code) {
801
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
802
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
803
0
        return nullptr;
804
0
    }
805
0
    (void)options;
806
0
    try {
807
0
        const std::string codeStr(code);
808
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx), auth_name);
809
0
        IdentifiedObjectPtr obj;
810
0
        switch (category) {
811
0
        case PJ_CATEGORY_ELLIPSOID:
812
0
            obj = factory->createEllipsoid(codeStr).as_nullable();
813
0
            break;
814
0
        case PJ_CATEGORY_PRIME_MERIDIAN:
815
0
            obj = factory->createPrimeMeridian(codeStr).as_nullable();
816
0
            break;
817
0
        case PJ_CATEGORY_DATUM:
818
0
            obj = factory->createDatum(codeStr).as_nullable();
819
0
            break;
820
0
        case PJ_CATEGORY_CRS:
821
0
            obj =
822
0
                factory->createCoordinateReferenceSystem(codeStr).as_nullable();
823
0
            break;
824
0
        case PJ_CATEGORY_COORDINATE_OPERATION:
825
0
            obj = factory
826
0
                      ->createCoordinateOperation(
827
0
                          codeStr, usePROJAlternativeGridNames != 0)
828
0
                      .as_nullable();
829
0
            break;
830
0
        case PJ_CATEGORY_DATUM_ENSEMBLE:
831
0
            obj = factory->createDatumEnsemble(codeStr).as_nullable();
832
0
            break;
833
0
        }
834
0
        return pj_obj_create(ctx, NN_NO_CHECK(obj));
835
0
    } catch (const NoSuchAuthorityCodeException &e) {
836
0
        proj_log_error(ctx, __FUNCTION__,
837
0
                       std::string(e.what())
838
0
                           .append(": ")
839
0
                           .append(e.getAuthority())
840
0
                           .append(":")
841
0
                           .append(e.getAuthorityCode())
842
0
                           .c_str());
843
0
    } catch (const std::exception &e) {
844
0
        proj_log_error(ctx, __FUNCTION__, e.what());
845
0
    }
846
0
    return nullptr;
847
0
}
848
849
// ---------------------------------------------------------------------------
850
851
//! @cond Doxygen_Suppress
852
static const char *get_unit_category(const std::string &unit_name,
853
0
                                     UnitOfMeasure::Type type) {
854
0
    const char *ret = nullptr;
855
0
    switch (type) {
856
0
    case UnitOfMeasure::Type::UNKNOWN:
857
0
        ret = "unknown";
858
0
        break;
859
0
    case UnitOfMeasure::Type::NONE:
860
0
        ret = "none";
861
0
        break;
862
0
    case UnitOfMeasure::Type::ANGULAR:
863
0
        ret = unit_name.find(" per ") != std::string::npos ? "angular_per_time"
864
0
                                                           : "angular";
865
0
        break;
866
0
    case UnitOfMeasure::Type::LINEAR:
867
0
        ret = unit_name.find(" per ") != std::string::npos ? "linear_per_time"
868
0
                                                           : "linear";
869
0
        break;
870
0
    case UnitOfMeasure::Type::SCALE:
871
0
        ret = unit_name.find(" per year") != std::string::npos ||
872
0
                      unit_name.find(" per second") != std::string::npos
873
0
                  ? "scale_per_time"
874
0
                  : "scale";
875
0
        break;
876
0
    case UnitOfMeasure::Type::TIME:
877
0
        ret = "time";
878
0
        break;
879
0
    case UnitOfMeasure::Type::PARAMETRIC:
880
0
        ret = unit_name.find(" per ") != std::string::npos
881
0
                  ? "parametric_per_time"
882
0
                  : "parametric";
883
0
        break;
884
0
    }
885
0
    return ret;
886
0
}
887
//! @endcond
888
889
// ---------------------------------------------------------------------------
890
891
/** \brief Get information for a unit of measure from a database lookup.
892
 *
893
 * @param ctx Context, or NULL for default context.
894
 * @param auth_name Authority name (must not be NULL)
895
 * @param code Unit of measure code (must not be NULL)
896
 * @param out_name Pointer to a string value to store the parameter name. or
897
 * NULL. This value remains valid until the next call to
898
 * proj_uom_get_info_from_database() or the context destruction.
899
 * @param out_conv_factor Pointer to a value to store the conversion
900
 * factor of the prime meridian longitude unit to radian. or NULL
901
 * @param out_category Pointer to a string value to store the parameter name. or
902
 * NULL. This value might be "unknown", "none", "linear", "linear_per_time",
903
 * "angular", "angular_per_time", "scale", "scale_per_time", "time",
904
 * "parametric" or "parametric_per_time"
905
 * @return TRUE in case of success
906
 */
907
int proj_uom_get_info_from_database(PJ_CONTEXT *ctx, const char *auth_name,
908
                                    const char *code, const char **out_name,
909
                                    double *out_conv_factor,
910
0
                                    const char **out_category) {
911
912
0
    SANITIZE_CTX(ctx);
913
0
    if (!auth_name || !code) {
914
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
915
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
916
0
        return false;
917
0
    }
918
0
    try {
919
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx), auth_name);
920
0
        auto obj = factory->createUnitOfMeasure(code);
921
0
        if (out_name) {
922
0
            ctx->get_cpp_context()->lastUOMName_ = obj->name();
923
0
            *out_name = ctx->cpp_context->lastUOMName_.c_str();
924
0
        }
925
0
        if (out_conv_factor) {
926
0
            *out_conv_factor = obj->conversionToSI();
927
0
        }
928
0
        if (out_category) {
929
0
            *out_category = get_unit_category(obj->name(), obj->type());
930
0
        }
931
0
        return true;
932
0
    } catch (const std::exception &e) {
933
0
        proj_log_error(ctx, __FUNCTION__, e.what());
934
0
    }
935
0
    return false;
936
0
}
937
938
// ---------------------------------------------------------------------------
939
940
/** \brief Get information for a grid from a database lookup.
941
 *
942
 * @param ctx Context, or NULL for default context.
943
 * @param grid_name Grid name (must not be NULL)
944
 * @param out_full_name Pointer to a string value to store the grid full
945
 * filename. or NULL
946
 * @param out_package_name Pointer to a string value to store the package name
947
 * where
948
 * the grid might be found. or NULL
949
 * @param out_url Pointer to a string value to store the grid URL or the
950
 * package URL where the grid might be found. or NULL
951
 * @param out_direct_download Pointer to a int (boolean) value to store whether
952
 * *out_url can be downloaded directly. or NULL
953
 * @param out_open_license Pointer to a int (boolean) value to store whether
954
 * the grid is released with an open license. or NULL
955
 * @param out_available Pointer to a int (boolean) value to store whether the
956
 * grid is available at runtime. or NULL
957
 * @return TRUE in case of success.
958
 */
959
int proj_grid_get_info_from_database(
960
    PJ_CONTEXT *ctx, const char *grid_name, const char **out_full_name,
961
    const char **out_package_name, const char **out_url,
962
0
    int *out_direct_download, int *out_open_license, int *out_available) {
963
0
    SANITIZE_CTX(ctx);
964
0
    if (!grid_name) {
965
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
966
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
967
0
        return false;
968
0
    }
969
0
    try {
970
0
        auto db_context = getDBcontext(ctx);
971
0
        bool direct_download;
972
0
        bool open_license;
973
0
        bool available;
974
0
        if (!db_context->lookForGridInfo(
975
0
                grid_name, false, ctx->get_cpp_context()->lastGridFullName_,
976
0
                ctx->get_cpp_context()->lastGridPackageName_,
977
0
                ctx->get_cpp_context()->lastGridUrl_, direct_download,
978
0
                open_license, available)) {
979
0
            return false;
980
0
        }
981
982
0
        if (out_full_name)
983
0
            *out_full_name = ctx->get_cpp_context()->lastGridFullName_.c_str();
984
0
        if (out_package_name)
985
0
            *out_package_name =
986
0
                ctx->get_cpp_context()->lastGridPackageName_.c_str();
987
0
        if (out_url)
988
0
            *out_url = ctx->get_cpp_context()->lastGridUrl_.c_str();
989
0
        if (out_direct_download)
990
0
            *out_direct_download = direct_download ? 1 : 0;
991
0
        if (out_open_license)
992
0
            *out_open_license = open_license ? 1 : 0;
993
0
        if (out_available)
994
0
            *out_available = available ? 1 : 0;
995
996
0
        return true;
997
0
    } catch (const std::exception &e) {
998
0
        proj_log_error(ctx, __FUNCTION__, e.what());
999
0
    }
1000
0
    return false;
1001
0
}
1002
1003
// ---------------------------------------------------------------------------
1004
1005
/** \brief Return GeodeticCRS that use the specified datum.
1006
 *
1007
 * @param ctx Context, or NULL for default context.
1008
 * @param crs_auth_name CRS authority name, or NULL.
1009
 * @param datum_auth_name Datum authority name (must not be NULL)
1010
 * @param datum_code Datum code (must not be NULL)
1011
 * @param crs_type "geographic 2D", "geographic 3D", "geocentric" or NULL
1012
 * @return a result set that must be unreferenced with
1013
 * proj_list_destroy(), or NULL in case of error.
1014
 */
1015
PJ_OBJ_LIST *proj_query_geodetic_crs_from_datum(PJ_CONTEXT *ctx,
1016
                                                const char *crs_auth_name,
1017
                                                const char *datum_auth_name,
1018
                                                const char *datum_code,
1019
0
                                                const char *crs_type) {
1020
0
    SANITIZE_CTX(ctx);
1021
0
    if (!datum_auth_name || !datum_code) {
1022
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
1023
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
1024
0
        return nullptr;
1025
0
    }
1026
0
    try {
1027
0
        auto factory = AuthorityFactory::create(
1028
0
            getDBcontext(ctx), crs_auth_name ? crs_auth_name : "");
1029
0
        auto res = factory->createGeodeticCRSFromDatum(
1030
0
            datum_auth_name, datum_code, crs_type ? crs_type : "");
1031
0
        std::vector<IdentifiedObjectNNPtr> objects;
1032
0
        for (const auto &obj : res) {
1033
0
            objects.push_back(obj);
1034
0
        }
1035
0
        return new PJ_OBJ_LIST(std::move(objects));
1036
0
    } catch (const std::exception &e) {
1037
0
        proj_log_error(ctx, __FUNCTION__, e.what());
1038
0
    }
1039
0
    return nullptr;
1040
0
}
1041
1042
// ---------------------------------------------------------------------------
1043
1044
//! @cond Doxygen_Suppress
1045
static AuthorityFactory::ObjectType
1046
0
convertPJObjectTypeToObjectType(PJ_TYPE type, bool &valid) {
1047
0
    valid = true;
1048
0
    AuthorityFactory::ObjectType cppType = AuthorityFactory::ObjectType::CRS;
1049
0
    switch (type) {
1050
0
    case PJ_TYPE_ELLIPSOID:
1051
0
        cppType = AuthorityFactory::ObjectType::ELLIPSOID;
1052
0
        break;
1053
1054
0
    case PJ_TYPE_PRIME_MERIDIAN:
1055
0
        cppType = AuthorityFactory::ObjectType::PRIME_MERIDIAN;
1056
0
        break;
1057
1058
0
    case PJ_TYPE_GEODETIC_REFERENCE_FRAME:
1059
0
        cppType = AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME;
1060
0
        break;
1061
1062
0
    case PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME:
1063
0
        cppType =
1064
0
            AuthorityFactory::ObjectType::DYNAMIC_GEODETIC_REFERENCE_FRAME;
1065
0
        break;
1066
1067
0
    case PJ_TYPE_VERTICAL_REFERENCE_FRAME:
1068
0
        cppType = AuthorityFactory::ObjectType::VERTICAL_REFERENCE_FRAME;
1069
0
        break;
1070
1071
0
    case PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME:
1072
0
        cppType =
1073
0
            AuthorityFactory::ObjectType::DYNAMIC_VERTICAL_REFERENCE_FRAME;
1074
0
        break;
1075
1076
0
    case PJ_TYPE_DATUM_ENSEMBLE:
1077
0
        cppType = AuthorityFactory::ObjectType::DATUM_ENSEMBLE;
1078
0
        break;
1079
1080
0
    case PJ_TYPE_TEMPORAL_DATUM:
1081
0
        valid = false;
1082
0
        break;
1083
1084
0
    case PJ_TYPE_ENGINEERING_DATUM:
1085
0
        cppType = AuthorityFactory::ObjectType::ENGINEERING_DATUM;
1086
0
        break;
1087
1088
0
    case PJ_TYPE_PARAMETRIC_DATUM:
1089
0
        valid = false;
1090
0
        break;
1091
1092
0
    case PJ_TYPE_CRS:
1093
0
        cppType = AuthorityFactory::ObjectType::CRS;
1094
0
        break;
1095
1096
0
    case PJ_TYPE_GEODETIC_CRS:
1097
0
        cppType = AuthorityFactory::ObjectType::GEODETIC_CRS;
1098
0
        break;
1099
1100
0
    case PJ_TYPE_GEOCENTRIC_CRS:
1101
0
        cppType = AuthorityFactory::ObjectType::GEOCENTRIC_CRS;
1102
0
        break;
1103
1104
0
    case PJ_TYPE_GEOGRAPHIC_CRS:
1105
0
        cppType = AuthorityFactory::ObjectType::GEOGRAPHIC_CRS;
1106
0
        break;
1107
1108
0
    case PJ_TYPE_GEOGRAPHIC_2D_CRS:
1109
0
        cppType = AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS;
1110
0
        break;
1111
1112
0
    case PJ_TYPE_GEOGRAPHIC_3D_CRS:
1113
0
        cppType = AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS;
1114
0
        break;
1115
1116
0
    case PJ_TYPE_VERTICAL_CRS:
1117
0
        cppType = AuthorityFactory::ObjectType::VERTICAL_CRS;
1118
0
        break;
1119
1120
0
    case PJ_TYPE_PROJECTED_CRS:
1121
0
        cppType = AuthorityFactory::ObjectType::PROJECTED_CRS;
1122
0
        break;
1123
1124
0
    case PJ_TYPE_DERIVED_PROJECTED_CRS:
1125
0
        valid = false;
1126
0
        break;
1127
1128
0
    case PJ_TYPE_COMPOUND_CRS:
1129
0
        cppType = AuthorityFactory::ObjectType::COMPOUND_CRS;
1130
0
        break;
1131
1132
0
    case PJ_TYPE_ENGINEERING_CRS:
1133
0
        cppType = AuthorityFactory::ObjectType::ENGINEERING_CRS;
1134
0
        break;
1135
1136
0
    case PJ_TYPE_TEMPORAL_CRS:
1137
0
        valid = false;
1138
0
        break;
1139
1140
0
    case PJ_TYPE_BOUND_CRS:
1141
0
        valid = false;
1142
0
        break;
1143
1144
0
    case PJ_TYPE_OTHER_CRS:
1145
0
        cppType = AuthorityFactory::ObjectType::CRS;
1146
0
        break;
1147
1148
0
    case PJ_TYPE_CONVERSION:
1149
0
        cppType = AuthorityFactory::ObjectType::CONVERSION;
1150
0
        break;
1151
1152
0
    case PJ_TYPE_TRANSFORMATION:
1153
0
        cppType = AuthorityFactory::ObjectType::TRANSFORMATION;
1154
0
        break;
1155
1156
0
    case PJ_TYPE_CONCATENATED_OPERATION:
1157
0
        cppType = AuthorityFactory::ObjectType::CONCATENATED_OPERATION;
1158
0
        break;
1159
1160
0
    case PJ_TYPE_OTHER_COORDINATE_OPERATION:
1161
0
        cppType = AuthorityFactory::ObjectType::COORDINATE_OPERATION;
1162
0
        break;
1163
1164
0
    case PJ_TYPE_UNKNOWN:
1165
0
        valid = false;
1166
0
        break;
1167
1168
0
    case PJ_TYPE_COORDINATE_METADATA:
1169
0
        valid = false;
1170
0
        break;
1171
0
    }
1172
0
    return cppType;
1173
0
}
1174
//! @endcond
1175
1176
// ---------------------------------------------------------------------------
1177
1178
/** \brief Return a list of objects by their name.
1179
 *
1180
 * @param ctx Context, or NULL for default context.
1181
 * @param auth_name Authority name, used to restrict the search.
1182
 * Or NULL for all authorities.
1183
 * @param searchedName Searched name. Must be at least 2 character long.
1184
 * @param types List of object types into which to search. If
1185
 * NULL, all object types will be searched.
1186
 * @param typesCount Number of elements in types, or 0 if types is NULL
1187
 * @param approximateMatch Whether approximate name identification is allowed.
1188
 * @param limitResultCount Maximum number of results to return.
1189
 * Or 0 for unlimited.
1190
 * @param options should be set to NULL for now
1191
 * @return a result set that must be unreferenced with
1192
 * proj_list_destroy(), or NULL in case of error.
1193
 */
1194
PJ_OBJ_LIST *proj_create_from_name(PJ_CONTEXT *ctx, const char *auth_name,
1195
                                   const char *searchedName,
1196
                                   const PJ_TYPE *types, size_t typesCount,
1197
                                   int approximateMatch,
1198
                                   size_t limitResultCount,
1199
0
                                   const char *const *options) {
1200
0
    SANITIZE_CTX(ctx);
1201
0
    if (!searchedName || (types != nullptr && typesCount == 0) ||
1202
0
        (types == nullptr && typesCount > 0)) {
1203
0
        proj_log_error(ctx, __FUNCTION__, "invalid input");
1204
0
        return nullptr;
1205
0
    }
1206
0
    (void)options;
1207
0
    try {
1208
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx),
1209
0
                                                auth_name ? auth_name : "");
1210
0
        std::vector<AuthorityFactory::ObjectType> allowedTypes;
1211
0
        for (size_t i = 0; i < typesCount; ++i) {
1212
0
            bool valid = false;
1213
0
            auto type = convertPJObjectTypeToObjectType(types[i], valid);
1214
0
            if (valid) {
1215
0
                allowedTypes.push_back(type);
1216
0
            }
1217
0
        }
1218
0
        auto res = factory->createObjectsFromName(searchedName, allowedTypes,
1219
0
                                                  approximateMatch != 0,
1220
0
                                                  limitResultCount);
1221
0
        std::vector<IdentifiedObjectNNPtr> objects;
1222
0
        for (const auto &obj : res) {
1223
0
            objects.push_back(obj);
1224
0
        }
1225
0
        return new PJ_OBJ_LIST(std::move(objects));
1226
0
    } catch (const std::exception &e) {
1227
0
        proj_log_error(ctx, __FUNCTION__, e.what());
1228
0
    }
1229
0
    return nullptr;
1230
0
}
1231
1232
// ---------------------------------------------------------------------------
1233
1234
/** \brief Return the type of an object.
1235
 *
1236
 * @param obj Object (must not be NULL)
1237
 * @return its type.
1238
 */
1239
0
PJ_TYPE proj_get_type(const PJ *obj) {
1240
0
    if (!obj || !obj->iso_obj) {
1241
0
        return PJ_TYPE_UNKNOWN;
1242
0
    }
1243
0
    if (obj->type != PJ_TYPE_UNKNOWN)
1244
0
        return obj->type;
1245
1246
0
    const auto getType = [&obj]() {
1247
0
        auto ptr = obj->iso_obj.get();
1248
0
        if (dynamic_cast<Ellipsoid *>(ptr)) {
1249
0
            return PJ_TYPE_ELLIPSOID;
1250
0
        }
1251
1252
0
        if (dynamic_cast<PrimeMeridian *>(ptr)) {
1253
0
            return PJ_TYPE_PRIME_MERIDIAN;
1254
0
        }
1255
1256
0
        if (dynamic_cast<DynamicGeodeticReferenceFrame *>(ptr)) {
1257
0
            return PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME;
1258
0
        }
1259
0
        if (dynamic_cast<GeodeticReferenceFrame *>(ptr)) {
1260
0
            return PJ_TYPE_GEODETIC_REFERENCE_FRAME;
1261
0
        }
1262
0
        if (dynamic_cast<DynamicVerticalReferenceFrame *>(ptr)) {
1263
0
            return PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
1264
0
        }
1265
0
        if (dynamic_cast<VerticalReferenceFrame *>(ptr)) {
1266
0
            return PJ_TYPE_VERTICAL_REFERENCE_FRAME;
1267
0
        }
1268
0
        if (dynamic_cast<DatumEnsemble *>(ptr)) {
1269
0
            return PJ_TYPE_DATUM_ENSEMBLE;
1270
0
        }
1271
0
        if (dynamic_cast<TemporalDatum *>(ptr)) {
1272
0
            return PJ_TYPE_TEMPORAL_DATUM;
1273
0
        }
1274
0
        if (dynamic_cast<EngineeringDatum *>(ptr)) {
1275
0
            return PJ_TYPE_ENGINEERING_DATUM;
1276
0
        }
1277
0
        if (dynamic_cast<ParametricDatum *>(ptr)) {
1278
0
            return PJ_TYPE_PARAMETRIC_DATUM;
1279
0
        }
1280
1281
0
        if (auto crs = dynamic_cast<GeographicCRS *>(ptr)) {
1282
0
            if (crs->coordinateSystem()->axisList().size() == 2) {
1283
0
                return PJ_TYPE_GEOGRAPHIC_2D_CRS;
1284
0
            } else {
1285
0
                return PJ_TYPE_GEOGRAPHIC_3D_CRS;
1286
0
            }
1287
0
        }
1288
1289
0
        if (auto crs = dynamic_cast<GeodeticCRS *>(ptr)) {
1290
0
            if (crs->isGeocentric()) {
1291
0
                return PJ_TYPE_GEOCENTRIC_CRS;
1292
0
            } else {
1293
0
                return PJ_TYPE_GEODETIC_CRS;
1294
0
            }
1295
0
        }
1296
1297
0
        if (dynamic_cast<VerticalCRS *>(ptr)) {
1298
0
            return PJ_TYPE_VERTICAL_CRS;
1299
0
        }
1300
0
        if (dynamic_cast<ProjectedCRS *>(ptr)) {
1301
0
            return PJ_TYPE_PROJECTED_CRS;
1302
0
        }
1303
0
        if (dynamic_cast<DerivedProjectedCRS *>(ptr)) {
1304
0
            return PJ_TYPE_DERIVED_PROJECTED_CRS;
1305
0
        }
1306
0
        if (dynamic_cast<CompoundCRS *>(ptr)) {
1307
0
            return PJ_TYPE_COMPOUND_CRS;
1308
0
        }
1309
0
        if (dynamic_cast<TemporalCRS *>(ptr)) {
1310
0
            return PJ_TYPE_TEMPORAL_CRS;
1311
0
        }
1312
0
        if (dynamic_cast<EngineeringCRS *>(ptr)) {
1313
0
            return PJ_TYPE_ENGINEERING_CRS;
1314
0
        }
1315
0
        if (dynamic_cast<BoundCRS *>(ptr)) {
1316
0
            return PJ_TYPE_BOUND_CRS;
1317
0
        }
1318
0
        if (dynamic_cast<CRS *>(ptr)) {
1319
0
            return PJ_TYPE_OTHER_CRS;
1320
0
        }
1321
1322
0
        if (dynamic_cast<Conversion *>(ptr)) {
1323
0
            return PJ_TYPE_CONVERSION;
1324
0
        }
1325
0
        if (dynamic_cast<Transformation *>(ptr)) {
1326
0
            return PJ_TYPE_TRANSFORMATION;
1327
0
        }
1328
0
        if (dynamic_cast<ConcatenatedOperation *>(ptr)) {
1329
0
            return PJ_TYPE_CONCATENATED_OPERATION;
1330
0
        }
1331
0
        if (dynamic_cast<CoordinateOperation *>(ptr)) {
1332
0
            return PJ_TYPE_OTHER_COORDINATE_OPERATION;
1333
0
        }
1334
1335
0
        if (dynamic_cast<CoordinateMetadata *>(ptr)) {
1336
0
            return PJ_TYPE_COORDINATE_METADATA;
1337
0
        }
1338
1339
0
        return PJ_TYPE_UNKNOWN;
1340
0
    };
1341
1342
0
    obj->type = getType();
1343
0
    return obj->type;
1344
0
}
1345
1346
// ---------------------------------------------------------------------------
1347
1348
/** \brief Return whether an object is deprecated.
1349
 *
1350
 * @param obj Object (must not be NULL)
1351
 * @return TRUE if it is deprecated, FALSE otherwise
1352
 */
1353
0
int proj_is_deprecated(const PJ *obj) {
1354
0
    if (!obj) {
1355
0
        return false;
1356
0
    }
1357
0
    auto identifiedObj = dynamic_cast<IdentifiedObject *>(obj->iso_obj.get());
1358
0
    if (!identifiedObj) {
1359
0
        return false;
1360
0
    }
1361
0
    return identifiedObj->isDeprecated();
1362
0
}
1363
1364
// ---------------------------------------------------------------------------
1365
1366
/** \brief Return a list of non-deprecated objects related to the passed one
1367
 *
1368
 * @param ctx Context, or NULL for default context.
1369
 * @param obj Object (of type CRS for now) for which non-deprecated objects
1370
 * must be searched. Must not be NULL
1371
 * @return a result set that must be unreferenced with
1372
 * proj_list_destroy(), or NULL in case of error.
1373
 */
1374
0
PJ_OBJ_LIST *proj_get_non_deprecated(PJ_CONTEXT *ctx, const PJ *obj) {
1375
0
    SANITIZE_CTX(ctx);
1376
0
    if (!obj) {
1377
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
1378
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
1379
0
        return nullptr;
1380
0
    }
1381
0
    auto crs = dynamic_cast<const CRS *>(obj->iso_obj.get());
1382
0
    if (!crs) {
1383
0
        return nullptr;
1384
0
    }
1385
0
    try {
1386
0
        std::vector<IdentifiedObjectNNPtr> objects;
1387
0
        auto res = crs->getNonDeprecated(getDBcontext(ctx));
1388
0
        for (const auto &resObj : res) {
1389
0
            objects.push_back(resObj);
1390
0
        }
1391
0
        return new PJ_OBJ_LIST(std::move(objects));
1392
0
    } catch (const std::exception &e) {
1393
0
        proj_log_error(ctx, __FUNCTION__, e.what());
1394
0
    }
1395
0
    return nullptr;
1396
0
}
1397
1398
// ---------------------------------------------------------------------------
1399
1400
static int proj_is_equivalent_to_internal(PJ_CONTEXT *ctx, const PJ *obj,
1401
                                          const PJ *other,
1402
0
                                          PJ_COMPARISON_CRITERION criterion) {
1403
1404
0
    if (!obj || !other) {
1405
0
        if (ctx) {
1406
0
            proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
1407
0
            proj_log_error(ctx, __FUNCTION__, "missing required input");
1408
0
        }
1409
0
        return false;
1410
0
    }
1411
1412
0
    if (obj->iso_obj == nullptr && other->iso_obj == nullptr &&
1413
0
        !obj->alternativeCoordinateOperations.empty() &&
1414
0
        obj->alternativeCoordinateOperations.size() ==
1415
0
            other->alternativeCoordinateOperations.size()) {
1416
0
        for (size_t i = 0; i < obj->alternativeCoordinateOperations.size();
1417
0
             ++i) {
1418
0
            if (obj->alternativeCoordinateOperations[i] !=
1419
0
                other->alternativeCoordinateOperations[i]) {
1420
0
                return false;
1421
0
            }
1422
0
        }
1423
0
        return true;
1424
0
    }
1425
1426
0
    auto identifiedObj = dynamic_cast<IdentifiedObject *>(obj->iso_obj.get());
1427
0
    if (!identifiedObj) {
1428
0
        return false;
1429
0
    }
1430
0
    auto otherIdentifiedObj =
1431
0
        dynamic_cast<IdentifiedObject *>(other->iso_obj.get());
1432
0
    if (!otherIdentifiedObj) {
1433
0
        return false;
1434
0
    }
1435
0
    const auto cppCriterion = ([](PJ_COMPARISON_CRITERION l_criterion) {
1436
0
        switch (l_criterion) {
1437
0
        case PJ_COMP_STRICT:
1438
0
            return IComparable::Criterion::STRICT;
1439
0
        case PJ_COMP_EQUIVALENT:
1440
0
            return IComparable::Criterion::EQUIVALENT;
1441
0
        case PJ_COMP_EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS:
1442
0
            break;
1443
0
        }
1444
0
        return IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS;
1445
0
    })(criterion);
1446
1447
0
    int res = identifiedObj->isEquivalentTo(
1448
0
        otherIdentifiedObj, cppCriterion,
1449
0
        ctx ? getDBcontextNoException(ctx, "proj_is_equivalent_to_with_ctx")
1450
0
            : nullptr);
1451
0
    return res;
1452
0
}
1453
1454
// ---------------------------------------------------------------------------
1455
1456
/** \brief Return whether two objects are equivalent.
1457
 *
1458
 * Use proj_is_equivalent_to_with_ctx() to be able to use database information.
1459
 *
1460
 * @param obj Object (must not be NULL)
1461
 * @param other Other object (must not be NULL)
1462
 * @param criterion Comparison criterion
1463
 * @return TRUE if they are equivalent
1464
 */
1465
int proj_is_equivalent_to(const PJ *obj, const PJ *other,
1466
0
                          PJ_COMPARISON_CRITERION criterion) {
1467
0
    return proj_is_equivalent_to_internal(nullptr, obj, other, criterion);
1468
0
}
1469
1470
// ---------------------------------------------------------------------------
1471
1472
/** \brief Return whether two objects are equivalent
1473
 *
1474
 * Possibly using database to check for name aliases.
1475
 *
1476
 * @param ctx PROJ context, or NULL for default context
1477
 * @param obj Object (must not be NULL)
1478
 * @param other Other object (must not be NULL)
1479
 * @param criterion Comparison criterion
1480
 * @return TRUE if they are equivalent
1481
 * @since 6.3
1482
 */
1483
int proj_is_equivalent_to_with_ctx(PJ_CONTEXT *ctx, const PJ *obj,
1484
                                   const PJ *other,
1485
0
                                   PJ_COMPARISON_CRITERION criterion) {
1486
0
    SANITIZE_CTX(ctx);
1487
0
    return proj_is_equivalent_to_internal(ctx, obj, other, criterion);
1488
0
}
1489
1490
// ---------------------------------------------------------------------------
1491
1492
/** \brief Return whether an object is a CRS
1493
 *
1494
 * @param obj Object (must not be NULL)
1495
 */
1496
0
int proj_is_crs(const PJ *obj) {
1497
0
    if (!obj) {
1498
0
        return false;
1499
0
    }
1500
0
    return dynamic_cast<CRS *>(obj->iso_obj.get()) != nullptr;
1501
0
}
1502
1503
// ---------------------------------------------------------------------------
1504
1505
/** \brief Get the name of an object.
1506
 *
1507
 * The lifetime of the returned string is the same as the input obj parameter.
1508
 *
1509
 * @param obj Object (must not be NULL)
1510
 * @return a string, or NULL in case of error or missing name.
1511
 */
1512
0
const char *proj_get_name(const PJ *obj) {
1513
0
    if (!obj) {
1514
0
        return nullptr;
1515
0
    }
1516
0
    auto identifiedObj = dynamic_cast<IdentifiedObject *>(obj->iso_obj.get());
1517
0
    if (!identifiedObj) {
1518
0
        return nullptr;
1519
0
    }
1520
0
    const auto &desc = identifiedObj->name()->description();
1521
0
    if (!desc.has_value()) {
1522
0
        return nullptr;
1523
0
    }
1524
    // The object will still be alive after the function call.
1525
    // cppcheck-suppress stlcstr
1526
0
    return desc->c_str();
1527
0
}
1528
1529
// ---------------------------------------------------------------------------
1530
1531
/** \brief Get the remarks of an object.
1532
 *
1533
 * The lifetime of the returned string is the same as the input obj parameter.
1534
 *
1535
 * @param obj Object (must not be NULL)
1536
 * @return a string, or NULL in case of error.
1537
 */
1538
0
const char *proj_get_remarks(const PJ *obj) {
1539
0
    if (!obj) {
1540
0
        return nullptr;
1541
0
    }
1542
0
    auto identifiedObj = dynamic_cast<IdentifiedObject *>(obj->iso_obj.get());
1543
0
    if (!identifiedObj) {
1544
0
        return nullptr;
1545
0
    }
1546
    // The object will still be alive after the function call.
1547
    // cppcheck-suppress stlcstr
1548
0
    return identifiedObj->remarks().c_str();
1549
0
}
1550
1551
// ---------------------------------------------------------------------------
1552
1553
/** \brief Get the authority name / codespace of an identifier of an object.
1554
 *
1555
 * The lifetime of the returned string is the same as the input obj parameter.
1556
 *
1557
 * @param obj Object (must not be NULL)
1558
 * @param index Index of the identifier. 0 = first identifier
1559
 * @return a string, or NULL in case of error or missing name.
1560
 */
1561
0
const char *proj_get_id_auth_name(const PJ *obj, int index) {
1562
0
    if (!obj) {
1563
0
        return nullptr;
1564
0
    }
1565
0
    auto identifiedObj = dynamic_cast<IdentifiedObject *>(obj->iso_obj.get());
1566
0
    if (!identifiedObj) {
1567
0
        return nullptr;
1568
0
    }
1569
0
    const auto &ids = identifiedObj->identifiers();
1570
0
    if (static_cast<size_t>(index) >= ids.size()) {
1571
0
        return nullptr;
1572
0
    }
1573
0
    const auto &codeSpace = ids[index]->codeSpace();
1574
0
    if (!codeSpace.has_value()) {
1575
0
        return nullptr;
1576
0
    }
1577
    // The object will still be alive after the function call.
1578
    // cppcheck-suppress stlcstr
1579
0
    return codeSpace->c_str();
1580
0
}
1581
1582
// ---------------------------------------------------------------------------
1583
1584
/** \brief Get the code of an identifier of an object.
1585
 *
1586
 * The lifetime of the returned string is the same as the input obj parameter.
1587
 *
1588
 * @param obj Object (must not be NULL)
1589
 * @param index Index of the identifier. 0 = first identifier
1590
 * @return a string, or NULL in case of error or missing name.
1591
 */
1592
0
const char *proj_get_id_code(const PJ *obj, int index) {
1593
0
    if (!obj) {
1594
0
        return nullptr;
1595
0
    }
1596
0
    auto identifiedObj = dynamic_cast<IdentifiedObject *>(obj->iso_obj.get());
1597
0
    if (!identifiedObj) {
1598
0
        return nullptr;
1599
0
    }
1600
0
    const auto &ids = identifiedObj->identifiers();
1601
0
    if (static_cast<size_t>(index) >= ids.size()) {
1602
0
        return nullptr;
1603
0
    }
1604
0
    return ids[index]->code().c_str();
1605
0
}
1606
1607
// ---------------------------------------------------------------------------
1608
1609
/** \brief Get a WKT representation of an object.
1610
 *
1611
 * The returned string is valid while the input obj parameter is valid,
1612
 * and until a next call to proj_as_wkt() with the same input object.
1613
 *
1614
 * This function calls osgeo::proj::io::IWKTExportable::exportToWKT().
1615
 *
1616
 * This function may return NULL if the object is not compatible with an
1617
 * export to the requested type.
1618
 *
1619
 * @param ctx PROJ context, or NULL for default context
1620
 * @param obj Object (must not be NULL)
1621
 * @param type WKT version.
1622
 * @param options null-terminated list of options, or NULL. Currently
1623
 * supported options are:
1624
 * <ul>
1625
 * <li>MULTILINE=YES/NO. Defaults to YES, except for WKT1_ESRI</li>
1626
 * <li>INDENTATION_WIDTH=number. Defaults to 4 (when multiline output is
1627
 * on).</li>
1628
 * <li>OUTPUT_AXIS=AUTO/YES/NO. In AUTO mode, axis will be output for WKT2
1629
 * variants, for WKT1_GDAL for ProjectedCRS with easting/northing ordering
1630
 * (otherwise stripped), but not for WKT1_ESRI. Setting to YES will output
1631
 * them unconditionally, and to NO will omit them unconditionally.</li>
1632
 * <li>STRICT=YES/NO. Default is YES. If NO, a Geographic 3D CRS can be for
1633
 * example exported as WKT1_GDAL with 3 axes, whereas this is normally not
1634
 * allowed.</li>
1635
 * <li>ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS=YES/NO. Default is NO. If set
1636
 * to YES and type == PJ_WKT1_GDAL, a Geographic 3D CRS or a Projected 3D CRS
1637
 * will be exported as a compound CRS whose vertical part represents an
1638
 * ellipsoidal height (for example for use with LAS 1.4 WKT1).</li>
1639
 * <li>ALLOW_LINUNIT_NODE=YES/NO. Default is YES starting with PROJ 9.1.
1640
 * Only taken into account with type == PJ_WKT1_ESRI on a Geographic 3D
1641
 * CRS.</li>
1642
 * </ul>
1643
 * @return a string, or NULL in case of error.
1644
 */
1645
const char *proj_as_wkt(PJ_CONTEXT *ctx, const PJ *obj, PJ_WKT_TYPE type,
1646
0
                        const char *const *options) {
1647
0
    SANITIZE_CTX(ctx);
1648
0
    if (!obj) {
1649
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
1650
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
1651
0
        return nullptr;
1652
0
    }
1653
0
    auto iWKTExportable = dynamic_cast<IWKTExportable *>(obj->iso_obj.get());
1654
0
    if (!iWKTExportable) {
1655
0
        return nullptr;
1656
0
    }
1657
1658
0
    const auto convention = ([](PJ_WKT_TYPE l_type) {
1659
0
        switch (l_type) {
1660
0
        case PJ_WKT2_2015:
1661
0
            return WKTFormatter::Convention::WKT2_2015;
1662
0
        case PJ_WKT2_2015_SIMPLIFIED:
1663
0
            return WKTFormatter::Convention::WKT2_2015_SIMPLIFIED;
1664
0
        case PJ_WKT2_2019:
1665
0
            return WKTFormatter::Convention::WKT2_2019;
1666
0
        case PJ_WKT2_2019_SIMPLIFIED:
1667
0
            return WKTFormatter::Convention::WKT2_2019_SIMPLIFIED;
1668
0
        case PJ_WKT1_GDAL:
1669
0
            return WKTFormatter::Convention::WKT1_GDAL;
1670
0
        case PJ_WKT1_ESRI:
1671
0
            break;
1672
0
        }
1673
0
        return WKTFormatter::Convention::WKT1_ESRI;
1674
0
    })(type);
1675
1676
0
    try {
1677
0
        auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
1678
0
        auto formatter = WKTFormatter::create(convention, std::move(dbContext));
1679
0
        for (auto iter = options; iter && iter[0]; ++iter) {
1680
0
            const char *value;
1681
0
            if ((value = getOptionValue(*iter, "MULTILINE="))) {
1682
0
                formatter->setMultiLine(ci_equal(value, "YES"));
1683
0
            } else if ((value = getOptionValue(*iter, "INDENTATION_WIDTH="))) {
1684
0
                formatter->setIndentationWidth(std::atoi(value));
1685
0
            } else if ((value = getOptionValue(*iter, "OUTPUT_AXIS="))) {
1686
0
                if (!ci_equal(value, "AUTO")) {
1687
0
                    formatter->setOutputAxis(
1688
0
                        ci_equal(value, "YES")
1689
0
                            ? WKTFormatter::OutputAxisRule::YES
1690
0
                            : WKTFormatter::OutputAxisRule::NO);
1691
0
                }
1692
0
            } else if ((value = getOptionValue(*iter, "STRICT="))) {
1693
0
                formatter->setStrict(ci_equal(value, "YES"));
1694
0
            } else if ((value = getOptionValue(
1695
0
                            *iter,
1696
0
                            "ALLOW_ELLIPSOIDAL_HEIGHT_AS_VERTICAL_CRS="))) {
1697
0
                formatter->setAllowEllipsoidalHeightAsVerticalCRS(
1698
0
                    ci_equal(value, "YES"));
1699
0
            } else if ((value = getOptionValue(*iter, "ALLOW_LINUNIT_NODE="))) {
1700
0
                formatter->setAllowLINUNITNode(ci_equal(value, "YES"));
1701
0
            } else {
1702
0
                std::string msg("Unknown option :");
1703
0
                msg += *iter;
1704
0
                proj_log_error(ctx, __FUNCTION__, msg.c_str());
1705
0
                return nullptr;
1706
0
            }
1707
0
        }
1708
0
        obj->lastWKT = iWKTExportable->exportToWKT(formatter.get());
1709
0
        return obj->lastWKT.c_str();
1710
0
    } catch (const std::exception &e) {
1711
0
        proj_log_error(ctx, __FUNCTION__, e.what());
1712
0
        return nullptr;
1713
0
    }
1714
0
}
1715
1716
// ---------------------------------------------------------------------------
1717
1718
/** \brief Get a PROJ string representation of an object.
1719
 *
1720
 * The returned string is valid while the input obj parameter is valid,
1721
 * and until a next call to proj_as_proj_string() with the same input
1722
 * object.
1723
 *
1724
 * \warning If a CRS object was not created from a PROJ string,
1725
 *          exporting to a PROJ string will in most cases
1726
 *          cause a loss of information. This can potentially lead to
1727
 *          erroneous transformations.
1728
 *
1729
 * This function calls
1730
 * osgeo::proj::io::IPROJStringExportable::exportToPROJString().
1731
 *
1732
 * This function may return NULL if the object is not compatible with an
1733
 * export to the requested type.
1734
 *
1735
 * @param ctx PROJ context, or NULL for default context
1736
 * @param obj Object (must not be NULL)
1737
 * @param type PROJ String version.
1738
 * @param options NULL-terminated list of strings with "KEY=VALUE" format. or
1739
 * NULL. Currently supported options are:
1740
 * <ul>
1741
 * <li>USE_APPROX_TMERC=YES to add the +approx flag to +proj=tmerc or
1742
 * +proj=utm.</li>
1743
 * <li>MULTILINE=YES/NO. Defaults to NO</li>
1744
 * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
1745
 * on).</li>
1746
 * <li>MAX_LINE_LENGTH=number. Defaults to 80 (when multiline output is
1747
 * on).</li>
1748
 * </ul>
1749
 * @return a string, or NULL in case of error.
1750
 */
1751
const char *proj_as_proj_string(PJ_CONTEXT *ctx, const PJ *obj,
1752
                                PJ_PROJ_STRING_TYPE type,
1753
0
                                const char *const *options) {
1754
0
    SANITIZE_CTX(ctx);
1755
0
    if (!obj) {
1756
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
1757
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
1758
0
        return nullptr;
1759
0
    }
1760
0
    auto exportable =
1761
0
        dynamic_cast<const IPROJStringExportable *>(obj->iso_obj.get());
1762
0
    if (!exportable) {
1763
0
        proj_log_error(ctx, __FUNCTION__, "Object type not exportable to PROJ");
1764
0
        return nullptr;
1765
0
    }
1766
    // Make sure that the C and C++ enumeration match
1767
0
    static_assert(static_cast<int>(PJ_PROJ_5) ==
1768
0
                      static_cast<int>(PROJStringFormatter::Convention::PROJ_5),
1769
0
                  "");
1770
0
    static_assert(static_cast<int>(PJ_PROJ_4) ==
1771
0
                      static_cast<int>(PROJStringFormatter::Convention::PROJ_4),
1772
0
                  "");
1773
    // Make sure we enumerate all values. If adding a new value, as we
1774
    // don't have a default clause, the compiler will warn.
1775
0
    switch (type) {
1776
0
    case PJ_PROJ_5:
1777
0
    case PJ_PROJ_4:
1778
0
        break;
1779
0
    }
1780
0
    const PROJStringFormatter::Convention convention =
1781
0
        static_cast<PROJStringFormatter::Convention>(type);
1782
0
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
1783
0
    try {
1784
0
        auto formatter =
1785
0
            PROJStringFormatter::create(convention, std::move(dbContext));
1786
0
        for (auto iter = options; iter && iter[0]; ++iter) {
1787
0
            const char *value;
1788
0
            if ((value = getOptionValue(*iter, "MULTILINE="))) {
1789
0
                formatter->setMultiLine(ci_equal(value, "YES"));
1790
0
            } else if ((value = getOptionValue(*iter, "INDENTATION_WIDTH="))) {
1791
0
                formatter->setIndentationWidth(std::atoi(value));
1792
0
            } else if ((value = getOptionValue(*iter, "MAX_LINE_LENGTH="))) {
1793
0
                formatter->setMaxLineLength(std::atoi(value));
1794
0
            } else if ((value = getOptionValue(*iter, "USE_APPROX_TMERC="))) {
1795
0
                formatter->setUseApproxTMerc(ci_equal(value, "YES"));
1796
0
            } else {
1797
0
                std::string msg("Unknown option :");
1798
0
                msg += *iter;
1799
0
                proj_log_error(ctx, __FUNCTION__, msg.c_str());
1800
0
                return nullptr;
1801
0
            }
1802
0
        }
1803
0
        obj->lastPROJString = exportable->exportToPROJString(formatter.get());
1804
0
        return obj->lastPROJString.c_str();
1805
0
    } catch (const std::exception &e) {
1806
0
        proj_log_error(ctx, __FUNCTION__, e.what());
1807
0
        return nullptr;
1808
0
    }
1809
0
}
1810
1811
// ---------------------------------------------------------------------------
1812
1813
/** \brief Get a PROJJSON string representation of an object.
1814
 *
1815
 * The returned string is valid while the input obj parameter is valid,
1816
 * and until a next call to proj_as_proj_string() with the same input
1817
 * object.
1818
 *
1819
 * This function calls
1820
 * osgeo::proj::io::IJSONExportable::exportToJSON().
1821
 *
1822
 * This function may return NULL if the object is not compatible with an
1823
 * export to the requested type.
1824
 *
1825
 * @param ctx PROJ context, or NULL for default context
1826
 * @param obj Object (must not be NULL)
1827
 * @param options NULL-terminated list of strings with "KEY=VALUE" format. or
1828
 * NULL. Currently
1829
 * supported options are:
1830
 * <ul>
1831
 * <li>MULTILINE=YES/NO. Defaults to YES</li>
1832
 * <li>INDENTATION_WIDTH=number. Defaults to 2 (when multiline output is
1833
 * on).</li>
1834
 * <li>SCHEMA=string. URL to PROJJSON schema. Can be set to empty string to
1835
 * disable it.</li>
1836
 * </ul>
1837
 * @return a string, or NULL in case of error.
1838
 *
1839
 * @since 6.2
1840
 */
1841
const char *proj_as_projjson(PJ_CONTEXT *ctx, const PJ *obj,
1842
0
                             const char *const *options) {
1843
0
    SANITIZE_CTX(ctx);
1844
0
    if (!obj) {
1845
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
1846
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
1847
0
        return nullptr;
1848
0
    }
1849
0
    auto exportable = dynamic_cast<const IJSONExportable *>(obj->iso_obj.get());
1850
0
    if (!exportable) {
1851
0
        proj_log_error(ctx, __FUNCTION__, "Object type not exportable to JSON");
1852
0
        return nullptr;
1853
0
    }
1854
1855
0
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
1856
0
    try {
1857
0
        auto formatter = JSONFormatter::create(std::move(dbContext));
1858
0
        for (auto iter = options; iter && iter[0]; ++iter) {
1859
0
            const char *value;
1860
0
            if ((value = getOptionValue(*iter, "MULTILINE="))) {
1861
0
                formatter->setMultiLine(ci_equal(value, "YES"));
1862
0
            } else if ((value = getOptionValue(*iter, "INDENTATION_WIDTH="))) {
1863
0
                formatter->setIndentationWidth(std::atoi(value));
1864
0
            } else if ((value = getOptionValue(*iter, "SCHEMA="))) {
1865
0
                formatter->setSchema(value);
1866
0
            } else {
1867
0
                std::string msg("Unknown option :");
1868
0
                msg += *iter;
1869
0
                proj_log_error(ctx, __FUNCTION__, msg.c_str());
1870
0
                return nullptr;
1871
0
            }
1872
0
        }
1873
0
        obj->lastJSONString = exportable->exportToJSON(formatter.get());
1874
0
        return obj->lastJSONString.c_str();
1875
0
    } catch (const std::exception &e) {
1876
0
        proj_log_error(ctx, __FUNCTION__, e.what());
1877
0
        return nullptr;
1878
0
    }
1879
0
}
1880
1881
// ---------------------------------------------------------------------------
1882
1883
/** \brief Get the number of domains/usages for a given object.
1884
 *
1885
 * Most objects have a single domain/usage, but for some of them, there might
1886
 * be multiple.
1887
 *
1888
 * @param obj Object (must not be NULL)
1889
 * @return the number of domains, or 0 in case of error.
1890
 * @since 9.2
1891
 */
1892
0
int proj_get_domain_count(const PJ *obj) {
1893
0
    if (!obj || !obj->iso_obj) {
1894
0
        return 0;
1895
0
    }
1896
0
    auto objectUsage = dynamic_cast<const ObjectUsage *>(obj->iso_obj.get());
1897
0
    if (!objectUsage) {
1898
0
        return 0;
1899
0
    }
1900
0
    const auto &domains = objectUsage->domains();
1901
0
    return static_cast<int>(domains.size());
1902
0
}
1903
1904
// ---------------------------------------------------------------------------
1905
1906
/** \brief Get the scope of an object.
1907
 *
1908
 * In case of multiple usages, this will be the one of first usage.
1909
 *
1910
 * The lifetime of the returned string is the same as the input obj parameter.
1911
 *
1912
 * @param obj Object (must not be NULL)
1913
 * @return a string, or NULL in case of error or missing scope.
1914
 */
1915
0
const char *proj_get_scope(const PJ *obj) { return proj_get_scope_ex(obj, 0); }
1916
1917
// ---------------------------------------------------------------------------
1918
1919
/** \brief Get the scope of an object.
1920
 *
1921
 * The lifetime of the returned string is the same as the input obj parameter.
1922
 *
1923
 * @param obj Object (must not be NULL)
1924
 * @param domainIdx Index of the domain/usage. In [0,proj_get_domain_count(obj)[
1925
 * @return a string, or NULL in case of error or missing scope.
1926
 * @since 9.2
1927
 */
1928
0
const char *proj_get_scope_ex(const PJ *obj, int domainIdx) {
1929
0
    if (!obj || !obj->iso_obj) {
1930
0
        return nullptr;
1931
0
    }
1932
0
    auto objectUsage = dynamic_cast<const ObjectUsage *>(obj->iso_obj.get());
1933
0
    if (!objectUsage) {
1934
0
        return nullptr;
1935
0
    }
1936
0
    const auto &domains = objectUsage->domains();
1937
0
    if (domainIdx < 0 || static_cast<size_t>(domainIdx) >= domains.size()) {
1938
0
        return nullptr;
1939
0
    }
1940
0
    const auto &scope = domains[domainIdx]->scope();
1941
0
    if (!scope.has_value()) {
1942
0
        return nullptr;
1943
0
    }
1944
    // The object will still be alive after the function call.
1945
    // cppcheck-suppress stlcstr
1946
0
    return scope->c_str();
1947
0
}
1948
1949
// ---------------------------------------------------------------------------
1950
1951
/** \brief Return the area of use of an object.
1952
 *
1953
 * In case of multiple usages, this will be the one of first usage.
1954
 *
1955
 * @param ctx PROJ context, or NULL for default context
1956
 * @param obj Object (must not be NULL)
1957
 * @param out_west_lon_degree Pointer to a double to receive the west longitude
1958
 * (in degrees). Or NULL. If the returned value is -1000, the bounding box is
1959
 * unknown.
1960
 * @param out_south_lat_degree Pointer to a double to receive the south latitude
1961
 * (in degrees). Or NULL. If the returned value is -1000, the bounding box is
1962
 * unknown.
1963
 * @param out_east_lon_degree Pointer to a double to receive the east longitude
1964
 * (in degrees). Or NULL. If the returned value is -1000, the bounding box is
1965
 * unknown.
1966
 * @param out_north_lat_degree Pointer to a double to receive the north latitude
1967
 * (in degrees). Or NULL. If the returned value is -1000, the bounding box is
1968
 * unknown.
1969
 * @param out_area_name Pointer to a string to receive the name of the area of
1970
 * use. Or NULL. *p_area_name is valid while obj is valid itself.
1971
 * @return TRUE in case of success, FALSE in case of error or if the area
1972
 * of use is unknown.
1973
 */
1974
int proj_get_area_of_use(PJ_CONTEXT *ctx, const PJ *obj,
1975
                         double *out_west_lon_degree,
1976
                         double *out_south_lat_degree,
1977
                         double *out_east_lon_degree,
1978
                         double *out_north_lat_degree,
1979
0
                         const char **out_area_name) {
1980
0
    return proj_get_area_of_use_ex(ctx, obj, 0, out_west_lon_degree,
1981
0
                                   out_south_lat_degree, out_east_lon_degree,
1982
0
                                   out_north_lat_degree, out_area_name);
1983
0
}
1984
1985
// ---------------------------------------------------------------------------
1986
1987
/** \brief Return the area of use of an object.
1988
 *
1989
 * @param ctx PROJ context, or NULL for default context
1990
 * @param obj Object (must not be NULL)
1991
 * @param domainIdx Index of the domain/usage. In [0,proj_get_domain_count(obj)[
1992
 * @param out_west_lon_degree Pointer to a double to receive the west longitude
1993
 * (in degrees). Or NULL. If the returned value is -1000, the bounding box is
1994
 * unknown.
1995
 * @param out_south_lat_degree Pointer to a double to receive the south latitude
1996
 * (in degrees). Or NULL. If the returned value is -1000, the bounding box is
1997
 * unknown.
1998
 * @param out_east_lon_degree Pointer to a double to receive the east longitude
1999
 * (in degrees). Or NULL. If the returned value is -1000, the bounding box is
2000
 * unknown.
2001
 * @param out_north_lat_degree Pointer to a double to receive the north latitude
2002
 * (in degrees). Or NULL. If the returned value is -1000, the bounding box is
2003
 * unknown.
2004
 * @param out_area_name Pointer to a string to receive the name of the area of
2005
 * use. Or NULL. *p_area_name is valid while obj is valid itself.
2006
 * @return TRUE in case of success, FALSE in case of error or if the area
2007
 * of use is unknown.
2008
 */
2009
int proj_get_area_of_use_ex(PJ_CONTEXT *ctx, const PJ *obj, int domainIdx,
2010
                            double *out_west_lon_degree,
2011
                            double *out_south_lat_degree,
2012
                            double *out_east_lon_degree,
2013
                            double *out_north_lat_degree,
2014
0
                            const char **out_area_name) {
2015
0
    (void)ctx;
2016
0
    if (out_area_name) {
2017
0
        *out_area_name = nullptr;
2018
0
    }
2019
0
    auto objectUsage = dynamic_cast<const ObjectUsage *>(obj->iso_obj.get());
2020
0
    if (!objectUsage) {
2021
0
        return false;
2022
0
    }
2023
0
    const auto &domains = objectUsage->domains();
2024
0
    if (domainIdx < 0 || static_cast<size_t>(domainIdx) >= domains.size()) {
2025
0
        return false;
2026
0
    }
2027
0
    const auto &extent = domains[domainIdx]->domainOfValidity();
2028
0
    if (!extent) {
2029
0
        return false;
2030
0
    }
2031
0
    const auto &desc = extent->description();
2032
0
    if (desc.has_value() && out_area_name) {
2033
0
        *out_area_name = desc->c_str();
2034
0
    }
2035
2036
0
    const auto &geogElements = extent->geographicElements();
2037
0
    if (!geogElements.empty()) {
2038
0
        auto bbox =
2039
0
            dynamic_cast<const GeographicBoundingBox *>(geogElements[0].get());
2040
0
        if (bbox) {
2041
0
            if (out_west_lon_degree) {
2042
0
                *out_west_lon_degree = bbox->westBoundLongitude();
2043
0
            }
2044
0
            if (out_south_lat_degree) {
2045
0
                *out_south_lat_degree = bbox->southBoundLatitude();
2046
0
            }
2047
0
            if (out_east_lon_degree) {
2048
0
                *out_east_lon_degree = bbox->eastBoundLongitude();
2049
0
            }
2050
0
            if (out_north_lat_degree) {
2051
0
                *out_north_lat_degree = bbox->northBoundLatitude();
2052
0
            }
2053
0
            return true;
2054
0
        }
2055
0
    }
2056
0
    if (out_west_lon_degree) {
2057
0
        *out_west_lon_degree = -1000;
2058
0
    }
2059
0
    if (out_south_lat_degree) {
2060
0
        *out_south_lat_degree = -1000;
2061
0
    }
2062
0
    if (out_east_lon_degree) {
2063
0
        *out_east_lon_degree = -1000;
2064
0
    }
2065
0
    if (out_north_lat_degree) {
2066
0
        *out_north_lat_degree = -1000;
2067
0
    }
2068
0
    return true;
2069
0
}
2070
2071
// ---------------------------------------------------------------------------
2072
2073
static const GeodeticCRS *extractGeodeticCRS(PJ_CONTEXT *ctx, const PJ *crs,
2074
0
                                             const char *fname) {
2075
0
    if (!crs) {
2076
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2077
0
        proj_log_error(ctx, fname, "missing required input");
2078
0
        return nullptr;
2079
0
    }
2080
0
    auto l_crs = dynamic_cast<const CRS *>(crs->iso_obj.get());
2081
0
    if (!l_crs) {
2082
0
        proj_log_error(ctx, fname, "Object is not a CRS");
2083
0
        return nullptr;
2084
0
    }
2085
0
    auto geodCRS = l_crs->extractGeodeticCRSRaw();
2086
0
    if (!geodCRS) {
2087
0
        proj_log_error(ctx, fname, "CRS has no geodetic CRS");
2088
0
    }
2089
0
    return geodCRS;
2090
0
}
2091
2092
// ---------------------------------------------------------------------------
2093
2094
/** \brief Get the geodeticCRS / geographicCRS from a CRS
2095
 *
2096
 * The returned object must be unreferenced with proj_destroy() after
2097
 * use.
2098
 * It should be used by at most one thread at a time.
2099
 *
2100
 * @param ctx PROJ context, or NULL for default context
2101
 * @param crs Object of type CRS (must not be NULL)
2102
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2103
 * in case of error.
2104
 */
2105
0
PJ *proj_crs_get_geodetic_crs(PJ_CONTEXT *ctx, const PJ *crs) {
2106
0
    SANITIZE_CTX(ctx);
2107
0
    auto geodCRS = extractGeodeticCRS(ctx, crs, __FUNCTION__);
2108
0
    if (!geodCRS) {
2109
0
        return nullptr;
2110
0
    }
2111
0
    return pj_obj_create(ctx,
2112
0
                         NN_NO_CHECK(nn_dynamic_pointer_cast<IdentifiedObject>(
2113
0
                             geodCRS->shared_from_this())));
2114
0
}
2115
2116
// ---------------------------------------------------------------------------
2117
2118
/** \brief Returns whether a CRS is a derived CRS.
2119
 *
2120
 * @param ctx PROJ context, or NULL for default context
2121
 * @param crs Object of type CRS (must not be NULL)
2122
 * @return TRUE if the CRS is a derived CRS.
2123
 * @since 8.0
2124
 */
2125
0
int proj_crs_is_derived(PJ_CONTEXT *ctx, const PJ *crs) {
2126
0
    if (!crs) {
2127
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2128
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2129
0
        return false;
2130
0
    }
2131
0
    auto l_crs = dynamic_cast<const CRS *>(crs->iso_obj.get());
2132
0
    if (!l_crs) {
2133
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CRS");
2134
0
        return false;
2135
0
    }
2136
0
    return dynamic_cast<const DerivedCRS *>(l_crs) != nullptr;
2137
0
}
2138
2139
// ---------------------------------------------------------------------------
2140
2141
/** \brief Returns whether (at least one component of a) CRS has a dynamic
2142
 * reference frame
2143
 *
2144
 * @param ctx PROJ context, or NULL for default context
2145
 * @param crs Object of type CRS (must not be NULL)
2146
 * @return TRUE if the CRS is a dynamic CRS.
2147
 * @since 9.9
2148
 */
2149
0
int proj_crs_is_dynamic(PJ_CONTEXT *ctx, const PJ *crs) {
2150
0
    if (!crs) {
2151
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2152
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2153
0
        return false;
2154
0
    }
2155
0
    auto l_crs = dynamic_cast<const CRS *>(crs->iso_obj.get());
2156
0
    if (!l_crs) {
2157
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CRS");
2158
0
        return false;
2159
0
    }
2160
0
    return l_crs->isDynamic(/* considerWGS84AsDynamic = */ false);
2161
0
}
2162
2163
// ---------------------------------------------------------------------------
2164
2165
/** \brief Get a CRS component from a CompoundCRS
2166
 *
2167
 * The returned object must be unreferenced with proj_destroy() after
2168
 * use.
2169
 * It should be used by at most one thread at a time.
2170
 *
2171
 * @param ctx PROJ context, or NULL for default context
2172
 * @param crs Object of type CRS (must not be NULL)
2173
 * @param index Index of the CRS component (typically 0 = horizontal, 1 =
2174
 * vertical)
2175
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2176
 * in case of error.
2177
 */
2178
0
PJ *proj_crs_get_sub_crs(PJ_CONTEXT *ctx, const PJ *crs, int index) {
2179
0
    SANITIZE_CTX(ctx);
2180
0
    if (!crs) {
2181
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2182
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2183
0
        return nullptr;
2184
0
    }
2185
0
    auto l_crs = dynamic_cast<CompoundCRS *>(crs->iso_obj.get());
2186
0
    if (!l_crs) {
2187
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CompoundCRS");
2188
0
        return nullptr;
2189
0
    }
2190
0
    const auto &components = l_crs->componentReferenceSystems();
2191
0
    if (static_cast<size_t>(index) >= components.size()) {
2192
0
        return nullptr;
2193
0
    }
2194
0
    return pj_obj_create(ctx, components[index]);
2195
0
}
2196
2197
// ---------------------------------------------------------------------------
2198
2199
/** \brief Returns a BoundCRS
2200
 *
2201
 * The returned object must be unreferenced with proj_destroy() after
2202
 * use.
2203
 * It should be used by at most one thread at a time.
2204
 *
2205
 * @param ctx PROJ context, or NULL for default context
2206
 * @param base_crs Base CRS (must not be NULL)
2207
 * @param hub_crs Hub CRS (must not be NULL)
2208
 * @param transformation Transformation (must not be NULL)
2209
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2210
 * in case of error.
2211
 */
2212
PJ *proj_crs_create_bound_crs(PJ_CONTEXT *ctx, const PJ *base_crs,
2213
0
                              const PJ *hub_crs, const PJ *transformation) {
2214
0
    SANITIZE_CTX(ctx);
2215
0
    if (!base_crs || !hub_crs || !transformation) {
2216
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2217
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2218
0
        return nullptr;
2219
0
    }
2220
0
    auto l_base_crs = std::dynamic_pointer_cast<CRS>(base_crs->iso_obj);
2221
0
    if (!l_base_crs) {
2222
0
        proj_log_error(ctx, __FUNCTION__, "base_crs is not a CRS");
2223
0
        return nullptr;
2224
0
    }
2225
0
    auto l_hub_crs = std::dynamic_pointer_cast<CRS>(hub_crs->iso_obj);
2226
0
    if (!l_hub_crs) {
2227
0
        proj_log_error(ctx, __FUNCTION__, "hub_crs is not a CRS");
2228
0
        return nullptr;
2229
0
    }
2230
0
    auto l_transformation =
2231
0
        std::dynamic_pointer_cast<Transformation>(transformation->iso_obj);
2232
0
    if (!l_transformation) {
2233
0
        proj_log_error(ctx, __FUNCTION__, "transformation is not a CRS");
2234
0
        return nullptr;
2235
0
    }
2236
0
    try {
2237
0
        return pj_obj_create(ctx,
2238
0
                             BoundCRS::create(NN_NO_CHECK(l_base_crs),
2239
0
                                              NN_NO_CHECK(l_hub_crs),
2240
0
                                              NN_NO_CHECK(l_transformation)));
2241
0
    } catch (const std::exception &e) {
2242
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2243
0
        return nullptr;
2244
0
    }
2245
0
}
2246
2247
// ---------------------------------------------------------------------------
2248
2249
/** \brief Returns potentially
2250
 * a BoundCRS, with a transformation to EPSG:4326, wrapping this CRS
2251
 *
2252
 * The returned object must be unreferenced with proj_destroy() after
2253
 * use.
2254
 * It should be used by at most one thread at a time.
2255
 *
2256
 * This is the same as method
2257
 * osgeo::proj::crs::CRS::createBoundCRSToWGS84IfPossible()
2258
 *
2259
 * @param ctx PROJ context, or NULL for default context
2260
 * @param crs Object of type CRS (must not be NULL)
2261
 * @param options null-terminated list of options, or NULL. Currently
2262
 * supported options are:
2263
 * <ul>
2264
 * <li>ALLOW_INTERMEDIATE_CRS=ALWAYS/IF_NO_DIRECT_TRANSFORMATION/NEVER. Defaults
2265
 * to NEVER. When set to ALWAYS/IF_NO_DIRECT_TRANSFORMATION,
2266
 * intermediate CRS may be considered when computing the possible
2267
 * transformations. Slower.</li>
2268
 * </ul>
2269
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2270
 * in case of error.
2271
 */
2272
PJ *proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, const PJ *crs,
2273
0
                                       const char *const *options) {
2274
0
    SANITIZE_CTX(ctx);
2275
0
    if (!crs) {
2276
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2277
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2278
0
        return nullptr;
2279
0
    }
2280
0
    auto l_crs = dynamic_cast<const CRS *>(crs->iso_obj.get());
2281
0
    if (!l_crs) {
2282
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CRS");
2283
0
        return nullptr;
2284
0
    }
2285
0
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
2286
0
    try {
2287
0
        CoordinateOperationContext::IntermediateCRSUse allowIntermediateCRS =
2288
0
            CoordinateOperationContext::IntermediateCRSUse::NEVER;
2289
0
        for (auto iter = options; iter && iter[0]; ++iter) {
2290
0
            const char *value;
2291
0
            if ((value = getOptionValue(*iter, "ALLOW_INTERMEDIATE_CRS="))) {
2292
0
                if (ci_equal(value, "YES") || ci_equal(value, "ALWAYS")) {
2293
0
                    allowIntermediateCRS =
2294
0
                        CoordinateOperationContext::IntermediateCRSUse::ALWAYS;
2295
0
                } else if (ci_equal(value, "IF_NO_DIRECT_TRANSFORMATION")) {
2296
0
                    allowIntermediateCRS = CoordinateOperationContext::
2297
0
                        IntermediateCRSUse::IF_NO_DIRECT_TRANSFORMATION;
2298
0
                }
2299
0
            } else {
2300
0
                std::string msg("Unknown option :");
2301
0
                msg += *iter;
2302
0
                proj_log_error(ctx, __FUNCTION__, msg.c_str());
2303
0
                return nullptr;
2304
0
            }
2305
0
        }
2306
0
        return pj_obj_create(ctx, l_crs->createBoundCRSToWGS84IfPossible(
2307
0
                                      dbContext, allowIntermediateCRS));
2308
0
    } catch (const std::exception &e) {
2309
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2310
0
        return nullptr;
2311
0
    }
2312
0
}
2313
2314
// ---------------------------------------------------------------------------
2315
2316
/** \brief Returns a BoundCRS, with a transformation to a hub geographic 3D crs
2317
 * (use EPSG:4979 for WGS84 for example), using a grid.
2318
 *
2319
 * The returned object must be unreferenced with proj_destroy() after
2320
 * use.
2321
 * It should be used by at most one thread at a time.
2322
 *
2323
 * @param ctx PROJ context, or NULL for default context
2324
 * @param vert_crs Object of type VerticalCRS (must not be NULL)
2325
 * @param hub_geographic_3D_crs Object of type Geographic 3D CRS (must not be
2326
 * NULL)
2327
 * @param grid_name Grid name (typically a .gtx file)
2328
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2329
 * in case of error.
2330
 * @since 6.3
2331
 */
2332
PJ *proj_crs_create_bound_vertical_crs(PJ_CONTEXT *ctx, const PJ *vert_crs,
2333
                                       const PJ *hub_geographic_3D_crs,
2334
0
                                       const char *grid_name) {
2335
0
    SANITIZE_CTX(ctx);
2336
0
    if (!vert_crs || !hub_geographic_3D_crs || !grid_name) {
2337
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2338
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2339
0
        return nullptr;
2340
0
    }
2341
0
    auto l_crs = std::dynamic_pointer_cast<VerticalCRS>(vert_crs->iso_obj);
2342
0
    if (!l_crs) {
2343
0
        proj_log_error(ctx, __FUNCTION__, "vert_crs is not a VerticalCRS");
2344
0
        return nullptr;
2345
0
    }
2346
0
    auto hub_crs =
2347
0
        std::dynamic_pointer_cast<CRS>(hub_geographic_3D_crs->iso_obj);
2348
0
    if (!hub_crs) {
2349
0
        proj_log_error(ctx, __FUNCTION__, "hub_geographic_3D_crs is not a CRS");
2350
0
        return nullptr;
2351
0
    }
2352
0
    try {
2353
0
        auto nnCRS = NN_NO_CHECK(l_crs);
2354
0
        auto nnHubCRS = NN_NO_CHECK(hub_crs);
2355
0
        auto transformation =
2356
0
            Transformation::createGravityRelatedHeightToGeographic3D(
2357
0
                PropertyMap().set(IdentifiedObject::NAME_KEY,
2358
0
                                  "unknown to " + hub_crs->nameStr() +
2359
0
                                      " ellipsoidal height"),
2360
0
                nnCRS, nnHubCRS, nullptr, std::string(grid_name),
2361
0
                std::vector<PositionalAccuracyNNPtr>());
2362
0
        return pj_obj_create(ctx,
2363
0
                             BoundCRS::create(nnCRS, nnHubCRS, transformation));
2364
0
    } catch (const std::exception &e) {
2365
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2366
0
        return nullptr;
2367
0
    }
2368
0
}
2369
2370
// ---------------------------------------------------------------------------
2371
2372
/** \brief Get the ellipsoid from a CRS or a GeodeticReferenceFrame.
2373
 *
2374
 * The returned object must be unreferenced with proj_destroy() after
2375
 * use.
2376
 * It should be used by at most one thread at a time.
2377
 *
2378
 * @param ctx PROJ context, or NULL for default context
2379
 * @param obj Object of type CRS or GeodeticReferenceFrame (must not be NULL)
2380
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2381
 * in case of error.
2382
 */
2383
0
PJ *proj_get_ellipsoid(PJ_CONTEXT *ctx, const PJ *obj) {
2384
0
    SANITIZE_CTX(ctx);
2385
0
    auto ptr = obj->iso_obj.get();
2386
0
    if (dynamic_cast<const CRS *>(ptr)) {
2387
0
        auto geodCRS = extractGeodeticCRS(ctx, obj, __FUNCTION__);
2388
0
        if (geodCRS) {
2389
0
            return pj_obj_create(ctx, geodCRS->ellipsoid());
2390
0
        }
2391
0
    } else {
2392
0
        auto datum = dynamic_cast<const GeodeticReferenceFrame *>(ptr);
2393
0
        if (datum) {
2394
0
            return pj_obj_create(ctx, datum->ellipsoid());
2395
0
        }
2396
0
    }
2397
0
    proj_log_error(ctx, __FUNCTION__,
2398
0
                   "Object is not a CRS or GeodeticReferenceFrame");
2399
0
    return nullptr;
2400
0
}
2401
2402
// ---------------------------------------------------------------------------
2403
2404
/** \brief Get the name of the celestial body of this object.
2405
 *
2406
 * Object should be a CRS, Datum or Ellipsoid.
2407
 *
2408
 * @param ctx PROJ context, or NULL for default context
2409
 * @param obj Object of type CRS, Datum or Ellipsoid.(must not be NULL)
2410
 * @return the name of the celestial body, or NULL.
2411
 * @since 8.1
2412
 */
2413
0
const char *proj_get_celestial_body_name(PJ_CONTEXT *ctx, const PJ *obj) {
2414
0
    SANITIZE_CTX(ctx);
2415
0
    const BaseObject *ptr = obj->iso_obj.get();
2416
0
    if (dynamic_cast<const CRS *>(ptr)) {
2417
0
        const auto geodCRS = extractGeodeticCRS(ctx, obj, __FUNCTION__);
2418
0
        if (!geodCRS) {
2419
            // FIXME when vertical CRS can be non-EARTH...
2420
0
            return datum::Ellipsoid::EARTH.c_str();
2421
0
        }
2422
0
        return geodCRS->ellipsoid()->celestialBody().c_str();
2423
0
    }
2424
0
    const auto ensemble = dynamic_cast<const DatumEnsemble *>(ptr);
2425
0
    if (ensemble) {
2426
0
        ptr = ensemble->datums().front().get();
2427
        // Go on
2428
0
    }
2429
0
    const auto geodetic_datum =
2430
0
        dynamic_cast<const GeodeticReferenceFrame *>(ptr);
2431
0
    if (geodetic_datum) {
2432
0
        return geodetic_datum->ellipsoid()->celestialBody().c_str();
2433
0
    }
2434
0
    const auto vertical_datum =
2435
0
        dynamic_cast<const VerticalReferenceFrame *>(ptr);
2436
0
    if (vertical_datum) {
2437
        // FIXME when vertical CRS can be non-EARTH...
2438
0
        return datum::Ellipsoid::EARTH.c_str();
2439
0
    }
2440
0
    const auto ellipsoid = dynamic_cast<const Ellipsoid *>(ptr);
2441
0
    if (ellipsoid) {
2442
0
        return ellipsoid->celestialBody().c_str();
2443
0
    }
2444
0
    proj_log_error(ctx, __FUNCTION__,
2445
0
                   "Object is not a CRS, Datum or Ellipsoid");
2446
0
    return nullptr;
2447
0
}
2448
2449
// ---------------------------------------------------------------------------
2450
2451
/** \brief Get the horizontal datum from a CRS
2452
 *
2453
 * This function may return a Datum or DatumEnsemble object.
2454
 *
2455
 * The returned object must be unreferenced with proj_destroy() after
2456
 * use.
2457
 * It should be used by at most one thread at a time.
2458
 *
2459
 * @param ctx PROJ context, or NULL for default context
2460
 * @param crs Object of type CRS (must not be NULL)
2461
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2462
 * in case of error.
2463
 */
2464
0
PJ *proj_crs_get_horizontal_datum(PJ_CONTEXT *ctx, const PJ *crs) {
2465
0
    SANITIZE_CTX(ctx);
2466
0
    auto geodCRS = extractGeodeticCRS(ctx, crs, __FUNCTION__);
2467
0
    if (!geodCRS) {
2468
0
        return nullptr;
2469
0
    }
2470
0
    const auto &datum = geodCRS->datum();
2471
0
    if (datum) {
2472
0
        return pj_obj_create(ctx, NN_NO_CHECK(datum));
2473
0
    }
2474
2475
0
    const auto &datumEnsemble = geodCRS->datumEnsemble();
2476
0
    if (datumEnsemble) {
2477
0
        return pj_obj_create(ctx, NN_NO_CHECK(datumEnsemble));
2478
0
    }
2479
0
    proj_log_error(ctx, __FUNCTION__, "CRS has no datum");
2480
0
    return nullptr;
2481
0
}
2482
2483
// ---------------------------------------------------------------------------
2484
2485
/** \brief Return ellipsoid parameters.
2486
 *
2487
 * @param ctx PROJ context, or NULL for default context
2488
 * @param ellipsoid Object of type Ellipsoid (must not be NULL)
2489
 * @param out_semi_major_metre Pointer to a value to store the semi-major axis
2490
 * in
2491
 * metre. or NULL
2492
 * @param out_semi_minor_metre Pointer to a value to store the semi-minor axis
2493
 * in
2494
 * metre. or NULL
2495
 * @param out_is_semi_minor_computed Pointer to a boolean value to indicate if
2496
 * the
2497
 * semi-minor value was computed. If FALSE, its value comes from the
2498
 * definition. or NULL
2499
 * @param out_inv_flattening Pointer to a value to store the inverse
2500
 * flattening. or NULL
2501
 * @return TRUE in case of success.
2502
 */
2503
int proj_ellipsoid_get_parameters(PJ_CONTEXT *ctx, const PJ *ellipsoid,
2504
                                  double *out_semi_major_metre,
2505
                                  double *out_semi_minor_metre,
2506
                                  int *out_is_semi_minor_computed,
2507
0
                                  double *out_inv_flattening) {
2508
0
    SANITIZE_CTX(ctx);
2509
0
    if (!ellipsoid) {
2510
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2511
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2512
0
        return FALSE;
2513
0
    }
2514
0
    auto l_ellipsoid =
2515
0
        dynamic_cast<const Ellipsoid *>(ellipsoid->iso_obj.get());
2516
0
    if (!l_ellipsoid) {
2517
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a Ellipsoid");
2518
0
        return FALSE;
2519
0
    }
2520
2521
0
    if (out_semi_major_metre) {
2522
0
        *out_semi_major_metre = l_ellipsoid->semiMajorAxis().getSIValue();
2523
0
    }
2524
0
    if (out_semi_minor_metre) {
2525
0
        *out_semi_minor_metre =
2526
0
            l_ellipsoid->computeSemiMinorAxis().getSIValue();
2527
0
    }
2528
0
    if (out_is_semi_minor_computed) {
2529
0
        *out_is_semi_minor_computed =
2530
0
            !(l_ellipsoid->semiMinorAxis().has_value());
2531
0
    }
2532
0
    if (out_inv_flattening) {
2533
0
        *out_inv_flattening = l_ellipsoid->computedInverseFlattening();
2534
0
    }
2535
0
    return TRUE;
2536
0
}
2537
2538
// ---------------------------------------------------------------------------
2539
2540
/** \brief Get the prime meridian of a CRS or a GeodeticReferenceFrame.
2541
 *
2542
 * The returned object must be unreferenced with proj_destroy() after
2543
 * use.
2544
 * It should be used by at most one thread at a time.
2545
 *
2546
 * @param ctx PROJ context, or NULL for default context
2547
 * @param obj Object of type CRS or GeodeticReferenceFrame (must not be NULL)
2548
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2549
 * in case of error.
2550
 */
2551
2552
0
PJ *proj_get_prime_meridian(PJ_CONTEXT *ctx, const PJ *obj) {
2553
0
    SANITIZE_CTX(ctx);
2554
0
    auto ptr = obj->iso_obj.get();
2555
0
    if (dynamic_cast<CRS *>(ptr)) {
2556
0
        auto geodCRS = extractGeodeticCRS(ctx, obj, __FUNCTION__);
2557
0
        if (geodCRS) {
2558
0
            return pj_obj_create(ctx, geodCRS->primeMeridian());
2559
0
        }
2560
0
    } else {
2561
0
        auto datum = dynamic_cast<const GeodeticReferenceFrame *>(ptr);
2562
0
        if (datum) {
2563
0
            return pj_obj_create(ctx, datum->primeMeridian());
2564
0
        }
2565
0
    }
2566
0
    proj_log_error(ctx, __FUNCTION__,
2567
0
                   "Object is not a CRS or GeodeticReferenceFrame");
2568
0
    return nullptr;
2569
0
}
2570
2571
// ---------------------------------------------------------------------------
2572
2573
/** \brief Return prime meridian parameters.
2574
 *
2575
 * @param ctx PROJ context, or NULL for default context
2576
 * @param prime_meridian Object of type PrimeMeridian (must not be NULL)
2577
 * @param out_longitude Pointer to a value to store the longitude of the prime
2578
 * meridian, in its native unit. or NULL
2579
 * @param out_unit_conv_factor Pointer to a value to store the conversion
2580
 * factor of the prime meridian longitude unit to radian. or NULL
2581
 * @param out_unit_name Pointer to a string value to store the unit name.
2582
 * or NULL
2583
 * @return TRUE in case of success.
2584
 */
2585
int proj_prime_meridian_get_parameters(PJ_CONTEXT *ctx,
2586
                                       const PJ *prime_meridian,
2587
                                       double *out_longitude,
2588
                                       double *out_unit_conv_factor,
2589
0
                                       const char **out_unit_name) {
2590
0
    SANITIZE_CTX(ctx);
2591
0
    if (!prime_meridian) {
2592
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2593
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2594
0
        return false;
2595
0
    }
2596
0
    auto l_pm =
2597
0
        dynamic_cast<const PrimeMeridian *>(prime_meridian->iso_obj.get());
2598
0
    if (!l_pm) {
2599
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a PrimeMeridian");
2600
0
        return false;
2601
0
    }
2602
0
    const auto &longitude = l_pm->longitude();
2603
0
    if (out_longitude) {
2604
0
        *out_longitude = longitude.value();
2605
0
    }
2606
0
    const auto &unit = longitude.unit();
2607
0
    if (out_unit_conv_factor) {
2608
0
        *out_unit_conv_factor = unit.conversionToSI();
2609
0
    }
2610
0
    if (out_unit_name) {
2611
0
        *out_unit_name = unit.name().c_str();
2612
0
    }
2613
0
    return true;
2614
0
}
2615
2616
// ---------------------------------------------------------------------------
2617
2618
/** \brief Return the base CRS of a BoundCRS or a DerivedCRS/ProjectedCRS, or
2619
 * the source CRS of a CoordinateOperation, or the CRS of a CoordinateMetadata.
2620
 *
2621
 * The returned object must be unreferenced with proj_destroy() after
2622
 * use.
2623
 * It should be used by at most one thread at a time.
2624
 *
2625
 * @param ctx PROJ context, or NULL for default context
2626
 * @param obj Object of type BoundCRS or CoordinateOperation (must not be NULL)
2627
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2628
 * in case of error, or missing source CRS.
2629
 */
2630
0
PJ *proj_get_source_crs(PJ_CONTEXT *ctx, const PJ *obj) {
2631
0
    SANITIZE_CTX(ctx);
2632
0
    if (!obj) {
2633
0
        return nullptr;
2634
0
    }
2635
0
    auto ptr = obj->iso_obj.get();
2636
0
    auto boundCRS = dynamic_cast<const BoundCRS *>(ptr);
2637
0
    if (boundCRS) {
2638
0
        return pj_obj_create(ctx, boundCRS->baseCRS());
2639
0
    }
2640
0
    auto derivedCRS = dynamic_cast<const DerivedCRS *>(ptr);
2641
0
    if (derivedCRS) {
2642
0
        return pj_obj_create(ctx, derivedCRS->baseCRS());
2643
0
    }
2644
0
    auto co = dynamic_cast<const CoordinateOperation *>(ptr);
2645
0
    if (co) {
2646
0
        auto sourceCRS = co->sourceCRS();
2647
0
        if (sourceCRS) {
2648
0
            return pj_obj_create(ctx, NN_NO_CHECK(sourceCRS));
2649
0
        }
2650
0
        return nullptr;
2651
0
    }
2652
0
    if (!obj->alternativeCoordinateOperations.empty()) {
2653
0
        return proj_get_source_crs(ctx,
2654
0
                                   obj->alternativeCoordinateOperations[0].pj);
2655
0
    }
2656
0
    auto coordinateMetadata = dynamic_cast<const CoordinateMetadata *>(ptr);
2657
0
    if (coordinateMetadata) {
2658
0
        return pj_obj_create(ctx, coordinateMetadata->crs());
2659
0
    }
2660
2661
0
    proj_log_error(ctx, __FUNCTION__,
2662
0
                   "Object is not a BoundCRS, a CoordinateOperation or a "
2663
0
                   "CoordinateMetadata");
2664
0
    return nullptr;
2665
0
}
2666
2667
// ---------------------------------------------------------------------------
2668
2669
/** \brief Return the hub CRS of a BoundCRS or the target CRS of a
2670
 * CoordinateOperation.
2671
 *
2672
 * The returned object must be unreferenced with proj_destroy() after
2673
 * use.
2674
 * It should be used by at most one thread at a time.
2675
 *
2676
 * @param ctx PROJ context, or NULL for default context
2677
 * @param obj Object of type BoundCRS or CoordinateOperation (must not be NULL)
2678
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2679
 * in case of error, or missing target CRS.
2680
 */
2681
0
PJ *proj_get_target_crs(PJ_CONTEXT *ctx, const PJ *obj) {
2682
0
    SANITIZE_CTX(ctx);
2683
0
    if (!obj) {
2684
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2685
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2686
0
        return nullptr;
2687
0
    }
2688
0
    auto ptr = obj->iso_obj.get();
2689
0
    auto boundCRS = dynamic_cast<const BoundCRS *>(ptr);
2690
0
    if (boundCRS) {
2691
0
        return pj_obj_create(ctx, boundCRS->hubCRS());
2692
0
    }
2693
0
    auto co = dynamic_cast<const CoordinateOperation *>(ptr);
2694
0
    if (co) {
2695
0
        auto targetCRS = co->targetCRS();
2696
0
        if (targetCRS) {
2697
0
            return pj_obj_create(ctx, NN_NO_CHECK(targetCRS));
2698
0
        }
2699
0
        return nullptr;
2700
0
    }
2701
0
    if (!obj->alternativeCoordinateOperations.empty()) {
2702
0
        return proj_get_target_crs(ctx,
2703
0
                                   obj->alternativeCoordinateOperations[0].pj);
2704
0
    }
2705
0
    proj_log_error(ctx, __FUNCTION__,
2706
0
                   "Object is not a BoundCRS or a CoordinateOperation");
2707
0
    return nullptr;
2708
0
}
2709
2710
// ---------------------------------------------------------------------------
2711
2712
/** \brief Identify the CRS with reference CRSs.
2713
 *
2714
 * The candidate CRSs are either hard-coded, or looked in the database when
2715
 * it is available.
2716
 *
2717
 * Note that the implementation uses a set of heuristics to have a good
2718
 * compromise of successful identifications over execution time. It might miss
2719
 * legitimate matches in some circumstances.
2720
 *
2721
 * The method returns a list of matching reference CRS, and the percentage
2722
 * (0-100) of confidence in the match. The list is sorted by decreasing
2723
 * confidence.
2724
 * <ul>
2725
 * <li>100% means that the name of the reference entry
2726
 * perfectly matches the CRS name, and both are equivalent. In which case a
2727
 * single result is returned.
2728
 * Note: in the case of a GeographicCRS whose axis
2729
 * order is implicit in the input definition (for example ESRI WKT), then axis
2730
 * order is ignored for the purpose of identification. That is the CRS built
2731
 * from
2732
 * GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],
2733
 * PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]
2734
 * will be identified to EPSG:4326, but will not pass a
2735
 * isEquivalentTo(EPSG_4326, util::IComparable::Criterion::EQUIVALENT) test,
2736
 * but rather isEquivalentTo(EPSG_4326,
2737
 * util::IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS)
2738
 * </li>
2739
 * <li>90% means that CRS are equivalent, but the names are not exactly the
2740
 * same.</li>
2741
 * <li>70% means that CRS are equivalent, but the names are not equivalent.
2742
 * </li>
2743
 * <li>25% means that the CRS are not equivalent, but there is some similarity
2744
 * in
2745
 * the names.</li>
2746
 * </ul>
2747
 * Other confidence values may be returned by some specialized implementations.
2748
 *
2749
 * This is implemented for GeodeticCRS, ProjectedCRS, VerticalCRS and
2750
 * CompoundCRS.
2751
 *
2752
 * @param ctx PROJ context, or NULL for default context
2753
 * @param obj Object of type CRS. Must not be NULL
2754
 * @param auth_name Authority name, or NULL for all authorities
2755
 * @param options Placeholder for future options. Should be set to NULL.
2756
 * @param out_confidence Output parameter. Pointer to an array of integers that
2757
 * will be allocated by the function and filled with the confidence values
2758
 * (0-100). There are as many elements in this array as
2759
 * proj_list_get_count()
2760
 * returns on the return value of this function. *confidence should be
2761
 * released with proj_int_list_destroy().
2762
 * @return a list of matching reference CRS, or nullptr in case of error.
2763
 */
2764
PJ_OBJ_LIST *proj_identify(PJ_CONTEXT *ctx, const PJ *obj,
2765
                           const char *auth_name, const char *const *options,
2766
0
                           int **out_confidence) {
2767
0
    SANITIZE_CTX(ctx);
2768
0
    if (!obj) {
2769
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2770
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2771
0
        return nullptr;
2772
0
    }
2773
0
    (void)options;
2774
0
    if (out_confidence) {
2775
0
        *out_confidence = nullptr;
2776
0
    }
2777
0
    auto ptr = obj->iso_obj.get();
2778
0
    auto crs = dynamic_cast<const CRS *>(ptr);
2779
0
    if (!crs) {
2780
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CRS");
2781
0
    } else {
2782
0
        int *confidenceTemp = nullptr;
2783
0
        try {
2784
0
            auto factory = AuthorityFactory::create(getDBcontext(ctx),
2785
0
                                                    auth_name ? auth_name : "");
2786
0
            auto res = crs->identify(factory);
2787
0
            std::vector<IdentifiedObjectNNPtr> objects;
2788
0
            confidenceTemp = out_confidence ? new int[res.size()] : nullptr;
2789
0
            size_t i = 0;
2790
0
            for (const auto &pair : res) {
2791
0
                objects.push_back(pair.first);
2792
0
                if (confidenceTemp) {
2793
0
                    confidenceTemp[i] = pair.second;
2794
0
                    ++i;
2795
0
                }
2796
0
            }
2797
0
            auto ret = std::make_unique<PJ_OBJ_LIST>(std::move(objects));
2798
0
            if (out_confidence) {
2799
0
                *out_confidence = confidenceTemp;
2800
0
                confidenceTemp = nullptr;
2801
0
            }
2802
0
            return ret.release();
2803
0
        } catch (const std::exception &e) {
2804
0
            delete[] confidenceTemp;
2805
0
            proj_log_error(ctx, __FUNCTION__, e.what());
2806
0
        }
2807
0
    }
2808
0
    return nullptr;
2809
0
}
2810
2811
// ---------------------------------------------------------------------------
2812
2813
/** \brief Free an array of integer. */
2814
0
void proj_int_list_destroy(int *list) { delete[] list; }
2815
2816
// ---------------------------------------------------------------------------
2817
2818
/** \brief Return the list of authorities used in the database.
2819
 *
2820
 * The returned list is NULL terminated and must be freed with
2821
 * proj_string_list_destroy().
2822
 *
2823
 * @param ctx PROJ context, or NULL for default context
2824
 *
2825
 * @return a NULL terminated list of NUL-terminated strings that must be
2826
 * freed with proj_string_list_destroy(), or NULL in case of error.
2827
 */
2828
0
PROJ_STRING_LIST proj_get_authorities_from_database(PJ_CONTEXT *ctx) {
2829
0
    SANITIZE_CTX(ctx);
2830
0
    try {
2831
0
        auto ret = to_string_list(getDBcontext(ctx)->getAuthorities());
2832
0
        return ret;
2833
0
    } catch (const std::exception &e) {
2834
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2835
0
    }
2836
0
    return nullptr;
2837
0
}
2838
2839
// ---------------------------------------------------------------------------
2840
2841
/** \brief Returns the set of authority codes of the given object type.
2842
 *
2843
 * The returned list is NULL terminated and must be freed with
2844
 * proj_string_list_destroy().
2845
 *
2846
 * @param ctx PROJ context, or NULL for default context.
2847
 * @param auth_name Authority name (must not be NULL)
2848
 * @param type Object type.
2849
 * @param allow_deprecated whether we should return deprecated objects as well.
2850
 *
2851
 * @return a NULL terminated list of NUL-terminated strings that must be
2852
 * freed with proj_string_list_destroy(), or NULL in case of error.
2853
 *
2854
 * @see proj_get_crs_info_list_from_database()
2855
 */
2856
PROJ_STRING_LIST proj_get_codes_from_database(PJ_CONTEXT *ctx,
2857
                                              const char *auth_name,
2858
                                              PJ_TYPE type,
2859
0
                                              int allow_deprecated) {
2860
0
    SANITIZE_CTX(ctx);
2861
0
    if (!auth_name) {
2862
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2863
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2864
0
        return nullptr;
2865
0
    }
2866
0
    try {
2867
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx), auth_name);
2868
0
        bool valid = false;
2869
0
        auto typeInternal = convertPJObjectTypeToObjectType(type, valid);
2870
0
        if (!valid) {
2871
0
            return nullptr;
2872
0
        }
2873
0
        auto ret = to_string_list(
2874
0
            factory->getAuthorityCodes(typeInternal, allow_deprecated != 0));
2875
0
        return ret;
2876
2877
0
    } catch (const std::exception &e) {
2878
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2879
0
    }
2880
0
    return nullptr;
2881
0
}
2882
2883
// ---------------------------------------------------------------------------
2884
2885
/** \brief Enumerate celestial bodies from the database.
2886
 *
2887
 * The returned object is an array of PROJ_CELESTIAL_BODY_INFO* pointers, whose
2888
 * last entry is NULL. This array should be freed with
2889
 * proj_celestial_body_list_destroy()
2890
 *
2891
 * @param ctx PROJ context, or NULL for default context
2892
 * @param auth_name Authority name, used to restrict the search.
2893
 * Or NULL for all authorities.
2894
 * @param out_result_count Output parameter pointing to an integer to receive
2895
 * the size of the result list. Might be NULL
2896
 * @return an array of PROJ_CELESTIAL_BODY_INFO* pointers to be freed with
2897
 * proj_celestial_body_list_destroy(), or NULL in case of error.
2898
 * @since 8.1
2899
 */
2900
PROJ_CELESTIAL_BODY_INFO **proj_get_celestial_body_list_from_database(
2901
0
    PJ_CONTEXT *ctx, const char *auth_name, int *out_result_count) {
2902
0
    SANITIZE_CTX(ctx);
2903
0
    PROJ_CELESTIAL_BODY_INFO **ret = nullptr;
2904
0
    int i = 0;
2905
0
    try {
2906
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx),
2907
0
                                                auth_name ? auth_name : "");
2908
0
        auto list = factory->getCelestialBodyList();
2909
0
        ret = new PROJ_CELESTIAL_BODY_INFO *[list.size() + 1];
2910
0
        for (const auto &info : list) {
2911
0
            ret[i] = new PROJ_CELESTIAL_BODY_INFO;
2912
0
            ret[i]->auth_name = pj_strdup(info.authName.c_str());
2913
0
            ret[i]->name = pj_strdup(info.name.c_str());
2914
0
            i++;
2915
0
        }
2916
0
        ret[i] = nullptr;
2917
0
        if (out_result_count)
2918
0
            *out_result_count = i;
2919
0
        return ret;
2920
0
    } catch (const std::exception &e) {
2921
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2922
0
        if (ret) {
2923
0
            ret[i + 1] = nullptr;
2924
0
            proj_celestial_body_list_destroy(ret);
2925
0
        }
2926
0
        if (out_result_count)
2927
0
            *out_result_count = 0;
2928
0
    }
2929
0
    return nullptr;
2930
0
}
2931
2932
// ---------------------------------------------------------------------------
2933
2934
/** \brief Destroy the result returned by
2935
 * proj_get_celestial_body_list_from_database().
2936
 *
2937
 * @since 8.1
2938
 */
2939
0
void proj_celestial_body_list_destroy(PROJ_CELESTIAL_BODY_INFO **list) {
2940
0
    if (list) {
2941
0
        for (int i = 0; list[i] != nullptr; i++) {
2942
0
            free(list[i]->auth_name);
2943
0
            free(list[i]->name);
2944
0
            delete list[i];
2945
0
        }
2946
0
        delete[] list;
2947
0
    }
2948
0
}
2949
2950
// ---------------------------------------------------------------------------
2951
2952
/** Free a list of NULL terminated strings. */
2953
0
void proj_string_list_destroy(PROJ_STRING_LIST list) {
2954
0
    if (list) {
2955
0
        for (size_t i = 0; list[i] != nullptr; i++) {
2956
0
            delete[] list[i];
2957
0
        }
2958
0
        delete[] list;
2959
0
    }
2960
0
}
2961
2962
// ---------------------------------------------------------------------------
2963
2964
/** \brief Instantiate a default set of parameters to be used by
2965
 * proj_get_crs_list().
2966
 *
2967
 * @return a new object to free with proj_get_crs_list_parameters_destroy() */
2968
0
PROJ_CRS_LIST_PARAMETERS *proj_get_crs_list_parameters_create() {
2969
0
    auto ret = new (std::nothrow) PROJ_CRS_LIST_PARAMETERS();
2970
0
    if (ret) {
2971
0
        ret->types = nullptr;
2972
0
        ret->typesCount = 0;
2973
0
        ret->crs_area_of_use_contains_bbox = TRUE;
2974
0
        ret->bbox_valid = FALSE;
2975
0
        ret->west_lon_degree = 0.0;
2976
0
        ret->south_lat_degree = 0.0;
2977
0
        ret->east_lon_degree = 0.0;
2978
0
        ret->north_lat_degree = 0.0;
2979
0
        ret->allow_deprecated = FALSE;
2980
0
        ret->celestial_body_name = nullptr;
2981
0
    }
2982
0
    return ret;
2983
0
}
2984
2985
// ---------------------------------------------------------------------------
2986
2987
/** \brief Destroy an object returned by proj_get_crs_list_parameters_create()
2988
 */
2989
0
void proj_get_crs_list_parameters_destroy(PROJ_CRS_LIST_PARAMETERS *params) {
2990
0
    delete params;
2991
0
}
2992
2993
// ---------------------------------------------------------------------------
2994
2995
/** \brief Enumerate CRS objects from the database, taking into account various
2996
 * criteria.
2997
 *
2998
 * The returned object is an array of PROJ_CRS_INFO* pointers, whose last
2999
 * entry is NULL. This array should be freed with proj_crs_info_list_destroy()
3000
 *
3001
 * When no filter parameters are set, this is functionally equivalent to
3002
 * proj_get_codes_from_database(), instantiating a PJ* object for each
3003
 * of the codes with proj_create_from_database() and retrieving information
3004
 * with the various getters. However this function will be much faster.
3005
 *
3006
 * @param ctx PROJ context, or NULL for default context
3007
 * @param auth_name Authority name, used to restrict the search.
3008
 * Or NULL for all authorities.
3009
 * @param params Additional criteria, or NULL. If not-NULL, params SHOULD
3010
 * have been allocated by proj_get_crs_list_parameters_create(), as the
3011
 * PROJ_CRS_LIST_PARAMETERS structure might grow over time.
3012
 * @param out_result_count Output parameter pointing to an integer to receive
3013
 * the size of the result list. Might be NULL
3014
 * @return an array of PROJ_CRS_INFO* pointers to be freed with
3015
 * proj_crs_info_list_destroy(), or NULL in case of error.
3016
 */
3017
PROJ_CRS_INFO **
3018
proj_get_crs_info_list_from_database(PJ_CONTEXT *ctx, const char *auth_name,
3019
                                     const PROJ_CRS_LIST_PARAMETERS *params,
3020
0
                                     int *out_result_count) {
3021
0
    SANITIZE_CTX(ctx);
3022
0
    PROJ_CRS_INFO **ret = nullptr;
3023
0
    int i = 0;
3024
0
    try {
3025
0
        auto dbContext = getDBcontext(ctx);
3026
0
        std::string authName = auth_name ? auth_name : "";
3027
0
        auto actualAuthNames =
3028
0
            dbContext->getVersionedAuthoritiesFromName(authName);
3029
0
        if (actualAuthNames.empty())
3030
0
            actualAuthNames.push_back(std::move(authName));
3031
0
        std::list<AuthorityFactory::CRSInfo> concatList;
3032
0
        for (const auto &actualAuthName : actualAuthNames) {
3033
0
            auto factory = AuthorityFactory::create(dbContext, actualAuthName);
3034
0
            auto list = factory->getCRSInfoList();
3035
0
            concatList.splice(concatList.end(), std::move(list));
3036
0
        }
3037
0
        ret = new PROJ_CRS_INFO *[concatList.size() + 1];
3038
0
        GeographicBoundingBoxPtr bbox;
3039
0
        if (params && params->bbox_valid) {
3040
0
            bbox = GeographicBoundingBox::create(
3041
0
                       params->west_lon_degree, params->south_lat_degree,
3042
0
                       params->east_lon_degree, params->north_lat_degree)
3043
0
                       .as_nullable();
3044
0
        }
3045
0
        for (const auto &info : concatList) {
3046
0
            auto type = PJ_TYPE_CRS;
3047
0
            if (info.type == AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS) {
3048
0
                type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
3049
0
            } else if (info.type ==
3050
0
                       AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS) {
3051
0
                type = PJ_TYPE_GEOGRAPHIC_3D_CRS;
3052
0
            } else if (info.type ==
3053
0
                       AuthorityFactory::ObjectType::GEOCENTRIC_CRS) {
3054
0
                type = PJ_TYPE_GEOCENTRIC_CRS;
3055
0
            } else if (info.type ==
3056
0
                       AuthorityFactory::ObjectType::GEODETIC_CRS) {
3057
0
                type = PJ_TYPE_GEODETIC_CRS;
3058
0
            } else if (info.type ==
3059
0
                       AuthorityFactory::ObjectType::PROJECTED_CRS) {
3060
0
                type = PJ_TYPE_PROJECTED_CRS;
3061
0
            } else if (info.type ==
3062
0
                       AuthorityFactory::ObjectType::VERTICAL_CRS) {
3063
0
                type = PJ_TYPE_VERTICAL_CRS;
3064
0
            } else if (info.type ==
3065
0
                       AuthorityFactory::ObjectType::COMPOUND_CRS) {
3066
0
                type = PJ_TYPE_COMPOUND_CRS;
3067
0
            }
3068
0
            if (params && params->typesCount) {
3069
0
                bool typeValid = false;
3070
0
                for (size_t j = 0; j < params->typesCount; j++) {
3071
0
                    if (params->types[j] == type) {
3072
0
                        typeValid = true;
3073
0
                        break;
3074
0
                    } else if (params->types[j] == PJ_TYPE_GEOGRAPHIC_CRS &&
3075
0
                               (type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3076
0
                                type == PJ_TYPE_GEOGRAPHIC_3D_CRS)) {
3077
0
                        typeValid = true;
3078
0
                        break;
3079
0
                    } else if (params->types[j] == PJ_TYPE_GEODETIC_CRS &&
3080
0
                               (type == PJ_TYPE_GEOCENTRIC_CRS ||
3081
0
                                type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3082
0
                                type == PJ_TYPE_GEOGRAPHIC_3D_CRS)) {
3083
0
                        typeValid = true;
3084
0
                        break;
3085
0
                    }
3086
0
                }
3087
0
                if (!typeValid) {
3088
0
                    continue;
3089
0
                }
3090
0
            }
3091
0
            if (params && !params->allow_deprecated && info.deprecated) {
3092
0
                continue;
3093
0
            }
3094
0
            if (params && params->bbox_valid) {
3095
0
                if (!info.bbox_valid) {
3096
0
                    continue;
3097
0
                }
3098
0
                if (info.west_lon_degree <= info.east_lon_degree &&
3099
0
                    params->west_lon_degree <= params->east_lon_degree) {
3100
0
                    if (params->crs_area_of_use_contains_bbox) {
3101
0
                        if (params->west_lon_degree < info.west_lon_degree ||
3102
0
                            params->east_lon_degree > info.east_lon_degree ||
3103
0
                            params->south_lat_degree < info.south_lat_degree ||
3104
0
                            params->north_lat_degree > info.north_lat_degree) {
3105
0
                            continue;
3106
0
                        }
3107
0
                    } else {
3108
0
                        if (info.east_lon_degree < params->west_lon_degree ||
3109
0
                            info.west_lon_degree > params->east_lon_degree ||
3110
0
                            info.north_lat_degree < params->south_lat_degree ||
3111
0
                            info.south_lat_degree > params->north_lat_degree) {
3112
0
                            continue;
3113
0
                        }
3114
0
                    }
3115
0
                } else {
3116
0
                    auto crsExtent = GeographicBoundingBox::create(
3117
0
                        info.west_lon_degree, info.south_lat_degree,
3118
0
                        info.east_lon_degree, info.north_lat_degree);
3119
0
                    if (params->crs_area_of_use_contains_bbox) {
3120
0
                        if (!crsExtent->contains(NN_NO_CHECK(bbox))) {
3121
0
                            continue;
3122
0
                        }
3123
0
                    } else {
3124
0
                        if (!bbox->intersects(crsExtent)) {
3125
0
                            continue;
3126
0
                        }
3127
0
                    }
3128
0
                }
3129
0
            }
3130
0
            if (params && params->celestial_body_name &&
3131
0
                params->celestial_body_name != info.celestialBodyName) {
3132
0
                continue;
3133
0
            }
3134
3135
0
            ret[i] = new PROJ_CRS_INFO;
3136
0
            ret[i]->auth_name = pj_strdup(info.authName.c_str());
3137
0
            ret[i]->code = pj_strdup(info.code.c_str());
3138
0
            ret[i]->name = pj_strdup(info.name.c_str());
3139
0
            ret[i]->type = type;
3140
0
            ret[i]->deprecated = info.deprecated;
3141
0
            ret[i]->bbox_valid = info.bbox_valid;
3142
0
            ret[i]->west_lon_degree = info.west_lon_degree;
3143
0
            ret[i]->south_lat_degree = info.south_lat_degree;
3144
0
            ret[i]->east_lon_degree = info.east_lon_degree;
3145
0
            ret[i]->north_lat_degree = info.north_lat_degree;
3146
0
            ret[i]->area_name = pj_strdup(info.areaName.c_str());
3147
0
            ret[i]->projection_method_name =
3148
0
                info.projectionMethodName.empty()
3149
0
                    ? nullptr
3150
0
                    : pj_strdup(info.projectionMethodName.c_str());
3151
0
            ret[i]->celestial_body_name =
3152
0
                pj_strdup(info.celestialBodyName.c_str());
3153
0
            i++;
3154
0
        }
3155
0
        ret[i] = nullptr;
3156
0
        if (out_result_count)
3157
0
            *out_result_count = i;
3158
0
        return ret;
3159
0
    } catch (const std::exception &e) {
3160
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3161
0
        if (ret) {
3162
0
            ret[i + 1] = nullptr;
3163
0
            proj_crs_info_list_destroy(ret);
3164
0
        }
3165
0
        if (out_result_count)
3166
0
            *out_result_count = 0;
3167
0
    }
3168
0
    return nullptr;
3169
0
}
3170
3171
// ---------------------------------------------------------------------------
3172
3173
/** \brief Destroy the result returned by
3174
 * proj_get_crs_info_list_from_database().
3175
 */
3176
0
void proj_crs_info_list_destroy(PROJ_CRS_INFO **list) {
3177
0
    if (list) {
3178
0
        for (int i = 0; list[i] != nullptr; i++) {
3179
0
            free(list[i]->auth_name);
3180
0
            free(list[i]->code);
3181
0
            free(list[i]->name);
3182
0
            free(list[i]->area_name);
3183
0
            free(list[i]->projection_method_name);
3184
0
            free(list[i]->celestial_body_name);
3185
0
            delete list[i];
3186
0
        }
3187
0
        delete[] list;
3188
0
    }
3189
0
}
3190
3191
// ---------------------------------------------------------------------------
3192
3193
/** \brief Enumerate units from the database, taking into account various
3194
 * criteria.
3195
 *
3196
 * The returned object is an array of PROJ_UNIT_INFO* pointers, whose last
3197
 * entry is NULL. This array should be freed with proj_unit_list_destroy()
3198
 *
3199
 * @param ctx PROJ context, or NULL for default context
3200
 * @param auth_name Authority name, used to restrict the search.
3201
 * Or NULL for all authorities.
3202
 * @param category Filter by category, if this parameter is not NULL. Category
3203
 * is one of "linear", "linear_per_time", "angular", "angular_per_time",
3204
 * "scale", "scale_per_time" or "time"
3205
 * @param allow_deprecated whether we should return deprecated objects as well.
3206
 * @param out_result_count Output parameter pointing to an integer to receive
3207
 * the size of the result list. Might be NULL
3208
 * @return an array of PROJ_UNIT_INFO* pointers to be freed with
3209
 * proj_unit_list_destroy(), or NULL in case of error.
3210
 *
3211
 * @since 7.1
3212
 */
3213
PROJ_UNIT_INFO **proj_get_units_from_database(PJ_CONTEXT *ctx,
3214
                                              const char *auth_name,
3215
                                              const char *category,
3216
                                              int allow_deprecated,
3217
0
                                              int *out_result_count) {
3218
0
    SANITIZE_CTX(ctx);
3219
0
    PROJ_UNIT_INFO **ret = nullptr;
3220
0
    int i = 0;
3221
0
    try {
3222
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx),
3223
0
                                                auth_name ? auth_name : "");
3224
0
        auto list = factory->getUnitList();
3225
0
        ret = new PROJ_UNIT_INFO *[list.size() + 1];
3226
0
        for (const auto &info : list) {
3227
0
            if (category && info.category != category) {
3228
0
                continue;
3229
0
            }
3230
0
            if (!allow_deprecated && info.deprecated) {
3231
0
                continue;
3232
0
            }
3233
0
            ret[i] = new PROJ_UNIT_INFO;
3234
0
            ret[i]->auth_name = pj_strdup(info.authName.c_str());
3235
0
            ret[i]->code = pj_strdup(info.code.c_str());
3236
0
            ret[i]->name = pj_strdup(info.name.c_str());
3237
0
            ret[i]->category = pj_strdup(info.category.c_str());
3238
0
            ret[i]->conv_factor = info.convFactor;
3239
0
            ret[i]->proj_short_name =
3240
0
                info.projShortName.empty()
3241
0
                    ? nullptr
3242
0
                    : pj_strdup(info.projShortName.c_str());
3243
0
            ret[i]->deprecated = info.deprecated;
3244
0
            i++;
3245
0
        }
3246
0
        ret[i] = nullptr;
3247
0
        if (out_result_count)
3248
0
            *out_result_count = i;
3249
0
        return ret;
3250
0
    } catch (const std::exception &e) {
3251
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3252
0
        if (ret) {
3253
0
            ret[i + 1] = nullptr;
3254
0
            proj_unit_list_destroy(ret);
3255
0
        }
3256
0
        if (out_result_count)
3257
0
            *out_result_count = 0;
3258
0
    }
3259
0
    return nullptr;
3260
0
}
3261
3262
// ---------------------------------------------------------------------------
3263
3264
/** \brief Destroy the result returned by
3265
 * proj_get_units_from_database().
3266
 *
3267
 * @since 7.1
3268
 */
3269
0
void proj_unit_list_destroy(PROJ_UNIT_INFO **list) {
3270
0
    if (list) {
3271
0
        for (int i = 0; list[i] != nullptr; i++) {
3272
0
            free(list[i]->auth_name);
3273
0
            free(list[i]->code);
3274
0
            free(list[i]->name);
3275
0
            free(list[i]->category);
3276
0
            free(list[i]->proj_short_name);
3277
0
            delete list[i];
3278
0
        }
3279
0
        delete[] list;
3280
0
    }
3281
0
}
3282
3283
// ---------------------------------------------------------------------------
3284
3285
/** \brief Return the Conversion of a DerivedCRS (such as a ProjectedCRS),
3286
 * or the Transformation from the baseCRS to the hubCRS of a BoundCRS
3287
 *
3288
 * The returned object must be unreferenced with proj_destroy() after
3289
 * use.
3290
 * It should be used by at most one thread at a time.
3291
 *
3292
 * @param ctx PROJ context, or NULL for default context
3293
 * @param crs Object of type DerivedCRS or BoundCRSs (must not be NULL)
3294
 * @return Object of type SingleOperation that must be unreferenced with
3295
 * proj_destroy(), or NULL in case of error.
3296
 */
3297
0
PJ *proj_crs_get_coordoperation(PJ_CONTEXT *ctx, const PJ *crs) {
3298
0
    SANITIZE_CTX(ctx);
3299
0
    if (!crs) {
3300
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
3301
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
3302
0
        return nullptr;
3303
0
    }
3304
0
    SingleOperationPtr co;
3305
3306
0
    auto derivedCRS = dynamic_cast<const DerivedCRS *>(crs->iso_obj.get());
3307
0
    if (derivedCRS) {
3308
0
        co = derivedCRS->derivingConversion().as_nullable();
3309
0
    } else {
3310
0
        auto boundCRS = dynamic_cast<const BoundCRS *>(crs->iso_obj.get());
3311
0
        if (boundCRS) {
3312
0
            co = boundCRS->transformation().as_nullable();
3313
0
        } else {
3314
0
            proj_log_error(ctx, __FUNCTION__,
3315
0
                           "Object is not a DerivedCRS or BoundCRS");
3316
0
            return nullptr;
3317
0
        }
3318
0
    }
3319
3320
0
    return pj_obj_create(ctx, NN_NO_CHECK(co));
3321
0
}
3322
3323
// ---------------------------------------------------------------------------
3324
3325
/** \brief Return information on the operation method of the SingleOperation.
3326
 *
3327
 * @param ctx PROJ context, or NULL for default context
3328
 * @param coordoperation Object of type SingleOperation (typically a Conversion
3329
 * or Transformation) (must not be NULL)
3330
 * @param out_method_name Pointer to a string value to store the method
3331
 * (projection) name. or NULL
3332
 * @param out_method_auth_name Pointer to a string value to store the method
3333
 * authority name. or NULL
3334
 * @param out_method_code Pointer to a string value to store the method
3335
 * code. or NULL
3336
 * @return TRUE in case of success.
3337
 */
3338
int proj_coordoperation_get_method_info(PJ_CONTEXT *ctx,
3339
                                        const PJ *coordoperation,
3340
                                        const char **out_method_name,
3341
                                        const char **out_method_auth_name,
3342
0
                                        const char **out_method_code) {
3343
0
    SANITIZE_CTX(ctx);
3344
0
    if (!coordoperation) {
3345
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
3346
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
3347
0
        return false;
3348
0
    }
3349
0
    auto singleOp =
3350
0
        dynamic_cast<const SingleOperation *>(coordoperation->iso_obj.get());
3351
0
    if (!singleOp) {
3352
0
        proj_log_error(ctx, __FUNCTION__,
3353
0
                       "Object is not a DerivedCRS or BoundCRS");
3354
0
        return false;
3355
0
    }
3356
3357
0
    const auto &method = singleOp->method();
3358
0
    const auto &method_ids = method->identifiers();
3359
0
    if (out_method_name) {
3360
0
        *out_method_name = method->name()->description()->c_str();
3361
0
    }
3362
0
    if (out_method_auth_name) {
3363
0
        if (!method_ids.empty()) {
3364
0
            *out_method_auth_name = method_ids[0]->codeSpace()->c_str();
3365
0
        } else {
3366
0
            *out_method_auth_name = nullptr;
3367
0
        }
3368
0
    }
3369
0
    if (out_method_code) {
3370
0
        if (!method_ids.empty()) {
3371
0
            *out_method_code = method_ids[0]->code().c_str();
3372
0
        } else {
3373
0
            *out_method_code = nullptr;
3374
0
        }
3375
0
    }
3376
0
    return true;
3377
0
}
3378
3379
// ---------------------------------------------------------------------------
3380
3381
//! @cond Doxygen_Suppress
3382
static PropertyMap createPropertyMapName(const char *c_name,
3383
                                         const char *auth_name = nullptr,
3384
0
                                         const char *code = nullptr) {
3385
0
    std::string name(c_name ? c_name : "unnamed");
3386
0
    PropertyMap properties;
3387
0
    if (ends_with(name, " (deprecated)")) {
3388
0
        name.resize(name.size() - strlen(" (deprecated)"));
3389
0
        properties.set(common::IdentifiedObject::DEPRECATED_KEY, true);
3390
0
    }
3391
0
    if (auth_name && code) {
3392
0
        properties.set(metadata::Identifier::CODESPACE_KEY, auth_name);
3393
0
        properties.set(metadata::Identifier::CODE_KEY, code);
3394
0
    }
3395
0
    return properties.set(common::IdentifiedObject::NAME_KEY, name);
3396
0
}
3397
3398
// ---------------------------------------------------------------------------
3399
3400
static UnitOfMeasure createLinearUnit(const char *name, double convFactor,
3401
                                      const char *unit_auth_name = nullptr,
3402
0
                                      const char *unit_code = nullptr) {
3403
0
    return name == nullptr
3404
0
               ? UnitOfMeasure::METRE
3405
0
               : UnitOfMeasure(name, convFactor, UnitOfMeasure::Type::LINEAR,
3406
0
                               unit_auth_name ? unit_auth_name : "",
3407
0
                               unit_code ? unit_code : "");
3408
0
}
3409
3410
// ---------------------------------------------------------------------------
3411
3412
static UnitOfMeasure createAngularUnit(const char *name, double convFactor,
3413
                                       const char *unit_auth_name = nullptr,
3414
0
                                       const char *unit_code = nullptr) {
3415
0
    return name ? (ci_equal(name, "degree") ? UnitOfMeasure::DEGREE
3416
0
                   : ci_equal(name, "grad")
3417
0
                       ? UnitOfMeasure::GRAD
3418
0
                       : UnitOfMeasure(name, convFactor,
3419
0
                                       UnitOfMeasure::Type::ANGULAR,
3420
0
                                       unit_auth_name ? unit_auth_name : "",
3421
0
                                       unit_code ? unit_code : ""))
3422
0
                : UnitOfMeasure::DEGREE;
3423
0
}
3424
3425
// ---------------------------------------------------------------------------
3426
3427
static GeodeticReferenceFrameNNPtr createGeodeticReferenceFrame(
3428
    PJ_CONTEXT *ctx, const char *datum_name, const char *ellps_name,
3429
    double semi_major_metre, double inv_flattening,
3430
    const char *prime_meridian_name, double prime_meridian_offset,
3431
0
    const char *angular_units, double angular_units_conv) {
3432
0
    const UnitOfMeasure angUnit(
3433
0
        createAngularUnit(angular_units, angular_units_conv));
3434
0
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
3435
0
    auto body = Ellipsoid::guessBodyName(dbContext, semi_major_metre);
3436
0
    auto ellpsName = createPropertyMapName(ellps_name);
3437
0
    auto ellps = inv_flattening != 0.0
3438
0
                     ? Ellipsoid::createFlattenedSphere(
3439
0
                           ellpsName, Length(semi_major_metre),
3440
0
                           Scale(inv_flattening), body)
3441
0
                     : Ellipsoid::createSphere(ellpsName,
3442
0
                                               Length(semi_major_metre), body);
3443
0
    auto pm = PrimeMeridian::create(
3444
0
        PropertyMap().set(
3445
0
            common::IdentifiedObject::NAME_KEY,
3446
0
            prime_meridian_name ? prime_meridian_name
3447
0
            : prime_meridian_offset == 0.0
3448
0
                ? (ellps->celestialBody() == Ellipsoid::EARTH
3449
0
                       ? PrimeMeridian::GREENWICH->nameStr().c_str()
3450
0
                       : PrimeMeridian::REFERENCE_MERIDIAN->nameStr().c_str())
3451
0
                : "unnamed"),
3452
0
        Angle(prime_meridian_offset, angUnit));
3453
3454
0
    std::string datumName(datum_name ? datum_name : "unnamed");
3455
0
    if (datumName == "WGS_1984") {
3456
0
        datumName = GeodeticReferenceFrame::EPSG_6326->nameStr();
3457
0
    } else if (datumName.find('_') != std::string::npos) {
3458
        // Likely coming from WKT1
3459
0
        if (dbContext) {
3460
0
            auto authFactory =
3461
0
                AuthorityFactory::create(NN_NO_CHECK(dbContext), std::string());
3462
0
            auto res = authFactory->createObjectsFromName(
3463
0
                datumName,
3464
0
                {AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME}, true,
3465
0
                1);
3466
0
            if (!res.empty()) {
3467
0
                const auto &refDatum = res.front();
3468
0
                if (metadata::Identifier::isEquivalentName(
3469
0
                        datumName.c_str(), refDatum->nameStr().c_str())) {
3470
0
                    datumName = refDatum->nameStr();
3471
0
                } else if (refDatum->identifiers().size() == 1) {
3472
0
                    const auto &id = refDatum->identifiers()[0];
3473
0
                    const auto aliases =
3474
0
                        authFactory->databaseContext()->getAliases(
3475
0
                            *id->codeSpace(), id->code(), refDatum->nameStr(),
3476
0
                            "geodetic_datum", std::string());
3477
0
                    for (const auto &alias : aliases) {
3478
0
                        if (metadata::Identifier::isEquivalentName(
3479
0
                                datumName.c_str(), alias.c_str())) {
3480
0
                            datumName = refDatum->nameStr();
3481
0
                            break;
3482
0
                        }
3483
0
                    }
3484
0
                }
3485
0
            }
3486
0
        }
3487
0
    }
3488
3489
0
    return GeodeticReferenceFrame::create(
3490
0
        createPropertyMapName(datumName.c_str()), ellps,
3491
0
        util::optional<std::string>(), pm);
3492
0
}
3493
3494
//! @endcond
3495
3496
// ---------------------------------------------------------------------------
3497
3498
/** \brief Create a GeographicCRS.
3499
 *
3500
 * The returned object must be unreferenced with proj_destroy() after
3501
 * use.
3502
 * It should be used by at most one thread at a time.
3503
 *
3504
 * @param ctx PROJ context, or NULL for default context
3505
 * @param crs_name Name of the GeographicCRS. Or NULL
3506
 * @param datum_name Name of the GeodeticReferenceFrame. Or NULL
3507
 * @param ellps_name Name of the Ellipsoid. Or NULL
3508
 * @param semi_major_metre Ellipsoid semi-major axis, in metres.
3509
 * @param inv_flattening Ellipsoid inverse flattening. Or 0 for a sphere.
3510
 * @param prime_meridian_name Name of the PrimeMeridian. Or NULL
3511
 * @param prime_meridian_offset Offset of the prime meridian, expressed in the
3512
 * specified angular units.
3513
 * @param pm_angular_units Name of the angular units. Or NULL for Degree
3514
 * @param pm_angular_units_conv Conversion factor from the angular unit to
3515
 * radian.
3516
 * Or
3517
 * 0 for Degree if pm_angular_units == NULL. Otherwise should be not NULL
3518
 * @param ellipsoidal_cs Coordinate system. Must not be NULL.
3519
 *
3520
 * @return Object of type GeographicCRS that must be unreferenced with
3521
 * proj_destroy(), or NULL in case of error.
3522
 */
3523
PJ *proj_create_geographic_crs(PJ_CONTEXT *ctx, const char *crs_name,
3524
                               const char *datum_name, const char *ellps_name,
3525
                               double semi_major_metre, double inv_flattening,
3526
                               const char *prime_meridian_name,
3527
                               double prime_meridian_offset,
3528
                               const char *pm_angular_units,
3529
                               double pm_angular_units_conv,
3530
0
                               const PJ *ellipsoidal_cs) {
3531
3532
0
    SANITIZE_CTX(ctx);
3533
0
    auto cs = std::dynamic_pointer_cast<EllipsoidalCS>(ellipsoidal_cs->iso_obj);
3534
0
    if (!cs) {
3535
0
        return nullptr;
3536
0
    }
3537
0
    try {
3538
0
        auto datum = createGeodeticReferenceFrame(
3539
0
            ctx, datum_name, ellps_name, semi_major_metre, inv_flattening,
3540
0
            prime_meridian_name, prime_meridian_offset, pm_angular_units,
3541
0
            pm_angular_units_conv);
3542
0
        auto geogCRS = GeographicCRS::create(createPropertyMapName(crs_name),
3543
0
                                             datum, NN_NO_CHECK(cs));
3544
0
        return pj_obj_create(ctx, geogCRS);
3545
0
    } catch (const std::exception &e) {
3546
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3547
0
    }
3548
0
    return nullptr;
3549
0
}
3550
3551
// ---------------------------------------------------------------------------
3552
3553
/** \brief Create a GeographicCRS.
3554
 *
3555
 * The returned object must be unreferenced with proj_destroy() after
3556
 * use.
3557
 * It should be used by at most one thread at a time.
3558
 *
3559
 * @param ctx PROJ context, or NULL for default context
3560
 * @param crs_name Name of the GeographicCRS. Or NULL
3561
 * @param datum_or_datum_ensemble Datum or DatumEnsemble (DatumEnsemble possible
3562
 * since 7.2). Must not be NULL.
3563
 * @param ellipsoidal_cs Coordinate system. Must not be NULL.
3564
 *
3565
 * @return Object of type GeographicCRS that must be unreferenced with
3566
 * proj_destroy(), or NULL in case of error.
3567
 */
3568
PJ *proj_create_geographic_crs_from_datum(PJ_CONTEXT *ctx, const char *crs_name,
3569
                                          const PJ *datum_or_datum_ensemble,
3570
0
                                          const PJ *ellipsoidal_cs) {
3571
3572
0
    SANITIZE_CTX(ctx);
3573
0
    if (datum_or_datum_ensemble == nullptr) {
3574
0
        proj_log_error(ctx, __FUNCTION__,
3575
0
                       "Missing input datum_or_datum_ensemble");
3576
0
        return nullptr;
3577
0
    }
3578
0
    auto l_datum = std::dynamic_pointer_cast<GeodeticReferenceFrame>(
3579
0
        datum_or_datum_ensemble->iso_obj);
3580
0
    auto l_datum_ensemble = std::dynamic_pointer_cast<DatumEnsemble>(
3581
0
        datum_or_datum_ensemble->iso_obj);
3582
0
    auto cs = std::dynamic_pointer_cast<EllipsoidalCS>(ellipsoidal_cs->iso_obj);
3583
0
    if (!cs) {
3584
0
        return nullptr;
3585
0
    }
3586
0
    try {
3587
0
        auto geogCRS =
3588
0
            GeographicCRS::create(createPropertyMapName(crs_name), l_datum,
3589
0
                                  l_datum_ensemble, NN_NO_CHECK(cs));
3590
0
        return pj_obj_create(ctx, geogCRS);
3591
0
    } catch (const std::exception &e) {
3592
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3593
0
    }
3594
0
    return nullptr;
3595
0
}
3596
3597
// ---------------------------------------------------------------------------
3598
3599
/** \brief Create a GeodeticCRS of geocentric type.
3600
 *
3601
 * The returned object must be unreferenced with proj_destroy() after
3602
 * use.
3603
 * It should be used by at most one thread at a time.
3604
 *
3605
 * @param ctx PROJ context, or NULL for default context
3606
 * @param crs_name Name of the GeographicCRS. Or NULL
3607
 * @param datum_name Name of the GeodeticReferenceFrame. Or NULL
3608
 * @param ellps_name Name of the Ellipsoid. Or NULL
3609
 * @param semi_major_metre Ellipsoid semi-major axis, in metres.
3610
 * @param inv_flattening Ellipsoid inverse flattening. Or 0 for a sphere.
3611
 * @param prime_meridian_name Name of the PrimeMeridian. Or NULL
3612
 * @param prime_meridian_offset Offset of the prime meridian, expressed in the
3613
 * specified angular units.
3614
 * @param angular_units Name of the angular units. Or NULL for Degree
3615
 * @param angular_units_conv Conversion factor from the angular unit to radian.
3616
 * Or
3617
 * 0 for Degree if angular_units == NULL. Otherwise should be not NULL
3618
 * @param linear_units Name of the linear units. Or NULL for Metre
3619
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
3620
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
3621
 *
3622
 * @return Object of type GeodeticCRS that must be unreferenced with
3623
 * proj_destroy(), or NULL in case of error.
3624
 */
3625
PJ *proj_create_geocentric_crs(
3626
    PJ_CONTEXT *ctx, const char *crs_name, const char *datum_name,
3627
    const char *ellps_name, double semi_major_metre, double inv_flattening,
3628
    const char *prime_meridian_name, double prime_meridian_offset,
3629
    const char *angular_units, double angular_units_conv,
3630
0
    const char *linear_units, double linear_units_conv) {
3631
3632
0
    SANITIZE_CTX(ctx);
3633
0
    try {
3634
0
        const UnitOfMeasure linearUnit(
3635
0
            createLinearUnit(linear_units, linear_units_conv));
3636
0
        auto datum = createGeodeticReferenceFrame(
3637
0
            ctx, datum_name, ellps_name, semi_major_metre, inv_flattening,
3638
0
            prime_meridian_name, prime_meridian_offset, angular_units,
3639
0
            angular_units_conv);
3640
3641
0
        auto geodCRS =
3642
0
            GeodeticCRS::create(createPropertyMapName(crs_name), datum,
3643
0
                                cs::CartesianCS::createGeocentric(linearUnit));
3644
0
        return pj_obj_create(ctx, geodCRS);
3645
0
    } catch (const std::exception &e) {
3646
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3647
0
    }
3648
0
    return nullptr;
3649
0
}
3650
3651
// ---------------------------------------------------------------------------
3652
3653
/** \brief Create a GeodeticCRS of geocentric type.
3654
 *
3655
 * The returned object must be unreferenced with proj_destroy() after
3656
 * use.
3657
 * It should be used by at most one thread at a time.
3658
 *
3659
 * @param ctx PROJ context, or NULL for default context
3660
 * @param crs_name Name of the GeographicCRS. Or NULL
3661
 * @param datum_or_datum_ensemble Datum or DatumEnsemble (DatumEnsemble possible
3662
 * since 7.2). Must not be NULL.
3663
 * @param linear_units Name of the linear units. Or NULL for Metre
3664
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
3665
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
3666
 *
3667
 * @return Object of type GeodeticCRS that must be unreferenced with
3668
 * proj_destroy(), or NULL in case of error.
3669
 */
3670
PJ *proj_create_geocentric_crs_from_datum(PJ_CONTEXT *ctx, const char *crs_name,
3671
                                          const PJ *datum_or_datum_ensemble,
3672
                                          const char *linear_units,
3673
0
                                          double linear_units_conv) {
3674
0
    SANITIZE_CTX(ctx);
3675
0
    if (datum_or_datum_ensemble == nullptr) {
3676
0
        proj_log_error(ctx, __FUNCTION__,
3677
0
                       "Missing input datum_or_datum_ensemble");
3678
0
        return nullptr;
3679
0
    }
3680
0
    auto l_datum = std::dynamic_pointer_cast<GeodeticReferenceFrame>(
3681
0
        datum_or_datum_ensemble->iso_obj);
3682
0
    auto l_datum_ensemble = std::dynamic_pointer_cast<DatumEnsemble>(
3683
0
        datum_or_datum_ensemble->iso_obj);
3684
0
    try {
3685
0
        const UnitOfMeasure linearUnit(
3686
0
            createLinearUnit(linear_units, linear_units_conv));
3687
0
        auto geodCRS = GeodeticCRS::create(
3688
0
            createPropertyMapName(crs_name), l_datum, l_datum_ensemble,
3689
0
            cs::CartesianCS::createGeocentric(linearUnit));
3690
0
        return pj_obj_create(ctx, geodCRS);
3691
0
    } catch (const std::exception &e) {
3692
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3693
0
    }
3694
0
    return nullptr;
3695
0
}
3696
3697
// ---------------------------------------------------------------------------
3698
3699
/** \brief Create a DerivedGeograhicCRS.
3700
 *
3701
 * The returned object must be unreferenced with proj_destroy() after
3702
 * use.
3703
 * It should be used by at most one thread at a time.
3704
 *
3705
 * @param ctx PROJ context, or NULL for default context
3706
 * @param crs_name Name of the GeographicCRS. Or NULL
3707
 * @param base_geographic_crs Base Geographic CRS. Must not be NULL.
3708
 * @param conversion Conversion from the base Geographic to the
3709
 * DerivedGeograhicCRS. Must not be NULL.
3710
 * @param ellipsoidal_cs Coordinate system. Must not be NULL.
3711
 *
3712
 * @return Object of type GeodeticCRS that must be unreferenced with
3713
 * proj_destroy(), or NULL in case of error.
3714
 *
3715
 * @since 7.0
3716
 */
3717
PJ *proj_create_derived_geographic_crs(PJ_CONTEXT *ctx, const char *crs_name,
3718
                                       const PJ *base_geographic_crs,
3719
                                       const PJ *conversion,
3720
0
                                       const PJ *ellipsoidal_cs) {
3721
0
    SANITIZE_CTX(ctx);
3722
0
    auto base_crs =
3723
0
        std::dynamic_pointer_cast<GeographicCRS>(base_geographic_crs->iso_obj);
3724
0
    auto conversion_cpp =
3725
0
        std::dynamic_pointer_cast<Conversion>(conversion->iso_obj);
3726
0
    auto cs = std::dynamic_pointer_cast<EllipsoidalCS>(ellipsoidal_cs->iso_obj);
3727
0
    if (!base_crs || !conversion_cpp || !cs) {
3728
0
        return nullptr;
3729
0
    }
3730
0
    try {
3731
0
        auto derivedCRS = DerivedGeographicCRS::create(
3732
0
            createPropertyMapName(crs_name), NN_NO_CHECK(base_crs),
3733
0
            NN_NO_CHECK(conversion_cpp), NN_NO_CHECK(cs));
3734
0
        return pj_obj_create(ctx, derivedCRS);
3735
0
    } catch (const std::exception &e) {
3736
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3737
0
    }
3738
0
    return nullptr;
3739
0
}
3740
3741
// ---------------------------------------------------------------------------
3742
3743
/** \brief Return whether a CRS is a Derived CRS.
3744
 *
3745
 * @param ctx PROJ context, or NULL for default context
3746
 * @param crs CRS. Must not be NULL.
3747
 *
3748
 * @return whether a CRS is a Derived CRS.
3749
 *
3750
 * @since 7.0
3751
 */
3752
0
int proj_is_derived_crs(PJ_CONTEXT *ctx, const PJ *crs) {
3753
0
    SANITIZE_CTX(ctx);
3754
0
    return dynamic_cast<DerivedCRS *>(crs->iso_obj.get()) != nullptr;
3755
0
}
3756
3757
// ---------------------------------------------------------------------------
3758
3759
/** \brief Create a VerticalCRS
3760
 *
3761
 * The returned object must be unreferenced with proj_destroy() after
3762
 * use.
3763
 * It should be used by at most one thread at a time.
3764
 *
3765
 * @param ctx PROJ context, or NULL for default context
3766
 * @param crs_name Name of the GeographicCRS. Or NULL
3767
 * @param datum_name Name of the VerticalReferenceFrame. Or NULL
3768
 * @param linear_units Name of the linear units. Or NULL for Metre
3769
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
3770
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
3771
 *
3772
 * @return Object of type VerticalCRS that must be unreferenced with
3773
 * proj_destroy(), or NULL in case of error.
3774
 */
3775
PJ *proj_create_vertical_crs(PJ_CONTEXT *ctx, const char *crs_name,
3776
                             const char *datum_name, const char *linear_units,
3777
0
                             double linear_units_conv) {
3778
3779
0
    return proj_create_vertical_crs_ex(
3780
0
        ctx, crs_name, datum_name, nullptr, nullptr, linear_units,
3781
0
        linear_units_conv, nullptr, nullptr, nullptr, nullptr, nullptr);
3782
0
}
3783
3784
// ---------------------------------------------------------------------------
3785
3786
/** \brief Create a VerticalCRS
3787
 *
3788
 * The returned object must be unreferenced with proj_destroy() after
3789
 * use.
3790
 * It should be used by at most one thread at a time.
3791
 *
3792
 * This is an extended (_ex) version of proj_create_vertical_crs() that adds
3793
 * the capability of defining a geoid model.
3794
 *
3795
 * @param ctx PROJ context, or NULL for default context
3796
 * @param crs_name Name of the GeographicCRS. Or NULL
3797
 * @param datum_name Name of the VerticalReferenceFrame. Or NULL
3798
 * @param datum_auth_name Authority name of the VerticalReferenceFrame. Or NULL
3799
 * @param datum_code Code of the VerticalReferenceFrame. Or NULL
3800
 * @param linear_units Name of the linear units. Or NULL for Metre
3801
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
3802
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
3803
 * @param geoid_model_name Geoid model name, or NULL. Can be a name from the
3804
 * geoid_model name or a string "PROJ foo.gtx"
3805
 * @param geoid_model_auth_name Authority name of the transformation for
3806
 * the geoid model. or NULL
3807
 * @param geoid_model_code Code of the transformation for
3808
 * the geoid model. or NULL
3809
 * @param geoid_geog_crs Geographic CRS for the geoid transformation, or NULL.
3810
 * @param options NULL-terminated list of strings with "KEY=VALUE" format. or
3811
 * NULL.
3812
 * The currently recognized option is ACCURACY=value, where value is in metre.
3813
 * @return Object of type VerticalCRS that must be unreferenced with
3814
 * proj_destroy(), or NULL in case of error.
3815
 */
3816
PJ *proj_create_vertical_crs_ex(
3817
    PJ_CONTEXT *ctx, const char *crs_name, const char *datum_name,
3818
    const char *datum_auth_name, const char *datum_code,
3819
    const char *linear_units, double linear_units_conv,
3820
    const char *geoid_model_name, const char *geoid_model_auth_name,
3821
    const char *geoid_model_code, const PJ *geoid_geog_crs,
3822
0
    const char *const *options) {
3823
0
    SANITIZE_CTX(ctx);
3824
0
    try {
3825
0
        const UnitOfMeasure linearUnit(
3826
0
            createLinearUnit(linear_units, linear_units_conv));
3827
0
        auto datum = VerticalReferenceFrame::create(
3828
0
            createPropertyMapName(datum_name, datum_auth_name, datum_code));
3829
0
        auto props = createPropertyMapName(crs_name);
3830
0
        auto cs = cs::VerticalCS::createGravityRelatedHeight(linearUnit);
3831
0
        if (geoid_model_name) {
3832
0
            auto propsModel = createPropertyMapName(
3833
0
                geoid_model_name, geoid_model_auth_name, geoid_model_code);
3834
0
            const auto vertCRSWithoutGeoid =
3835
0
                VerticalCRS::create(props, datum, cs);
3836
0
            const auto interpCRS =
3837
0
                geoid_geog_crs && std::dynamic_pointer_cast<GeographicCRS>(
3838
0
                                      geoid_geog_crs->iso_obj)
3839
0
                    ? std::dynamic_pointer_cast<CRS>(geoid_geog_crs->iso_obj)
3840
0
                    : nullptr;
3841
3842
0
            std::vector<metadata::PositionalAccuracyNNPtr> accuracies;
3843
0
            for (auto iter = options; iter && iter[0]; ++iter) {
3844
0
                const char *value;
3845
0
                if ((value = getOptionValue(*iter, "ACCURACY="))) {
3846
0
                    accuracies.emplace_back(
3847
0
                        metadata::PositionalAccuracy::create(value));
3848
0
                }
3849
0
            }
3850
0
            const auto model(Transformation::create(
3851
0
                propsModel, vertCRSWithoutGeoid,
3852
0
                GeographicCRS::EPSG_4979, // arbitrarily chosen. Ignored
3853
0
                interpCRS,
3854
0
                OperationMethod::create(PropertyMap(),
3855
0
                                        std::vector<OperationParameterNNPtr>()),
3856
0
                {}, accuracies));
3857
0
            props.set("GEOID_MODEL", model);
3858
0
        }
3859
0
        auto vertCRS = VerticalCRS::create(props, datum, cs);
3860
0
        return pj_obj_create(ctx, vertCRS);
3861
0
    } catch (const std::exception &e) {
3862
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3863
0
    }
3864
0
    return nullptr;
3865
0
}
3866
3867
// ---------------------------------------------------------------------------
3868
3869
/** \brief Create a CompoundCRS
3870
 *
3871
 * The returned object must be unreferenced with proj_destroy() after
3872
 * use.
3873
 * It should be used by at most one thread at a time.
3874
 *
3875
 * @param ctx PROJ context, or NULL for default context
3876
 * @param crs_name Name of the GeographicCRS. Or NULL
3877
 * @param horiz_crs Horizontal CRS. must not be NULL.
3878
 * @param vert_crs Vertical CRS. must not be NULL.
3879
 *
3880
 * @return Object of type CompoundCRS that must be unreferenced with
3881
 * proj_destroy(), or NULL in case of error.
3882
 */
3883
PJ *proj_create_compound_crs(PJ_CONTEXT *ctx, const char *crs_name,
3884
0
                             const PJ *horiz_crs, const PJ *vert_crs) {
3885
3886
0
    SANITIZE_CTX(ctx);
3887
0
    if (!horiz_crs || !vert_crs) {
3888
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
3889
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
3890
0
        return nullptr;
3891
0
    }
3892
0
    auto l_horiz_crs = std::dynamic_pointer_cast<CRS>(horiz_crs->iso_obj);
3893
0
    if (!l_horiz_crs) {
3894
0
        return nullptr;
3895
0
    }
3896
0
    auto l_vert_crs = std::dynamic_pointer_cast<CRS>(vert_crs->iso_obj);
3897
0
    if (!l_vert_crs) {
3898
0
        return nullptr;
3899
0
    }
3900
0
    try {
3901
0
        auto compoundCRS = CompoundCRS::create(
3902
0
            createPropertyMapName(crs_name),
3903
0
            {NN_NO_CHECK(l_horiz_crs), NN_NO_CHECK(l_vert_crs)});
3904
0
        return pj_obj_create(ctx, compoundCRS);
3905
0
    } catch (const std::exception &e) {
3906
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3907
0
    }
3908
0
    return nullptr;
3909
0
}
3910
3911
// ---------------------------------------------------------------------------
3912
3913
/** \brief Return a copy of the object with its name changed
3914
 *
3915
 * Currently, only implemented on CRS objects.
3916
 *
3917
 * The returned object must be unreferenced with proj_destroy() after
3918
 * use.
3919
 * It should be used by at most one thread at a time.
3920
 *
3921
 * @param ctx PROJ context, or NULL for default context
3922
 * @param obj Object of type CRS. Must not be NULL
3923
 * @param name New name. Must not be NULL
3924
 *
3925
 * @return Object that must be unreferenced with
3926
 * proj_destroy(), or NULL in case of error.
3927
 */
3928
0
PJ *proj_alter_name(PJ_CONTEXT *ctx, const PJ *obj, const char *name) {
3929
0
    SANITIZE_CTX(ctx);
3930
0
    if (!obj || !name) {
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->alterName(name));
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 object with its identifier changed/set
3950
 *
3951
 * Currently, only implemented on CRS objects.
3952
 *
3953
 * The returned object must be unreferenced with proj_destroy() after
3954
 * use.
3955
 * It should be used by at most one thread at a time.
3956
 *
3957
 * @param ctx PROJ context, or NULL for default context
3958
 * @param obj Object of type CRS. Must not be NULL
3959
 * @param auth_name Authority name. Must not be NULL
3960
 * @param code Code. Must not be NULL
3961
 *
3962
 * @return Object that must be unreferenced with
3963
 * proj_destroy(), or NULL in case of error.
3964
 */
3965
PJ *proj_alter_id(PJ_CONTEXT *ctx, const PJ *obj, const char *auth_name,
3966
0
                  const char *code) {
3967
0
    SANITIZE_CTX(ctx);
3968
0
    if (!obj || !auth_name || !code) {
3969
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
3970
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
3971
0
        return nullptr;
3972
0
    }
3973
0
    auto crs = dynamic_cast<const CRS *>(obj->iso_obj.get());
3974
0
    if (!crs) {
3975
0
        return nullptr;
3976
0
    }
3977
0
    try {
3978
0
        return pj_obj_create(ctx, crs->alterId(auth_name, code));
3979
0
    } catch (const std::exception &e) {
3980
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3981
0
    }
3982
0
    return nullptr;
3983
0
}
3984
3985
// ---------------------------------------------------------------------------
3986
3987
/** \brief Return a copy of the CRS with its geodetic CRS changed
3988
 *
3989
 * Currently, when obj is a GeodeticCRS, it returns a clone of new_geod_crs
3990
 * When obj is a ProjectedCRS, it replaces its base CRS with new_geod_crs.
3991
 * When obj is a CompoundCRS, it replaces the GeodeticCRS part of the horizontal
3992
 * CRS with new_geod_crs.
3993
 * In other cases, it returns a clone of obj.
3994
 *
3995
 * The returned object must be unreferenced with proj_destroy() after
3996
 * use.
3997
 * It should be used by at most one thread at a time.
3998
 *
3999
 * @param ctx PROJ context, or NULL for default context
4000
 * @param obj Object of type CRS. Must not be NULL
4001
 * @param new_geod_crs Object of type GeodeticCRS. Must not be NULL
4002
 *
4003
 * @return Object that must be unreferenced with
4004
 * proj_destroy(), or NULL in case of error.
4005
 */
4006
PJ *proj_crs_alter_geodetic_crs(PJ_CONTEXT *ctx, const PJ *obj,
4007
0
                                const PJ *new_geod_crs) {
4008
0
    SANITIZE_CTX(ctx);
4009
0
    if (!obj || !new_geod_crs) {
4010
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4011
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4012
0
        return nullptr;
4013
0
    }
4014
0
    auto l_new_geod_crs =
4015
0
        std::dynamic_pointer_cast<GeodeticCRS>(new_geod_crs->iso_obj);
4016
0
    if (!l_new_geod_crs) {
4017
0
        proj_log_error(ctx, __FUNCTION__, "new_geod_crs is not a GeodeticCRS");
4018
0
        return nullptr;
4019
0
    }
4020
4021
0
    auto crs = dynamic_cast<const CRS *>(obj->iso_obj.get());
4022
0
    if (!crs) {
4023
0
        proj_log_error(ctx, __FUNCTION__, "obj is not a CRS");
4024
0
        return nullptr;
4025
0
    }
4026
4027
0
    try {
4028
0
        return pj_obj_create(
4029
0
            ctx, crs->alterGeodeticCRS(NN_NO_CHECK(l_new_geod_crs)));
4030
0
    } catch (const std::exception &e) {
4031
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4032
0
        return nullptr;
4033
0
    }
4034
0
}
4035
4036
// ---------------------------------------------------------------------------
4037
4038
/** \brief Return a copy of the CRS with its angular units changed
4039
 *
4040
 * The CRS must be or contain a GeographicCRS.
4041
 *
4042
 * The returned object must be unreferenced with proj_destroy() after
4043
 * use.
4044
 * It should be used by at most one thread at a time.
4045
 *
4046
 * @param ctx PROJ context, or NULL for default context
4047
 * @param obj Object of type CRS. Must not be NULL
4048
 * @param angular_units Name of the angular units. Or NULL for Degree
4049
 * @param angular_units_conv Conversion factor from the angular unit to radian.
4050
 * Or 0 for Degree if angular_units == NULL. Otherwise should be not NULL
4051
 * @param unit_auth_name Unit authority name. Or NULL.
4052
 * @param unit_code Unit code. Or NULL.
4053
 *
4054
 * @return Object that must be unreferenced with
4055
 * proj_destroy(), or NULL in case of error.
4056
 */
4057
PJ *proj_crs_alter_cs_angular_unit(PJ_CONTEXT *ctx, const PJ *obj,
4058
                                   const char *angular_units,
4059
                                   double angular_units_conv,
4060
                                   const char *unit_auth_name,
4061
0
                                   const char *unit_code) {
4062
4063
0
    SANITIZE_CTX(ctx);
4064
0
    auto geodCRS = proj_crs_get_geodetic_crs(ctx, obj);
4065
0
    if (!geodCRS) {
4066
0
        return nullptr;
4067
0
    }
4068
0
    auto geogCRS = dynamic_cast<const GeographicCRS *>(geodCRS->iso_obj.get());
4069
0
    if (!geogCRS) {
4070
0
        proj_destroy(geodCRS);
4071
0
        return nullptr;
4072
0
    }
4073
4074
0
    PJ *geogCRSAltered = nullptr;
4075
0
    try {
4076
0
        const UnitOfMeasure angUnit(createAngularUnit(
4077
0
            angular_units, angular_units_conv, unit_auth_name, unit_code));
4078
0
        geogCRSAltered = pj_obj_create(
4079
0
            ctx, GeographicCRS::create(
4080
0
                     createPropertyMapName(proj_get_name(geodCRS)),
4081
0
                     geogCRS->datum(), geogCRS->datumEnsemble(),
4082
0
                     geogCRS->coordinateSystem()->alterAngularUnit(angUnit)));
4083
0
        proj_destroy(geodCRS);
4084
0
    } catch (const std::exception &e) {
4085
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4086
0
        proj_destroy(geodCRS);
4087
0
        return nullptr;
4088
0
    }
4089
4090
0
    auto ret = proj_crs_alter_geodetic_crs(ctx, obj, geogCRSAltered);
4091
0
    proj_destroy(geogCRSAltered);
4092
0
    return ret;
4093
0
}
4094
4095
// ---------------------------------------------------------------------------
4096
4097
/** \brief Return a copy of the CRS with the linear units of its coordinate
4098
 * system changed
4099
 *
4100
 * The CRS must be or contain a ProjectedCRS, VerticalCRS or a GeocentricCRS.
4101
 *
4102
 * The returned object must be unreferenced with proj_destroy() after
4103
 * use.
4104
 * It should be used by at most one thread at a time.
4105
 *
4106
 * @param ctx PROJ context, or NULL for default context
4107
 * @param obj Object of type CRS. Must not be NULL
4108
 * @param linear_units Name of the linear units. Or NULL for Metre
4109
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
4110
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
4111
 * @param unit_auth_name Unit authority name. Or NULL.
4112
 * @param unit_code Unit code. Or NULL.
4113
 *
4114
 * @return Object that must be unreferenced with
4115
 * proj_destroy(), or NULL in case of error.
4116
 */
4117
PJ *proj_crs_alter_cs_linear_unit(PJ_CONTEXT *ctx, const PJ *obj,
4118
                                  const char *linear_units,
4119
                                  double linear_units_conv,
4120
                                  const char *unit_auth_name,
4121
0
                                  const char *unit_code) {
4122
0
    SANITIZE_CTX(ctx);
4123
0
    if (!obj) {
4124
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4125
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4126
0
        return nullptr;
4127
0
    }
4128
0
    auto crs = dynamic_cast<const CRS *>(obj->iso_obj.get());
4129
0
    if (!crs) {
4130
0
        return nullptr;
4131
0
    }
4132
4133
0
    try {
4134
0
        const UnitOfMeasure linearUnit(createLinearUnit(
4135
0
            linear_units, linear_units_conv, unit_auth_name, unit_code));
4136
0
        return pj_obj_create(ctx, crs->alterCSLinearUnit(linearUnit));
4137
0
    } catch (const std::exception &e) {
4138
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4139
0
        return nullptr;
4140
0
    }
4141
0
}
4142
4143
// ---------------------------------------------------------------------------
4144
4145
/** \brief Return a copy of the CRS with the linear units of the parameters
4146
 * of its conversion modified.
4147
 *
4148
 * The CRS must be or contain a ProjectedCRS, VerticalCRS or a GeocentricCRS.
4149
 *
4150
 * The returned object must be unreferenced with proj_destroy() after
4151
 * use.
4152
 * It should be used by at most one thread at a time.
4153
 *
4154
 * @param ctx PROJ context, or NULL for default context
4155
 * @param obj Object of type ProjectedCRS. Must not be NULL
4156
 * @param linear_units Name of the linear units. Or NULL for Metre
4157
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
4158
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
4159
 * @param unit_auth_name Unit authority name. Or NULL.
4160
 * @param unit_code Unit code. Or NULL.
4161
 * @param convert_to_new_unit TRUE if existing values should be converted from
4162
 * their current unit to the new unit. If FALSE, their value will be left
4163
 * unchanged and the unit overridden (so the resulting CRS will not be
4164
 * equivalent to the original one for reprojection purposes).
4165
 *
4166
 * @return Object that must be unreferenced with
4167
 * proj_destroy(), or NULL in case of error.
4168
 */
4169
PJ *proj_crs_alter_parameters_linear_unit(PJ_CONTEXT *ctx, const PJ *obj,
4170
                                          const char *linear_units,
4171
                                          double linear_units_conv,
4172
                                          const char *unit_auth_name,
4173
                                          const char *unit_code,
4174
0
                                          int convert_to_new_unit) {
4175
0
    SANITIZE_CTX(ctx);
4176
0
    if (!obj) {
4177
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4178
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4179
0
        return nullptr;
4180
0
    }
4181
0
    auto crs = dynamic_cast<const ProjectedCRS *>(obj->iso_obj.get());
4182
0
    if (!crs) {
4183
0
        return nullptr;
4184
0
    }
4185
4186
0
    try {
4187
0
        const UnitOfMeasure linearUnit(createLinearUnit(
4188
0
            linear_units, linear_units_conv, unit_auth_name, unit_code));
4189
0
        return pj_obj_create(ctx, crs->alterParametersLinearUnit(
4190
0
                                      linearUnit, convert_to_new_unit == TRUE));
4191
0
    } catch (const std::exception &e) {
4192
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4193
0
        return nullptr;
4194
0
    }
4195
0
}
4196
4197
// ---------------------------------------------------------------------------
4198
4199
/** \brief Create a 3D CRS from an existing 2D CRS.
4200
 *
4201
 * The new axis will be ellipsoidal height, oriented upwards, and with metre
4202
 * units.
4203
 *
4204
 * See osgeo::proj::crs::CRS::promoteTo3D().
4205
 *
4206
 * The returned object must be unreferenced with proj_destroy() after
4207
 * use.
4208
 * It should be used by at most one thread at a time.
4209
 *
4210
 * @param ctx PROJ context, or NULL for default context
4211
 * @param crs_3D_name CRS name. Or NULL (in which case the name of crs_2D
4212
 * will be used)
4213
 * @param crs_2D 2D CRS to be "promoted" to 3D. Must not be NULL.
4214
 *
4215
 * @return Object that must be unreferenced with
4216
 * proj_destroy(), or NULL in case of error.
4217
 * @since 6.3
4218
 */
4219
PJ *proj_crs_promote_to_3D(PJ_CONTEXT *ctx, const char *crs_3D_name,
4220
0
                           const PJ *crs_2D) {
4221
0
    SANITIZE_CTX(ctx);
4222
0
    if (!crs_2D) {
4223
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4224
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4225
0
        return nullptr;
4226
0
    }
4227
0
    auto cpp_2D_crs = dynamic_cast<const CRS *>(crs_2D->iso_obj.get());
4228
0
    if (!cpp_2D_crs) {
4229
0
        auto coordinateMetadata =
4230
0
            dynamic_cast<const CoordinateMetadata *>(crs_2D->iso_obj.get());
4231
0
        if (!coordinateMetadata) {
4232
0
            proj_log_error(ctx, __FUNCTION__,
4233
0
                           "crs_2D is not a CRS or a CoordinateMetadata");
4234
0
            return nullptr;
4235
0
        }
4236
4237
0
        try {
4238
0
            auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
4239
0
            auto crs = coordinateMetadata->crs();
4240
0
            auto crs_3D = crs->promoteTo3D(
4241
0
                crs_3D_name ? std::string(crs_3D_name) : crs->nameStr(),
4242
0
                dbContext);
4243
0
            if (coordinateMetadata->coordinateEpoch().has_value()) {
4244
0
                return pj_obj_create(
4245
0
                    ctx, CoordinateMetadata::create(
4246
0
                             crs_3D,
4247
0
                             coordinateMetadata->coordinateEpochAsDecimalYear(),
4248
0
                             dbContext));
4249
0
            } else {
4250
0
                return pj_obj_create(ctx, CoordinateMetadata::create(crs_3D));
4251
0
            }
4252
0
        } catch (const std::exception &e) {
4253
0
            proj_log_error(ctx, __FUNCTION__, e.what());
4254
0
            return nullptr;
4255
0
        }
4256
0
    } else {
4257
0
        try {
4258
0
            auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
4259
0
            return pj_obj_create(ctx, cpp_2D_crs->promoteTo3D(
4260
0
                                          crs_3D_name ? std::string(crs_3D_name)
4261
0
                                                      : cpp_2D_crs->nameStr(),
4262
0
                                          dbContext));
4263
0
        } catch (const std::exception &e) {
4264
0
            proj_log_error(ctx, __FUNCTION__, e.what());
4265
0
            return nullptr;
4266
0
        }
4267
0
    }
4268
0
}
4269
4270
// ---------------------------------------------------------------------------
4271
4272
/** \brief Create a projected 3D CRS from an existing projected 2D CRS.
4273
 *
4274
 * The passed projected_2D_crs is used so that its name is replaced by
4275
 * crs_name and its base geographic CRS is replaced by geog_3D_crs. The vertical
4276
 * axis of geog_3D_crs (ellipsoidal height) will be added as the 3rd axis of
4277
 * the resulting projected 3D CRS.
4278
 * Normally, the passed geog_3D_crs should be the 3D counterpart of the original
4279
 * 2D base geographic CRS of projected_2D_crs, but such no check is done.
4280
 *
4281
 * It is also possible to invoke this function with a NULL geog_3D_crs. In which
4282
 * case, the existing base geographic 2D CRS of projected_2D_crs will be
4283
 * automatically promoted to 3D by assuming a 3rd axis being an ellipsoidal
4284
 * height, oriented upwards, and with metre units. This is equivalent to using
4285
 * proj_crs_promote_to_3D().
4286
 *
4287
 * The returned object must be unreferenced with proj_destroy() after
4288
 * use.
4289
 * It should be used by at most one thread at a time.
4290
 *
4291
 * @param ctx PROJ context, or NULL for default context
4292
 * @param crs_name CRS name. Or NULL (in which case the name of projected_2D_crs
4293
 * will be used)
4294
 * @param projected_2D_crs Projected 2D CRS to be "promoted" to 3D. Must not be
4295
 * NULL.
4296
 * @param geog_3D_crs Base geographic 3D CRS for the new CRS. May be NULL.
4297
 *
4298
 * @return Object that must be unreferenced with
4299
 * proj_destroy(), or NULL in case of error.
4300
 * @since 6.3
4301
 */
4302
PJ *proj_crs_create_projected_3D_crs_from_2D(PJ_CONTEXT *ctx,
4303
                                             const char *crs_name,
4304
                                             const PJ *projected_2D_crs,
4305
0
                                             const PJ *geog_3D_crs) {
4306
0
    SANITIZE_CTX(ctx);
4307
0
    if (!projected_2D_crs) {
4308
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4309
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4310
0
        return nullptr;
4311
0
    }
4312
0
    auto cpp_projected_2D_crs =
4313
0
        dynamic_cast<const ProjectedCRS *>(projected_2D_crs->iso_obj.get());
4314
0
    if (!cpp_projected_2D_crs) {
4315
0
        proj_log_error(ctx, __FUNCTION__,
4316
0
                       "projected_2D_crs is not a Projected CRS");
4317
0
        return nullptr;
4318
0
    }
4319
0
    const auto &oldCS = cpp_projected_2D_crs->coordinateSystem();
4320
0
    const auto &oldCSAxisList = oldCS->axisList();
4321
4322
0
    if (geog_3D_crs && geog_3D_crs->iso_obj) {
4323
0
        auto cpp_geog_3D_CRS =
4324
0
            std::dynamic_pointer_cast<GeographicCRS>(geog_3D_crs->iso_obj);
4325
0
        if (!cpp_geog_3D_CRS) {
4326
0
            proj_log_error(ctx, __FUNCTION__,
4327
0
                           "geog_3D_crs is not a Geographic CRS");
4328
0
            return nullptr;
4329
0
        }
4330
4331
0
        const auto &geogCS = cpp_geog_3D_CRS->coordinateSystem();
4332
0
        const auto &geogCSAxisList = geogCS->axisList();
4333
0
        if (geogCSAxisList.size() != 3) {
4334
0
            proj_log_error(ctx, __FUNCTION__,
4335
0
                           "geog_3D_crs is not a Geographic 3D CRS");
4336
0
            return nullptr;
4337
0
        }
4338
0
        try {
4339
0
            auto newCS =
4340
0
                cs::CartesianCS::create(PropertyMap(), oldCSAxisList[0],
4341
0
                                        oldCSAxisList[1], geogCSAxisList[2]);
4342
0
            return pj_obj_create(
4343
0
                ctx,
4344
0
                ProjectedCRS::create(
4345
0
                    createPropertyMapName(
4346
0
                        crs_name ? crs_name
4347
0
                                 : cpp_projected_2D_crs->nameStr().c_str()),
4348
0
                    NN_NO_CHECK(cpp_geog_3D_CRS),
4349
0
                    cpp_projected_2D_crs->derivingConversion(), newCS));
4350
0
        } catch (const std::exception &e) {
4351
0
            proj_log_error(ctx, __FUNCTION__, e.what());
4352
0
            return nullptr;
4353
0
        }
4354
0
    } else {
4355
0
        try {
4356
0
            auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
4357
0
            return pj_obj_create(ctx,
4358
0
                                 cpp_projected_2D_crs->promoteTo3D(
4359
0
                                     crs_name ? std::string(crs_name)
4360
0
                                              : cpp_projected_2D_crs->nameStr(),
4361
0
                                     dbContext));
4362
0
        } catch (const std::exception &e) {
4363
0
            proj_log_error(ctx, __FUNCTION__, e.what());
4364
0
            return nullptr;
4365
0
        }
4366
0
    }
4367
0
}
4368
4369
// ---------------------------------------------------------------------------
4370
4371
/** \brief Create a 2D CRS from an existing 3D CRS.
4372
 *
4373
 * See osgeo::proj::crs::CRS::demoteTo2D().
4374
 *
4375
 * The returned object must be unreferenced with proj_destroy() after
4376
 * use.
4377
 * It should be used by at most one thread at a time.
4378
 *
4379
 * @param ctx PROJ context, or NULL for default context
4380
 * @param crs_2D_name CRS name. Or NULL (in which case the name of crs_3D
4381
 * will be used)
4382
 * @param crs_3D 3D CRS to be "demoted" to 2D. Must not be NULL.
4383
 *
4384
 * @return Object that must be unreferenced with
4385
 * proj_destroy(), or NULL in case of error.
4386
 * @since 6.3
4387
 */
4388
PJ *proj_crs_demote_to_2D(PJ_CONTEXT *ctx, const char *crs_2D_name,
4389
0
                          const PJ *crs_3D) {
4390
0
    SANITIZE_CTX(ctx);
4391
0
    if (!crs_3D) {
4392
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4393
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4394
0
        return nullptr;
4395
0
    }
4396
0
    auto cpp_3D_crs = dynamic_cast<const CRS *>(crs_3D->iso_obj.get());
4397
0
    if (!cpp_3D_crs) {
4398
0
        proj_log_error(ctx, __FUNCTION__, "crs_3D is not a CRS");
4399
0
        return nullptr;
4400
0
    }
4401
0
    try {
4402
0
        auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
4403
0
        return pj_obj_create(
4404
0
            ctx, cpp_3D_crs->demoteTo2D(crs_2D_name ? std::string(crs_2D_name)
4405
0
                                                    : cpp_3D_crs->nameStr(),
4406
0
                                        dbContext));
4407
0
    } catch (const std::exception &e) {
4408
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4409
0
        return nullptr;
4410
0
    }
4411
0
}
4412
4413
// ---------------------------------------------------------------------------
4414
4415
/** \brief Instantiate a EngineeringCRS with just a name
4416
 *
4417
 * The returned object must be unreferenced with proj_destroy() after
4418
 * use.
4419
 * It should be used by at most one thread at a time.
4420
 *
4421
 * @param ctx PROJ context, or NULL for default context
4422
 * @param crs_name CRS name. Or NULL.
4423
 *
4424
 * @return Object that must be unreferenced with
4425
 * proj_destroy(), or NULL in case of error.
4426
 */
4427
0
PJ *proj_create_engineering_crs(PJ_CONTEXT *ctx, const char *crs_name) {
4428
0
    SANITIZE_CTX(ctx);
4429
0
    try {
4430
0
        return pj_obj_create(
4431
0
            ctx, EngineeringCRS::create(
4432
0
                     createPropertyMapName(crs_name),
4433
0
                     EngineeringDatum::create(
4434
0
                         createPropertyMapName(UNKNOWN_ENGINEERING_DATUM)),
4435
0
                     CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)));
4436
0
    } catch (const std::exception &e) {
4437
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4438
0
        return nullptr;
4439
0
    }
4440
0
}
4441
4442
// ---------------------------------------------------------------------------
4443
4444
//! @cond Doxygen_Suppress
4445
4446
static void setSingleOperationElements(
4447
    const char *name, const char *auth_name, const char *code,
4448
    const char *method_name, const char *method_auth_name,
4449
    const char *method_code, int param_count,
4450
    const PJ_PARAM_DESCRIPTION *params, PropertyMap &propSingleOp,
4451
    PropertyMap &propMethod, std::vector<OperationParameterNNPtr> &parameters,
4452
0
    std::vector<ParameterValueNNPtr> &values) {
4453
0
    propSingleOp.set(common::IdentifiedObject::NAME_KEY,
4454
0
                     name ? name : "unnamed");
4455
0
    if (auth_name && code) {
4456
0
        propSingleOp.set(metadata::Identifier::CODESPACE_KEY, auth_name)
4457
0
            .set(metadata::Identifier::CODE_KEY, code);
4458
0
    }
4459
4460
0
    propMethod.set(common::IdentifiedObject::NAME_KEY,
4461
0
                   method_name ? method_name : "unnamed");
4462
0
    if (method_auth_name && method_code) {
4463
0
        propMethod.set(metadata::Identifier::CODESPACE_KEY, method_auth_name)
4464
0
            .set(metadata::Identifier::CODE_KEY, method_code);
4465
0
    }
4466
4467
0
    for (int i = 0; i < param_count; i++) {
4468
0
        PropertyMap propParam;
4469
0
        propParam.set(common::IdentifiedObject::NAME_KEY,
4470
0
                      params[i].name ? params[i].name : "unnamed");
4471
0
        if (params[i].auth_name && params[i].code) {
4472
0
            propParam
4473
0
                .set(metadata::Identifier::CODESPACE_KEY, params[i].auth_name)
4474
0
                .set(metadata::Identifier::CODE_KEY, params[i].code);
4475
0
        }
4476
0
        parameters.emplace_back(OperationParameter::create(propParam));
4477
0
        auto unit_type = UnitOfMeasure::Type::UNKNOWN;
4478
0
        switch (params[i].unit_type) {
4479
0
        case PJ_UT_ANGULAR:
4480
0
            unit_type = UnitOfMeasure::Type::ANGULAR;
4481
0
            break;
4482
0
        case PJ_UT_LINEAR:
4483
0
            unit_type = UnitOfMeasure::Type::LINEAR;
4484
0
            break;
4485
0
        case PJ_UT_SCALE:
4486
0
            unit_type = UnitOfMeasure::Type::SCALE;
4487
0
            break;
4488
0
        case PJ_UT_TIME:
4489
0
            unit_type = UnitOfMeasure::Type::TIME;
4490
0
            break;
4491
0
        case PJ_UT_PARAMETRIC:
4492
0
            unit_type = UnitOfMeasure::Type::PARAMETRIC;
4493
0
            break;
4494
0
        }
4495
4496
0
        Measure measure(
4497
0
            params[i].value,
4498
0
            params[i].unit_type == PJ_UT_ANGULAR
4499
0
                ? createAngularUnit(params[i].unit_name,
4500
0
                                    params[i].unit_conv_factor)
4501
0
            : params[i].unit_type == PJ_UT_LINEAR
4502
0
                ? createLinearUnit(params[i].unit_name,
4503
0
                                   params[i].unit_conv_factor)
4504
0
                : UnitOfMeasure(params[i].unit_name ? params[i].unit_name
4505
0
                                                    : "unnamed",
4506
0
                                params[i].unit_conv_factor, unit_type));
4507
0
        values.emplace_back(ParameterValue::create(measure));
4508
0
    }
4509
0
}
4510
4511
//! @endcond
4512
4513
// ---------------------------------------------------------------------------
4514
4515
/** \brief Instantiate a Conversion
4516
 *
4517
 * The returned object must be unreferenced with proj_destroy() after
4518
 * use.
4519
 * It should be used by at most one thread at a time.
4520
 *
4521
 * @param ctx PROJ context, or NULL for default context
4522
 * @param name Conversion name. Or NULL.
4523
 * @param auth_name Conversion authority name. Or NULL.
4524
 * @param code Conversion code. Or NULL.
4525
 * @param method_name Method name. Or NULL.
4526
 * @param method_auth_name Method authority name. Or NULL.
4527
 * @param method_code Method code. Or NULL.
4528
 * @param param_count Number of parameters (size of params argument)
4529
 * @param params Parameter descriptions (array of size param_count)
4530
 *
4531
 * @return Object that must be unreferenced with
4532
 * proj_destroy(), or NULL in case of error.
4533
 */
4534
4535
PJ *proj_create_conversion(PJ_CONTEXT *ctx, const char *name,
4536
                           const char *auth_name, const char *code,
4537
                           const char *method_name,
4538
                           const char *method_auth_name,
4539
                           const char *method_code, int param_count,
4540
0
                           const PJ_PARAM_DESCRIPTION *params) {
4541
0
    SANITIZE_CTX(ctx);
4542
0
    try {
4543
0
        PropertyMap propSingleOp;
4544
0
        PropertyMap propMethod;
4545
0
        std::vector<OperationParameterNNPtr> parameters;
4546
0
        std::vector<ParameterValueNNPtr> values;
4547
4548
0
        setSingleOperationElements(
4549
0
            name, auth_name, code, method_name, method_auth_name, method_code,
4550
0
            param_count, params, propSingleOp, propMethod, parameters, values);
4551
4552
0
        return pj_obj_create(ctx, Conversion::create(propSingleOp, propMethod,
4553
0
                                                     parameters, values));
4554
0
    } catch (const std::exception &e) {
4555
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4556
0
        return nullptr;
4557
0
    }
4558
0
}
4559
4560
// ---------------------------------------------------------------------------
4561
4562
/** \brief Instantiate a Transformation
4563
 *
4564
 * The returned object must be unreferenced with proj_destroy() after
4565
 * use.
4566
 * It should be used by at most one thread at a time.
4567
 *
4568
 * @param ctx PROJ context, or NULL for default context
4569
 * @param name Transformation name. Or NULL.
4570
 * @param auth_name Transformation authority name. Or NULL.
4571
 * @param code Transformation code. Or NULL.
4572
 * @param source_crs Object of type CRS representing the source CRS.
4573
 * Must not be NULL.
4574
 * @param target_crs Object of type CRS representing the target CRS.
4575
 * Must not be NULL.
4576
 * @param interpolation_crs Object of type CRS representing the interpolation
4577
 * CRS. Or NULL.
4578
 * @param method_name Method name. Or NULL.
4579
 * @param method_auth_name Method authority name. Or NULL.
4580
 * @param method_code Method code. Or NULL.
4581
 * @param param_count Number of parameters (size of params argument)
4582
 * @param params Parameter descriptions (array of size param_count)
4583
 * @param accuracy Accuracy of the transformation in meters. A negative
4584
 * values means unknown.
4585
 *
4586
 * @return Object that must be unreferenced with
4587
 * proj_destroy(), or NULL in case of error.
4588
 */
4589
4590
PJ *proj_create_transformation(
4591
    PJ_CONTEXT *ctx, const char *name, const char *auth_name, const char *code,
4592
    const PJ *source_crs, const PJ *target_crs, const PJ *interpolation_crs,
4593
    const char *method_name, const char *method_auth_name,
4594
    const char *method_code, int param_count,
4595
0
    const PJ_PARAM_DESCRIPTION *params, double accuracy) {
4596
0
    SANITIZE_CTX(ctx);
4597
0
    if (!source_crs || !target_crs) {
4598
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4599
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4600
0
        return nullptr;
4601
0
    }
4602
4603
0
    auto l_sourceCRS = std::dynamic_pointer_cast<CRS>(source_crs->iso_obj);
4604
0
    if (!l_sourceCRS) {
4605
0
        proj_log_error(ctx, __FUNCTION__, "source_crs is not a CRS");
4606
0
        return nullptr;
4607
0
    }
4608
4609
0
    auto l_targetCRS = std::dynamic_pointer_cast<CRS>(target_crs->iso_obj);
4610
0
    if (!l_targetCRS) {
4611
0
        proj_log_error(ctx, __FUNCTION__, "target_crs is not a CRS");
4612
0
        return nullptr;
4613
0
    }
4614
4615
0
    CRSPtr l_interpolationCRS;
4616
0
    if (interpolation_crs) {
4617
0
        l_interpolationCRS =
4618
0
            std::dynamic_pointer_cast<CRS>(interpolation_crs->iso_obj);
4619
0
        if (!l_interpolationCRS) {
4620
0
            proj_log_error(ctx, __FUNCTION__, "interpolation_crs is not a CRS");
4621
0
            return nullptr;
4622
0
        }
4623
0
    }
4624
4625
0
    try {
4626
0
        PropertyMap propSingleOp;
4627
0
        PropertyMap propMethod;
4628
0
        std::vector<OperationParameterNNPtr> parameters;
4629
0
        std::vector<ParameterValueNNPtr> values;
4630
4631
0
        setSingleOperationElements(
4632
0
            name, auth_name, code, method_name, method_auth_name, method_code,
4633
0
            param_count, params, propSingleOp, propMethod, parameters, values);
4634
4635
0
        std::vector<metadata::PositionalAccuracyNNPtr> accuracies;
4636
0
        if (accuracy >= 0.0) {
4637
0
            accuracies.emplace_back(
4638
0
                PositionalAccuracy::create(toString(accuracy)));
4639
0
        }
4640
4641
0
        return pj_obj_create(
4642
0
            ctx,
4643
0
            Transformation::create(propSingleOp, NN_NO_CHECK(l_sourceCRS),
4644
0
                                   NN_NO_CHECK(l_targetCRS), l_interpolationCRS,
4645
0
                                   propMethod, parameters, values, accuracies));
4646
0
    } catch (const std::exception &e) {
4647
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4648
0
        return nullptr;
4649
0
    }
4650
0
}
4651
4652
// ---------------------------------------------------------------------------
4653
4654
/**
4655
 * \brief Return an equivalent projection.
4656
 *
4657
 * Currently implemented:
4658
 * <ul>
4659
 * <li>EPSG_CODE_METHOD_MERCATOR_VARIANT_A (1SP) to
4660
 * EPSG_CODE_METHOD_MERCATOR_VARIANT_B (2SP)</li>
4661
 * <li>EPSG_CODE_METHOD_MERCATOR_VARIANT_B (2SP) to
4662
 * EPSG_CODE_METHOD_MERCATOR_VARIANT_A (1SP)</li>
4663
 * <li>EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP to
4664
 * EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP</li>
4665
 * <li>EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP to
4666
 * EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP</li>
4667
 * </ul>
4668
 *
4669
 * @param ctx PROJ context, or NULL for default context
4670
 * @param conversion Object of type Conversion. Must not be NULL.
4671
 * @param new_method_epsg_code EPSG code of the target method. Or 0 (in which
4672
 * case new_method_name must be specified).
4673
 * @param new_method_name EPSG or PROJ target method name. Or nullptr  (in which
4674
 * case new_method_epsg_code must be specified).
4675
 * @return new conversion that must be unreferenced with
4676
 * proj_destroy(), or NULL in case of error.
4677
 */
4678
PJ *proj_convert_conversion_to_other_method(PJ_CONTEXT *ctx,
4679
                                            const PJ *conversion,
4680
                                            int new_method_epsg_code,
4681
0
                                            const char *new_method_name) {
4682
0
    SANITIZE_CTX(ctx);
4683
0
    if (!conversion) {
4684
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4685
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4686
0
        return nullptr;
4687
0
    }
4688
0
    auto conv = dynamic_cast<const Conversion *>(conversion->iso_obj.get());
4689
0
    if (!conv) {
4690
0
        proj_log_error(ctx, __FUNCTION__, "not a Conversion");
4691
0
        return nullptr;
4692
0
    }
4693
0
    if (new_method_epsg_code == 0) {
4694
0
        if (!new_method_name) {
4695
0
            return nullptr;
4696
0
        }
4697
0
        if (metadata::Identifier::isEquivalentName(
4698
0
                new_method_name, EPSG_NAME_METHOD_MERCATOR_VARIANT_A)) {
4699
0
            new_method_epsg_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
4700
0
        } else if (metadata::Identifier::isEquivalentName(
4701
0
                       new_method_name, EPSG_NAME_METHOD_MERCATOR_VARIANT_B)) {
4702
0
            new_method_epsg_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
4703
0
        } else if (metadata::Identifier::isEquivalentName(
4704
0
                       new_method_name,
4705
0
                       EPSG_NAME_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)) {
4706
0
            new_method_epsg_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
4707
0
        } else if (metadata::Identifier::isEquivalentName(
4708
0
                       new_method_name,
4709
0
                       EPSG_NAME_METHOD_LAMBERT_CONIC_CONFORMAL_2SP)) {
4710
0
            new_method_epsg_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
4711
0
        }
4712
0
    }
4713
0
    try {
4714
0
        auto new_conv = conv->convertToOtherMethod(new_method_epsg_code);
4715
0
        if (!new_conv)
4716
0
            return nullptr;
4717
0
        return pj_obj_create(ctx, NN_NO_CHECK(new_conv));
4718
0
    } catch (const std::exception &e) {
4719
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4720
0
        return nullptr;
4721
0
    }
4722
0
}
4723
4724
// ---------------------------------------------------------------------------
4725
4726
//! @cond Doxygen_Suppress
4727
4728
0
static CoordinateSystemAxisNNPtr createAxis(const PJ_AXIS_DESCRIPTION &axis) {
4729
0
    const auto dir =
4730
0
        axis.direction ? AxisDirection::valueOf(axis.direction) : nullptr;
4731
0
    if (dir == nullptr)
4732
0
        throw Exception("invalid value for axis direction");
4733
0
    auto unit_type = UnitOfMeasure::Type::UNKNOWN;
4734
0
    switch (axis.unit_type) {
4735
0
    case PJ_UT_ANGULAR:
4736
0
        unit_type = UnitOfMeasure::Type::ANGULAR;
4737
0
        break;
4738
0
    case PJ_UT_LINEAR:
4739
0
        unit_type = UnitOfMeasure::Type::LINEAR;
4740
0
        break;
4741
0
    case PJ_UT_SCALE:
4742
0
        unit_type = UnitOfMeasure::Type::SCALE;
4743
0
        break;
4744
0
    case PJ_UT_TIME:
4745
0
        unit_type = UnitOfMeasure::Type::TIME;
4746
0
        break;
4747
0
    case PJ_UT_PARAMETRIC:
4748
0
        unit_type = UnitOfMeasure::Type::PARAMETRIC;
4749
0
        break;
4750
0
    }
4751
0
    const common::UnitOfMeasure unit(
4752
0
        axis.unit_type == PJ_UT_ANGULAR
4753
0
            ? createAngularUnit(axis.unit_name, axis.unit_conv_factor)
4754
0
        : axis.unit_type == PJ_UT_LINEAR
4755
0
            ? createLinearUnit(axis.unit_name, axis.unit_conv_factor)
4756
0
            : UnitOfMeasure(axis.unit_name ? axis.unit_name : "unnamed",
4757
0
                            axis.unit_conv_factor, unit_type));
4758
4759
0
    return CoordinateSystemAxis::create(
4760
0
        createPropertyMapName(axis.name),
4761
0
        axis.abbreviation ? axis.abbreviation : std::string(), *dir, unit);
4762
0
}
4763
4764
//! @endcond
4765
4766
// ---------------------------------------------------------------------------
4767
4768
/** \brief Instantiate a CoordinateSystem.
4769
 *
4770
 * The returned object must be unreferenced with proj_destroy() after
4771
 * use.
4772
 * It should be used by at most one thread at a time.
4773
 *
4774
 * @param ctx PROJ context, or NULL for default context
4775
 * @param type Coordinate system type.
4776
 * @param axis_count Number of axis
4777
 * @param axis Axis description (array of size axis_count)
4778
 *
4779
 * @return Object that must be unreferenced with
4780
 * proj_destroy(), or NULL in case of error.
4781
 */
4782
4783
PJ *proj_create_cs(PJ_CONTEXT *ctx, PJ_COORDINATE_SYSTEM_TYPE type,
4784
0
                   int axis_count, const PJ_AXIS_DESCRIPTION *axis) {
4785
0
    SANITIZE_CTX(ctx);
4786
0
    try {
4787
0
        switch (type) {
4788
0
        case PJ_CS_TYPE_UNKNOWN:
4789
0
            return nullptr;
4790
4791
0
        case PJ_CS_TYPE_CARTESIAN: {
4792
0
            if (axis_count == 2) {
4793
0
                return pj_obj_create(
4794
0
                    ctx, CartesianCS::create(PropertyMap(), createAxis(axis[0]),
4795
0
                                             createAxis(axis[1])));
4796
0
            } else if (axis_count == 3) {
4797
0
                return pj_obj_create(
4798
0
                    ctx, CartesianCS::create(PropertyMap(), createAxis(axis[0]),
4799
0
                                             createAxis(axis[1]),
4800
0
                                             createAxis(axis[2])));
4801
0
            }
4802
0
            break;
4803
0
        }
4804
4805
0
        case PJ_CS_TYPE_ELLIPSOIDAL: {
4806
0
            if (axis_count == 2) {
4807
0
                return pj_obj_create(
4808
0
                    ctx,
4809
0
                    EllipsoidalCS::create(PropertyMap(), createAxis(axis[0]),
4810
0
                                          createAxis(axis[1])));
4811
0
            } else if (axis_count == 3) {
4812
0
                return pj_obj_create(
4813
0
                    ctx, EllipsoidalCS::create(
4814
0
                             PropertyMap(), createAxis(axis[0]),
4815
0
                             createAxis(axis[1]), createAxis(axis[2])));
4816
0
            }
4817
0
            break;
4818
0
        }
4819
4820
0
        case PJ_CS_TYPE_VERTICAL: {
4821
0
            if (axis_count == 1) {
4822
0
                return pj_obj_create(
4823
0
                    ctx,
4824
0
                    VerticalCS::create(PropertyMap(), createAxis(axis[0])));
4825
0
            }
4826
0
            break;
4827
0
        }
4828
4829
0
        case PJ_CS_TYPE_SPHERICAL: {
4830
0
            if (axis_count == 3) {
4831
0
                return pj_obj_create(
4832
0
                    ctx, EllipsoidalCS::create(
4833
0
                             PropertyMap(), createAxis(axis[0]),
4834
0
                             createAxis(axis[1]), createAxis(axis[2])));
4835
0
            }
4836
0
            break;
4837
0
        }
4838
4839
0
        case PJ_CS_TYPE_PARAMETRIC: {
4840
0
            if (axis_count == 1) {
4841
0
                return pj_obj_create(
4842
0
                    ctx,
4843
0
                    ParametricCS::create(PropertyMap(), createAxis(axis[0])));
4844
0
            }
4845
0
            break;
4846
0
        }
4847
4848
0
        case PJ_CS_TYPE_ORDINAL: {
4849
0
            std::vector<CoordinateSystemAxisNNPtr> axisVector;
4850
0
            for (int i = 0; i < axis_count; i++) {
4851
0
                axisVector.emplace_back(createAxis(axis[i]));
4852
0
            }
4853
4854
0
            return pj_obj_create(ctx,
4855
0
                                 OrdinalCS::create(PropertyMap(), axisVector));
4856
0
        }
4857
4858
0
        case PJ_CS_TYPE_DATETIMETEMPORAL: {
4859
0
            if (axis_count == 1) {
4860
0
                return pj_obj_create(
4861
0
                    ctx, DateTimeTemporalCS::create(PropertyMap(),
4862
0
                                                    createAxis(axis[0])));
4863
0
            }
4864
0
            break;
4865
0
        }
4866
4867
0
        case PJ_CS_TYPE_TEMPORALCOUNT: {
4868
0
            if (axis_count == 1) {
4869
0
                return pj_obj_create(
4870
0
                    ctx, TemporalCountCS::create(PropertyMap(),
4871
0
                                                 createAxis(axis[0])));
4872
0
            }
4873
0
            break;
4874
0
        }
4875
4876
0
        case PJ_CS_TYPE_TEMPORALMEASURE: {
4877
0
            if (axis_count == 1) {
4878
0
                return pj_obj_create(
4879
0
                    ctx, TemporalMeasureCS::create(PropertyMap(),
4880
0
                                                   createAxis(axis[0])));
4881
0
            }
4882
0
            break;
4883
0
        }
4884
0
        }
4885
4886
0
    } catch (const std::exception &e) {
4887
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4888
0
        return nullptr;
4889
0
    }
4890
0
    proj_log_error(ctx, __FUNCTION__, "Wrong value for axis_count");
4891
0
    return nullptr;
4892
0
}
4893
4894
// ---------------------------------------------------------------------------
4895
4896
/** \brief Instantiate a CartesiansCS 2D
4897
 *
4898
 * The returned object must be unreferenced with proj_destroy() after
4899
 * use.
4900
 * It should be used by at most one thread at a time.
4901
 *
4902
 * @param ctx PROJ context, or NULL for default context
4903
 * @param type Coordinate system type.
4904
 * @param unit_name Unit name.
4905
 * @param unit_conv_factor Unit conversion factor to SI.
4906
 *
4907
 * @return Object that must be unreferenced with
4908
 * proj_destroy(), or NULL in case of error.
4909
 */
4910
4911
PJ *proj_create_cartesian_2D_cs(PJ_CONTEXT *ctx, PJ_CARTESIAN_CS_2D_TYPE type,
4912
                                const char *unit_name,
4913
0
                                double unit_conv_factor) {
4914
0
    SANITIZE_CTX(ctx);
4915
0
    try {
4916
0
        switch (type) {
4917
0
        case PJ_CART2D_EASTING_NORTHING:
4918
0
            return pj_obj_create(
4919
0
                ctx, CartesianCS::createEastingNorthing(
4920
0
                         createLinearUnit(unit_name, unit_conv_factor)));
4921
4922
0
        case PJ_CART2D_NORTHING_EASTING:
4923
0
            return pj_obj_create(
4924
0
                ctx, CartesianCS::createNorthingEasting(
4925
0
                         createLinearUnit(unit_name, unit_conv_factor)));
4926
4927
0
        case PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH:
4928
0
            return pj_obj_create(
4929
0
                ctx, CartesianCS::createNorthPoleEastingSouthNorthingSouth(
4930
0
                         createLinearUnit(unit_name, unit_conv_factor)));
4931
4932
0
        case PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH:
4933
0
            return pj_obj_create(
4934
0
                ctx, CartesianCS::createSouthPoleEastingNorthNorthingNorth(
4935
0
                         createLinearUnit(unit_name, unit_conv_factor)));
4936
4937
0
        case PJ_CART2D_WESTING_SOUTHING:
4938
0
            return pj_obj_create(
4939
0
                ctx, CartesianCS::createWestingSouthing(
4940
0
                         createLinearUnit(unit_name, unit_conv_factor)));
4941
0
        }
4942
0
    } catch (const std::exception &e) {
4943
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4944
0
    }
4945
0
    return nullptr;
4946
0
}
4947
4948
// ---------------------------------------------------------------------------
4949
4950
/** \brief Instantiate a Ellipsoidal 2D
4951
 *
4952
 * The returned object must be unreferenced with proj_destroy() after
4953
 * use.
4954
 * It should be used by at most one thread at a time.
4955
 *
4956
 * @param ctx PROJ context, or NULL for default context
4957
 * @param type Coordinate system type.
4958
 * @param unit_name Name of the angular units. Or NULL for Degree
4959
 * @param unit_conv_factor Conversion factor from the angular unit to radian.
4960
 * Or 0 for Degree if unit_name == NULL. Otherwise should be not NULL
4961
 *
4962
 * @return Object that must be unreferenced with
4963
 * proj_destroy(), or NULL in case of error.
4964
 */
4965
4966
PJ *proj_create_ellipsoidal_2D_cs(PJ_CONTEXT *ctx,
4967
                                  PJ_ELLIPSOIDAL_CS_2D_TYPE type,
4968
                                  const char *unit_name,
4969
0
                                  double unit_conv_factor) {
4970
0
    SANITIZE_CTX(ctx);
4971
0
    try {
4972
0
        switch (type) {
4973
0
        case PJ_ELLPS2D_LONGITUDE_LATITUDE:
4974
0
            return pj_obj_create(
4975
0
                ctx, EllipsoidalCS::createLongitudeLatitude(
4976
0
                         createAngularUnit(unit_name, unit_conv_factor)));
4977
4978
0
        case PJ_ELLPS2D_LATITUDE_LONGITUDE:
4979
0
            return pj_obj_create(
4980
0
                ctx, EllipsoidalCS::createLatitudeLongitude(
4981
0
                         createAngularUnit(unit_name, unit_conv_factor)));
4982
0
        }
4983
0
    } catch (const std::exception &e) {
4984
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4985
0
    }
4986
0
    return nullptr;
4987
0
}
4988
4989
// ---------------------------------------------------------------------------
4990
4991
/** \brief Instantiate a Ellipsoidal 3D
4992
 *
4993
 * The returned object must be unreferenced with proj_destroy() after
4994
 * use.
4995
 * It should be used by at most one thread at a time.
4996
 *
4997
 * @param ctx PROJ context, or NULL for default context
4998
 * @param type Coordinate system type.
4999
 * @param horizontal_angular_unit_name Name of the angular units. Or NULL for
5000
 * Degree.
5001
 * @param horizontal_angular_unit_conv_factor Conversion factor from the angular
5002
 * unit to radian. Or 0 for Degree if horizontal_angular_unit_name == NULL.
5003
 * Otherwise should be not NULL
5004
 * @param vertical_linear_unit_name Vertical linear unit name. Or NULL for
5005
 * Metre.
5006
 * @param vertical_linear_unit_conv_factor Vertical linear unit conversion
5007
 * factor to metre. Or 0 for Metre if vertical_linear_unit_name == NULL.
5008
 * Otherwise should be not NULL
5009
5010
 * @return Object that must be unreferenced with
5011
 * proj_destroy(), or NULL in case of error.
5012
 * @since 6.3
5013
 */
5014
5015
PJ *proj_create_ellipsoidal_3D_cs(PJ_CONTEXT *ctx,
5016
                                  PJ_ELLIPSOIDAL_CS_3D_TYPE type,
5017
                                  const char *horizontal_angular_unit_name,
5018
                                  double horizontal_angular_unit_conv_factor,
5019
                                  const char *vertical_linear_unit_name,
5020
0
                                  double vertical_linear_unit_conv_factor) {
5021
0
    SANITIZE_CTX(ctx);
5022
0
    try {
5023
0
        switch (type) {
5024
0
        case PJ_ELLPS3D_LONGITUDE_LATITUDE_HEIGHT:
5025
0
            return pj_obj_create(
5026
0
                ctx, EllipsoidalCS::createLongitudeLatitudeEllipsoidalHeight(
5027
0
                         createAngularUnit(horizontal_angular_unit_name,
5028
0
                                           horizontal_angular_unit_conv_factor),
5029
0
                         createLinearUnit(vertical_linear_unit_name,
5030
0
                                          vertical_linear_unit_conv_factor)));
5031
5032
0
        case PJ_ELLPS3D_LATITUDE_LONGITUDE_HEIGHT:
5033
0
            return pj_obj_create(
5034
0
                ctx, EllipsoidalCS::createLatitudeLongitudeEllipsoidalHeight(
5035
0
                         createAngularUnit(horizontal_angular_unit_name,
5036
0
                                           horizontal_angular_unit_conv_factor),
5037
0
                         createLinearUnit(vertical_linear_unit_name,
5038
0
                                          vertical_linear_unit_conv_factor)));
5039
0
        }
5040
0
    } catch (const std::exception &e) {
5041
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5042
0
    }
5043
0
    return nullptr;
5044
0
}
5045
5046
// ---------------------------------------------------------------------------
5047
5048
/** \brief Instantiate a ProjectedCRS
5049
 *
5050
 * The returned object must be unreferenced with proj_destroy() after
5051
 * use.
5052
 * It should be used by at most one thread at a time.
5053
 *
5054
 * @param ctx PROJ context, or NULL for default context
5055
 * @param crs_name CRS name. Or NULL
5056
 * @param geodetic_crs Base GeodeticCRS. Must not be NULL.
5057
 * @param conversion Conversion. Must not be NULL.
5058
 * @param coordinate_system Cartesian coordinate system. Must not be NULL.
5059
 *
5060
 * @return Object that must be unreferenced with
5061
 * proj_destroy(), or NULL in case of error.
5062
 */
5063
5064
PJ *proj_create_projected_crs(PJ_CONTEXT *ctx, const char *crs_name,
5065
                              const PJ *geodetic_crs, const PJ *conversion,
5066
0
                              const PJ *coordinate_system) {
5067
0
    SANITIZE_CTX(ctx);
5068
0
    if (!geodetic_crs || !conversion || !coordinate_system) {
5069
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
5070
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
5071
0
        return nullptr;
5072
0
    }
5073
0
    auto geodCRS =
5074
0
        std::dynamic_pointer_cast<GeodeticCRS>(geodetic_crs->iso_obj);
5075
0
    if (!geodCRS) {
5076
0
        return nullptr;
5077
0
    }
5078
0
    auto conv = std::dynamic_pointer_cast<Conversion>(conversion->iso_obj);
5079
0
    if (!conv) {
5080
0
        return nullptr;
5081
0
    }
5082
0
    auto cs =
5083
0
        std::dynamic_pointer_cast<CartesianCS>(coordinate_system->iso_obj);
5084
0
    if (!cs) {
5085
0
        return nullptr;
5086
0
    }
5087
0
    try {
5088
0
        return pj_obj_create(
5089
0
            ctx, ProjectedCRS::create(createPropertyMapName(crs_name),
5090
0
                                      NN_NO_CHECK(geodCRS), NN_NO_CHECK(conv),
5091
0
                                      NN_NO_CHECK(cs)));
5092
0
    } catch (const std::exception &e) {
5093
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5094
0
    }
5095
0
    return nullptr;
5096
0
}
5097
5098
// ---------------------------------------------------------------------------
5099
5100
//! @cond Doxygen_Suppress
5101
5102
static PJ *proj_create_conversion(PJ_CONTEXT *ctx,
5103
0
                                  const ConversionNNPtr &conv) {
5104
0
    return pj_obj_create(ctx, conv);
5105
0
}
5106
5107
//! @endcond
5108
5109
/* BEGIN: Generated by scripts/create_c_api_projections.py*/
5110
5111
// ---------------------------------------------------------------------------
5112
5113
/** \brief Instantiate a ProjectedCRS with a Universal Transverse Mercator
5114
 * conversion.
5115
 *
5116
 * See osgeo::proj::operation::Conversion::createUTM().
5117
 *
5118
 * Linear parameters are expressed in (linear_unit_name,
5119
 * linear_unit_conv_factor).
5120
 */
5121
0
PJ *proj_create_conversion_utm(PJ_CONTEXT *ctx, int zone, int north) {
5122
0
    SANITIZE_CTX(ctx);
5123
0
    try {
5124
0
        auto conv = Conversion::createUTM(PropertyMap(), zone, north != 0);
5125
0
        return proj_create_conversion(ctx, conv);
5126
0
    } catch (const std::exception &e) {
5127
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5128
0
    }
5129
0
    return nullptr;
5130
0
}
5131
// ---------------------------------------------------------------------------
5132
5133
/** \brief Instantiate a ProjectedCRS with a conversion based on the Transverse
5134
 * Mercator projection method.
5135
 *
5136
 * See osgeo::proj::operation::Conversion::createTransverseMercator().
5137
 *
5138
 * Linear parameters are expressed in (linear_unit_name,
5139
 * linear_unit_conv_factor).
5140
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5141
 */
5142
PJ *proj_create_conversion_transverse_mercator(
5143
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
5144
    double false_easting, double false_northing, const char *ang_unit_name,
5145
    double ang_unit_conv_factor, const char *linear_unit_name,
5146
0
    double linear_unit_conv_factor) {
5147
0
    SANITIZE_CTX(ctx);
5148
0
    try {
5149
0
        UnitOfMeasure linearUnit(
5150
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5151
0
        UnitOfMeasure angUnit(
5152
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5153
0
        auto conv = Conversion::createTransverseMercator(
5154
0
            PropertyMap(), Angle(center_lat, angUnit),
5155
0
            Angle(center_long, angUnit), Scale(scale),
5156
0
            Length(false_easting, linearUnit),
5157
0
            Length(false_northing, linearUnit));
5158
0
        return proj_create_conversion(ctx, conv);
5159
0
    } catch (const std::exception &e) {
5160
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5161
0
    }
5162
0
    return nullptr;
5163
0
}
5164
// ---------------------------------------------------------------------------
5165
5166
/** \brief Instantiate a ProjectedCRS with a conversion based on the Gauss
5167
 * Schreiber Transverse Mercator projection method.
5168
 *
5169
 * See
5170
 * osgeo::proj::operation::Conversion::createGaussSchreiberTransverseMercator().
5171
 *
5172
 * Linear parameters are expressed in (linear_unit_name,
5173
 * linear_unit_conv_factor).
5174
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5175
 */
5176
PJ *proj_create_conversion_gauss_schreiber_transverse_mercator(
5177
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
5178
    double false_easting, double false_northing, const char *ang_unit_name,
5179
    double ang_unit_conv_factor, const char *linear_unit_name,
5180
0
    double linear_unit_conv_factor) {
5181
0
    SANITIZE_CTX(ctx);
5182
0
    try {
5183
0
        UnitOfMeasure linearUnit(
5184
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5185
0
        UnitOfMeasure angUnit(
5186
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5187
0
        auto conv = Conversion::createGaussSchreiberTransverseMercator(
5188
0
            PropertyMap(), Angle(center_lat, angUnit),
5189
0
            Angle(center_long, angUnit), Scale(scale),
5190
0
            Length(false_easting, linearUnit),
5191
0
            Length(false_northing, linearUnit));
5192
0
        return proj_create_conversion(ctx, conv);
5193
0
    } catch (const std::exception &e) {
5194
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5195
0
    }
5196
0
    return nullptr;
5197
0
}
5198
// ---------------------------------------------------------------------------
5199
5200
/** \brief Instantiate a ProjectedCRS with a conversion based on the Transverse
5201
 * Mercator South Orientated projection method.
5202
 *
5203
 * See
5204
 * osgeo::proj::operation::Conversion::createTransverseMercatorSouthOriented().
5205
 *
5206
 * Linear parameters are expressed in (linear_unit_name,
5207
 * linear_unit_conv_factor).
5208
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5209
 */
5210
PJ *proj_create_conversion_transverse_mercator_south_oriented(
5211
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
5212
    double false_easting, double false_northing, const char *ang_unit_name,
5213
    double ang_unit_conv_factor, const char *linear_unit_name,
5214
0
    double linear_unit_conv_factor) {
5215
0
    SANITIZE_CTX(ctx);
5216
0
    try {
5217
0
        UnitOfMeasure linearUnit(
5218
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5219
0
        UnitOfMeasure angUnit(
5220
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5221
0
        auto conv = Conversion::createTransverseMercatorSouthOriented(
5222
0
            PropertyMap(), Angle(center_lat, angUnit),
5223
0
            Angle(center_long, angUnit), Scale(scale),
5224
0
            Length(false_easting, linearUnit),
5225
0
            Length(false_northing, linearUnit));
5226
0
        return proj_create_conversion(ctx, conv);
5227
0
    } catch (const std::exception &e) {
5228
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5229
0
    }
5230
0
    return nullptr;
5231
0
}
5232
// ---------------------------------------------------------------------------
5233
5234
/** \brief Instantiate a ProjectedCRS with a conversion based on the Two Point
5235
 * Equidistant projection method.
5236
 *
5237
 * See osgeo::proj::operation::Conversion::createTwoPointEquidistant().
5238
 *
5239
 * Linear parameters are expressed in (linear_unit_name,
5240
 * linear_unit_conv_factor).
5241
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5242
 */
5243
PJ *proj_create_conversion_two_point_equidistant(
5244
    PJ_CONTEXT *ctx, double latitude_first_point, double longitude_first_point,
5245
    double latitude_second_point, double longitude_secon_point,
5246
    double false_easting, double false_northing, const char *ang_unit_name,
5247
    double ang_unit_conv_factor, const char *linear_unit_name,
5248
0
    double linear_unit_conv_factor) {
5249
0
    SANITIZE_CTX(ctx);
5250
0
    try {
5251
0
        UnitOfMeasure linearUnit(
5252
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5253
0
        UnitOfMeasure angUnit(
5254
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5255
0
        auto conv = Conversion::createTwoPointEquidistant(
5256
0
            PropertyMap(), Angle(latitude_first_point, angUnit),
5257
0
            Angle(longitude_first_point, angUnit),
5258
0
            Angle(latitude_second_point, angUnit),
5259
0
            Angle(longitude_secon_point, angUnit),
5260
0
            Length(false_easting, linearUnit),
5261
0
            Length(false_northing, linearUnit));
5262
0
        return proj_create_conversion(ctx, conv);
5263
0
    } catch (const std::exception &e) {
5264
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5265
0
    }
5266
0
    return nullptr;
5267
0
}
5268
// ---------------------------------------------------------------------------
5269
5270
/** \brief Instantiate a ProjectedCRS with a conversion based on the Tunisia
5271
 * Mining Grid projection method.
5272
 *
5273
 * See osgeo::proj::operation::Conversion::createTunisiaMiningGrid().
5274
 *
5275
 * Linear parameters are expressed in (linear_unit_name,
5276
 * linear_unit_conv_factor).
5277
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5278
 *
5279
 * @since 9.2
5280
 */
5281
PJ *proj_create_conversion_tunisia_mining_grid(
5282
    PJ_CONTEXT *ctx, double center_lat, double center_long,
5283
    double false_easting, double false_northing, const char *ang_unit_name,
5284
    double ang_unit_conv_factor, const char *linear_unit_name,
5285
0
    double linear_unit_conv_factor) {
5286
0
    SANITIZE_CTX(ctx);
5287
0
    try {
5288
0
        UnitOfMeasure linearUnit(
5289
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5290
0
        UnitOfMeasure angUnit(
5291
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5292
0
        auto conv = Conversion::createTunisiaMiningGrid(
5293
0
            PropertyMap(), Angle(center_lat, angUnit),
5294
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
5295
0
            Length(false_northing, linearUnit));
5296
0
        return proj_create_conversion(ctx, conv);
5297
0
    } catch (const std::exception &e) {
5298
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5299
0
    }
5300
0
    return nullptr;
5301
0
}
5302
// ---------------------------------------------------------------------------
5303
5304
/** \brief Instantiate a ProjectedCRS with a conversion based on the Tunisia
5305
 * Mining Grid projection method.
5306
 *
5307
 * See osgeo::proj::operation::Conversion::createTunisiaMiningGrid().
5308
 *
5309
 * Linear parameters are expressed in (linear_unit_name,
5310
 * linear_unit_conv_factor).
5311
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5312
 *
5313
 * @deprecated Replaced by proj_create_conversion_tunisia_mining_grid
5314
 */
5315
PJ *proj_create_conversion_tunisia_mapping_grid(
5316
    PJ_CONTEXT *ctx, double center_lat, double center_long,
5317
    double false_easting, double false_northing, const char *ang_unit_name,
5318
    double ang_unit_conv_factor, const char *linear_unit_name,
5319
0
    double linear_unit_conv_factor) {
5320
0
    SANITIZE_CTX(ctx);
5321
0
    try {
5322
0
        UnitOfMeasure linearUnit(
5323
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5324
0
        UnitOfMeasure angUnit(
5325
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5326
0
        auto conv = Conversion::createTunisiaMiningGrid(
5327
0
            PropertyMap(), Angle(center_lat, angUnit),
5328
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
5329
0
            Length(false_northing, linearUnit));
5330
0
        return proj_create_conversion(ctx, conv);
5331
0
    } catch (const std::exception &e) {
5332
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5333
0
    }
5334
0
    return nullptr;
5335
0
}
5336
// ---------------------------------------------------------------------------
5337
5338
/** \brief Instantiate a ProjectedCRS with a conversion based on the Albers
5339
 * Conic Equal Area projection method.
5340
 *
5341
 * See osgeo::proj::operation::Conversion::createAlbersEqualArea().
5342
 *
5343
 * Linear parameters are expressed in (linear_unit_name,
5344
 * linear_unit_conv_factor).
5345
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5346
 */
5347
PJ *proj_create_conversion_albers_equal_area(
5348
    PJ_CONTEXT *ctx, double latitude_false_origin,
5349
    double longitude_false_origin, double latitude_first_parallel,
5350
    double latitude_second_parallel, double easting_false_origin,
5351
    double northing_false_origin, const char *ang_unit_name,
5352
    double ang_unit_conv_factor, const char *linear_unit_name,
5353
0
    double linear_unit_conv_factor) {
5354
0
    SANITIZE_CTX(ctx);
5355
0
    try {
5356
0
        UnitOfMeasure linearUnit(
5357
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5358
0
        UnitOfMeasure angUnit(
5359
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5360
0
        auto conv = Conversion::createAlbersEqualArea(
5361
0
            PropertyMap(), Angle(latitude_false_origin, angUnit),
5362
0
            Angle(longitude_false_origin, angUnit),
5363
0
            Angle(latitude_first_parallel, angUnit),
5364
0
            Angle(latitude_second_parallel, angUnit),
5365
0
            Length(easting_false_origin, linearUnit),
5366
0
            Length(northing_false_origin, linearUnit));
5367
0
        return proj_create_conversion(ctx, conv);
5368
0
    } catch (const std::exception &e) {
5369
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5370
0
    }
5371
0
    return nullptr;
5372
0
}
5373
// ---------------------------------------------------------------------------
5374
5375
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5376
 * Conic Conformal 1SP projection method.
5377
 *
5378
 * See osgeo::proj::operation::Conversion::createLambertConicConformal_1SP().
5379
 *
5380
 * Linear parameters are expressed in (linear_unit_name,
5381
 * linear_unit_conv_factor).
5382
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5383
 */
5384
PJ *proj_create_conversion_lambert_conic_conformal_1sp(
5385
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
5386
    double false_easting, double false_northing, const char *ang_unit_name,
5387
    double ang_unit_conv_factor, const char *linear_unit_name,
5388
0
    double linear_unit_conv_factor) {
5389
0
    SANITIZE_CTX(ctx);
5390
0
    try {
5391
0
        UnitOfMeasure linearUnit(
5392
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5393
0
        UnitOfMeasure angUnit(
5394
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5395
0
        auto conv = Conversion::createLambertConicConformal_1SP(
5396
0
            PropertyMap(), Angle(center_lat, angUnit),
5397
0
            Angle(center_long, angUnit), Scale(scale),
5398
0
            Length(false_easting, linearUnit),
5399
0
            Length(false_northing, linearUnit));
5400
0
        return proj_create_conversion(ctx, conv);
5401
0
    } catch (const std::exception &e) {
5402
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5403
0
    }
5404
0
    return nullptr;
5405
0
}
5406
// ---------------------------------------------------------------------------
5407
5408
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5409
 * Conic Conformal (1SP Variant B) projection method.
5410
 *
5411
 * See
5412
 * osgeo::proj::operation::Conversion::createLambertConicConformal_1SP_VariantB().
5413
 *
5414
 * Linear parameters are expressed in (linear_unit_name,
5415
 * linear_unit_conv_factor).
5416
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5417
 * @since 9.2.1
5418
 */
5419
PJ *proj_create_conversion_lambert_conic_conformal_1sp_variant_b(
5420
    PJ_CONTEXT *ctx, double latitude_nat_origin, double scale,
5421
    double latitude_false_origin, double longitude_false_origin,
5422
    double easting_false_origin, double northing_false_origin,
5423
    const char *ang_unit_name, double ang_unit_conv_factor,
5424
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
5425
0
    SANITIZE_CTX(ctx);
5426
0
    try {
5427
0
        UnitOfMeasure linearUnit(
5428
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5429
0
        UnitOfMeasure angUnit(
5430
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5431
0
        auto conv = Conversion::createLambertConicConformal_1SP_VariantB(
5432
0
            PropertyMap(), Angle(latitude_nat_origin, angUnit), Scale(scale),
5433
0
            Angle(latitude_false_origin, angUnit),
5434
0
            Angle(longitude_false_origin, angUnit),
5435
0
            Length(easting_false_origin, linearUnit),
5436
0
            Length(northing_false_origin, linearUnit));
5437
0
        return proj_create_conversion(ctx, conv);
5438
0
    } catch (const std::exception &e) {
5439
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5440
0
    }
5441
0
    return nullptr;
5442
0
}
5443
// ---------------------------------------------------------------------------
5444
5445
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5446
 * Conic Conformal (2SP) projection method.
5447
 *
5448
 * See osgeo::proj::operation::Conversion::createLambertConicConformal_2SP().
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(
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, const char *ang_unit_name,
5459
    double ang_unit_conv_factor, const char *linear_unit_name,
5460
0
    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(
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
        return proj_create_conversion(ctx, conv);
5475
0
    } catch (const std::exception &e) {
5476
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5477
0
    }
5478
0
    return nullptr;
5479
0
}
5480
// ---------------------------------------------------------------------------
5481
5482
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5483
 * Conic Conformal (2SP Michigan) projection method.
5484
 *
5485
 * See
5486
 * osgeo::proj::operation::Conversion::createLambertConicConformal_2SP_Michigan().
5487
 *
5488
 * Linear parameters are expressed in (linear_unit_name,
5489
 * linear_unit_conv_factor).
5490
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5491
 */
5492
PJ *proj_create_conversion_lambert_conic_conformal_2sp_michigan(
5493
    PJ_CONTEXT *ctx, double latitude_false_origin,
5494
    double longitude_false_origin, double latitude_first_parallel,
5495
    double latitude_second_parallel, double easting_false_origin,
5496
    double northing_false_origin, double ellipsoid_scaling_factor,
5497
    const char *ang_unit_name, double ang_unit_conv_factor,
5498
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
5499
0
    SANITIZE_CTX(ctx);
5500
0
    try {
5501
0
        UnitOfMeasure linearUnit(
5502
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5503
0
        UnitOfMeasure angUnit(
5504
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5505
0
        auto conv = Conversion::createLambertConicConformal_2SP_Michigan(
5506
0
            PropertyMap(), Angle(latitude_false_origin, angUnit),
5507
0
            Angle(longitude_false_origin, angUnit),
5508
0
            Angle(latitude_first_parallel, angUnit),
5509
0
            Angle(latitude_second_parallel, angUnit),
5510
0
            Length(easting_false_origin, linearUnit),
5511
0
            Length(northing_false_origin, linearUnit),
5512
0
            Scale(ellipsoid_scaling_factor));
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 Lambert
5522
 * Conic Conformal (2SP Belgium) projection method.
5523
 *
5524
 * See
5525
 * osgeo::proj::operation::Conversion::createLambertConicConformal_2SP_Belgium().
5526
 *
5527
 * Linear parameters are expressed in (linear_unit_name,
5528
 * linear_unit_conv_factor).
5529
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5530
 */
5531
PJ *proj_create_conversion_lambert_conic_conformal_2sp_belgium(
5532
    PJ_CONTEXT *ctx, double latitude_false_origin,
5533
    double longitude_false_origin, double latitude_first_parallel,
5534
    double latitude_second_parallel, double easting_false_origin,
5535
    double northing_false_origin, const char *ang_unit_name,
5536
    double ang_unit_conv_factor, const char *linear_unit_name,
5537
0
    double linear_unit_conv_factor) {
5538
0
    SANITIZE_CTX(ctx);
5539
0
    try {
5540
0
        UnitOfMeasure linearUnit(
5541
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5542
0
        UnitOfMeasure angUnit(
5543
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5544
0
        auto conv = Conversion::createLambertConicConformal_2SP_Belgium(
5545
0
            PropertyMap(), Angle(latitude_false_origin, angUnit),
5546
0
            Angle(longitude_false_origin, angUnit),
5547
0
            Angle(latitude_first_parallel, angUnit),
5548
0
            Angle(latitude_second_parallel, angUnit),
5549
0
            Length(easting_false_origin, linearUnit),
5550
0
            Length(northing_false_origin, linearUnit));
5551
0
        return proj_create_conversion(ctx, conv);
5552
0
    } catch (const std::exception &e) {
5553
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5554
0
    }
5555
0
    return nullptr;
5556
0
}
5557
// ---------------------------------------------------------------------------
5558
5559
/** \brief Instantiate a ProjectedCRS with a conversion based on the Modified
5560
 * Azimuthal Equidistant projection method.
5561
 *
5562
 * See osgeo::proj::operation::Conversion::createAzimuthalEquidistant().
5563
 *
5564
 * Linear parameters are expressed in (linear_unit_name,
5565
 * linear_unit_conv_factor).
5566
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5567
 */
5568
PJ *proj_create_conversion_azimuthal_equidistant(
5569
    PJ_CONTEXT *ctx, double latitude_nat_origin, double longitude_nat_origin,
5570
    double false_easting, double false_northing, const char *ang_unit_name,
5571
    double ang_unit_conv_factor, const char *linear_unit_name,
5572
0
    double linear_unit_conv_factor) {
5573
0
    SANITIZE_CTX(ctx);
5574
0
    try {
5575
0
        UnitOfMeasure linearUnit(
5576
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5577
0
        UnitOfMeasure angUnit(
5578
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5579
0
        auto conv = Conversion::createAzimuthalEquidistant(
5580
0
            PropertyMap(), Angle(latitude_nat_origin, angUnit),
5581
0
            Angle(longitude_nat_origin, angUnit),
5582
0
            Length(false_easting, linearUnit),
5583
0
            Length(false_northing, linearUnit));
5584
0
        return proj_create_conversion(ctx, conv);
5585
0
    } catch (const std::exception &e) {
5586
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5587
0
    }
5588
0
    return nullptr;
5589
0
}
5590
// ---------------------------------------------------------------------------
5591
5592
/** \brief Instantiate a ProjectedCRS with a conversion based on the Guam
5593
 * Projection projection method.
5594
 *
5595
 * See osgeo::proj::operation::Conversion::createGuamProjection().
5596
 *
5597
 * Linear parameters are expressed in (linear_unit_name,
5598
 * linear_unit_conv_factor).
5599
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5600
 */
5601
PJ *proj_create_conversion_guam_projection(
5602
    PJ_CONTEXT *ctx, double latitude_nat_origin, double longitude_nat_origin,
5603
    double false_easting, double false_northing, const char *ang_unit_name,
5604
    double ang_unit_conv_factor, const char *linear_unit_name,
5605
0
    double linear_unit_conv_factor) {
5606
0
    SANITIZE_CTX(ctx);
5607
0
    try {
5608
0
        UnitOfMeasure linearUnit(
5609
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5610
0
        UnitOfMeasure angUnit(
5611
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5612
0
        auto conv = Conversion::createGuamProjection(
5613
0
            PropertyMap(), Angle(latitude_nat_origin, angUnit),
5614
0
            Angle(longitude_nat_origin, angUnit),
5615
0
            Length(false_easting, linearUnit),
5616
0
            Length(false_northing, linearUnit));
5617
0
        return proj_create_conversion(ctx, conv);
5618
0
    } catch (const std::exception &e) {
5619
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5620
0
    }
5621
0
    return nullptr;
5622
0
}
5623
// ---------------------------------------------------------------------------
5624
5625
/** \brief Instantiate a ProjectedCRS with a conversion based on the Bonne
5626
 * projection method.
5627
 *
5628
 * See osgeo::proj::operation::Conversion::createBonne().
5629
 *
5630
 * Linear parameters are expressed in (linear_unit_name,
5631
 * linear_unit_conv_factor).
5632
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5633
 */
5634
PJ *proj_create_conversion_bonne(PJ_CONTEXT *ctx, double latitude_nat_origin,
5635
                                 double longitude_nat_origin,
5636
                                 double false_easting, double false_northing,
5637
                                 const char *ang_unit_name,
5638
                                 double ang_unit_conv_factor,
5639
                                 const char *linear_unit_name,
5640
0
                                 double linear_unit_conv_factor) {
5641
0
    SANITIZE_CTX(ctx);
5642
0
    try {
5643
0
        UnitOfMeasure linearUnit(
5644
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5645
0
        UnitOfMeasure angUnit(
5646
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5647
0
        auto conv = Conversion::createBonne(
5648
0
            PropertyMap(), Angle(latitude_nat_origin, angUnit),
5649
0
            Angle(longitude_nat_origin, angUnit),
5650
0
            Length(false_easting, linearUnit),
5651
0
            Length(false_northing, linearUnit));
5652
0
        return proj_create_conversion(ctx, conv);
5653
0
    } catch (const std::exception &e) {
5654
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5655
0
    }
5656
0
    return nullptr;
5657
0
}
5658
// ---------------------------------------------------------------------------
5659
5660
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5661
 * Cylindrical Equal Area (Spherical) projection method.
5662
 *
5663
 * See
5664
 * osgeo::proj::operation::Conversion::createLambertCylindricalEqualAreaSpherical().
5665
 *
5666
 * Linear parameters are expressed in (linear_unit_name,
5667
 * linear_unit_conv_factor).
5668
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5669
 */
5670
PJ *proj_create_conversion_lambert_cylindrical_equal_area_spherical(
5671
    PJ_CONTEXT *ctx, double latitude_first_parallel,
5672
    double longitude_nat_origin, double false_easting, double false_northing,
5673
    const char *ang_unit_name, double ang_unit_conv_factor,
5674
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
5675
0
    SANITIZE_CTX(ctx);
5676
0
    try {
5677
0
        UnitOfMeasure linearUnit(
5678
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5679
0
        UnitOfMeasure angUnit(
5680
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5681
0
        auto conv = Conversion::createLambertCylindricalEqualAreaSpherical(
5682
0
            PropertyMap(), Angle(latitude_first_parallel, angUnit),
5683
0
            Angle(longitude_nat_origin, angUnit),
5684
0
            Length(false_easting, linearUnit),
5685
0
            Length(false_northing, linearUnit));
5686
0
        return proj_create_conversion(ctx, conv);
5687
0
    } catch (const std::exception &e) {
5688
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5689
0
    }
5690
0
    return nullptr;
5691
0
}
5692
// ---------------------------------------------------------------------------
5693
5694
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5695
 * Cylindrical Equal Area (ellipsoidal form) projection method.
5696
 *
5697
 * See osgeo::proj::operation::Conversion::createLambertCylindricalEqualArea().
5698
 *
5699
 * Linear parameters are expressed in (linear_unit_name,
5700
 * linear_unit_conv_factor).
5701
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5702
 */
5703
PJ *proj_create_conversion_lambert_cylindrical_equal_area(
5704
    PJ_CONTEXT *ctx, double latitude_first_parallel,
5705
    double longitude_nat_origin, double false_easting, double false_northing,
5706
    const char *ang_unit_name, double ang_unit_conv_factor,
5707
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
5708
0
    SANITIZE_CTX(ctx);
5709
0
    try {
5710
0
        UnitOfMeasure linearUnit(
5711
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5712
0
        UnitOfMeasure angUnit(
5713
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5714
0
        auto conv = Conversion::createLambertCylindricalEqualArea(
5715
0
            PropertyMap(), Angle(latitude_first_parallel, angUnit),
5716
0
            Angle(longitude_nat_origin, angUnit),
5717
0
            Length(false_easting, linearUnit),
5718
0
            Length(false_northing, linearUnit));
5719
0
        return proj_create_conversion(ctx, conv);
5720
0
    } catch (const std::exception &e) {
5721
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5722
0
    }
5723
0
    return nullptr;
5724
0
}
5725
// ---------------------------------------------------------------------------
5726
5727
/** \brief Instantiate a ProjectedCRS with a conversion based on the
5728
 * Cassini-Soldner projection method.
5729
 *
5730
 * See osgeo::proj::operation::Conversion::createCassiniSoldner().
5731
 *
5732
 * Linear parameters are expressed in (linear_unit_name,
5733
 * linear_unit_conv_factor).
5734
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5735
 */
5736
PJ *proj_create_conversion_cassini_soldner(
5737
    PJ_CONTEXT *ctx, double center_lat, double center_long,
5738
    double false_easting, double false_northing, const char *ang_unit_name,
5739
    double ang_unit_conv_factor, const char *linear_unit_name,
5740
0
    double linear_unit_conv_factor) {
5741
0
    SANITIZE_CTX(ctx);
5742
0
    try {
5743
0
        UnitOfMeasure linearUnit(
5744
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5745
0
        UnitOfMeasure angUnit(
5746
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5747
0
        auto conv = Conversion::createCassiniSoldner(
5748
0
            PropertyMap(), Angle(center_lat, angUnit),
5749
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
5750
0
            Length(false_northing, linearUnit));
5751
0
        return proj_create_conversion(ctx, conv);
5752
0
    } catch (const std::exception &e) {
5753
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5754
0
    }
5755
0
    return nullptr;
5756
0
}
5757
// ---------------------------------------------------------------------------
5758
5759
/** \brief Instantiate a ProjectedCRS with a conversion based on the Equidistant
5760
 * Conic projection method.
5761
 *
5762
 * See osgeo::proj::operation::Conversion::createEquidistantConic().
5763
 *
5764
 * Linear parameters are expressed in (linear_unit_name,
5765
 * linear_unit_conv_factor).
5766
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5767
 */
5768
PJ *proj_create_conversion_equidistant_conic(
5769
    PJ_CONTEXT *ctx, double center_lat, double center_long,
5770
    double latitude_first_parallel, double latitude_second_parallel,
5771
    double false_easting, double false_northing, const char *ang_unit_name,
5772
    double ang_unit_conv_factor, const char *linear_unit_name,
5773
0
    double linear_unit_conv_factor) {
5774
0
    SANITIZE_CTX(ctx);
5775
0
    try {
5776
0
        UnitOfMeasure linearUnit(
5777
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5778
0
        UnitOfMeasure angUnit(
5779
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5780
0
        auto conv = Conversion::createEquidistantConic(
5781
0
            PropertyMap(), Angle(center_lat, angUnit),
5782
0
            Angle(center_long, angUnit),
5783
0
            Angle(latitude_first_parallel, angUnit),
5784
0
            Angle(latitude_second_parallel, angUnit),
5785
0
            Length(false_easting, linearUnit),
5786
0
            Length(false_northing, linearUnit));
5787
0
        return proj_create_conversion(ctx, conv);
5788
0
    } catch (const std::exception &e) {
5789
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5790
0
    }
5791
0
    return nullptr;
5792
0
}
5793
// ---------------------------------------------------------------------------
5794
5795
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert I
5796
 * projection method.
5797
 *
5798
 * See osgeo::proj::operation::Conversion::createEckertI().
5799
 *
5800
 * Linear parameters are expressed in (linear_unit_name,
5801
 * linear_unit_conv_factor).
5802
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5803
 */
5804
PJ *proj_create_conversion_eckert_i(PJ_CONTEXT *ctx, double center_long,
5805
                                    double false_easting, double false_northing,
5806
                                    const char *ang_unit_name,
5807
                                    double ang_unit_conv_factor,
5808
                                    const char *linear_unit_name,
5809
0
                                    double linear_unit_conv_factor) {
5810
0
    SANITIZE_CTX(ctx);
5811
0
    try {
5812
0
        UnitOfMeasure linearUnit(
5813
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5814
0
        UnitOfMeasure angUnit(
5815
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5816
0
        auto conv = Conversion::createEckertI(
5817
0
            PropertyMap(), Angle(center_long, angUnit),
5818
0
            Length(false_easting, linearUnit),
5819
0
            Length(false_northing, linearUnit));
5820
0
        return proj_create_conversion(ctx, conv);
5821
0
    } catch (const std::exception &e) {
5822
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5823
0
    }
5824
0
    return nullptr;
5825
0
}
5826
// ---------------------------------------------------------------------------
5827
5828
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert II
5829
 * projection method.
5830
 *
5831
 * See osgeo::proj::operation::Conversion::createEckertII().
5832
 *
5833
 * Linear parameters are expressed in (linear_unit_name,
5834
 * linear_unit_conv_factor).
5835
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5836
 */
5837
PJ *proj_create_conversion_eckert_ii(PJ_CONTEXT *ctx, double center_long,
5838
                                     double false_easting,
5839
                                     double false_northing,
5840
                                     const char *ang_unit_name,
5841
                                     double ang_unit_conv_factor,
5842
                                     const char *linear_unit_name,
5843
0
                                     double linear_unit_conv_factor) {
5844
0
    SANITIZE_CTX(ctx);
5845
0
    try {
5846
0
        UnitOfMeasure linearUnit(
5847
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5848
0
        UnitOfMeasure angUnit(
5849
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5850
0
        auto conv = Conversion::createEckertII(
5851
0
            PropertyMap(), Angle(center_long, angUnit),
5852
0
            Length(false_easting, linearUnit),
5853
0
            Length(false_northing, linearUnit));
5854
0
        return proj_create_conversion(ctx, conv);
5855
0
    } catch (const std::exception &e) {
5856
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5857
0
    }
5858
0
    return nullptr;
5859
0
}
5860
// ---------------------------------------------------------------------------
5861
5862
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert III
5863
 * projection method.
5864
 *
5865
 * See osgeo::proj::operation::Conversion::createEckertIII().
5866
 *
5867
 * Linear parameters are expressed in (linear_unit_name,
5868
 * linear_unit_conv_factor).
5869
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5870
 */
5871
PJ *proj_create_conversion_eckert_iii(PJ_CONTEXT *ctx, double center_long,
5872
                                      double false_easting,
5873
                                      double false_northing,
5874
                                      const char *ang_unit_name,
5875
                                      double ang_unit_conv_factor,
5876
                                      const char *linear_unit_name,
5877
0
                                      double linear_unit_conv_factor) {
5878
0
    SANITIZE_CTX(ctx);
5879
0
    try {
5880
0
        UnitOfMeasure linearUnit(
5881
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5882
0
        UnitOfMeasure angUnit(
5883
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5884
0
        auto conv = Conversion::createEckertIII(
5885
0
            PropertyMap(), Angle(center_long, angUnit),
5886
0
            Length(false_easting, linearUnit),
5887
0
            Length(false_northing, linearUnit));
5888
0
        return proj_create_conversion(ctx, conv);
5889
0
    } catch (const std::exception &e) {
5890
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5891
0
    }
5892
0
    return nullptr;
5893
0
}
5894
// ---------------------------------------------------------------------------
5895
5896
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert IV
5897
 * projection method.
5898
 *
5899
 * See osgeo::proj::operation::Conversion::createEckertIV().
5900
 *
5901
 * Linear parameters are expressed in (linear_unit_name,
5902
 * linear_unit_conv_factor).
5903
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5904
 */
5905
PJ *proj_create_conversion_eckert_iv(PJ_CONTEXT *ctx, double center_long,
5906
                                     double false_easting,
5907
                                     double false_northing,
5908
                                     const char *ang_unit_name,
5909
                                     double ang_unit_conv_factor,
5910
                                     const char *linear_unit_name,
5911
0
                                     double linear_unit_conv_factor) {
5912
0
    SANITIZE_CTX(ctx);
5913
0
    try {
5914
0
        UnitOfMeasure linearUnit(
5915
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5916
0
        UnitOfMeasure angUnit(
5917
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5918
0
        auto conv = Conversion::createEckertIV(
5919
0
            PropertyMap(), Angle(center_long, angUnit),
5920
0
            Length(false_easting, linearUnit),
5921
0
            Length(false_northing, linearUnit));
5922
0
        return proj_create_conversion(ctx, conv);
5923
0
    } catch (const std::exception &e) {
5924
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5925
0
    }
5926
0
    return nullptr;
5927
0
}
5928
// ---------------------------------------------------------------------------
5929
5930
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert V
5931
 * projection method.
5932
 *
5933
 * See osgeo::proj::operation::Conversion::createEckertV().
5934
 *
5935
 * Linear parameters are expressed in (linear_unit_name,
5936
 * linear_unit_conv_factor).
5937
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5938
 */
5939
PJ *proj_create_conversion_eckert_v(PJ_CONTEXT *ctx, double center_long,
5940
                                    double false_easting, double false_northing,
5941
                                    const char *ang_unit_name,
5942
                                    double ang_unit_conv_factor,
5943
                                    const char *linear_unit_name,
5944
0
                                    double linear_unit_conv_factor) {
5945
0
    SANITIZE_CTX(ctx);
5946
0
    try {
5947
0
        UnitOfMeasure linearUnit(
5948
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5949
0
        UnitOfMeasure angUnit(
5950
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5951
0
        auto conv = Conversion::createEckertV(
5952
0
            PropertyMap(), Angle(center_long, angUnit),
5953
0
            Length(false_easting, linearUnit),
5954
0
            Length(false_northing, linearUnit));
5955
0
        return proj_create_conversion(ctx, conv);
5956
0
    } catch (const std::exception &e) {
5957
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5958
0
    }
5959
0
    return nullptr;
5960
0
}
5961
// ---------------------------------------------------------------------------
5962
5963
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert VI
5964
 * projection method.
5965
 *
5966
 * See osgeo::proj::operation::Conversion::createEckertVI().
5967
 *
5968
 * Linear parameters are expressed in (linear_unit_name,
5969
 * linear_unit_conv_factor).
5970
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5971
 */
5972
PJ *proj_create_conversion_eckert_vi(PJ_CONTEXT *ctx, double center_long,
5973
                                     double false_easting,
5974
                                     double false_northing,
5975
                                     const char *ang_unit_name,
5976
                                     double ang_unit_conv_factor,
5977
                                     const char *linear_unit_name,
5978
0
                                     double linear_unit_conv_factor) {
5979
0
    SANITIZE_CTX(ctx);
5980
0
    try {
5981
0
        UnitOfMeasure linearUnit(
5982
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5983
0
        UnitOfMeasure angUnit(
5984
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5985
0
        auto conv = Conversion::createEckertVI(
5986
0
            PropertyMap(), Angle(center_long, angUnit),
5987
0
            Length(false_easting, linearUnit),
5988
0
            Length(false_northing, linearUnit));
5989
0
        return proj_create_conversion(ctx, conv);
5990
0
    } catch (const std::exception &e) {
5991
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5992
0
    }
5993
0
    return nullptr;
5994
0
}
5995
// ---------------------------------------------------------------------------
5996
5997
/** \brief Instantiate a ProjectedCRS with a conversion based on the Equidistant
5998
 * Cylindrical projection method.
5999
 *
6000
 * See osgeo::proj::operation::Conversion::createEquidistantCylindrical().
6001
 *
6002
 * Linear parameters are expressed in (linear_unit_name,
6003
 * linear_unit_conv_factor).
6004
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6005
 */
6006
PJ *proj_create_conversion_equidistant_cylindrical(
6007
    PJ_CONTEXT *ctx, double latitude_first_parallel,
6008
    double longitude_nat_origin, double false_easting, double false_northing,
6009
    const char *ang_unit_name, double ang_unit_conv_factor,
6010
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
6011
0
    SANITIZE_CTX(ctx);
6012
0
    try {
6013
0
        UnitOfMeasure linearUnit(
6014
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6015
0
        UnitOfMeasure angUnit(
6016
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6017
0
        auto conv = Conversion::createEquidistantCylindrical(
6018
0
            PropertyMap(), Angle(latitude_first_parallel, angUnit),
6019
0
            Angle(longitude_nat_origin, angUnit),
6020
0
            Length(false_easting, linearUnit),
6021
0
            Length(false_northing, linearUnit));
6022
0
        return proj_create_conversion(ctx, conv);
6023
0
    } catch (const std::exception &e) {
6024
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6025
0
    }
6026
0
    return nullptr;
6027
0
}
6028
// ---------------------------------------------------------------------------
6029
6030
/** \brief Instantiate a ProjectedCRS with a conversion based on the Equidistant
6031
 * Cylindrical (Spherical) projection method.
6032
 *
6033
 * See
6034
 * osgeo::proj::operation::Conversion::createEquidistantCylindricalSpherical().
6035
 *
6036
 * Linear parameters are expressed in (linear_unit_name,
6037
 * linear_unit_conv_factor).
6038
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6039
 */
6040
PJ *proj_create_conversion_equidistant_cylindrical_spherical(
6041
    PJ_CONTEXT *ctx, double latitude_first_parallel,
6042
    double longitude_nat_origin, double false_easting, double false_northing,
6043
    const char *ang_unit_name, double ang_unit_conv_factor,
6044
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
6045
0
    SANITIZE_CTX(ctx);
6046
0
    try {
6047
0
        UnitOfMeasure linearUnit(
6048
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6049
0
        UnitOfMeasure angUnit(
6050
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6051
0
        auto conv = Conversion::createEquidistantCylindricalSpherical(
6052
0
            PropertyMap(), Angle(latitude_first_parallel, angUnit),
6053
0
            Angle(longitude_nat_origin, angUnit),
6054
0
            Length(false_easting, linearUnit),
6055
0
            Length(false_northing, linearUnit));
6056
0
        return proj_create_conversion(ctx, conv);
6057
0
    } catch (const std::exception &e) {
6058
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6059
0
    }
6060
0
    return nullptr;
6061
0
}
6062
// ---------------------------------------------------------------------------
6063
6064
/** \brief Instantiate a ProjectedCRS with a conversion based on the Gall
6065
 * (Stereographic) projection method.
6066
 *
6067
 * See osgeo::proj::operation::Conversion::createGall().
6068
 *
6069
 * Linear parameters are expressed in (linear_unit_name,
6070
 * linear_unit_conv_factor).
6071
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6072
 */
6073
PJ *proj_create_conversion_gall(PJ_CONTEXT *ctx, double center_long,
6074
                                double false_easting, double false_northing,
6075
                                const char *ang_unit_name,
6076
                                double ang_unit_conv_factor,
6077
                                const char *linear_unit_name,
6078
0
                                double linear_unit_conv_factor) {
6079
0
    SANITIZE_CTX(ctx);
6080
0
    try {
6081
0
        UnitOfMeasure linearUnit(
6082
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6083
0
        UnitOfMeasure angUnit(
6084
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6085
0
        auto conv =
6086
0
            Conversion::createGall(PropertyMap(), Angle(center_long, angUnit),
6087
0
                                   Length(false_easting, linearUnit),
6088
0
                                   Length(false_northing, linearUnit));
6089
0
        return proj_create_conversion(ctx, conv);
6090
0
    } catch (const std::exception &e) {
6091
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6092
0
    }
6093
0
    return nullptr;
6094
0
}
6095
// ---------------------------------------------------------------------------
6096
6097
/** \brief Instantiate a ProjectedCRS with a conversion based on the Goode
6098
 * Homolosine projection method.
6099
 *
6100
 * See osgeo::proj::operation::Conversion::createGoodeHomolosine().
6101
 *
6102
 * Linear parameters are expressed in (linear_unit_name,
6103
 * linear_unit_conv_factor).
6104
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6105
 */
6106
PJ *proj_create_conversion_goode_homolosine(PJ_CONTEXT *ctx, double center_long,
6107
                                            double false_easting,
6108
                                            double false_northing,
6109
                                            const char *ang_unit_name,
6110
                                            double ang_unit_conv_factor,
6111
                                            const char *linear_unit_name,
6112
0
                                            double linear_unit_conv_factor) {
6113
0
    SANITIZE_CTX(ctx);
6114
0
    try {
6115
0
        UnitOfMeasure linearUnit(
6116
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6117
0
        UnitOfMeasure angUnit(
6118
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6119
0
        auto conv = Conversion::createGoodeHomolosine(
6120
0
            PropertyMap(), Angle(center_long, angUnit),
6121
0
            Length(false_easting, linearUnit),
6122
0
            Length(false_northing, linearUnit));
6123
0
        return proj_create_conversion(ctx, conv);
6124
0
    } catch (const std::exception &e) {
6125
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6126
0
    }
6127
0
    return nullptr;
6128
0
}
6129
// ---------------------------------------------------------------------------
6130
6131
/** \brief Instantiate a ProjectedCRS with a conversion based on the Interrupted
6132
 * Goode Homolosine projection method.
6133
 *
6134
 * See osgeo::proj::operation::Conversion::createInterruptedGoodeHomolosine().
6135
 *
6136
 * Linear parameters are expressed in (linear_unit_name,
6137
 * linear_unit_conv_factor).
6138
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6139
 */
6140
PJ *proj_create_conversion_interrupted_goode_homolosine(
6141
    PJ_CONTEXT *ctx, double center_long, double false_easting,
6142
    double false_northing, const char *ang_unit_name,
6143
    double ang_unit_conv_factor, const char *linear_unit_name,
6144
0
    double linear_unit_conv_factor) {
6145
0
    SANITIZE_CTX(ctx);
6146
0
    try {
6147
0
        UnitOfMeasure linearUnit(
6148
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6149
0
        UnitOfMeasure angUnit(
6150
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6151
0
        auto conv = Conversion::createInterruptedGoodeHomolosine(
6152
0
            PropertyMap(), Angle(center_long, angUnit),
6153
0
            Length(false_easting, linearUnit),
6154
0
            Length(false_northing, linearUnit));
6155
0
        return proj_create_conversion(ctx, conv);
6156
0
    } catch (const std::exception &e) {
6157
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6158
0
    }
6159
0
    return nullptr;
6160
0
}
6161
// ---------------------------------------------------------------------------
6162
6163
/** \brief Instantiate a ProjectedCRS with a conversion based on the
6164
 * Geostationary Satellite View projection method, with the sweep angle axis of
6165
 * the viewing instrument being x.
6166
 *
6167
 * See osgeo::proj::operation::Conversion::createGeostationarySatelliteSweepX().
6168
 *
6169
 * Linear parameters are expressed in (linear_unit_name,
6170
 * linear_unit_conv_factor).
6171
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6172
 */
6173
PJ *proj_create_conversion_geostationary_satellite_sweep_x(
6174
    PJ_CONTEXT *ctx, double center_long, double height, double false_easting,
6175
    double false_northing, const char *ang_unit_name,
6176
    double ang_unit_conv_factor, const char *linear_unit_name,
6177
0
    double linear_unit_conv_factor) {
6178
0
    SANITIZE_CTX(ctx);
6179
0
    try {
6180
0
        UnitOfMeasure linearUnit(
6181
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6182
0
        UnitOfMeasure angUnit(
6183
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6184
0
        auto conv = Conversion::createGeostationarySatelliteSweepX(
6185
0
            PropertyMap(), Angle(center_long, angUnit),
6186
0
            Length(height, linearUnit), Length(false_easting, linearUnit),
6187
0
            Length(false_northing, linearUnit));
6188
0
        return proj_create_conversion(ctx, conv);
6189
0
    } catch (const std::exception &e) {
6190
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6191
0
    }
6192
0
    return nullptr;
6193
0
}
6194
// ---------------------------------------------------------------------------
6195
6196
/** \brief Instantiate a ProjectedCRS with a conversion based on the
6197
 * Geostationary Satellite View projection method, with the sweep angle axis of
6198
 * the viewing instrument being y.
6199
 *
6200
 * See osgeo::proj::operation::Conversion::createGeostationarySatelliteSweepY().
6201
 *
6202
 * Linear parameters are expressed in (linear_unit_name,
6203
 * linear_unit_conv_factor).
6204
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6205
 */
6206
PJ *proj_create_conversion_geostationary_satellite_sweep_y(
6207
    PJ_CONTEXT *ctx, double center_long, double height, double false_easting,
6208
    double false_northing, const char *ang_unit_name,
6209
    double ang_unit_conv_factor, const char *linear_unit_name,
6210
0
    double linear_unit_conv_factor) {
6211
0
    SANITIZE_CTX(ctx);
6212
0
    try {
6213
0
        UnitOfMeasure linearUnit(
6214
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6215
0
        UnitOfMeasure angUnit(
6216
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6217
0
        auto conv = Conversion::createGeostationarySatelliteSweepY(
6218
0
            PropertyMap(), Angle(center_long, angUnit),
6219
0
            Length(height, linearUnit), Length(false_easting, linearUnit),
6220
0
            Length(false_northing, linearUnit));
6221
0
        return proj_create_conversion(ctx, conv);
6222
0
    } catch (const std::exception &e) {
6223
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6224
0
    }
6225
0
    return nullptr;
6226
0
}
6227
// ---------------------------------------------------------------------------
6228
6229
/** \brief Instantiate a ProjectedCRS with a conversion based on the Gnomonic
6230
 * projection method.
6231
 *
6232
 * See osgeo::proj::operation::Conversion::createGnomonic().
6233
 *
6234
 * Linear parameters are expressed in (linear_unit_name,
6235
 * linear_unit_conv_factor).
6236
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6237
 */
6238
PJ *proj_create_conversion_gnomonic(PJ_CONTEXT *ctx, double center_lat,
6239
                                    double center_long, double false_easting,
6240
                                    double false_northing,
6241
                                    const char *ang_unit_name,
6242
                                    double ang_unit_conv_factor,
6243
                                    const char *linear_unit_name,
6244
0
                                    double linear_unit_conv_factor) {
6245
0
    SANITIZE_CTX(ctx);
6246
0
    try {
6247
0
        UnitOfMeasure linearUnit(
6248
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6249
0
        UnitOfMeasure angUnit(
6250
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6251
0
        auto conv = Conversion::createGnomonic(
6252
0
            PropertyMap(), Angle(center_lat, angUnit),
6253
0
            Angle(center_long, angUnit), 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 A) projection method.
6265
 *
6266
 * See
6267
 * osgeo::proj::operation::Conversion::createHotineObliqueMercatorVariantA().
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_a(
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 false_easting, double false_northing, const char *ang_unit_name,
6278
    double ang_unit_conv_factor, const char *linear_unit_name,
6279
0
    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::createHotineObliqueMercatorVariantA(
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(false_easting, linearUnit),
6292
0
            Length(false_northing, 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 (Variant B) projection method.
6303
 *
6304
 * See
6305
 * osgeo::proj::operation::Conversion::createHotineObliqueMercatorVariantB().
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_variant_b(
6312
    PJ_CONTEXT *ctx, double latitude_projection_centre,
6313
    double longitude_projection_centre, double azimuth_initial_line,
6314
    double angle_from_rectified_to_skrew_grid, double scale,
6315
    double easting_projection_centre, double northing_projection_centre,
6316
    const char *ang_unit_name, double ang_unit_conv_factor,
6317
0
    const char *linear_unit_name, 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 = Conversion::createHotineObliqueMercatorVariantB(
6325
0
            PropertyMap(), Angle(latitude_projection_centre, angUnit),
6326
0
            Angle(longitude_projection_centre, angUnit),
6327
0
            Angle(azimuth_initial_line, angUnit),
6328
0
            Angle(angle_from_rectified_to_skrew_grid, angUnit), Scale(scale),
6329
0
            Length(easting_projection_centre, linearUnit),
6330
0
            Length(northing_projection_centre, linearUnit));
6331
0
        return proj_create_conversion(ctx, conv);
6332
0
    } catch (const std::exception &e) {
6333
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6334
0
    }
6335
0
    return nullptr;
6336
0
}
6337
// ---------------------------------------------------------------------------
6338
6339
/** \brief Instantiate a ProjectedCRS with a conversion based on the Hotine
6340
 * Oblique Mercator Two Point Natural Origin projection method.
6341
 *
6342
 * See
6343
 * osgeo::proj::operation::Conversion::createHotineObliqueMercatorTwoPointNaturalOrigin().
6344
 *
6345
 * Linear parameters are expressed in (linear_unit_name,
6346
 * linear_unit_conv_factor).
6347
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6348
 */
6349
PJ *proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
6350
    PJ_CONTEXT *ctx, double latitude_projection_centre, double latitude_point1,
6351
    double longitude_point1, double latitude_point2, double longitude_point2,
6352
    double scale, double easting_projection_centre,
6353
    double northing_projection_centre, const char *ang_unit_name,
6354
    double ang_unit_conv_factor, const char *linear_unit_name,
6355
0
    double linear_unit_conv_factor) {
6356
0
    SANITIZE_CTX(ctx);
6357
0
    try {
6358
0
        UnitOfMeasure linearUnit(
6359
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6360
0
        UnitOfMeasure angUnit(
6361
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6362
0
        auto conv =
6363
0
            Conversion::createHotineObliqueMercatorTwoPointNaturalOrigin(
6364
0
                PropertyMap(), Angle(latitude_projection_centre, angUnit),
6365
0
                Angle(latitude_point1, angUnit),
6366
0
                Angle(longitude_point1, angUnit),
6367
0
                Angle(latitude_point2, angUnit),
6368
0
                Angle(longitude_point2, angUnit), Scale(scale),
6369
0
                Length(easting_projection_centre, linearUnit),
6370
0
                Length(northing_projection_centre, linearUnit));
6371
0
        return proj_create_conversion(ctx, conv);
6372
0
    } catch (const std::exception &e) {
6373
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6374
0
    }
6375
0
    return nullptr;
6376
0
}
6377
// ---------------------------------------------------------------------------
6378
6379
/** \brief Instantiate a ProjectedCRS with a conversion based on the Laborde
6380
 * Oblique Mercator projection method.
6381
 *
6382
 * See
6383
 * osgeo::proj::operation::Conversion::createLabordeObliqueMercator().
6384
 *
6385
 * Linear parameters are expressed in (linear_unit_name,
6386
 * linear_unit_conv_factor).
6387
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6388
 */
6389
PJ *proj_create_conversion_laborde_oblique_mercator(
6390
    PJ_CONTEXT *ctx, double latitude_projection_centre,
6391
    double longitude_projection_centre, double azimuth_initial_line,
6392
    double scale, double false_easting, double false_northing,
6393
    const char *ang_unit_name, double ang_unit_conv_factor,
6394
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
6395
0
    SANITIZE_CTX(ctx);
6396
0
    try {
6397
0
        UnitOfMeasure linearUnit(
6398
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6399
0
        UnitOfMeasure angUnit(
6400
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6401
0
        auto conv = Conversion::createLabordeObliqueMercator(
6402
0
            PropertyMap(), Angle(latitude_projection_centre, angUnit),
6403
0
            Angle(longitude_projection_centre, angUnit),
6404
0
            Angle(azimuth_initial_line, angUnit), Scale(scale),
6405
0
            Length(false_easting, linearUnit),
6406
0
            Length(false_northing, linearUnit));
6407
0
        return proj_create_conversion(ctx, conv);
6408
0
    } catch (const std::exception &e) {
6409
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6410
0
    }
6411
0
    return nullptr;
6412
0
}
6413
// ---------------------------------------------------------------------------
6414
6415
/** \brief Instantiate a ProjectedCRS with a conversion based on the
6416
 * International Map of the World Polyconic projection method.
6417
 *
6418
 * See
6419
 * osgeo::proj::operation::Conversion::createInternationalMapWorldPolyconic().
6420
 *
6421
 * Linear parameters are expressed in (linear_unit_name,
6422
 * linear_unit_conv_factor).
6423
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6424
 */
6425
PJ *proj_create_conversion_international_map_world_polyconic(
6426
    PJ_CONTEXT *ctx, double center_long, double latitude_first_parallel,
6427
    double latitude_second_parallel, double false_easting,
6428
    double false_northing, const char *ang_unit_name,
6429
    double ang_unit_conv_factor, const char *linear_unit_name,
6430
0
    double linear_unit_conv_factor) {
6431
0
    SANITIZE_CTX(ctx);
6432
0
    try {
6433
0
        UnitOfMeasure linearUnit(
6434
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6435
0
        UnitOfMeasure angUnit(
6436
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6437
0
        auto conv = Conversion::createInternationalMapWorldPolyconic(
6438
0
            PropertyMap(), Angle(center_long, angUnit),
6439
0
            Angle(latitude_first_parallel, angUnit),
6440
0
            Angle(latitude_second_parallel, angUnit),
6441
0
            Length(false_easting, linearUnit),
6442
0
            Length(false_northing, linearUnit));
6443
0
        return proj_create_conversion(ctx, conv);
6444
0
    } catch (const std::exception &e) {
6445
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6446
0
    }
6447
0
    return nullptr;
6448
0
}
6449
// ---------------------------------------------------------------------------
6450
6451
/** \brief Instantiate a ProjectedCRS with a conversion based on the Krovak
6452
 * (north oriented) projection method.
6453
 *
6454
 * See osgeo::proj::operation::Conversion::createKrovakNorthOriented().
6455
 *
6456
 * Linear parameters are expressed in (linear_unit_name,
6457
 * linear_unit_conv_factor).
6458
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6459
 */
6460
PJ *proj_create_conversion_krovak_north_oriented(
6461
    PJ_CONTEXT *ctx, double latitude_projection_centre,
6462
    double longitude_of_origin, double colatitude_cone_axis,
6463
    double latitude_pseudo_standard_parallel,
6464
    double scale_factor_pseudo_standard_parallel, double false_easting,
6465
    double false_northing, const char *ang_unit_name,
6466
    double ang_unit_conv_factor, const char *linear_unit_name,
6467
0
    double linear_unit_conv_factor) {
6468
0
    SANITIZE_CTX(ctx);
6469
0
    try {
6470
0
        UnitOfMeasure linearUnit(
6471
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6472
0
        UnitOfMeasure angUnit(
6473
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6474
0
        auto conv = Conversion::createKrovakNorthOriented(
6475
0
            PropertyMap(), Angle(latitude_projection_centre, angUnit),
6476
0
            Angle(longitude_of_origin, angUnit),
6477
0
            Angle(colatitude_cone_axis, angUnit),
6478
0
            Angle(latitude_pseudo_standard_parallel, angUnit),
6479
0
            Scale(scale_factor_pseudo_standard_parallel),
6480
0
            Length(false_easting, linearUnit),
6481
0
            Length(false_northing, linearUnit));
6482
0
        return proj_create_conversion(ctx, conv);
6483
0
    } catch (const std::exception &e) {
6484
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6485
0
    }
6486
0
    return nullptr;
6487
0
}
6488
// ---------------------------------------------------------------------------
6489
6490
/** \brief Instantiate a ProjectedCRS with a conversion based on the Krovak
6491
 * projection method.
6492
 *
6493
 * See osgeo::proj::operation::Conversion::createKrovak().
6494
 *
6495
 * Linear parameters are expressed in (linear_unit_name,
6496
 * linear_unit_conv_factor).
6497
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6498
 */
6499
PJ *proj_create_conversion_krovak(
6500
    PJ_CONTEXT *ctx, double latitude_projection_centre,
6501
    double longitude_of_origin, double colatitude_cone_axis,
6502
    double latitude_pseudo_standard_parallel,
6503
    double scale_factor_pseudo_standard_parallel, double false_easting,
6504
    double false_northing, const char *ang_unit_name,
6505
    double ang_unit_conv_factor, const char *linear_unit_name,
6506
0
    double linear_unit_conv_factor) {
6507
0
    SANITIZE_CTX(ctx);
6508
0
    try {
6509
0
        UnitOfMeasure linearUnit(
6510
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6511
0
        UnitOfMeasure angUnit(
6512
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6513
0
        auto conv = Conversion::createKrovak(
6514
0
            PropertyMap(), Angle(latitude_projection_centre, angUnit),
6515
0
            Angle(longitude_of_origin, angUnit),
6516
0
            Angle(colatitude_cone_axis, angUnit),
6517
0
            Angle(latitude_pseudo_standard_parallel, angUnit),
6518
0
            Scale(scale_factor_pseudo_standard_parallel),
6519
0
            Length(false_easting, linearUnit),
6520
0
            Length(false_northing, linearUnit));
6521
0
        return proj_create_conversion(ctx, conv);
6522
0
    } catch (const std::exception &e) {
6523
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6524
0
    }
6525
0
    return nullptr;
6526
0
}
6527
// ---------------------------------------------------------------------------
6528
6529
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
6530
 * Azimuthal Equal Area projection method.
6531
 *
6532
 * See osgeo::proj::operation::Conversion::createLambertAzimuthalEqualArea().
6533
 *
6534
 * Linear parameters are expressed in (linear_unit_name,
6535
 * linear_unit_conv_factor).
6536
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6537
 */
6538
PJ *proj_create_conversion_lambert_azimuthal_equal_area(
6539
    PJ_CONTEXT *ctx, double latitude_nat_origin, double longitude_nat_origin,
6540
    double false_easting, double false_northing, const char *ang_unit_name,
6541
    double ang_unit_conv_factor, const char *linear_unit_name,
6542
0
    double linear_unit_conv_factor) {
6543
0
    SANITIZE_CTX(ctx);
6544
0
    try {
6545
0
        UnitOfMeasure linearUnit(
6546
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6547
0
        UnitOfMeasure angUnit(
6548
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6549
0
        auto conv = Conversion::createLambertAzimuthalEqualArea(
6550
0
            PropertyMap(), Angle(latitude_nat_origin, angUnit),
6551
0
            Angle(longitude_nat_origin, angUnit),
6552
0
            Length(false_easting, linearUnit),
6553
0
            Length(false_northing, linearUnit));
6554
0
        return proj_create_conversion(ctx, conv);
6555
0
    } catch (const std::exception &e) {
6556
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6557
0
    }
6558
0
    return nullptr;
6559
0
}
6560
// ---------------------------------------------------------------------------
6561
6562
/** \brief Instantiate a ProjectedCRS with a conversion based on the Miller
6563
 * Cylindrical projection method.
6564
 *
6565
 * See osgeo::proj::operation::Conversion::createMillerCylindrical().
6566
 *
6567
 * Linear parameters are expressed in (linear_unit_name,
6568
 * linear_unit_conv_factor).
6569
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6570
 */
6571
PJ *proj_create_conversion_miller_cylindrical(
6572
    PJ_CONTEXT *ctx, double center_long, double false_easting,
6573
    double false_northing, const char *ang_unit_name,
6574
    double ang_unit_conv_factor, const char *linear_unit_name,
6575
0
    double linear_unit_conv_factor) {
6576
0
    SANITIZE_CTX(ctx);
6577
0
    try {
6578
0
        UnitOfMeasure linearUnit(
6579
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6580
0
        UnitOfMeasure angUnit(
6581
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6582
0
        auto conv = Conversion::createMillerCylindrical(
6583
0
            PropertyMap(), Angle(center_long, angUnit),
6584
0
            Length(false_easting, linearUnit),
6585
0
            Length(false_northing, linearUnit));
6586
0
        return proj_create_conversion(ctx, conv);
6587
0
    } catch (const std::exception &e) {
6588
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6589
0
    }
6590
0
    return nullptr;
6591
0
}
6592
// ---------------------------------------------------------------------------
6593
6594
/** \brief Instantiate a ProjectedCRS with a conversion based on the Mercator
6595
 * projection method.
6596
 *
6597
 * See osgeo::proj::operation::Conversion::createMercatorVariantA().
6598
 *
6599
 * Linear parameters are expressed in (linear_unit_name,
6600
 * linear_unit_conv_factor).
6601
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6602
 */
6603
PJ *proj_create_conversion_mercator_variant_a(
6604
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
6605
    double false_easting, double false_northing, const char *ang_unit_name,
6606
    double ang_unit_conv_factor, const char *linear_unit_name,
6607
0
    double linear_unit_conv_factor) {
6608
0
    SANITIZE_CTX(ctx);
6609
0
    try {
6610
0
        UnitOfMeasure linearUnit(
6611
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6612
0
        UnitOfMeasure angUnit(
6613
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6614
0
        auto conv = Conversion::createMercatorVariantA(
6615
0
            PropertyMap(), Angle(center_lat, angUnit),
6616
0
            Angle(center_long, angUnit), Scale(scale),
6617
0
            Length(false_easting, linearUnit),
6618
0
            Length(false_northing, linearUnit));
6619
0
        return proj_create_conversion(ctx, conv);
6620
0
    } catch (const std::exception &e) {
6621
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6622
0
    }
6623
0
    return nullptr;
6624
0
}
6625
// ---------------------------------------------------------------------------
6626
6627
/** \brief Instantiate a ProjectedCRS with a conversion based on the Mercator
6628
 * projection method.
6629
 *
6630
 * See osgeo::proj::operation::Conversion::createMercatorVariantB().
6631
 *
6632
 * Linear parameters are expressed in (linear_unit_name,
6633
 * linear_unit_conv_factor).
6634
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6635
 */
6636
PJ *proj_create_conversion_mercator_variant_b(
6637
    PJ_CONTEXT *ctx, double latitude_first_parallel, double center_long,
6638
    double false_easting, double false_northing, const char *ang_unit_name,
6639
    double ang_unit_conv_factor, const char *linear_unit_name,
6640
0
    double linear_unit_conv_factor) {
6641
0
    SANITIZE_CTX(ctx);
6642
0
    try {
6643
0
        UnitOfMeasure linearUnit(
6644
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6645
0
        UnitOfMeasure angUnit(
6646
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6647
0
        auto conv = Conversion::createMercatorVariantB(
6648
0
            PropertyMap(), Angle(latitude_first_parallel, angUnit),
6649
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
6650
0
            Length(false_northing, linearUnit));
6651
0
        return proj_create_conversion(ctx, conv);
6652
0
    } catch (const std::exception &e) {
6653
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6654
0
    }
6655
0
    return nullptr;
6656
0
}
6657
// ---------------------------------------------------------------------------
6658
6659
/** \brief Instantiate a ProjectedCRS with a conversion based on the Popular
6660
 * Visualisation Pseudo Mercator projection method.
6661
 *
6662
 * See
6663
 * osgeo::proj::operation::Conversion::createPopularVisualisationPseudoMercator().
6664
 *
6665
 * Linear parameters are expressed in (linear_unit_name,
6666
 * linear_unit_conv_factor).
6667
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6668
 */
6669
PJ *proj_create_conversion_popular_visualisation_pseudo_mercator(
6670
    PJ_CONTEXT *ctx, double center_lat, double center_long,
6671
    double false_easting, double false_northing, const char *ang_unit_name,
6672
    double ang_unit_conv_factor, const char *linear_unit_name,
6673
0
    double linear_unit_conv_factor) {
6674
0
    SANITIZE_CTX(ctx);
6675
0
    try {
6676
0
        UnitOfMeasure linearUnit(
6677
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6678
0
        UnitOfMeasure angUnit(
6679
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6680
0
        auto conv = Conversion::createPopularVisualisationPseudoMercator(
6681
0
            PropertyMap(), Angle(center_lat, angUnit),
6682
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
6683
0
            Length(false_northing, linearUnit));
6684
0
        return proj_create_conversion(ctx, conv);
6685
0
    } catch (const std::exception &e) {
6686
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6687
0
    }
6688
0
    return nullptr;
6689
0
}
6690
// ---------------------------------------------------------------------------
6691
6692
/** \brief Instantiate a ProjectedCRS with a conversion based on the Mollweide
6693
 * projection method.
6694
 *
6695
 * See osgeo::proj::operation::Conversion::createMollweide().
6696
 *
6697
 * Linear parameters are expressed in (linear_unit_name,
6698
 * linear_unit_conv_factor).
6699
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6700
 */
6701
PJ *proj_create_conversion_mollweide(PJ_CONTEXT *ctx, double center_long,
6702
                                     double false_easting,
6703
                                     double false_northing,
6704
                                     const char *ang_unit_name,
6705
                                     double ang_unit_conv_factor,
6706
                                     const char *linear_unit_name,
6707
0
                                     double linear_unit_conv_factor) {
6708
0
    SANITIZE_CTX(ctx);
6709
0
    try {
6710
0
        UnitOfMeasure linearUnit(
6711
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6712
0
        UnitOfMeasure angUnit(
6713
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6714
0
        auto conv = Conversion::createMollweide(
6715
0
            PropertyMap(), Angle(center_long, angUnit),
6716
0
            Length(false_easting, linearUnit),
6717
0
            Length(false_northing, linearUnit));
6718
0
        return proj_create_conversion(ctx, conv);
6719
0
    } catch (const std::exception &e) {
6720
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6721
0
    }
6722
0
    return nullptr;
6723
0
}
6724
// ---------------------------------------------------------------------------
6725
6726
/** \brief Instantiate a ProjectedCRS with a conversion based on the New Zealand
6727
 * Map Grid projection method.
6728
 *
6729
 * See osgeo::proj::operation::Conversion::createNewZealandMappingGrid().
6730
 *
6731
 * Linear parameters are expressed in (linear_unit_name,
6732
 * linear_unit_conv_factor).
6733
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6734
 */
6735
PJ *proj_create_conversion_new_zealand_mapping_grid(
6736
    PJ_CONTEXT *ctx, double center_lat, double center_long,
6737
    double false_easting, double false_northing, const char *ang_unit_name,
6738
    double ang_unit_conv_factor, const char *linear_unit_name,
6739
0
    double linear_unit_conv_factor) {
6740
0
    SANITIZE_CTX(ctx);
6741
0
    try {
6742
0
        UnitOfMeasure linearUnit(
6743
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6744
0
        UnitOfMeasure angUnit(
6745
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6746
0
        auto conv = Conversion::createNewZealandMappingGrid(
6747
0
            PropertyMap(), Angle(center_lat, angUnit),
6748
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
6749
0
            Length(false_northing, linearUnit));
6750
0
        return proj_create_conversion(ctx, conv);
6751
0
    } catch (const std::exception &e) {
6752
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6753
0
    }
6754
0
    return nullptr;
6755
0
}
6756
// ---------------------------------------------------------------------------
6757
6758
/** \brief Instantiate a ProjectedCRS with a conversion based on the Oblique
6759
 * Stereographic (Alternative) projection method.
6760
 *
6761
 * See osgeo::proj::operation::Conversion::createObliqueStereographic().
6762
 *
6763
 * Linear parameters are expressed in (linear_unit_name,
6764
 * linear_unit_conv_factor).
6765
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6766
 */
6767
PJ *proj_create_conversion_oblique_stereographic(
6768
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
6769
    double false_easting, double false_northing, const char *ang_unit_name,
6770
    double ang_unit_conv_factor, const char *linear_unit_name,
6771
0
    double linear_unit_conv_factor) {
6772
0
    SANITIZE_CTX(ctx);
6773
0
    try {
6774
0
        UnitOfMeasure linearUnit(
6775
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6776
0
        UnitOfMeasure angUnit(
6777
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6778
0
        auto conv = Conversion::createObliqueStereographic(
6779
0
            PropertyMap(), Angle(center_lat, angUnit),
6780
0
            Angle(center_long, angUnit), Scale(scale),
6781
0
            Length(false_easting, linearUnit),
6782
0
            Length(false_northing, linearUnit));
6783
0
        return proj_create_conversion(ctx, conv);
6784
0
    } catch (const std::exception &e) {
6785
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6786
0
    }
6787
0
    return nullptr;
6788
0
}
6789
// ---------------------------------------------------------------------------
6790
6791
/** \brief Instantiate a ProjectedCRS with a conversion based on the
6792
 * Orthographic projection method.
6793
 *
6794
 * See osgeo::proj::operation::Conversion::createOrthographic().
6795
 *
6796
 * Linear parameters are expressed in (linear_unit_name,
6797
 * linear_unit_conv_factor).
6798
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6799
 */
6800
PJ *proj_create_conversion_orthographic(
6801
    PJ_CONTEXT *ctx, double center_lat, double center_long,
6802
    double false_easting, double false_northing, const char *ang_unit_name,
6803
    double ang_unit_conv_factor, const char *linear_unit_name,
6804
0
    double linear_unit_conv_factor) {
6805
0
    SANITIZE_CTX(ctx);
6806
0
    try {
6807
0
        UnitOfMeasure linearUnit(
6808
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6809
0
        UnitOfMeasure angUnit(
6810
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6811
0
        auto conv = Conversion::createOrthographic(
6812
0
            PropertyMap(), Angle(center_lat, angUnit),
6813
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
6814
0
            Length(false_northing, linearUnit));
6815
0
        return proj_create_conversion(ctx, conv);
6816
0
    } catch (const std::exception &e) {
6817
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6818
0
    }
6819
0
    return nullptr;
6820
0
}
6821
// ---------------------------------------------------------------------------
6822
6823
/** \brief Instantiate a ProjectedCRS with a conversion based on the Local
6824
 * Orthographic projection method.
6825
 *
6826
 * See osgeo::proj::operation::Conversion::createLocalOrthographic().
6827
 *
6828
 * Linear parameters are expressed in (linear_unit_name,
6829
 * linear_unit_conv_factor).
6830
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6831
 */
6832
PJ *proj_create_conversion_local_orthographic(
6833
    PJ_CONTEXT *ctx, double center_lat, double center_long, double azimuth,
6834
    double scale, double false_easting, double false_northing,
6835
    const char *ang_unit_name, double ang_unit_conv_factor,
6836
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
6837
0
    SANITIZE_CTX(ctx);
6838
0
    try {
6839
0
        UnitOfMeasure linearUnit(
6840
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6841
0
        UnitOfMeasure angUnit(
6842
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6843
0
        auto conv = Conversion::createLocalOrthographic(
6844
0
            PropertyMap(), Angle(center_lat, angUnit),
6845
0
            Angle(center_long, angUnit), Angle(azimuth, angUnit), Scale(scale),
6846
0
            Length(false_easting, linearUnit),
6847
0
            Length(false_northing, linearUnit));
6848
0
        return proj_create_conversion(ctx, conv);
6849
0
    } catch (const std::exception &e) {
6850
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6851
0
    }
6852
0
    return nullptr;
6853
0
}
6854
// ---------------------------------------------------------------------------
6855
6856
/** \brief Instantiate a ProjectedCRS with a conversion based on the American
6857
 * Polyconic projection method.
6858
 *
6859
 * See osgeo::proj::operation::Conversion::createAmericanPolyconic().
6860
 *
6861
 * Linear parameters are expressed in (linear_unit_name,
6862
 * linear_unit_conv_factor).
6863
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6864
 */
6865
PJ *proj_create_conversion_american_polyconic(
6866
    PJ_CONTEXT *ctx, double center_lat, double center_long,
6867
    double false_easting, double false_northing, const char *ang_unit_name,
6868
    double ang_unit_conv_factor, const char *linear_unit_name,
6869
0
    double linear_unit_conv_factor) {
6870
0
    SANITIZE_CTX(ctx);
6871
0
    try {
6872
0
        UnitOfMeasure linearUnit(
6873
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6874
0
        UnitOfMeasure angUnit(
6875
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6876
0
        auto conv = Conversion::createAmericanPolyconic(
6877
0
            PropertyMap(), Angle(center_lat, angUnit),
6878
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
6879
0
            Length(false_northing, linearUnit));
6880
0
        return proj_create_conversion(ctx, conv);
6881
0
    } catch (const std::exception &e) {
6882
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6883
0
    }
6884
0
    return nullptr;
6885
0
}
6886
// ---------------------------------------------------------------------------
6887
6888
/** \brief Instantiate a ProjectedCRS with a conversion based on the Polar
6889
 * Stereographic (Variant A) projection method.
6890
 *
6891
 * See osgeo::proj::operation::Conversion::createPolarStereographicVariantA().
6892
 *
6893
 * Linear parameters are expressed in (linear_unit_name,
6894
 * linear_unit_conv_factor).
6895
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6896
 */
6897
PJ *proj_create_conversion_polar_stereographic_variant_a(
6898
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
6899
    double false_easting, double false_northing, const char *ang_unit_name,
6900
    double ang_unit_conv_factor, const char *linear_unit_name,
6901
0
    double linear_unit_conv_factor) {
6902
0
    SANITIZE_CTX(ctx);
6903
0
    try {
6904
0
        UnitOfMeasure linearUnit(
6905
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6906
0
        UnitOfMeasure angUnit(
6907
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6908
0
        auto conv = Conversion::createPolarStereographicVariantA(
6909
0
            PropertyMap(), Angle(center_lat, angUnit),
6910
0
            Angle(center_long, angUnit), Scale(scale),
6911
0
            Length(false_easting, linearUnit),
6912
0
            Length(false_northing, linearUnit));
6913
0
        return proj_create_conversion(ctx, conv);
6914
0
    } catch (const std::exception &e) {
6915
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6916
0
    }
6917
0
    return nullptr;
6918
0
}
6919
// ---------------------------------------------------------------------------
6920
6921
/** \brief Instantiate a ProjectedCRS with a conversion based on the Polar
6922
 * Stereographic (Variant B) projection method.
6923
 *
6924
 * See osgeo::proj::operation::Conversion::createPolarStereographicVariantB().
6925
 *
6926
 * Linear parameters are expressed in (linear_unit_name,
6927
 * linear_unit_conv_factor).
6928
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6929
 */
6930
PJ *proj_create_conversion_polar_stereographic_variant_b(
6931
    PJ_CONTEXT *ctx, double latitude_standard_parallel,
6932
    double longitude_of_origin, double false_easting, double false_northing,
6933
    const char *ang_unit_name, double ang_unit_conv_factor,
6934
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
6935
0
    SANITIZE_CTX(ctx);
6936
0
    try {
6937
0
        UnitOfMeasure linearUnit(
6938
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6939
0
        UnitOfMeasure angUnit(
6940
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6941
0
        auto conv = Conversion::createPolarStereographicVariantB(
6942
0
            PropertyMap(), Angle(latitude_standard_parallel, angUnit),
6943
0
            Angle(longitude_of_origin, angUnit),
6944
0
            Length(false_easting, linearUnit),
6945
0
            Length(false_northing, linearUnit));
6946
0
        return proj_create_conversion(ctx, conv);
6947
0
    } catch (const std::exception &e) {
6948
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6949
0
    }
6950
0
    return nullptr;
6951
0
}
6952
// ---------------------------------------------------------------------------
6953
6954
/** \brief Instantiate a ProjectedCRS with a conversion based on the Robinson
6955
 * projection method.
6956
 *
6957
 * See osgeo::proj::operation::Conversion::createRobinson().
6958
 *
6959
 * Linear parameters are expressed in (linear_unit_name,
6960
 * linear_unit_conv_factor).
6961
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6962
 */
6963
PJ *proj_create_conversion_robinson(PJ_CONTEXT *ctx, double center_long,
6964
                                    double false_easting, double false_northing,
6965
                                    const char *ang_unit_name,
6966
                                    double ang_unit_conv_factor,
6967
                                    const char *linear_unit_name,
6968
0
                                    double linear_unit_conv_factor) {
6969
0
    SANITIZE_CTX(ctx);
6970
0
    try {
6971
0
        UnitOfMeasure linearUnit(
6972
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6973
0
        UnitOfMeasure angUnit(
6974
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6975
0
        auto conv = Conversion::createRobinson(
6976
0
            PropertyMap(), Angle(center_long, angUnit),
6977
0
            Length(false_easting, linearUnit),
6978
0
            Length(false_northing, linearUnit));
6979
0
        return proj_create_conversion(ctx, conv);
6980
0
    } catch (const std::exception &e) {
6981
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6982
0
    }
6983
0
    return nullptr;
6984
0
}
6985
// ---------------------------------------------------------------------------
6986
6987
/** \brief Instantiate a ProjectedCRS with a conversion based on the Sinusoidal
6988
 * projection method.
6989
 *
6990
 * See osgeo::proj::operation::Conversion::createSinusoidal().
6991
 *
6992
 * Linear parameters are expressed in (linear_unit_name,
6993
 * linear_unit_conv_factor).
6994
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6995
 */
6996
PJ *proj_create_conversion_sinusoidal(PJ_CONTEXT *ctx, double center_long,
6997
                                      double false_easting,
6998
                                      double false_northing,
6999
                                      const char *ang_unit_name,
7000
                                      double ang_unit_conv_factor,
7001
                                      const char *linear_unit_name,
7002
0
                                      double linear_unit_conv_factor) {
7003
0
    SANITIZE_CTX(ctx);
7004
0
    try {
7005
0
        UnitOfMeasure linearUnit(
7006
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7007
0
        UnitOfMeasure angUnit(
7008
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7009
0
        auto conv = Conversion::createSinusoidal(
7010
0
            PropertyMap(), Angle(center_long, angUnit),
7011
0
            Length(false_easting, linearUnit),
7012
0
            Length(false_northing, linearUnit));
7013
0
        return proj_create_conversion(ctx, conv);
7014
0
    } catch (const std::exception &e) {
7015
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7016
0
    }
7017
0
    return nullptr;
7018
0
}
7019
// ---------------------------------------------------------------------------
7020
7021
/** \brief Instantiate a ProjectedCRS with a conversion based on the
7022
 * Stereographic projection method.
7023
 *
7024
 * See osgeo::proj::operation::Conversion::createStereographic().
7025
 *
7026
 * Linear parameters are expressed in (linear_unit_name,
7027
 * linear_unit_conv_factor).
7028
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7029
 */
7030
PJ *proj_create_conversion_stereographic(
7031
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
7032
    double false_easting, double false_northing, const char *ang_unit_name,
7033
    double ang_unit_conv_factor, const char *linear_unit_name,
7034
0
    double linear_unit_conv_factor) {
7035
0
    SANITIZE_CTX(ctx);
7036
0
    try {
7037
0
        UnitOfMeasure linearUnit(
7038
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7039
0
        UnitOfMeasure angUnit(
7040
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7041
0
        auto conv = Conversion::createStereographic(
7042
0
            PropertyMap(), Angle(center_lat, angUnit),
7043
0
            Angle(center_long, angUnit), Scale(scale),
7044
0
            Length(false_easting, linearUnit),
7045
0
            Length(false_northing, linearUnit));
7046
0
        return proj_create_conversion(ctx, conv);
7047
0
    } catch (const std::exception &e) {
7048
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7049
0
    }
7050
0
    return nullptr;
7051
0
}
7052
// ---------------------------------------------------------------------------
7053
7054
/** \brief Instantiate a ProjectedCRS with a conversion based on the Van der
7055
 * Grinten projection method.
7056
 *
7057
 * See osgeo::proj::operation::Conversion::createVanDerGrinten().
7058
 *
7059
 * Linear parameters are expressed in (linear_unit_name,
7060
 * linear_unit_conv_factor).
7061
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7062
 */
7063
PJ *proj_create_conversion_van_der_grinten(PJ_CONTEXT *ctx, double center_long,
7064
                                           double false_easting,
7065
                                           double false_northing,
7066
                                           const char *ang_unit_name,
7067
                                           double ang_unit_conv_factor,
7068
                                           const char *linear_unit_name,
7069
0
                                           double linear_unit_conv_factor) {
7070
0
    SANITIZE_CTX(ctx);
7071
0
    try {
7072
0
        UnitOfMeasure linearUnit(
7073
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7074
0
        UnitOfMeasure angUnit(
7075
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7076
0
        auto conv = Conversion::createVanDerGrinten(
7077
0
            PropertyMap(), Angle(center_long, angUnit),
7078
0
            Length(false_easting, linearUnit),
7079
0
            Length(false_northing, linearUnit));
7080
0
        return proj_create_conversion(ctx, conv);
7081
0
    } catch (const std::exception &e) {
7082
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7083
0
    }
7084
0
    return nullptr;
7085
0
}
7086
// ---------------------------------------------------------------------------
7087
7088
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner I
7089
 * projection method.
7090
 *
7091
 * See osgeo::proj::operation::Conversion::createWagnerI().
7092
 *
7093
 * Linear parameters are expressed in (linear_unit_name,
7094
 * linear_unit_conv_factor).
7095
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7096
 */
7097
PJ *proj_create_conversion_wagner_i(PJ_CONTEXT *ctx, double center_long,
7098
                                    double false_easting, double false_northing,
7099
                                    const char *ang_unit_name,
7100
                                    double ang_unit_conv_factor,
7101
                                    const char *linear_unit_name,
7102
0
                                    double linear_unit_conv_factor) {
7103
0
    SANITIZE_CTX(ctx);
7104
0
    try {
7105
0
        UnitOfMeasure linearUnit(
7106
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7107
0
        UnitOfMeasure angUnit(
7108
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7109
0
        auto conv = Conversion::createWagnerI(
7110
0
            PropertyMap(), Angle(center_long, angUnit),
7111
0
            Length(false_easting, linearUnit),
7112
0
            Length(false_northing, linearUnit));
7113
0
        return proj_create_conversion(ctx, conv);
7114
0
    } catch (const std::exception &e) {
7115
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7116
0
    }
7117
0
    return nullptr;
7118
0
}
7119
// ---------------------------------------------------------------------------
7120
7121
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner II
7122
 * projection method.
7123
 *
7124
 * See osgeo::proj::operation::Conversion::createWagnerII().
7125
 *
7126
 * Linear parameters are expressed in (linear_unit_name,
7127
 * linear_unit_conv_factor).
7128
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7129
 */
7130
PJ *proj_create_conversion_wagner_ii(PJ_CONTEXT *ctx, double center_long,
7131
                                     double false_easting,
7132
                                     double false_northing,
7133
                                     const char *ang_unit_name,
7134
                                     double ang_unit_conv_factor,
7135
                                     const char *linear_unit_name,
7136
0
                                     double linear_unit_conv_factor) {
7137
0
    SANITIZE_CTX(ctx);
7138
0
    try {
7139
0
        UnitOfMeasure linearUnit(
7140
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7141
0
        UnitOfMeasure angUnit(
7142
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7143
0
        auto conv = Conversion::createWagnerII(
7144
0
            PropertyMap(), Angle(center_long, angUnit),
7145
0
            Length(false_easting, linearUnit),
7146
0
            Length(false_northing, linearUnit));
7147
0
        return proj_create_conversion(ctx, conv);
7148
0
    } catch (const std::exception &e) {
7149
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7150
0
    }
7151
0
    return nullptr;
7152
0
}
7153
// ---------------------------------------------------------------------------
7154
7155
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner III
7156
 * projection method.
7157
 *
7158
 * See osgeo::proj::operation::Conversion::createWagnerIII().
7159
 *
7160
 * Linear parameters are expressed in (linear_unit_name,
7161
 * linear_unit_conv_factor).
7162
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7163
 */
7164
PJ *proj_create_conversion_wagner_iii(
7165
    PJ_CONTEXT *ctx, double latitude_true_scale, double center_long,
7166
    double false_easting, double false_northing, const char *ang_unit_name,
7167
    double ang_unit_conv_factor, const char *linear_unit_name,
7168
0
    double linear_unit_conv_factor) {
7169
0
    SANITIZE_CTX(ctx);
7170
0
    try {
7171
0
        UnitOfMeasure linearUnit(
7172
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7173
0
        UnitOfMeasure angUnit(
7174
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7175
0
        auto conv = Conversion::createWagnerIII(
7176
0
            PropertyMap(), Angle(latitude_true_scale, angUnit),
7177
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
7178
0
            Length(false_northing, linearUnit));
7179
0
        return proj_create_conversion(ctx, conv);
7180
0
    } catch (const std::exception &e) {
7181
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7182
0
    }
7183
0
    return nullptr;
7184
0
}
7185
// ---------------------------------------------------------------------------
7186
7187
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner IV
7188
 * projection method.
7189
 *
7190
 * See osgeo::proj::operation::Conversion::createWagnerIV().
7191
 *
7192
 * Linear parameters are expressed in (linear_unit_name,
7193
 * linear_unit_conv_factor).
7194
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7195
 */
7196
PJ *proj_create_conversion_wagner_iv(PJ_CONTEXT *ctx, double center_long,
7197
                                     double false_easting,
7198
                                     double false_northing,
7199
                                     const char *ang_unit_name,
7200
                                     double ang_unit_conv_factor,
7201
                                     const char *linear_unit_name,
7202
0
                                     double linear_unit_conv_factor) {
7203
0
    SANITIZE_CTX(ctx);
7204
0
    try {
7205
0
        UnitOfMeasure linearUnit(
7206
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7207
0
        UnitOfMeasure angUnit(
7208
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7209
0
        auto conv = Conversion::createWagnerIV(
7210
0
            PropertyMap(), Angle(center_long, angUnit),
7211
0
            Length(false_easting, linearUnit),
7212
0
            Length(false_northing, linearUnit));
7213
0
        return proj_create_conversion(ctx, conv);
7214
0
    } catch (const std::exception &e) {
7215
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7216
0
    }
7217
0
    return nullptr;
7218
0
}
7219
// ---------------------------------------------------------------------------
7220
7221
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner V
7222
 * projection method.
7223
 *
7224
 * See osgeo::proj::operation::Conversion::createWagnerV().
7225
 *
7226
 * Linear parameters are expressed in (linear_unit_name,
7227
 * linear_unit_conv_factor).
7228
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7229
 */
7230
PJ *proj_create_conversion_wagner_v(PJ_CONTEXT *ctx, double center_long,
7231
                                    double false_easting, double false_northing,
7232
                                    const char *ang_unit_name,
7233
                                    double ang_unit_conv_factor,
7234
                                    const char *linear_unit_name,
7235
0
                                    double linear_unit_conv_factor) {
7236
0
    SANITIZE_CTX(ctx);
7237
0
    try {
7238
0
        UnitOfMeasure linearUnit(
7239
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7240
0
        UnitOfMeasure angUnit(
7241
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7242
0
        auto conv = Conversion::createWagnerV(
7243
0
            PropertyMap(), Angle(center_long, angUnit),
7244
0
            Length(false_easting, linearUnit),
7245
0
            Length(false_northing, linearUnit));
7246
0
        return proj_create_conversion(ctx, conv);
7247
0
    } catch (const std::exception &e) {
7248
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7249
0
    }
7250
0
    return nullptr;
7251
0
}
7252
// ---------------------------------------------------------------------------
7253
7254
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner VI
7255
 * projection method.
7256
 *
7257
 * See osgeo::proj::operation::Conversion::createWagnerVI().
7258
 *
7259
 * Linear parameters are expressed in (linear_unit_name,
7260
 * linear_unit_conv_factor).
7261
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7262
 */
7263
PJ *proj_create_conversion_wagner_vi(PJ_CONTEXT *ctx, double center_long,
7264
                                     double false_easting,
7265
                                     double false_northing,
7266
                                     const char *ang_unit_name,
7267
                                     double ang_unit_conv_factor,
7268
                                     const char *linear_unit_name,
7269
0
                                     double linear_unit_conv_factor) {
7270
0
    SANITIZE_CTX(ctx);
7271
0
    try {
7272
0
        UnitOfMeasure linearUnit(
7273
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7274
0
        UnitOfMeasure angUnit(
7275
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7276
0
        auto conv = Conversion::createWagnerVI(
7277
0
            PropertyMap(), Angle(center_long, angUnit),
7278
0
            Length(false_easting, linearUnit),
7279
0
            Length(false_northing, linearUnit));
7280
0
        return proj_create_conversion(ctx, conv);
7281
0
    } catch (const std::exception &e) {
7282
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7283
0
    }
7284
0
    return nullptr;
7285
0
}
7286
// ---------------------------------------------------------------------------
7287
7288
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner VII
7289
 * projection method.
7290
 *
7291
 * See osgeo::proj::operation::Conversion::createWagnerVII().
7292
 *
7293
 * Linear parameters are expressed in (linear_unit_name,
7294
 * linear_unit_conv_factor).
7295
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7296
 */
7297
PJ *proj_create_conversion_wagner_vii(PJ_CONTEXT *ctx, double center_long,
7298
                                      double false_easting,
7299
                                      double false_northing,
7300
                                      const char *ang_unit_name,
7301
                                      double ang_unit_conv_factor,
7302
                                      const char *linear_unit_name,
7303
0
                                      double linear_unit_conv_factor) {
7304
0
    SANITIZE_CTX(ctx);
7305
0
    try {
7306
0
        UnitOfMeasure linearUnit(
7307
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7308
0
        UnitOfMeasure angUnit(
7309
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7310
0
        auto conv = Conversion::createWagnerVII(
7311
0
            PropertyMap(), Angle(center_long, angUnit),
7312
0
            Length(false_easting, linearUnit),
7313
0
            Length(false_northing, linearUnit));
7314
0
        return proj_create_conversion(ctx, conv);
7315
0
    } catch (const std::exception &e) {
7316
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7317
0
    }
7318
0
    return nullptr;
7319
0
}
7320
// ---------------------------------------------------------------------------
7321
7322
/** \brief Instantiate a ProjectedCRS with a conversion based on the
7323
 * Quadrilateralized Spherical Cube projection method.
7324
 *
7325
 * See
7326
 * osgeo::proj::operation::Conversion::createQuadrilateralizedSphericalCube().
7327
 *
7328
 * Linear parameters are expressed in (linear_unit_name,
7329
 * linear_unit_conv_factor).
7330
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7331
 */
7332
PJ *proj_create_conversion_quadrilateralized_spherical_cube(
7333
    PJ_CONTEXT *ctx, double center_lat, double center_long,
7334
    double false_easting, double false_northing, const char *ang_unit_name,
7335
    double ang_unit_conv_factor, const char *linear_unit_name,
7336
0
    double linear_unit_conv_factor) {
7337
0
    SANITIZE_CTX(ctx);
7338
0
    try {
7339
0
        UnitOfMeasure linearUnit(
7340
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7341
0
        UnitOfMeasure angUnit(
7342
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7343
0
        auto conv = Conversion::createQuadrilateralizedSphericalCube(
7344
0
            PropertyMap(), Angle(center_lat, angUnit),
7345
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
7346
0
            Length(false_northing, linearUnit));
7347
0
        return proj_create_conversion(ctx, conv);
7348
0
    } catch (const std::exception &e) {
7349
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7350
0
    }
7351
0
    return nullptr;
7352
0
}
7353
// ---------------------------------------------------------------------------
7354
7355
/** \brief Instantiate a ProjectedCRS with a conversion based on the Spherical
7356
 * Cross-Track Height projection method.
7357
 *
7358
 * See osgeo::proj::operation::Conversion::createSphericalCrossTrackHeight().
7359
 *
7360
 * Linear parameters are expressed in (linear_unit_name,
7361
 * linear_unit_conv_factor).
7362
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7363
 */
7364
PJ *proj_create_conversion_spherical_cross_track_height(
7365
    PJ_CONTEXT *ctx, double peg_point_lat, double peg_point_long,
7366
    double peg_point_heading, double peg_point_height,
7367
    const char *ang_unit_name, double ang_unit_conv_factor,
7368
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
7369
0
    SANITIZE_CTX(ctx);
7370
0
    try {
7371
0
        UnitOfMeasure linearUnit(
7372
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7373
0
        UnitOfMeasure angUnit(
7374
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7375
0
        auto conv = Conversion::createSphericalCrossTrackHeight(
7376
0
            PropertyMap(), Angle(peg_point_lat, angUnit),
7377
0
            Angle(peg_point_long, angUnit), Angle(peg_point_heading, angUnit),
7378
0
            Length(peg_point_height, linearUnit));
7379
0
        return proj_create_conversion(ctx, conv);
7380
0
    } catch (const std::exception &e) {
7381
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7382
0
    }
7383
0
    return nullptr;
7384
0
}
7385
// ---------------------------------------------------------------------------
7386
7387
/** \brief Instantiate a ProjectedCRS with a conversion based on the Equal Earth
7388
 * projection method.
7389
 *
7390
 * See osgeo::proj::operation::Conversion::createEqualEarth().
7391
 *
7392
 * Linear parameters are expressed in (linear_unit_name,
7393
 * linear_unit_conv_factor).
7394
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7395
 */
7396
PJ *proj_create_conversion_equal_earth(PJ_CONTEXT *ctx, double center_long,
7397
                                       double false_easting,
7398
                                       double false_northing,
7399
                                       const char *ang_unit_name,
7400
                                       double ang_unit_conv_factor,
7401
                                       const char *linear_unit_name,
7402
0
                                       double linear_unit_conv_factor) {
7403
0
    SANITIZE_CTX(ctx);
7404
0
    try {
7405
0
        UnitOfMeasure linearUnit(
7406
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7407
0
        UnitOfMeasure angUnit(
7408
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7409
0
        auto conv = Conversion::createEqualEarth(
7410
0
            PropertyMap(), Angle(center_long, angUnit),
7411
0
            Length(false_easting, linearUnit),
7412
0
            Length(false_northing, linearUnit));
7413
0
        return proj_create_conversion(ctx, conv);
7414
0
    } catch (const std::exception &e) {
7415
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7416
0
    }
7417
0
    return nullptr;
7418
0
}
7419
7420
// ---------------------------------------------------------------------------
7421
7422
/** \brief Instantiate a conversion based on the Vertical Perspective projection
7423
 * method.
7424
 *
7425
 * See osgeo::proj::operation::Conversion::createVerticalPerspective().
7426
 *
7427
 * Linear parameters are expressed in (linear_unit_name,
7428
 * linear_unit_conv_factor).
7429
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7430
 *
7431
 * @since 6.3
7432
 */
7433
PJ *proj_create_conversion_vertical_perspective(
7434
    PJ_CONTEXT *ctx, double topo_origin_lat, double topo_origin_long,
7435
    double topo_origin_height, double view_point_height, double false_easting,
7436
    double false_northing, const char *ang_unit_name,
7437
    double ang_unit_conv_factor, const char *linear_unit_name,
7438
0
    double linear_unit_conv_factor) {
7439
0
    SANITIZE_CTX(ctx);
7440
0
    try {
7441
0
        UnitOfMeasure linearUnit(
7442
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7443
0
        UnitOfMeasure angUnit(
7444
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7445
0
        auto conv = Conversion::createVerticalPerspective(
7446
0
            PropertyMap(), Angle(topo_origin_lat, angUnit),
7447
0
            Angle(topo_origin_long, angUnit),
7448
0
            Length(topo_origin_height, linearUnit),
7449
0
            Length(view_point_height, linearUnit),
7450
0
            Length(false_easting, linearUnit),
7451
0
            Length(false_northing, linearUnit));
7452
0
        return proj_create_conversion(ctx, conv);
7453
0
    } catch (const std::exception &e) {
7454
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7455
0
    }
7456
0
    return nullptr;
7457
0
}
7458
7459
// ---------------------------------------------------------------------------
7460
7461
/** \brief Instantiate a conversion based on the Pole Rotation method, using the
7462
 * conventions of the GRIB 1 and GRIB 2 data formats.
7463
 *
7464
 * See osgeo::proj::operation::Conversion::createPoleRotationGRIBConvention().
7465
 *
7466
 * Linear parameters are expressed in (linear_unit_name,
7467
 * linear_unit_conv_factor).
7468
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7469
 */
7470
PJ *proj_create_conversion_pole_rotation_grib_convention(
7471
    PJ_CONTEXT *ctx, double south_pole_lat_in_unrotated_crs,
7472
    double south_pole_long_in_unrotated_crs, double axis_rotation,
7473
0
    const char *ang_unit_name, double ang_unit_conv_factor) {
7474
0
    SANITIZE_CTX(ctx);
7475
0
    try {
7476
0
        UnitOfMeasure angUnit(
7477
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7478
0
        auto conv = Conversion::createPoleRotationGRIBConvention(
7479
0
            PropertyMap(), Angle(south_pole_lat_in_unrotated_crs, angUnit),
7480
0
            Angle(south_pole_long_in_unrotated_crs, angUnit),
7481
0
            Angle(axis_rotation, angUnit));
7482
0
        return proj_create_conversion(ctx, conv);
7483
0
    } catch (const std::exception &e) {
7484
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7485
0
    }
7486
0
    return nullptr;
7487
0
}
7488
7489
// ---------------------------------------------------------------------------
7490
7491
/** \brief Instantiate a conversion based on the Pole Rotation method, using
7492
 * the conventions of the netCDF CF convention for the netCDF format.
7493
 *
7494
 * See
7495
 * osgeo::proj::operation::Conversion::createPoleRotationNetCDFCFConvention().
7496
 *
7497
 * Linear parameters are expressed in (linear_unit_name,
7498
 * linear_unit_conv_factor).
7499
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7500
 */
7501
PJ *proj_create_conversion_pole_rotation_netcdf_cf_convention(
7502
    PJ_CONTEXT *ctx, double grid_north_pole_latitude,
7503
    double grid_north_pole_longitude, double north_pole_grid_longitude,
7504
0
    const char *ang_unit_name, double ang_unit_conv_factor) {
7505
0
    SANITIZE_CTX(ctx);
7506
0
    try {
7507
0
        UnitOfMeasure angUnit(
7508
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7509
0
        auto conv = Conversion::createPoleRotationNetCDFCFConvention(
7510
0
            PropertyMap(), Angle(grid_north_pole_latitude, angUnit),
7511
0
            Angle(grid_north_pole_longitude, angUnit),
7512
0
            Angle(north_pole_grid_longitude, angUnit));
7513
0
        return proj_create_conversion(ctx, conv);
7514
0
    } catch (const std::exception &e) {
7515
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7516
0
    }
7517
0
    return nullptr;
7518
0
}
7519
7520
/* END: Generated by scripts/create_c_api_projections.py*/
7521
7522
// ---------------------------------------------------------------------------
7523
7524
/** \brief Return whether a coordinate operation can be instantiated as
7525
 * a PROJ pipeline, checking in particular that referenced grids are
7526
 * available.
7527
 *
7528
 * @param ctx PROJ context, or NULL for default context
7529
 * @param coordoperation Object of type CoordinateOperation or derived classes
7530
 * (must not be NULL)
7531
 * @return TRUE or FALSE.
7532
 */
7533
7534
int proj_coordoperation_is_instantiable(PJ_CONTEXT *ctx,
7535
0
                                        const PJ *coordoperation) {
7536
0
    SANITIZE_CTX(ctx);
7537
0
    if (!coordoperation) {
7538
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7539
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7540
0
        return false;
7541
0
    }
7542
0
    auto op = dynamic_cast<const CoordinateOperation *>(
7543
0
        coordoperation->iso_obj.get());
7544
0
    if (!op) {
7545
0
        proj_log_error(ctx, __FUNCTION__,
7546
0
                       "Object is not a CoordinateOperation");
7547
0
        return 0;
7548
0
    }
7549
0
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
7550
0
    try {
7551
0
        auto ret = op->isPROJInstantiable(
7552
0
                       dbContext, proj_context_is_network_enabled(ctx) != FALSE)
7553
0
                       ? 1
7554
0
                       : 0;
7555
0
        return ret;
7556
0
    } catch (const std::exception &) {
7557
0
        return 0;
7558
0
    }
7559
0
}
7560
7561
// ---------------------------------------------------------------------------
7562
7563
/** \brief Return whether a coordinate operation has a "ballpark"
7564
 * transformation,
7565
 * that is a very approximate one, due to lack of more accurate transformations.
7566
 *
7567
 * Typically a null geographic offset between two horizontal datum, or a
7568
 * null vertical offset (or limited to unit changes) between two vertical
7569
 * datum. Errors of several tens to one hundred meters might be expected,
7570
 * compared to more accurate transformations.
7571
 *
7572
 * @param ctx PROJ context, or NULL for default context
7573
 * @param coordoperation Object of type CoordinateOperation or derived classes
7574
 * (must not be NULL)
7575
 * @return TRUE or FALSE.
7576
 */
7577
7578
int proj_coordoperation_has_ballpark_transformation(PJ_CONTEXT *ctx,
7579
0
                                                    const PJ *coordoperation) {
7580
0
    SANITIZE_CTX(ctx);
7581
0
    if (!coordoperation) {
7582
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7583
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7584
0
        return false;
7585
0
    }
7586
0
    auto op = dynamic_cast<const CoordinateOperation *>(
7587
0
        coordoperation->iso_obj.get());
7588
0
    if (!op) {
7589
0
        proj_log_error(ctx, __FUNCTION__,
7590
0
                       "Object is not a CoordinateOperation");
7591
0
        return 0;
7592
0
    }
7593
0
    return op->hasBallparkTransformation();
7594
0
}
7595
7596
// ---------------------------------------------------------------------------
7597
7598
/** \brief Return whether a coordinate operation requires coordinate tuples
7599
 * to have a valid input time for the coordinate transformation to succeed.
7600
 * (this applies for the forward direction)
7601
 *
7602
 * Note: in the case of a time-dependent Helmert transformation, this function
7603
 * will return true, but when executing proj_trans(), execution will still
7604
 * succeed if the time information is missing, due to the transformation central
7605
 * epoch being used as a fallback.
7606
 *
7607
 * @param ctx PROJ context, or NULL for default context
7608
 * @param coordoperation Object of type CoordinateOperation or derived classes
7609
 * (must not be NULL)
7610
 * @return TRUE or FALSE.
7611
 * @since 9.5
7612
 */
7613
7614
int proj_coordoperation_requires_per_coordinate_input_time(
7615
0
    PJ_CONTEXT *ctx, const PJ *coordoperation) {
7616
0
    SANITIZE_CTX(ctx);
7617
0
    if (!coordoperation) {
7618
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7619
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7620
0
        return false;
7621
0
    }
7622
0
    auto op = dynamic_cast<const CoordinateOperation *>(
7623
0
        coordoperation->iso_obj.get());
7624
0
    if (!op) {
7625
0
        proj_log_error(ctx, __FUNCTION__,
7626
0
                       "Object is not a CoordinateOperation");
7627
0
        return false;
7628
0
    }
7629
0
    return op->requiresPerCoordinateInputTime();
7630
0
}
7631
7632
// ---------------------------------------------------------------------------
7633
7634
/** \brief Return the number of parameters of a SingleOperation
7635
 *
7636
 * @param ctx PROJ context, or NULL for default context
7637
 * @param coordoperation Object of type SingleOperation or derived classes
7638
 * (must not be NULL)
7639
 */
7640
7641
int proj_coordoperation_get_param_count(PJ_CONTEXT *ctx,
7642
0
                                        const PJ *coordoperation) {
7643
0
    SANITIZE_CTX(ctx);
7644
0
    if (!coordoperation) {
7645
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7646
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7647
0
        return false;
7648
0
    }
7649
0
    auto op =
7650
0
        dynamic_cast<const SingleOperation *>(coordoperation->iso_obj.get());
7651
0
    if (!op) {
7652
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleOperation");
7653
0
        return 0;
7654
0
    }
7655
0
    return static_cast<int>(op->parameterValues().size());
7656
0
}
7657
7658
// ---------------------------------------------------------------------------
7659
7660
/** \brief Return the index of a parameter of a SingleOperation
7661
 *
7662
 * @param ctx PROJ context, or NULL for default context
7663
 * @param coordoperation Object of type SingleOperation or derived classes
7664
 * (must not be NULL)
7665
 * @param name Parameter name. Must not be NULL
7666
 * @return index (>=0), or -1 in case of error.
7667
 */
7668
7669
int proj_coordoperation_get_param_index(PJ_CONTEXT *ctx,
7670
                                        const PJ *coordoperation,
7671
0
                                        const char *name) {
7672
0
    SANITIZE_CTX(ctx);
7673
0
    if (!coordoperation || !name) {
7674
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7675
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7676
0
        return -1;
7677
0
    }
7678
0
    auto op =
7679
0
        dynamic_cast<const SingleOperation *>(coordoperation->iso_obj.get());
7680
0
    if (!op) {
7681
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleOperation");
7682
0
        return -1;
7683
0
    }
7684
0
    int index = 0;
7685
0
    for (const auto &genParam : op->method()->parameters()) {
7686
0
        if (Identifier::isEquivalentName(genParam->nameStr().c_str(), name)) {
7687
0
            return index;
7688
0
        }
7689
0
        index++;
7690
0
    }
7691
0
    return -1;
7692
0
}
7693
7694
// ---------------------------------------------------------------------------
7695
7696
/** \brief Return a parameter of a SingleOperation
7697
 *
7698
 * @param ctx PROJ context, or NULL for default context
7699
 * @param coordoperation Object of type SingleOperation or derived classes
7700
 * (must not be NULL)
7701
 * @param index Parameter index.
7702
 * @param out_name Pointer to a string value to store the parameter name. or
7703
 * NULL
7704
 * @param out_auth_name Pointer to a string value to store the parameter
7705
 * authority name. or NULL
7706
 * @param out_code Pointer to a string value to store the parameter
7707
 * code. or NULL
7708
 * @param out_value Pointer to a double value to store the parameter
7709
 * value (if numeric). or NULL
7710
 * @param out_value_string Pointer to a string value to store the parameter
7711
 * value (if of type string). or NULL
7712
 * @param out_unit_conv_factor Pointer to a double value to store the parameter
7713
 * unit conversion factor. or NULL
7714
 * @param out_unit_name Pointer to a string value to store the parameter
7715
 * unit name. or NULL
7716
 * @param out_unit_auth_name Pointer to a string value to store the
7717
 * unit authority name. or NULL
7718
 * @param out_unit_code Pointer to a string value to store the
7719
 * unit code. or NULL
7720
 * @param out_unit_category Pointer to a string value to store the parameter
7721
 * name. or
7722
 * NULL. This value might be "unknown", "none", "linear", "linear_per_time",
7723
 * "angular", "angular_per_time", "scale", "scale_per_time", "time",
7724
 * "parametric" or "parametric_per_time"
7725
 * @return TRUE in case of success.
7726
 */
7727
7728
int proj_coordoperation_get_param(
7729
    PJ_CONTEXT *ctx, const PJ *coordoperation, int index, const char **out_name,
7730
    const char **out_auth_name, const char **out_code, double *out_value,
7731
    const char **out_value_string, double *out_unit_conv_factor,
7732
    const char **out_unit_name, const char **out_unit_auth_name,
7733
0
    const char **out_unit_code, const char **out_unit_category) {
7734
0
    SANITIZE_CTX(ctx);
7735
0
    if (!coordoperation) {
7736
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7737
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7738
0
        return false;
7739
0
    }
7740
0
    auto op =
7741
0
        dynamic_cast<const SingleOperation *>(coordoperation->iso_obj.get());
7742
0
    if (!op) {
7743
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleOperation");
7744
0
        return false;
7745
0
    }
7746
0
    const auto &parameters = op->method()->parameters();
7747
0
    const auto &values = op->parameterValues();
7748
0
    if (static_cast<size_t>(index) >= parameters.size() ||
7749
0
        static_cast<size_t>(index) >= values.size()) {
7750
0
        proj_log_error(ctx, __FUNCTION__, "Invalid index");
7751
0
        return false;
7752
0
    }
7753
7754
0
    const auto &param = parameters[index];
7755
0
    const auto &param_ids = param->identifiers();
7756
0
    if (out_name) {
7757
0
        *out_name = param->name()->description()->c_str();
7758
0
    }
7759
0
    if (out_auth_name) {
7760
0
        if (!param_ids.empty()) {
7761
0
            *out_auth_name = param_ids[0]->codeSpace()->c_str();
7762
0
        } else {
7763
0
            *out_auth_name = nullptr;
7764
0
        }
7765
0
    }
7766
0
    if (out_code) {
7767
0
        if (!param_ids.empty()) {
7768
0
            *out_code = param_ids[0]->code().c_str();
7769
0
        } else {
7770
0
            *out_code = nullptr;
7771
0
        }
7772
0
    }
7773
7774
0
    const auto &value = values[index];
7775
0
    ParameterValuePtr paramValue = nullptr;
7776
0
    auto opParamValue =
7777
0
        dynamic_cast<const OperationParameterValue *>(value.get());
7778
0
    if (opParamValue) {
7779
0
        paramValue = opParamValue->parameterValue().as_nullable();
7780
0
    }
7781
0
    if (out_value) {
7782
0
        *out_value = 0;
7783
0
        if (paramValue) {
7784
0
            if (paramValue->type() == ParameterValue::Type::MEASURE) {
7785
0
                *out_value = paramValue->value().value();
7786
0
            }
7787
0
        }
7788
0
    }
7789
0
    if (out_value_string) {
7790
0
        *out_value_string = nullptr;
7791
0
        if (paramValue) {
7792
0
            if (paramValue->type() == ParameterValue::Type::FILENAME) {
7793
0
                *out_value_string = paramValue->valueFile().c_str();
7794
0
            } else if (paramValue->type() == ParameterValue::Type::STRING) {
7795
0
                *out_value_string = paramValue->stringValue().c_str();
7796
0
            }
7797
0
        }
7798
0
    }
7799
0
    if (out_unit_conv_factor) {
7800
0
        *out_unit_conv_factor = 0;
7801
0
    }
7802
0
    if (out_unit_name) {
7803
0
        *out_unit_name = nullptr;
7804
0
    }
7805
0
    if (out_unit_auth_name) {
7806
0
        *out_unit_auth_name = nullptr;
7807
0
    }
7808
0
    if (out_unit_code) {
7809
0
        *out_unit_code = nullptr;
7810
0
    }
7811
0
    if (out_unit_category) {
7812
0
        *out_unit_category = nullptr;
7813
0
    }
7814
0
    if (paramValue) {
7815
0
        if (paramValue->type() == ParameterValue::Type::MEASURE) {
7816
0
            const auto &unit = paramValue->value().unit();
7817
0
            if (out_unit_conv_factor) {
7818
0
                *out_unit_conv_factor = unit.conversionToSI();
7819
0
            }
7820
0
            if (out_unit_name) {
7821
0
                *out_unit_name = unit.name().c_str();
7822
0
            }
7823
0
            if (out_unit_auth_name) {
7824
0
                *out_unit_auth_name = unit.codeSpace().c_str();
7825
0
            }
7826
0
            if (out_unit_code) {
7827
0
                *out_unit_code = unit.code().c_str();
7828
0
            }
7829
0
            if (out_unit_category) {
7830
0
                *out_unit_category =
7831
0
                    get_unit_category(unit.name(), unit.type());
7832
0
            }
7833
0
        }
7834
0
    }
7835
7836
0
    return true;
7837
0
}
7838
7839
// ---------------------------------------------------------------------------
7840
7841
/** \brief Return the parameters of a Helmert transformation as WKT1 TOWGS84
7842
 * values.
7843
 *
7844
 * @param ctx PROJ context, or NULL for default context
7845
 * @param coordoperation Object of type Transformation, that can be represented
7846
 * as a WKT1 TOWGS84 node (must not be NULL)
7847
 * @param out_values Pointer to an array of value_count double values.
7848
 * @param value_count Size of out_values array. The suggested size is 7 to get
7849
 * translation, rotation and scale difference parameters. Rotation and scale
7850
 * difference terms might be zero if the transformation only includes
7851
 * translation
7852
 * parameters. In that case, value_count could be set to 3.
7853
 * @param emit_error_if_incompatible Boolean to indicate if an error must be
7854
 * logged if coordoperation is not compatible with a WKT1 TOWGS84
7855
 * representation.
7856
 * @return TRUE in case of success, or FALSE if coordoperation is not
7857
 * compatible with a WKT1 TOWGS84 representation.
7858
 */
7859
7860
int proj_coordoperation_get_towgs84_values(PJ_CONTEXT *ctx,
7861
                                           const PJ *coordoperation,
7862
                                           double *out_values, int value_count,
7863
0
                                           int emit_error_if_incompatible) {
7864
0
    SANITIZE_CTX(ctx);
7865
0
    if (!coordoperation) {
7866
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7867
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7868
0
        return false;
7869
0
    }
7870
0
    auto transf =
7871
0
        dynamic_cast<const Transformation *>(coordoperation->iso_obj.get());
7872
0
    if (!transf) {
7873
0
        if (emit_error_if_incompatible) {
7874
0
            proj_log_error(ctx, __FUNCTION__, "Object is not a Transformation");
7875
0
        }
7876
0
        return FALSE;
7877
0
    }
7878
7879
0
    const auto values = transf->getTOWGS84Parameters(false);
7880
0
    if (!values.empty()) {
7881
0
        for (int i = 0;
7882
0
             i < value_count && static_cast<size_t>(i) < values.size(); i++) {
7883
0
            out_values[i] = values[i];
7884
0
        }
7885
0
        return TRUE;
7886
0
    } else {
7887
0
        if (emit_error_if_incompatible) {
7888
0
            proj_log_error(ctx, __FUNCTION__,
7889
0
                           "Transformation cannot be formatted as WKT1 TOWGS84 "
7890
0
                           "parameters");
7891
0
        }
7892
0
        return FALSE;
7893
0
    }
7894
0
}
7895
7896
// ---------------------------------------------------------------------------
7897
7898
/** \brief Return the number of grids used by a CoordinateOperation
7899
 *
7900
 * @param ctx PROJ context, or NULL for default context
7901
 * @param coordoperation Object of type CoordinateOperation or derived classes
7902
 * (must not be NULL)
7903
 */
7904
7905
int proj_coordoperation_get_grid_used_count(PJ_CONTEXT *ctx,
7906
0
                                            const PJ *coordoperation) {
7907
0
    SANITIZE_CTX(ctx);
7908
0
    if (!coordoperation) {
7909
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7910
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7911
0
        return false;
7912
0
    }
7913
0
    auto co = dynamic_cast<const CoordinateOperation *>(
7914
0
        coordoperation->iso_obj.get());
7915
0
    if (!co) {
7916
0
        proj_log_error(ctx, __FUNCTION__,
7917
0
                       "Object is not a CoordinateOperation");
7918
0
        return 0;
7919
0
    }
7920
0
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
7921
0
    try {
7922
0
        if (!coordoperation->gridsNeededAsked) {
7923
0
            coordoperation->gridsNeededAsked = true;
7924
0
            const auto gridsNeeded = co->gridsNeeded(
7925
0
                dbContext, proj_context_is_network_enabled(ctx) != FALSE);
7926
0
            for (const auto &gridDesc : gridsNeeded) {
7927
0
                coordoperation->gridsNeeded.emplace_back(gridDesc);
7928
0
            }
7929
0
        }
7930
0
        return static_cast<int>(coordoperation->gridsNeeded.size());
7931
0
    } catch (const std::exception &e) {
7932
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7933
0
        return 0;
7934
0
    }
7935
0
}
7936
7937
// ---------------------------------------------------------------------------
7938
7939
/** \brief Return a parameter of a SingleOperation
7940
 *
7941
 * @param ctx PROJ context, or NULL for default context
7942
 * @param coordoperation Object of type SingleOperation or derived classes
7943
 * (must not be NULL)
7944
 * @param index Parameter index.
7945
 * @param out_short_name Pointer to a string value to store the grid short name.
7946
 * or NULL
7947
 * @param out_full_name Pointer to a string value to store the grid full
7948
 * filename. or NULL
7949
 * @param out_package_name Pointer to a string value to store the package name
7950
 * where
7951
 * the grid might be found. or NULL
7952
 * @param out_url Pointer to a string value to store the grid URL or the
7953
 * package URL where the grid might be found. or NULL
7954
 * @param out_direct_download Pointer to a int (boolean) value to store whether
7955
 * *out_url can be downloaded directly. or NULL
7956
 * @param out_open_license Pointer to a int (boolean) value to store whether
7957
 * the grid is released with an open license. or NULL
7958
 * @param out_available Pointer to a int (boolean) value to store whether the
7959
 * grid is available at runtime. or NULL
7960
 * @return TRUE in case of success.
7961
 */
7962
7963
int proj_coordoperation_get_grid_used(
7964
    PJ_CONTEXT *ctx, const PJ *coordoperation, int index,
7965
    const char **out_short_name, const char **out_full_name,
7966
    const char **out_package_name, const char **out_url,
7967
0
    int *out_direct_download, int *out_open_license, int *out_available) {
7968
0
    SANITIZE_CTX(ctx);
7969
0
    const int count =
7970
0
        proj_coordoperation_get_grid_used_count(ctx, coordoperation);
7971
0
    if (index < 0 || index >= count) {
7972
0
        proj_log_error(ctx, __FUNCTION__, "Invalid index");
7973
0
        return false;
7974
0
    }
7975
7976
0
    const auto &gridDesc = coordoperation->gridsNeeded[index];
7977
0
    if (out_short_name) {
7978
0
        *out_short_name = gridDesc.shortName.c_str();
7979
0
    }
7980
7981
0
    if (out_full_name) {
7982
0
        *out_full_name = gridDesc.fullName.c_str();
7983
0
    }
7984
7985
0
    if (out_package_name) {
7986
0
        *out_package_name = gridDesc.packageName.c_str();
7987
0
    }
7988
7989
0
    if (out_url) {
7990
0
        *out_url = gridDesc.url.c_str();
7991
0
    }
7992
7993
0
    if (out_direct_download) {
7994
0
        *out_direct_download = gridDesc.directDownload;
7995
0
    }
7996
7997
0
    if (out_open_license) {
7998
0
        *out_open_license = gridDesc.openLicense;
7999
0
    }
8000
8001
0
    if (out_available) {
8002
0
        *out_available = gridDesc.available;
8003
0
    }
8004
8005
0
    return true;
8006
0
}
8007
8008
// ---------------------------------------------------------------------------
8009
8010
/** \brief Opaque object representing an operation factory context. */
8011
struct PJ_OPERATION_FACTORY_CONTEXT {
8012
    //! @cond Doxygen_Suppress
8013
    CoordinateOperationContextNNPtr operationContext;
8014
8015
    explicit PJ_OPERATION_FACTORY_CONTEXT(
8016
        CoordinateOperationContextNNPtr &&operationContextIn)
8017
0
        : operationContext(std::move(operationContextIn)) {}
8018
8019
    PJ_OPERATION_FACTORY_CONTEXT(const PJ_OPERATION_FACTORY_CONTEXT &) = delete;
8020
    PJ_OPERATION_FACTORY_CONTEXT &
8021
    operator=(const PJ_OPERATION_FACTORY_CONTEXT &) = delete;
8022
    //! @endcond
8023
};
8024
8025
// ---------------------------------------------------------------------------
8026
8027
/** \brief Instantiate a context for building coordinate operations between
8028
 * two CRS.
8029
 *
8030
 * The returned object must be unreferenced with
8031
 * proj_operation_factory_context_destroy() after use.
8032
 *
8033
 * If authority is NULL or the empty string, then coordinate
8034
 * operations from any authority will be searched, with the restrictions set
8035
 * in the authority_to_authority_preference database table.
8036
 * If authority is set to "any", then coordinate
8037
 * operations from any authority will be searched
8038
 * If authority is a non-empty string different of "any",
8039
 * then coordinate operations will be searched only in that authority namespace.
8040
 *
8041
 * @param ctx Context, or NULL for default context.
8042
 * @param authority Name of authority to which to restrict the search of
8043
 *                  candidate operations.
8044
 * @return Object that must be unreferenced with
8045
 * proj_operation_factory_context_destroy(), or NULL in
8046
 * case of error.
8047
 */
8048
PJ_OPERATION_FACTORY_CONTEXT *
8049
0
proj_create_operation_factory_context(PJ_CONTEXT *ctx, const char *authority) {
8050
0
    SANITIZE_CTX(ctx);
8051
0
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
8052
0
    try {
8053
0
        if (dbContext) {
8054
0
            auto factory = CoordinateOperationFactory::create();
8055
0
            auto authFactory = AuthorityFactory::create(
8056
0
                NN_NO_CHECK(dbContext),
8057
0
                std::string(authority ? authority : ""));
8058
0
            auto operationContext =
8059
0
                CoordinateOperationContext::create(authFactory, nullptr, 0.0);
8060
0
            return new PJ_OPERATION_FACTORY_CONTEXT(
8061
0
                std::move(operationContext));
8062
0
        } else {
8063
0
            auto operationContext =
8064
0
                CoordinateOperationContext::create(nullptr, nullptr, 0.0);
8065
0
            return new PJ_OPERATION_FACTORY_CONTEXT(
8066
0
                std::move(operationContext));
8067
0
        }
8068
0
    } catch (const std::exception &e) {
8069
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8070
0
    }
8071
0
    return nullptr;
8072
0
}
8073
8074
// ---------------------------------------------------------------------------
8075
8076
/** \brief Drops a reference on an object.
8077
 *
8078
 * This method should be called one and exactly one for each function
8079
 * returning a PJ_OPERATION_FACTORY_CONTEXT*
8080
 *
8081
 * @param ctx Object, or NULL.
8082
 */
8083
0
void proj_operation_factory_context_destroy(PJ_OPERATION_FACTORY_CONTEXT *ctx) {
8084
0
    delete ctx;
8085
0
}
8086
8087
// ---------------------------------------------------------------------------
8088
8089
/** \brief Set the desired accuracy of the resulting coordinate transformations.
8090
 * @param ctx PROJ context, or NULL for default context
8091
 * @param factory_ctx Operation factory context. must not be NULL
8092
 * @param accuracy Accuracy in meter (or 0 to disable the filter).
8093
 */
8094
void proj_operation_factory_context_set_desired_accuracy(
8095
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8096
0
    double accuracy) {
8097
0
    SANITIZE_CTX(ctx);
8098
0
    if (!factory_ctx) {
8099
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8100
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8101
0
        return;
8102
0
    }
8103
0
    try {
8104
0
        factory_ctx->operationContext->setDesiredAccuracy(accuracy);
8105
0
    } catch (const std::exception &e) {
8106
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8107
0
    }
8108
0
}
8109
8110
// ---------------------------------------------------------------------------
8111
8112
/** \brief Set the desired area of interest for the resulting coordinate
8113
 * transformations.
8114
 *
8115
 * For an area of interest crossing the anti-meridian, west_lon_degree will be
8116
 * greater than east_lon_degree.
8117
 *
8118
 * @param ctx PROJ context, or NULL for default context
8119
 * @param factory_ctx Operation factory context. must not be NULL
8120
 * @param west_lon_degree West longitude (in degrees).
8121
 * @param south_lat_degree South latitude (in degrees).
8122
 * @param east_lon_degree East longitude (in degrees).
8123
 * @param north_lat_degree North latitude (in degrees).
8124
 */
8125
void proj_operation_factory_context_set_area_of_interest(
8126
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8127
    double west_lon_degree, double south_lat_degree, double east_lon_degree,
8128
0
    double north_lat_degree) {
8129
0
    SANITIZE_CTX(ctx);
8130
0
    if (!factory_ctx) {
8131
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8132
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8133
0
        return;
8134
0
    }
8135
0
    try {
8136
0
        factory_ctx->operationContext->setAreaOfInterest(
8137
0
            Extent::createFromBBOX(west_lon_degree, south_lat_degree,
8138
0
                                   east_lon_degree, north_lat_degree));
8139
0
    } catch (const std::exception &e) {
8140
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8141
0
    }
8142
0
}
8143
8144
// ---------------------------------------------------------------------------
8145
8146
/** \brief Set the name of the desired area of interest for the resulting
8147
 * coordinate transformations.
8148
 *
8149
 * @param ctx PROJ context, or NULL for default context
8150
 * @param factory_ctx Operation factory context. must not be NULL
8151
 * @param area_name Area name. Must be known of the database.
8152
 */
8153
void proj_operation_factory_context_set_area_of_interest_name(
8154
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8155
0
    const char *area_name) {
8156
0
    SANITIZE_CTX(ctx);
8157
0
    if (!factory_ctx || !area_name) {
8158
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8159
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8160
0
        return;
8161
0
    }
8162
0
    try {
8163
0
        auto extent = factory_ctx->operationContext->getAreaOfInterest();
8164
0
        if (extent == nullptr) {
8165
0
            auto dbContext = getDBcontext(ctx);
8166
0
            auto factory = AuthorityFactory::create(dbContext, std::string());
8167
0
            auto res = factory->listAreaOfUseFromName(area_name, false);
8168
0
            if (res.size() == 1) {
8169
0
                factory_ctx->operationContext->setAreaOfInterest(
8170
0
                    AuthorityFactory::create(dbContext, res.front().first)
8171
0
                        ->createExtent(res.front().second)
8172
0
                        .as_nullable());
8173
0
            } else {
8174
0
                proj_log_error(ctx, __FUNCTION__, "cannot find area");
8175
0
                return;
8176
0
            }
8177
0
        } else {
8178
0
            factory_ctx->operationContext->setAreaOfInterest(
8179
0
                metadata::Extent::create(util::optional<std::string>(area_name),
8180
0
                                         extent->geographicElements(),
8181
0
                                         extent->verticalElements(),
8182
0
                                         extent->temporalElements())
8183
0
                    .as_nullable());
8184
0
        }
8185
0
    } catch (const std::exception &e) {
8186
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8187
0
    }
8188
0
}
8189
8190
// ---------------------------------------------------------------------------
8191
8192
/** \brief Set how source and target CRS extent should be used
8193
 * when considering if a transformation can be used (only takes effect if
8194
 * no area of interest is explicitly defined).
8195
 *
8196
 * The default is PJ_CRS_EXTENT_SMALLEST.
8197
 *
8198
 * @param ctx PROJ context, or NULL for default context
8199
 * @param factory_ctx Operation factory context. must not be NULL
8200
 * @param use How source and target CRS extent should be used.
8201
 */
8202
void proj_operation_factory_context_set_crs_extent_use(
8203
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8204
0
    PROJ_CRS_EXTENT_USE use) {
8205
0
    SANITIZE_CTX(ctx);
8206
0
    if (!factory_ctx) {
8207
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8208
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8209
0
        return;
8210
0
    }
8211
0
    try {
8212
0
        switch (use) {
8213
0
        case PJ_CRS_EXTENT_NONE:
8214
0
            factory_ctx->operationContext->setSourceAndTargetCRSExtentUse(
8215
0
                CoordinateOperationContext::SourceTargetCRSExtentUse::NONE);
8216
0
            break;
8217
8218
0
        case PJ_CRS_EXTENT_BOTH:
8219
0
            factory_ctx->operationContext->setSourceAndTargetCRSExtentUse(
8220
0
                CoordinateOperationContext::SourceTargetCRSExtentUse::BOTH);
8221
0
            break;
8222
8223
0
        case PJ_CRS_EXTENT_INTERSECTION:
8224
0
            factory_ctx->operationContext->setSourceAndTargetCRSExtentUse(
8225
0
                CoordinateOperationContext::SourceTargetCRSExtentUse::
8226
0
                    INTERSECTION);
8227
0
            break;
8228
8229
0
        case PJ_CRS_EXTENT_SMALLEST:
8230
0
            factory_ctx->operationContext->setSourceAndTargetCRSExtentUse(
8231
0
                CoordinateOperationContext::SourceTargetCRSExtentUse::SMALLEST);
8232
0
            break;
8233
0
        }
8234
0
    } catch (const std::exception &e) {
8235
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8236
0
    }
8237
0
}
8238
8239
// ---------------------------------------------------------------------------
8240
8241
/** \brief Set the spatial criterion to use when comparing the area of
8242
 * validity of coordinate operations with the area of interest / area of
8243
 * validity of
8244
 * source and target CRS.
8245
 *
8246
 * The default is PROJ_SPATIAL_CRITERION_STRICT_CONTAINMENT.
8247
 *
8248
 * @param ctx PROJ context, or NULL for default context
8249
 * @param factory_ctx Operation factory context. must not be NULL
8250
 * @param criterion spatial criterion to use
8251
 */
8252
void proj_operation_factory_context_set_spatial_criterion(
8253
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8254
0
    PROJ_SPATIAL_CRITERION criterion) {
8255
0
    SANITIZE_CTX(ctx);
8256
0
    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
0
    try {
8262
0
        switch (criterion) {
8263
0
        case PROJ_SPATIAL_CRITERION_STRICT_CONTAINMENT:
8264
0
            factory_ctx->operationContext->setSpatialCriterion(
8265
0
                CoordinateOperationContext::SpatialCriterion::
8266
0
                    STRICT_CONTAINMENT);
8267
0
            break;
8268
8269
0
        case PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION:
8270
0
            factory_ctx->operationContext->setSpatialCriterion(
8271
0
                CoordinateOperationContext::SpatialCriterion::
8272
0
                    PARTIAL_INTERSECTION);
8273
0
            break;
8274
0
        }
8275
0
    } catch (const std::exception &e) {
8276
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8277
0
    }
8278
0
}
8279
8280
// ---------------------------------------------------------------------------
8281
8282
/** \brief Set how grid availability is used.
8283
 *
8284
 * The default is USE_FOR_SORTING.
8285
 *
8286
 * @param ctx PROJ context, or NULL for default context
8287
 * @param factory_ctx Operation factory context. must not be NULL
8288
 * @param use how grid availability is used.
8289
 */
8290
void proj_operation_factory_context_set_grid_availability_use(
8291
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8292
0
    PROJ_GRID_AVAILABILITY_USE use) {
8293
0
    SANITIZE_CTX(ctx);
8294
0
    if (!factory_ctx) {
8295
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8296
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8297
0
        return;
8298
0
    }
8299
0
    try {
8300
0
        switch (use) {
8301
0
        case PROJ_GRID_AVAILABILITY_USED_FOR_SORTING:
8302
0
            factory_ctx->operationContext->setGridAvailabilityUse(
8303
0
                CoordinateOperationContext::GridAvailabilityUse::
8304
0
                    USE_FOR_SORTING);
8305
0
            break;
8306
8307
0
        case PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID:
8308
0
            factory_ctx->operationContext->setGridAvailabilityUse(
8309
0
                CoordinateOperationContext::GridAvailabilityUse::
8310
0
                    DISCARD_OPERATION_IF_MISSING_GRID);
8311
0
            break;
8312
8313
0
        case PROJ_GRID_AVAILABILITY_IGNORED:
8314
0
            factory_ctx->operationContext->setGridAvailabilityUse(
8315
0
                CoordinateOperationContext::GridAvailabilityUse::
8316
0
                    IGNORE_GRID_AVAILABILITY);
8317
0
            break;
8318
8319
0
        case PROJ_GRID_AVAILABILITY_KNOWN_AVAILABLE:
8320
0
            factory_ctx->operationContext->setGridAvailabilityUse(
8321
0
                CoordinateOperationContext::GridAvailabilityUse::
8322
0
                    KNOWN_AVAILABLE);
8323
0
            break;
8324
0
        }
8325
0
    } catch (const std::exception &e) {
8326
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8327
0
    }
8328
0
}
8329
8330
// ---------------------------------------------------------------------------
8331
8332
/** \brief Set whether PROJ alternative grid names should be substituted to
8333
 * the official authority names.
8334
 *
8335
 * The default is true.
8336
 *
8337
 * @param ctx PROJ context, or NULL for default context
8338
 * @param factory_ctx Operation factory context. must not be NULL
8339
 * @param usePROJNames whether PROJ alternative grid names should be used
8340
 */
8341
void proj_operation_factory_context_set_use_proj_alternative_grid_names(
8342
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8343
0
    int usePROJNames) {
8344
0
    SANITIZE_CTX(ctx);
8345
0
    if (!factory_ctx) {
8346
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8347
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8348
0
        return;
8349
0
    }
8350
0
    try {
8351
0
        factory_ctx->operationContext->setUsePROJAlternativeGridNames(
8352
0
            usePROJNames != 0);
8353
0
    } catch (const std::exception &e) {
8354
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8355
0
    }
8356
0
}
8357
8358
// ---------------------------------------------------------------------------
8359
8360
/** \brief Set whether an intermediate pivot CRS can be used for researching
8361
 * coordinate operations between a source and target CRS.
8362
 *
8363
 * Concretely if in the database there is an operation from A to C
8364
 * (or C to A), and another one from C to B (or B to C), but no direct
8365
 * operation between A and B, setting this parameter to true, allow
8366
 * chaining both operations.
8367
 *
8368
 * The current implementation is limited to researching one intermediate
8369
 * step.
8370
 *
8371
 * By default, with the IF_NO_DIRECT_TRANSFORMATION strategy, all potential
8372
 * C candidates will be used if there is no direct transformation.
8373
 *
8374
 * @param ctx PROJ context, or NULL for default context
8375
 * @param factory_ctx Operation factory context. must not be NULL
8376
 * @param use whether and how intermediate CRS may be used.
8377
 */
8378
void proj_operation_factory_context_set_allow_use_intermediate_crs(
8379
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8380
0
    PROJ_INTERMEDIATE_CRS_USE use) {
8381
0
    SANITIZE_CTX(ctx);
8382
0
    if (!factory_ctx) {
8383
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8384
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8385
0
        return;
8386
0
    }
8387
0
    try {
8388
0
        switch (use) {
8389
0
        case PROJ_INTERMEDIATE_CRS_USE_ALWAYS:
8390
0
            factory_ctx->operationContext->setAllowUseIntermediateCRS(
8391
0
                CoordinateOperationContext::IntermediateCRSUse::ALWAYS);
8392
0
            break;
8393
8394
0
        case PROJ_INTERMEDIATE_CRS_USE_IF_NO_DIRECT_TRANSFORMATION:
8395
0
            factory_ctx->operationContext->setAllowUseIntermediateCRS(
8396
0
                CoordinateOperationContext::IntermediateCRSUse::
8397
0
                    IF_NO_DIRECT_TRANSFORMATION);
8398
0
            break;
8399
8400
0
        case PROJ_INTERMEDIATE_CRS_USE_NEVER:
8401
0
            factory_ctx->operationContext->setAllowUseIntermediateCRS(
8402
0
                CoordinateOperationContext::IntermediateCRSUse::NEVER);
8403
0
            break;
8404
0
        }
8405
0
    } catch (const std::exception &e) {
8406
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8407
0
    }
8408
0
}
8409
8410
// ---------------------------------------------------------------------------
8411
8412
/** \brief Restrict the potential pivot CRSs that can be used when trying to
8413
 * build a coordinate operation between two CRS that have no direct operation.
8414
 *
8415
 * @param ctx PROJ context, or NULL for default context
8416
 * @param factory_ctx Operation factory context. must not be NULL
8417
 * @param list_of_auth_name_codes an array of strings NLL terminated,
8418
 * with the format { "auth_name1", "code1", "auth_name2", "code2", ... NULL }
8419
 */
8420
void proj_operation_factory_context_set_allowed_intermediate_crs(
8421
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8422
0
    const char *const *list_of_auth_name_codes) {
8423
0
    SANITIZE_CTX(ctx);
8424
0
    if (!factory_ctx) {
8425
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8426
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8427
0
        return;
8428
0
    }
8429
0
    try {
8430
0
        std::vector<std::pair<std::string, std::string>> pivots;
8431
0
        for (auto iter = list_of_auth_name_codes; iter && iter[0] && iter[1];
8432
0
             iter += 2) {
8433
0
            pivots.emplace_back(std::pair<std::string, std::string>(
8434
0
                std::string(iter[0]), std::string(iter[1])));
8435
0
        }
8436
0
        factory_ctx->operationContext->setIntermediateCRS(pivots);
8437
0
    } catch (const std::exception &e) {
8438
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8439
0
    }
8440
0
}
8441
8442
// ---------------------------------------------------------------------------
8443
8444
/** \brief Set whether transformations that are superseded (but not deprecated)
8445
 * should be discarded.
8446
 *
8447
 * @param ctx PROJ context, or NULL for default context
8448
 * @param factory_ctx Operation factory context. must not be NULL
8449
 * @param discard superseded crs or not
8450
 */
8451
void proj_operation_factory_context_set_discard_superseded(
8452
0
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, int discard) {
8453
0
    SANITIZE_CTX(ctx);
8454
0
    if (!factory_ctx) {
8455
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8456
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8457
0
        return;
8458
0
    }
8459
0
    try {
8460
0
        factory_ctx->operationContext->setDiscardSuperseded(discard != 0);
8461
0
    } catch (const std::exception &e) {
8462
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8463
0
    }
8464
0
}
8465
8466
// ---------------------------------------------------------------------------
8467
8468
/** \brief Set whether ballpark transformations are allowed.
8469
 *
8470
 * @param ctx PROJ context, or NULL for default context
8471
 * @param factory_ctx Operation factory context. must not be NULL
8472
 * @param allow set to TRUE to allow ballpark transformations.
8473
 * @since 7.1
8474
 */
8475
void proj_operation_factory_context_set_allow_ballpark_transformations(
8476
0
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, int allow) {
8477
0
    SANITIZE_CTX(ctx);
8478
0
    if (!factory_ctx) {
8479
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8480
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8481
0
        return;
8482
0
    }
8483
0
    try {
8484
0
        factory_ctx->operationContext->setAllowBallparkTransformations(allow !=
8485
0
                                                                       0);
8486
0
    } catch (const std::exception &e) {
8487
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8488
0
    }
8489
0
}
8490
8491
// ---------------------------------------------------------------------------
8492
8493
//! @cond Doxygen_Suppress
8494
/** \brief Opaque object representing a set of operation results. */
8495
struct PJ_OPERATION_LIST : PJ_OBJ_LIST {
8496
8497
    PJ *source_crs;
8498
    PJ *target_crs;
8499
    bool hasPreparedOperation = false;
8500
    std::vector<PJCoordOperation> preparedOperations{};
8501
8502
    explicit PJ_OPERATION_LIST(PJ_CONTEXT *ctx, const PJ *source_crsIn,
8503
                               const PJ *target_crsIn,
8504
                               std::vector<IdentifiedObjectNNPtr> &&objectsIn);
8505
    ~PJ_OPERATION_LIST() override;
8506
8507
    PJ_OPERATION_LIST(const PJ_OPERATION_LIST &) = delete;
8508
    PJ_OPERATION_LIST &operator=(const PJ_OPERATION_LIST &) = delete;
8509
8510
    const std::vector<PJCoordOperation> &getPreparedOperations(PJ_CONTEXT *ctx);
8511
};
8512
8513
// ---------------------------------------------------------------------------
8514
8515
PJ_OPERATION_LIST::PJ_OPERATION_LIST(
8516
    PJ_CONTEXT *ctx, const PJ *source_crsIn, const PJ *target_crsIn,
8517
    std::vector<IdentifiedObjectNNPtr> &&objectsIn)
8518
0
    : PJ_OBJ_LIST(std::move(objectsIn)),
8519
0
      source_crs(proj_clone(ctx, source_crsIn)),
8520
0
      target_crs(proj_clone(ctx, target_crsIn)) {}
8521
8522
// ---------------------------------------------------------------------------
8523
8524
0
PJ_OPERATION_LIST::~PJ_OPERATION_LIST() {
8525
0
    auto tmpCtxt = proj_context_create();
8526
0
    proj_assign_context(source_crs, tmpCtxt);
8527
0
    proj_assign_context(target_crs, tmpCtxt);
8528
0
    proj_destroy(source_crs);
8529
0
    proj_destroy(target_crs);
8530
0
    proj_context_destroy(tmpCtxt);
8531
0
}
8532
8533
// ---------------------------------------------------------------------------
8534
8535
const std::vector<PJCoordOperation> &
8536
0
PJ_OPERATION_LIST::getPreparedOperations(PJ_CONTEXT *ctx) {
8537
0
    if (!hasPreparedOperation) {
8538
0
        hasPreparedOperation = true;
8539
0
        preparedOperations =
8540
0
            pj_create_prepared_operations(ctx, source_crs, target_crs, this);
8541
0
    }
8542
0
    return preparedOperations;
8543
0
}
8544
8545
//! @endcond
8546
8547
// ---------------------------------------------------------------------------
8548
8549
/** \brief Find a list of CoordinateOperation from source_crs to target_crs.
8550
 *
8551
 * The operations are sorted with the most relevant ones first: by
8552
 * descending
8553
 * area (intersection of the transformation area with the area of interest,
8554
 * or intersection of the transformation with the area of use of the CRS),
8555
 * and
8556
 * by increasing accuracy. Operations with unknown accuracy are sorted last,
8557
 * whatever their area.
8558
 *
8559
 * Starting with PROJ 9.1, vertical transformations are only done if both
8560
 * source CRS and target CRS are 3D CRS or Compound CRS with a vertical
8561
 * component. You may need to use proj_crs_promote_to_3D().
8562
 *
8563
 * @param ctx PROJ context, or NULL for default context
8564
 * @param source_crs source CRS or CoordinateMetadata. Must not be NULL.
8565
 * @param target_crs target CRS or CoordinateMetadata. Must not be NULL.
8566
 * @param operationContext Search context. Must not be NULL.
8567
 * @return a result set that must be unreferenced with
8568
 * proj_list_destroy(), or NULL in case of error.
8569
 */
8570
PJ_OBJ_LIST *
8571
proj_create_operations(PJ_CONTEXT *ctx, const PJ *source_crs,
8572
                       const PJ *target_crs,
8573
0
                       const PJ_OPERATION_FACTORY_CONTEXT *operationContext) {
8574
0
    SANITIZE_CTX(ctx);
8575
0
    if (!source_crs || !target_crs || !operationContext) {
8576
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8577
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8578
0
        return nullptr;
8579
0
    }
8580
0
    auto sourceCRS = std::dynamic_pointer_cast<CRS>(source_crs->iso_obj);
8581
0
    CoordinateMetadataPtr sourceCoordinateMetadata;
8582
0
    if (!sourceCRS) {
8583
0
        sourceCoordinateMetadata =
8584
0
            std::dynamic_pointer_cast<CoordinateMetadata>(source_crs->iso_obj);
8585
0
        if (!sourceCoordinateMetadata) {
8586
0
            proj_log_error(ctx, __FUNCTION__,
8587
0
                           "source_crs is not a CRS or a CoordinateMetadata");
8588
0
            return nullptr;
8589
0
        }
8590
0
        if (!sourceCoordinateMetadata->coordinateEpoch().has_value()) {
8591
0
            sourceCRS = sourceCoordinateMetadata->crs().as_nullable();
8592
0
            sourceCoordinateMetadata.reset();
8593
0
        }
8594
0
    }
8595
0
    auto targetCRS = std::dynamic_pointer_cast<CRS>(target_crs->iso_obj);
8596
0
    CoordinateMetadataPtr targetCoordinateMetadata;
8597
0
    if (!targetCRS) {
8598
0
        targetCoordinateMetadata =
8599
0
            std::dynamic_pointer_cast<CoordinateMetadata>(target_crs->iso_obj);
8600
0
        if (!targetCoordinateMetadata) {
8601
0
            proj_log_error(ctx, __FUNCTION__,
8602
0
                           "target_crs is not a CRS or a CoordinateMetadata");
8603
0
            return nullptr;
8604
0
        }
8605
0
        if (!targetCoordinateMetadata->coordinateEpoch().has_value()) {
8606
0
            targetCRS = targetCoordinateMetadata->crs().as_nullable();
8607
0
            targetCoordinateMetadata.reset();
8608
0
        }
8609
0
    }
8610
8611
0
    try {
8612
0
        auto factory = CoordinateOperationFactory::create();
8613
0
        std::vector<IdentifiedObjectNNPtr> objects;
8614
0
        auto ops = sourceCoordinateMetadata != nullptr
8615
0
                       ? (targetCoordinateMetadata != nullptr
8616
0
                              ? factory->createOperations(
8617
0
                                    NN_NO_CHECK(sourceCoordinateMetadata),
8618
0
                                    NN_NO_CHECK(targetCoordinateMetadata),
8619
0
                                    operationContext->operationContext)
8620
0
                              : factory->createOperations(
8621
0
                                    NN_NO_CHECK(sourceCoordinateMetadata),
8622
0
                                    NN_NO_CHECK(targetCRS),
8623
0
                                    operationContext->operationContext))
8624
0
                   : targetCoordinateMetadata != nullptr
8625
0
                       ? factory->createOperations(
8626
0
                             NN_NO_CHECK(sourceCRS),
8627
0
                             NN_NO_CHECK(targetCoordinateMetadata),
8628
0
                             operationContext->operationContext)
8629
0
                       : factory->createOperations(
8630
0
                             NN_NO_CHECK(sourceCRS), NN_NO_CHECK(targetCRS),
8631
0
                             operationContext->operationContext);
8632
0
        for (const auto &op : ops) {
8633
0
            objects.emplace_back(op);
8634
0
        }
8635
0
        return new PJ_OPERATION_LIST(ctx, source_crs, target_crs,
8636
0
                                     std::move(objects));
8637
0
    } catch (const std::exception &e) {
8638
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8639
0
        return nullptr;
8640
0
    }
8641
0
}
8642
8643
// ---------------------------------------------------------------------------
8644
8645
/** Return the index of the operation that would be the most appropriate to
8646
 * transform the specified coordinates.
8647
 *
8648
 * This operation may use resources that are not locally available, depending
8649
 * on the search criteria used by proj_create_operations().
8650
 *
8651
 * This could be done by using proj_create_operations() with a punctual bounding
8652
 * box, but this function is faster when one needs to evaluate on many points
8653
 * with the same (source_crs, target_crs) tuple.
8654
 *
8655
 * @param ctx PROJ context, or NULL for default context
8656
 * @param operations List of operations returned by proj_create_operations()
8657
 * @param direction Direction into which to transform the point.
8658
 * @param coord Coordinate to transform
8659
 * @return the index in operations that would be used to transform coord. Or -1
8660
 * in case of error, or no match.
8661
 *
8662
 * @since 7.1
8663
 */
8664
int proj_get_suggested_operation(PJ_CONTEXT *ctx, PJ_OBJ_LIST *operations,
8665
                                 // cppcheck-suppress passedByValue
8666
0
                                 PJ_DIRECTION direction, PJ_COORD coord) {
8667
0
    SANITIZE_CTX(ctx);
8668
0
    auto opList = dynamic_cast<PJ_OPERATION_LIST *>(operations);
8669
0
    if (opList == nullptr) {
8670
0
        proj_log_error(ctx, __FUNCTION__,
8671
0
                       "operations is not a list of operations");
8672
0
        return -1;
8673
0
    }
8674
8675
    // Special case:
8676
    // proj_create_crs_to_crs_from_pj() always use the unique operation
8677
    // if there's a single one
8678
0
    if (opList->objects.size() == 1) {
8679
0
        return 0;
8680
0
    }
8681
8682
0
    int iExcluded[2] = {-1, -1};
8683
0
    const auto &preparedOps = opList->getPreparedOperations(ctx);
8684
0
    int idx = pj_get_suggested_operation(ctx, preparedOps, iExcluded,
8685
0
                                         /* skipNonInstantiable= */ false,
8686
0
                                         direction, coord);
8687
0
    if (idx >= 0) {
8688
0
        idx = preparedOps[idx].idxInOriginalList;
8689
0
    }
8690
0
    return idx;
8691
0
}
8692
8693
// ---------------------------------------------------------------------------
8694
8695
/** \brief Return the number of objects in the result set
8696
 *
8697
 * @param result Object of type PJ_OBJ_LIST (must not be NULL)
8698
 */
8699
0
int proj_list_get_count(const PJ_OBJ_LIST *result) {
8700
0
    if (!result) {
8701
0
        return 0;
8702
0
    }
8703
0
    return static_cast<int>(result->objects.size());
8704
0
}
8705
8706
// ---------------------------------------------------------------------------
8707
8708
/** \brief Return an object from the result set
8709
 *
8710
 * The returned object must be unreferenced with proj_destroy() after
8711
 * use.
8712
 * It should be used by at most one thread at a time.
8713
 *
8714
 * @param ctx PROJ context, or NULL for default context
8715
 * @param result Object of type PJ_OBJ_LIST (must not be NULL)
8716
 * @param index Index
8717
 * @return a new object that must be unreferenced with proj_destroy(),
8718
 * or nullptr in case of error.
8719
 */
8720
8721
0
PJ *proj_list_get(PJ_CONTEXT *ctx, const PJ_OBJ_LIST *result, int index) {
8722
0
    SANITIZE_CTX(ctx);
8723
0
    if (!result) {
8724
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8725
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8726
0
        return nullptr;
8727
0
    }
8728
0
    if (index < 0 || index >= proj_list_get_count(result)) {
8729
0
        proj_log_error(ctx, __FUNCTION__, "Invalid index");
8730
0
        return nullptr;
8731
0
    }
8732
0
    return pj_obj_create(ctx, result->objects[index]);
8733
0
}
8734
8735
// ---------------------------------------------------------------------------
8736
8737
/** \brief Drops a reference on the result set.
8738
 *
8739
 * This method should be called one and exactly one for each function
8740
 * returning a PJ_OBJ_LIST*
8741
 *
8742
 * @param result Object, or NULL.
8743
 */
8744
0
void proj_list_destroy(PJ_OBJ_LIST *result) { delete result; }
8745
8746
// ---------------------------------------------------------------------------
8747
8748
/** \brief Return the accuracy (in metre) of a coordinate operation.
8749
 *
8750
 * @param ctx PROJ context, or NULL for default context
8751
 * @param coordoperation Coordinate operation. Must not be NULL.
8752
 * @return the accuracy, or a negative value if unknown or in case of error.
8753
 */
8754
double proj_coordoperation_get_accuracy(PJ_CONTEXT *ctx,
8755
0
                                        const PJ *coordoperation) {
8756
0
    SANITIZE_CTX(ctx);
8757
0
    if (!coordoperation) {
8758
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8759
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8760
0
        return -1;
8761
0
    }
8762
0
    auto co = dynamic_cast<const CoordinateOperation *>(
8763
0
        coordoperation->iso_obj.get());
8764
0
    if (!co) {
8765
0
        proj_log_error(ctx, __FUNCTION__,
8766
0
                       "Object is not a CoordinateOperation");
8767
0
        return -1;
8768
0
    }
8769
0
    const auto &accuracies = co->coordinateOperationAccuracies();
8770
0
    if (accuracies.empty()) {
8771
0
        return -1;
8772
0
    }
8773
0
    try {
8774
0
        return c_locale_stod(accuracies[0]->value());
8775
0
    } catch (const std::exception &) {
8776
0
    }
8777
0
    return -1;
8778
0
}
8779
8780
// ---------------------------------------------------------------------------
8781
8782
/** \brief Returns the datum of a SingleCRS.
8783
 *
8784
 * If that function returns NULL, @see proj_crs_get_datum_ensemble() to
8785
 * potentially get a DatumEnsemble instead.
8786
 *
8787
 * The returned object must be unreferenced with proj_destroy() after
8788
 * use.
8789
 * It should be used by at most one thread at a time.
8790
 *
8791
 * @param ctx PROJ context, or NULL for default context
8792
 * @param crs Object of type SingleCRS (must not be NULL)
8793
 * @return Object that must be unreferenced with proj_destroy(), or NULL
8794
 * in case of error (or if there is no datum)
8795
 */
8796
0
PJ *proj_crs_get_datum(PJ_CONTEXT *ctx, const PJ *crs) {
8797
0
    SANITIZE_CTX(ctx);
8798
0
    if (!crs) {
8799
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8800
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8801
0
        return nullptr;
8802
0
    }
8803
0
    auto l_crs = dynamic_cast<const SingleCRS *>(crs->iso_obj.get());
8804
0
    if (!l_crs) {
8805
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleCRS");
8806
0
        return nullptr;
8807
0
    }
8808
0
    const auto &datum = l_crs->datum();
8809
0
    if (!datum) {
8810
0
        return nullptr;
8811
0
    }
8812
0
    return pj_obj_create(ctx, NN_NO_CHECK(datum));
8813
0
}
8814
8815
// ---------------------------------------------------------------------------
8816
8817
/** \brief Returns the datum ensemble of a SingleCRS.
8818
 *
8819
 * If that function returns NULL, @see proj_crs_get_datum() to
8820
 * potentially get a Datum instead.
8821
 *
8822
 * The returned object must be unreferenced with proj_destroy() after
8823
 * use.
8824
 * It should be used by at most one thread at a time.
8825
 *
8826
 * @param ctx PROJ context, or NULL for default context
8827
 * @param crs Object of type SingleCRS (must not be NULL)
8828
 * @return Object that must be unreferenced with proj_destroy(), or NULL
8829
 * in case of error (or if there is no datum ensemble)
8830
 *
8831
 * @since 7.2
8832
 */
8833
0
PJ *proj_crs_get_datum_ensemble(PJ_CONTEXT *ctx, const PJ *crs) {
8834
0
    SANITIZE_CTX(ctx);
8835
0
    if (!crs) {
8836
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8837
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8838
0
        return nullptr;
8839
0
    }
8840
0
    auto l_crs = dynamic_cast<const SingleCRS *>(crs->iso_obj.get());
8841
0
    if (!l_crs) {
8842
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleCRS");
8843
0
        return nullptr;
8844
0
    }
8845
0
    const auto &datumEnsemble = l_crs->datumEnsemble();
8846
0
    if (!datumEnsemble) {
8847
0
        return nullptr;
8848
0
    }
8849
0
    return pj_obj_create(ctx, NN_NO_CHECK(datumEnsemble));
8850
0
}
8851
8852
// ---------------------------------------------------------------------------
8853
8854
/** \brief Returns the number of members of a datum ensemble.
8855
 *
8856
 * @param ctx PROJ context, or NULL for default context
8857
 * @param datum_ensemble Object of type DatumEnsemble (must not be NULL)
8858
 *
8859
 * @since 7.2
8860
 */
8861
int proj_datum_ensemble_get_member_count(PJ_CONTEXT *ctx,
8862
0
                                         const PJ *datum_ensemble) {
8863
0
    SANITIZE_CTX(ctx);
8864
0
    if (!datum_ensemble) {
8865
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8866
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8867
0
        return 0;
8868
0
    }
8869
0
    auto l_datum_ensemble =
8870
0
        dynamic_cast<const DatumEnsemble *>(datum_ensemble->iso_obj.get());
8871
0
    if (!l_datum_ensemble) {
8872
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a DatumEnsemble");
8873
0
        return 0;
8874
0
    }
8875
0
    return static_cast<int>(l_datum_ensemble->datums().size());
8876
0
}
8877
8878
// ---------------------------------------------------------------------------
8879
8880
/** \brief Returns the positional accuracy of the datum ensemble.
8881
 *
8882
 * @param ctx PROJ context, or NULL for default context
8883
 * @param datum_ensemble Object of type DatumEnsemble (must not be NULL)
8884
 * @return the accuracy, or -1 in case of error.
8885
 *
8886
 * @since 7.2
8887
 */
8888
double proj_datum_ensemble_get_accuracy(PJ_CONTEXT *ctx,
8889
0
                                        const PJ *datum_ensemble) {
8890
0
    SANITIZE_CTX(ctx);
8891
0
    if (!datum_ensemble) {
8892
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8893
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8894
0
        return -1;
8895
0
    }
8896
0
    auto l_datum_ensemble =
8897
0
        dynamic_cast<const DatumEnsemble *>(datum_ensemble->iso_obj.get());
8898
0
    if (!l_datum_ensemble) {
8899
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a DatumEnsemble");
8900
0
        return -1;
8901
0
    }
8902
0
    const auto &accuracy = l_datum_ensemble->positionalAccuracy();
8903
0
    try {
8904
0
        return c_locale_stod(accuracy->value());
8905
0
    } catch (const std::exception &) {
8906
0
    }
8907
0
    return -1;
8908
0
}
8909
8910
// ---------------------------------------------------------------------------
8911
8912
/** \brief Returns a member from a datum ensemble.
8913
 *
8914
 * The returned object must be unreferenced with proj_destroy() after
8915
 * use.
8916
 * It should be used by at most one thread at a time.
8917
 *
8918
 * @param ctx PROJ context, or NULL for default context
8919
 * @param datum_ensemble Object of type DatumEnsemble (must not be NULL)
8920
 * @param member_index Index of the datum member to extract (between 0 and
8921
 * proj_datum_ensemble_get_member_count()-1)
8922
 * @return Object that must be unreferenced with proj_destroy(), or NULL
8923
 * in case of error (or if there is no datum ensemble)
8924
 *
8925
 * @since 7.2
8926
 */
8927
PJ *proj_datum_ensemble_get_member(PJ_CONTEXT *ctx, const PJ *datum_ensemble,
8928
0
                                   int member_index) {
8929
0
    SANITIZE_CTX(ctx);
8930
0
    if (!datum_ensemble) {
8931
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8932
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8933
0
        return nullptr;
8934
0
    }
8935
0
    auto l_datum_ensemble =
8936
0
        dynamic_cast<const DatumEnsemble *>(datum_ensemble->iso_obj.get());
8937
0
    if (!l_datum_ensemble) {
8938
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a DatumEnsemble");
8939
0
        return nullptr;
8940
0
    }
8941
0
    if (member_index < 0 ||
8942
0
        member_index >= static_cast<int>(l_datum_ensemble->datums().size())) {
8943
0
        proj_log_error(ctx, __FUNCTION__, "Invalid member_index");
8944
0
        return nullptr;
8945
0
    }
8946
0
    return pj_obj_create(ctx, l_datum_ensemble->datums()[member_index]);
8947
0
}
8948
8949
// ---------------------------------------------------------------------------
8950
8951
/** \brief Returns a datum for a SingleCRS.
8952
 *
8953
 * If the SingleCRS has a datum, then this datum is returned.
8954
 * Otherwise, the SingleCRS has a datum ensemble, and this datum ensemble is
8955
 * returned as a regular datum instead of a datum ensemble.
8956
 *
8957
 * The returned object must be unreferenced with proj_destroy() after
8958
 * use.
8959
 * It should be used by at most one thread at a time.
8960
 *
8961
 * @param ctx PROJ context, or NULL for default context
8962
 * @param crs Object of type SingleCRS (must not be NULL)
8963
 * @return Object that must be unreferenced with proj_destroy(), or NULL
8964
 * in case of error (or if there is no datum)
8965
 *
8966
 * @since 7.2
8967
 */
8968
0
PJ *proj_crs_get_datum_forced(PJ_CONTEXT *ctx, const PJ *crs) {
8969
0
    SANITIZE_CTX(ctx);
8970
0
    if (!crs) {
8971
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8972
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8973
0
        return nullptr;
8974
0
    }
8975
0
    auto l_crs = dynamic_cast<const SingleCRS *>(crs->iso_obj.get());
8976
0
    if (!l_crs) {
8977
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleCRS");
8978
0
        return nullptr;
8979
0
    }
8980
0
    const auto &datum = l_crs->datum();
8981
0
    if (datum) {
8982
0
        return pj_obj_create(ctx, NN_NO_CHECK(datum));
8983
0
    }
8984
0
    const auto &datumEnsemble = l_crs->datumEnsemble();
8985
0
    assert(datumEnsemble);
8986
0
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
8987
0
    try {
8988
0
        return pj_obj_create(ctx, datumEnsemble->asDatum(dbContext));
8989
0
    } catch (const std::exception &e) {
8990
0
        proj_log_debug(ctx, __FUNCTION__, e.what());
8991
0
        return nullptr;
8992
0
    }
8993
0
}
8994
8995
// ---------------------------------------------------------------------------
8996
8997
/** \brief Returns the frame reference epoch of a dynamic geodetic or vertical
8998
 * reference frame.
8999
 *
9000
 * @param ctx PROJ context, or NULL for default context
9001
 * @param datum Object of type DynamicGeodeticReferenceFrame or
9002
 * DynamicVerticalReferenceFrame (must not be NULL)
9003
 * @return the frame reference epoch as decimal year, or -1 in case of error.
9004
 *
9005
 * @since 7.2
9006
 */
9007
double proj_dynamic_datum_get_frame_reference_epoch(PJ_CONTEXT *ctx,
9008
0
                                                    const PJ *datum) {
9009
0
    SANITIZE_CTX(ctx);
9010
0
    if (!datum) {
9011
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9012
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9013
0
        return -1;
9014
0
    }
9015
0
    auto dgrf = dynamic_cast<const DynamicGeodeticReferenceFrame *>(
9016
0
        datum->iso_obj.get());
9017
0
    auto dvrf = dynamic_cast<const DynamicVerticalReferenceFrame *>(
9018
0
        datum->iso_obj.get());
9019
0
    if (!dgrf && !dvrf) {
9020
0
        proj_log_error(ctx, __FUNCTION__,
9021
0
                       "Object is not a "
9022
0
                       "DynamicGeodeticReferenceFrame or "
9023
0
                       "DynamicVerticalReferenceFrame");
9024
0
        return -1;
9025
0
    }
9026
0
    const auto &frameReferenceEpoch =
9027
0
        dgrf ? dgrf->frameReferenceEpoch() : dvrf->frameReferenceEpoch();
9028
0
    return frameReferenceEpoch.value();
9029
0
}
9030
9031
// ---------------------------------------------------------------------------
9032
9033
/** \brief Returns the coordinate system of a SingleCRS.
9034
 *
9035
 * The returned object must be unreferenced with proj_destroy() after
9036
 * use.
9037
 * It should be used by at most one thread at a time.
9038
 *
9039
 * @param ctx PROJ context, or NULL for default context
9040
 * @param crs Object of type SingleCRS (must not be NULL)
9041
 * @return Object that must be unreferenced with proj_destroy(), or NULL
9042
 * in case of error.
9043
 */
9044
0
PJ *proj_crs_get_coordinate_system(PJ_CONTEXT *ctx, const PJ *crs) {
9045
0
    SANITIZE_CTX(ctx);
9046
0
    if (!crs) {
9047
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9048
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9049
0
        return nullptr;
9050
0
    }
9051
0
    auto l_crs = dynamic_cast<const SingleCRS *>(crs->iso_obj.get());
9052
0
    if (!l_crs) {
9053
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleCRS");
9054
0
        return nullptr;
9055
0
    }
9056
0
    return pj_obj_create(ctx, l_crs->coordinateSystem());
9057
0
}
9058
9059
// ---------------------------------------------------------------------------
9060
9061
/** \brief Returns the type of the coordinate system.
9062
 *
9063
 * @param ctx PROJ context, or NULL for default context
9064
 * @param cs Object of type CoordinateSystem (must not be NULL)
9065
 * @return type, or PJ_CS_TYPE_UNKNOWN in case of error.
9066
 */
9067
0
PJ_COORDINATE_SYSTEM_TYPE proj_cs_get_type(PJ_CONTEXT *ctx, const PJ *cs) {
9068
0
    SANITIZE_CTX(ctx);
9069
0
    if (!cs) {
9070
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9071
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9072
0
        return PJ_CS_TYPE_UNKNOWN;
9073
0
    }
9074
0
    auto l_cs = dynamic_cast<const CoordinateSystem *>(cs->iso_obj.get());
9075
0
    if (!l_cs) {
9076
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateSystem");
9077
0
        return PJ_CS_TYPE_UNKNOWN;
9078
0
    }
9079
0
    if (dynamic_cast<const CartesianCS *>(l_cs)) {
9080
0
        return PJ_CS_TYPE_CARTESIAN;
9081
0
    }
9082
0
    if (dynamic_cast<const EllipsoidalCS *>(l_cs)) {
9083
0
        return PJ_CS_TYPE_ELLIPSOIDAL;
9084
0
    }
9085
0
    if (dynamic_cast<const VerticalCS *>(l_cs)) {
9086
0
        return PJ_CS_TYPE_VERTICAL;
9087
0
    }
9088
0
    if (dynamic_cast<const SphericalCS *>(l_cs)) {
9089
0
        return PJ_CS_TYPE_SPHERICAL;
9090
0
    }
9091
0
    if (dynamic_cast<const OrdinalCS *>(l_cs)) {
9092
0
        return PJ_CS_TYPE_ORDINAL;
9093
0
    }
9094
0
    if (dynamic_cast<const ParametricCS *>(l_cs)) {
9095
0
        return PJ_CS_TYPE_PARAMETRIC;
9096
0
    }
9097
0
    if (dynamic_cast<const DateTimeTemporalCS *>(l_cs)) {
9098
0
        return PJ_CS_TYPE_DATETIMETEMPORAL;
9099
0
    }
9100
0
    if (dynamic_cast<const TemporalCountCS *>(l_cs)) {
9101
0
        return PJ_CS_TYPE_TEMPORALCOUNT;
9102
0
    }
9103
0
    if (dynamic_cast<const TemporalMeasureCS *>(l_cs)) {
9104
0
        return PJ_CS_TYPE_TEMPORALMEASURE;
9105
0
    }
9106
0
    return PJ_CS_TYPE_UNKNOWN;
9107
0
}
9108
9109
// ---------------------------------------------------------------------------
9110
9111
/** \brief Returns the number of axis of the coordinate system.
9112
 *
9113
 * @param ctx PROJ context, or NULL for default context
9114
 * @param cs Object of type CoordinateSystem (must not be NULL)
9115
 * @return number of axis, or -1 in case of error.
9116
 */
9117
0
int proj_cs_get_axis_count(PJ_CONTEXT *ctx, const PJ *cs) {
9118
0
    SANITIZE_CTX(ctx);
9119
0
    if (!cs) {
9120
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9121
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9122
0
        return -1;
9123
0
    }
9124
0
    auto l_cs = dynamic_cast<const CoordinateSystem *>(cs->iso_obj.get());
9125
0
    if (!l_cs) {
9126
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateSystem");
9127
0
        return -1;
9128
0
    }
9129
0
    return static_cast<int>(l_cs->axisList().size());
9130
0
}
9131
9132
// ---------------------------------------------------------------------------
9133
9134
/** \brief Returns information on an axis
9135
 *
9136
 * @param ctx PROJ context, or NULL for default context
9137
 * @param cs Object of type CoordinateSystem (must not be NULL)
9138
 * @param index Index of the coordinate system (between 0 and
9139
 * proj_cs_get_axis_count() - 1)
9140
 * @param out_name Pointer to a string value to store the axis name. or NULL
9141
 * @param out_abbrev Pointer to a string value to store the axis abbreviation.
9142
 * or NULL
9143
 * @param out_direction Pointer to a string value to store the axis direction.
9144
 * or NULL
9145
 * @param out_unit_conv_factor Pointer to a double value to store the axis
9146
 * unit conversion factor. or NULL
9147
 * @param out_unit_name Pointer to a string value to store the axis
9148
 * unit name. or NULL
9149
 * @param out_unit_auth_name Pointer to a string value to store the axis
9150
 * unit authority name. or NULL
9151
 * @param out_unit_code Pointer to a string value to store the axis
9152
 * unit code. or NULL
9153
 * @return TRUE in case of success
9154
 */
9155
int proj_cs_get_axis_info(PJ_CONTEXT *ctx, const PJ *cs, int index,
9156
                          const char **out_name, const char **out_abbrev,
9157
                          const char **out_direction,
9158
                          double *out_unit_conv_factor,
9159
                          const char **out_unit_name,
9160
                          const char **out_unit_auth_name,
9161
0
                          const char **out_unit_code) {
9162
0
    SANITIZE_CTX(ctx);
9163
0
    if (!cs) {
9164
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9165
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9166
0
        return false;
9167
0
    }
9168
0
    auto l_cs = dynamic_cast<const CoordinateSystem *>(cs->iso_obj.get());
9169
0
    if (!l_cs) {
9170
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateSystem");
9171
0
        return false;
9172
0
    }
9173
0
    const auto &axisList = l_cs->axisList();
9174
0
    if (index < 0 || static_cast<size_t>(index) >= axisList.size()) {
9175
0
        proj_log_error(ctx, __FUNCTION__, "Invalid index");
9176
0
        return false;
9177
0
    }
9178
0
    const auto &axis = axisList[index];
9179
0
    if (out_name) {
9180
0
        *out_name = axis->nameStr().c_str();
9181
0
    }
9182
0
    if (out_abbrev) {
9183
0
        *out_abbrev = axis->abbreviation().c_str();
9184
0
    }
9185
0
    if (out_direction) {
9186
0
        *out_direction = axis->direction().toString().c_str();
9187
0
    }
9188
0
    if (out_unit_conv_factor) {
9189
0
        *out_unit_conv_factor = axis->unit().conversionToSI();
9190
0
    }
9191
0
    if (out_unit_name) {
9192
0
        *out_unit_name = axis->unit().name().c_str();
9193
0
    }
9194
0
    if (out_unit_auth_name) {
9195
0
        *out_unit_auth_name = axis->unit().codeSpace().c_str();
9196
0
    }
9197
0
    if (out_unit_code) {
9198
0
        *out_unit_code = axis->unit().code().c_str();
9199
0
    }
9200
0
    return true;
9201
0
}
9202
9203
// ---------------------------------------------------------------------------
9204
9205
/** \brief Returns a PJ* object whose axis order is the one expected for
9206
 * visualization purposes.
9207
 *
9208
 * The input object must be either:
9209
 * <ul>
9210
 * <li>a coordinate operation, that has been created with
9211
 *     proj_create_crs_to_crs(). If the axis order of its source or target CRS
9212
 *     is northing,easting, then an axis swap operation will be inserted.</li>
9213
 * <li>or a CRS. The axis order of geographic CRS will be longitude, latitude
9214
 *     [,height], and the one of projected CRS will be easting, northing
9215
 *     [, height]</li>
9216
 * </ul>
9217
 *
9218
 * @param ctx PROJ context, or NULL for default context
9219
 * @param obj Object of type CRS, or CoordinateOperation created with
9220
 * proj_create_crs_to_crs() (must not be NULL)
9221
 * @return a new PJ* object to free with proj_destroy() in case of success, or
9222
 * nullptr in case of error
9223
 */
9224
0
PJ *proj_normalize_for_visualization(PJ_CONTEXT *ctx, const PJ *obj) {
9225
9226
0
    SANITIZE_CTX(ctx);
9227
0
    if (!obj->alternativeCoordinateOperations.empty()) {
9228
0
        try {
9229
0
            auto pjNew = std::unique_ptr<PJ>(pj_new());
9230
0
            if (!pjNew)
9231
0
                return nullptr;
9232
0
            pjNew->ctx = ctx;
9233
0
            pjNew->descr = "Set of coordinate operations";
9234
0
            pjNew->left = obj->left;
9235
0
            pjNew->right = obj->right;
9236
0
            pjNew->copyStateFrom(*obj);
9237
9238
0
            for (const auto &alt : obj->alternativeCoordinateOperations) {
9239
0
                auto co = dynamic_cast<const CoordinateOperation *>(
9240
0
                    alt.pj->iso_obj.get());
9241
0
                if (co) {
9242
0
                    double minxSrc = alt.minxSrc;
9243
0
                    double minySrc = alt.minySrc;
9244
0
                    double maxxSrc = alt.maxxSrc;
9245
0
                    double maxySrc = alt.maxySrc;
9246
0
                    double minxDst = alt.minxDst;
9247
0
                    double minyDst = alt.minyDst;
9248
0
                    double maxxDst = alt.maxxDst;
9249
0
                    double maxyDst = alt.maxyDst;
9250
9251
0
                    auto l_sourceCRS = co->sourceCRS();
9252
0
                    auto l_targetCRS = co->targetCRS();
9253
0
                    if (l_sourceCRS && l_targetCRS) {
9254
0
                        const bool swapSource =
9255
0
                            l_sourceCRS
9256
0
                                ->mustAxisOrderBeSwitchedForVisualization();
9257
0
                        if (swapSource) {
9258
0
                            std::swap(minxSrc, minySrc);
9259
0
                            std::swap(maxxSrc, maxySrc);
9260
0
                        }
9261
0
                        const bool swapTarget =
9262
0
                            l_targetCRS
9263
0
                                ->mustAxisOrderBeSwitchedForVisualization();
9264
0
                        if (swapTarget) {
9265
0
                            std::swap(minxDst, minyDst);
9266
0
                            std::swap(maxxDst, maxyDst);
9267
0
                        }
9268
0
                    }
9269
0
                    ctx->forceOver = alt.pj->over != 0;
9270
0
                    auto pjNormalized =
9271
0
                        pj_obj_create(ctx, co->normalizeForVisualization());
9272
0
                    ctx->forceOver = false;
9273
9274
0
                    pjNormalized->copyStateFrom(*(alt.pj));
9275
9276
0
                    pjNew->alternativeCoordinateOperations.emplace_back(
9277
0
                        alt.idxInOriginalList, minxSrc, minySrc, maxxSrc,
9278
0
                        maxySrc, minxDst, minyDst, maxxDst, maxyDst,
9279
0
                        pjNormalized, co->nameStr(), alt.accuracy,
9280
0
                        alt.pseudoArea, alt.areaName.c_str(),
9281
0
                        alt.pjSrcGeocentricToLonLat,
9282
0
                        alt.pjDstGeocentricToLonLat);
9283
0
                }
9284
0
            }
9285
0
            return pjNew.release();
9286
0
        } catch (const std::exception &e) {
9287
0
            ctx->forceOver = false;
9288
0
            proj_log_debug(ctx, __FUNCTION__, e.what());
9289
0
            return nullptr;
9290
0
        }
9291
0
    }
9292
9293
0
    auto crs = dynamic_cast<const CRS *>(obj->iso_obj.get());
9294
0
    if (crs) {
9295
0
        try {
9296
0
            return pj_obj_create(ctx, crs->normalizeForVisualization());
9297
0
        } catch (const std::exception &e) {
9298
0
            proj_log_debug(ctx, __FUNCTION__, e.what());
9299
0
            return nullptr;
9300
0
        }
9301
0
    }
9302
9303
0
    auto co = dynamic_cast<const CoordinateOperation *>(obj->iso_obj.get());
9304
0
    if (!co) {
9305
0
        proj_log_error(ctx, __FUNCTION__,
9306
0
                       "Object is not a CoordinateOperation "
9307
0
                       "created with "
9308
0
                       "proj_create_crs_to_crs");
9309
0
        return nullptr;
9310
0
    }
9311
0
    try {
9312
0
        ctx->forceOver = obj->over != 0;
9313
0
        auto pjNormalized = pj_obj_create(ctx, co->normalizeForVisualization());
9314
0
        pjNormalized->over = obj->over;
9315
0
        ctx->forceOver = false;
9316
0
        return pjNormalized;
9317
0
    } catch (const std::exception &e) {
9318
0
        ctx->forceOver = false;
9319
0
        proj_log_debug(ctx, __FUNCTION__, e.what());
9320
0
        return nullptr;
9321
0
    }
9322
0
}
9323
9324
// ---------------------------------------------------------------------------
9325
9326
/** \brief Returns a PJ* coordinate operation object which represents the
9327
 * inverse operation of the specified coordinate operation.
9328
 *
9329
 * @param ctx PROJ context, or NULL for default context
9330
 * @param obj Object of type CoordinateOperation (must not be NULL)
9331
 * @return a new PJ* object to free with proj_destroy() in case of success, or
9332
 * nullptr in case of error
9333
 * @since 6.3
9334
 */
9335
0
PJ *proj_coordoperation_create_inverse(PJ_CONTEXT *ctx, const PJ *obj) {
9336
9337
0
    SANITIZE_CTX(ctx);
9338
0
    if (!obj) {
9339
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9340
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9341
0
        return nullptr;
9342
0
    }
9343
0
    auto co = dynamic_cast<const CoordinateOperation *>(obj->iso_obj.get());
9344
0
    if (!co) {
9345
0
        proj_log_error(ctx, __FUNCTION__,
9346
0
                       "Object is not a CoordinateOperation");
9347
0
        return nullptr;
9348
0
    }
9349
0
    try {
9350
0
        return pj_obj_create(ctx, co->inverse());
9351
0
    } catch (const std::exception &e) {
9352
0
        proj_log_debug(ctx, __FUNCTION__, e.what());
9353
0
        return nullptr;
9354
0
    }
9355
0
}
9356
9357
// ---------------------------------------------------------------------------
9358
9359
/** \brief Returns the number of steps of a concatenated operation.
9360
 *
9361
 * The input object must be a concatenated operation.
9362
 *
9363
 * @param ctx PROJ context, or NULL for default context
9364
 * @param concatoperation Concatenated operation (must not be NULL)
9365
 * @return the number of steps, or 0 in case of error.
9366
 */
9367
int proj_concatoperation_get_step_count(PJ_CONTEXT *ctx,
9368
0
                                        const PJ *concatoperation) {
9369
0
    SANITIZE_CTX(ctx);
9370
0
    if (!concatoperation) {
9371
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9372
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9373
0
        return false;
9374
0
    }
9375
0
    auto l_co = dynamic_cast<const ConcatenatedOperation *>(
9376
0
        concatoperation->iso_obj.get());
9377
0
    if (!l_co) {
9378
0
        proj_log_error(ctx, __FUNCTION__,
9379
0
                       "Object is not a ConcatenatedOperation");
9380
0
        return false;
9381
0
    }
9382
0
    return static_cast<int>(l_co->operations().size());
9383
0
}
9384
// ---------------------------------------------------------------------------
9385
9386
/** \brief Returns a step of a concatenated operation.
9387
 *
9388
 * The input object must be a concatenated operation.
9389
 *
9390
 * The returned object must be unreferenced with proj_destroy() after
9391
 * use.
9392
 * It should be used by at most one thread at a time.
9393
 *
9394
 * @param ctx PROJ context, or NULL for default context
9395
 * @param concatoperation Concatenated operation (must not be NULL)
9396
 * @param i_step Index of the step to extract. Between 0 and
9397
 *               proj_concatoperation_get_step_count()-1
9398
 * @return Object that must be unreferenced with proj_destroy(), or NULL
9399
 * in case of error.
9400
 */
9401
PJ *proj_concatoperation_get_step(PJ_CONTEXT *ctx, const PJ *concatoperation,
9402
0
                                  int i_step) {
9403
0
    SANITIZE_CTX(ctx);
9404
0
    if (!concatoperation) {
9405
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9406
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9407
0
        return nullptr;
9408
0
    }
9409
0
    auto l_co = dynamic_cast<const ConcatenatedOperation *>(
9410
0
        concatoperation->iso_obj.get());
9411
0
    if (!l_co) {
9412
0
        proj_log_error(ctx, __FUNCTION__,
9413
0
                       "Object is not a ConcatenatedOperation");
9414
0
        return nullptr;
9415
0
    }
9416
0
    const auto &steps = l_co->operations();
9417
0
    if (i_step < 0 || static_cast<size_t>(i_step) >= steps.size()) {
9418
0
        proj_log_error(ctx, __FUNCTION__, "Invalid step index");
9419
0
        return nullptr;
9420
0
    }
9421
0
    return pj_obj_create(ctx, steps[i_step]);
9422
0
}
9423
// ---------------------------------------------------------------------------
9424
9425
/** \brief Opaque object representing an insertion session. */
9426
struct PJ_INSERT_SESSION {
9427
    //! @cond Doxygen_Suppress
9428
    PJ_CONTEXT *ctx = nullptr;
9429
    //!  @endcond
9430
};
9431
9432
// ---------------------------------------------------------------------------
9433
9434
/** \brief Starts a session for proj_get_insert_statements()
9435
 *
9436
 * Starts a new session for one or several calls to
9437
 * proj_get_insert_statements().
9438
 *
9439
 * An insertion session guarantees that the inserted objects will not create
9440
 * conflicting intermediate objects.
9441
 *
9442
 * The session must be stopped with proj_insert_object_session_destroy().
9443
 *
9444
 * Only one session may be active at a time for a given context.
9445
 *
9446
 * @param ctx PROJ context, or NULL for default context
9447
 * @return the session, or NULL in case of error.
9448
 *
9449
 * @since 8.1
9450
 */
9451
0
PJ_INSERT_SESSION *proj_insert_object_session_create(PJ_CONTEXT *ctx) {
9452
0
    SANITIZE_CTX(ctx);
9453
0
    try {
9454
0
        auto dbContext = getDBcontext(ctx);
9455
0
        dbContext->startInsertStatementsSession();
9456
0
        PJ_INSERT_SESSION *session = new PJ_INSERT_SESSION;
9457
0
        session->ctx = ctx;
9458
0
        return session;
9459
0
    } catch (const std::exception &e) {
9460
0
        proj_log_error(ctx, __FUNCTION__, e.what());
9461
0
        return nullptr;
9462
0
    }
9463
0
}
9464
9465
// ---------------------------------------------------------------------------
9466
9467
/** \brief Stops an insertion session started with
9468
 * proj_insert_object_session_create()
9469
 *
9470
 * @param ctx PROJ context, or NULL for default context
9471
 * @param session The insertion session.
9472
 * @since 8.1
9473
 */
9474
void proj_insert_object_session_destroy(PJ_CONTEXT *ctx,
9475
0
                                        PJ_INSERT_SESSION *session) {
9476
0
    SANITIZE_CTX(ctx);
9477
0
    if (session) {
9478
0
        try {
9479
0
            if (session->ctx != ctx) {
9480
0
                proj_log_error(ctx, __FUNCTION__,
9481
0
                               "proj_insert_object_session_destroy() called "
9482
0
                               "with a context different from the one of "
9483
0
                               "proj_insert_object_session_create()");
9484
0
            } else {
9485
0
                auto dbContext = getDBcontext(ctx);
9486
0
                dbContext->stopInsertStatementsSession();
9487
0
            }
9488
0
        } catch (const std::exception &e) {
9489
0
            proj_log_error(ctx, __FUNCTION__, e.what());
9490
0
        }
9491
0
        delete session;
9492
0
    }
9493
0
}
9494
9495
// ---------------------------------------------------------------------------
9496
9497
/** \brief Suggests a database code for the passed object.
9498
 *
9499
 * Supported type of objects are PrimeMeridian, Ellipsoid, Datum, DatumEnsemble,
9500
 * GeodeticCRS, ProjectedCRS, VerticalCRS, CompoundCRS, BoundCRS, Conversion.
9501
 *
9502
 * @param ctx PROJ context, or NULL for default context
9503
 * @param object Object for which to suggest a code.
9504
 * @param authority Authority name into which the object will be inserted.
9505
 * @param numeric_code Whether the code should be numeric, or derived from the
9506
 * object name.
9507
 * @param options NULL terminated list of options, or NULL.
9508
 *                No options are supported currently.
9509
 * @return the suggested code, that is guaranteed to not conflict with an
9510
 * existing one (to be freed with proj_string_destroy),
9511
 * or nullptr in case of error.
9512
 *
9513
 * @since 8.1
9514
 */
9515
char *proj_suggests_code_for(PJ_CONTEXT *ctx, const PJ *object,
9516
                             const char *authority, int numeric_code,
9517
0
                             const char *const *options) {
9518
0
    SANITIZE_CTX(ctx);
9519
0
    (void)options;
9520
9521
0
    if (!object || !authority) {
9522
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9523
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9524
0
        return nullptr;
9525
0
    }
9526
0
    auto identifiedObject =
9527
0
        std::dynamic_pointer_cast<IdentifiedObject>(object->iso_obj);
9528
0
    if (!identifiedObject) {
9529
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9530
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a IdentifiedObject");
9531
0
        return nullptr;
9532
0
    }
9533
9534
0
    try {
9535
0
        auto dbContext = getDBcontext(ctx);
9536
0
        return pj_strdup(dbContext
9537
0
                             ->suggestsCodeFor(NN_NO_CHECK(identifiedObject),
9538
0
                                               std::string(authority),
9539
0
                                               numeric_code != FALSE)
9540
0
                             .c_str());
9541
0
    } catch (const std::exception &e) {
9542
0
        proj_log_error(ctx, __FUNCTION__, e.what());
9543
0
    }
9544
0
    return nullptr;
9545
0
}
9546
9547
// ---------------------------------------------------------------------------
9548
9549
/** \brief Free a string.
9550
 *
9551
 * Only to be used with functions that document using this function.
9552
 *
9553
 * @param str String to free.
9554
 *
9555
 * @since 8.1
9556
 */
9557
0
void proj_string_destroy(char *str) { free(str); }
9558
9559
// ---------------------------------------------------------------------------
9560
9561
/** \brief Returns SQL statements needed to insert the passed object into the
9562
 * database.
9563
 *
9564
 * proj_insert_object_session_create() may have been called previously.
9565
 *
9566
 * It is strongly recommended that new objects should not be added in common
9567
 * registries, such as "EPSG", "ESRI", "IAU", etc. Users should use a custom
9568
 * authority name instead. If a new object should be
9569
 * added to the official EPSG registry, users are invited to follow the
9570
 * procedure explained at https://epsg.org/dataset-change-requests.html.
9571
 *
9572
 * Combined with proj_context_get_database_structure(), users can create
9573
 * auxiliary databases, instead of directly modifying the main proj.db database.
9574
 * Those auxiliary databases can be specified through
9575
 * proj_context_set_database_path() or the PROJ_AUX_DB environment variable.
9576
 *
9577
 * @param ctx PROJ context, or NULL for default context
9578
 * @param session The insertion session. May be NULL if a single object must be
9579
 *                inserted.
9580
 * @param object The object to insert into the database. Currently only
9581
 *               PrimeMeridian, Ellipsoid, Datum, GeodeticCRS, ProjectedCRS,
9582
 *               VerticalCRS, CompoundCRS or BoundCRS are supported.
9583
 * @param authority Authority name into which the object will be inserted.
9584
 *                  Must not be NULL.
9585
 * @param code Code with which the object will be inserted.Must not be NULL.
9586
 * @param numeric_codes Whether intermediate objects that can be created should
9587
 *                      use numeric codes (true), or may be alphanumeric (false)
9588
 * @param allowed_authorities NULL terminated list of authority names, or NULL.
9589
 *                            Authorities to which intermediate objects are
9590
 *                            allowed to refer to. "authority" will be
9591
 *                            implicitly added to it. Note that unit,
9592
 *                            coordinate systems, projection methods and
9593
 *                            parameters will in any case be allowed to refer
9594
 *                            to EPSG.
9595
 *                            If NULL, allowed_authorities defaults to
9596
 *                            {"EPSG", "PROJ", nullptr}
9597
 * @param options NULL terminated list of options, or NULL.
9598
 *                No options are supported currently.
9599
 *
9600
 * @return a list of insert statements (to be freed with
9601
 *         proj_string_list_destroy()), or NULL in case of error.
9602
 * @since 8.1
9603
 */
9604
PROJ_STRING_LIST proj_get_insert_statements(
9605
    PJ_CONTEXT *ctx, PJ_INSERT_SESSION *session, const PJ *object,
9606
    const char *authority, const char *code, int numeric_codes,
9607
0
    const char *const *allowed_authorities, const char *const *options) {
9608
0
    SANITIZE_CTX(ctx);
9609
0
    (void)options;
9610
9611
0
    struct TempSessionHolder {
9612
0
      private:
9613
0
        PJ_CONTEXT *m_ctx;
9614
0
        PJ_INSERT_SESSION *m_tempSession;
9615
0
        TempSessionHolder(const TempSessionHolder &) = delete;
9616
0
        TempSessionHolder &operator=(const TempSessionHolder &) = delete;
9617
9618
0
      public:
9619
0
        TempSessionHolder(PJ_CONTEXT *ctx, PJ_INSERT_SESSION *session)
9620
0
            : m_ctx(ctx),
9621
0
              m_tempSession(session ? nullptr
9622
0
                                    : proj_insert_object_session_create(ctx)) {}
9623
9624
0
        ~TempSessionHolder() {
9625
0
            if (m_tempSession) {
9626
0
                proj_insert_object_session_destroy(m_ctx, m_tempSession);
9627
0
            }
9628
0
        }
9629
9630
0
        inline PJ_INSERT_SESSION *GetTempSession() const {
9631
0
            return m_tempSession;
9632
0
        }
9633
0
    };
9634
9635
0
    try {
9636
0
        TempSessionHolder oHolder(ctx, session);
9637
0
        if (!session) {
9638
0
            session = oHolder.GetTempSession();
9639
0
            if (!session) {
9640
0
                return nullptr;
9641
0
            }
9642
0
        }
9643
9644
0
        if (!object || !authority || !code) {
9645
0
            proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9646
0
            proj_log_error(ctx, __FUNCTION__, "missing required input");
9647
0
            return nullptr;
9648
0
        }
9649
0
        auto identifiedObject =
9650
0
            std::dynamic_pointer_cast<IdentifiedObject>(object->iso_obj);
9651
0
        if (!identifiedObject) {
9652
0
            proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9653
0
            proj_log_error(ctx, __FUNCTION__,
9654
0
                           "Object is not a IdentifiedObject");
9655
0
            return nullptr;
9656
0
        }
9657
9658
0
        auto dbContext = getDBcontext(ctx);
9659
0
        std::vector<std::string> allowedAuthorities{"EPSG", "PROJ"};
9660
0
        if (allowed_authorities) {
9661
0
            allowedAuthorities.clear();
9662
0
            for (auto iter = allowed_authorities; *iter; ++iter) {
9663
0
                allowedAuthorities.emplace_back(*iter);
9664
0
            }
9665
0
        }
9666
0
        auto statements = dbContext->getInsertStatementsFor(
9667
0
            NN_NO_CHECK(identifiedObject), authority, code,
9668
0
            numeric_codes != FALSE, allowedAuthorities);
9669
0
        return to_string_list(std::move(statements));
9670
0
    } catch (const std::exception &e) {
9671
0
        proj_log_error(ctx, __FUNCTION__, e.what());
9672
0
    }
9673
0
    return nullptr;
9674
0
}
9675
9676
// ---------------------------------------------------------------------------
9677
9678
/** \brief Returns a list of geoid models available for that crs
9679
 *
9680
 * The list includes the geoid models connected directly with the crs,
9681
 * or via "Height Depth Reversal" or "Change of Vertical Unit" transformations.
9682
 * The returned list is NULL terminated and must be freed with
9683
 * proj_string_list_destroy().
9684
 *
9685
 * @param ctx Context, or NULL for default context.
9686
 * @param auth_name Authority name (must not be NULL)
9687
 * @param code Object code (must not be NULL)
9688
 * @param options should be set to NULL for now
9689
 * @return list of geoid models names (to be freed with
9690
 * proj_string_list_destroy()), or NULL in case of error.
9691
 * @since 8.1
9692
 */
9693
PROJ_STRING_LIST
9694
proj_get_geoid_models_from_database(PJ_CONTEXT *ctx, const char *auth_name,
9695
                                    const char *code,
9696
0
                                    const char *const *options) {
9697
0
    SANITIZE_CTX(ctx);
9698
0
    if (!auth_name || !code) {
9699
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9700
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9701
0
        return nullptr;
9702
0
    }
9703
0
    (void)options;
9704
0
    try {
9705
0
        const std::string codeStr(code);
9706
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx), auth_name);
9707
0
        auto geoidModels = factory->getGeoidModels(codeStr);
9708
0
        return to_string_list(std::move(geoidModels));
9709
0
    } catch (const std::exception &e) {
9710
0
        proj_log_error(ctx, __FUNCTION__, e.what());
9711
0
    }
9712
0
    return nullptr;
9713
0
}
9714
9715
// ---------------------------------------------------------------------------
9716
9717
/** \brief Instantiate a CoordinateMetadata object
9718
 *
9719
 * @since 9.4
9720
 */
9721
9722
PJ *proj_coordinate_metadata_create(PJ_CONTEXT *ctx, const PJ *crs,
9723
0
                                    double epoch) {
9724
0
    SANITIZE_CTX(ctx);
9725
0
    if (!crs) {
9726
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9727
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9728
0
        return nullptr;
9729
0
    }
9730
0
    auto crsCast = std::dynamic_pointer_cast<CRS>(crs->iso_obj);
9731
0
    if (!crsCast) {
9732
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CRS");
9733
0
        return nullptr;
9734
0
    }
9735
0
    try {
9736
0
        auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
9737
0
        return pj_obj_create(ctx, CoordinateMetadata::create(
9738
0
                                      NN_NO_CHECK(crsCast), epoch, dbContext));
9739
0
    } catch (const std::exception &e) {
9740
0
        proj_log_debug(ctx, __FUNCTION__, e.what());
9741
0
        return nullptr;
9742
0
    }
9743
0
}
9744
9745
// ---------------------------------------------------------------------------
9746
9747
/** \brief Return the coordinate epoch associated with a CoordinateMetadata.
9748
 *
9749
 * It may return a NaN value if there is no associated coordinate epoch.
9750
 *
9751
 * @since 9.2
9752
 */
9753
0
double proj_coordinate_metadata_get_epoch(PJ_CONTEXT *ctx, const PJ *obj) {
9754
0
    SANITIZE_CTX(ctx);
9755
0
    if (!obj) {
9756
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9757
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9758
0
        return std::numeric_limits<double>::quiet_NaN();
9759
0
    }
9760
0
    auto ptr = obj->iso_obj.get();
9761
0
    auto coordinateMetadata = dynamic_cast<const CoordinateMetadata *>(ptr);
9762
0
    if (coordinateMetadata) {
9763
0
        if (coordinateMetadata->coordinateEpoch().has_value()) {
9764
0
            return coordinateMetadata->coordinateEpochAsDecimalYear();
9765
0
        }
9766
0
        return std::numeric_limits<double>::quiet_NaN();
9767
0
    }
9768
0
    proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateMetadata");
9769
0
    return std::numeric_limits<double>::quiet_NaN();
9770
0
}
9771
9772
// ---------------------------------------------------------------------------
9773
9774
/** \brief Return whether a CRS has an associated PointMotionOperation
9775
 *
9776
 * @since 9.4
9777
 */
9778
0
int proj_crs_has_point_motion_operation(PJ_CONTEXT *ctx, const PJ *crs) {
9779
0
    SANITIZE_CTX(ctx);
9780
0
    if (!crs) {
9781
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9782
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9783
0
        return false;
9784
0
    }
9785
0
    auto l_crs = dynamic_cast<const CRS *>(crs->iso_obj.get());
9786
0
    if (!l_crs) {
9787
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CRS");
9788
0
        return false;
9789
0
    }
9790
0
    auto geodeticCRS = l_crs->extractGeodeticCRS();
9791
0
    if (!geodeticCRS)
9792
0
        return false;
9793
0
    try {
9794
0
        auto factory =
9795
0
            AuthorityFactory::create(getDBcontext(ctx), std::string());
9796
0
        return !factory
9797
0
                    ->getPointMotionOperationsFor(NN_NO_CHECK(geodeticCRS),
9798
0
                                                  false)
9799
0
                    .empty();
9800
0
    } catch (const std::exception &e) {
9801
0
        proj_log_error(ctx, __FUNCTION__, e.what());
9802
0
    }
9803
0
    return false;
9804
0
}
9805
9806
// ---------------------------------------------------------------------------
9807
9808
static UnitOfMeasure createScaleUnit(const char *name, double convFactor,
9809
                                     const char *unit_auth_name = nullptr,
9810
0
                                     const char *unit_code = nullptr) {
9811
0
    return name == nullptr
9812
0
               ? UnitOfMeasure::SCALE_UNITY
9813
0
               : UnitOfMeasure(name, convFactor, UnitOfMeasure::Type::SCALE,
9814
0
                               unit_auth_name ? unit_auth_name : "",
9815
0
                               unit_code ? unit_code : "");
9816
0
}
9817
9818
// ---------------------------------------------------------------------------
9819
9820
/** \brief Instantiate a conversion with method Affine Parametric, assuming
9821
 * it works in linear coordinate space
9822
 *
9823
 * This method is defined as
9824
 * <a href="https://epsg.org/coord-operation-method_9624/index.html">
9825
 * EPSG:9624</a>.
9826
 *
9827
 * Use this method for a CRS whose coordinate system has 2 axis.
9828
 *
9829
 * The returned object must be unreferenced with proj_destroy() after
9830
 * use.
9831
 * It should be used by at most one thread at a time.
9832
 *
9833
 * @param ctx PROJ context, or NULL for default context
9834
 * @param name Conversion name, or nullptr
9835
 * @param A0 translation term for output first axis
9836
 * @param A0_unit_name Name of the linear unit for A0. Or NULL for metre.
9837
 * @param A0_unit_conv_factor Conversion factor to metre for A0. Note: this is
9838
 * indicative only, and not taken into account in coordinate conversion
9839
 * @param A1 coefficient term for output first axis taking that is multiplied
9840
 * with the value along the source first axis
9841
 * @param A1_unit_name Name of the scale unit for A1. Or NULL for unity.
9842
 * @param A1_unit_conv_factor Conversion factor to unity for A1. Note: this is
9843
 * indicative only, and not taken into account in coordinate conversion
9844
 * @param A2 coefficient term for output first axis taking that is multiplied
9845
 * with the value along the source second axis
9846
 * @param A2_unit_name Name of the scale unit for A2. Or NULL for unity.
9847
 * @param A2_unit_conv_factor Conversion factor to unity for A2. Note: this is
9848
 * indicative only, and not taken into account in coordinate conversion
9849
 * @param B0 translation term for output second axis
9850
 * @param B0_unit_name Name of the linear unit for B0. Or NULL for metre.
9851
 * @param B0_unit_conv_factor Conversion factor to metre for B0. Note: this is
9852
 * indicative only, and not taken into account in coordinate conversion
9853
 * @param B1 coefficient term for output second axis taking that is multiplied
9854
 * with the value along the source first axis
9855
 * @param B1_unit_name Name of the scale unit for B1. Or NULL for unity.
9856
 * @param B1_unit_conv_factor Conversion factor to unity for B1. Note: this is
9857
 * indicative only, and not taken into account in coordinate conversion
9858
 * @param B2 coefficient term for output second axis taking that is multiplied
9859
 * with the value along the source second axis
9860
 * @param B2_unit_name Name of the scale unit for B2. Or NULL for unity.
9861
 * @param B2_unit_conv_factor Conversion factor to unity for B2. Note: this is
9862
 * indicative only, and not taken into account in coordinate conversion
9863
 * @return Object that must be unreferenced with proj_destroy(), or NULL
9864
 * in case of error.
9865
 *
9866
 * @since 9.8
9867
 */
9868
/* clang-format off */
9869
PJ PROJ_DLL *proj_create_linear_affine_parametric_conversion(
9870
    PJ_CONTEXT *ctx, const char* name,
9871
    double A0, const char *A0_unit_name, double A0_unit_conv_factor,
9872
    double A1, const char *A1_unit_name, double A1_unit_conv_factor,
9873
    double A2, const char *A2_unit_name, double A2_unit_conv_factor,
9874
    double B0, const char *B0_unit_name, double B0_unit_conv_factor,
9875
    double B1, const char *B1_unit_name, double B1_unit_conv_factor,
9876
    double B2, const char *B2_unit_name, double B2_unit_conv_factor)
9877
/* clang-format on */
9878
0
{
9879
0
    SANITIZE_CTX(ctx);
9880
0
    try {
9881
0
        auto conv = Conversion::createAffineParametric(
9882
0
            createPropertyMapName(name),
9883
0
            Length(A0, createLinearUnit(A0_unit_name, A0_unit_conv_factor)),
9884
0
            Scale(A1, createScaleUnit(A1_unit_name, A1_unit_conv_factor)),
9885
0
            Scale(A2, createScaleUnit(A2_unit_name, A2_unit_conv_factor)),
9886
0
            Length(B0, createLinearUnit(B0_unit_name, B0_unit_conv_factor)),
9887
0
            Scale(B1, createScaleUnit(B1_unit_name, B1_unit_conv_factor)),
9888
0
            Scale(B2, createScaleUnit(B2_unit_name, B2_unit_conv_factor)));
9889
0
        return pj_obj_create(ctx, conv);
9890
0
    } catch (const std::exception &e) {
9891
0
        proj_log_error(ctx, __FUNCTION__, e.what());
9892
0
    }
9893
0
    return nullptr;
9894
0
}
9895
9896
// ---------------------------------------------------------------------------
9897
9898
/** \brief Instantiate a conversion with method 3D Affine Parametric, assuming
9899
 * it works in linear coordinate space
9900
 *
9901
 * Use this method for a CRS whose coordinate system has 3 axis.
9902
 *
9903
 * The returned object must be unreferenced with proj_destroy() after
9904
 * use.
9905
 * It should be used by at most one thread at a time.
9906
 *
9907
 * @param ctx PROJ context, or NULL for default context
9908
 * @param name Conversion name, or nullptr
9909
 * @param A0 translation term for output first axis
9910
 * @param A0_unit_name Name of the linear unit for A0. Or NULL for metre.
9911
 * @param A0_unit_conv_factor Conversion factor to metre for A0. Note: this is
9912
 * indicative only, and not taken into account in coordinate conversion
9913
 * @param A1 coefficient term for output first axis taking that is multiplied
9914
 * with the value along the source first axis
9915
 * @param A1_unit_name Name of the scale unit for A1. Or NULL for unity.
9916
 * @param A1_unit_conv_factor Conversion factor to unity for A1. Note: this is
9917
 * indicative only, and not taken into account in coordinate conversion
9918
 * @param A2 coefficient term for output first axis taking that is multiplied
9919
 * with the value along the source second axis
9920
 * @param A2_unit_name Name of the scale unit for A2. Or NULL for unity.
9921
 * @param A2_unit_conv_factor Conversion factor to unity for A2. Note: this is
9922
 * indicative only, and not taken into account in coordinate conversion
9923
 * @param A3 coefficient term for output first axis taking that is multiplied
9924
 * with the value along the source third axis
9925
 * @param A3_unit_name Name of the scale unit for A3. Or NULL for unity.
9926
 * @param A3_unit_conv_factor Conversion factor to unity for A3. Note: this is
9927
 * indicative only, and not taken into account in coordinate conversion
9928
 * @param B0 translation term for output second axis
9929
 * @param B0_unit_name Name of the linear unit for B0. Or NULL for metre.
9930
 * @param B0_unit_conv_factor Conversion factor to metre for B0. Note: this is
9931
 * indicative only, and not taken into account in coordinate conversion
9932
 * @param B1 coefficient term for output second axis taking that is multiplied
9933
 * with the value along the source first axis
9934
 * @param B1_unit_name Name of the scale unit for B1. Or NULL for unity.
9935
 * @param B1_unit_conv_factor Conversion factor to unity for B1. Note: this is
9936
 * indicative only, and not taken into account in coordinate conversion
9937
 * @param B2 coefficient term for output second axis taking that is multiplied
9938
 * with the value along the source second axis
9939
 * @param B2_unit_name Name of the scale unit for B2. Or NULL for unity.
9940
 * @param B2_unit_conv_factor Conversion factor to unity for B2. Note: this is
9941
 * indicative only, and not taken into account in coordinate conversion
9942
 * @param B3 coefficient term for output second axis taking that is multiplied
9943
 * with the value along the source third axis
9944
 * @param B3_unit_name Name of the scale unit for B3. Or NULL for unity.
9945
 * @param B3_unit_conv_factor Conversion factor to unity for B3. Note: this is
9946
 * indicative only, and not taken into account in coordinate conversion
9947
 * @param C0 translation term for output third axis
9948
 * @param C0_unit_name Name of the linear unit for C0. Or NULL for metre.
9949
 * @param C0_unit_conv_factor Conversion factor to metre for C0. Note: this is
9950
 * indicative only, and not taken into account in coordinate conversion
9951
 * @param C1 coefficient term for output third axis taking that is multiplied
9952
 * with the value along the source first axis
9953
 * @param C1_unit_name Name of the scale unit for C1. Or NULL for unity.
9954
 * @param C1_unit_conv_factor Conversion factor to unity for C1. Note: this is
9955
 * indicative only, and not taken into account in coordinate conversion
9956
 * @param C2 coefficient term for output third axis taking that is multiplied
9957
 * with the value along the source second axis
9958
 * @param C2_unit_name Name of the scale unit for C2. Or NULL for unity.
9959
 * @param C2_unit_conv_factor Conversion factor to unity for C2. Note: this is
9960
 * indicative only, and not taken into account in coordinate conversion
9961
 * @param C3 coefficient term for output third axis taking that is multiplied
9962
 * with the value along the source third axis
9963
 * @param C3_unit_name Name of the scale unit for C3. Or NULL for unity.
9964
 * @param C3_unit_conv_factor Conversion factor to unity for C3. Note: this is
9965
 * indicative only, and not taken into account in coordinate conversion
9966
 * @return Object that must be unreferenced with proj_destroy(), or NULL
9967
 * in case of error.
9968
 *
9969
 * @since 9.9
9970
 */
9971
/* clang-format off */
9972
PJ PROJ_DLL *proj_create_linear_3D_affine_parametric_conversion(
9973
    PJ_CONTEXT *ctx, const char* name,
9974
    double A0, const char *A0_unit_name, double A0_unit_conv_factor,
9975
    double A1, const char *A1_unit_name, double A1_unit_conv_factor,
9976
    double A2, const char *A2_unit_name, double A2_unit_conv_factor,
9977
    double A3, const char *A3_unit_name, double A3_unit_conv_factor,
9978
    double B0, const char *B0_unit_name, double B0_unit_conv_factor,
9979
    double B1, const char *B1_unit_name, double B1_unit_conv_factor,
9980
    double B2, const char *B2_unit_name, double B2_unit_conv_factor,
9981
    double B3, const char *B3_unit_name, double B3_unit_conv_factor,
9982
    double C0, const char *C0_unit_name, double C0_unit_conv_factor,
9983
    double C1, const char *C1_unit_name, double C1_unit_conv_factor,
9984
    double C2, const char *C2_unit_name, double C2_unit_conv_factor,
9985
    double C3, const char *C3_unit_name, double C3_unit_conv_factor)
9986
/* clang-format on */
9987
0
{
9988
0
    SANITIZE_CTX(ctx);
9989
0
    try {
9990
0
        auto conv = Conversion::createAffineParametric(
9991
0
            createPropertyMapName(name),
9992
0
            Length(A0, createLinearUnit(A0_unit_name, A0_unit_conv_factor)),
9993
0
            Scale(A1, createScaleUnit(A1_unit_name, A1_unit_conv_factor)),
9994
0
            Scale(A2, createScaleUnit(A2_unit_name, A2_unit_conv_factor)),
9995
0
            Scale(A3, createScaleUnit(A3_unit_name, A3_unit_conv_factor)),
9996
0
            Length(B0, createLinearUnit(B0_unit_name, B0_unit_conv_factor)),
9997
0
            Scale(B1, createScaleUnit(B1_unit_name, B1_unit_conv_factor)),
9998
0
            Scale(B2, createScaleUnit(B2_unit_name, B2_unit_conv_factor)),
9999
0
            Scale(B3, createScaleUnit(B3_unit_name, B3_unit_conv_factor)),
10000
0
            Length(C0, createLinearUnit(C0_unit_name, C0_unit_conv_factor)),
10001
0
            Scale(C1, createScaleUnit(C1_unit_name, C1_unit_conv_factor)),
10002
0
            Scale(C2, createScaleUnit(C2_unit_name, C2_unit_conv_factor)),
10003
0
            Scale(C3, createScaleUnit(C3_unit_name, C3_unit_conv_factor)));
10004
0
        return pj_obj_create(ctx, conv);
10005
0
    } catch (const std::exception &e) {
10006
0
        proj_log_error(ctx, __FUNCTION__, e.what());
10007
0
    }
10008
0
    return nullptr;
10009
0
}
10010
10011
// ---------------------------------------------------------------------------
10012
10013
/** \brief Instantiate a DerivedProjectedCRS
10014
 *
10015
 * The returned object must be unreferenced with proj_destroy() after
10016
 * use.
10017
 * It should be used by at most one thread at a time.
10018
 *
10019
 * @param ctx PROJ context, or NULL for default context
10020
 * @param crs_name CRS name. Or NULL
10021
 * @param base_proj_crs Base ProjectedCRS. Must not be NULL.
10022
 * @param deriving_conversion Conversion. Must not be NULL.
10023
 * @param coordinate_system Cartesian coordinate system. Must not be NULL.
10024
 *
10025
 * @return Object that must be unreferenced with
10026
 * proj_destroy(), or NULL in case of error.
10027
 *
10028
 * @since 9.8
10029
 */
10030
10031
PJ *proj_create_derived_projected_crs(PJ_CONTEXT *ctx, const char *crs_name,
10032
                                      const PJ *base_proj_crs,
10033
                                      const PJ *deriving_conversion,
10034
0
                                      const PJ *coordinate_system) {
10035
0
    SANITIZE_CTX(ctx);
10036
0
    if (!base_proj_crs || !deriving_conversion || !coordinate_system) {
10037
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
10038
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
10039
0
        return nullptr;
10040
0
    }
10041
0
    auto baseProjCRS =
10042
0
        std::dynamic_pointer_cast<ProjectedCRS>(base_proj_crs->iso_obj);
10043
0
    if (!baseProjCRS) {
10044
0
        return nullptr;
10045
0
    }
10046
0
    auto conv =
10047
0
        std::dynamic_pointer_cast<Conversion>(deriving_conversion->iso_obj);
10048
0
    if (!conv) {
10049
0
        return nullptr;
10050
0
    }
10051
0
    auto cs =
10052
0
        std::dynamic_pointer_cast<CartesianCS>(coordinate_system->iso_obj);
10053
0
    if (!cs) {
10054
0
        return nullptr;
10055
0
    }
10056
0
    try {
10057
0
        return pj_obj_create(ctx, DerivedProjectedCRS::create(
10058
0
                                      createPropertyMapName(crs_name),
10059
0
                                      NN_NO_CHECK(baseProjCRS),
10060
0
                                      NN_NO_CHECK(conv), NN_NO_CHECK(cs)));
10061
0
    } catch (const std::exception &e) {
10062
0
        proj_log_error(ctx, __FUNCTION__, e.what());
10063
0
    }
10064
0
    return nullptr;
10065
0
}
10066
10067
// ---------------------------------------------------------------------------
10068
10069
/** \brief Add a deriving conversion to a horizontal CRS
10070
 *
10071
 * This is for now restricting when the horizontal CRS is a ProjectedCRS,
10072
 * a CompoundCRS with a ProjectedCRS, or a BoundCRS of a ProjectedCRS.
10073
 *
10074
 * The returned object must be unreferenced with proj_destroy() after
10075
 * use.
10076
 * It should be used by at most one thread at a time.
10077
 *
10078
 * @param ctx PROJ context, or NULL for default context
10079
 * @param crs_name Name of the derived projected CRS. Or NULL
10080
 * @param base_crs ProjectedCRS, CompoundCRS with a ProjectedCRS, or
10081
 * BoundCRS of a ProjectedCRS. Must not be NULL.
10082
 * @param deriving_conversion Conversion. Must not be NULL.
10083
 * @param coordinate_system Cartesian coordinate system. Must not be NULL.
10084
 *
10085
 * @return Object that must be unreferenced with
10086
 * proj_destroy(), or NULL in case of error.
10087
 *
10088
 * @since 9.8
10089
 */
10090
PJ *proj_crs_add_horizontal_derived_conversion(PJ_CONTEXT *ctx,
10091
                                               const char *crs_name,
10092
                                               const PJ *base_crs,
10093
                                               const PJ *deriving_conversion,
10094
0
                                               const PJ *coordinate_system) {
10095
0
    SANITIZE_CTX(ctx);
10096
0
    if (!base_crs || !deriving_conversion || !coordinate_system) {
10097
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
10098
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
10099
0
        return nullptr;
10100
0
    }
10101
10102
0
    CRSPtr baseCRS = std::dynamic_pointer_cast<CRS>(base_crs->iso_obj);
10103
0
    if (!baseCRS)
10104
0
        return nullptr;
10105
0
    auto conv =
10106
0
        std::dynamic_pointer_cast<Conversion>(deriving_conversion->iso_obj);
10107
0
    if (!conv) {
10108
0
        return nullptr;
10109
0
    }
10110
0
    auto cs =
10111
0
        std::dynamic_pointer_cast<CartesianCS>(coordinate_system->iso_obj);
10112
0
    if (!cs) {
10113
0
        return nullptr;
10114
0
    }
10115
10116
0
    auto compoundCRS = std::dynamic_pointer_cast<CompoundCRS>(baseCRS);
10117
0
    CRSPtr verticalCRS;
10118
0
    if (compoundCRS) {
10119
0
        const auto &components = compoundCRS->componentReferenceSystems();
10120
0
        if (components.size() == 2) {
10121
0
            baseCRS = components[0];
10122
0
            verticalCRS = components[1];
10123
0
        }
10124
0
    }
10125
10126
0
    auto boundCRS = std::dynamic_pointer_cast<BoundCRS>(baseCRS);
10127
0
    if (boundCRS) {
10128
0
        baseCRS = boundCRS->baseCRS();
10129
0
    }
10130
10131
0
    auto baseProjCRS = std::dynamic_pointer_cast<ProjectedCRS>(baseCRS);
10132
0
    if (!baseProjCRS) {
10133
0
        return nullptr;
10134
0
    }
10135
10136
0
    try {
10137
0
        CRSNNPtr retCRS = DerivedProjectedCRS::create(
10138
0
            createPropertyMapName(crs_name), NN_NO_CHECK(baseProjCRS),
10139
0
            NN_NO_CHECK(conv), NN_NO_CHECK(cs));
10140
10141
0
        if (boundCRS) {
10142
0
            retCRS = BoundCRS::create(retCRS, boundCRS->hubCRS(),
10143
0
                                      boundCRS->transformation());
10144
0
        }
10145
0
        if (verticalCRS) {
10146
0
            retCRS = CompoundCRS::create(
10147
0
                createPropertyMapName(
10148
0
                    (retCRS->nameStr() + " + " + verticalCRS->nameStr())
10149
0
                        .c_str()),
10150
0
                {retCRS, NN_NO_CHECK(verticalCRS)});
10151
0
        }
10152
0
        return pj_obj_create(ctx, retCRS);
10153
0
    } catch (const std::exception &e) {
10154
0
        proj_log_error(ctx, __FUNCTION__, e.what());
10155
0
    }
10156
0
    return nullptr;
10157
0
}