Coverage Report

Created: 2026-11-12 07:32

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
6.37k
                                          const char *text) {
83
6.37k
    if (ctx->debug_level != PJ_LOG_NONE) {
84
6.37k
        std::string msg(function);
85
6.37k
        msg += ": ";
86
6.37k
        msg += text;
87
6.37k
        ctx->logger(ctx->logger_app_data, PJ_LOG_ERROR, msg.c_str());
88
6.37k
    }
89
6.37k
    auto previous_errno = proj_context_errno(ctx);
90
6.37k
    if (previous_errno == 0) {
91
        // only set errno if it wasn't set deeper down the call stack
92
569
        proj_context_errno_set(ctx, PROJ_ERR_OTHER);
93
569
    }
94
6.37k
}
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
52.1k
void proj_context_delete_cpp_context(struct projCppContext *cppContext) {
135
52.1k
    delete cppContext;
136
52.1k
}
137
// ---------------------------------------------------------------------------
138
139
projCppContext::projCppContext(PJ_CONTEXT *ctx, const char *dbPath,
140
                               const std::vector<std::string> &auxDbPaths)
141
52.1k
    : ctx_(ctx), dbPath_(dbPath ? dbPath : std::string()),
142
52.1k
      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
52.1k
projCppContext *projCppContext::clone(PJ_CONTEXT *ctx) const {
158
52.1k
    projCppContext *newContext =
159
52.1k
        new projCppContext(ctx, getDbPath().c_str(), getAuxDbPaths());
160
52.1k
    return newContext;
161
52.1k
}
162
163
// ---------------------------------------------------------------------------
164
165
82.2k
NS_PROJ::io::DatabaseContextNNPtr projCppContext::getDatabaseContext() {
166
82.2k
    if (databaseContext_) {
167
72.9k
        return NN_NO_CHECK(databaseContext_);
168
72.9k
    }
169
9.26k
    auto dbContext =
170
9.26k
        NS_PROJ::io::DatabaseContext::create(dbPath_, auxDbPaths_, ctx_);
171
9.26k
    databaseContext_ = dbContext;
172
9.26k
    return dbContext;
173
82.2k
}
174
175
// ---------------------------------------------------------------------------
176
177
21.3k
static PROJ_NO_INLINE DatabaseContextNNPtr getDBcontext(PJ_CONTEXT *ctx) {
178
21.3k
    return ctx->get_cpp_context()->getDatabaseContext();
179
21.3k
}
180
181
// ---------------------------------------------------------------------------
182
183
static PROJ_NO_INLINE DatabaseContextPtr
184
21.3k
getDBcontextNoException(PJ_CONTEXT *ctx, const char *function) {
185
21.3k
    try {
186
21.3k
        return getDBcontext(ctx).as_nullable();
187
21.3k
    } catch (const std::exception &e) {
188
0
        proj_log_debug(ctx, function, e.what());
189
0
        return nullptr;
190
0
    }
191
21.3k
}
192
// ---------------------------------------------------------------------------
193
194
106k
PJ *pj_obj_create(PJ_CONTEXT *ctx, const BaseObjectNNPtr &objIn) {
195
106k
    auto coordop = dynamic_cast<const CoordinateOperation *>(objIn.get());
196
106k
    if (coordop) {
197
33.9k
        auto singleOp = dynamic_cast<const SingleOperation *>(coordop);
198
33.9k
        bool bTryToExportToProj = true;
199
33.9k
        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
1
            bTryToExportToProj = false;
205
1
        }
206
33.9k
        if (bTryToExportToProj) {
207
33.9k
            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
33.9k
                auto dbContext =
216
33.9k
                    ctx->cpp_context
217
33.9k
                        ? ctx->get_cpp_context()->getDatabaseContextIfOpen()
218
33.9k
                        : nullptr;
219
33.9k
                auto formatter = PROJStringFormatter::create(
220
33.9k
                    PROJStringFormatter::Convention::PROJ_5,
221
33.9k
                    std::move(dbContext));
222
33.9k
                auto projString = coordop->exportToPROJString(formatter.get());
223
33.9k
                const bool defer_grid_opening_backup = ctx->defer_grid_opening;
224
33.9k
                if (!defer_grid_opening_backup &&
225
33.5k
                    proj_context_is_network_enabled(ctx)) {
226
0
                    ctx->defer_grid_opening = true;
227
0
                }
228
33.9k
                auto pj = pj_create_internal(ctx, projString.c_str());
229
33.9k
                ctx->defer_grid_opening = defer_grid_opening_backup;
230
33.9k
                if (pj) {
231
14.7k
                    pj->iso_obj = objIn;
232
14.7k
                    pj->iso_obj_is_coordinate_operation = true;
233
14.7k
                    auto sourceEpoch = coordop->sourceCoordinateEpoch();
234
14.7k
                    auto targetEpoch = coordop->targetCoordinateEpoch();
235
14.7k
                    if (sourceEpoch.has_value()) {
236
52
                        if (!targetEpoch.has_value()) {
237
51
                            pj->hasCoordinateEpoch = true;
238
51
                            pj->coordinateEpoch =
239
51
                                sourceEpoch->coordinateEpoch().convertToUnit(
240
51
                                    common::UnitOfMeasure::YEAR);
241
51
                        }
242
14.7k
                    } else {
243
14.7k
                        if (targetEpoch.has_value()) {
244
8
                            pj->hasCoordinateEpoch = true;
245
8
                            pj->coordinateEpoch =
246
8
                                targetEpoch->coordinateEpoch().convertToUnit(
247
8
                                    common::UnitOfMeasure::YEAR);
248
8
                        }
249
14.7k
                    }
250
14.7k
                    return pj;
251
14.7k
                }
252
33.9k
            } catch (const std::exception &) {
253
                // Silence, since we may not always be able to export as a
254
                // PROJ string.
255
404
            }
256
33.9k
        }
257
33.9k
    }
258
91.8k
    auto pj = pj_new();
259
91.8k
    if (pj) {
260
91.8k
        pj->ctx = ctx;
261
91.8k
        pj->descr = "ISO-19111 object";
262
91.8k
        pj->iso_obj = objIn;
263
91.8k
        pj->iso_obj_is_coordinate_operation = coordop != nullptr;
264
91.8k
        try {
265
91.8k
            auto crs = dynamic_cast<const CRS *>(objIn.get());
266
91.8k
            if (crs) {
267
58.0k
                auto geodCRS = crs->extractGeodeticCRS();
268
58.0k
                if (geodCRS) {
269
57.6k
                    const auto &ellps = geodCRS->ellipsoid();
270
57.6k
                    const double a = ellps->semiMajorAxis().getSIValue();
271
57.6k
                    const double es = ellps->squaredEccentricity();
272
57.6k
                    if (!(a > 0 && es >= 0 && es < 1)) {
273
20
                        proj_log_error(pj, _("Invalid ellipsoid parameters"));
274
20
                        proj_errno_set(pj,
275
20
                                       PROJ_ERR_INVALID_OP_ILLEGAL_ARG_VALUE);
276
20
                        proj_destroy(pj);
277
20
                        return nullptr;
278
20
                    }
279
57.6k
                    pj_calc_ellipsoid_params(pj, a, es);
280
57.6k
                    assert(pj->geod == nullptr);
281
57.6k
                    pj->geod = static_cast<struct geod_geodesic *>(
282
57.6k
                        calloc(1, sizeof(struct geod_geodesic)));
283
57.6k
                    if (pj->geod) {
284
57.6k
                        geod_init(pj->geod, pj->a,
285
57.6k
                                  pj->es / (1 + sqrt(pj->one_es)));
286
57.6k
                    }
287
57.6k
                }
288
58.0k
            }
289
91.8k
        } catch (const std::exception &) {
290
0
        }
291
91.8k
    }
292
91.8k
    return pj;
293
91.8k
}
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
6.89k
        : 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
6.89k
PJ_OBJ_LIST::~PJ_OBJ_LIST() = default;
314
//! @endcond
315
316
// ---------------------------------------------------------------------------
317
318
//! @cond Doxygen_Suppress
319
320
#define SANITIZE_CTX(ctx)                                                      \
321
174k
    do {                                                                       \
322
174k
        if (ctx == nullptr) {                                                  \
323
0
            ctx = pj_get_default_ctx();                                        \
324
0
        }                                                                      \
325
174k
    } 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
17.0k
PJ *proj_clone(PJ_CONTEXT *ctx, const PJ *obj) {
557
17.0k
    SANITIZE_CTX(ctx);
558
17.0k
    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
17.0k
    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
17.0k
    try {
585
17.0k
        ctx->forceOver = obj->over != 0;
586
17.0k
        PJ *newPj = pj_obj_create(ctx, NN_NO_CHECK(obj->iso_obj));
587
17.0k
        ctx->forceOver = false;
588
17.0k
        if (newPj) {
589
17.0k
            newPj->copyStateFrom(*obj);
590
17.0k
        }
591
17.0k
        return newPj;
592
17.0k
    } catch (const std::exception &e) {
593
0
        proj_log_error(ctx, __FUNCTION__, e.what());
594
0
    }
595
0
    return nullptr;
596
17.0k
}
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
18.7k
PJ *proj_create(PJ_CONTEXT *ctx, const char *text) {
617
18.7k
    SANITIZE_CTX(ctx);
618
18.7k
    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
18.7k
    if (strstr(text, "proj=") == nullptr || strstr(text, "init=") != nullptr) {
626
6.57k
        getDBcontextNoException(ctx, __FUNCTION__);
627
6.57k
    }
628
18.7k
    try {
629
18.7k
        auto obj =
630
18.7k
            nn_dynamic_pointer_cast<BaseObject>(createFromUserInput(text, ctx));
631
18.7k
        if (obj) {
632
12.9k
            return pj_obj_create(ctx, NN_NO_CHECK(obj));
633
12.9k
        }
634
18.7k
    } catch (const io::ParsingException &e) {
635
5.54k
        if (proj_context_errno(ctx) == 0) {
636
1.35k
            proj_context_errno_set(ctx, PROJ_ERR_INVALID_OP_WRONG_SYNTAX);
637
1.35k
        }
638
5.54k
        proj_log_error(ctx, __FUNCTION__, e.what());
639
5.54k
    } catch (const NoSuchAuthorityCodeException &e) {
640
70
        proj_log_error(ctx, __FUNCTION__,
641
70
                       std::string(e.what())
642
70
                           .append(": ")
643
70
                           .append(e.getAuthority())
644
70
                           .append(":")
645
70
                           .append(e.getAuthorityCode())
646
70
                           .c_str());
647
125
    } catch (const std::exception &e) {
648
125
        proj_log_error(ctx, __FUNCTION__, e.what());
649
125
    }
650
5.74k
    return nullptr;
651
18.7k
}
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
44.5k
PJ_TYPE proj_get_type(const PJ *obj) {
1240
44.5k
    if (!obj || !obj->iso_obj) {
1241
0
        return PJ_TYPE_UNKNOWN;
1242
0
    }
1243
44.5k
    if (obj->type != PJ_TYPE_UNKNOWN)
1244
4.91k
        return obj->type;
1245
1246
39.6k
    const auto getType = [&obj]() {
1247
39.6k
        auto ptr = obj->iso_obj.get();
1248
39.6k
        if (dynamic_cast<Ellipsoid *>(ptr)) {
1249
0
            return PJ_TYPE_ELLIPSOID;
1250
0
        }
1251
1252
39.6k
        if (dynamic_cast<PrimeMeridian *>(ptr)) {
1253
0
            return PJ_TYPE_PRIME_MERIDIAN;
1254
0
        }
1255
1256
39.6k
        if (dynamic_cast<DynamicGeodeticReferenceFrame *>(ptr)) {
1257
0
            return PJ_TYPE_DYNAMIC_GEODETIC_REFERENCE_FRAME;
1258
0
        }
1259
39.6k
        if (dynamic_cast<GeodeticReferenceFrame *>(ptr)) {
1260
0
            return PJ_TYPE_GEODETIC_REFERENCE_FRAME;
1261
0
        }
1262
39.6k
        if (dynamic_cast<DynamicVerticalReferenceFrame *>(ptr)) {
1263
0
            return PJ_TYPE_DYNAMIC_VERTICAL_REFERENCE_FRAME;
1264
0
        }
1265
39.6k
        if (dynamic_cast<VerticalReferenceFrame *>(ptr)) {
1266
0
            return PJ_TYPE_VERTICAL_REFERENCE_FRAME;
1267
0
        }
1268
39.6k
        if (dynamic_cast<DatumEnsemble *>(ptr)) {
1269
0
            return PJ_TYPE_DATUM_ENSEMBLE;
1270
0
        }
1271
39.6k
        if (dynamic_cast<TemporalDatum *>(ptr)) {
1272
0
            return PJ_TYPE_TEMPORAL_DATUM;
1273
0
        }
1274
39.6k
        if (dynamic_cast<EngineeringDatum *>(ptr)) {
1275
0
            return PJ_TYPE_ENGINEERING_DATUM;
1276
0
        }
1277
39.6k
        if (dynamic_cast<ParametricDatum *>(ptr)) {
1278
0
            return PJ_TYPE_PARAMETRIC_DATUM;
1279
0
        }
1280
1281
39.6k
        if (auto crs = dynamic_cast<GeographicCRS *>(ptr)) {
1282
12.5k
            if (crs->coordinateSystem()->axisList().size() == 2) {
1283
12.0k
                return PJ_TYPE_GEOGRAPHIC_2D_CRS;
1284
12.0k
            } else {
1285
408
                return PJ_TYPE_GEOGRAPHIC_3D_CRS;
1286
408
            }
1287
12.5k
        }
1288
1289
27.1k
        if (auto crs = dynamic_cast<GeodeticCRS *>(ptr)) {
1290
4.26k
            if (crs->isGeocentric()) {
1291
4.22k
                return PJ_TYPE_GEOCENTRIC_CRS;
1292
4.22k
            } else {
1293
47
                return PJ_TYPE_GEODETIC_CRS;
1294
47
            }
1295
4.26k
        }
1296
1297
22.8k
        if (dynamic_cast<VerticalCRS *>(ptr)) {
1298
84
            return PJ_TYPE_VERTICAL_CRS;
1299
84
        }
1300
22.8k
        if (dynamic_cast<ProjectedCRS *>(ptr)) {
1301
9.09k
            return PJ_TYPE_PROJECTED_CRS;
1302
9.09k
        }
1303
13.7k
        if (dynamic_cast<DerivedProjectedCRS *>(ptr)) {
1304
0
            return PJ_TYPE_DERIVED_PROJECTED_CRS;
1305
0
        }
1306
13.7k
        if (dynamic_cast<CompoundCRS *>(ptr)) {
1307
12.1k
            return PJ_TYPE_COMPOUND_CRS;
1308
12.1k
        }
1309
1.51k
        if (dynamic_cast<TemporalCRS *>(ptr)) {
1310
0
            return PJ_TYPE_TEMPORAL_CRS;
1311
0
        }
1312
1.51k
        if (dynamic_cast<EngineeringCRS *>(ptr)) {
1313
8
            return PJ_TYPE_ENGINEERING_CRS;
1314
8
        }
1315
1.50k
        if (dynamic_cast<BoundCRS *>(ptr)) {
1316
1.46k
            return PJ_TYPE_BOUND_CRS;
1317
1.46k
        }
1318
44
        if (dynamic_cast<CRS *>(ptr)) {
1319
0
            return PJ_TYPE_OTHER_CRS;
1320
0
        }
1321
1322
44
        if (dynamic_cast<Conversion *>(ptr)) {
1323
0
            return PJ_TYPE_CONVERSION;
1324
0
        }
1325
44
        if (dynamic_cast<Transformation *>(ptr)) {
1326
0
            return PJ_TYPE_TRANSFORMATION;
1327
0
        }
1328
44
        if (dynamic_cast<ConcatenatedOperation *>(ptr)) {
1329
0
            return PJ_TYPE_CONCATENATED_OPERATION;
1330
0
        }
1331
44
        if (dynamic_cast<CoordinateOperation *>(ptr)) {
1332
0
            return PJ_TYPE_OTHER_COORDINATE_OPERATION;
1333
0
        }
1334
1335
44
        if (dynamic_cast<CoordinateMetadata *>(ptr)) {
1336
44
            return PJ_TYPE_COORDINATE_METADATA;
1337
44
        }
1338
1339
0
        return PJ_TYPE_UNKNOWN;
1340
44
    };
1341
1342
39.6k
    obj->type = getType();
1343
39.6k
    return obj->type;
1344
44.5k
}
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
15.7k
const char *proj_get_name(const PJ *obj) {
1513
15.7k
    if (!obj) {
1514
0
        return nullptr;
1515
0
    }
1516
15.7k
    auto identifiedObj = dynamic_cast<IdentifiedObject *>(obj->iso_obj.get());
1517
15.7k
    if (!identifiedObj) {
1518
0
        return nullptr;
1519
0
    }
1520
15.7k
    const auto &desc = identifiedObj->name()->description();
1521
15.7k
    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
15.7k
    return desc->c_str();
1527
15.7k
}
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
21.9k
                         const char **out_area_name) {
1980
21.9k
    return proj_get_area_of_use_ex(ctx, obj, 0, out_west_lon_degree,
1981
21.9k
                                   out_south_lat_degree, out_east_lon_degree,
1982
21.9k
                                   out_north_lat_degree, out_area_name);
1983
21.9k
}
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
21.9k
                            const char **out_area_name) {
2015
21.9k
    (void)ctx;
2016
21.9k
    if (out_area_name) {
2017
21.9k
        *out_area_name = nullptr;
2018
21.9k
    }
2019
21.9k
    auto objectUsage = dynamic_cast<const ObjectUsage *>(obj->iso_obj.get());
2020
21.9k
    if (!objectUsage) {
2021
0
        return false;
2022
0
    }
2023
21.9k
    const auto &domains = objectUsage->domains();
2024
21.9k
    if (domainIdx < 0 || static_cast<size_t>(domainIdx) >= domains.size()) {
2025
46
        return false;
2026
46
    }
2027
21.9k
    const auto &extent = domains[domainIdx]->domainOfValidity();
2028
21.9k
    if (!extent) {
2029
0
        return false;
2030
0
    }
2031
21.9k
    const auto &desc = extent->description();
2032
21.9k
    if (desc.has_value() && out_area_name) {
2033
19.6k
        *out_area_name = desc->c_str();
2034
19.6k
    }
2035
2036
21.9k
    const auto &geogElements = extent->geographicElements();
2037
21.9k
    if (!geogElements.empty()) {
2038
21.9k
        auto bbox =
2039
21.9k
            dynamic_cast<const GeographicBoundingBox *>(geogElements[0].get());
2040
21.9k
        if (bbox) {
2041
21.9k
            if (out_west_lon_degree) {
2042
21.9k
                *out_west_lon_degree = bbox->westBoundLongitude();
2043
21.9k
            }
2044
21.9k
            if (out_south_lat_degree) {
2045
21.9k
                *out_south_lat_degree = bbox->southBoundLatitude();
2046
21.9k
            }
2047
21.9k
            if (out_east_lon_degree) {
2048
21.9k
                *out_east_lon_degree = bbox->eastBoundLongitude();
2049
21.9k
            }
2050
21.9k
            if (out_north_lat_degree) {
2051
21.9k
                *out_north_lat_degree = bbox->northBoundLatitude();
2052
21.9k
            }
2053
21.9k
            return true;
2054
21.9k
        }
2055
21.9k
    }
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
21.9k
}
2070
2071
// ---------------------------------------------------------------------------
2072
2073
static const GeodeticCRS *extractGeodeticCRS(PJ_CONTEXT *ctx, const PJ *crs,
2074
2.35k
                                             const char *fname) {
2075
2.35k
    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
2.35k
    auto l_crs = dynamic_cast<const CRS *>(crs->iso_obj.get());
2081
2.35k
    if (!l_crs) {
2082
0
        proj_log_error(ctx, fname, "Object is not a CRS");
2083
0
        return nullptr;
2084
0
    }
2085
2.35k
    auto geodCRS = l_crs->extractGeodeticCRSRaw();
2086
2.35k
    if (!geodCRS) {
2087
0
        proj_log_error(ctx, fname, "CRS has no geodetic CRS");
2088
0
    }
2089
2.35k
    return geodCRS;
2090
2.35k
}
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
2.35k
PJ *proj_crs_get_geodetic_crs(PJ_CONTEXT *ctx, const PJ *crs) {
2106
2.35k
    SANITIZE_CTX(ctx);
2107
2.35k
    auto geodCRS = extractGeodeticCRS(ctx, crs, __FUNCTION__);
2108
2.35k
    if (!geodCRS) {
2109
0
        return nullptr;
2110
0
    }
2111
2.35k
    return pj_obj_create(ctx,
2112
2.35k
                         NN_NO_CHECK(nn_dynamic_pointer_cast<IdentifiedObject>(
2113
2.35k
                             geodCRS->shared_from_this())));
2114
2.35k
}
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
    SANITIZE_CTX(ctx);
2127
0
    if (!crs) {
2128
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2129
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2130
0
        return false;
2131
0
    }
2132
0
    auto l_crs = dynamic_cast<const CRS *>(crs->iso_obj.get());
2133
0
    if (!l_crs) {
2134
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CRS");
2135
0
        return false;
2136
0
    }
2137
0
    return dynamic_cast<const DerivedCRS *>(l_crs) != nullptr;
2138
0
}
2139
2140
// ---------------------------------------------------------------------------
2141
2142
/** \brief Returns whether (at least one component of a) CRS has a dynamic
2143
 * reference frame
2144
 *
2145
 * @param ctx PROJ context, or NULL for default context
2146
 * @param crs Object of type CRS (must not be NULL)
2147
 * @return TRUE if the CRS is a dynamic CRS.
2148
 * @since 9.9
2149
 */
2150
0
int proj_crs_is_dynamic(PJ_CONTEXT *ctx, const PJ *crs) {
2151
0
    SANITIZE_CTX(ctx);
2152
0
    if (!crs) {
2153
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2154
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2155
0
        return false;
2156
0
    }
2157
0
    auto l_crs = dynamic_cast<const CRS *>(crs->iso_obj.get());
2158
0
    if (!l_crs) {
2159
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CRS");
2160
0
        return false;
2161
0
    }
2162
0
    return l_crs->isDynamic(/* considerWGS84AsDynamic = */ false);
2163
0
}
2164
2165
// ---------------------------------------------------------------------------
2166
2167
/** \brief Get a CRS component from a CompoundCRS
2168
 *
2169
 * The returned object must be unreferenced with proj_destroy() after
2170
 * use.
2171
 * It should be used by at most one thread at a time.
2172
 *
2173
 * @param ctx PROJ context, or NULL for default context
2174
 * @param crs Object of type CRS (must not be NULL)
2175
 * @param index Index of the CRS component (typically 0 = horizontal, 1 =
2176
 * vertical)
2177
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2178
 * in case of error.
2179
 */
2180
0
PJ *proj_crs_get_sub_crs(PJ_CONTEXT *ctx, const PJ *crs, int index) {
2181
0
    SANITIZE_CTX(ctx);
2182
0
    if (!crs) {
2183
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2184
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2185
0
        return nullptr;
2186
0
    }
2187
0
    auto l_crs = dynamic_cast<CompoundCRS *>(crs->iso_obj.get());
2188
0
    if (!l_crs) {
2189
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CompoundCRS");
2190
0
        return nullptr;
2191
0
    }
2192
0
    const auto &components = l_crs->componentReferenceSystems();
2193
0
    if (static_cast<size_t>(index) >= components.size()) {
2194
0
        return nullptr;
2195
0
    }
2196
0
    return pj_obj_create(ctx, components[index]);
2197
0
}
2198
2199
// ---------------------------------------------------------------------------
2200
2201
/** \brief Returns a BoundCRS
2202
 *
2203
 * The returned object must be unreferenced with proj_destroy() after
2204
 * use.
2205
 * It should be used by at most one thread at a time.
2206
 *
2207
 * @param ctx PROJ context, or NULL for default context
2208
 * @param base_crs Base CRS (must not be NULL)
2209
 * @param hub_crs Hub CRS (must not be NULL)
2210
 * @param transformation Transformation (must not be NULL)
2211
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2212
 * in case of error.
2213
 */
2214
PJ *proj_crs_create_bound_crs(PJ_CONTEXT *ctx, const PJ *base_crs,
2215
0
                              const PJ *hub_crs, const PJ *transformation) {
2216
0
    SANITIZE_CTX(ctx);
2217
0
    if (!base_crs || !hub_crs || !transformation) {
2218
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2219
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2220
0
        return nullptr;
2221
0
    }
2222
0
    auto l_base_crs = std::dynamic_pointer_cast<CRS>(base_crs->iso_obj);
2223
0
    if (!l_base_crs) {
2224
0
        proj_log_error(ctx, __FUNCTION__, "base_crs is not a CRS");
2225
0
        return nullptr;
2226
0
    }
2227
0
    auto l_hub_crs = std::dynamic_pointer_cast<CRS>(hub_crs->iso_obj);
2228
0
    if (!l_hub_crs) {
2229
0
        proj_log_error(ctx, __FUNCTION__, "hub_crs is not a CRS");
2230
0
        return nullptr;
2231
0
    }
2232
0
    auto l_transformation =
2233
0
        std::dynamic_pointer_cast<Transformation>(transformation->iso_obj);
2234
0
    if (!l_transformation) {
2235
0
        proj_log_error(ctx, __FUNCTION__, "transformation is not a CRS");
2236
0
        return nullptr;
2237
0
    }
2238
0
    try {
2239
0
        return pj_obj_create(ctx,
2240
0
                             BoundCRS::create(NN_NO_CHECK(l_base_crs),
2241
0
                                              NN_NO_CHECK(l_hub_crs),
2242
0
                                              NN_NO_CHECK(l_transformation)));
2243
0
    } catch (const std::exception &e) {
2244
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2245
0
        return nullptr;
2246
0
    }
2247
0
}
2248
2249
// ---------------------------------------------------------------------------
2250
2251
/** \brief Returns potentially
2252
 * a BoundCRS, with a transformation to EPSG:4326, wrapping this CRS
2253
 *
2254
 * The returned object must be unreferenced with proj_destroy() after
2255
 * use.
2256
 * It should be used by at most one thread at a time.
2257
 *
2258
 * This is the same as method
2259
 * osgeo::proj::crs::CRS::createBoundCRSToWGS84IfPossible()
2260
 *
2261
 * @param ctx PROJ context, or NULL for default context
2262
 * @param crs Object of type CRS (must not be NULL)
2263
 * @param options null-terminated list of options, or NULL. Currently
2264
 * supported options are:
2265
 * <ul>
2266
 * <li>ALLOW_INTERMEDIATE_CRS=ALWAYS/IF_NO_DIRECT_TRANSFORMATION/NEVER. Defaults
2267
 * to NEVER. When set to ALWAYS/IF_NO_DIRECT_TRANSFORMATION,
2268
 * intermediate CRS may be considered when computing the possible
2269
 * transformations. Slower.</li>
2270
 * </ul>
2271
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2272
 * in case of error.
2273
 */
2274
PJ *proj_crs_create_bound_crs_to_WGS84(PJ_CONTEXT *ctx, const PJ *crs,
2275
0
                                       const char *const *options) {
2276
0
    SANITIZE_CTX(ctx);
2277
0
    if (!crs) {
2278
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2279
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2280
0
        return nullptr;
2281
0
    }
2282
0
    auto l_crs = dynamic_cast<const CRS *>(crs->iso_obj.get());
2283
0
    if (!l_crs) {
2284
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CRS");
2285
0
        return nullptr;
2286
0
    }
2287
0
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
2288
0
    try {
2289
0
        CoordinateOperationContext::IntermediateCRSUse allowIntermediateCRS =
2290
0
            CoordinateOperationContext::IntermediateCRSUse::NEVER;
2291
0
        for (auto iter = options; iter && iter[0]; ++iter) {
2292
0
            const char *value;
2293
0
            if ((value = getOptionValue(*iter, "ALLOW_INTERMEDIATE_CRS="))) {
2294
0
                if (ci_equal(value, "YES") || ci_equal(value, "ALWAYS")) {
2295
0
                    allowIntermediateCRS =
2296
0
                        CoordinateOperationContext::IntermediateCRSUse::ALWAYS;
2297
0
                } else if (ci_equal(value, "IF_NO_DIRECT_TRANSFORMATION")) {
2298
0
                    allowIntermediateCRS = CoordinateOperationContext::
2299
0
                        IntermediateCRSUse::IF_NO_DIRECT_TRANSFORMATION;
2300
0
                }
2301
0
            } else {
2302
0
                std::string msg("Unknown option :");
2303
0
                msg += *iter;
2304
0
                proj_log_error(ctx, __FUNCTION__, msg.c_str());
2305
0
                return nullptr;
2306
0
            }
2307
0
        }
2308
0
        return pj_obj_create(ctx, l_crs->createBoundCRSToWGS84IfPossible(
2309
0
                                      dbContext, allowIntermediateCRS));
2310
0
    } catch (const std::exception &e) {
2311
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2312
0
        return nullptr;
2313
0
    }
2314
0
}
2315
2316
// ---------------------------------------------------------------------------
2317
2318
/** \brief Returns a BoundCRS, with a transformation to a hub geographic 3D crs
2319
 * (use EPSG:4979 for WGS84 for example), using a grid.
2320
 *
2321
 * The returned object must be unreferenced with proj_destroy() after
2322
 * use.
2323
 * It should be used by at most one thread at a time.
2324
 *
2325
 * @param ctx PROJ context, or NULL for default context
2326
 * @param vert_crs Object of type VerticalCRS (must not be NULL)
2327
 * @param hub_geographic_3D_crs Object of type Geographic 3D CRS (must not be
2328
 * NULL)
2329
 * @param grid_name Grid name (typically a .gtx file)
2330
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2331
 * in case of error.
2332
 * @since 6.3
2333
 */
2334
PJ *proj_crs_create_bound_vertical_crs(PJ_CONTEXT *ctx, const PJ *vert_crs,
2335
                                       const PJ *hub_geographic_3D_crs,
2336
0
                                       const char *grid_name) {
2337
0
    SANITIZE_CTX(ctx);
2338
0
    if (!vert_crs || !hub_geographic_3D_crs || !grid_name) {
2339
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2340
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2341
0
        return nullptr;
2342
0
    }
2343
0
    auto l_crs = std::dynamic_pointer_cast<VerticalCRS>(vert_crs->iso_obj);
2344
0
    if (!l_crs) {
2345
0
        proj_log_error(ctx, __FUNCTION__, "vert_crs is not a VerticalCRS");
2346
0
        return nullptr;
2347
0
    }
2348
0
    auto hub_crs =
2349
0
        std::dynamic_pointer_cast<CRS>(hub_geographic_3D_crs->iso_obj);
2350
0
    if (!hub_crs) {
2351
0
        proj_log_error(ctx, __FUNCTION__, "hub_geographic_3D_crs is not a CRS");
2352
0
        return nullptr;
2353
0
    }
2354
0
    try {
2355
0
        auto nnCRS = NN_NO_CHECK(l_crs);
2356
0
        auto nnHubCRS = NN_NO_CHECK(hub_crs);
2357
0
        auto transformation =
2358
0
            Transformation::createGravityRelatedHeightToGeographic3D(
2359
0
                PropertyMap().set(IdentifiedObject::NAME_KEY,
2360
0
                                  "unknown to " + hub_crs->nameStr() +
2361
0
                                      " ellipsoidal height"),
2362
0
                nnCRS, nnHubCRS, nullptr, std::string(grid_name),
2363
0
                std::vector<PositionalAccuracyNNPtr>());
2364
0
        return pj_obj_create(ctx,
2365
0
                             BoundCRS::create(nnCRS, nnHubCRS, transformation));
2366
0
    } catch (const std::exception &e) {
2367
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2368
0
        return nullptr;
2369
0
    }
2370
0
}
2371
2372
// ---------------------------------------------------------------------------
2373
2374
/** \brief Get the ellipsoid from a CRS or a GeodeticReferenceFrame.
2375
 *
2376
 * The returned object must be unreferenced with proj_destroy() after
2377
 * use.
2378
 * It should be used by at most one thread at a time.
2379
 *
2380
 * @param ctx PROJ context, or NULL for default context
2381
 * @param obj Object of type CRS or GeodeticReferenceFrame (must not be NULL)
2382
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2383
 * in case of error.
2384
 */
2385
2.54k
PJ *proj_get_ellipsoid(PJ_CONTEXT *ctx, const PJ *obj) {
2386
2.54k
    SANITIZE_CTX(ctx);
2387
2.54k
    auto ptr = obj->iso_obj.get();
2388
2.54k
    if (dynamic_cast<const CRS *>(ptr)) {
2389
0
        auto geodCRS = extractGeodeticCRS(ctx, obj, __FUNCTION__);
2390
0
        if (geodCRS) {
2391
0
            return pj_obj_create(ctx, geodCRS->ellipsoid());
2392
0
        }
2393
2.54k
    } else {
2394
2.54k
        auto datum = dynamic_cast<const GeodeticReferenceFrame *>(ptr);
2395
2.54k
        if (datum) {
2396
2.54k
            return pj_obj_create(ctx, datum->ellipsoid());
2397
2.54k
        }
2398
2.54k
    }
2399
0
    proj_log_error(ctx, __FUNCTION__,
2400
0
                   "Object is not a CRS or GeodeticReferenceFrame");
2401
0
    return nullptr;
2402
2.54k
}
2403
2404
// ---------------------------------------------------------------------------
2405
2406
/** \brief Get the name of the celestial body of this object.
2407
 *
2408
 * Object should be a CRS, Datum or Ellipsoid.
2409
 *
2410
 * @param ctx PROJ context, or NULL for default context
2411
 * @param obj Object of type CRS, Datum or Ellipsoid.(must not be NULL)
2412
 * @return the name of the celestial body, or NULL.
2413
 * @since 8.1
2414
 */
2415
0
const char *proj_get_celestial_body_name(PJ_CONTEXT *ctx, const PJ *obj) {
2416
0
    SANITIZE_CTX(ctx);
2417
0
    const BaseObject *ptr = obj->iso_obj.get();
2418
0
    if (dynamic_cast<const CRS *>(ptr)) {
2419
0
        const auto geodCRS = extractGeodeticCRS(ctx, obj, __FUNCTION__);
2420
0
        if (!geodCRS) {
2421
            // FIXME when vertical CRS can be non-EARTH...
2422
0
            return datum::Ellipsoid::EARTH.c_str();
2423
0
        }
2424
0
        return geodCRS->ellipsoid()->celestialBody().c_str();
2425
0
    }
2426
0
    const auto ensemble = dynamic_cast<const DatumEnsemble *>(ptr);
2427
0
    if (ensemble) {
2428
0
        ptr = ensemble->datums().front().get();
2429
        // Go on
2430
0
    }
2431
0
    const auto geodetic_datum =
2432
0
        dynamic_cast<const GeodeticReferenceFrame *>(ptr);
2433
0
    if (geodetic_datum) {
2434
0
        return geodetic_datum->ellipsoid()->celestialBody().c_str();
2435
0
    }
2436
0
    const auto vertical_datum =
2437
0
        dynamic_cast<const VerticalReferenceFrame *>(ptr);
2438
0
    if (vertical_datum) {
2439
        // FIXME when vertical CRS can be non-EARTH...
2440
0
        return datum::Ellipsoid::EARTH.c_str();
2441
0
    }
2442
0
    const auto ellipsoid = dynamic_cast<const Ellipsoid *>(ptr);
2443
0
    if (ellipsoid) {
2444
0
        return ellipsoid->celestialBody().c_str();
2445
0
    }
2446
0
    proj_log_error(ctx, __FUNCTION__,
2447
0
                   "Object is not a CRS, Datum or Ellipsoid");
2448
0
    return nullptr;
2449
0
}
2450
2451
// ---------------------------------------------------------------------------
2452
2453
/** \brief Get the horizontal datum from a CRS
2454
 *
2455
 * This function may return a Datum or DatumEnsemble object.
2456
 *
2457
 * The returned object must be unreferenced with proj_destroy() after
2458
 * use.
2459
 * It should be used by at most one thread at a time.
2460
 *
2461
 * @param ctx PROJ context, or NULL for default context
2462
 * @param crs Object of type CRS (must not be NULL)
2463
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2464
 * in case of error.
2465
 */
2466
0
PJ *proj_crs_get_horizontal_datum(PJ_CONTEXT *ctx, const PJ *crs) {
2467
0
    SANITIZE_CTX(ctx);
2468
0
    auto geodCRS = extractGeodeticCRS(ctx, crs, __FUNCTION__);
2469
0
    if (!geodCRS) {
2470
0
        return nullptr;
2471
0
    }
2472
0
    const auto &datum = geodCRS->datum();
2473
0
    if (datum) {
2474
0
        return pj_obj_create(ctx, NN_NO_CHECK(datum));
2475
0
    }
2476
2477
0
    const auto &datumEnsemble = geodCRS->datumEnsemble();
2478
0
    if (datumEnsemble) {
2479
0
        return pj_obj_create(ctx, NN_NO_CHECK(datumEnsemble));
2480
0
    }
2481
0
    proj_log_error(ctx, __FUNCTION__, "CRS has no datum");
2482
0
    return nullptr;
2483
0
}
2484
2485
// ---------------------------------------------------------------------------
2486
2487
/** \brief Return ellipsoid parameters.
2488
 *
2489
 * @param ctx PROJ context, or NULL for default context
2490
 * @param ellipsoid Object of type Ellipsoid (must not be NULL)
2491
 * @param out_semi_major_metre Pointer to a value to store the semi-major axis
2492
 * in
2493
 * metre. or NULL
2494
 * @param out_semi_minor_metre Pointer to a value to store the semi-minor axis
2495
 * in
2496
 * metre. or NULL
2497
 * @param out_is_semi_minor_computed Pointer to a boolean value to indicate if
2498
 * the
2499
 * semi-minor value was computed. If FALSE, its value comes from the
2500
 * definition. or NULL
2501
 * @param out_inv_flattening Pointer to a value to store the inverse
2502
 * flattening. or NULL
2503
 * @return TRUE in case of success.
2504
 */
2505
int proj_ellipsoid_get_parameters(PJ_CONTEXT *ctx, const PJ *ellipsoid,
2506
                                  double *out_semi_major_metre,
2507
                                  double *out_semi_minor_metre,
2508
                                  int *out_is_semi_minor_computed,
2509
2.54k
                                  double *out_inv_flattening) {
2510
2.54k
    SANITIZE_CTX(ctx);
2511
2.54k
    if (!ellipsoid) {
2512
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2513
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2514
0
        return FALSE;
2515
0
    }
2516
2.54k
    auto l_ellipsoid =
2517
2.54k
        dynamic_cast<const Ellipsoid *>(ellipsoid->iso_obj.get());
2518
2.54k
    if (!l_ellipsoid) {
2519
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a Ellipsoid");
2520
0
        return FALSE;
2521
0
    }
2522
2523
2.54k
    if (out_semi_major_metre) {
2524
2.54k
        *out_semi_major_metre = l_ellipsoid->semiMajorAxis().getSIValue();
2525
2.54k
    }
2526
2.54k
    if (out_semi_minor_metre) {
2527
0
        *out_semi_minor_metre =
2528
0
            l_ellipsoid->computeSemiMinorAxis().getSIValue();
2529
0
    }
2530
2.54k
    if (out_is_semi_minor_computed) {
2531
0
        *out_is_semi_minor_computed =
2532
0
            !(l_ellipsoid->semiMinorAxis().has_value());
2533
0
    }
2534
2.54k
    if (out_inv_flattening) {
2535
2.54k
        *out_inv_flattening = l_ellipsoid->computedInverseFlattening();
2536
2.54k
    }
2537
2.54k
    return TRUE;
2538
2.54k
}
2539
2540
// ---------------------------------------------------------------------------
2541
2542
/** \brief Get the prime meridian of a CRS or a GeodeticReferenceFrame.
2543
 *
2544
 * The returned object must be unreferenced with proj_destroy() after
2545
 * use.
2546
 * It should be used by at most one thread at a time.
2547
 *
2548
 * @param ctx PROJ context, or NULL for default context
2549
 * @param obj Object of type CRS or GeodeticReferenceFrame (must not be NULL)
2550
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2551
 * in case of error.
2552
 */
2553
2554
0
PJ *proj_get_prime_meridian(PJ_CONTEXT *ctx, const PJ *obj) {
2555
0
    SANITIZE_CTX(ctx);
2556
0
    auto ptr = obj->iso_obj.get();
2557
0
    if (dynamic_cast<CRS *>(ptr)) {
2558
0
        auto geodCRS = extractGeodeticCRS(ctx, obj, __FUNCTION__);
2559
0
        if (geodCRS) {
2560
0
            return pj_obj_create(ctx, geodCRS->primeMeridian());
2561
0
        }
2562
0
    } else {
2563
0
        auto datum = dynamic_cast<const GeodeticReferenceFrame *>(ptr);
2564
0
        if (datum) {
2565
0
            return pj_obj_create(ctx, datum->primeMeridian());
2566
0
        }
2567
0
    }
2568
0
    proj_log_error(ctx, __FUNCTION__,
2569
0
                   "Object is not a CRS or GeodeticReferenceFrame");
2570
0
    return nullptr;
2571
0
}
2572
2573
// ---------------------------------------------------------------------------
2574
2575
/** \brief Return prime meridian parameters.
2576
 *
2577
 * @param ctx PROJ context, or NULL for default context
2578
 * @param prime_meridian Object of type PrimeMeridian (must not be NULL)
2579
 * @param out_longitude Pointer to a value to store the longitude of the prime
2580
 * meridian, in its native unit. or NULL
2581
 * @param out_unit_conv_factor Pointer to a value to store the conversion
2582
 * factor of the prime meridian longitude unit to radian. or NULL
2583
 * @param out_unit_name Pointer to a string value to store the unit name.
2584
 * or NULL
2585
 * @return TRUE in case of success.
2586
 */
2587
int proj_prime_meridian_get_parameters(PJ_CONTEXT *ctx,
2588
                                       const PJ *prime_meridian,
2589
                                       double *out_longitude,
2590
                                       double *out_unit_conv_factor,
2591
0
                                       const char **out_unit_name) {
2592
0
    SANITIZE_CTX(ctx);
2593
0
    if (!prime_meridian) {
2594
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2595
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2596
0
        return false;
2597
0
    }
2598
0
    auto l_pm =
2599
0
        dynamic_cast<const PrimeMeridian *>(prime_meridian->iso_obj.get());
2600
0
    if (!l_pm) {
2601
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a PrimeMeridian");
2602
0
        return false;
2603
0
    }
2604
0
    const auto &longitude = l_pm->longitude();
2605
0
    if (out_longitude) {
2606
0
        *out_longitude = longitude.value();
2607
0
    }
2608
0
    const auto &unit = longitude.unit();
2609
0
    if (out_unit_conv_factor) {
2610
0
        *out_unit_conv_factor = unit.conversionToSI();
2611
0
    }
2612
0
    if (out_unit_name) {
2613
0
        *out_unit_name = unit.name().c_str();
2614
0
    }
2615
0
    return true;
2616
0
}
2617
2618
// ---------------------------------------------------------------------------
2619
2620
/** \brief Return the base CRS of a BoundCRS or a DerivedCRS/ProjectedCRS, or
2621
 * the source CRS of a CoordinateOperation, or the CRS of a CoordinateMetadata.
2622
 *
2623
 * The returned object must be unreferenced with proj_destroy() after
2624
 * use.
2625
 * It should be used by at most one thread at a time.
2626
 *
2627
 * @param ctx PROJ context, or NULL for default context
2628
 * @param obj Object of type BoundCRS or CoordinateOperation (must not be NULL)
2629
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2630
 * in case of error, or missing source CRS.
2631
 */
2632
13.1k
PJ *proj_get_source_crs(PJ_CONTEXT *ctx, const PJ *obj) {
2633
13.1k
    SANITIZE_CTX(ctx);
2634
13.1k
    if (!obj) {
2635
0
        return nullptr;
2636
0
    }
2637
13.1k
    auto ptr = obj->iso_obj.get();
2638
13.1k
    auto boundCRS = dynamic_cast<const BoundCRS *>(ptr);
2639
13.1k
    if (boundCRS) {
2640
0
        return pj_obj_create(ctx, boundCRS->baseCRS());
2641
0
    }
2642
13.1k
    auto derivedCRS = dynamic_cast<const DerivedCRS *>(ptr);
2643
13.1k
    if (derivedCRS) {
2644
0
        return pj_obj_create(ctx, derivedCRS->baseCRS());
2645
0
    }
2646
13.1k
    auto co = dynamic_cast<const CoordinateOperation *>(ptr);
2647
13.1k
    if (co) {
2648
13.1k
        auto sourceCRS = co->sourceCRS();
2649
13.1k
        if (sourceCRS) {
2650
13.1k
            return pj_obj_create(ctx, NN_NO_CHECK(sourceCRS));
2651
13.1k
        }
2652
0
        return nullptr;
2653
13.1k
    }
2654
3
    if (!obj->alternativeCoordinateOperations.empty()) {
2655
0
        return proj_get_source_crs(ctx,
2656
0
                                   obj->alternativeCoordinateOperations[0].pj);
2657
0
    }
2658
3
    auto coordinateMetadata = dynamic_cast<const CoordinateMetadata *>(ptr);
2659
3
    if (coordinateMetadata) {
2660
3
        return pj_obj_create(ctx, coordinateMetadata->crs());
2661
3
    }
2662
2663
0
    proj_log_error(ctx, __FUNCTION__,
2664
0
                   "Object is not a BoundCRS, a CoordinateOperation or a "
2665
0
                   "CoordinateMetadata");
2666
0
    return nullptr;
2667
3
}
2668
2669
// ---------------------------------------------------------------------------
2670
2671
/** \brief Return the hub CRS of a BoundCRS or the target CRS of a
2672
 * CoordinateOperation.
2673
 *
2674
 * The returned object must be unreferenced with proj_destroy() after
2675
 * use.
2676
 * It should be used by at most one thread at a time.
2677
 *
2678
 * @param ctx PROJ context, or NULL for default context
2679
 * @param obj Object of type BoundCRS or CoordinateOperation (must not be NULL)
2680
 * @return Object that must be unreferenced with proj_destroy(), or NULL
2681
 * in case of error, or missing target CRS.
2682
 */
2683
13.1k
PJ *proj_get_target_crs(PJ_CONTEXT *ctx, const PJ *obj) {
2684
13.1k
    SANITIZE_CTX(ctx);
2685
13.1k
    if (!obj) {
2686
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2687
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2688
0
        return nullptr;
2689
0
    }
2690
13.1k
    auto ptr = obj->iso_obj.get();
2691
13.1k
    auto boundCRS = dynamic_cast<const BoundCRS *>(ptr);
2692
13.1k
    if (boundCRS) {
2693
0
        return pj_obj_create(ctx, boundCRS->hubCRS());
2694
0
    }
2695
13.1k
    auto co = dynamic_cast<const CoordinateOperation *>(ptr);
2696
13.1k
    if (co) {
2697
13.1k
        auto targetCRS = co->targetCRS();
2698
13.1k
        if (targetCRS) {
2699
13.1k
            return pj_obj_create(ctx, NN_NO_CHECK(targetCRS));
2700
13.1k
        }
2701
0
        return nullptr;
2702
13.1k
    }
2703
0
    if (!obj->alternativeCoordinateOperations.empty()) {
2704
0
        return proj_get_target_crs(ctx,
2705
0
                                   obj->alternativeCoordinateOperations[0].pj);
2706
0
    }
2707
0
    proj_log_error(ctx, __FUNCTION__,
2708
0
                   "Object is not a BoundCRS or a CoordinateOperation");
2709
0
    return nullptr;
2710
0
}
2711
2712
// ---------------------------------------------------------------------------
2713
2714
/** \brief Identify the CRS with reference CRSs.
2715
 *
2716
 * The candidate CRSs are either hard-coded, or looked in the database when
2717
 * it is available.
2718
 *
2719
 * Note that the implementation uses a set of heuristics to have a good
2720
 * compromise of successful identifications over execution time. It might miss
2721
 * legitimate matches in some circumstances.
2722
 *
2723
 * The method returns a list of matching reference CRS, and the percentage
2724
 * (0-100) of confidence in the match. The list is sorted by decreasing
2725
 * confidence.
2726
 * <ul>
2727
 * <li>100% means that the name of the reference entry
2728
 * perfectly matches the CRS name, and both are equivalent. In which case a
2729
 * single result is returned.
2730
 * Note: in the case of a GeographicCRS whose axis
2731
 * order is implicit in the input definition (for example ESRI WKT), then axis
2732
 * order is ignored for the purpose of identification. That is the CRS built
2733
 * from
2734
 * GEOGCS["GCS_WGS_1984",DATUM["D_WGS_1984",SPHEROID["WGS_1984",6378137.0,298.257223563]],
2735
 * PRIMEM["Greenwich",0.0],UNIT["Degree",0.0174532925199433]]
2736
 * will be identified to EPSG:4326, but will not pass a
2737
 * isEquivalentTo(EPSG_4326, util::IComparable::Criterion::EQUIVALENT) test,
2738
 * but rather isEquivalentTo(EPSG_4326,
2739
 * util::IComparable::Criterion::EQUIVALENT_EXCEPT_AXIS_ORDER_GEOGCRS)
2740
 * </li>
2741
 * <li>90% means that CRS are equivalent, but the names are not exactly the
2742
 * same.</li>
2743
 * <li>70% means that CRS are equivalent, but the names are not equivalent.
2744
 * </li>
2745
 * <li>25% means that the CRS are not equivalent, but there is some similarity
2746
 * in
2747
 * the names.</li>
2748
 * </ul>
2749
 * Other confidence values may be returned by some specialized implementations.
2750
 *
2751
 * This is implemented for GeodeticCRS, ProjectedCRS, VerticalCRS and
2752
 * CompoundCRS.
2753
 *
2754
 * @param ctx PROJ context, or NULL for default context
2755
 * @param obj Object of type CRS. Must not be NULL
2756
 * @param auth_name Authority name, or NULL for all authorities
2757
 * @param options Placeholder for future options. Should be set to NULL.
2758
 * @param out_confidence Output parameter. Pointer to an array of integers that
2759
 * will be allocated by the function and filled with the confidence values
2760
 * (0-100). There are as many elements in this array as
2761
 * proj_list_get_count()
2762
 * returns on the return value of this function. *confidence should be
2763
 * released with proj_int_list_destroy().
2764
 * @return a list of matching reference CRS, or nullptr in case of error.
2765
 */
2766
PJ_OBJ_LIST *proj_identify(PJ_CONTEXT *ctx, const PJ *obj,
2767
                           const char *auth_name, const char *const *options,
2768
0
                           int **out_confidence) {
2769
0
    SANITIZE_CTX(ctx);
2770
0
    if (!obj) {
2771
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2772
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2773
0
        return nullptr;
2774
0
    }
2775
0
    (void)options;
2776
0
    if (out_confidence) {
2777
0
        *out_confidence = nullptr;
2778
0
    }
2779
0
    auto ptr = obj->iso_obj.get();
2780
0
    auto crs = dynamic_cast<const CRS *>(ptr);
2781
0
    if (!crs) {
2782
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CRS");
2783
0
    } else {
2784
0
        int *confidenceTemp = nullptr;
2785
0
        try {
2786
0
            auto factory = AuthorityFactory::create(getDBcontext(ctx),
2787
0
                                                    auth_name ? auth_name : "");
2788
0
            auto res = crs->identify(factory);
2789
0
            std::vector<IdentifiedObjectNNPtr> objects;
2790
0
            confidenceTemp = out_confidence ? new int[res.size()] : nullptr;
2791
0
            size_t i = 0;
2792
0
            for (const auto &pair : res) {
2793
0
                objects.push_back(pair.first);
2794
0
                if (confidenceTemp) {
2795
0
                    confidenceTemp[i] = pair.second;
2796
0
                    ++i;
2797
0
                }
2798
0
            }
2799
0
            auto ret = std::make_unique<PJ_OBJ_LIST>(std::move(objects));
2800
0
            if (out_confidence) {
2801
0
                *out_confidence = confidenceTemp;
2802
0
                confidenceTemp = nullptr;
2803
0
            }
2804
0
            return ret.release();
2805
0
        } catch (const std::exception &e) {
2806
0
            delete[] confidenceTemp;
2807
0
            proj_log_error(ctx, __FUNCTION__, e.what());
2808
0
        }
2809
0
    }
2810
0
    return nullptr;
2811
0
}
2812
2813
// ---------------------------------------------------------------------------
2814
2815
/** \brief Free an array of integer. */
2816
0
void proj_int_list_destroy(int *list) { delete[] list; }
2817
2818
// ---------------------------------------------------------------------------
2819
2820
/** \brief Return the list of authorities used in the database.
2821
 *
2822
 * The returned list is NULL terminated and must be freed with
2823
 * proj_string_list_destroy().
2824
 *
2825
 * @param ctx PROJ context, or NULL for default context
2826
 *
2827
 * @return a NULL terminated list of NUL-terminated strings that must be
2828
 * freed with proj_string_list_destroy(), or NULL in case of error.
2829
 */
2830
0
PROJ_STRING_LIST proj_get_authorities_from_database(PJ_CONTEXT *ctx) {
2831
0
    SANITIZE_CTX(ctx);
2832
0
    try {
2833
0
        auto ret = to_string_list(getDBcontext(ctx)->getAuthorities());
2834
0
        return ret;
2835
0
    } catch (const std::exception &e) {
2836
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2837
0
    }
2838
0
    return nullptr;
2839
0
}
2840
2841
// ---------------------------------------------------------------------------
2842
2843
/** \brief Returns the set of authority codes of the given object type.
2844
 *
2845
 * The returned list is NULL terminated and must be freed with
2846
 * proj_string_list_destroy().
2847
 *
2848
 * @param ctx PROJ context, or NULL for default context.
2849
 * @param auth_name Authority name (must not be NULL)
2850
 * @param type Object type.
2851
 * @param allow_deprecated whether we should return deprecated objects as well.
2852
 *
2853
 * @return a NULL terminated list of NUL-terminated strings that must be
2854
 * freed with proj_string_list_destroy(), or NULL in case of error.
2855
 *
2856
 * @see proj_get_crs_info_list_from_database()
2857
 */
2858
PROJ_STRING_LIST proj_get_codes_from_database(PJ_CONTEXT *ctx,
2859
                                              const char *auth_name,
2860
                                              PJ_TYPE type,
2861
0
                                              int allow_deprecated) {
2862
0
    SANITIZE_CTX(ctx);
2863
0
    if (!auth_name) {
2864
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
2865
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
2866
0
        return nullptr;
2867
0
    }
2868
0
    try {
2869
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx), auth_name);
2870
0
        bool valid = false;
2871
0
        auto typeInternal = convertPJObjectTypeToObjectType(type, valid);
2872
0
        if (!valid) {
2873
0
            return nullptr;
2874
0
        }
2875
0
        auto ret = to_string_list(
2876
0
            factory->getAuthorityCodes(typeInternal, allow_deprecated != 0));
2877
0
        return ret;
2878
2879
0
    } catch (const std::exception &e) {
2880
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2881
0
    }
2882
0
    return nullptr;
2883
0
}
2884
2885
// ---------------------------------------------------------------------------
2886
2887
/** \brief Enumerate celestial bodies from the database.
2888
 *
2889
 * The returned object is an array of PROJ_CELESTIAL_BODY_INFO* pointers, whose
2890
 * last entry is NULL. This array should be freed with
2891
 * proj_celestial_body_list_destroy()
2892
 *
2893
 * @param ctx PROJ context, or NULL for default context
2894
 * @param auth_name Authority name, used to restrict the search.
2895
 * Or NULL for all authorities.
2896
 * @param out_result_count Output parameter pointing to an integer to receive
2897
 * the size of the result list. Might be NULL
2898
 * @return an array of PROJ_CELESTIAL_BODY_INFO* pointers to be freed with
2899
 * proj_celestial_body_list_destroy(), or NULL in case of error.
2900
 * @since 8.1
2901
 */
2902
PROJ_CELESTIAL_BODY_INFO **proj_get_celestial_body_list_from_database(
2903
0
    PJ_CONTEXT *ctx, const char *auth_name, int *out_result_count) {
2904
0
    SANITIZE_CTX(ctx);
2905
0
    PROJ_CELESTIAL_BODY_INFO **ret = nullptr;
2906
0
    int i = 0;
2907
0
    try {
2908
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx),
2909
0
                                                auth_name ? auth_name : "");
2910
0
        auto list = factory->getCelestialBodyList();
2911
0
        ret = new PROJ_CELESTIAL_BODY_INFO *[list.size() + 1];
2912
0
        for (const auto &info : list) {
2913
0
            ret[i] = new PROJ_CELESTIAL_BODY_INFO;
2914
0
            ret[i]->auth_name = pj_strdup(info.authName.c_str());
2915
0
            ret[i]->name = pj_strdup(info.name.c_str());
2916
0
            i++;
2917
0
        }
2918
0
        ret[i] = nullptr;
2919
0
        if (out_result_count)
2920
0
            *out_result_count = i;
2921
0
        return ret;
2922
0
    } catch (const std::exception &e) {
2923
0
        proj_log_error(ctx, __FUNCTION__, e.what());
2924
0
        if (ret) {
2925
0
            ret[i + 1] = nullptr;
2926
0
            proj_celestial_body_list_destroy(ret);
2927
0
        }
2928
0
        if (out_result_count)
2929
0
            *out_result_count = 0;
2930
0
    }
2931
0
    return nullptr;
2932
0
}
2933
2934
// ---------------------------------------------------------------------------
2935
2936
/** \brief Destroy the result returned by
2937
 * proj_get_celestial_body_list_from_database().
2938
 *
2939
 * @since 8.1
2940
 */
2941
0
void proj_celestial_body_list_destroy(PROJ_CELESTIAL_BODY_INFO **list) {
2942
0
    if (list) {
2943
0
        for (int i = 0; list[i] != nullptr; i++) {
2944
0
            free(list[i]->auth_name);
2945
0
            free(list[i]->name);
2946
0
            delete list[i];
2947
0
        }
2948
0
        delete[] list;
2949
0
    }
2950
0
}
2951
2952
// ---------------------------------------------------------------------------
2953
2954
/** Free a list of NULL terminated strings. */
2955
0
void proj_string_list_destroy(PROJ_STRING_LIST list) {
2956
0
    if (list) {
2957
0
        for (size_t i = 0; list[i] != nullptr; i++) {
2958
0
            delete[] list[i];
2959
0
        }
2960
0
        delete[] list;
2961
0
    }
2962
0
}
2963
2964
// ---------------------------------------------------------------------------
2965
2966
/** \brief Instantiate a default set of parameters to be used by
2967
 * proj_get_crs_list().
2968
 *
2969
 * @return a new object to free with proj_get_crs_list_parameters_destroy() */
2970
0
PROJ_CRS_LIST_PARAMETERS *proj_get_crs_list_parameters_create() {
2971
0
    auto ret = new (std::nothrow) PROJ_CRS_LIST_PARAMETERS();
2972
0
    if (ret) {
2973
0
        ret->types = nullptr;
2974
0
        ret->typesCount = 0;
2975
0
        ret->crs_area_of_use_contains_bbox = TRUE;
2976
0
        ret->bbox_valid = FALSE;
2977
0
        ret->west_lon_degree = 0.0;
2978
0
        ret->south_lat_degree = 0.0;
2979
0
        ret->east_lon_degree = 0.0;
2980
0
        ret->north_lat_degree = 0.0;
2981
0
        ret->allow_deprecated = FALSE;
2982
0
        ret->celestial_body_name = nullptr;
2983
0
    }
2984
0
    return ret;
2985
0
}
2986
2987
// ---------------------------------------------------------------------------
2988
2989
/** \brief Destroy an object returned by proj_get_crs_list_parameters_create()
2990
 */
2991
0
void proj_get_crs_list_parameters_destroy(PROJ_CRS_LIST_PARAMETERS *params) {
2992
0
    delete params;
2993
0
}
2994
2995
// ---------------------------------------------------------------------------
2996
2997
/** \brief Enumerate CRS objects from the database, taking into account various
2998
 * criteria.
2999
 *
3000
 * The returned object is an array of PROJ_CRS_INFO* pointers, whose last
3001
 * entry is NULL. This array should be freed with proj_crs_info_list_destroy()
3002
 *
3003
 * When no filter parameters are set, this is functionally equivalent to
3004
 * proj_get_codes_from_database(), instantiating a PJ* object for each
3005
 * of the codes with proj_create_from_database() and retrieving information
3006
 * with the various getters. However this function will be much faster.
3007
 *
3008
 * @param ctx PROJ context, or NULL for default context
3009
 * @param auth_name Authority name, used to restrict the search.
3010
 * Or NULL for all authorities.
3011
 * @param params Additional criteria, or NULL. If not-NULL, params SHOULD
3012
 * have been allocated by proj_get_crs_list_parameters_create(), as the
3013
 * PROJ_CRS_LIST_PARAMETERS structure might grow over time.
3014
 * @param out_result_count Output parameter pointing to an integer to receive
3015
 * the size of the result list. Might be NULL
3016
 * @return an array of PROJ_CRS_INFO* pointers to be freed with
3017
 * proj_crs_info_list_destroy(), or NULL in case of error.
3018
 */
3019
PROJ_CRS_INFO **
3020
proj_get_crs_info_list_from_database(PJ_CONTEXT *ctx, const char *auth_name,
3021
                                     const PROJ_CRS_LIST_PARAMETERS *params,
3022
0
                                     int *out_result_count) {
3023
0
    SANITIZE_CTX(ctx);
3024
0
    PROJ_CRS_INFO **ret = nullptr;
3025
0
    int i = 0;
3026
0
    try {
3027
0
        auto dbContext = getDBcontext(ctx);
3028
0
        std::string authName = auth_name ? auth_name : "";
3029
0
        auto actualAuthNames =
3030
0
            dbContext->getVersionedAuthoritiesFromName(authName);
3031
0
        if (actualAuthNames.empty())
3032
0
            actualAuthNames.push_back(std::move(authName));
3033
0
        std::list<AuthorityFactory::CRSInfo> concatList;
3034
0
        for (const auto &actualAuthName : actualAuthNames) {
3035
0
            auto factory = AuthorityFactory::create(dbContext, actualAuthName);
3036
0
            auto list = factory->getCRSInfoList();
3037
0
            concatList.splice(concatList.end(), std::move(list));
3038
0
        }
3039
0
        ret = new PROJ_CRS_INFO *[concatList.size() + 1];
3040
0
        GeographicBoundingBoxPtr bbox;
3041
0
        if (params && params->bbox_valid) {
3042
0
            bbox = GeographicBoundingBox::create(
3043
0
                       params->west_lon_degree, params->south_lat_degree,
3044
0
                       params->east_lon_degree, params->north_lat_degree)
3045
0
                       .as_nullable();
3046
0
        }
3047
0
        for (const auto &info : concatList) {
3048
0
            auto type = PJ_TYPE_CRS;
3049
0
            if (info.type == AuthorityFactory::ObjectType::GEOGRAPHIC_2D_CRS) {
3050
0
                type = PJ_TYPE_GEOGRAPHIC_2D_CRS;
3051
0
            } else if (info.type ==
3052
0
                       AuthorityFactory::ObjectType::GEOGRAPHIC_3D_CRS) {
3053
0
                type = PJ_TYPE_GEOGRAPHIC_3D_CRS;
3054
0
            } else if (info.type ==
3055
0
                       AuthorityFactory::ObjectType::GEOCENTRIC_CRS) {
3056
0
                type = PJ_TYPE_GEOCENTRIC_CRS;
3057
0
            } else if (info.type ==
3058
0
                       AuthorityFactory::ObjectType::GEODETIC_CRS) {
3059
0
                type = PJ_TYPE_GEODETIC_CRS;
3060
0
            } else if (info.type ==
3061
0
                       AuthorityFactory::ObjectType::PROJECTED_CRS) {
3062
0
                type = PJ_TYPE_PROJECTED_CRS;
3063
0
            } else if (info.type ==
3064
0
                       AuthorityFactory::ObjectType::VERTICAL_CRS) {
3065
0
                type = PJ_TYPE_VERTICAL_CRS;
3066
0
            } else if (info.type ==
3067
0
                       AuthorityFactory::ObjectType::COMPOUND_CRS) {
3068
0
                type = PJ_TYPE_COMPOUND_CRS;
3069
0
            }
3070
0
            if (params && params->typesCount) {
3071
0
                bool typeValid = false;
3072
0
                for (size_t j = 0; j < params->typesCount; j++) {
3073
0
                    if (params->types[j] == type) {
3074
0
                        typeValid = true;
3075
0
                        break;
3076
0
                    } else if (params->types[j] == PJ_TYPE_GEOGRAPHIC_CRS &&
3077
0
                               (type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3078
0
                                type == PJ_TYPE_GEOGRAPHIC_3D_CRS)) {
3079
0
                        typeValid = true;
3080
0
                        break;
3081
0
                    } else if (params->types[j] == PJ_TYPE_GEODETIC_CRS &&
3082
0
                               (type == PJ_TYPE_GEOCENTRIC_CRS ||
3083
0
                                type == PJ_TYPE_GEOGRAPHIC_2D_CRS ||
3084
0
                                type == PJ_TYPE_GEOGRAPHIC_3D_CRS)) {
3085
0
                        typeValid = true;
3086
0
                        break;
3087
0
                    }
3088
0
                }
3089
0
                if (!typeValid) {
3090
0
                    continue;
3091
0
                }
3092
0
            }
3093
0
            if (params && !params->allow_deprecated && info.deprecated) {
3094
0
                continue;
3095
0
            }
3096
0
            if (params && params->bbox_valid) {
3097
0
                if (!info.bbox_valid) {
3098
0
                    continue;
3099
0
                }
3100
0
                if (info.west_lon_degree <= info.east_lon_degree &&
3101
0
                    params->west_lon_degree <= params->east_lon_degree) {
3102
0
                    if (params->crs_area_of_use_contains_bbox) {
3103
0
                        if (params->west_lon_degree < info.west_lon_degree ||
3104
0
                            params->east_lon_degree > info.east_lon_degree ||
3105
0
                            params->south_lat_degree < info.south_lat_degree ||
3106
0
                            params->north_lat_degree > info.north_lat_degree) {
3107
0
                            continue;
3108
0
                        }
3109
0
                    } else {
3110
0
                        if (info.east_lon_degree < params->west_lon_degree ||
3111
0
                            info.west_lon_degree > params->east_lon_degree ||
3112
0
                            info.north_lat_degree < params->south_lat_degree ||
3113
0
                            info.south_lat_degree > params->north_lat_degree) {
3114
0
                            continue;
3115
0
                        }
3116
0
                    }
3117
0
                } else {
3118
0
                    auto crsExtent = GeographicBoundingBox::create(
3119
0
                        info.west_lon_degree, info.south_lat_degree,
3120
0
                        info.east_lon_degree, info.north_lat_degree);
3121
0
                    if (params->crs_area_of_use_contains_bbox) {
3122
0
                        if (!crsExtent->contains(NN_NO_CHECK(bbox))) {
3123
0
                            continue;
3124
0
                        }
3125
0
                    } else {
3126
0
                        if (!bbox->intersects(crsExtent)) {
3127
0
                            continue;
3128
0
                        }
3129
0
                    }
3130
0
                }
3131
0
            }
3132
0
            if (params && params->celestial_body_name &&
3133
0
                params->celestial_body_name != info.celestialBodyName) {
3134
0
                continue;
3135
0
            }
3136
3137
0
            ret[i] = new PROJ_CRS_INFO;
3138
0
            ret[i]->auth_name = pj_strdup(info.authName.c_str());
3139
0
            ret[i]->code = pj_strdup(info.code.c_str());
3140
0
            ret[i]->name = pj_strdup(info.name.c_str());
3141
0
            ret[i]->type = type;
3142
0
            ret[i]->deprecated = info.deprecated;
3143
0
            ret[i]->bbox_valid = info.bbox_valid;
3144
0
            ret[i]->west_lon_degree = info.west_lon_degree;
3145
0
            ret[i]->south_lat_degree = info.south_lat_degree;
3146
0
            ret[i]->east_lon_degree = info.east_lon_degree;
3147
0
            ret[i]->north_lat_degree = info.north_lat_degree;
3148
0
            ret[i]->area_name = pj_strdup(info.areaName.c_str());
3149
0
            ret[i]->projection_method_name =
3150
0
                info.projectionMethodName.empty()
3151
0
                    ? nullptr
3152
0
                    : pj_strdup(info.projectionMethodName.c_str());
3153
0
            ret[i]->celestial_body_name =
3154
0
                pj_strdup(info.celestialBodyName.c_str());
3155
0
            i++;
3156
0
        }
3157
0
        ret[i] = nullptr;
3158
0
        if (out_result_count)
3159
0
            *out_result_count = i;
3160
0
        return ret;
3161
0
    } catch (const std::exception &e) {
3162
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3163
0
        if (ret) {
3164
0
            ret[i + 1] = nullptr;
3165
0
            proj_crs_info_list_destroy(ret);
3166
0
        }
3167
0
        if (out_result_count)
3168
0
            *out_result_count = 0;
3169
0
    }
3170
0
    return nullptr;
3171
0
}
3172
3173
// ---------------------------------------------------------------------------
3174
3175
/** \brief Destroy the result returned by
3176
 * proj_get_crs_info_list_from_database().
3177
 */
3178
0
void proj_crs_info_list_destroy(PROJ_CRS_INFO **list) {
3179
0
    if (list) {
3180
0
        for (int i = 0; list[i] != nullptr; i++) {
3181
0
            free(list[i]->auth_name);
3182
0
            free(list[i]->code);
3183
0
            free(list[i]->name);
3184
0
            free(list[i]->area_name);
3185
0
            free(list[i]->projection_method_name);
3186
0
            free(list[i]->celestial_body_name);
3187
0
            delete list[i];
3188
0
        }
3189
0
        delete[] list;
3190
0
    }
3191
0
}
3192
3193
// ---------------------------------------------------------------------------
3194
3195
/** \brief Enumerate units from the database, taking into account various
3196
 * criteria.
3197
 *
3198
 * The returned object is an array of PROJ_UNIT_INFO* pointers, whose last
3199
 * entry is NULL. This array should be freed with proj_unit_list_destroy()
3200
 *
3201
 * @param ctx PROJ context, or NULL for default context
3202
 * @param auth_name Authority name, used to restrict the search.
3203
 * Or NULL for all authorities.
3204
 * @param category Filter by category, if this parameter is not NULL. Category
3205
 * is one of "linear", "linear_per_time", "angular", "angular_per_time",
3206
 * "scale", "scale_per_time" or "time"
3207
 * @param allow_deprecated whether we should return deprecated objects as well.
3208
 * @param out_result_count Output parameter pointing to an integer to receive
3209
 * the size of the result list. Might be NULL
3210
 * @return an array of PROJ_UNIT_INFO* pointers to be freed with
3211
 * proj_unit_list_destroy(), or NULL in case of error.
3212
 *
3213
 * @since 7.1
3214
 */
3215
PROJ_UNIT_INFO **proj_get_units_from_database(PJ_CONTEXT *ctx,
3216
                                              const char *auth_name,
3217
                                              const char *category,
3218
                                              int allow_deprecated,
3219
0
                                              int *out_result_count) {
3220
0
    SANITIZE_CTX(ctx);
3221
0
    PROJ_UNIT_INFO **ret = nullptr;
3222
0
    int i = 0;
3223
0
    try {
3224
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx),
3225
0
                                                auth_name ? auth_name : "");
3226
0
        auto list = factory->getUnitList();
3227
0
        ret = new PROJ_UNIT_INFO *[list.size() + 1];
3228
0
        for (const auto &info : list) {
3229
0
            if (category && info.category != category) {
3230
0
                continue;
3231
0
            }
3232
0
            if (!allow_deprecated && info.deprecated) {
3233
0
                continue;
3234
0
            }
3235
0
            ret[i] = new PROJ_UNIT_INFO;
3236
0
            ret[i]->auth_name = pj_strdup(info.authName.c_str());
3237
0
            ret[i]->code = pj_strdup(info.code.c_str());
3238
0
            ret[i]->name = pj_strdup(info.name.c_str());
3239
0
            ret[i]->category = pj_strdup(info.category.c_str());
3240
0
            ret[i]->conv_factor = info.convFactor;
3241
0
            ret[i]->proj_short_name =
3242
0
                info.projShortName.empty()
3243
0
                    ? nullptr
3244
0
                    : pj_strdup(info.projShortName.c_str());
3245
0
            ret[i]->deprecated = info.deprecated;
3246
0
            i++;
3247
0
        }
3248
0
        ret[i] = nullptr;
3249
0
        if (out_result_count)
3250
0
            *out_result_count = i;
3251
0
        return ret;
3252
0
    } catch (const std::exception &e) {
3253
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3254
0
        if (ret) {
3255
0
            ret[i + 1] = nullptr;
3256
0
            proj_unit_list_destroy(ret);
3257
0
        }
3258
0
        if (out_result_count)
3259
0
            *out_result_count = 0;
3260
0
    }
3261
0
    return nullptr;
3262
0
}
3263
3264
// ---------------------------------------------------------------------------
3265
3266
/** \brief Destroy the result returned by
3267
 * proj_get_units_from_database().
3268
 *
3269
 * @since 7.1
3270
 */
3271
0
void proj_unit_list_destroy(PROJ_UNIT_INFO **list) {
3272
0
    if (list) {
3273
0
        for (int i = 0; list[i] != nullptr; i++) {
3274
0
            free(list[i]->auth_name);
3275
0
            free(list[i]->code);
3276
0
            free(list[i]->name);
3277
0
            free(list[i]->category);
3278
0
            free(list[i]->proj_short_name);
3279
0
            delete list[i];
3280
0
        }
3281
0
        delete[] list;
3282
0
    }
3283
0
}
3284
3285
// ---------------------------------------------------------------------------
3286
3287
/** \brief Return the Conversion of a DerivedCRS (such as a ProjectedCRS),
3288
 * or the Transformation from the baseCRS to the hubCRS of a BoundCRS
3289
 *
3290
 * The returned object must be unreferenced with proj_destroy() after
3291
 * use.
3292
 * It should be used by at most one thread at a time.
3293
 *
3294
 * @param ctx PROJ context, or NULL for default context
3295
 * @param crs Object of type DerivedCRS or BoundCRSs (must not be NULL)
3296
 * @return Object of type SingleOperation that must be unreferenced with
3297
 * proj_destroy(), or NULL in case of error.
3298
 */
3299
0
PJ *proj_crs_get_coordoperation(PJ_CONTEXT *ctx, const PJ *crs) {
3300
0
    SANITIZE_CTX(ctx);
3301
0
    if (!crs) {
3302
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
3303
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
3304
0
        return nullptr;
3305
0
    }
3306
0
    SingleOperationPtr co;
3307
3308
0
    auto derivedCRS = dynamic_cast<const DerivedCRS *>(crs->iso_obj.get());
3309
0
    if (derivedCRS) {
3310
0
        co = derivedCRS->derivingConversion().as_nullable();
3311
0
    } else {
3312
0
        auto boundCRS = dynamic_cast<const BoundCRS *>(crs->iso_obj.get());
3313
0
        if (boundCRS) {
3314
0
            co = boundCRS->transformation().as_nullable();
3315
0
        } else {
3316
0
            proj_log_error(ctx, __FUNCTION__,
3317
0
                           "Object is not a DerivedCRS or BoundCRS");
3318
0
            return nullptr;
3319
0
        }
3320
0
    }
3321
3322
0
    return pj_obj_create(ctx, NN_NO_CHECK(co));
3323
0
}
3324
3325
// ---------------------------------------------------------------------------
3326
3327
/** \brief Return information on the operation method of the SingleOperation.
3328
 *
3329
 * @param ctx PROJ context, or NULL for default context
3330
 * @param coordoperation Object of type SingleOperation (typically a Conversion
3331
 * or Transformation) (must not be NULL)
3332
 * @param out_method_name Pointer to a string value to store the method
3333
 * (projection) name. or NULL
3334
 * @param out_method_auth_name Pointer to a string value to store the method
3335
 * authority name. or NULL
3336
 * @param out_method_code Pointer to a string value to store the method
3337
 * code. or NULL
3338
 * @return TRUE in case of success.
3339
 */
3340
int proj_coordoperation_get_method_info(PJ_CONTEXT *ctx,
3341
                                        const PJ *coordoperation,
3342
                                        const char **out_method_name,
3343
                                        const char **out_method_auth_name,
3344
0
                                        const char **out_method_code) {
3345
0
    SANITIZE_CTX(ctx);
3346
0
    if (!coordoperation) {
3347
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
3348
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
3349
0
        return false;
3350
0
    }
3351
0
    auto singleOp =
3352
0
        dynamic_cast<const SingleOperation *>(coordoperation->iso_obj.get());
3353
0
    if (!singleOp) {
3354
0
        proj_log_error(ctx, __FUNCTION__,
3355
0
                       "Object is not a DerivedCRS or BoundCRS");
3356
0
        return false;
3357
0
    }
3358
3359
0
    const auto &method = singleOp->method();
3360
0
    const auto &method_ids = method->identifiers();
3361
0
    if (out_method_name) {
3362
0
        *out_method_name = method->name()->description()->c_str();
3363
0
    }
3364
0
    if (out_method_auth_name) {
3365
0
        if (!method_ids.empty()) {
3366
0
            *out_method_auth_name = method_ids[0]->codeSpace()->c_str();
3367
0
        } else {
3368
0
            *out_method_auth_name = nullptr;
3369
0
        }
3370
0
    }
3371
0
    if (out_method_code) {
3372
0
        if (!method_ids.empty()) {
3373
0
            *out_method_code = method_ids[0]->code().c_str();
3374
0
        } else {
3375
0
            *out_method_code = nullptr;
3376
0
        }
3377
0
    }
3378
0
    return true;
3379
0
}
3380
3381
// ---------------------------------------------------------------------------
3382
3383
//! @cond Doxygen_Suppress
3384
static PropertyMap createPropertyMapName(const char *c_name,
3385
                                         const char *auth_name = nullptr,
3386
7.62k
                                         const char *code = nullptr) {
3387
7.62k
    std::string name(c_name ? c_name : "unnamed");
3388
7.62k
    PropertyMap properties;
3389
7.62k
    if (ends_with(name, " (deprecated)")) {
3390
0
        name.resize(name.size() - strlen(" (deprecated)"));
3391
0
        properties.set(common::IdentifiedObject::DEPRECATED_KEY, true);
3392
0
    }
3393
7.62k
    if (auth_name && code) {
3394
0
        properties.set(metadata::Identifier::CODESPACE_KEY, auth_name);
3395
0
        properties.set(metadata::Identifier::CODE_KEY, code);
3396
0
    }
3397
7.62k
    return properties.set(common::IdentifiedObject::NAME_KEY, name);
3398
7.62k
}
3399
3400
// ---------------------------------------------------------------------------
3401
3402
static UnitOfMeasure createLinearUnit(const char *name, double convFactor,
3403
                                      const char *unit_auth_name = nullptr,
3404
0
                                      const char *unit_code = nullptr) {
3405
0
    return name == nullptr
3406
0
               ? UnitOfMeasure::METRE
3407
0
               : UnitOfMeasure(name, convFactor, UnitOfMeasure::Type::LINEAR,
3408
0
                               unit_auth_name ? unit_auth_name : "",
3409
0
                               unit_code ? unit_code : "");
3410
0
}
3411
3412
// ---------------------------------------------------------------------------
3413
3414
static UnitOfMeasure createAngularUnit(const char *name, double convFactor,
3415
                                       const char *unit_auth_name = nullptr,
3416
5.08k
                                       const char *unit_code = nullptr) {
3417
5.08k
    return name ? (ci_equal(name, "degree") ? UnitOfMeasure::DEGREE
3418
0
                   : ci_equal(name, "grad")
3419
0
                       ? UnitOfMeasure::GRAD
3420
0
                       : UnitOfMeasure(name, convFactor,
3421
0
                                       UnitOfMeasure::Type::ANGULAR,
3422
0
                                       unit_auth_name ? unit_auth_name : "",
3423
0
                                       unit_code ? unit_code : ""))
3424
5.08k
                : UnitOfMeasure::DEGREE;
3425
5.08k
}
3426
3427
// ---------------------------------------------------------------------------
3428
3429
static GeodeticReferenceFrameNNPtr createGeodeticReferenceFrame(
3430
    PJ_CONTEXT *ctx, const char *datum_name, const char *ellps_name,
3431
    double semi_major_metre, double inv_flattening,
3432
    const char *prime_meridian_name, double prime_meridian_offset,
3433
2.54k
    const char *angular_units, double angular_units_conv) {
3434
2.54k
    const UnitOfMeasure angUnit(
3435
2.54k
        createAngularUnit(angular_units, angular_units_conv));
3436
2.54k
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
3437
2.54k
    auto body = Ellipsoid::guessBodyName(dbContext, semi_major_metre);
3438
2.54k
    auto ellpsName = createPropertyMapName(ellps_name);
3439
2.54k
    auto ellps = inv_flattening != 0.0
3440
2.54k
                     ? Ellipsoid::createFlattenedSphere(
3441
2.51k
                           ellpsName, Length(semi_major_metre),
3442
2.51k
                           Scale(inv_flattening), body)
3443
2.54k
                     : Ellipsoid::createSphere(ellpsName,
3444
25
                                               Length(semi_major_metre), body);
3445
2.54k
    auto pm = PrimeMeridian::create(
3446
2.54k
        PropertyMap().set(
3447
2.54k
            common::IdentifiedObject::NAME_KEY,
3448
2.54k
            prime_meridian_name ? prime_meridian_name
3449
2.54k
            : prime_meridian_offset == 0.0
3450
0
                ? (ellps->celestialBody() == Ellipsoid::EARTH
3451
0
                       ? PrimeMeridian::GREENWICH->nameStr().c_str()
3452
0
                       : PrimeMeridian::REFERENCE_MERIDIAN->nameStr().c_str())
3453
0
                : "unnamed"),
3454
2.54k
        Angle(prime_meridian_offset, angUnit));
3455
3456
2.54k
    std::string datumName(datum_name ? datum_name : "unnamed");
3457
2.54k
    if (datumName == "WGS_1984") {
3458
0
        datumName = GeodeticReferenceFrame::EPSG_6326->nameStr();
3459
2.54k
    } else if (datumName.find('_') != std::string::npos) {
3460
        // Likely coming from WKT1
3461
0
        if (dbContext) {
3462
0
            auto authFactory =
3463
0
                AuthorityFactory::create(NN_NO_CHECK(dbContext), std::string());
3464
0
            auto res = authFactory->createObjectsFromName(
3465
0
                datumName,
3466
0
                {AuthorityFactory::ObjectType::GEODETIC_REFERENCE_FRAME}, true,
3467
0
                1);
3468
0
            if (!res.empty()) {
3469
0
                const auto &refDatum = res.front();
3470
0
                if (metadata::Identifier::isEquivalentName(
3471
0
                        datumName.c_str(), refDatum->nameStr().c_str())) {
3472
0
                    datumName = refDatum->nameStr();
3473
0
                } else if (refDatum->identifiers().size() == 1) {
3474
0
                    const auto &id = refDatum->identifiers()[0];
3475
0
                    const auto aliases =
3476
0
                        authFactory->databaseContext()->getAliases(
3477
0
                            *id->codeSpace(), id->code(), refDatum->nameStr(),
3478
0
                            "geodetic_datum", std::string());
3479
0
                    for (const auto &alias : aliases) {
3480
0
                        if (metadata::Identifier::isEquivalentName(
3481
0
                                datumName.c_str(), alias.c_str())) {
3482
0
                            datumName = refDatum->nameStr();
3483
0
                            break;
3484
0
                        }
3485
0
                    }
3486
0
                }
3487
0
            }
3488
0
        }
3489
0
    }
3490
3491
2.54k
    return GeodeticReferenceFrame::create(
3492
2.54k
        createPropertyMapName(datumName.c_str()), ellps,
3493
2.54k
        util::optional<std::string>(), pm);
3494
2.54k
}
3495
3496
//! @endcond
3497
3498
// ---------------------------------------------------------------------------
3499
3500
/** \brief Create a GeographicCRS.
3501
 *
3502
 * The returned object must be unreferenced with proj_destroy() after
3503
 * use.
3504
 * It should be used by at most one thread at a time.
3505
 *
3506
 * @param ctx PROJ context, or NULL for default context
3507
 * @param crs_name Name of the GeographicCRS. Or NULL
3508
 * @param datum_name Name of the GeodeticReferenceFrame. Or NULL
3509
 * @param ellps_name Name of the Ellipsoid. Or NULL
3510
 * @param semi_major_metre Ellipsoid semi-major axis, in metres.
3511
 * @param inv_flattening Ellipsoid inverse flattening. Or 0 for a sphere.
3512
 * @param prime_meridian_name Name of the PrimeMeridian. Or NULL
3513
 * @param prime_meridian_offset Offset of the prime meridian, expressed in the
3514
 * specified angular units.
3515
 * @param pm_angular_units Name of the angular units. Or NULL for Degree
3516
 * @param pm_angular_units_conv Conversion factor from the angular unit to
3517
 * radian.
3518
 * Or
3519
 * 0 for Degree if pm_angular_units == NULL. Otherwise should be not NULL
3520
 * @param ellipsoidal_cs Coordinate system. Must not be NULL.
3521
 *
3522
 * @return Object of type GeographicCRS that must be unreferenced with
3523
 * proj_destroy(), or NULL in case of error.
3524
 */
3525
PJ *proj_create_geographic_crs(PJ_CONTEXT *ctx, const char *crs_name,
3526
                               const char *datum_name, const char *ellps_name,
3527
                               double semi_major_metre, double inv_flattening,
3528
                               const char *prime_meridian_name,
3529
                               double prime_meridian_offset,
3530
                               const char *pm_angular_units,
3531
                               double pm_angular_units_conv,
3532
2.54k
                               const PJ *ellipsoidal_cs) {
3533
3534
2.54k
    SANITIZE_CTX(ctx);
3535
2.54k
    auto cs = std::dynamic_pointer_cast<EllipsoidalCS>(ellipsoidal_cs->iso_obj);
3536
2.54k
    if (!cs) {
3537
0
        return nullptr;
3538
0
    }
3539
2.54k
    try {
3540
2.54k
        auto datum = createGeodeticReferenceFrame(
3541
2.54k
            ctx, datum_name, ellps_name, semi_major_metre, inv_flattening,
3542
2.54k
            prime_meridian_name, prime_meridian_offset, pm_angular_units,
3543
2.54k
            pm_angular_units_conv);
3544
2.54k
        auto geogCRS = GeographicCRS::create(createPropertyMapName(crs_name),
3545
2.54k
                                             datum, NN_NO_CHECK(cs));
3546
2.54k
        return pj_obj_create(ctx, geogCRS);
3547
2.54k
    } catch (const std::exception &e) {
3548
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3549
0
    }
3550
0
    return nullptr;
3551
2.54k
}
3552
3553
// ---------------------------------------------------------------------------
3554
3555
/** \brief Create a GeographicCRS.
3556
 *
3557
 * The returned object must be unreferenced with proj_destroy() after
3558
 * use.
3559
 * It should be used by at most one thread at a time.
3560
 *
3561
 * @param ctx PROJ context, or NULL for default context
3562
 * @param crs_name Name of the GeographicCRS. Or NULL
3563
 * @param datum_or_datum_ensemble Datum or DatumEnsemble (DatumEnsemble possible
3564
 * since 7.2). Must not be NULL.
3565
 * @param ellipsoidal_cs Coordinate system. Must not be NULL.
3566
 *
3567
 * @return Object of type GeographicCRS that must be unreferenced with
3568
 * proj_destroy(), or NULL in case of error.
3569
 */
3570
PJ *proj_create_geographic_crs_from_datum(PJ_CONTEXT *ctx, const char *crs_name,
3571
                                          const PJ *datum_or_datum_ensemble,
3572
0
                                          const PJ *ellipsoidal_cs) {
3573
3574
0
    SANITIZE_CTX(ctx);
3575
0
    if (datum_or_datum_ensemble == nullptr) {
3576
0
        proj_log_error(ctx, __FUNCTION__,
3577
0
                       "Missing input datum_or_datum_ensemble");
3578
0
        return nullptr;
3579
0
    }
3580
0
    auto l_datum = std::dynamic_pointer_cast<GeodeticReferenceFrame>(
3581
0
        datum_or_datum_ensemble->iso_obj);
3582
0
    auto l_datum_ensemble = std::dynamic_pointer_cast<DatumEnsemble>(
3583
0
        datum_or_datum_ensemble->iso_obj);
3584
0
    auto cs = std::dynamic_pointer_cast<EllipsoidalCS>(ellipsoidal_cs->iso_obj);
3585
0
    if (!cs) {
3586
0
        return nullptr;
3587
0
    }
3588
0
    try {
3589
0
        auto geogCRS =
3590
0
            GeographicCRS::create(createPropertyMapName(crs_name), l_datum,
3591
0
                                  l_datum_ensemble, NN_NO_CHECK(cs));
3592
0
        return pj_obj_create(ctx, geogCRS);
3593
0
    } catch (const std::exception &e) {
3594
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3595
0
    }
3596
0
    return nullptr;
3597
0
}
3598
3599
// ---------------------------------------------------------------------------
3600
3601
/** \brief Create a GeodeticCRS of geocentric type.
3602
 *
3603
 * The returned object must be unreferenced with proj_destroy() after
3604
 * use.
3605
 * It should be used by at most one thread at a time.
3606
 *
3607
 * @param ctx PROJ context, or NULL for default context
3608
 * @param crs_name Name of the GeographicCRS. Or NULL
3609
 * @param datum_name Name of the GeodeticReferenceFrame. Or NULL
3610
 * @param ellps_name Name of the Ellipsoid. Or NULL
3611
 * @param semi_major_metre Ellipsoid semi-major axis, in metres.
3612
 * @param inv_flattening Ellipsoid inverse flattening. Or 0 for a sphere.
3613
 * @param prime_meridian_name Name of the PrimeMeridian. Or NULL
3614
 * @param prime_meridian_offset Offset of the prime meridian, expressed in the
3615
 * specified angular units.
3616
 * @param angular_units Name of the angular units. Or NULL for Degree
3617
 * @param angular_units_conv Conversion factor from the angular unit to radian.
3618
 * Or
3619
 * 0 for Degree if angular_units == NULL. Otherwise should be not NULL
3620
 * @param linear_units Name of the linear units. Or NULL for Metre
3621
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
3622
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
3623
 *
3624
 * @return Object of type GeodeticCRS that must be unreferenced with
3625
 * proj_destroy(), or NULL in case of error.
3626
 */
3627
PJ *proj_create_geocentric_crs(
3628
    PJ_CONTEXT *ctx, const char *crs_name, const char *datum_name,
3629
    const char *ellps_name, double semi_major_metre, double inv_flattening,
3630
    const char *prime_meridian_name, double prime_meridian_offset,
3631
    const char *angular_units, double angular_units_conv,
3632
0
    const char *linear_units, double linear_units_conv) {
3633
3634
0
    SANITIZE_CTX(ctx);
3635
0
    try {
3636
0
        const UnitOfMeasure linearUnit(
3637
0
            createLinearUnit(linear_units, linear_units_conv));
3638
0
        auto datum = createGeodeticReferenceFrame(
3639
0
            ctx, datum_name, ellps_name, semi_major_metre, inv_flattening,
3640
0
            prime_meridian_name, prime_meridian_offset, angular_units,
3641
0
            angular_units_conv);
3642
3643
0
        auto geodCRS =
3644
0
            GeodeticCRS::create(createPropertyMapName(crs_name), datum,
3645
0
                                cs::CartesianCS::createGeocentric(linearUnit));
3646
0
        return pj_obj_create(ctx, geodCRS);
3647
0
    } catch (const std::exception &e) {
3648
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3649
0
    }
3650
0
    return nullptr;
3651
0
}
3652
3653
// ---------------------------------------------------------------------------
3654
3655
/** \brief Create a GeodeticCRS of geocentric type.
3656
 *
3657
 * The returned object must be unreferenced with proj_destroy() after
3658
 * use.
3659
 * It should be used by at most one thread at a time.
3660
 *
3661
 * @param ctx PROJ context, or NULL for default context
3662
 * @param crs_name Name of the GeographicCRS. Or NULL
3663
 * @param datum_or_datum_ensemble Datum or DatumEnsemble (DatumEnsemble possible
3664
 * since 7.2). Must not be NULL.
3665
 * @param linear_units Name of the linear units. Or NULL for Metre
3666
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
3667
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
3668
 *
3669
 * @return Object of type GeodeticCRS that must be unreferenced with
3670
 * proj_destroy(), or NULL in case of error.
3671
 */
3672
PJ *proj_create_geocentric_crs_from_datum(PJ_CONTEXT *ctx, const char *crs_name,
3673
                                          const PJ *datum_or_datum_ensemble,
3674
                                          const char *linear_units,
3675
0
                                          double linear_units_conv) {
3676
0
    SANITIZE_CTX(ctx);
3677
0
    if (datum_or_datum_ensemble == nullptr) {
3678
0
        proj_log_error(ctx, __FUNCTION__,
3679
0
                       "Missing input datum_or_datum_ensemble");
3680
0
        return nullptr;
3681
0
    }
3682
0
    auto l_datum = std::dynamic_pointer_cast<GeodeticReferenceFrame>(
3683
0
        datum_or_datum_ensemble->iso_obj);
3684
0
    auto l_datum_ensemble = std::dynamic_pointer_cast<DatumEnsemble>(
3685
0
        datum_or_datum_ensemble->iso_obj);
3686
0
    try {
3687
0
        const UnitOfMeasure linearUnit(
3688
0
            createLinearUnit(linear_units, linear_units_conv));
3689
0
        auto geodCRS = GeodeticCRS::create(
3690
0
            createPropertyMapName(crs_name), l_datum, l_datum_ensemble,
3691
0
            cs::CartesianCS::createGeocentric(linearUnit));
3692
0
        return pj_obj_create(ctx, geodCRS);
3693
0
    } catch (const std::exception &e) {
3694
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3695
0
    }
3696
0
    return nullptr;
3697
0
}
3698
3699
// ---------------------------------------------------------------------------
3700
3701
/** \brief Create a DerivedGeograhicCRS.
3702
 *
3703
 * The returned object must be unreferenced with proj_destroy() after
3704
 * use.
3705
 * It should be used by at most one thread at a time.
3706
 *
3707
 * @param ctx PROJ context, or NULL for default context
3708
 * @param crs_name Name of the GeographicCRS. Or NULL
3709
 * @param base_geographic_crs Base Geographic CRS. Must not be NULL.
3710
 * @param conversion Conversion from the base Geographic to the
3711
 * DerivedGeograhicCRS. Must not be NULL.
3712
 * @param ellipsoidal_cs Coordinate system. Must not be NULL.
3713
 *
3714
 * @return Object of type GeodeticCRS that must be unreferenced with
3715
 * proj_destroy(), or NULL in case of error.
3716
 *
3717
 * @since 7.0
3718
 */
3719
PJ *proj_create_derived_geographic_crs(PJ_CONTEXT *ctx, const char *crs_name,
3720
                                       const PJ *base_geographic_crs,
3721
                                       const PJ *conversion,
3722
0
                                       const PJ *ellipsoidal_cs) {
3723
0
    SANITIZE_CTX(ctx);
3724
0
    auto base_crs =
3725
0
        std::dynamic_pointer_cast<GeographicCRS>(base_geographic_crs->iso_obj);
3726
0
    auto conversion_cpp =
3727
0
        std::dynamic_pointer_cast<Conversion>(conversion->iso_obj);
3728
0
    auto cs = std::dynamic_pointer_cast<EllipsoidalCS>(ellipsoidal_cs->iso_obj);
3729
0
    if (!base_crs || !conversion_cpp || !cs) {
3730
0
        return nullptr;
3731
0
    }
3732
0
    try {
3733
0
        auto derivedCRS = DerivedGeographicCRS::create(
3734
0
            createPropertyMapName(crs_name), NN_NO_CHECK(base_crs),
3735
0
            NN_NO_CHECK(conversion_cpp), NN_NO_CHECK(cs));
3736
0
        return pj_obj_create(ctx, derivedCRS);
3737
0
    } catch (const std::exception &e) {
3738
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3739
0
    }
3740
0
    return nullptr;
3741
0
}
3742
3743
// ---------------------------------------------------------------------------
3744
3745
/** \brief Return whether a CRS is a Derived CRS.
3746
 *
3747
 * @param ctx PROJ context, or NULL for default context
3748
 * @param crs CRS. Must not be NULL.
3749
 *
3750
 * @return whether a CRS is a Derived CRS.
3751
 *
3752
 * @since 7.0
3753
 */
3754
0
int proj_is_derived_crs(PJ_CONTEXT *ctx, const PJ *crs) {
3755
0
    SANITIZE_CTX(ctx);
3756
0
    return dynamic_cast<DerivedCRS *>(crs->iso_obj.get()) != nullptr;
3757
0
}
3758
3759
// ---------------------------------------------------------------------------
3760
3761
/** \brief Create a VerticalCRS
3762
 *
3763
 * The returned object must be unreferenced with proj_destroy() after
3764
 * use.
3765
 * It should be used by at most one thread at a time.
3766
 *
3767
 * @param ctx PROJ context, or NULL for default context
3768
 * @param crs_name Name of the GeographicCRS. Or NULL
3769
 * @param datum_name Name of the VerticalReferenceFrame. Or NULL
3770
 * @param linear_units Name of the linear units. Or NULL for Metre
3771
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
3772
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
3773
 *
3774
 * @return Object of type VerticalCRS that must be unreferenced with
3775
 * proj_destroy(), or NULL in case of error.
3776
 */
3777
PJ *proj_create_vertical_crs(PJ_CONTEXT *ctx, const char *crs_name,
3778
                             const char *datum_name, const char *linear_units,
3779
0
                             double linear_units_conv) {
3780
3781
0
    return proj_create_vertical_crs_ex(
3782
0
        ctx, crs_name, datum_name, nullptr, nullptr, linear_units,
3783
0
        linear_units_conv, nullptr, nullptr, nullptr, nullptr, nullptr);
3784
0
}
3785
3786
// ---------------------------------------------------------------------------
3787
3788
/** \brief Create a VerticalCRS
3789
 *
3790
 * The returned object must be unreferenced with proj_destroy() after
3791
 * use.
3792
 * It should be used by at most one thread at a time.
3793
 *
3794
 * This is an extended (_ex) version of proj_create_vertical_crs() that adds
3795
 * the capability of defining a geoid model.
3796
 *
3797
 * @param ctx PROJ context, or NULL for default context
3798
 * @param crs_name Name of the GeographicCRS. Or NULL
3799
 * @param datum_name Name of the VerticalReferenceFrame. Or NULL
3800
 * @param datum_auth_name Authority name of the VerticalReferenceFrame. Or NULL
3801
 * @param datum_code Code of the VerticalReferenceFrame. Or NULL
3802
 * @param linear_units Name of the linear units. Or NULL for Metre
3803
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
3804
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
3805
 * @param geoid_model_name Geoid model name, or NULL. Can be a name from the
3806
 * geoid_model name or a string "PROJ foo.gtx"
3807
 * @param geoid_model_auth_name Authority name of the transformation for
3808
 * the geoid model. or NULL
3809
 * @param geoid_model_code Code of the transformation for
3810
 * the geoid model. or NULL
3811
 * @param geoid_geog_crs Geographic CRS for the geoid transformation, or NULL.
3812
 * @param options NULL-terminated list of strings with "KEY=VALUE" format. or
3813
 * NULL.
3814
 * The currently recognized option is ACCURACY=value, where value is in metre.
3815
 * @return Object of type VerticalCRS that must be unreferenced with
3816
 * proj_destroy(), or NULL in case of error.
3817
 */
3818
PJ *proj_create_vertical_crs_ex(
3819
    PJ_CONTEXT *ctx, const char *crs_name, const char *datum_name,
3820
    const char *datum_auth_name, const char *datum_code,
3821
    const char *linear_units, double linear_units_conv,
3822
    const char *geoid_model_name, const char *geoid_model_auth_name,
3823
    const char *geoid_model_code, const PJ *geoid_geog_crs,
3824
0
    const char *const *options) {
3825
0
    SANITIZE_CTX(ctx);
3826
0
    try {
3827
0
        const UnitOfMeasure linearUnit(
3828
0
            createLinearUnit(linear_units, linear_units_conv));
3829
0
        auto datum = VerticalReferenceFrame::create(
3830
0
            createPropertyMapName(datum_name, datum_auth_name, datum_code));
3831
0
        auto props = createPropertyMapName(crs_name);
3832
0
        auto cs = cs::VerticalCS::createGravityRelatedHeight(linearUnit);
3833
0
        if (geoid_model_name) {
3834
0
            auto propsModel = createPropertyMapName(
3835
0
                geoid_model_name, geoid_model_auth_name, geoid_model_code);
3836
0
            const auto vertCRSWithoutGeoid =
3837
0
                VerticalCRS::create(props, datum, cs);
3838
0
            const auto interpCRS =
3839
0
                geoid_geog_crs && std::dynamic_pointer_cast<GeographicCRS>(
3840
0
                                      geoid_geog_crs->iso_obj)
3841
0
                    ? std::dynamic_pointer_cast<CRS>(geoid_geog_crs->iso_obj)
3842
0
                    : nullptr;
3843
3844
0
            std::vector<metadata::PositionalAccuracyNNPtr> accuracies;
3845
0
            for (auto iter = options; iter && iter[0]; ++iter) {
3846
0
                const char *value;
3847
0
                if ((value = getOptionValue(*iter, "ACCURACY="))) {
3848
0
                    accuracies.emplace_back(
3849
0
                        metadata::PositionalAccuracy::create(value));
3850
0
                }
3851
0
            }
3852
0
            const auto model(Transformation::create(
3853
0
                propsModel, vertCRSWithoutGeoid,
3854
0
                GeographicCRS::EPSG_4979, // arbitrarily chosen. Ignored
3855
0
                interpCRS,
3856
0
                OperationMethod::create(PropertyMap(),
3857
0
                                        std::vector<OperationParameterNNPtr>()),
3858
0
                {}, accuracies));
3859
0
            props.set("GEOID_MODEL", model);
3860
0
        }
3861
0
        auto vertCRS = VerticalCRS::create(props, datum, cs);
3862
0
        return pj_obj_create(ctx, vertCRS);
3863
0
    } catch (const std::exception &e) {
3864
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3865
0
    }
3866
0
    return nullptr;
3867
0
}
3868
3869
// ---------------------------------------------------------------------------
3870
3871
/** \brief Create a CompoundCRS
3872
 *
3873
 * The returned object must be unreferenced with proj_destroy() after
3874
 * use.
3875
 * It should be used by at most one thread at a time.
3876
 *
3877
 * @param ctx PROJ context, or NULL for default context
3878
 * @param crs_name Name of the GeographicCRS. Or NULL
3879
 * @param horiz_crs Horizontal CRS. must not be NULL.
3880
 * @param vert_crs Vertical CRS. must not be NULL.
3881
 *
3882
 * @return Object of type CompoundCRS that must be unreferenced with
3883
 * proj_destroy(), or NULL in case of error.
3884
 */
3885
PJ *proj_create_compound_crs(PJ_CONTEXT *ctx, const char *crs_name,
3886
0
                             const PJ *horiz_crs, const PJ *vert_crs) {
3887
3888
0
    SANITIZE_CTX(ctx);
3889
0
    if (!horiz_crs || !vert_crs) {
3890
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
3891
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
3892
0
        return nullptr;
3893
0
    }
3894
0
    auto l_horiz_crs = std::dynamic_pointer_cast<CRS>(horiz_crs->iso_obj);
3895
0
    if (!l_horiz_crs) {
3896
0
        return nullptr;
3897
0
    }
3898
0
    auto l_vert_crs = std::dynamic_pointer_cast<CRS>(vert_crs->iso_obj);
3899
0
    if (!l_vert_crs) {
3900
0
        return nullptr;
3901
0
    }
3902
0
    try {
3903
0
        auto compoundCRS = CompoundCRS::create(
3904
0
            createPropertyMapName(crs_name),
3905
0
            {NN_NO_CHECK(l_horiz_crs), NN_NO_CHECK(l_vert_crs)});
3906
0
        return pj_obj_create(ctx, compoundCRS);
3907
0
    } catch (const std::exception &e) {
3908
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3909
0
    }
3910
0
    return nullptr;
3911
0
}
3912
3913
// ---------------------------------------------------------------------------
3914
3915
/** \brief Return a copy of the object with its name changed
3916
 *
3917
 * Currently, only implemented on CRS objects.
3918
 *
3919
 * The returned object must be unreferenced with proj_destroy() after
3920
 * use.
3921
 * It should be used by at most one thread at a time.
3922
 *
3923
 * @param ctx PROJ context, or NULL for default context
3924
 * @param obj Object of type CRS. Must not be NULL
3925
 * @param name New name. Must not be NULL
3926
 *
3927
 * @return Object that must be unreferenced with
3928
 * proj_destroy(), or NULL in case of error.
3929
 */
3930
0
PJ *proj_alter_name(PJ_CONTEXT *ctx, const PJ *obj, const char *name) {
3931
0
    SANITIZE_CTX(ctx);
3932
0
    if (!obj || !name) {
3933
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
3934
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
3935
0
        return nullptr;
3936
0
    }
3937
0
    auto crs = dynamic_cast<const CRS *>(obj->iso_obj.get());
3938
0
    if (!crs) {
3939
0
        return nullptr;
3940
0
    }
3941
0
    try {
3942
0
        return pj_obj_create(ctx, crs->alterName(name));
3943
0
    } catch (const std::exception &e) {
3944
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3945
0
    }
3946
0
    return nullptr;
3947
0
}
3948
3949
// ---------------------------------------------------------------------------
3950
3951
/** \brief Return a copy of the object with its identifier changed/set
3952
 *
3953
 * Currently, only implemented on CRS objects.
3954
 *
3955
 * The returned object must be unreferenced with proj_destroy() after
3956
 * use.
3957
 * It should be used by at most one thread at a time.
3958
 *
3959
 * @param ctx PROJ context, or NULL for default context
3960
 * @param obj Object of type CRS. Must not be NULL
3961
 * @param auth_name Authority name. Must not be NULL
3962
 * @param code Code. Must not be NULL
3963
 *
3964
 * @return Object that must be unreferenced with
3965
 * proj_destroy(), or NULL in case of error.
3966
 */
3967
PJ *proj_alter_id(PJ_CONTEXT *ctx, const PJ *obj, const char *auth_name,
3968
0
                  const char *code) {
3969
0
    SANITIZE_CTX(ctx);
3970
0
    if (!obj || !auth_name || !code) {
3971
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
3972
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
3973
0
        return nullptr;
3974
0
    }
3975
0
    auto crs = dynamic_cast<const CRS *>(obj->iso_obj.get());
3976
0
    if (!crs) {
3977
0
        return nullptr;
3978
0
    }
3979
0
    try {
3980
0
        return pj_obj_create(ctx, crs->alterId(auth_name, code));
3981
0
    } catch (const std::exception &e) {
3982
0
        proj_log_error(ctx, __FUNCTION__, e.what());
3983
0
    }
3984
0
    return nullptr;
3985
0
}
3986
3987
// ---------------------------------------------------------------------------
3988
3989
/** \brief Return a copy of the CRS with its geodetic CRS changed
3990
 *
3991
 * Currently, when obj is a GeodeticCRS, it returns a clone of new_geod_crs
3992
 * When obj is a ProjectedCRS, it replaces its base CRS with new_geod_crs.
3993
 * When obj is a CompoundCRS, it replaces the GeodeticCRS part of the horizontal
3994
 * CRS with new_geod_crs.
3995
 * In other cases, it returns a clone of obj.
3996
 *
3997
 * The returned object must be unreferenced with proj_destroy() after
3998
 * use.
3999
 * It should be used by at most one thread at a time.
4000
 *
4001
 * @param ctx PROJ context, or NULL for default context
4002
 * @param obj Object of type CRS. Must not be NULL
4003
 * @param new_geod_crs Object of type GeodeticCRS. Must not be NULL
4004
 *
4005
 * @return Object that must be unreferenced with
4006
 * proj_destroy(), or NULL in case of error.
4007
 */
4008
PJ *proj_crs_alter_geodetic_crs(PJ_CONTEXT *ctx, const PJ *obj,
4009
0
                                const PJ *new_geod_crs) {
4010
0
    SANITIZE_CTX(ctx);
4011
0
    if (!obj || !new_geod_crs) {
4012
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4013
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4014
0
        return nullptr;
4015
0
    }
4016
0
    auto l_new_geod_crs =
4017
0
        std::dynamic_pointer_cast<GeodeticCRS>(new_geod_crs->iso_obj);
4018
0
    if (!l_new_geod_crs) {
4019
0
        proj_log_error(ctx, __FUNCTION__, "new_geod_crs is not a GeodeticCRS");
4020
0
        return nullptr;
4021
0
    }
4022
4023
0
    auto crs = dynamic_cast<const CRS *>(obj->iso_obj.get());
4024
0
    if (!crs) {
4025
0
        proj_log_error(ctx, __FUNCTION__, "obj is not a CRS");
4026
0
        return nullptr;
4027
0
    }
4028
4029
0
    try {
4030
0
        return pj_obj_create(
4031
0
            ctx, crs->alterGeodeticCRS(NN_NO_CHECK(l_new_geod_crs)));
4032
0
    } catch (const std::exception &e) {
4033
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4034
0
        return nullptr;
4035
0
    }
4036
0
}
4037
4038
// ---------------------------------------------------------------------------
4039
4040
/** \brief Return a copy of the CRS with its angular units changed
4041
 *
4042
 * The CRS must be or contain a GeographicCRS.
4043
 *
4044
 * The returned object must be unreferenced with proj_destroy() after
4045
 * use.
4046
 * It should be used by at most one thread at a time.
4047
 *
4048
 * @param ctx PROJ context, or NULL for default context
4049
 * @param obj Object of type CRS. Must not be NULL
4050
 * @param angular_units Name of the angular units. Or NULL for Degree
4051
 * @param angular_units_conv Conversion factor from the angular unit to radian.
4052
 * Or 0 for Degree if angular_units == NULL. Otherwise should be not NULL
4053
 * @param unit_auth_name Unit authority name. Or NULL.
4054
 * @param unit_code Unit code. Or NULL.
4055
 *
4056
 * @return Object that must be unreferenced with
4057
 * proj_destroy(), or NULL in case of error.
4058
 */
4059
PJ *proj_crs_alter_cs_angular_unit(PJ_CONTEXT *ctx, const PJ *obj,
4060
                                   const char *angular_units,
4061
                                   double angular_units_conv,
4062
                                   const char *unit_auth_name,
4063
0
                                   const char *unit_code) {
4064
4065
0
    SANITIZE_CTX(ctx);
4066
0
    auto geodCRS = proj_crs_get_geodetic_crs(ctx, obj);
4067
0
    if (!geodCRS) {
4068
0
        return nullptr;
4069
0
    }
4070
0
    auto geogCRS = dynamic_cast<const GeographicCRS *>(geodCRS->iso_obj.get());
4071
0
    if (!geogCRS) {
4072
0
        proj_destroy(geodCRS);
4073
0
        return nullptr;
4074
0
    }
4075
4076
0
    PJ *geogCRSAltered = nullptr;
4077
0
    try {
4078
0
        const UnitOfMeasure angUnit(createAngularUnit(
4079
0
            angular_units, angular_units_conv, unit_auth_name, unit_code));
4080
0
        geogCRSAltered = pj_obj_create(
4081
0
            ctx, GeographicCRS::create(
4082
0
                     createPropertyMapName(proj_get_name(geodCRS)),
4083
0
                     geogCRS->datum(), geogCRS->datumEnsemble(),
4084
0
                     geogCRS->coordinateSystem()->alterAngularUnit(angUnit)));
4085
0
        proj_destroy(geodCRS);
4086
0
    } catch (const std::exception &e) {
4087
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4088
0
        proj_destroy(geodCRS);
4089
0
        return nullptr;
4090
0
    }
4091
4092
0
    auto ret = proj_crs_alter_geodetic_crs(ctx, obj, geogCRSAltered);
4093
0
    proj_destroy(geogCRSAltered);
4094
0
    return ret;
4095
0
}
4096
4097
// ---------------------------------------------------------------------------
4098
4099
/** \brief Return a copy of the CRS with the linear units of its coordinate
4100
 * system changed
4101
 *
4102
 * The CRS must be or contain a ProjectedCRS, VerticalCRS or a GeocentricCRS.
4103
 *
4104
 * The returned object must be unreferenced with proj_destroy() after
4105
 * use.
4106
 * It should be used by at most one thread at a time.
4107
 *
4108
 * @param ctx PROJ context, or NULL for default context
4109
 * @param obj Object of type CRS. Must not be NULL
4110
 * @param linear_units Name of the linear units. Or NULL for Metre
4111
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
4112
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
4113
 * @param unit_auth_name Unit authority name. Or NULL.
4114
 * @param unit_code Unit code. Or NULL.
4115
 *
4116
 * @return Object that must be unreferenced with
4117
 * proj_destroy(), or NULL in case of error.
4118
 */
4119
PJ *proj_crs_alter_cs_linear_unit(PJ_CONTEXT *ctx, const PJ *obj,
4120
                                  const char *linear_units,
4121
                                  double linear_units_conv,
4122
                                  const char *unit_auth_name,
4123
0
                                  const char *unit_code) {
4124
0
    SANITIZE_CTX(ctx);
4125
0
    if (!obj) {
4126
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4127
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4128
0
        return nullptr;
4129
0
    }
4130
0
    auto crs = dynamic_cast<const CRS *>(obj->iso_obj.get());
4131
0
    if (!crs) {
4132
0
        return nullptr;
4133
0
    }
4134
4135
0
    try {
4136
0
        const UnitOfMeasure linearUnit(createLinearUnit(
4137
0
            linear_units, linear_units_conv, unit_auth_name, unit_code));
4138
0
        return pj_obj_create(ctx, crs->alterCSLinearUnit(linearUnit));
4139
0
    } catch (const std::exception &e) {
4140
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4141
0
        return nullptr;
4142
0
    }
4143
0
}
4144
4145
// ---------------------------------------------------------------------------
4146
4147
/** \brief Return a copy of the CRS with the linear units of the parameters
4148
 * of its conversion modified.
4149
 *
4150
 * The CRS must be or contain a ProjectedCRS, VerticalCRS or a GeocentricCRS.
4151
 *
4152
 * The returned object must be unreferenced with proj_destroy() after
4153
 * use.
4154
 * It should be used by at most one thread at a time.
4155
 *
4156
 * @param ctx PROJ context, or NULL for default context
4157
 * @param obj Object of type ProjectedCRS. Must not be NULL
4158
 * @param linear_units Name of the linear units. Or NULL for Metre
4159
 * @param linear_units_conv Conversion factor from the linear unit to metre. Or
4160
 * 0 for Metre if linear_units == NULL. Otherwise should be not NULL
4161
 * @param unit_auth_name Unit authority name. Or NULL.
4162
 * @param unit_code Unit code. Or NULL.
4163
 * @param convert_to_new_unit TRUE if existing values should be converted from
4164
 * their current unit to the new unit. If FALSE, their value will be left
4165
 * unchanged and the unit overridden (so the resulting CRS will not be
4166
 * equivalent to the original one for reprojection purposes).
4167
 *
4168
 * @return Object that must be unreferenced with
4169
 * proj_destroy(), or NULL in case of error.
4170
 */
4171
PJ *proj_crs_alter_parameters_linear_unit(PJ_CONTEXT *ctx, const PJ *obj,
4172
                                          const char *linear_units,
4173
                                          double linear_units_conv,
4174
                                          const char *unit_auth_name,
4175
                                          const char *unit_code,
4176
0
                                          int convert_to_new_unit) {
4177
0
    SANITIZE_CTX(ctx);
4178
0
    if (!obj) {
4179
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4180
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4181
0
        return nullptr;
4182
0
    }
4183
0
    auto crs = dynamic_cast<const ProjectedCRS *>(obj->iso_obj.get());
4184
0
    if (!crs) {
4185
0
        return nullptr;
4186
0
    }
4187
4188
0
    try {
4189
0
        const UnitOfMeasure linearUnit(createLinearUnit(
4190
0
            linear_units, linear_units_conv, unit_auth_name, unit_code));
4191
0
        return pj_obj_create(ctx, crs->alterParametersLinearUnit(
4192
0
                                      linearUnit, convert_to_new_unit == TRUE));
4193
0
    } catch (const std::exception &e) {
4194
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4195
0
        return nullptr;
4196
0
    }
4197
0
}
4198
4199
// ---------------------------------------------------------------------------
4200
4201
/** \brief Create a 3D CRS from an existing 2D CRS.
4202
 *
4203
 * The new axis will be ellipsoidal height, oriented upwards, and with metre
4204
 * units.
4205
 *
4206
 * See osgeo::proj::crs::CRS::promoteTo3D().
4207
 *
4208
 * The returned object must be unreferenced with proj_destroy() after
4209
 * use.
4210
 * It should be used by at most one thread at a time.
4211
 *
4212
 * @param ctx PROJ context, or NULL for default context
4213
 * @param crs_3D_name CRS name. Or NULL (in which case the name of crs_2D
4214
 * will be used)
4215
 * @param crs_2D 2D CRS to be "promoted" to 3D. Must not be NULL.
4216
 *
4217
 * @return Object that must be unreferenced with
4218
 * proj_destroy(), or NULL in case of error.
4219
 * @since 6.3
4220
 */
4221
PJ *proj_crs_promote_to_3D(PJ_CONTEXT *ctx, const char *crs_3D_name,
4222
0
                           const PJ *crs_2D) {
4223
0
    SANITIZE_CTX(ctx);
4224
0
    if (!crs_2D) {
4225
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4226
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4227
0
        return nullptr;
4228
0
    }
4229
0
    auto cpp_2D_crs = dynamic_cast<const CRS *>(crs_2D->iso_obj.get());
4230
0
    if (!cpp_2D_crs) {
4231
0
        auto coordinateMetadata =
4232
0
            dynamic_cast<const CoordinateMetadata *>(crs_2D->iso_obj.get());
4233
0
        if (!coordinateMetadata) {
4234
0
            proj_log_error(ctx, __FUNCTION__,
4235
0
                           "crs_2D is not a CRS or a CoordinateMetadata");
4236
0
            return nullptr;
4237
0
        }
4238
4239
0
        try {
4240
0
            auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
4241
0
            auto crs = coordinateMetadata->crs();
4242
0
            auto crs_3D = crs->promoteTo3D(
4243
0
                crs_3D_name ? std::string(crs_3D_name) : crs->nameStr(),
4244
0
                dbContext);
4245
0
            if (coordinateMetadata->coordinateEpoch().has_value()) {
4246
0
                return pj_obj_create(
4247
0
                    ctx, CoordinateMetadata::create(
4248
0
                             crs_3D,
4249
0
                             coordinateMetadata->coordinateEpochAsDecimalYear(),
4250
0
                             dbContext));
4251
0
            } else {
4252
0
                return pj_obj_create(ctx, CoordinateMetadata::create(crs_3D));
4253
0
            }
4254
0
        } catch (const std::exception &e) {
4255
0
            proj_log_error(ctx, __FUNCTION__, e.what());
4256
0
            return nullptr;
4257
0
        }
4258
0
    } else {
4259
0
        try {
4260
0
            auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
4261
0
            return pj_obj_create(ctx, cpp_2D_crs->promoteTo3D(
4262
0
                                          crs_3D_name ? std::string(crs_3D_name)
4263
0
                                                      : cpp_2D_crs->nameStr(),
4264
0
                                          dbContext));
4265
0
        } catch (const std::exception &e) {
4266
0
            proj_log_error(ctx, __FUNCTION__, e.what());
4267
0
            return nullptr;
4268
0
        }
4269
0
    }
4270
0
}
4271
4272
// ---------------------------------------------------------------------------
4273
4274
/** \brief Create a projected 3D CRS from an existing projected 2D CRS.
4275
 *
4276
 * The passed projected_2D_crs is used so that its name is replaced by
4277
 * crs_name and its base geographic CRS is replaced by geog_3D_crs. The vertical
4278
 * axis of geog_3D_crs (ellipsoidal height) will be added as the 3rd axis of
4279
 * the resulting projected 3D CRS.
4280
 * Normally, the passed geog_3D_crs should be the 3D counterpart of the original
4281
 * 2D base geographic CRS of projected_2D_crs, but such no check is done.
4282
 *
4283
 * It is also possible to invoke this function with a NULL geog_3D_crs. In which
4284
 * case, the existing base geographic 2D CRS of projected_2D_crs will be
4285
 * automatically promoted to 3D by assuming a 3rd axis being an ellipsoidal
4286
 * height, oriented upwards, and with metre units. This is equivalent to using
4287
 * proj_crs_promote_to_3D().
4288
 *
4289
 * The returned object must be unreferenced with proj_destroy() after
4290
 * use.
4291
 * It should be used by at most one thread at a time.
4292
 *
4293
 * @param ctx PROJ context, or NULL for default context
4294
 * @param crs_name CRS name. Or NULL (in which case the name of projected_2D_crs
4295
 * will be used)
4296
 * @param projected_2D_crs Projected 2D CRS to be "promoted" to 3D. Must not be
4297
 * NULL.
4298
 * @param geog_3D_crs Base geographic 3D CRS for the new CRS. May be NULL.
4299
 *
4300
 * @return Object that must be unreferenced with
4301
 * proj_destroy(), or NULL in case of error.
4302
 * @since 6.3
4303
 */
4304
PJ *proj_crs_create_projected_3D_crs_from_2D(PJ_CONTEXT *ctx,
4305
                                             const char *crs_name,
4306
                                             const PJ *projected_2D_crs,
4307
0
                                             const PJ *geog_3D_crs) {
4308
0
    SANITIZE_CTX(ctx);
4309
0
    if (!projected_2D_crs) {
4310
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4311
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4312
0
        return nullptr;
4313
0
    }
4314
0
    auto cpp_projected_2D_crs =
4315
0
        dynamic_cast<const ProjectedCRS *>(projected_2D_crs->iso_obj.get());
4316
0
    if (!cpp_projected_2D_crs) {
4317
0
        proj_log_error(ctx, __FUNCTION__,
4318
0
                       "projected_2D_crs is not a Projected CRS");
4319
0
        return nullptr;
4320
0
    }
4321
0
    const auto &oldCS = cpp_projected_2D_crs->coordinateSystem();
4322
0
    const auto &oldCSAxisList = oldCS->axisList();
4323
4324
0
    if (geog_3D_crs && geog_3D_crs->iso_obj) {
4325
0
        auto cpp_geog_3D_CRS =
4326
0
            std::dynamic_pointer_cast<GeographicCRS>(geog_3D_crs->iso_obj);
4327
0
        if (!cpp_geog_3D_CRS) {
4328
0
            proj_log_error(ctx, __FUNCTION__,
4329
0
                           "geog_3D_crs is not a Geographic CRS");
4330
0
            return nullptr;
4331
0
        }
4332
4333
0
        const auto &geogCS = cpp_geog_3D_CRS->coordinateSystem();
4334
0
        const auto &geogCSAxisList = geogCS->axisList();
4335
0
        if (geogCSAxisList.size() != 3) {
4336
0
            proj_log_error(ctx, __FUNCTION__,
4337
0
                           "geog_3D_crs is not a Geographic 3D CRS");
4338
0
            return nullptr;
4339
0
        }
4340
0
        try {
4341
0
            auto newCS =
4342
0
                cs::CartesianCS::create(PropertyMap(), oldCSAxisList[0],
4343
0
                                        oldCSAxisList[1], geogCSAxisList[2]);
4344
0
            return pj_obj_create(
4345
0
                ctx,
4346
0
                ProjectedCRS::create(
4347
0
                    createPropertyMapName(
4348
0
                        crs_name ? crs_name
4349
0
                                 : cpp_projected_2D_crs->nameStr().c_str()),
4350
0
                    NN_NO_CHECK(cpp_geog_3D_CRS),
4351
0
                    cpp_projected_2D_crs->derivingConversion(), newCS));
4352
0
        } catch (const std::exception &e) {
4353
0
            proj_log_error(ctx, __FUNCTION__, e.what());
4354
0
            return nullptr;
4355
0
        }
4356
0
    } else {
4357
0
        try {
4358
0
            auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
4359
0
            return pj_obj_create(ctx,
4360
0
                                 cpp_projected_2D_crs->promoteTo3D(
4361
0
                                     crs_name ? std::string(crs_name)
4362
0
                                              : cpp_projected_2D_crs->nameStr(),
4363
0
                                     dbContext));
4364
0
        } catch (const std::exception &e) {
4365
0
            proj_log_error(ctx, __FUNCTION__, e.what());
4366
0
            return nullptr;
4367
0
        }
4368
0
    }
4369
0
}
4370
4371
// ---------------------------------------------------------------------------
4372
4373
/** \brief Create a 2D CRS from an existing 3D CRS.
4374
 *
4375
 * See osgeo::proj::crs::CRS::demoteTo2D().
4376
 *
4377
 * The returned object must be unreferenced with proj_destroy() after
4378
 * use.
4379
 * It should be used by at most one thread at a time.
4380
 *
4381
 * @param ctx PROJ context, or NULL for default context
4382
 * @param crs_2D_name CRS name. Or NULL (in which case the name of crs_3D
4383
 * will be used)
4384
 * @param crs_3D 3D CRS to be "demoted" to 2D. Must not be NULL.
4385
 *
4386
 * @return Object that must be unreferenced with
4387
 * proj_destroy(), or NULL in case of error.
4388
 * @since 6.3
4389
 */
4390
PJ *proj_crs_demote_to_2D(PJ_CONTEXT *ctx, const char *crs_2D_name,
4391
2.33k
                          const PJ *crs_3D) {
4392
2.33k
    SANITIZE_CTX(ctx);
4393
2.33k
    if (!crs_3D) {
4394
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4395
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4396
0
        return nullptr;
4397
0
    }
4398
2.33k
    auto cpp_3D_crs = dynamic_cast<const CRS *>(crs_3D->iso_obj.get());
4399
2.33k
    if (!cpp_3D_crs) {
4400
0
        proj_log_error(ctx, __FUNCTION__, "crs_3D is not a CRS");
4401
0
        return nullptr;
4402
0
    }
4403
2.33k
    try {
4404
2.33k
        auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
4405
2.33k
        return pj_obj_create(
4406
2.33k
            ctx, cpp_3D_crs->demoteTo2D(crs_2D_name ? std::string(crs_2D_name)
4407
2.33k
                                                    : cpp_3D_crs->nameStr(),
4408
2.33k
                                        dbContext));
4409
2.33k
    } catch (const std::exception &e) {
4410
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4411
0
        return nullptr;
4412
0
    }
4413
2.33k
}
4414
4415
// ---------------------------------------------------------------------------
4416
4417
/** \brief Instantiate a EngineeringCRS with just a name
4418
 *
4419
 * The returned object must be unreferenced with proj_destroy() after
4420
 * use.
4421
 * It should be used by at most one thread at a time.
4422
 *
4423
 * @param ctx PROJ context, or NULL for default context
4424
 * @param crs_name CRS name. Or NULL.
4425
 *
4426
 * @return Object that must be unreferenced with
4427
 * proj_destroy(), or NULL in case of error.
4428
 */
4429
0
PJ *proj_create_engineering_crs(PJ_CONTEXT *ctx, const char *crs_name) {
4430
0
    SANITIZE_CTX(ctx);
4431
0
    try {
4432
0
        return pj_obj_create(
4433
0
            ctx, EngineeringCRS::create(
4434
0
                     createPropertyMapName(crs_name),
4435
0
                     EngineeringDatum::create(
4436
0
                         createPropertyMapName(UNKNOWN_ENGINEERING_DATUM)),
4437
0
                     CartesianCS::createEastingNorthing(UnitOfMeasure::METRE)));
4438
0
    } catch (const std::exception &e) {
4439
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4440
0
        return nullptr;
4441
0
    }
4442
0
}
4443
4444
// ---------------------------------------------------------------------------
4445
4446
//! @cond Doxygen_Suppress
4447
4448
static void setSingleOperationElements(
4449
    const char *name, const char *auth_name, const char *code,
4450
    const char *method_name, const char *method_auth_name,
4451
    const char *method_code, int param_count,
4452
    const PJ_PARAM_DESCRIPTION *params, PropertyMap &propSingleOp,
4453
    PropertyMap &propMethod, std::vector<OperationParameterNNPtr> &parameters,
4454
0
    std::vector<ParameterValueNNPtr> &values) {
4455
0
    propSingleOp.set(common::IdentifiedObject::NAME_KEY,
4456
0
                     name ? name : "unnamed");
4457
0
    if (auth_name && code) {
4458
0
        propSingleOp.set(metadata::Identifier::CODESPACE_KEY, auth_name)
4459
0
            .set(metadata::Identifier::CODE_KEY, code);
4460
0
    }
4461
4462
0
    propMethod.set(common::IdentifiedObject::NAME_KEY,
4463
0
                   method_name ? method_name : "unnamed");
4464
0
    if (method_auth_name && method_code) {
4465
0
        propMethod.set(metadata::Identifier::CODESPACE_KEY, method_auth_name)
4466
0
            .set(metadata::Identifier::CODE_KEY, method_code);
4467
0
    }
4468
4469
0
    for (int i = 0; i < param_count; i++) {
4470
0
        PropertyMap propParam;
4471
0
        propParam.set(common::IdentifiedObject::NAME_KEY,
4472
0
                      params[i].name ? params[i].name : "unnamed");
4473
0
        if (params[i].auth_name && params[i].code) {
4474
0
            propParam
4475
0
                .set(metadata::Identifier::CODESPACE_KEY, params[i].auth_name)
4476
0
                .set(metadata::Identifier::CODE_KEY, params[i].code);
4477
0
        }
4478
0
        parameters.emplace_back(OperationParameter::create(propParam));
4479
0
        auto unit_type = UnitOfMeasure::Type::UNKNOWN;
4480
0
        switch (params[i].unit_type) {
4481
0
        case PJ_UT_ANGULAR:
4482
0
            unit_type = UnitOfMeasure::Type::ANGULAR;
4483
0
            break;
4484
0
        case PJ_UT_LINEAR:
4485
0
            unit_type = UnitOfMeasure::Type::LINEAR;
4486
0
            break;
4487
0
        case PJ_UT_SCALE:
4488
0
            unit_type = UnitOfMeasure::Type::SCALE;
4489
0
            break;
4490
0
        case PJ_UT_TIME:
4491
0
            unit_type = UnitOfMeasure::Type::TIME;
4492
0
            break;
4493
0
        case PJ_UT_PARAMETRIC:
4494
0
            unit_type = UnitOfMeasure::Type::PARAMETRIC;
4495
0
            break;
4496
0
        }
4497
4498
0
        Measure measure(
4499
0
            params[i].value,
4500
0
            params[i].unit_type == PJ_UT_ANGULAR
4501
0
                ? createAngularUnit(params[i].unit_name,
4502
0
                                    params[i].unit_conv_factor)
4503
0
            : params[i].unit_type == PJ_UT_LINEAR
4504
0
                ? createLinearUnit(params[i].unit_name,
4505
0
                                   params[i].unit_conv_factor)
4506
0
                : UnitOfMeasure(params[i].unit_name ? params[i].unit_name
4507
0
                                                    : "unnamed",
4508
0
                                params[i].unit_conv_factor, unit_type));
4509
0
        values.emplace_back(ParameterValue::create(measure));
4510
0
    }
4511
0
}
4512
4513
//! @endcond
4514
4515
// ---------------------------------------------------------------------------
4516
4517
/** \brief Instantiate a Conversion
4518
 *
4519
 * The returned object must be unreferenced with proj_destroy() after
4520
 * use.
4521
 * It should be used by at most one thread at a time.
4522
 *
4523
 * @param ctx PROJ context, or NULL for default context
4524
 * @param name Conversion name. Or NULL.
4525
 * @param auth_name Conversion authority name. Or NULL.
4526
 * @param code Conversion code. Or NULL.
4527
 * @param method_name Method name. Or NULL.
4528
 * @param method_auth_name Method authority name. Or NULL.
4529
 * @param method_code Method code. Or NULL.
4530
 * @param param_count Number of parameters (size of params argument)
4531
 * @param params Parameter descriptions (array of size param_count)
4532
 *
4533
 * @return Object that must be unreferenced with
4534
 * proj_destroy(), or NULL in case of error.
4535
 */
4536
4537
PJ *proj_create_conversion(PJ_CONTEXT *ctx, const char *name,
4538
                           const char *auth_name, const char *code,
4539
                           const char *method_name,
4540
                           const char *method_auth_name,
4541
                           const char *method_code, int param_count,
4542
0
                           const PJ_PARAM_DESCRIPTION *params) {
4543
0
    SANITIZE_CTX(ctx);
4544
0
    try {
4545
0
        PropertyMap propSingleOp;
4546
0
        PropertyMap propMethod;
4547
0
        std::vector<OperationParameterNNPtr> parameters;
4548
0
        std::vector<ParameterValueNNPtr> values;
4549
4550
0
        setSingleOperationElements(
4551
0
            name, auth_name, code, method_name, method_auth_name, method_code,
4552
0
            param_count, params, propSingleOp, propMethod, parameters, values);
4553
4554
0
        return pj_obj_create(ctx, Conversion::create(propSingleOp, propMethod,
4555
0
                                                     parameters, values));
4556
0
    } catch (const std::exception &e) {
4557
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4558
0
        return nullptr;
4559
0
    }
4560
0
}
4561
4562
// ---------------------------------------------------------------------------
4563
4564
/** \brief Instantiate a Transformation
4565
 *
4566
 * The returned object must be unreferenced with proj_destroy() after
4567
 * use.
4568
 * It should be used by at most one thread at a time.
4569
 *
4570
 * @param ctx PROJ context, or NULL for default context
4571
 * @param name Transformation name. Or NULL.
4572
 * @param auth_name Transformation authority name. Or NULL.
4573
 * @param code Transformation code. Or NULL.
4574
 * @param source_crs Object of type CRS representing the source CRS.
4575
 * Must not be NULL.
4576
 * @param target_crs Object of type CRS representing the target CRS.
4577
 * Must not be NULL.
4578
 * @param interpolation_crs Object of type CRS representing the interpolation
4579
 * CRS. Or NULL.
4580
 * @param method_name Method name. Or NULL.
4581
 * @param method_auth_name Method authority name. Or NULL.
4582
 * @param method_code Method code. Or NULL.
4583
 * @param param_count Number of parameters (size of params argument)
4584
 * @param params Parameter descriptions (array of size param_count)
4585
 * @param accuracy Accuracy of the transformation in meters. A negative
4586
 * values means unknown.
4587
 *
4588
 * @return Object that must be unreferenced with
4589
 * proj_destroy(), or NULL in case of error.
4590
 */
4591
4592
PJ *proj_create_transformation(
4593
    PJ_CONTEXT *ctx, const char *name, const char *auth_name, const char *code,
4594
    const PJ *source_crs, const PJ *target_crs, const PJ *interpolation_crs,
4595
    const char *method_name, const char *method_auth_name,
4596
    const char *method_code, int param_count,
4597
0
    const PJ_PARAM_DESCRIPTION *params, double accuracy) {
4598
0
    SANITIZE_CTX(ctx);
4599
0
    if (!source_crs || !target_crs) {
4600
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4601
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4602
0
        return nullptr;
4603
0
    }
4604
4605
0
    auto l_sourceCRS = std::dynamic_pointer_cast<CRS>(source_crs->iso_obj);
4606
0
    if (!l_sourceCRS) {
4607
0
        proj_log_error(ctx, __FUNCTION__, "source_crs is not a CRS");
4608
0
        return nullptr;
4609
0
    }
4610
4611
0
    auto l_targetCRS = std::dynamic_pointer_cast<CRS>(target_crs->iso_obj);
4612
0
    if (!l_targetCRS) {
4613
0
        proj_log_error(ctx, __FUNCTION__, "target_crs is not a CRS");
4614
0
        return nullptr;
4615
0
    }
4616
4617
0
    CRSPtr l_interpolationCRS;
4618
0
    if (interpolation_crs) {
4619
0
        l_interpolationCRS =
4620
0
            std::dynamic_pointer_cast<CRS>(interpolation_crs->iso_obj);
4621
0
        if (!l_interpolationCRS) {
4622
0
            proj_log_error(ctx, __FUNCTION__, "interpolation_crs is not a CRS");
4623
0
            return nullptr;
4624
0
        }
4625
0
    }
4626
4627
0
    try {
4628
0
        PropertyMap propSingleOp;
4629
0
        PropertyMap propMethod;
4630
0
        std::vector<OperationParameterNNPtr> parameters;
4631
0
        std::vector<ParameterValueNNPtr> values;
4632
4633
0
        setSingleOperationElements(
4634
0
            name, auth_name, code, method_name, method_auth_name, method_code,
4635
0
            param_count, params, propSingleOp, propMethod, parameters, values);
4636
4637
0
        std::vector<metadata::PositionalAccuracyNNPtr> accuracies;
4638
0
        if (accuracy >= 0.0) {
4639
0
            accuracies.emplace_back(
4640
0
                PositionalAccuracy::create(toString(accuracy)));
4641
0
        }
4642
4643
0
        return pj_obj_create(
4644
0
            ctx,
4645
0
            Transformation::create(propSingleOp, NN_NO_CHECK(l_sourceCRS),
4646
0
                                   NN_NO_CHECK(l_targetCRS), l_interpolationCRS,
4647
0
                                   propMethod, parameters, values, accuracies));
4648
0
    } catch (const std::exception &e) {
4649
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4650
0
        return nullptr;
4651
0
    }
4652
0
}
4653
4654
// ---------------------------------------------------------------------------
4655
4656
/**
4657
 * \brief Return an equivalent projection.
4658
 *
4659
 * Currently implemented:
4660
 * <ul>
4661
 * <li>EPSG_CODE_METHOD_MERCATOR_VARIANT_A (1SP) to
4662
 * EPSG_CODE_METHOD_MERCATOR_VARIANT_B (2SP)</li>
4663
 * <li>EPSG_CODE_METHOD_MERCATOR_VARIANT_B (2SP) to
4664
 * EPSG_CODE_METHOD_MERCATOR_VARIANT_A (1SP)</li>
4665
 * <li>EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP to
4666
 * EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP</li>
4667
 * <li>EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP to
4668
 * EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP</li>
4669
 * </ul>
4670
 *
4671
 * @param ctx PROJ context, or NULL for default context
4672
 * @param conversion Object of type Conversion. Must not be NULL.
4673
 * @param new_method_epsg_code EPSG code of the target method. Or 0 (in which
4674
 * case new_method_name must be specified).
4675
 * @param new_method_name EPSG or PROJ target method name. Or nullptr  (in which
4676
 * case new_method_epsg_code must be specified).
4677
 * @return new conversion that must be unreferenced with
4678
 * proj_destroy(), or NULL in case of error.
4679
 */
4680
PJ *proj_convert_conversion_to_other_method(PJ_CONTEXT *ctx,
4681
                                            const PJ *conversion,
4682
                                            int new_method_epsg_code,
4683
0
                                            const char *new_method_name) {
4684
0
    SANITIZE_CTX(ctx);
4685
0
    if (!conversion) {
4686
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
4687
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
4688
0
        return nullptr;
4689
0
    }
4690
0
    auto conv = dynamic_cast<const Conversion *>(conversion->iso_obj.get());
4691
0
    if (!conv) {
4692
0
        proj_log_error(ctx, __FUNCTION__, "not a Conversion");
4693
0
        return nullptr;
4694
0
    }
4695
0
    if (new_method_epsg_code == 0) {
4696
0
        if (!new_method_name) {
4697
0
            return nullptr;
4698
0
        }
4699
0
        if (metadata::Identifier::isEquivalentName(
4700
0
                new_method_name, EPSG_NAME_METHOD_MERCATOR_VARIANT_A)) {
4701
0
            new_method_epsg_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_A;
4702
0
        } else if (metadata::Identifier::isEquivalentName(
4703
0
                       new_method_name, EPSG_NAME_METHOD_MERCATOR_VARIANT_B)) {
4704
0
            new_method_epsg_code = EPSG_CODE_METHOD_MERCATOR_VARIANT_B;
4705
0
        } else if (metadata::Identifier::isEquivalentName(
4706
0
                       new_method_name,
4707
0
                       EPSG_NAME_METHOD_LAMBERT_CONIC_CONFORMAL_1SP)) {
4708
0
            new_method_epsg_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_1SP;
4709
0
        } else if (metadata::Identifier::isEquivalentName(
4710
0
                       new_method_name,
4711
0
                       EPSG_NAME_METHOD_LAMBERT_CONIC_CONFORMAL_2SP)) {
4712
0
            new_method_epsg_code = EPSG_CODE_METHOD_LAMBERT_CONIC_CONFORMAL_2SP;
4713
0
        }
4714
0
    }
4715
0
    try {
4716
0
        auto new_conv = conv->convertToOtherMethod(new_method_epsg_code);
4717
0
        if (!new_conv)
4718
0
            return nullptr;
4719
0
        return pj_obj_create(ctx, NN_NO_CHECK(new_conv));
4720
0
    } catch (const std::exception &e) {
4721
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4722
0
        return nullptr;
4723
0
    }
4724
0
}
4725
4726
// ---------------------------------------------------------------------------
4727
4728
//! @cond Doxygen_Suppress
4729
4730
0
static CoordinateSystemAxisNNPtr createAxis(const PJ_AXIS_DESCRIPTION &axis) {
4731
0
    const auto dir =
4732
0
        axis.direction ? AxisDirection::valueOf(axis.direction) : nullptr;
4733
0
    if (dir == nullptr)
4734
0
        throw Exception("invalid value for axis direction");
4735
0
    auto unit_type = UnitOfMeasure::Type::UNKNOWN;
4736
0
    switch (axis.unit_type) {
4737
0
    case PJ_UT_ANGULAR:
4738
0
        unit_type = UnitOfMeasure::Type::ANGULAR;
4739
0
        break;
4740
0
    case PJ_UT_LINEAR:
4741
0
        unit_type = UnitOfMeasure::Type::LINEAR;
4742
0
        break;
4743
0
    case PJ_UT_SCALE:
4744
0
        unit_type = UnitOfMeasure::Type::SCALE;
4745
0
        break;
4746
0
    case PJ_UT_TIME:
4747
0
        unit_type = UnitOfMeasure::Type::TIME;
4748
0
        break;
4749
0
    case PJ_UT_PARAMETRIC:
4750
0
        unit_type = UnitOfMeasure::Type::PARAMETRIC;
4751
0
        break;
4752
0
    }
4753
0
    const common::UnitOfMeasure unit(
4754
0
        axis.unit_type == PJ_UT_ANGULAR
4755
0
            ? createAngularUnit(axis.unit_name, axis.unit_conv_factor)
4756
0
        : axis.unit_type == PJ_UT_LINEAR
4757
0
            ? createLinearUnit(axis.unit_name, axis.unit_conv_factor)
4758
0
            : UnitOfMeasure(axis.unit_name ? axis.unit_name : "unnamed",
4759
0
                            axis.unit_conv_factor, unit_type));
4760
4761
0
    return CoordinateSystemAxis::create(
4762
0
        createPropertyMapName(axis.name),
4763
0
        axis.abbreviation ? axis.abbreviation : std::string(), *dir, unit);
4764
0
}
4765
4766
//! @endcond
4767
4768
// ---------------------------------------------------------------------------
4769
4770
/** \brief Instantiate a CoordinateSystem.
4771
 *
4772
 * The returned object must be unreferenced with proj_destroy() after
4773
 * use.
4774
 * It should be used by at most one thread at a time.
4775
 *
4776
 * @param ctx PROJ context, or NULL for default context
4777
 * @param type Coordinate system type.
4778
 * @param axis_count Number of axis
4779
 * @param axis Axis description (array of size axis_count)
4780
 *
4781
 * @return Object that must be unreferenced with
4782
 * proj_destroy(), or NULL in case of error.
4783
 */
4784
4785
PJ *proj_create_cs(PJ_CONTEXT *ctx, PJ_COORDINATE_SYSTEM_TYPE type,
4786
0
                   int axis_count, const PJ_AXIS_DESCRIPTION *axis) {
4787
0
    SANITIZE_CTX(ctx);
4788
0
    try {
4789
0
        switch (type) {
4790
0
        case PJ_CS_TYPE_UNKNOWN:
4791
0
            return nullptr;
4792
4793
0
        case PJ_CS_TYPE_CARTESIAN: {
4794
0
            if (axis_count == 2) {
4795
0
                return pj_obj_create(
4796
0
                    ctx, CartesianCS::create(PropertyMap(), createAxis(axis[0]),
4797
0
                                             createAxis(axis[1])));
4798
0
            } else if (axis_count == 3) {
4799
0
                return pj_obj_create(
4800
0
                    ctx, CartesianCS::create(PropertyMap(), createAxis(axis[0]),
4801
0
                                             createAxis(axis[1]),
4802
0
                                             createAxis(axis[2])));
4803
0
            }
4804
0
            break;
4805
0
        }
4806
4807
0
        case PJ_CS_TYPE_ELLIPSOIDAL: {
4808
0
            if (axis_count == 2) {
4809
0
                return pj_obj_create(
4810
0
                    ctx,
4811
0
                    EllipsoidalCS::create(PropertyMap(), createAxis(axis[0]),
4812
0
                                          createAxis(axis[1])));
4813
0
            } else if (axis_count == 3) {
4814
0
                return pj_obj_create(
4815
0
                    ctx, EllipsoidalCS::create(
4816
0
                             PropertyMap(), createAxis(axis[0]),
4817
0
                             createAxis(axis[1]), createAxis(axis[2])));
4818
0
            }
4819
0
            break;
4820
0
        }
4821
4822
0
        case PJ_CS_TYPE_VERTICAL: {
4823
0
            if (axis_count == 1) {
4824
0
                return pj_obj_create(
4825
0
                    ctx,
4826
0
                    VerticalCS::create(PropertyMap(), createAxis(axis[0])));
4827
0
            }
4828
0
            break;
4829
0
        }
4830
4831
0
        case PJ_CS_TYPE_SPHERICAL: {
4832
0
            if (axis_count == 3) {
4833
0
                return pj_obj_create(
4834
0
                    ctx, EllipsoidalCS::create(
4835
0
                             PropertyMap(), createAxis(axis[0]),
4836
0
                             createAxis(axis[1]), createAxis(axis[2])));
4837
0
            }
4838
0
            break;
4839
0
        }
4840
4841
0
        case PJ_CS_TYPE_PARAMETRIC: {
4842
0
            if (axis_count == 1) {
4843
0
                return pj_obj_create(
4844
0
                    ctx,
4845
0
                    ParametricCS::create(PropertyMap(), createAxis(axis[0])));
4846
0
            }
4847
0
            break;
4848
0
        }
4849
4850
0
        case PJ_CS_TYPE_ORDINAL: {
4851
0
            std::vector<CoordinateSystemAxisNNPtr> axisVector;
4852
0
            for (int i = 0; i < axis_count; i++) {
4853
0
                axisVector.emplace_back(createAxis(axis[i]));
4854
0
            }
4855
4856
0
            return pj_obj_create(ctx,
4857
0
                                 OrdinalCS::create(PropertyMap(), axisVector));
4858
0
        }
4859
4860
0
        case PJ_CS_TYPE_DATETIMETEMPORAL: {
4861
0
            if (axis_count == 1) {
4862
0
                return pj_obj_create(
4863
0
                    ctx, DateTimeTemporalCS::create(PropertyMap(),
4864
0
                                                    createAxis(axis[0])));
4865
0
            }
4866
0
            break;
4867
0
        }
4868
4869
0
        case PJ_CS_TYPE_TEMPORALCOUNT: {
4870
0
            if (axis_count == 1) {
4871
0
                return pj_obj_create(
4872
0
                    ctx, TemporalCountCS::create(PropertyMap(),
4873
0
                                                 createAxis(axis[0])));
4874
0
            }
4875
0
            break;
4876
0
        }
4877
4878
0
        case PJ_CS_TYPE_TEMPORALMEASURE: {
4879
0
            if (axis_count == 1) {
4880
0
                return pj_obj_create(
4881
0
                    ctx, TemporalMeasureCS::create(PropertyMap(),
4882
0
                                                   createAxis(axis[0])));
4883
0
            }
4884
0
            break;
4885
0
        }
4886
0
        }
4887
4888
0
    } catch (const std::exception &e) {
4889
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4890
0
        return nullptr;
4891
0
    }
4892
0
    proj_log_error(ctx, __FUNCTION__, "Wrong value for axis_count");
4893
0
    return nullptr;
4894
0
}
4895
4896
// ---------------------------------------------------------------------------
4897
4898
/** \brief Instantiate a CartesiansCS 2D
4899
 *
4900
 * The returned object must be unreferenced with proj_destroy() after
4901
 * use.
4902
 * It should be used by at most one thread at a time.
4903
 *
4904
 * @param ctx PROJ context, or NULL for default context
4905
 * @param type Coordinate system type.
4906
 * @param unit_name Unit name.
4907
 * @param unit_conv_factor Unit conversion factor to SI.
4908
 *
4909
 * @return Object that must be unreferenced with
4910
 * proj_destroy(), or NULL in case of error.
4911
 */
4912
4913
PJ *proj_create_cartesian_2D_cs(PJ_CONTEXT *ctx, PJ_CARTESIAN_CS_2D_TYPE type,
4914
                                const char *unit_name,
4915
0
                                double unit_conv_factor) {
4916
0
    SANITIZE_CTX(ctx);
4917
0
    try {
4918
0
        switch (type) {
4919
0
        case PJ_CART2D_EASTING_NORTHING:
4920
0
            return pj_obj_create(
4921
0
                ctx, CartesianCS::createEastingNorthing(
4922
0
                         createLinearUnit(unit_name, unit_conv_factor)));
4923
4924
0
        case PJ_CART2D_NORTHING_EASTING:
4925
0
            return pj_obj_create(
4926
0
                ctx, CartesianCS::createNorthingEasting(
4927
0
                         createLinearUnit(unit_name, unit_conv_factor)));
4928
4929
0
        case PJ_CART2D_NORTH_POLE_EASTING_SOUTH_NORTHING_SOUTH:
4930
0
            return pj_obj_create(
4931
0
                ctx, CartesianCS::createNorthPoleEastingSouthNorthingSouth(
4932
0
                         createLinearUnit(unit_name, unit_conv_factor)));
4933
4934
0
        case PJ_CART2D_SOUTH_POLE_EASTING_NORTH_NORTHING_NORTH:
4935
0
            return pj_obj_create(
4936
0
                ctx, CartesianCS::createSouthPoleEastingNorthNorthingNorth(
4937
0
                         createLinearUnit(unit_name, unit_conv_factor)));
4938
4939
0
        case PJ_CART2D_WESTING_SOUTHING:
4940
0
            return pj_obj_create(
4941
0
                ctx, CartesianCS::createWestingSouthing(
4942
0
                         createLinearUnit(unit_name, unit_conv_factor)));
4943
0
        }
4944
0
    } catch (const std::exception &e) {
4945
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4946
0
    }
4947
0
    return nullptr;
4948
0
}
4949
4950
// ---------------------------------------------------------------------------
4951
4952
/** \brief Instantiate a Ellipsoidal 2D
4953
 *
4954
 * The returned object must be unreferenced with proj_destroy() after
4955
 * use.
4956
 * It should be used by at most one thread at a time.
4957
 *
4958
 * @param ctx PROJ context, or NULL for default context
4959
 * @param type Coordinate system type.
4960
 * @param unit_name Name of the angular units. Or NULL for Degree
4961
 * @param unit_conv_factor Conversion factor from the angular unit to radian.
4962
 * Or 0 for Degree if unit_name == NULL. Otherwise should be not NULL
4963
 *
4964
 * @return Object that must be unreferenced with
4965
 * proj_destroy(), or NULL in case of error.
4966
 */
4967
4968
PJ *proj_create_ellipsoidal_2D_cs(PJ_CONTEXT *ctx,
4969
                                  PJ_ELLIPSOIDAL_CS_2D_TYPE type,
4970
                                  const char *unit_name,
4971
2.54k
                                  double unit_conv_factor) {
4972
2.54k
    SANITIZE_CTX(ctx);
4973
2.54k
    try {
4974
2.54k
        switch (type) {
4975
2.54k
        case PJ_ELLPS2D_LONGITUDE_LATITUDE:
4976
2.54k
            return pj_obj_create(
4977
2.54k
                ctx, EllipsoidalCS::createLongitudeLatitude(
4978
2.54k
                         createAngularUnit(unit_name, unit_conv_factor)));
4979
4980
0
        case PJ_ELLPS2D_LATITUDE_LONGITUDE:
4981
0
            return pj_obj_create(
4982
0
                ctx, EllipsoidalCS::createLatitudeLongitude(
4983
0
                         createAngularUnit(unit_name, unit_conv_factor)));
4984
2.54k
        }
4985
2.54k
    } catch (const std::exception &e) {
4986
0
        proj_log_error(ctx, __FUNCTION__, e.what());
4987
0
    }
4988
0
    return nullptr;
4989
2.54k
}
4990
4991
// ---------------------------------------------------------------------------
4992
4993
/** \brief Instantiate a Ellipsoidal 3D
4994
 *
4995
 * The returned object must be unreferenced with proj_destroy() after
4996
 * use.
4997
 * It should be used by at most one thread at a time.
4998
 *
4999
 * @param ctx PROJ context, or NULL for default context
5000
 * @param type Coordinate system type.
5001
 * @param horizontal_angular_unit_name Name of the angular units. Or NULL for
5002
 * Degree.
5003
 * @param horizontal_angular_unit_conv_factor Conversion factor from the angular
5004
 * unit to radian. Or 0 for Degree if horizontal_angular_unit_name == NULL.
5005
 * Otherwise should be not NULL
5006
 * @param vertical_linear_unit_name Vertical linear unit name. Or NULL for
5007
 * Metre.
5008
 * @param vertical_linear_unit_conv_factor Vertical linear unit conversion
5009
 * factor to metre. Or 0 for Metre if vertical_linear_unit_name == NULL.
5010
 * Otherwise should be not NULL
5011
5012
 * @return Object that must be unreferenced with
5013
 * proj_destroy(), or NULL in case of error.
5014
 * @since 6.3
5015
 */
5016
5017
PJ *proj_create_ellipsoidal_3D_cs(PJ_CONTEXT *ctx,
5018
                                  PJ_ELLIPSOIDAL_CS_3D_TYPE type,
5019
                                  const char *horizontal_angular_unit_name,
5020
                                  double horizontal_angular_unit_conv_factor,
5021
                                  const char *vertical_linear_unit_name,
5022
0
                                  double vertical_linear_unit_conv_factor) {
5023
0
    SANITIZE_CTX(ctx);
5024
0
    try {
5025
0
        switch (type) {
5026
0
        case PJ_ELLPS3D_LONGITUDE_LATITUDE_HEIGHT:
5027
0
            return pj_obj_create(
5028
0
                ctx, EllipsoidalCS::createLongitudeLatitudeEllipsoidalHeight(
5029
0
                         createAngularUnit(horizontal_angular_unit_name,
5030
0
                                           horizontal_angular_unit_conv_factor),
5031
0
                         createLinearUnit(vertical_linear_unit_name,
5032
0
                                          vertical_linear_unit_conv_factor)));
5033
5034
0
        case PJ_ELLPS3D_LATITUDE_LONGITUDE_HEIGHT:
5035
0
            return pj_obj_create(
5036
0
                ctx, EllipsoidalCS::createLatitudeLongitudeEllipsoidalHeight(
5037
0
                         createAngularUnit(horizontal_angular_unit_name,
5038
0
                                           horizontal_angular_unit_conv_factor),
5039
0
                         createLinearUnit(vertical_linear_unit_name,
5040
0
                                          vertical_linear_unit_conv_factor)));
5041
0
        }
5042
0
    } catch (const std::exception &e) {
5043
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5044
0
    }
5045
0
    return nullptr;
5046
0
}
5047
5048
// ---------------------------------------------------------------------------
5049
5050
/** \brief Instantiate a ProjectedCRS
5051
 *
5052
 * The returned object must be unreferenced with proj_destroy() after
5053
 * use.
5054
 * It should be used by at most one thread at a time.
5055
 *
5056
 * @param ctx PROJ context, or NULL for default context
5057
 * @param crs_name CRS name. Or NULL
5058
 * @param geodetic_crs Base GeodeticCRS. Must not be NULL.
5059
 * @param conversion Conversion. Must not be NULL.
5060
 * @param coordinate_system Cartesian coordinate system. Must not be NULL.
5061
 *
5062
 * @return Object that must be unreferenced with
5063
 * proj_destroy(), or NULL in case of error.
5064
 */
5065
5066
PJ *proj_create_projected_crs(PJ_CONTEXT *ctx, const char *crs_name,
5067
                              const PJ *geodetic_crs, const PJ *conversion,
5068
0
                              const PJ *coordinate_system) {
5069
0
    SANITIZE_CTX(ctx);
5070
0
    if (!geodetic_crs || !conversion || !coordinate_system) {
5071
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
5072
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
5073
0
        return nullptr;
5074
0
    }
5075
0
    auto geodCRS =
5076
0
        std::dynamic_pointer_cast<GeodeticCRS>(geodetic_crs->iso_obj);
5077
0
    if (!geodCRS) {
5078
0
        return nullptr;
5079
0
    }
5080
0
    auto conv = std::dynamic_pointer_cast<Conversion>(conversion->iso_obj);
5081
0
    if (!conv) {
5082
0
        return nullptr;
5083
0
    }
5084
0
    auto cs =
5085
0
        std::dynamic_pointer_cast<CartesianCS>(coordinate_system->iso_obj);
5086
0
    if (!cs) {
5087
0
        return nullptr;
5088
0
    }
5089
0
    try {
5090
0
        return pj_obj_create(
5091
0
            ctx, ProjectedCRS::create(createPropertyMapName(crs_name),
5092
0
                                      NN_NO_CHECK(geodCRS), NN_NO_CHECK(conv),
5093
0
                                      NN_NO_CHECK(cs)));
5094
0
    } catch (const std::exception &e) {
5095
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5096
0
    }
5097
0
    return nullptr;
5098
0
}
5099
5100
// ---------------------------------------------------------------------------
5101
5102
//! @cond Doxygen_Suppress
5103
5104
static PJ *proj_create_conversion(PJ_CONTEXT *ctx,
5105
0
                                  const ConversionNNPtr &conv) {
5106
0
    return pj_obj_create(ctx, conv);
5107
0
}
5108
5109
//! @endcond
5110
5111
/* BEGIN: Generated by scripts/create_c_api_projections.py*/
5112
5113
// ---------------------------------------------------------------------------
5114
5115
/** \brief Instantiate a ProjectedCRS with a Universal Transverse Mercator
5116
 * conversion.
5117
 *
5118
 * See osgeo::proj::operation::Conversion::createUTM().
5119
 *
5120
 * Linear parameters are expressed in (linear_unit_name,
5121
 * linear_unit_conv_factor).
5122
 */
5123
0
PJ *proj_create_conversion_utm(PJ_CONTEXT *ctx, int zone, int north) {
5124
0
    SANITIZE_CTX(ctx);
5125
0
    try {
5126
0
        auto conv = Conversion::createUTM(PropertyMap(), zone, north != 0);
5127
0
        return proj_create_conversion(ctx, conv);
5128
0
    } catch (const std::exception &e) {
5129
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5130
0
    }
5131
0
    return nullptr;
5132
0
}
5133
// ---------------------------------------------------------------------------
5134
5135
/** \brief Instantiate a ProjectedCRS with a conversion based on the Transverse
5136
 * Mercator projection method.
5137
 *
5138
 * See osgeo::proj::operation::Conversion::createTransverseMercator().
5139
 *
5140
 * Linear parameters are expressed in (linear_unit_name,
5141
 * linear_unit_conv_factor).
5142
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5143
 */
5144
PJ *proj_create_conversion_transverse_mercator(
5145
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
5146
    double false_easting, double false_northing, const char *ang_unit_name,
5147
    double ang_unit_conv_factor, const char *linear_unit_name,
5148
0
    double linear_unit_conv_factor) {
5149
0
    SANITIZE_CTX(ctx);
5150
0
    try {
5151
0
        UnitOfMeasure linearUnit(
5152
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5153
0
        UnitOfMeasure angUnit(
5154
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5155
0
        auto conv = Conversion::createTransverseMercator(
5156
0
            PropertyMap(), Angle(center_lat, angUnit),
5157
0
            Angle(center_long, angUnit), Scale(scale),
5158
0
            Length(false_easting, linearUnit),
5159
0
            Length(false_northing, linearUnit));
5160
0
        return proj_create_conversion(ctx, conv);
5161
0
    } catch (const std::exception &e) {
5162
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5163
0
    }
5164
0
    return nullptr;
5165
0
}
5166
// ---------------------------------------------------------------------------
5167
5168
/** \brief Instantiate a ProjectedCRS with a conversion based on the Gauss
5169
 * Schreiber Transverse Mercator projection method.
5170
 *
5171
 * See
5172
 * osgeo::proj::operation::Conversion::createGaussSchreiberTransverseMercator().
5173
 *
5174
 * Linear parameters are expressed in (linear_unit_name,
5175
 * linear_unit_conv_factor).
5176
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5177
 */
5178
PJ *proj_create_conversion_gauss_schreiber_transverse_mercator(
5179
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
5180
    double false_easting, double false_northing, const char *ang_unit_name,
5181
    double ang_unit_conv_factor, const char *linear_unit_name,
5182
0
    double linear_unit_conv_factor) {
5183
0
    SANITIZE_CTX(ctx);
5184
0
    try {
5185
0
        UnitOfMeasure linearUnit(
5186
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5187
0
        UnitOfMeasure angUnit(
5188
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5189
0
        auto conv = Conversion::createGaussSchreiberTransverseMercator(
5190
0
            PropertyMap(), Angle(center_lat, angUnit),
5191
0
            Angle(center_long, angUnit), Scale(scale),
5192
0
            Length(false_easting, linearUnit),
5193
0
            Length(false_northing, linearUnit));
5194
0
        return proj_create_conversion(ctx, conv);
5195
0
    } catch (const std::exception &e) {
5196
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5197
0
    }
5198
0
    return nullptr;
5199
0
}
5200
// ---------------------------------------------------------------------------
5201
5202
/** \brief Instantiate a ProjectedCRS with a conversion based on the Transverse
5203
 * Mercator South Orientated projection method.
5204
 *
5205
 * See
5206
 * osgeo::proj::operation::Conversion::createTransverseMercatorSouthOriented().
5207
 *
5208
 * Linear parameters are expressed in (linear_unit_name,
5209
 * linear_unit_conv_factor).
5210
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5211
 */
5212
PJ *proj_create_conversion_transverse_mercator_south_oriented(
5213
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
5214
    double false_easting, double false_northing, const char *ang_unit_name,
5215
    double ang_unit_conv_factor, const char *linear_unit_name,
5216
0
    double linear_unit_conv_factor) {
5217
0
    SANITIZE_CTX(ctx);
5218
0
    try {
5219
0
        UnitOfMeasure linearUnit(
5220
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5221
0
        UnitOfMeasure angUnit(
5222
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5223
0
        auto conv = Conversion::createTransverseMercatorSouthOriented(
5224
0
            PropertyMap(), Angle(center_lat, angUnit),
5225
0
            Angle(center_long, angUnit), Scale(scale),
5226
0
            Length(false_easting, linearUnit),
5227
0
            Length(false_northing, linearUnit));
5228
0
        return proj_create_conversion(ctx, conv);
5229
0
    } catch (const std::exception &e) {
5230
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5231
0
    }
5232
0
    return nullptr;
5233
0
}
5234
// ---------------------------------------------------------------------------
5235
5236
/** \brief Instantiate a ProjectedCRS with a conversion based on the Two Point
5237
 * Equidistant projection method.
5238
 *
5239
 * See osgeo::proj::operation::Conversion::createTwoPointEquidistant().
5240
 *
5241
 * Linear parameters are expressed in (linear_unit_name,
5242
 * linear_unit_conv_factor).
5243
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5244
 */
5245
PJ *proj_create_conversion_two_point_equidistant(
5246
    PJ_CONTEXT *ctx, double latitude_first_point, double longitude_first_point,
5247
    double latitude_second_point, double longitude_secon_point,
5248
    double false_easting, double false_northing, const char *ang_unit_name,
5249
    double ang_unit_conv_factor, const char *linear_unit_name,
5250
0
    double linear_unit_conv_factor) {
5251
0
    SANITIZE_CTX(ctx);
5252
0
    try {
5253
0
        UnitOfMeasure linearUnit(
5254
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5255
0
        UnitOfMeasure angUnit(
5256
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5257
0
        auto conv = Conversion::createTwoPointEquidistant(
5258
0
            PropertyMap(), Angle(latitude_first_point, angUnit),
5259
0
            Angle(longitude_first_point, angUnit),
5260
0
            Angle(latitude_second_point, angUnit),
5261
0
            Angle(longitude_secon_point, angUnit),
5262
0
            Length(false_easting, linearUnit),
5263
0
            Length(false_northing, linearUnit));
5264
0
        return proj_create_conversion(ctx, conv);
5265
0
    } catch (const std::exception &e) {
5266
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5267
0
    }
5268
0
    return nullptr;
5269
0
}
5270
// ---------------------------------------------------------------------------
5271
5272
/** \brief Instantiate a ProjectedCRS with a conversion based on the Tunisia
5273
 * Mining Grid projection method.
5274
 *
5275
 * See osgeo::proj::operation::Conversion::createTunisiaMiningGrid().
5276
 *
5277
 * Linear parameters are expressed in (linear_unit_name,
5278
 * linear_unit_conv_factor).
5279
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5280
 *
5281
 * @since 9.2
5282
 */
5283
PJ *proj_create_conversion_tunisia_mining_grid(
5284
    PJ_CONTEXT *ctx, double center_lat, double center_long,
5285
    double false_easting, double false_northing, const char *ang_unit_name,
5286
    double ang_unit_conv_factor, const char *linear_unit_name,
5287
0
    double linear_unit_conv_factor) {
5288
0
    SANITIZE_CTX(ctx);
5289
0
    try {
5290
0
        UnitOfMeasure linearUnit(
5291
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5292
0
        UnitOfMeasure angUnit(
5293
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5294
0
        auto conv = Conversion::createTunisiaMiningGrid(
5295
0
            PropertyMap(), Angle(center_lat, angUnit),
5296
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
5297
0
            Length(false_northing, linearUnit));
5298
0
        return proj_create_conversion(ctx, conv);
5299
0
    } catch (const std::exception &e) {
5300
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5301
0
    }
5302
0
    return nullptr;
5303
0
}
5304
// ---------------------------------------------------------------------------
5305
5306
/** \brief Instantiate a ProjectedCRS with a conversion based on the Tunisia
5307
 * Mining Grid projection method.
5308
 *
5309
 * See osgeo::proj::operation::Conversion::createTunisiaMiningGrid().
5310
 *
5311
 * Linear parameters are expressed in (linear_unit_name,
5312
 * linear_unit_conv_factor).
5313
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5314
 *
5315
 * @deprecated Replaced by proj_create_conversion_tunisia_mining_grid
5316
 */
5317
PJ *proj_create_conversion_tunisia_mapping_grid(
5318
    PJ_CONTEXT *ctx, double center_lat, double center_long,
5319
    double false_easting, double false_northing, const char *ang_unit_name,
5320
    double ang_unit_conv_factor, const char *linear_unit_name,
5321
0
    double linear_unit_conv_factor) {
5322
0
    SANITIZE_CTX(ctx);
5323
0
    try {
5324
0
        UnitOfMeasure linearUnit(
5325
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5326
0
        UnitOfMeasure angUnit(
5327
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5328
0
        auto conv = Conversion::createTunisiaMiningGrid(
5329
0
            PropertyMap(), Angle(center_lat, angUnit),
5330
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
5331
0
            Length(false_northing, linearUnit));
5332
0
        return proj_create_conversion(ctx, conv);
5333
0
    } catch (const std::exception &e) {
5334
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5335
0
    }
5336
0
    return nullptr;
5337
0
}
5338
// ---------------------------------------------------------------------------
5339
5340
/** \brief Instantiate a ProjectedCRS with a conversion based on the Albers
5341
 * Conic Equal Area projection method.
5342
 *
5343
 * See osgeo::proj::operation::Conversion::createAlbersEqualArea().
5344
 *
5345
 * Linear parameters are expressed in (linear_unit_name,
5346
 * linear_unit_conv_factor).
5347
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5348
 */
5349
PJ *proj_create_conversion_albers_equal_area(
5350
    PJ_CONTEXT *ctx, double latitude_false_origin,
5351
    double longitude_false_origin, double latitude_first_parallel,
5352
    double latitude_second_parallel, double easting_false_origin,
5353
    double northing_false_origin, const char *ang_unit_name,
5354
    double ang_unit_conv_factor, const char *linear_unit_name,
5355
0
    double linear_unit_conv_factor) {
5356
0
    SANITIZE_CTX(ctx);
5357
0
    try {
5358
0
        UnitOfMeasure linearUnit(
5359
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5360
0
        UnitOfMeasure angUnit(
5361
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5362
0
        auto conv = Conversion::createAlbersEqualArea(
5363
0
            PropertyMap(), Angle(latitude_false_origin, angUnit),
5364
0
            Angle(longitude_false_origin, angUnit),
5365
0
            Angle(latitude_first_parallel, angUnit),
5366
0
            Angle(latitude_second_parallel, angUnit),
5367
0
            Length(easting_false_origin, linearUnit),
5368
0
            Length(northing_false_origin, linearUnit));
5369
0
        return proj_create_conversion(ctx, conv);
5370
0
    } catch (const std::exception &e) {
5371
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5372
0
    }
5373
0
    return nullptr;
5374
0
}
5375
// ---------------------------------------------------------------------------
5376
5377
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5378
 * Conic Conformal 1SP projection method.
5379
 *
5380
 * See osgeo::proj::operation::Conversion::createLambertConicConformal_1SP().
5381
 *
5382
 * Linear parameters are expressed in (linear_unit_name,
5383
 * linear_unit_conv_factor).
5384
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5385
 */
5386
PJ *proj_create_conversion_lambert_conic_conformal_1sp(
5387
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
5388
    double false_easting, double false_northing, const char *ang_unit_name,
5389
    double ang_unit_conv_factor, const char *linear_unit_name,
5390
0
    double linear_unit_conv_factor) {
5391
0
    SANITIZE_CTX(ctx);
5392
0
    try {
5393
0
        UnitOfMeasure linearUnit(
5394
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5395
0
        UnitOfMeasure angUnit(
5396
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5397
0
        auto conv = Conversion::createLambertConicConformal_1SP(
5398
0
            PropertyMap(), Angle(center_lat, angUnit),
5399
0
            Angle(center_long, angUnit), Scale(scale),
5400
0
            Length(false_easting, linearUnit),
5401
0
            Length(false_northing, linearUnit));
5402
0
        return proj_create_conversion(ctx, conv);
5403
0
    } catch (const std::exception &e) {
5404
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5405
0
    }
5406
0
    return nullptr;
5407
0
}
5408
// ---------------------------------------------------------------------------
5409
5410
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5411
 * Conic Conformal (1SP Variant B) projection method.
5412
 *
5413
 * See
5414
 * osgeo::proj::operation::Conversion::createLambertConicConformal_1SP_VariantB().
5415
 *
5416
 * Linear parameters are expressed in (linear_unit_name,
5417
 * linear_unit_conv_factor).
5418
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5419
 * @since 9.2.1
5420
 */
5421
PJ *proj_create_conversion_lambert_conic_conformal_1sp_variant_b(
5422
    PJ_CONTEXT *ctx, double latitude_nat_origin, double scale,
5423
    double latitude_false_origin, double longitude_false_origin,
5424
    double easting_false_origin, double northing_false_origin,
5425
    const char *ang_unit_name, double ang_unit_conv_factor,
5426
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
5427
0
    SANITIZE_CTX(ctx);
5428
0
    try {
5429
0
        UnitOfMeasure linearUnit(
5430
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5431
0
        UnitOfMeasure angUnit(
5432
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5433
0
        auto conv = Conversion::createLambertConicConformal_1SP_VariantB(
5434
0
            PropertyMap(), Angle(latitude_nat_origin, angUnit), Scale(scale),
5435
0
            Angle(latitude_false_origin, angUnit),
5436
0
            Angle(longitude_false_origin, angUnit),
5437
0
            Length(easting_false_origin, linearUnit),
5438
0
            Length(northing_false_origin, linearUnit));
5439
0
        return proj_create_conversion(ctx, conv);
5440
0
    } catch (const std::exception &e) {
5441
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5442
0
    }
5443
0
    return nullptr;
5444
0
}
5445
// ---------------------------------------------------------------------------
5446
5447
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5448
 * Conic Conformal (2SP) projection method.
5449
 *
5450
 * See osgeo::proj::operation::Conversion::createLambertConicConformal_2SP().
5451
 *
5452
 * Linear parameters are expressed in (linear_unit_name,
5453
 * linear_unit_conv_factor).
5454
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5455
 */
5456
PJ *proj_create_conversion_lambert_conic_conformal_2sp(
5457
    PJ_CONTEXT *ctx, double latitude_false_origin,
5458
    double longitude_false_origin, double latitude_first_parallel,
5459
    double latitude_second_parallel, double easting_false_origin,
5460
    double northing_false_origin, const char *ang_unit_name,
5461
    double ang_unit_conv_factor, const char *linear_unit_name,
5462
0
    double linear_unit_conv_factor) {
5463
0
    SANITIZE_CTX(ctx);
5464
0
    try {
5465
0
        UnitOfMeasure linearUnit(
5466
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5467
0
        UnitOfMeasure angUnit(
5468
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5469
0
        auto conv = Conversion::createLambertConicConformal_2SP(
5470
0
            PropertyMap(), Angle(latitude_false_origin, angUnit),
5471
0
            Angle(longitude_false_origin, angUnit),
5472
0
            Angle(latitude_first_parallel, angUnit),
5473
0
            Angle(latitude_second_parallel, angUnit),
5474
0
            Length(easting_false_origin, linearUnit),
5475
0
            Length(northing_false_origin, linearUnit));
5476
0
        return proj_create_conversion(ctx, conv);
5477
0
    } catch (const std::exception &e) {
5478
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5479
0
    }
5480
0
    return nullptr;
5481
0
}
5482
// ---------------------------------------------------------------------------
5483
5484
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5485
 * Conic Conformal (2SP Michigan) projection method.
5486
 *
5487
 * See
5488
 * osgeo::proj::operation::Conversion::createLambertConicConformal_2SP_Michigan().
5489
 *
5490
 * Linear parameters are expressed in (linear_unit_name,
5491
 * linear_unit_conv_factor).
5492
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5493
 */
5494
PJ *proj_create_conversion_lambert_conic_conformal_2sp_michigan(
5495
    PJ_CONTEXT *ctx, double latitude_false_origin,
5496
    double longitude_false_origin, double latitude_first_parallel,
5497
    double latitude_second_parallel, double easting_false_origin,
5498
    double northing_false_origin, double ellipsoid_scaling_factor,
5499
    const char *ang_unit_name, double ang_unit_conv_factor,
5500
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
5501
0
    SANITIZE_CTX(ctx);
5502
0
    try {
5503
0
        UnitOfMeasure linearUnit(
5504
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5505
0
        UnitOfMeasure angUnit(
5506
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5507
0
        auto conv = Conversion::createLambertConicConformal_2SP_Michigan(
5508
0
            PropertyMap(), Angle(latitude_false_origin, angUnit),
5509
0
            Angle(longitude_false_origin, angUnit),
5510
0
            Angle(latitude_first_parallel, angUnit),
5511
0
            Angle(latitude_second_parallel, angUnit),
5512
0
            Length(easting_false_origin, linearUnit),
5513
0
            Length(northing_false_origin, linearUnit),
5514
0
            Scale(ellipsoid_scaling_factor));
5515
0
        return proj_create_conversion(ctx, conv);
5516
0
    } catch (const std::exception &e) {
5517
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5518
0
    }
5519
0
    return nullptr;
5520
0
}
5521
// ---------------------------------------------------------------------------
5522
5523
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5524
 * Conic Conformal (2SP Belgium) projection method.
5525
 *
5526
 * See
5527
 * osgeo::proj::operation::Conversion::createLambertConicConformal_2SP_Belgium().
5528
 *
5529
 * Linear parameters are expressed in (linear_unit_name,
5530
 * linear_unit_conv_factor).
5531
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5532
 */
5533
PJ *proj_create_conversion_lambert_conic_conformal_2sp_belgium(
5534
    PJ_CONTEXT *ctx, double latitude_false_origin,
5535
    double longitude_false_origin, double latitude_first_parallel,
5536
    double latitude_second_parallel, double easting_false_origin,
5537
    double northing_false_origin, const char *ang_unit_name,
5538
    double ang_unit_conv_factor, const char *linear_unit_name,
5539
0
    double linear_unit_conv_factor) {
5540
0
    SANITIZE_CTX(ctx);
5541
0
    try {
5542
0
        UnitOfMeasure linearUnit(
5543
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5544
0
        UnitOfMeasure angUnit(
5545
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5546
0
        auto conv = Conversion::createLambertConicConformal_2SP_Belgium(
5547
0
            PropertyMap(), Angle(latitude_false_origin, angUnit),
5548
0
            Angle(longitude_false_origin, angUnit),
5549
0
            Angle(latitude_first_parallel, angUnit),
5550
0
            Angle(latitude_second_parallel, angUnit),
5551
0
            Length(easting_false_origin, linearUnit),
5552
0
            Length(northing_false_origin, linearUnit));
5553
0
        return proj_create_conversion(ctx, conv);
5554
0
    } catch (const std::exception &e) {
5555
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5556
0
    }
5557
0
    return nullptr;
5558
0
}
5559
// ---------------------------------------------------------------------------
5560
5561
/** \brief Instantiate a ProjectedCRS with a conversion based on the Modified
5562
 * Azimuthal Equidistant projection method.
5563
 *
5564
 * See osgeo::proj::operation::Conversion::createAzimuthalEquidistant().
5565
 *
5566
 * Linear parameters are expressed in (linear_unit_name,
5567
 * linear_unit_conv_factor).
5568
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5569
 */
5570
PJ *proj_create_conversion_azimuthal_equidistant(
5571
    PJ_CONTEXT *ctx, double latitude_nat_origin, double longitude_nat_origin,
5572
    double false_easting, double false_northing, const char *ang_unit_name,
5573
    double ang_unit_conv_factor, const char *linear_unit_name,
5574
0
    double linear_unit_conv_factor) {
5575
0
    SANITIZE_CTX(ctx);
5576
0
    try {
5577
0
        UnitOfMeasure linearUnit(
5578
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5579
0
        UnitOfMeasure angUnit(
5580
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5581
0
        auto conv = Conversion::createAzimuthalEquidistant(
5582
0
            PropertyMap(), Angle(latitude_nat_origin, angUnit),
5583
0
            Angle(longitude_nat_origin, angUnit),
5584
0
            Length(false_easting, linearUnit),
5585
0
            Length(false_northing, linearUnit));
5586
0
        return proj_create_conversion(ctx, conv);
5587
0
    } catch (const std::exception &e) {
5588
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5589
0
    }
5590
0
    return nullptr;
5591
0
}
5592
// ---------------------------------------------------------------------------
5593
5594
/** \brief Instantiate a ProjectedCRS with a conversion based on the Guam
5595
 * Projection projection method.
5596
 *
5597
 * See osgeo::proj::operation::Conversion::createGuamProjection().
5598
 *
5599
 * Linear parameters are expressed in (linear_unit_name,
5600
 * linear_unit_conv_factor).
5601
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5602
 */
5603
PJ *proj_create_conversion_guam_projection(
5604
    PJ_CONTEXT *ctx, double latitude_nat_origin, double longitude_nat_origin,
5605
    double false_easting, double false_northing, const char *ang_unit_name,
5606
    double ang_unit_conv_factor, const char *linear_unit_name,
5607
0
    double linear_unit_conv_factor) {
5608
0
    SANITIZE_CTX(ctx);
5609
0
    try {
5610
0
        UnitOfMeasure linearUnit(
5611
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5612
0
        UnitOfMeasure angUnit(
5613
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5614
0
        auto conv = Conversion::createGuamProjection(
5615
0
            PropertyMap(), Angle(latitude_nat_origin, angUnit),
5616
0
            Angle(longitude_nat_origin, angUnit),
5617
0
            Length(false_easting, linearUnit),
5618
0
            Length(false_northing, linearUnit));
5619
0
        return proj_create_conversion(ctx, conv);
5620
0
    } catch (const std::exception &e) {
5621
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5622
0
    }
5623
0
    return nullptr;
5624
0
}
5625
// ---------------------------------------------------------------------------
5626
5627
/** \brief Instantiate a ProjectedCRS with a conversion based on the Bonne
5628
 * projection method.
5629
 *
5630
 * See osgeo::proj::operation::Conversion::createBonne().
5631
 *
5632
 * Linear parameters are expressed in (linear_unit_name,
5633
 * linear_unit_conv_factor).
5634
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5635
 */
5636
PJ *proj_create_conversion_bonne(PJ_CONTEXT *ctx, double latitude_nat_origin,
5637
                                 double longitude_nat_origin,
5638
                                 double false_easting, double false_northing,
5639
                                 const char *ang_unit_name,
5640
                                 double ang_unit_conv_factor,
5641
                                 const char *linear_unit_name,
5642
0
                                 double linear_unit_conv_factor) {
5643
0
    SANITIZE_CTX(ctx);
5644
0
    try {
5645
0
        UnitOfMeasure linearUnit(
5646
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5647
0
        UnitOfMeasure angUnit(
5648
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5649
0
        auto conv = Conversion::createBonne(
5650
0
            PropertyMap(), Angle(latitude_nat_origin, angUnit),
5651
0
            Angle(longitude_nat_origin, angUnit),
5652
0
            Length(false_easting, linearUnit),
5653
0
            Length(false_northing, linearUnit));
5654
0
        return proj_create_conversion(ctx, conv);
5655
0
    } catch (const std::exception &e) {
5656
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5657
0
    }
5658
0
    return nullptr;
5659
0
}
5660
// ---------------------------------------------------------------------------
5661
5662
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5663
 * Cylindrical Equal Area (Spherical) projection method.
5664
 *
5665
 * See
5666
 * osgeo::proj::operation::Conversion::createLambertCylindricalEqualAreaSpherical().
5667
 *
5668
 * Linear parameters are expressed in (linear_unit_name,
5669
 * linear_unit_conv_factor).
5670
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5671
 */
5672
PJ *proj_create_conversion_lambert_cylindrical_equal_area_spherical(
5673
    PJ_CONTEXT *ctx, double latitude_first_parallel,
5674
    double longitude_nat_origin, double false_easting, double false_northing,
5675
    const char *ang_unit_name, double ang_unit_conv_factor,
5676
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
5677
0
    SANITIZE_CTX(ctx);
5678
0
    try {
5679
0
        UnitOfMeasure linearUnit(
5680
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5681
0
        UnitOfMeasure angUnit(
5682
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5683
0
        auto conv = Conversion::createLambertCylindricalEqualAreaSpherical(
5684
0
            PropertyMap(), Angle(latitude_first_parallel, angUnit),
5685
0
            Angle(longitude_nat_origin, angUnit),
5686
0
            Length(false_easting, linearUnit),
5687
0
            Length(false_northing, linearUnit));
5688
0
        return proj_create_conversion(ctx, conv);
5689
0
    } catch (const std::exception &e) {
5690
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5691
0
    }
5692
0
    return nullptr;
5693
0
}
5694
// ---------------------------------------------------------------------------
5695
5696
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
5697
 * Cylindrical Equal Area (ellipsoidal form) projection method.
5698
 *
5699
 * See osgeo::proj::operation::Conversion::createLambertCylindricalEqualArea().
5700
 *
5701
 * Linear parameters are expressed in (linear_unit_name,
5702
 * linear_unit_conv_factor).
5703
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5704
 */
5705
PJ *proj_create_conversion_lambert_cylindrical_equal_area(
5706
    PJ_CONTEXT *ctx, double latitude_first_parallel,
5707
    double longitude_nat_origin, double false_easting, double false_northing,
5708
    const char *ang_unit_name, double ang_unit_conv_factor,
5709
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
5710
0
    SANITIZE_CTX(ctx);
5711
0
    try {
5712
0
        UnitOfMeasure linearUnit(
5713
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5714
0
        UnitOfMeasure angUnit(
5715
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5716
0
        auto conv = Conversion::createLambertCylindricalEqualArea(
5717
0
            PropertyMap(), Angle(latitude_first_parallel, angUnit),
5718
0
            Angle(longitude_nat_origin, angUnit),
5719
0
            Length(false_easting, linearUnit),
5720
0
            Length(false_northing, linearUnit));
5721
0
        return proj_create_conversion(ctx, conv);
5722
0
    } catch (const std::exception &e) {
5723
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5724
0
    }
5725
0
    return nullptr;
5726
0
}
5727
// ---------------------------------------------------------------------------
5728
5729
/** \brief Instantiate a ProjectedCRS with a conversion based on the
5730
 * Cassini-Soldner projection method.
5731
 *
5732
 * See osgeo::proj::operation::Conversion::createCassiniSoldner().
5733
 *
5734
 * Linear parameters are expressed in (linear_unit_name,
5735
 * linear_unit_conv_factor).
5736
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5737
 */
5738
PJ *proj_create_conversion_cassini_soldner(
5739
    PJ_CONTEXT *ctx, double center_lat, double center_long,
5740
    double false_easting, double false_northing, const char *ang_unit_name,
5741
    double ang_unit_conv_factor, const char *linear_unit_name,
5742
0
    double linear_unit_conv_factor) {
5743
0
    SANITIZE_CTX(ctx);
5744
0
    try {
5745
0
        UnitOfMeasure linearUnit(
5746
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5747
0
        UnitOfMeasure angUnit(
5748
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5749
0
        auto conv = Conversion::createCassiniSoldner(
5750
0
            PropertyMap(), Angle(center_lat, angUnit),
5751
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
5752
0
            Length(false_northing, linearUnit));
5753
0
        return proj_create_conversion(ctx, conv);
5754
0
    } catch (const std::exception &e) {
5755
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5756
0
    }
5757
0
    return nullptr;
5758
0
}
5759
// ---------------------------------------------------------------------------
5760
5761
/** \brief Instantiate a ProjectedCRS with a conversion based on the Equidistant
5762
 * Conic projection method.
5763
 *
5764
 * See osgeo::proj::operation::Conversion::createEquidistantConic().
5765
 *
5766
 * Linear parameters are expressed in (linear_unit_name,
5767
 * linear_unit_conv_factor).
5768
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5769
 */
5770
PJ *proj_create_conversion_equidistant_conic(
5771
    PJ_CONTEXT *ctx, double center_lat, double center_long,
5772
    double latitude_first_parallel, double latitude_second_parallel,
5773
    double false_easting, double false_northing, const char *ang_unit_name,
5774
    double ang_unit_conv_factor, const char *linear_unit_name,
5775
0
    double linear_unit_conv_factor) {
5776
0
    SANITIZE_CTX(ctx);
5777
0
    try {
5778
0
        UnitOfMeasure linearUnit(
5779
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5780
0
        UnitOfMeasure angUnit(
5781
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5782
0
        auto conv = Conversion::createEquidistantConic(
5783
0
            PropertyMap(), Angle(center_lat, angUnit),
5784
0
            Angle(center_long, angUnit),
5785
0
            Angle(latitude_first_parallel, angUnit),
5786
0
            Angle(latitude_second_parallel, angUnit),
5787
0
            Length(false_easting, linearUnit),
5788
0
            Length(false_northing, linearUnit));
5789
0
        return proj_create_conversion(ctx, conv);
5790
0
    } catch (const std::exception &e) {
5791
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5792
0
    }
5793
0
    return nullptr;
5794
0
}
5795
// ---------------------------------------------------------------------------
5796
5797
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert I
5798
 * projection method.
5799
 *
5800
 * See osgeo::proj::operation::Conversion::createEckertI().
5801
 *
5802
 * Linear parameters are expressed in (linear_unit_name,
5803
 * linear_unit_conv_factor).
5804
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5805
 */
5806
PJ *proj_create_conversion_eckert_i(PJ_CONTEXT *ctx, double center_long,
5807
                                    double false_easting, double false_northing,
5808
                                    const char *ang_unit_name,
5809
                                    double ang_unit_conv_factor,
5810
                                    const char *linear_unit_name,
5811
0
                                    double linear_unit_conv_factor) {
5812
0
    SANITIZE_CTX(ctx);
5813
0
    try {
5814
0
        UnitOfMeasure linearUnit(
5815
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5816
0
        UnitOfMeasure angUnit(
5817
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5818
0
        auto conv = Conversion::createEckertI(
5819
0
            PropertyMap(), Angle(center_long, angUnit),
5820
0
            Length(false_easting, linearUnit),
5821
0
            Length(false_northing, linearUnit));
5822
0
        return proj_create_conversion(ctx, conv);
5823
0
    } catch (const std::exception &e) {
5824
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5825
0
    }
5826
0
    return nullptr;
5827
0
}
5828
// ---------------------------------------------------------------------------
5829
5830
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert II
5831
 * projection method.
5832
 *
5833
 * See osgeo::proj::operation::Conversion::createEckertII().
5834
 *
5835
 * Linear parameters are expressed in (linear_unit_name,
5836
 * linear_unit_conv_factor).
5837
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5838
 */
5839
PJ *proj_create_conversion_eckert_ii(PJ_CONTEXT *ctx, double center_long,
5840
                                     double false_easting,
5841
                                     double false_northing,
5842
                                     const char *ang_unit_name,
5843
                                     double ang_unit_conv_factor,
5844
                                     const char *linear_unit_name,
5845
0
                                     double linear_unit_conv_factor) {
5846
0
    SANITIZE_CTX(ctx);
5847
0
    try {
5848
0
        UnitOfMeasure linearUnit(
5849
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5850
0
        UnitOfMeasure angUnit(
5851
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5852
0
        auto conv = Conversion::createEckertII(
5853
0
            PropertyMap(), Angle(center_long, angUnit),
5854
0
            Length(false_easting, linearUnit),
5855
0
            Length(false_northing, linearUnit));
5856
0
        return proj_create_conversion(ctx, conv);
5857
0
    } catch (const std::exception &e) {
5858
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5859
0
    }
5860
0
    return nullptr;
5861
0
}
5862
// ---------------------------------------------------------------------------
5863
5864
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert III
5865
 * projection method.
5866
 *
5867
 * See osgeo::proj::operation::Conversion::createEckertIII().
5868
 *
5869
 * Linear parameters are expressed in (linear_unit_name,
5870
 * linear_unit_conv_factor).
5871
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5872
 */
5873
PJ *proj_create_conversion_eckert_iii(PJ_CONTEXT *ctx, double center_long,
5874
                                      double false_easting,
5875
                                      double false_northing,
5876
                                      const char *ang_unit_name,
5877
                                      double ang_unit_conv_factor,
5878
                                      const char *linear_unit_name,
5879
0
                                      double linear_unit_conv_factor) {
5880
0
    SANITIZE_CTX(ctx);
5881
0
    try {
5882
0
        UnitOfMeasure linearUnit(
5883
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5884
0
        UnitOfMeasure angUnit(
5885
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5886
0
        auto conv = Conversion::createEckertIII(
5887
0
            PropertyMap(), Angle(center_long, angUnit),
5888
0
            Length(false_easting, linearUnit),
5889
0
            Length(false_northing, linearUnit));
5890
0
        return proj_create_conversion(ctx, conv);
5891
0
    } catch (const std::exception &e) {
5892
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5893
0
    }
5894
0
    return nullptr;
5895
0
}
5896
// ---------------------------------------------------------------------------
5897
5898
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert IV
5899
 * projection method.
5900
 *
5901
 * See osgeo::proj::operation::Conversion::createEckertIV().
5902
 *
5903
 * Linear parameters are expressed in (linear_unit_name,
5904
 * linear_unit_conv_factor).
5905
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5906
 */
5907
PJ *proj_create_conversion_eckert_iv(PJ_CONTEXT *ctx, double center_long,
5908
                                     double false_easting,
5909
                                     double false_northing,
5910
                                     const char *ang_unit_name,
5911
                                     double ang_unit_conv_factor,
5912
                                     const char *linear_unit_name,
5913
0
                                     double linear_unit_conv_factor) {
5914
0
    SANITIZE_CTX(ctx);
5915
0
    try {
5916
0
        UnitOfMeasure linearUnit(
5917
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5918
0
        UnitOfMeasure angUnit(
5919
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5920
0
        auto conv = Conversion::createEckertIV(
5921
0
            PropertyMap(), Angle(center_long, angUnit),
5922
0
            Length(false_easting, linearUnit),
5923
0
            Length(false_northing, linearUnit));
5924
0
        return proj_create_conversion(ctx, conv);
5925
0
    } catch (const std::exception &e) {
5926
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5927
0
    }
5928
0
    return nullptr;
5929
0
}
5930
// ---------------------------------------------------------------------------
5931
5932
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert V
5933
 * projection method.
5934
 *
5935
 * See osgeo::proj::operation::Conversion::createEckertV().
5936
 *
5937
 * Linear parameters are expressed in (linear_unit_name,
5938
 * linear_unit_conv_factor).
5939
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5940
 */
5941
PJ *proj_create_conversion_eckert_v(PJ_CONTEXT *ctx, double center_long,
5942
                                    double false_easting, double false_northing,
5943
                                    const char *ang_unit_name,
5944
                                    double ang_unit_conv_factor,
5945
                                    const char *linear_unit_name,
5946
0
                                    double linear_unit_conv_factor) {
5947
0
    SANITIZE_CTX(ctx);
5948
0
    try {
5949
0
        UnitOfMeasure linearUnit(
5950
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5951
0
        UnitOfMeasure angUnit(
5952
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5953
0
        auto conv = Conversion::createEckertV(
5954
0
            PropertyMap(), Angle(center_long, angUnit),
5955
0
            Length(false_easting, linearUnit),
5956
0
            Length(false_northing, linearUnit));
5957
0
        return proj_create_conversion(ctx, conv);
5958
0
    } catch (const std::exception &e) {
5959
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5960
0
    }
5961
0
    return nullptr;
5962
0
}
5963
// ---------------------------------------------------------------------------
5964
5965
/** \brief Instantiate a ProjectedCRS with a conversion based on the Eckert VI
5966
 * projection method.
5967
 *
5968
 * See osgeo::proj::operation::Conversion::createEckertVI().
5969
 *
5970
 * Linear parameters are expressed in (linear_unit_name,
5971
 * linear_unit_conv_factor).
5972
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
5973
 */
5974
PJ *proj_create_conversion_eckert_vi(PJ_CONTEXT *ctx, double center_long,
5975
                                     double false_easting,
5976
                                     double false_northing,
5977
                                     const char *ang_unit_name,
5978
                                     double ang_unit_conv_factor,
5979
                                     const char *linear_unit_name,
5980
0
                                     double linear_unit_conv_factor) {
5981
0
    SANITIZE_CTX(ctx);
5982
0
    try {
5983
0
        UnitOfMeasure linearUnit(
5984
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
5985
0
        UnitOfMeasure angUnit(
5986
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
5987
0
        auto conv = Conversion::createEckertVI(
5988
0
            PropertyMap(), Angle(center_long, angUnit),
5989
0
            Length(false_easting, linearUnit),
5990
0
            Length(false_northing, linearUnit));
5991
0
        return proj_create_conversion(ctx, conv);
5992
0
    } catch (const std::exception &e) {
5993
0
        proj_log_error(ctx, __FUNCTION__, e.what());
5994
0
    }
5995
0
    return nullptr;
5996
0
}
5997
// ---------------------------------------------------------------------------
5998
5999
/** \brief Instantiate a ProjectedCRS with a conversion based on the Equidistant
6000
 * Cylindrical projection method.
6001
 *
6002
 * See osgeo::proj::operation::Conversion::createEquidistantCylindrical().
6003
 *
6004
 * Linear parameters are expressed in (linear_unit_name,
6005
 * linear_unit_conv_factor).
6006
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6007
 */
6008
PJ *proj_create_conversion_equidistant_cylindrical(
6009
    PJ_CONTEXT *ctx, double latitude_first_parallel,
6010
    double longitude_nat_origin, double false_easting, double false_northing,
6011
    const char *ang_unit_name, double ang_unit_conv_factor,
6012
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
6013
0
    SANITIZE_CTX(ctx);
6014
0
    try {
6015
0
        UnitOfMeasure linearUnit(
6016
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6017
0
        UnitOfMeasure angUnit(
6018
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6019
0
        auto conv = Conversion::createEquidistantCylindrical(
6020
0
            PropertyMap(), Angle(latitude_first_parallel, angUnit),
6021
0
            Angle(longitude_nat_origin, angUnit),
6022
0
            Length(false_easting, linearUnit),
6023
0
            Length(false_northing, linearUnit));
6024
0
        return proj_create_conversion(ctx, conv);
6025
0
    } catch (const std::exception &e) {
6026
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6027
0
    }
6028
0
    return nullptr;
6029
0
}
6030
// ---------------------------------------------------------------------------
6031
6032
/** \brief Instantiate a ProjectedCRS with a conversion based on the Equidistant
6033
 * Cylindrical (Spherical) projection method.
6034
 *
6035
 * See
6036
 * osgeo::proj::operation::Conversion::createEquidistantCylindricalSpherical().
6037
 *
6038
 * Linear parameters are expressed in (linear_unit_name,
6039
 * linear_unit_conv_factor).
6040
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6041
 */
6042
PJ *proj_create_conversion_equidistant_cylindrical_spherical(
6043
    PJ_CONTEXT *ctx, double latitude_first_parallel,
6044
    double longitude_nat_origin, double false_easting, double false_northing,
6045
    const char *ang_unit_name, double ang_unit_conv_factor,
6046
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
6047
0
    SANITIZE_CTX(ctx);
6048
0
    try {
6049
0
        UnitOfMeasure linearUnit(
6050
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6051
0
        UnitOfMeasure angUnit(
6052
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6053
0
        auto conv = Conversion::createEquidistantCylindricalSpherical(
6054
0
            PropertyMap(), Angle(latitude_first_parallel, angUnit),
6055
0
            Angle(longitude_nat_origin, angUnit),
6056
0
            Length(false_easting, linearUnit),
6057
0
            Length(false_northing, linearUnit));
6058
0
        return proj_create_conversion(ctx, conv);
6059
0
    } catch (const std::exception &e) {
6060
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6061
0
    }
6062
0
    return nullptr;
6063
0
}
6064
// ---------------------------------------------------------------------------
6065
6066
/** \brief Instantiate a ProjectedCRS with a conversion based on the Gall
6067
 * (Stereographic) projection method.
6068
 *
6069
 * See osgeo::proj::operation::Conversion::createGall().
6070
 *
6071
 * Linear parameters are expressed in (linear_unit_name,
6072
 * linear_unit_conv_factor).
6073
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6074
 */
6075
PJ *proj_create_conversion_gall(PJ_CONTEXT *ctx, double center_long,
6076
                                double false_easting, double false_northing,
6077
                                const char *ang_unit_name,
6078
                                double ang_unit_conv_factor,
6079
                                const char *linear_unit_name,
6080
0
                                double linear_unit_conv_factor) {
6081
0
    SANITIZE_CTX(ctx);
6082
0
    try {
6083
0
        UnitOfMeasure linearUnit(
6084
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6085
0
        UnitOfMeasure angUnit(
6086
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6087
0
        auto conv =
6088
0
            Conversion::createGall(PropertyMap(), Angle(center_long, angUnit),
6089
0
                                   Length(false_easting, linearUnit),
6090
0
                                   Length(false_northing, linearUnit));
6091
0
        return proj_create_conversion(ctx, conv);
6092
0
    } catch (const std::exception &e) {
6093
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6094
0
    }
6095
0
    return nullptr;
6096
0
}
6097
// ---------------------------------------------------------------------------
6098
6099
/** \brief Instantiate a ProjectedCRS with a conversion based on the Goode
6100
 * Homolosine projection method.
6101
 *
6102
 * See osgeo::proj::operation::Conversion::createGoodeHomolosine().
6103
 *
6104
 * Linear parameters are expressed in (linear_unit_name,
6105
 * linear_unit_conv_factor).
6106
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6107
 */
6108
PJ *proj_create_conversion_goode_homolosine(PJ_CONTEXT *ctx, double center_long,
6109
                                            double false_easting,
6110
                                            double false_northing,
6111
                                            const char *ang_unit_name,
6112
                                            double ang_unit_conv_factor,
6113
                                            const char *linear_unit_name,
6114
0
                                            double linear_unit_conv_factor) {
6115
0
    SANITIZE_CTX(ctx);
6116
0
    try {
6117
0
        UnitOfMeasure linearUnit(
6118
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6119
0
        UnitOfMeasure angUnit(
6120
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6121
0
        auto conv = Conversion::createGoodeHomolosine(
6122
0
            PropertyMap(), Angle(center_long, angUnit),
6123
0
            Length(false_easting, linearUnit),
6124
0
            Length(false_northing, linearUnit));
6125
0
        return proj_create_conversion(ctx, conv);
6126
0
    } catch (const std::exception &e) {
6127
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6128
0
    }
6129
0
    return nullptr;
6130
0
}
6131
// ---------------------------------------------------------------------------
6132
6133
/** \brief Instantiate a ProjectedCRS with a conversion based on the Interrupted
6134
 * Goode Homolosine projection method.
6135
 *
6136
 * See osgeo::proj::operation::Conversion::createInterruptedGoodeHomolosine().
6137
 *
6138
 * Linear parameters are expressed in (linear_unit_name,
6139
 * linear_unit_conv_factor).
6140
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6141
 */
6142
PJ *proj_create_conversion_interrupted_goode_homolosine(
6143
    PJ_CONTEXT *ctx, double center_long, double false_easting,
6144
    double false_northing, const char *ang_unit_name,
6145
    double ang_unit_conv_factor, const char *linear_unit_name,
6146
0
    double linear_unit_conv_factor) {
6147
0
    SANITIZE_CTX(ctx);
6148
0
    try {
6149
0
        UnitOfMeasure linearUnit(
6150
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6151
0
        UnitOfMeasure angUnit(
6152
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6153
0
        auto conv = Conversion::createInterruptedGoodeHomolosine(
6154
0
            PropertyMap(), Angle(center_long, angUnit),
6155
0
            Length(false_easting, linearUnit),
6156
0
            Length(false_northing, linearUnit));
6157
0
        return proj_create_conversion(ctx, conv);
6158
0
    } catch (const std::exception &e) {
6159
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6160
0
    }
6161
0
    return nullptr;
6162
0
}
6163
// ---------------------------------------------------------------------------
6164
6165
/** \brief Instantiate a ProjectedCRS with a conversion based on the
6166
 * Geostationary Satellite View projection method, with the sweep angle axis of
6167
 * the viewing instrument being x.
6168
 *
6169
 * See osgeo::proj::operation::Conversion::createGeostationarySatelliteSweepX().
6170
 *
6171
 * Linear parameters are expressed in (linear_unit_name,
6172
 * linear_unit_conv_factor).
6173
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6174
 */
6175
PJ *proj_create_conversion_geostationary_satellite_sweep_x(
6176
    PJ_CONTEXT *ctx, double center_long, double height, double false_easting,
6177
    double false_northing, const char *ang_unit_name,
6178
    double ang_unit_conv_factor, const char *linear_unit_name,
6179
0
    double linear_unit_conv_factor) {
6180
0
    SANITIZE_CTX(ctx);
6181
0
    try {
6182
0
        UnitOfMeasure linearUnit(
6183
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6184
0
        UnitOfMeasure angUnit(
6185
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6186
0
        auto conv = Conversion::createGeostationarySatelliteSweepX(
6187
0
            PropertyMap(), Angle(center_long, angUnit),
6188
0
            Length(height, linearUnit), Length(false_easting, linearUnit),
6189
0
            Length(false_northing, linearUnit));
6190
0
        return proj_create_conversion(ctx, conv);
6191
0
    } catch (const std::exception &e) {
6192
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6193
0
    }
6194
0
    return nullptr;
6195
0
}
6196
// ---------------------------------------------------------------------------
6197
6198
/** \brief Instantiate a ProjectedCRS with a conversion based on the
6199
 * Geostationary Satellite View projection method, with the sweep angle axis of
6200
 * the viewing instrument being y.
6201
 *
6202
 * See osgeo::proj::operation::Conversion::createGeostationarySatelliteSweepY().
6203
 *
6204
 * Linear parameters are expressed in (linear_unit_name,
6205
 * linear_unit_conv_factor).
6206
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6207
 */
6208
PJ *proj_create_conversion_geostationary_satellite_sweep_y(
6209
    PJ_CONTEXT *ctx, double center_long, double height, double false_easting,
6210
    double false_northing, const char *ang_unit_name,
6211
    double ang_unit_conv_factor, const char *linear_unit_name,
6212
0
    double linear_unit_conv_factor) {
6213
0
    SANITIZE_CTX(ctx);
6214
0
    try {
6215
0
        UnitOfMeasure linearUnit(
6216
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6217
0
        UnitOfMeasure angUnit(
6218
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6219
0
        auto conv = Conversion::createGeostationarySatelliteSweepY(
6220
0
            PropertyMap(), Angle(center_long, angUnit),
6221
0
            Length(height, linearUnit), Length(false_easting, linearUnit),
6222
0
            Length(false_northing, linearUnit));
6223
0
        return proj_create_conversion(ctx, conv);
6224
0
    } catch (const std::exception &e) {
6225
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6226
0
    }
6227
0
    return nullptr;
6228
0
}
6229
// ---------------------------------------------------------------------------
6230
6231
/** \brief Instantiate a ProjectedCRS with a conversion based on the Gnomonic
6232
 * projection method.
6233
 *
6234
 * See osgeo::proj::operation::Conversion::createGnomonic().
6235
 *
6236
 * Linear parameters are expressed in (linear_unit_name,
6237
 * linear_unit_conv_factor).
6238
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6239
 */
6240
PJ *proj_create_conversion_gnomonic(PJ_CONTEXT *ctx, double center_lat,
6241
                                    double center_long, double false_easting,
6242
                                    double false_northing,
6243
                                    const char *ang_unit_name,
6244
                                    double ang_unit_conv_factor,
6245
                                    const char *linear_unit_name,
6246
0
                                    double linear_unit_conv_factor) {
6247
0
    SANITIZE_CTX(ctx);
6248
0
    try {
6249
0
        UnitOfMeasure linearUnit(
6250
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6251
0
        UnitOfMeasure angUnit(
6252
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6253
0
        auto conv = Conversion::createGnomonic(
6254
0
            PropertyMap(), Angle(center_lat, angUnit),
6255
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
6256
0
            Length(false_northing, linearUnit));
6257
0
        return proj_create_conversion(ctx, conv);
6258
0
    } catch (const std::exception &e) {
6259
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6260
0
    }
6261
0
    return nullptr;
6262
0
}
6263
// ---------------------------------------------------------------------------
6264
6265
/** \brief Instantiate a ProjectedCRS with a conversion based on the Hotine
6266
 * Oblique Mercator (Variant A) projection method.
6267
 *
6268
 * See
6269
 * osgeo::proj::operation::Conversion::createHotineObliqueMercatorVariantA().
6270
 *
6271
 * Linear parameters are expressed in (linear_unit_name,
6272
 * linear_unit_conv_factor).
6273
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6274
 */
6275
PJ *proj_create_conversion_hotine_oblique_mercator_variant_a(
6276
    PJ_CONTEXT *ctx, double latitude_projection_centre,
6277
    double longitude_projection_centre, double azimuth_initial_line,
6278
    double angle_from_rectified_to_skrew_grid, double scale,
6279
    double false_easting, double false_northing, const char *ang_unit_name,
6280
    double ang_unit_conv_factor, const char *linear_unit_name,
6281
0
    double linear_unit_conv_factor) {
6282
0
    SANITIZE_CTX(ctx);
6283
0
    try {
6284
0
        UnitOfMeasure linearUnit(
6285
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6286
0
        UnitOfMeasure angUnit(
6287
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6288
0
        auto conv = Conversion::createHotineObliqueMercatorVariantA(
6289
0
            PropertyMap(), Angle(latitude_projection_centre, angUnit),
6290
0
            Angle(longitude_projection_centre, angUnit),
6291
0
            Angle(azimuth_initial_line, angUnit),
6292
0
            Angle(angle_from_rectified_to_skrew_grid, angUnit), Scale(scale),
6293
0
            Length(false_easting, linearUnit),
6294
0
            Length(false_northing, linearUnit));
6295
0
        return proj_create_conversion(ctx, conv);
6296
0
    } catch (const std::exception &e) {
6297
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6298
0
    }
6299
0
    return nullptr;
6300
0
}
6301
// ---------------------------------------------------------------------------
6302
6303
/** \brief Instantiate a ProjectedCRS with a conversion based on the Hotine
6304
 * Oblique Mercator (Variant B) projection method.
6305
 *
6306
 * See
6307
 * osgeo::proj::operation::Conversion::createHotineObliqueMercatorVariantB().
6308
 *
6309
 * Linear parameters are expressed in (linear_unit_name,
6310
 * linear_unit_conv_factor).
6311
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6312
 */
6313
PJ *proj_create_conversion_hotine_oblique_mercator_variant_b(
6314
    PJ_CONTEXT *ctx, double latitude_projection_centre,
6315
    double longitude_projection_centre, double azimuth_initial_line,
6316
    double angle_from_rectified_to_skrew_grid, double scale,
6317
    double easting_projection_centre, double northing_projection_centre,
6318
    const char *ang_unit_name, double ang_unit_conv_factor,
6319
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
6320
0
    SANITIZE_CTX(ctx);
6321
0
    try {
6322
0
        UnitOfMeasure linearUnit(
6323
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6324
0
        UnitOfMeasure angUnit(
6325
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6326
0
        auto conv = Conversion::createHotineObliqueMercatorVariantB(
6327
0
            PropertyMap(), Angle(latitude_projection_centre, angUnit),
6328
0
            Angle(longitude_projection_centre, angUnit),
6329
0
            Angle(azimuth_initial_line, angUnit),
6330
0
            Angle(angle_from_rectified_to_skrew_grid, angUnit), Scale(scale),
6331
0
            Length(easting_projection_centre, linearUnit),
6332
0
            Length(northing_projection_centre, linearUnit));
6333
0
        return proj_create_conversion(ctx, conv);
6334
0
    } catch (const std::exception &e) {
6335
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6336
0
    }
6337
0
    return nullptr;
6338
0
}
6339
// ---------------------------------------------------------------------------
6340
6341
/** \brief Instantiate a ProjectedCRS with a conversion based on the Hotine
6342
 * Oblique Mercator Two Point Natural Origin projection method.
6343
 *
6344
 * See
6345
 * osgeo::proj::operation::Conversion::createHotineObliqueMercatorTwoPointNaturalOrigin().
6346
 *
6347
 * Linear parameters are expressed in (linear_unit_name,
6348
 * linear_unit_conv_factor).
6349
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6350
 */
6351
PJ *proj_create_conversion_hotine_oblique_mercator_two_point_natural_origin(
6352
    PJ_CONTEXT *ctx, double latitude_projection_centre, double latitude_point1,
6353
    double longitude_point1, double latitude_point2, double longitude_point2,
6354
    double scale, double easting_projection_centre,
6355
    double northing_projection_centre, const char *ang_unit_name,
6356
    double ang_unit_conv_factor, const char *linear_unit_name,
6357
0
    double linear_unit_conv_factor) {
6358
0
    SANITIZE_CTX(ctx);
6359
0
    try {
6360
0
        UnitOfMeasure linearUnit(
6361
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6362
0
        UnitOfMeasure angUnit(
6363
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6364
0
        auto conv =
6365
0
            Conversion::createHotineObliqueMercatorTwoPointNaturalOrigin(
6366
0
                PropertyMap(), Angle(latitude_projection_centre, angUnit),
6367
0
                Angle(latitude_point1, angUnit),
6368
0
                Angle(longitude_point1, angUnit),
6369
0
                Angle(latitude_point2, angUnit),
6370
0
                Angle(longitude_point2, angUnit), Scale(scale),
6371
0
                Length(easting_projection_centre, linearUnit),
6372
0
                Length(northing_projection_centre, linearUnit));
6373
0
        return proj_create_conversion(ctx, conv);
6374
0
    } catch (const std::exception &e) {
6375
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6376
0
    }
6377
0
    return nullptr;
6378
0
}
6379
// ---------------------------------------------------------------------------
6380
6381
/** \brief Instantiate a ProjectedCRS with a conversion based on the Laborde
6382
 * Oblique Mercator projection method.
6383
 *
6384
 * See
6385
 * osgeo::proj::operation::Conversion::createLabordeObliqueMercator().
6386
 *
6387
 * Linear parameters are expressed in (linear_unit_name,
6388
 * linear_unit_conv_factor).
6389
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6390
 */
6391
PJ *proj_create_conversion_laborde_oblique_mercator(
6392
    PJ_CONTEXT *ctx, double latitude_projection_centre,
6393
    double longitude_projection_centre, double azimuth_initial_line,
6394
    double scale, double false_easting, double false_northing,
6395
    const char *ang_unit_name, double ang_unit_conv_factor,
6396
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
6397
0
    SANITIZE_CTX(ctx);
6398
0
    try {
6399
0
        UnitOfMeasure linearUnit(
6400
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6401
0
        UnitOfMeasure angUnit(
6402
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6403
0
        auto conv = Conversion::createLabordeObliqueMercator(
6404
0
            PropertyMap(), Angle(latitude_projection_centre, angUnit),
6405
0
            Angle(longitude_projection_centre, angUnit),
6406
0
            Angle(azimuth_initial_line, angUnit), Scale(scale),
6407
0
            Length(false_easting, linearUnit),
6408
0
            Length(false_northing, linearUnit));
6409
0
        return proj_create_conversion(ctx, conv);
6410
0
    } catch (const std::exception &e) {
6411
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6412
0
    }
6413
0
    return nullptr;
6414
0
}
6415
// ---------------------------------------------------------------------------
6416
6417
/** \brief Instantiate a ProjectedCRS with a conversion based on the
6418
 * International Map of the World Polyconic projection method.
6419
 *
6420
 * See
6421
 * osgeo::proj::operation::Conversion::createInternationalMapWorldPolyconic().
6422
 *
6423
 * Linear parameters are expressed in (linear_unit_name,
6424
 * linear_unit_conv_factor).
6425
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6426
 */
6427
PJ *proj_create_conversion_international_map_world_polyconic(
6428
    PJ_CONTEXT *ctx, double center_long, double latitude_first_parallel,
6429
    double latitude_second_parallel, double false_easting,
6430
    double false_northing, const char *ang_unit_name,
6431
    double ang_unit_conv_factor, const char *linear_unit_name,
6432
0
    double linear_unit_conv_factor) {
6433
0
    SANITIZE_CTX(ctx);
6434
0
    try {
6435
0
        UnitOfMeasure linearUnit(
6436
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6437
0
        UnitOfMeasure angUnit(
6438
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6439
0
        auto conv = Conversion::createInternationalMapWorldPolyconic(
6440
0
            PropertyMap(), Angle(center_long, angUnit),
6441
0
            Angle(latitude_first_parallel, angUnit),
6442
0
            Angle(latitude_second_parallel, angUnit),
6443
0
            Length(false_easting, linearUnit),
6444
0
            Length(false_northing, linearUnit));
6445
0
        return proj_create_conversion(ctx, conv);
6446
0
    } catch (const std::exception &e) {
6447
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6448
0
    }
6449
0
    return nullptr;
6450
0
}
6451
// ---------------------------------------------------------------------------
6452
6453
/** \brief Instantiate a ProjectedCRS with a conversion based on the Krovak
6454
 * (north oriented) projection method.
6455
 *
6456
 * See osgeo::proj::operation::Conversion::createKrovakNorthOriented().
6457
 *
6458
 * Linear parameters are expressed in (linear_unit_name,
6459
 * linear_unit_conv_factor).
6460
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6461
 */
6462
PJ *proj_create_conversion_krovak_north_oriented(
6463
    PJ_CONTEXT *ctx, double latitude_projection_centre,
6464
    double longitude_of_origin, double colatitude_cone_axis,
6465
    double latitude_pseudo_standard_parallel,
6466
    double scale_factor_pseudo_standard_parallel, double false_easting,
6467
    double false_northing, const char *ang_unit_name,
6468
    double ang_unit_conv_factor, const char *linear_unit_name,
6469
0
    double linear_unit_conv_factor) {
6470
0
    SANITIZE_CTX(ctx);
6471
0
    try {
6472
0
        UnitOfMeasure linearUnit(
6473
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6474
0
        UnitOfMeasure angUnit(
6475
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6476
0
        auto conv = Conversion::createKrovakNorthOriented(
6477
0
            PropertyMap(), Angle(latitude_projection_centre, angUnit),
6478
0
            Angle(longitude_of_origin, angUnit),
6479
0
            Angle(colatitude_cone_axis, angUnit),
6480
0
            Angle(latitude_pseudo_standard_parallel, angUnit),
6481
0
            Scale(scale_factor_pseudo_standard_parallel),
6482
0
            Length(false_easting, linearUnit),
6483
0
            Length(false_northing, linearUnit));
6484
0
        return proj_create_conversion(ctx, conv);
6485
0
    } catch (const std::exception &e) {
6486
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6487
0
    }
6488
0
    return nullptr;
6489
0
}
6490
// ---------------------------------------------------------------------------
6491
6492
/** \brief Instantiate a ProjectedCRS with a conversion based on the Krovak
6493
 * projection method.
6494
 *
6495
 * See osgeo::proj::operation::Conversion::createKrovak().
6496
 *
6497
 * Linear parameters are expressed in (linear_unit_name,
6498
 * linear_unit_conv_factor).
6499
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6500
 */
6501
PJ *proj_create_conversion_krovak(
6502
    PJ_CONTEXT *ctx, double latitude_projection_centre,
6503
    double longitude_of_origin, double colatitude_cone_axis,
6504
    double latitude_pseudo_standard_parallel,
6505
    double scale_factor_pseudo_standard_parallel, double false_easting,
6506
    double false_northing, const char *ang_unit_name,
6507
    double ang_unit_conv_factor, const char *linear_unit_name,
6508
0
    double linear_unit_conv_factor) {
6509
0
    SANITIZE_CTX(ctx);
6510
0
    try {
6511
0
        UnitOfMeasure linearUnit(
6512
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6513
0
        UnitOfMeasure angUnit(
6514
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6515
0
        auto conv = Conversion::createKrovak(
6516
0
            PropertyMap(), Angle(latitude_projection_centre, angUnit),
6517
0
            Angle(longitude_of_origin, angUnit),
6518
0
            Angle(colatitude_cone_axis, angUnit),
6519
0
            Angle(latitude_pseudo_standard_parallel, angUnit),
6520
0
            Scale(scale_factor_pseudo_standard_parallel),
6521
0
            Length(false_easting, linearUnit),
6522
0
            Length(false_northing, linearUnit));
6523
0
        return proj_create_conversion(ctx, conv);
6524
0
    } catch (const std::exception &e) {
6525
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6526
0
    }
6527
0
    return nullptr;
6528
0
}
6529
// ---------------------------------------------------------------------------
6530
6531
/** \brief Instantiate a ProjectedCRS with a conversion based on the Lambert
6532
 * Azimuthal Equal Area projection method.
6533
 *
6534
 * See osgeo::proj::operation::Conversion::createLambertAzimuthalEqualArea().
6535
 *
6536
 * Linear parameters are expressed in (linear_unit_name,
6537
 * linear_unit_conv_factor).
6538
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6539
 */
6540
PJ *proj_create_conversion_lambert_azimuthal_equal_area(
6541
    PJ_CONTEXT *ctx, double latitude_nat_origin, double longitude_nat_origin,
6542
    double false_easting, double false_northing, const char *ang_unit_name,
6543
    double ang_unit_conv_factor, const char *linear_unit_name,
6544
0
    double linear_unit_conv_factor) {
6545
0
    SANITIZE_CTX(ctx);
6546
0
    try {
6547
0
        UnitOfMeasure linearUnit(
6548
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6549
0
        UnitOfMeasure angUnit(
6550
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6551
0
        auto conv = Conversion::createLambertAzimuthalEqualArea(
6552
0
            PropertyMap(), Angle(latitude_nat_origin, angUnit),
6553
0
            Angle(longitude_nat_origin, angUnit),
6554
0
            Length(false_easting, linearUnit),
6555
0
            Length(false_northing, linearUnit));
6556
0
        return proj_create_conversion(ctx, conv);
6557
0
    } catch (const std::exception &e) {
6558
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6559
0
    }
6560
0
    return nullptr;
6561
0
}
6562
// ---------------------------------------------------------------------------
6563
6564
/** \brief Instantiate a ProjectedCRS with a conversion based on the Miller
6565
 * Cylindrical projection method.
6566
 *
6567
 * See osgeo::proj::operation::Conversion::createMillerCylindrical().
6568
 *
6569
 * Linear parameters are expressed in (linear_unit_name,
6570
 * linear_unit_conv_factor).
6571
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6572
 */
6573
PJ *proj_create_conversion_miller_cylindrical(
6574
    PJ_CONTEXT *ctx, double center_long, double false_easting,
6575
    double false_northing, const char *ang_unit_name,
6576
    double ang_unit_conv_factor, const char *linear_unit_name,
6577
0
    double linear_unit_conv_factor) {
6578
0
    SANITIZE_CTX(ctx);
6579
0
    try {
6580
0
        UnitOfMeasure linearUnit(
6581
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6582
0
        UnitOfMeasure angUnit(
6583
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6584
0
        auto conv = Conversion::createMillerCylindrical(
6585
0
            PropertyMap(), Angle(center_long, angUnit),
6586
0
            Length(false_easting, linearUnit),
6587
0
            Length(false_northing, linearUnit));
6588
0
        return proj_create_conversion(ctx, conv);
6589
0
    } catch (const std::exception &e) {
6590
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6591
0
    }
6592
0
    return nullptr;
6593
0
}
6594
// ---------------------------------------------------------------------------
6595
6596
/** \brief Instantiate a ProjectedCRS with a conversion based on the Mercator
6597
 * projection method.
6598
 *
6599
 * See osgeo::proj::operation::Conversion::createMercatorVariantA().
6600
 *
6601
 * Linear parameters are expressed in (linear_unit_name,
6602
 * linear_unit_conv_factor).
6603
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6604
 */
6605
PJ *proj_create_conversion_mercator_variant_a(
6606
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
6607
    double false_easting, double false_northing, const char *ang_unit_name,
6608
    double ang_unit_conv_factor, const char *linear_unit_name,
6609
0
    double linear_unit_conv_factor) {
6610
0
    SANITIZE_CTX(ctx);
6611
0
    try {
6612
0
        UnitOfMeasure linearUnit(
6613
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6614
0
        UnitOfMeasure angUnit(
6615
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6616
0
        auto conv = Conversion::createMercatorVariantA(
6617
0
            PropertyMap(), Angle(center_lat, angUnit),
6618
0
            Angle(center_long, angUnit), Scale(scale),
6619
0
            Length(false_easting, linearUnit),
6620
0
            Length(false_northing, linearUnit));
6621
0
        return proj_create_conversion(ctx, conv);
6622
0
    } catch (const std::exception &e) {
6623
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6624
0
    }
6625
0
    return nullptr;
6626
0
}
6627
// ---------------------------------------------------------------------------
6628
6629
/** \brief Instantiate a ProjectedCRS with a conversion based on the Mercator
6630
 * projection method.
6631
 *
6632
 * See osgeo::proj::operation::Conversion::createMercatorVariantB().
6633
 *
6634
 * Linear parameters are expressed in (linear_unit_name,
6635
 * linear_unit_conv_factor).
6636
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6637
 */
6638
PJ *proj_create_conversion_mercator_variant_b(
6639
    PJ_CONTEXT *ctx, double latitude_first_parallel, double center_long,
6640
    double false_easting, double false_northing, const char *ang_unit_name,
6641
    double ang_unit_conv_factor, const char *linear_unit_name,
6642
0
    double linear_unit_conv_factor) {
6643
0
    SANITIZE_CTX(ctx);
6644
0
    try {
6645
0
        UnitOfMeasure linearUnit(
6646
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6647
0
        UnitOfMeasure angUnit(
6648
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6649
0
        auto conv = Conversion::createMercatorVariantB(
6650
0
            PropertyMap(), Angle(latitude_first_parallel, angUnit),
6651
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
6652
0
            Length(false_northing, linearUnit));
6653
0
        return proj_create_conversion(ctx, conv);
6654
0
    } catch (const std::exception &e) {
6655
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6656
0
    }
6657
0
    return nullptr;
6658
0
}
6659
// ---------------------------------------------------------------------------
6660
6661
/** \brief Instantiate a ProjectedCRS with a conversion based on the Popular
6662
 * Visualisation Pseudo Mercator projection method.
6663
 *
6664
 * See
6665
 * osgeo::proj::operation::Conversion::createPopularVisualisationPseudoMercator().
6666
 *
6667
 * Linear parameters are expressed in (linear_unit_name,
6668
 * linear_unit_conv_factor).
6669
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6670
 */
6671
PJ *proj_create_conversion_popular_visualisation_pseudo_mercator(
6672
    PJ_CONTEXT *ctx, double center_lat, double center_long,
6673
    double false_easting, double false_northing, const char *ang_unit_name,
6674
    double ang_unit_conv_factor, const char *linear_unit_name,
6675
0
    double linear_unit_conv_factor) {
6676
0
    SANITIZE_CTX(ctx);
6677
0
    try {
6678
0
        UnitOfMeasure linearUnit(
6679
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6680
0
        UnitOfMeasure angUnit(
6681
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6682
0
        auto conv = Conversion::createPopularVisualisationPseudoMercator(
6683
0
            PropertyMap(), Angle(center_lat, angUnit),
6684
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
6685
0
            Length(false_northing, linearUnit));
6686
0
        return proj_create_conversion(ctx, conv);
6687
0
    } catch (const std::exception &e) {
6688
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6689
0
    }
6690
0
    return nullptr;
6691
0
}
6692
// ---------------------------------------------------------------------------
6693
6694
/** \brief Instantiate a ProjectedCRS with a conversion based on the Mollweide
6695
 * projection method.
6696
 *
6697
 * See osgeo::proj::operation::Conversion::createMollweide().
6698
 *
6699
 * Linear parameters are expressed in (linear_unit_name,
6700
 * linear_unit_conv_factor).
6701
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6702
 */
6703
PJ *proj_create_conversion_mollweide(PJ_CONTEXT *ctx, double center_long,
6704
                                     double false_easting,
6705
                                     double false_northing,
6706
                                     const char *ang_unit_name,
6707
                                     double ang_unit_conv_factor,
6708
                                     const char *linear_unit_name,
6709
0
                                     double linear_unit_conv_factor) {
6710
0
    SANITIZE_CTX(ctx);
6711
0
    try {
6712
0
        UnitOfMeasure linearUnit(
6713
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6714
0
        UnitOfMeasure angUnit(
6715
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6716
0
        auto conv = Conversion::createMollweide(
6717
0
            PropertyMap(), Angle(center_long, angUnit),
6718
0
            Length(false_easting, linearUnit),
6719
0
            Length(false_northing, linearUnit));
6720
0
        return proj_create_conversion(ctx, conv);
6721
0
    } catch (const std::exception &e) {
6722
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6723
0
    }
6724
0
    return nullptr;
6725
0
}
6726
// ---------------------------------------------------------------------------
6727
6728
/** \brief Instantiate a ProjectedCRS with a conversion based on the New Zealand
6729
 * Map Grid projection method.
6730
 *
6731
 * See osgeo::proj::operation::Conversion::createNewZealandMappingGrid().
6732
 *
6733
 * Linear parameters are expressed in (linear_unit_name,
6734
 * linear_unit_conv_factor).
6735
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6736
 */
6737
PJ *proj_create_conversion_new_zealand_mapping_grid(
6738
    PJ_CONTEXT *ctx, double center_lat, double center_long,
6739
    double false_easting, double false_northing, const char *ang_unit_name,
6740
    double ang_unit_conv_factor, const char *linear_unit_name,
6741
0
    double linear_unit_conv_factor) {
6742
0
    SANITIZE_CTX(ctx);
6743
0
    try {
6744
0
        UnitOfMeasure linearUnit(
6745
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6746
0
        UnitOfMeasure angUnit(
6747
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6748
0
        auto conv = Conversion::createNewZealandMappingGrid(
6749
0
            PropertyMap(), Angle(center_lat, angUnit),
6750
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
6751
0
            Length(false_northing, linearUnit));
6752
0
        return proj_create_conversion(ctx, conv);
6753
0
    } catch (const std::exception &e) {
6754
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6755
0
    }
6756
0
    return nullptr;
6757
0
}
6758
// ---------------------------------------------------------------------------
6759
6760
/** \brief Instantiate a ProjectedCRS with a conversion based on the Oblique
6761
 * Stereographic (Alternative) projection method.
6762
 *
6763
 * See osgeo::proj::operation::Conversion::createObliqueStereographic().
6764
 *
6765
 * Linear parameters are expressed in (linear_unit_name,
6766
 * linear_unit_conv_factor).
6767
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6768
 */
6769
PJ *proj_create_conversion_oblique_stereographic(
6770
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
6771
    double false_easting, double false_northing, const char *ang_unit_name,
6772
    double ang_unit_conv_factor, const char *linear_unit_name,
6773
0
    double linear_unit_conv_factor) {
6774
0
    SANITIZE_CTX(ctx);
6775
0
    try {
6776
0
        UnitOfMeasure linearUnit(
6777
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6778
0
        UnitOfMeasure angUnit(
6779
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6780
0
        auto conv = Conversion::createObliqueStereographic(
6781
0
            PropertyMap(), Angle(center_lat, angUnit),
6782
0
            Angle(center_long, angUnit), Scale(scale),
6783
0
            Length(false_easting, linearUnit),
6784
0
            Length(false_northing, linearUnit));
6785
0
        return proj_create_conversion(ctx, conv);
6786
0
    } catch (const std::exception &e) {
6787
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6788
0
    }
6789
0
    return nullptr;
6790
0
}
6791
// ---------------------------------------------------------------------------
6792
6793
/** \brief Instantiate a ProjectedCRS with a conversion based on the
6794
 * Orthographic projection method.
6795
 *
6796
 * See osgeo::proj::operation::Conversion::createOrthographic().
6797
 *
6798
 * Linear parameters are expressed in (linear_unit_name,
6799
 * linear_unit_conv_factor).
6800
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6801
 */
6802
PJ *proj_create_conversion_orthographic(
6803
    PJ_CONTEXT *ctx, double center_lat, double center_long,
6804
    double false_easting, double false_northing, const char *ang_unit_name,
6805
    double ang_unit_conv_factor, const char *linear_unit_name,
6806
0
    double linear_unit_conv_factor) {
6807
0
    SANITIZE_CTX(ctx);
6808
0
    try {
6809
0
        UnitOfMeasure linearUnit(
6810
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6811
0
        UnitOfMeasure angUnit(
6812
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6813
0
        auto conv = Conversion::createOrthographic(
6814
0
            PropertyMap(), Angle(center_lat, angUnit),
6815
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
6816
0
            Length(false_northing, linearUnit));
6817
0
        return proj_create_conversion(ctx, conv);
6818
0
    } catch (const std::exception &e) {
6819
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6820
0
    }
6821
0
    return nullptr;
6822
0
}
6823
// ---------------------------------------------------------------------------
6824
6825
/** \brief Instantiate a ProjectedCRS with a conversion based on the Local
6826
 * Orthographic projection method.
6827
 *
6828
 * See osgeo::proj::operation::Conversion::createLocalOrthographic().
6829
 *
6830
 * Linear parameters are expressed in (linear_unit_name,
6831
 * linear_unit_conv_factor).
6832
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6833
 */
6834
PJ *proj_create_conversion_local_orthographic(
6835
    PJ_CONTEXT *ctx, double center_lat, double center_long, double azimuth,
6836
    double scale, double false_easting, double false_northing,
6837
    const char *ang_unit_name, double ang_unit_conv_factor,
6838
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
6839
0
    SANITIZE_CTX(ctx);
6840
0
    try {
6841
0
        UnitOfMeasure linearUnit(
6842
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6843
0
        UnitOfMeasure angUnit(
6844
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6845
0
        auto conv = Conversion::createLocalOrthographic(
6846
0
            PropertyMap(), Angle(center_lat, angUnit),
6847
0
            Angle(center_long, angUnit), Angle(azimuth, angUnit), Scale(scale),
6848
0
            Length(false_easting, linearUnit),
6849
0
            Length(false_northing, linearUnit));
6850
0
        return proj_create_conversion(ctx, conv);
6851
0
    } catch (const std::exception &e) {
6852
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6853
0
    }
6854
0
    return nullptr;
6855
0
}
6856
// ---------------------------------------------------------------------------
6857
6858
/** \brief Instantiate a ProjectedCRS with a conversion based on the American
6859
 * Polyconic projection method.
6860
 *
6861
 * See osgeo::proj::operation::Conversion::createAmericanPolyconic().
6862
 *
6863
 * Linear parameters are expressed in (linear_unit_name,
6864
 * linear_unit_conv_factor).
6865
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6866
 */
6867
PJ *proj_create_conversion_american_polyconic(
6868
    PJ_CONTEXT *ctx, double center_lat, double center_long,
6869
    double false_easting, double false_northing, const char *ang_unit_name,
6870
    double ang_unit_conv_factor, const char *linear_unit_name,
6871
0
    double linear_unit_conv_factor) {
6872
0
    SANITIZE_CTX(ctx);
6873
0
    try {
6874
0
        UnitOfMeasure linearUnit(
6875
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6876
0
        UnitOfMeasure angUnit(
6877
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6878
0
        auto conv = Conversion::createAmericanPolyconic(
6879
0
            PropertyMap(), Angle(center_lat, angUnit),
6880
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
6881
0
            Length(false_northing, linearUnit));
6882
0
        return proj_create_conversion(ctx, conv);
6883
0
    } catch (const std::exception &e) {
6884
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6885
0
    }
6886
0
    return nullptr;
6887
0
}
6888
// ---------------------------------------------------------------------------
6889
6890
/** \brief Instantiate a ProjectedCRS with a conversion based on the Polar
6891
 * Stereographic (Variant A) projection method.
6892
 *
6893
 * See osgeo::proj::operation::Conversion::createPolarStereographicVariantA().
6894
 *
6895
 * Linear parameters are expressed in (linear_unit_name,
6896
 * linear_unit_conv_factor).
6897
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6898
 */
6899
PJ *proj_create_conversion_polar_stereographic_variant_a(
6900
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
6901
    double false_easting, double false_northing, const char *ang_unit_name,
6902
    double ang_unit_conv_factor, const char *linear_unit_name,
6903
0
    double linear_unit_conv_factor) {
6904
0
    SANITIZE_CTX(ctx);
6905
0
    try {
6906
0
        UnitOfMeasure linearUnit(
6907
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6908
0
        UnitOfMeasure angUnit(
6909
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6910
0
        auto conv = Conversion::createPolarStereographicVariantA(
6911
0
            PropertyMap(), Angle(center_lat, angUnit),
6912
0
            Angle(center_long, angUnit), Scale(scale),
6913
0
            Length(false_easting, linearUnit),
6914
0
            Length(false_northing, linearUnit));
6915
0
        return proj_create_conversion(ctx, conv);
6916
0
    } catch (const std::exception &e) {
6917
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6918
0
    }
6919
0
    return nullptr;
6920
0
}
6921
// ---------------------------------------------------------------------------
6922
6923
/** \brief Instantiate a ProjectedCRS with a conversion based on the Polar
6924
 * Stereographic (Variant B) projection method.
6925
 *
6926
 * See osgeo::proj::operation::Conversion::createPolarStereographicVariantB().
6927
 *
6928
 * Linear parameters are expressed in (linear_unit_name,
6929
 * linear_unit_conv_factor).
6930
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6931
 */
6932
PJ *proj_create_conversion_polar_stereographic_variant_b(
6933
    PJ_CONTEXT *ctx, double latitude_standard_parallel,
6934
    double longitude_of_origin, double false_easting, double false_northing,
6935
    const char *ang_unit_name, double ang_unit_conv_factor,
6936
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
6937
0
    SANITIZE_CTX(ctx);
6938
0
    try {
6939
0
        UnitOfMeasure linearUnit(
6940
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6941
0
        UnitOfMeasure angUnit(
6942
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6943
0
        auto conv = Conversion::createPolarStereographicVariantB(
6944
0
            PropertyMap(), Angle(latitude_standard_parallel, angUnit),
6945
0
            Angle(longitude_of_origin, angUnit),
6946
0
            Length(false_easting, linearUnit),
6947
0
            Length(false_northing, linearUnit));
6948
0
        return proj_create_conversion(ctx, conv);
6949
0
    } catch (const std::exception &e) {
6950
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6951
0
    }
6952
0
    return nullptr;
6953
0
}
6954
// ---------------------------------------------------------------------------
6955
6956
/** \brief Instantiate a ProjectedCRS with a conversion based on the Robinson
6957
 * projection method.
6958
 *
6959
 * See osgeo::proj::operation::Conversion::createRobinson().
6960
 *
6961
 * Linear parameters are expressed in (linear_unit_name,
6962
 * linear_unit_conv_factor).
6963
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6964
 */
6965
PJ *proj_create_conversion_robinson(PJ_CONTEXT *ctx, double center_long,
6966
                                    double false_easting, double false_northing,
6967
                                    const char *ang_unit_name,
6968
                                    double ang_unit_conv_factor,
6969
                                    const char *linear_unit_name,
6970
0
                                    double linear_unit_conv_factor) {
6971
0
    SANITIZE_CTX(ctx);
6972
0
    try {
6973
0
        UnitOfMeasure linearUnit(
6974
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
6975
0
        UnitOfMeasure angUnit(
6976
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
6977
0
        auto conv = Conversion::createRobinson(
6978
0
            PropertyMap(), Angle(center_long, angUnit),
6979
0
            Length(false_easting, linearUnit),
6980
0
            Length(false_northing, linearUnit));
6981
0
        return proj_create_conversion(ctx, conv);
6982
0
    } catch (const std::exception &e) {
6983
0
        proj_log_error(ctx, __FUNCTION__, e.what());
6984
0
    }
6985
0
    return nullptr;
6986
0
}
6987
// ---------------------------------------------------------------------------
6988
6989
/** \brief Instantiate a ProjectedCRS with a conversion based on the Sinusoidal
6990
 * projection method.
6991
 *
6992
 * See osgeo::proj::operation::Conversion::createSinusoidal().
6993
 *
6994
 * Linear parameters are expressed in (linear_unit_name,
6995
 * linear_unit_conv_factor).
6996
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
6997
 */
6998
PJ *proj_create_conversion_sinusoidal(PJ_CONTEXT *ctx, double center_long,
6999
                                      double false_easting,
7000
                                      double false_northing,
7001
                                      const char *ang_unit_name,
7002
                                      double ang_unit_conv_factor,
7003
                                      const char *linear_unit_name,
7004
0
                                      double linear_unit_conv_factor) {
7005
0
    SANITIZE_CTX(ctx);
7006
0
    try {
7007
0
        UnitOfMeasure linearUnit(
7008
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7009
0
        UnitOfMeasure angUnit(
7010
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7011
0
        auto conv = Conversion::createSinusoidal(
7012
0
            PropertyMap(), Angle(center_long, angUnit),
7013
0
            Length(false_easting, linearUnit),
7014
0
            Length(false_northing, linearUnit));
7015
0
        return proj_create_conversion(ctx, conv);
7016
0
    } catch (const std::exception &e) {
7017
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7018
0
    }
7019
0
    return nullptr;
7020
0
}
7021
// ---------------------------------------------------------------------------
7022
7023
/** \brief Instantiate a ProjectedCRS with a conversion based on the
7024
 * Stereographic projection method.
7025
 *
7026
 * See osgeo::proj::operation::Conversion::createStereographic().
7027
 *
7028
 * Linear parameters are expressed in (linear_unit_name,
7029
 * linear_unit_conv_factor).
7030
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7031
 */
7032
PJ *proj_create_conversion_stereographic(
7033
    PJ_CONTEXT *ctx, double center_lat, double center_long, double scale,
7034
    double false_easting, double false_northing, const char *ang_unit_name,
7035
    double ang_unit_conv_factor, const char *linear_unit_name,
7036
0
    double linear_unit_conv_factor) {
7037
0
    SANITIZE_CTX(ctx);
7038
0
    try {
7039
0
        UnitOfMeasure linearUnit(
7040
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7041
0
        UnitOfMeasure angUnit(
7042
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7043
0
        auto conv = Conversion::createStereographic(
7044
0
            PropertyMap(), Angle(center_lat, angUnit),
7045
0
            Angle(center_long, angUnit), Scale(scale),
7046
0
            Length(false_easting, linearUnit),
7047
0
            Length(false_northing, linearUnit));
7048
0
        return proj_create_conversion(ctx, conv);
7049
0
    } catch (const std::exception &e) {
7050
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7051
0
    }
7052
0
    return nullptr;
7053
0
}
7054
// ---------------------------------------------------------------------------
7055
7056
/** \brief Instantiate a ProjectedCRS with a conversion based on the Van der
7057
 * Grinten projection method.
7058
 *
7059
 * See osgeo::proj::operation::Conversion::createVanDerGrinten().
7060
 *
7061
 * Linear parameters are expressed in (linear_unit_name,
7062
 * linear_unit_conv_factor).
7063
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7064
 */
7065
PJ *proj_create_conversion_van_der_grinten(PJ_CONTEXT *ctx, double center_long,
7066
                                           double false_easting,
7067
                                           double false_northing,
7068
                                           const char *ang_unit_name,
7069
                                           double ang_unit_conv_factor,
7070
                                           const char *linear_unit_name,
7071
0
                                           double linear_unit_conv_factor) {
7072
0
    SANITIZE_CTX(ctx);
7073
0
    try {
7074
0
        UnitOfMeasure linearUnit(
7075
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7076
0
        UnitOfMeasure angUnit(
7077
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7078
0
        auto conv = Conversion::createVanDerGrinten(
7079
0
            PropertyMap(), Angle(center_long, angUnit),
7080
0
            Length(false_easting, linearUnit),
7081
0
            Length(false_northing, linearUnit));
7082
0
        return proj_create_conversion(ctx, conv);
7083
0
    } catch (const std::exception &e) {
7084
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7085
0
    }
7086
0
    return nullptr;
7087
0
}
7088
// ---------------------------------------------------------------------------
7089
7090
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner I
7091
 * projection method.
7092
 *
7093
 * See osgeo::proj::operation::Conversion::createWagnerI().
7094
 *
7095
 * Linear parameters are expressed in (linear_unit_name,
7096
 * linear_unit_conv_factor).
7097
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7098
 */
7099
PJ *proj_create_conversion_wagner_i(PJ_CONTEXT *ctx, double center_long,
7100
                                    double false_easting, double false_northing,
7101
                                    const char *ang_unit_name,
7102
                                    double ang_unit_conv_factor,
7103
                                    const char *linear_unit_name,
7104
0
                                    double linear_unit_conv_factor) {
7105
0
    SANITIZE_CTX(ctx);
7106
0
    try {
7107
0
        UnitOfMeasure linearUnit(
7108
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7109
0
        UnitOfMeasure angUnit(
7110
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7111
0
        auto conv = Conversion::createWagnerI(
7112
0
            PropertyMap(), Angle(center_long, angUnit),
7113
0
            Length(false_easting, linearUnit),
7114
0
            Length(false_northing, linearUnit));
7115
0
        return proj_create_conversion(ctx, conv);
7116
0
    } catch (const std::exception &e) {
7117
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7118
0
    }
7119
0
    return nullptr;
7120
0
}
7121
// ---------------------------------------------------------------------------
7122
7123
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner II
7124
 * projection method.
7125
 *
7126
 * See osgeo::proj::operation::Conversion::createWagnerII().
7127
 *
7128
 * Linear parameters are expressed in (linear_unit_name,
7129
 * linear_unit_conv_factor).
7130
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7131
 */
7132
PJ *proj_create_conversion_wagner_ii(PJ_CONTEXT *ctx, double center_long,
7133
                                     double false_easting,
7134
                                     double false_northing,
7135
                                     const char *ang_unit_name,
7136
                                     double ang_unit_conv_factor,
7137
                                     const char *linear_unit_name,
7138
0
                                     double linear_unit_conv_factor) {
7139
0
    SANITIZE_CTX(ctx);
7140
0
    try {
7141
0
        UnitOfMeasure linearUnit(
7142
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7143
0
        UnitOfMeasure angUnit(
7144
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7145
0
        auto conv = Conversion::createWagnerII(
7146
0
            PropertyMap(), Angle(center_long, angUnit),
7147
0
            Length(false_easting, linearUnit),
7148
0
            Length(false_northing, linearUnit));
7149
0
        return proj_create_conversion(ctx, conv);
7150
0
    } catch (const std::exception &e) {
7151
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7152
0
    }
7153
0
    return nullptr;
7154
0
}
7155
// ---------------------------------------------------------------------------
7156
7157
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner III
7158
 * projection method.
7159
 *
7160
 * See osgeo::proj::operation::Conversion::createWagnerIII().
7161
 *
7162
 * Linear parameters are expressed in (linear_unit_name,
7163
 * linear_unit_conv_factor).
7164
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7165
 */
7166
PJ *proj_create_conversion_wagner_iii(
7167
    PJ_CONTEXT *ctx, double latitude_true_scale, double center_long,
7168
    double false_easting, double false_northing, const char *ang_unit_name,
7169
    double ang_unit_conv_factor, const char *linear_unit_name,
7170
0
    double linear_unit_conv_factor) {
7171
0
    SANITIZE_CTX(ctx);
7172
0
    try {
7173
0
        UnitOfMeasure linearUnit(
7174
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7175
0
        UnitOfMeasure angUnit(
7176
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7177
0
        auto conv = Conversion::createWagnerIII(
7178
0
            PropertyMap(), Angle(latitude_true_scale, angUnit),
7179
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
7180
0
            Length(false_northing, linearUnit));
7181
0
        return proj_create_conversion(ctx, conv);
7182
0
    } catch (const std::exception &e) {
7183
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7184
0
    }
7185
0
    return nullptr;
7186
0
}
7187
// ---------------------------------------------------------------------------
7188
7189
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner IV
7190
 * projection method.
7191
 *
7192
 * See osgeo::proj::operation::Conversion::createWagnerIV().
7193
 *
7194
 * Linear parameters are expressed in (linear_unit_name,
7195
 * linear_unit_conv_factor).
7196
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7197
 */
7198
PJ *proj_create_conversion_wagner_iv(PJ_CONTEXT *ctx, double center_long,
7199
                                     double false_easting,
7200
                                     double false_northing,
7201
                                     const char *ang_unit_name,
7202
                                     double ang_unit_conv_factor,
7203
                                     const char *linear_unit_name,
7204
0
                                     double linear_unit_conv_factor) {
7205
0
    SANITIZE_CTX(ctx);
7206
0
    try {
7207
0
        UnitOfMeasure linearUnit(
7208
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7209
0
        UnitOfMeasure angUnit(
7210
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7211
0
        auto conv = Conversion::createWagnerIV(
7212
0
            PropertyMap(), Angle(center_long, angUnit),
7213
0
            Length(false_easting, linearUnit),
7214
0
            Length(false_northing, linearUnit));
7215
0
        return proj_create_conversion(ctx, conv);
7216
0
    } catch (const std::exception &e) {
7217
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7218
0
    }
7219
0
    return nullptr;
7220
0
}
7221
// ---------------------------------------------------------------------------
7222
7223
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner V
7224
 * projection method.
7225
 *
7226
 * See osgeo::proj::operation::Conversion::createWagnerV().
7227
 *
7228
 * Linear parameters are expressed in (linear_unit_name,
7229
 * linear_unit_conv_factor).
7230
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7231
 */
7232
PJ *proj_create_conversion_wagner_v(PJ_CONTEXT *ctx, double center_long,
7233
                                    double false_easting, double false_northing,
7234
                                    const char *ang_unit_name,
7235
                                    double ang_unit_conv_factor,
7236
                                    const char *linear_unit_name,
7237
0
                                    double linear_unit_conv_factor) {
7238
0
    SANITIZE_CTX(ctx);
7239
0
    try {
7240
0
        UnitOfMeasure linearUnit(
7241
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7242
0
        UnitOfMeasure angUnit(
7243
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7244
0
        auto conv = Conversion::createWagnerV(
7245
0
            PropertyMap(), Angle(center_long, angUnit),
7246
0
            Length(false_easting, linearUnit),
7247
0
            Length(false_northing, linearUnit));
7248
0
        return proj_create_conversion(ctx, conv);
7249
0
    } catch (const std::exception &e) {
7250
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7251
0
    }
7252
0
    return nullptr;
7253
0
}
7254
// ---------------------------------------------------------------------------
7255
7256
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner VI
7257
 * projection method.
7258
 *
7259
 * See osgeo::proj::operation::Conversion::createWagnerVI().
7260
 *
7261
 * Linear parameters are expressed in (linear_unit_name,
7262
 * linear_unit_conv_factor).
7263
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7264
 */
7265
PJ *proj_create_conversion_wagner_vi(PJ_CONTEXT *ctx, double center_long,
7266
                                     double false_easting,
7267
                                     double false_northing,
7268
                                     const char *ang_unit_name,
7269
                                     double ang_unit_conv_factor,
7270
                                     const char *linear_unit_name,
7271
0
                                     double linear_unit_conv_factor) {
7272
0
    SANITIZE_CTX(ctx);
7273
0
    try {
7274
0
        UnitOfMeasure linearUnit(
7275
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7276
0
        UnitOfMeasure angUnit(
7277
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7278
0
        auto conv = Conversion::createWagnerVI(
7279
0
            PropertyMap(), Angle(center_long, angUnit),
7280
0
            Length(false_easting, linearUnit),
7281
0
            Length(false_northing, linearUnit));
7282
0
        return proj_create_conversion(ctx, conv);
7283
0
    } catch (const std::exception &e) {
7284
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7285
0
    }
7286
0
    return nullptr;
7287
0
}
7288
// ---------------------------------------------------------------------------
7289
7290
/** \brief Instantiate a ProjectedCRS with a conversion based on the Wagner VII
7291
 * projection method.
7292
 *
7293
 * See osgeo::proj::operation::Conversion::createWagnerVII().
7294
 *
7295
 * Linear parameters are expressed in (linear_unit_name,
7296
 * linear_unit_conv_factor).
7297
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7298
 */
7299
PJ *proj_create_conversion_wagner_vii(PJ_CONTEXT *ctx, double center_long,
7300
                                      double false_easting,
7301
                                      double false_northing,
7302
                                      const char *ang_unit_name,
7303
                                      double ang_unit_conv_factor,
7304
                                      const char *linear_unit_name,
7305
0
                                      double linear_unit_conv_factor) {
7306
0
    SANITIZE_CTX(ctx);
7307
0
    try {
7308
0
        UnitOfMeasure linearUnit(
7309
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7310
0
        UnitOfMeasure angUnit(
7311
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7312
0
        auto conv = Conversion::createWagnerVII(
7313
0
            PropertyMap(), Angle(center_long, angUnit),
7314
0
            Length(false_easting, linearUnit),
7315
0
            Length(false_northing, linearUnit));
7316
0
        return proj_create_conversion(ctx, conv);
7317
0
    } catch (const std::exception &e) {
7318
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7319
0
    }
7320
0
    return nullptr;
7321
0
}
7322
// ---------------------------------------------------------------------------
7323
7324
/** \brief Instantiate a ProjectedCRS with a conversion based on the
7325
 * Quadrilateralized Spherical Cube projection method.
7326
 *
7327
 * See
7328
 * osgeo::proj::operation::Conversion::createQuadrilateralizedSphericalCube().
7329
 *
7330
 * Linear parameters are expressed in (linear_unit_name,
7331
 * linear_unit_conv_factor).
7332
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7333
 */
7334
PJ *proj_create_conversion_quadrilateralized_spherical_cube(
7335
    PJ_CONTEXT *ctx, double center_lat, double center_long,
7336
    double false_easting, double false_northing, const char *ang_unit_name,
7337
    double ang_unit_conv_factor, const char *linear_unit_name,
7338
0
    double linear_unit_conv_factor) {
7339
0
    SANITIZE_CTX(ctx);
7340
0
    try {
7341
0
        UnitOfMeasure linearUnit(
7342
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7343
0
        UnitOfMeasure angUnit(
7344
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7345
0
        auto conv = Conversion::createQuadrilateralizedSphericalCube(
7346
0
            PropertyMap(), Angle(center_lat, angUnit),
7347
0
            Angle(center_long, angUnit), Length(false_easting, linearUnit),
7348
0
            Length(false_northing, linearUnit));
7349
0
        return proj_create_conversion(ctx, conv);
7350
0
    } catch (const std::exception &e) {
7351
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7352
0
    }
7353
0
    return nullptr;
7354
0
}
7355
// ---------------------------------------------------------------------------
7356
7357
/** \brief Instantiate a ProjectedCRS with a conversion based on the Spherical
7358
 * Cross-Track Height projection method.
7359
 *
7360
 * See osgeo::proj::operation::Conversion::createSphericalCrossTrackHeight().
7361
 *
7362
 * Linear parameters are expressed in (linear_unit_name,
7363
 * linear_unit_conv_factor).
7364
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7365
 */
7366
PJ *proj_create_conversion_spherical_cross_track_height(
7367
    PJ_CONTEXT *ctx, double peg_point_lat, double peg_point_long,
7368
    double peg_point_heading, double peg_point_height,
7369
    const char *ang_unit_name, double ang_unit_conv_factor,
7370
0
    const char *linear_unit_name, double linear_unit_conv_factor) {
7371
0
    SANITIZE_CTX(ctx);
7372
0
    try {
7373
0
        UnitOfMeasure linearUnit(
7374
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7375
0
        UnitOfMeasure angUnit(
7376
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7377
0
        auto conv = Conversion::createSphericalCrossTrackHeight(
7378
0
            PropertyMap(), Angle(peg_point_lat, angUnit),
7379
0
            Angle(peg_point_long, angUnit), Angle(peg_point_heading, angUnit),
7380
0
            Length(peg_point_height, linearUnit));
7381
0
        return proj_create_conversion(ctx, conv);
7382
0
    } catch (const std::exception &e) {
7383
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7384
0
    }
7385
0
    return nullptr;
7386
0
}
7387
// ---------------------------------------------------------------------------
7388
7389
/** \brief Instantiate a ProjectedCRS with a conversion based on the Equal Earth
7390
 * projection method.
7391
 *
7392
 * See osgeo::proj::operation::Conversion::createEqualEarth().
7393
 *
7394
 * Linear parameters are expressed in (linear_unit_name,
7395
 * linear_unit_conv_factor).
7396
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7397
 */
7398
PJ *proj_create_conversion_equal_earth(PJ_CONTEXT *ctx, double center_long,
7399
                                       double false_easting,
7400
                                       double false_northing,
7401
                                       const char *ang_unit_name,
7402
                                       double ang_unit_conv_factor,
7403
                                       const char *linear_unit_name,
7404
0
                                       double linear_unit_conv_factor) {
7405
0
    SANITIZE_CTX(ctx);
7406
0
    try {
7407
0
        UnitOfMeasure linearUnit(
7408
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7409
0
        UnitOfMeasure angUnit(
7410
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7411
0
        auto conv = Conversion::createEqualEarth(
7412
0
            PropertyMap(), Angle(center_long, angUnit),
7413
0
            Length(false_easting, linearUnit),
7414
0
            Length(false_northing, linearUnit));
7415
0
        return proj_create_conversion(ctx, conv);
7416
0
    } catch (const std::exception &e) {
7417
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7418
0
    }
7419
0
    return nullptr;
7420
0
}
7421
7422
// ---------------------------------------------------------------------------
7423
7424
/** \brief Instantiate a conversion based on the Vertical Perspective projection
7425
 * method.
7426
 *
7427
 * See osgeo::proj::operation::Conversion::createVerticalPerspective().
7428
 *
7429
 * Linear parameters are expressed in (linear_unit_name,
7430
 * linear_unit_conv_factor).
7431
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7432
 *
7433
 * @since 6.3
7434
 */
7435
PJ *proj_create_conversion_vertical_perspective(
7436
    PJ_CONTEXT *ctx, double topo_origin_lat, double topo_origin_long,
7437
    double topo_origin_height, double view_point_height, double false_easting,
7438
    double false_northing, const char *ang_unit_name,
7439
    double ang_unit_conv_factor, const char *linear_unit_name,
7440
0
    double linear_unit_conv_factor) {
7441
0
    SANITIZE_CTX(ctx);
7442
0
    try {
7443
0
        UnitOfMeasure linearUnit(
7444
0
            createLinearUnit(linear_unit_name, linear_unit_conv_factor));
7445
0
        UnitOfMeasure angUnit(
7446
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7447
0
        auto conv = Conversion::createVerticalPerspective(
7448
0
            PropertyMap(), Angle(topo_origin_lat, angUnit),
7449
0
            Angle(topo_origin_long, angUnit),
7450
0
            Length(topo_origin_height, linearUnit),
7451
0
            Length(view_point_height, linearUnit),
7452
0
            Length(false_easting, linearUnit),
7453
0
            Length(false_northing, linearUnit));
7454
0
        return proj_create_conversion(ctx, conv);
7455
0
    } catch (const std::exception &e) {
7456
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7457
0
    }
7458
0
    return nullptr;
7459
0
}
7460
7461
// ---------------------------------------------------------------------------
7462
7463
/** \brief Instantiate a conversion based on the Pole Rotation method, using the
7464
 * conventions of the GRIB 1 and GRIB 2 data formats.
7465
 *
7466
 * See osgeo::proj::operation::Conversion::createPoleRotationGRIBConvention().
7467
 *
7468
 * Linear parameters are expressed in (linear_unit_name,
7469
 * linear_unit_conv_factor).
7470
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7471
 */
7472
PJ *proj_create_conversion_pole_rotation_grib_convention(
7473
    PJ_CONTEXT *ctx, double south_pole_lat_in_unrotated_crs,
7474
    double south_pole_long_in_unrotated_crs, double axis_rotation,
7475
0
    const char *ang_unit_name, double ang_unit_conv_factor) {
7476
0
    SANITIZE_CTX(ctx);
7477
0
    try {
7478
0
        UnitOfMeasure angUnit(
7479
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7480
0
        auto conv = Conversion::createPoleRotationGRIBConvention(
7481
0
            PropertyMap(), Angle(south_pole_lat_in_unrotated_crs, angUnit),
7482
0
            Angle(south_pole_long_in_unrotated_crs, angUnit),
7483
0
            Angle(axis_rotation, angUnit));
7484
0
        return proj_create_conversion(ctx, conv);
7485
0
    } catch (const std::exception &e) {
7486
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7487
0
    }
7488
0
    return nullptr;
7489
0
}
7490
7491
// ---------------------------------------------------------------------------
7492
7493
/** \brief Instantiate a conversion based on the Pole Rotation method, using
7494
 * the conventions of the netCDF CF convention for the netCDF format.
7495
 *
7496
 * See
7497
 * osgeo::proj::operation::Conversion::createPoleRotationNetCDFCFConvention().
7498
 *
7499
 * Linear parameters are expressed in (linear_unit_name,
7500
 * linear_unit_conv_factor).
7501
 * Angular parameters are expressed in (ang_unit_name, ang_unit_conv_factor).
7502
 */
7503
PJ *proj_create_conversion_pole_rotation_netcdf_cf_convention(
7504
    PJ_CONTEXT *ctx, double grid_north_pole_latitude,
7505
    double grid_north_pole_longitude, double north_pole_grid_longitude,
7506
0
    const char *ang_unit_name, double ang_unit_conv_factor) {
7507
0
    SANITIZE_CTX(ctx);
7508
0
    try {
7509
0
        UnitOfMeasure angUnit(
7510
0
            createAngularUnit(ang_unit_name, ang_unit_conv_factor));
7511
0
        auto conv = Conversion::createPoleRotationNetCDFCFConvention(
7512
0
            PropertyMap(), Angle(grid_north_pole_latitude, angUnit),
7513
0
            Angle(grid_north_pole_longitude, angUnit),
7514
0
            Angle(north_pole_grid_longitude, angUnit));
7515
0
        return proj_create_conversion(ctx, conv);
7516
0
    } catch (const std::exception &e) {
7517
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7518
0
    }
7519
0
    return nullptr;
7520
0
}
7521
7522
/* END: Generated by scripts/create_c_api_projections.py*/
7523
7524
// ---------------------------------------------------------------------------
7525
7526
/** \brief Return whether a coordinate operation can be instantiated as
7527
 * a PROJ pipeline, checking in particular that referenced grids are
7528
 * available.
7529
 *
7530
 * @param ctx PROJ context, or NULL for default context
7531
 * @param coordoperation Object of type CoordinateOperation or derived classes
7532
 * (must not be NULL)
7533
 * @return TRUE or FALSE.
7534
 */
7535
7536
int proj_coordoperation_is_instantiable(PJ_CONTEXT *ctx,
7537
0
                                        const PJ *coordoperation) {
7538
0
    SANITIZE_CTX(ctx);
7539
0
    if (!coordoperation) {
7540
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7541
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7542
0
        return false;
7543
0
    }
7544
0
    auto op = dynamic_cast<const CoordinateOperation *>(
7545
0
        coordoperation->iso_obj.get());
7546
0
    if (!op) {
7547
0
        proj_log_error(ctx, __FUNCTION__,
7548
0
                       "Object is not a CoordinateOperation");
7549
0
        return 0;
7550
0
    }
7551
0
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
7552
0
    try {
7553
0
        auto ret = op->isPROJInstantiable(
7554
0
                       dbContext, proj_context_is_network_enabled(ctx) != FALSE)
7555
0
                       ? 1
7556
0
                       : 0;
7557
0
        return ret;
7558
0
    } catch (const std::exception &) {
7559
0
        return 0;
7560
0
    }
7561
0
}
7562
7563
// ---------------------------------------------------------------------------
7564
7565
/** \brief Return whether a coordinate operation has a "ballpark"
7566
 * transformation,
7567
 * that is a very approximate one, due to lack of more accurate transformations.
7568
 *
7569
 * Typically a null geographic offset between two horizontal datum, or a
7570
 * null vertical offset (or limited to unit changes) between two vertical
7571
 * datum. Errors of several tens to one hundred meters might be expected,
7572
 * compared to more accurate transformations.
7573
 *
7574
 * @param ctx PROJ context, or NULL for default context
7575
 * @param coordoperation Object of type CoordinateOperation or derived classes
7576
 * (must not be NULL)
7577
 * @return TRUE or FALSE.
7578
 */
7579
7580
int proj_coordoperation_has_ballpark_transformation(PJ_CONTEXT *ctx,
7581
0
                                                    const PJ *coordoperation) {
7582
0
    SANITIZE_CTX(ctx);
7583
0
    if (!coordoperation) {
7584
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7585
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7586
0
        return false;
7587
0
    }
7588
0
    auto op = dynamic_cast<const CoordinateOperation *>(
7589
0
        coordoperation->iso_obj.get());
7590
0
    if (!op) {
7591
0
        proj_log_error(ctx, __FUNCTION__,
7592
0
                       "Object is not a CoordinateOperation");
7593
0
        return 0;
7594
0
    }
7595
0
    return op->hasBallparkTransformation();
7596
0
}
7597
7598
// ---------------------------------------------------------------------------
7599
7600
/** \brief Return whether a coordinate operation requires coordinate tuples
7601
 * to have a valid input time for the coordinate transformation to succeed.
7602
 * (this applies for the forward direction)
7603
 *
7604
 * Note: in the case of a time-dependent Helmert transformation, this function
7605
 * will return true, but when executing proj_trans(), execution will still
7606
 * succeed if the time information is missing, due to the transformation central
7607
 * epoch being used as a fallback.
7608
 *
7609
 * @param ctx PROJ context, or NULL for default context
7610
 * @param coordoperation Object of type CoordinateOperation or derived classes
7611
 * (must not be NULL)
7612
 * @return TRUE or FALSE.
7613
 * @since 9.5
7614
 */
7615
7616
int proj_coordoperation_requires_per_coordinate_input_time(
7617
0
    PJ_CONTEXT *ctx, const PJ *coordoperation) {
7618
0
    SANITIZE_CTX(ctx);
7619
0
    if (!coordoperation) {
7620
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7621
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7622
0
        return false;
7623
0
    }
7624
0
    auto op = dynamic_cast<const CoordinateOperation *>(
7625
0
        coordoperation->iso_obj.get());
7626
0
    if (!op) {
7627
0
        proj_log_error(ctx, __FUNCTION__,
7628
0
                       "Object is not a CoordinateOperation");
7629
0
        return false;
7630
0
    }
7631
0
    return op->requiresPerCoordinateInputTime();
7632
0
}
7633
7634
// ---------------------------------------------------------------------------
7635
7636
/** \brief Return the number of parameters of a SingleOperation
7637
 *
7638
 * @param ctx PROJ context, or NULL for default context
7639
 * @param coordoperation Object of type SingleOperation or derived classes
7640
 * (must not be NULL)
7641
 */
7642
7643
int proj_coordoperation_get_param_count(PJ_CONTEXT *ctx,
7644
0
                                        const PJ *coordoperation) {
7645
0
    SANITIZE_CTX(ctx);
7646
0
    if (!coordoperation) {
7647
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7648
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7649
0
        return false;
7650
0
    }
7651
0
    auto op =
7652
0
        dynamic_cast<const SingleOperation *>(coordoperation->iso_obj.get());
7653
0
    if (!op) {
7654
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleOperation");
7655
0
        return 0;
7656
0
    }
7657
0
    return static_cast<int>(op->parameterValues().size());
7658
0
}
7659
7660
// ---------------------------------------------------------------------------
7661
7662
/** \brief Return the index of a parameter of a SingleOperation
7663
 *
7664
 * @param ctx PROJ context, or NULL for default context
7665
 * @param coordoperation Object of type SingleOperation or derived classes
7666
 * (must not be NULL)
7667
 * @param name Parameter name. Must not be NULL
7668
 * @return index (>=0), or -1 in case of error.
7669
 */
7670
7671
int proj_coordoperation_get_param_index(PJ_CONTEXT *ctx,
7672
                                        const PJ *coordoperation,
7673
0
                                        const char *name) {
7674
0
    SANITIZE_CTX(ctx);
7675
0
    if (!coordoperation || !name) {
7676
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7677
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7678
0
        return -1;
7679
0
    }
7680
0
    auto op =
7681
0
        dynamic_cast<const SingleOperation *>(coordoperation->iso_obj.get());
7682
0
    if (!op) {
7683
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleOperation");
7684
0
        return -1;
7685
0
    }
7686
0
    int index = 0;
7687
0
    for (const auto &genParam : op->method()->parameters()) {
7688
0
        if (Identifier::isEquivalentName(genParam->nameStr().c_str(), name)) {
7689
0
            return index;
7690
0
        }
7691
0
        index++;
7692
0
    }
7693
0
    return -1;
7694
0
}
7695
7696
// ---------------------------------------------------------------------------
7697
7698
/** \brief Return a parameter of a SingleOperation
7699
 *
7700
 * @param ctx PROJ context, or NULL for default context
7701
 * @param coordoperation Object of type SingleOperation or derived classes
7702
 * (must not be NULL)
7703
 * @param index Parameter index.
7704
 * @param out_name Pointer to a string value to store the parameter name. or
7705
 * NULL
7706
 * @param out_auth_name Pointer to a string value to store the parameter
7707
 * authority name. or NULL
7708
 * @param out_code Pointer to a string value to store the parameter
7709
 * code. or NULL
7710
 * @param out_value Pointer to a double value to store the parameter
7711
 * value (if numeric). or NULL
7712
 * @param out_value_string Pointer to a string value to store the parameter
7713
 * value (if of type string). or NULL
7714
 * @param out_unit_conv_factor Pointer to a double value to store the parameter
7715
 * unit conversion factor. or NULL
7716
 * @param out_unit_name Pointer to a string value to store the parameter
7717
 * unit name. or NULL
7718
 * @param out_unit_auth_name Pointer to a string value to store the
7719
 * unit authority name. or NULL
7720
 * @param out_unit_code Pointer to a string value to store the
7721
 * unit code. or NULL
7722
 * @param out_unit_category Pointer to a string value to store the parameter
7723
 * name. or
7724
 * NULL. This value might be "unknown", "none", "linear", "linear_per_time",
7725
 * "angular", "angular_per_time", "scale", "scale_per_time", "time",
7726
 * "parametric" or "parametric_per_time"
7727
 * @return TRUE in case of success.
7728
 */
7729
7730
int proj_coordoperation_get_param(
7731
    PJ_CONTEXT *ctx, const PJ *coordoperation, int index, const char **out_name,
7732
    const char **out_auth_name, const char **out_code, double *out_value,
7733
    const char **out_value_string, double *out_unit_conv_factor,
7734
    const char **out_unit_name, const char **out_unit_auth_name,
7735
0
    const char **out_unit_code, const char **out_unit_category) {
7736
0
    SANITIZE_CTX(ctx);
7737
0
    if (!coordoperation) {
7738
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7739
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7740
0
        return false;
7741
0
    }
7742
0
    auto op =
7743
0
        dynamic_cast<const SingleOperation *>(coordoperation->iso_obj.get());
7744
0
    if (!op) {
7745
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleOperation");
7746
0
        return false;
7747
0
    }
7748
0
    const auto &parameters = op->method()->parameters();
7749
0
    const auto &values = op->parameterValues();
7750
0
    if (static_cast<size_t>(index) >= parameters.size() ||
7751
0
        static_cast<size_t>(index) >= values.size()) {
7752
0
        proj_log_error(ctx, __FUNCTION__, "Invalid index");
7753
0
        return false;
7754
0
    }
7755
7756
0
    const auto &param = parameters[index];
7757
0
    const auto &param_ids = param->identifiers();
7758
0
    if (out_name) {
7759
0
        *out_name = param->name()->description()->c_str();
7760
0
    }
7761
0
    if (out_auth_name) {
7762
0
        if (!param_ids.empty()) {
7763
0
            *out_auth_name = param_ids[0]->codeSpace()->c_str();
7764
0
        } else {
7765
0
            *out_auth_name = nullptr;
7766
0
        }
7767
0
    }
7768
0
    if (out_code) {
7769
0
        if (!param_ids.empty()) {
7770
0
            *out_code = param_ids[0]->code().c_str();
7771
0
        } else {
7772
0
            *out_code = nullptr;
7773
0
        }
7774
0
    }
7775
7776
0
    const auto &value = values[index];
7777
0
    ParameterValuePtr paramValue = nullptr;
7778
0
    auto opParamValue =
7779
0
        dynamic_cast<const OperationParameterValue *>(value.get());
7780
0
    if (opParamValue) {
7781
0
        paramValue = opParamValue->parameterValue().as_nullable();
7782
0
    }
7783
0
    if (out_value) {
7784
0
        *out_value = 0;
7785
0
        if (paramValue) {
7786
0
            if (paramValue->type() == ParameterValue::Type::MEASURE) {
7787
0
                *out_value = paramValue->value().value();
7788
0
            }
7789
0
        }
7790
0
    }
7791
0
    if (out_value_string) {
7792
0
        *out_value_string = nullptr;
7793
0
        if (paramValue) {
7794
0
            if (paramValue->type() == ParameterValue::Type::FILENAME) {
7795
0
                *out_value_string = paramValue->valueFile().c_str();
7796
0
            } else if (paramValue->type() == ParameterValue::Type::STRING) {
7797
0
                *out_value_string = paramValue->stringValue().c_str();
7798
0
            }
7799
0
        }
7800
0
    }
7801
0
    if (out_unit_conv_factor) {
7802
0
        *out_unit_conv_factor = 0;
7803
0
    }
7804
0
    if (out_unit_name) {
7805
0
        *out_unit_name = nullptr;
7806
0
    }
7807
0
    if (out_unit_auth_name) {
7808
0
        *out_unit_auth_name = nullptr;
7809
0
    }
7810
0
    if (out_unit_code) {
7811
0
        *out_unit_code = nullptr;
7812
0
    }
7813
0
    if (out_unit_category) {
7814
0
        *out_unit_category = nullptr;
7815
0
    }
7816
0
    if (paramValue) {
7817
0
        if (paramValue->type() == ParameterValue::Type::MEASURE) {
7818
0
            const auto &unit = paramValue->value().unit();
7819
0
            if (out_unit_conv_factor) {
7820
0
                *out_unit_conv_factor = unit.conversionToSI();
7821
0
            }
7822
0
            if (out_unit_name) {
7823
0
                *out_unit_name = unit.name().c_str();
7824
0
            }
7825
0
            if (out_unit_auth_name) {
7826
0
                *out_unit_auth_name = unit.codeSpace().c_str();
7827
0
            }
7828
0
            if (out_unit_code) {
7829
0
                *out_unit_code = unit.code().c_str();
7830
0
            }
7831
0
            if (out_unit_category) {
7832
0
                *out_unit_category =
7833
0
                    get_unit_category(unit.name(), unit.type());
7834
0
            }
7835
0
        }
7836
0
    }
7837
7838
0
    return true;
7839
0
}
7840
7841
// ---------------------------------------------------------------------------
7842
7843
/** \brief Return the parameters of a Helmert transformation as WKT1 TOWGS84
7844
 * values.
7845
 *
7846
 * @param ctx PROJ context, or NULL for default context
7847
 * @param coordoperation Object of type Transformation, that can be represented
7848
 * as a WKT1 TOWGS84 node (must not be NULL)
7849
 * @param out_values Pointer to an array of value_count double values.
7850
 * @param value_count Size of out_values array. The suggested size is 7 to get
7851
 * translation, rotation and scale difference parameters. Rotation and scale
7852
 * difference terms might be zero if the transformation only includes
7853
 * translation
7854
 * parameters. In that case, value_count could be set to 3.
7855
 * @param emit_error_if_incompatible Boolean to indicate if an error must be
7856
 * logged if coordoperation is not compatible with a WKT1 TOWGS84
7857
 * representation.
7858
 * @return TRUE in case of success, or FALSE if coordoperation is not
7859
 * compatible with a WKT1 TOWGS84 representation.
7860
 */
7861
7862
int proj_coordoperation_get_towgs84_values(PJ_CONTEXT *ctx,
7863
                                           const PJ *coordoperation,
7864
                                           double *out_values, int value_count,
7865
0
                                           int emit_error_if_incompatible) {
7866
0
    SANITIZE_CTX(ctx);
7867
0
    if (!coordoperation) {
7868
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7869
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7870
0
        return false;
7871
0
    }
7872
0
    auto transf =
7873
0
        dynamic_cast<const Transformation *>(coordoperation->iso_obj.get());
7874
0
    if (!transf) {
7875
0
        if (emit_error_if_incompatible) {
7876
0
            proj_log_error(ctx, __FUNCTION__, "Object is not a Transformation");
7877
0
        }
7878
0
        return FALSE;
7879
0
    }
7880
7881
0
    const auto values = transf->getTOWGS84Parameters(false);
7882
0
    if (!values.empty()) {
7883
0
        for (int i = 0;
7884
0
             i < value_count && static_cast<size_t>(i) < values.size(); i++) {
7885
0
            out_values[i] = values[i];
7886
0
        }
7887
0
        return TRUE;
7888
0
    } else {
7889
0
        if (emit_error_if_incompatible) {
7890
0
            proj_log_error(ctx, __FUNCTION__,
7891
0
                           "Transformation cannot be formatted as WKT1 TOWGS84 "
7892
0
                           "parameters");
7893
0
        }
7894
0
        return FALSE;
7895
0
    }
7896
0
}
7897
7898
// ---------------------------------------------------------------------------
7899
7900
/** \brief Return the number of grids used by a CoordinateOperation
7901
 *
7902
 * @param ctx PROJ context, or NULL for default context
7903
 * @param coordoperation Object of type CoordinateOperation or derived classes
7904
 * (must not be NULL)
7905
 */
7906
7907
int proj_coordoperation_get_grid_used_count(PJ_CONTEXT *ctx,
7908
2.32k
                                            const PJ *coordoperation) {
7909
2.32k
    SANITIZE_CTX(ctx);
7910
2.32k
    if (!coordoperation) {
7911
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
7912
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
7913
0
        return false;
7914
0
    }
7915
2.32k
    auto co = dynamic_cast<const CoordinateOperation *>(
7916
2.32k
        coordoperation->iso_obj.get());
7917
2.32k
    if (!co) {
7918
0
        proj_log_error(ctx, __FUNCTION__,
7919
0
                       "Object is not a CoordinateOperation");
7920
0
        return 0;
7921
0
    }
7922
2.32k
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
7923
2.32k
    try {
7924
2.32k
        if (!coordoperation->gridsNeededAsked) {
7925
2.32k
            coordoperation->gridsNeededAsked = true;
7926
2.32k
            const auto gridsNeeded = co->gridsNeeded(
7927
2.32k
                dbContext, proj_context_is_network_enabled(ctx) != FALSE);
7928
2.32k
            for (const auto &gridDesc : gridsNeeded) {
7929
31
                coordoperation->gridsNeeded.emplace_back(gridDesc);
7930
31
            }
7931
2.32k
        }
7932
2.32k
        return static_cast<int>(coordoperation->gridsNeeded.size());
7933
2.32k
    } catch (const std::exception &e) {
7934
0
        proj_log_error(ctx, __FUNCTION__, e.what());
7935
0
        return 0;
7936
0
    }
7937
2.32k
}
7938
7939
// ---------------------------------------------------------------------------
7940
7941
/** \brief Return a parameter of a SingleOperation
7942
 *
7943
 * @param ctx PROJ context, or NULL for default context
7944
 * @param coordoperation Object of type SingleOperation or derived classes
7945
 * (must not be NULL)
7946
 * @param index Parameter index.
7947
 * @param out_short_name Pointer to a string value to store the grid short name.
7948
 * or NULL
7949
 * @param out_full_name Pointer to a string value to store the grid full
7950
 * filename. or NULL
7951
 * @param out_package_name Pointer to a string value to store the package name
7952
 * where
7953
 * the grid might be found. or NULL
7954
 * @param out_url Pointer to a string value to store the grid URL or the
7955
 * package URL where the grid might be found. or NULL
7956
 * @param out_direct_download Pointer to a int (boolean) value to store whether
7957
 * *out_url can be downloaded directly. or NULL
7958
 * @param out_open_license Pointer to a int (boolean) value to store whether
7959
 * the grid is released with an open license. or NULL
7960
 * @param out_available Pointer to a int (boolean) value to store whether the
7961
 * grid is available at runtime. or NULL
7962
 * @return TRUE in case of success.
7963
 */
7964
7965
int proj_coordoperation_get_grid_used(
7966
    PJ_CONTEXT *ctx, const PJ *coordoperation, int index,
7967
    const char **out_short_name, const char **out_full_name,
7968
    const char **out_package_name, const char **out_url,
7969
0
    int *out_direct_download, int *out_open_license, int *out_available) {
7970
0
    SANITIZE_CTX(ctx);
7971
0
    const int count =
7972
0
        proj_coordoperation_get_grid_used_count(ctx, coordoperation);
7973
0
    if (index < 0 || index >= count) {
7974
0
        proj_log_error(ctx, __FUNCTION__, "Invalid index");
7975
0
        return false;
7976
0
    }
7977
7978
0
    const auto &gridDesc = coordoperation->gridsNeeded[index];
7979
0
    if (out_short_name) {
7980
0
        *out_short_name = gridDesc.shortName.c_str();
7981
0
    }
7982
7983
0
    if (out_full_name) {
7984
0
        *out_full_name = gridDesc.fullName.c_str();
7985
0
    }
7986
7987
0
    if (out_package_name) {
7988
0
        *out_package_name = gridDesc.packageName.c_str();
7989
0
    }
7990
7991
0
    if (out_url) {
7992
0
        *out_url = gridDesc.url.c_str();
7993
0
    }
7994
7995
0
    if (out_direct_download) {
7996
0
        *out_direct_download = gridDesc.directDownload;
7997
0
    }
7998
7999
0
    if (out_open_license) {
8000
0
        *out_open_license = gridDesc.openLicense;
8001
0
    }
8002
8003
0
    if (out_available) {
8004
0
        *out_available = gridDesc.available;
8005
0
    }
8006
8007
0
    return true;
8008
0
}
8009
8010
// ---------------------------------------------------------------------------
8011
8012
/** \brief Opaque object representing an operation factory context. */
8013
struct PJ_OPERATION_FACTORY_CONTEXT {
8014
    //! @cond Doxygen_Suppress
8015
    CoordinateOperationContextNNPtr operationContext;
8016
8017
    explicit PJ_OPERATION_FACTORY_CONTEXT(
8018
        CoordinateOperationContextNNPtr &&operationContextIn)
8019
7.52k
        : operationContext(std::move(operationContextIn)) {}
8020
8021
    PJ_OPERATION_FACTORY_CONTEXT(const PJ_OPERATION_FACTORY_CONTEXT &) = delete;
8022
    PJ_OPERATION_FACTORY_CONTEXT &
8023
    operator=(const PJ_OPERATION_FACTORY_CONTEXT &) = delete;
8024
    //! @endcond
8025
};
8026
8027
// ---------------------------------------------------------------------------
8028
8029
/** \brief Instantiate a context for building coordinate operations between
8030
 * two CRS.
8031
 *
8032
 * The returned object must be unreferenced with
8033
 * proj_operation_factory_context_destroy() after use.
8034
 *
8035
 * If authority is NULL or the empty string, then coordinate
8036
 * operations from any authority will be searched, with the restrictions set
8037
 * in the authority_to_authority_preference database table.
8038
 * If authority is set to "any", then coordinate
8039
 * operations from any authority will be searched
8040
 * If authority is a non-empty string different of "any",
8041
 * then coordinate operations will be searched only in that authority namespace.
8042
 *
8043
 * @param ctx Context, or NULL for default context.
8044
 * @param authority Name of authority to which to restrict the search of
8045
 *                  candidate operations.
8046
 * @return Object that must be unreferenced with
8047
 * proj_operation_factory_context_destroy(), or NULL in
8048
 * case of error.
8049
 */
8050
PJ_OPERATION_FACTORY_CONTEXT *
8051
7.52k
proj_create_operation_factory_context(PJ_CONTEXT *ctx, const char *authority) {
8052
7.52k
    SANITIZE_CTX(ctx);
8053
7.52k
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
8054
7.52k
    try {
8055
7.52k
        if (dbContext) {
8056
7.52k
            auto factory = CoordinateOperationFactory::create();
8057
7.52k
            auto authFactory = AuthorityFactory::create(
8058
7.52k
                NN_NO_CHECK(dbContext),
8059
7.52k
                std::string(authority ? authority : ""));
8060
7.52k
            auto operationContext =
8061
7.52k
                CoordinateOperationContext::create(authFactory, nullptr, 0.0);
8062
7.52k
            return new PJ_OPERATION_FACTORY_CONTEXT(
8063
7.52k
                std::move(operationContext));
8064
7.52k
        } else {
8065
0
            auto operationContext =
8066
0
                CoordinateOperationContext::create(nullptr, nullptr, 0.0);
8067
0
            return new PJ_OPERATION_FACTORY_CONTEXT(
8068
0
                std::move(operationContext));
8069
0
        }
8070
7.52k
    } catch (const std::exception &e) {
8071
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8072
0
    }
8073
0
    return nullptr;
8074
7.52k
}
8075
8076
// ---------------------------------------------------------------------------
8077
8078
/** \brief Drops a reference on an object.
8079
 *
8080
 * This method should be called one and exactly one for each function
8081
 * returning a PJ_OPERATION_FACTORY_CONTEXT*
8082
 *
8083
 * @param ctx Object, or NULL.
8084
 */
8085
7.52k
void proj_operation_factory_context_destroy(PJ_OPERATION_FACTORY_CONTEXT *ctx) {
8086
7.52k
    delete ctx;
8087
7.52k
}
8088
8089
// ---------------------------------------------------------------------------
8090
8091
/** \brief Set the desired accuracy of the resulting coordinate transformations.
8092
 * @param ctx PROJ context, or NULL for default context
8093
 * @param factory_ctx Operation factory context. must not be NULL
8094
 * @param accuracy Accuracy in meter (or 0 to disable the filter).
8095
 */
8096
void proj_operation_factory_context_set_desired_accuracy(
8097
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8098
0
    double accuracy) {
8099
0
    SANITIZE_CTX(ctx);
8100
0
    if (!factory_ctx) {
8101
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8102
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8103
0
        return;
8104
0
    }
8105
0
    try {
8106
0
        factory_ctx->operationContext->setDesiredAccuracy(accuracy);
8107
0
    } catch (const std::exception &e) {
8108
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8109
0
    }
8110
0
}
8111
8112
// ---------------------------------------------------------------------------
8113
8114
/** \brief Set the desired area of interest for the resulting coordinate
8115
 * transformations.
8116
 *
8117
 * For an area of interest crossing the anti-meridian, west_lon_degree will be
8118
 * greater than east_lon_degree.
8119
 *
8120
 * @param ctx PROJ context, or NULL for default context
8121
 * @param factory_ctx Operation factory context. must not be NULL
8122
 * @param west_lon_degree West longitude (in degrees).
8123
 * @param south_lat_degree South latitude (in degrees).
8124
 * @param east_lon_degree East longitude (in degrees).
8125
 * @param north_lat_degree North latitude (in degrees).
8126
 */
8127
void proj_operation_factory_context_set_area_of_interest(
8128
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8129
    double west_lon_degree, double south_lat_degree, double east_lon_degree,
8130
0
    double north_lat_degree) {
8131
0
    SANITIZE_CTX(ctx);
8132
0
    if (!factory_ctx) {
8133
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8134
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8135
0
        return;
8136
0
    }
8137
0
    try {
8138
0
        factory_ctx->operationContext->setAreaOfInterest(
8139
0
            Extent::createFromBBOX(west_lon_degree, south_lat_degree,
8140
0
                                   east_lon_degree, north_lat_degree));
8141
0
    } catch (const std::exception &e) {
8142
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8143
0
    }
8144
0
}
8145
8146
// ---------------------------------------------------------------------------
8147
8148
/** \brief Set the name of the desired area of interest for the resulting
8149
 * coordinate transformations.
8150
 *
8151
 * @param ctx PROJ context, or NULL for default context
8152
 * @param factory_ctx Operation factory context. must not be NULL
8153
 * @param area_name Area name. Must be known of the database.
8154
 */
8155
void proj_operation_factory_context_set_area_of_interest_name(
8156
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8157
0
    const char *area_name) {
8158
0
    SANITIZE_CTX(ctx);
8159
0
    if (!factory_ctx || !area_name) {
8160
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8161
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8162
0
        return;
8163
0
    }
8164
0
    try {
8165
0
        auto extent = factory_ctx->operationContext->getAreaOfInterest();
8166
0
        if (extent == nullptr) {
8167
0
            auto dbContext = getDBcontext(ctx);
8168
0
            auto factory = AuthorityFactory::create(dbContext, std::string());
8169
0
            auto res = factory->listAreaOfUseFromName(area_name, false);
8170
0
            if (res.size() == 1) {
8171
0
                factory_ctx->operationContext->setAreaOfInterest(
8172
0
                    AuthorityFactory::create(dbContext, res.front().first)
8173
0
                        ->createExtent(res.front().second)
8174
0
                        .as_nullable());
8175
0
            } else {
8176
0
                proj_log_error(ctx, __FUNCTION__, "cannot find area");
8177
0
                return;
8178
0
            }
8179
0
        } else {
8180
0
            factory_ctx->operationContext->setAreaOfInterest(
8181
0
                metadata::Extent::create(util::optional<std::string>(area_name),
8182
0
                                         extent->geographicElements(),
8183
0
                                         extent->verticalElements(),
8184
0
                                         extent->temporalElements())
8185
0
                    .as_nullable());
8186
0
        }
8187
0
    } catch (const std::exception &e) {
8188
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8189
0
    }
8190
0
}
8191
8192
// ---------------------------------------------------------------------------
8193
8194
/** \brief Set how source and target CRS extent should be used
8195
 * when considering if a transformation can be used (only takes effect if
8196
 * no area of interest is explicitly defined).
8197
 *
8198
 * The default is PJ_CRS_EXTENT_SMALLEST.
8199
 *
8200
 * @param ctx PROJ context, or NULL for default context
8201
 * @param factory_ctx Operation factory context. must not be NULL
8202
 * @param use How source and target CRS extent should be used.
8203
 */
8204
void proj_operation_factory_context_set_crs_extent_use(
8205
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8206
0
    PROJ_CRS_EXTENT_USE use) {
8207
0
    SANITIZE_CTX(ctx);
8208
0
    if (!factory_ctx) {
8209
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8210
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8211
0
        return;
8212
0
    }
8213
0
    try {
8214
0
        switch (use) {
8215
0
        case PJ_CRS_EXTENT_NONE:
8216
0
            factory_ctx->operationContext->setSourceAndTargetCRSExtentUse(
8217
0
                CoordinateOperationContext::SourceTargetCRSExtentUse::NONE);
8218
0
            break;
8219
8220
0
        case PJ_CRS_EXTENT_BOTH:
8221
0
            factory_ctx->operationContext->setSourceAndTargetCRSExtentUse(
8222
0
                CoordinateOperationContext::SourceTargetCRSExtentUse::BOTH);
8223
0
            break;
8224
8225
0
        case PJ_CRS_EXTENT_INTERSECTION:
8226
0
            factory_ctx->operationContext->setSourceAndTargetCRSExtentUse(
8227
0
                CoordinateOperationContext::SourceTargetCRSExtentUse::
8228
0
                    INTERSECTION);
8229
0
            break;
8230
8231
0
        case PJ_CRS_EXTENT_SMALLEST:
8232
0
            factory_ctx->operationContext->setSourceAndTargetCRSExtentUse(
8233
0
                CoordinateOperationContext::SourceTargetCRSExtentUse::SMALLEST);
8234
0
            break;
8235
0
        }
8236
0
    } catch (const std::exception &e) {
8237
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8238
0
    }
8239
0
}
8240
8241
// ---------------------------------------------------------------------------
8242
8243
/** \brief Set the spatial criterion to use when comparing the area of
8244
 * validity of coordinate operations with the area of interest / area of
8245
 * validity of
8246
 * source and target CRS.
8247
 *
8248
 * The default is PROJ_SPATIAL_CRITERION_STRICT_CONTAINMENT.
8249
 *
8250
 * @param ctx PROJ context, or NULL for default context
8251
 * @param factory_ctx Operation factory context. must not be NULL
8252
 * @param criterion spatial criterion to use
8253
 */
8254
void proj_operation_factory_context_set_spatial_criterion(
8255
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8256
7.52k
    PROJ_SPATIAL_CRITERION criterion) {
8257
7.52k
    SANITIZE_CTX(ctx);
8258
7.52k
    if (!factory_ctx) {
8259
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8260
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8261
0
        return;
8262
0
    }
8263
7.52k
    try {
8264
7.52k
        switch (criterion) {
8265
0
        case PROJ_SPATIAL_CRITERION_STRICT_CONTAINMENT:
8266
0
            factory_ctx->operationContext->setSpatialCriterion(
8267
0
                CoordinateOperationContext::SpatialCriterion::
8268
0
                    STRICT_CONTAINMENT);
8269
0
            break;
8270
8271
7.52k
        case PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION:
8272
7.52k
            factory_ctx->operationContext->setSpatialCriterion(
8273
7.52k
                CoordinateOperationContext::SpatialCriterion::
8274
7.52k
                    PARTIAL_INTERSECTION);
8275
7.52k
            break;
8276
7.52k
        }
8277
7.52k
    } catch (const std::exception &e) {
8278
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8279
0
    }
8280
7.52k
}
8281
8282
// ---------------------------------------------------------------------------
8283
8284
/** \brief Set how grid availability is used.
8285
 *
8286
 * The default is USE_FOR_SORTING.
8287
 *
8288
 * @param ctx PROJ context, or NULL for default context
8289
 * @param factory_ctx Operation factory context. must not be NULL
8290
 * @param use how grid availability is used.
8291
 */
8292
void proj_operation_factory_context_set_grid_availability_use(
8293
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8294
7.52k
    PROJ_GRID_AVAILABILITY_USE use) {
8295
7.52k
    SANITIZE_CTX(ctx);
8296
7.52k
    if (!factory_ctx) {
8297
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8298
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8299
0
        return;
8300
0
    }
8301
7.52k
    try {
8302
7.52k
        switch (use) {
8303
0
        case PROJ_GRID_AVAILABILITY_USED_FOR_SORTING:
8304
0
            factory_ctx->operationContext->setGridAvailabilityUse(
8305
0
                CoordinateOperationContext::GridAvailabilityUse::
8306
0
                    USE_FOR_SORTING);
8307
0
            break;
8308
8309
7.52k
        case PROJ_GRID_AVAILABILITY_DISCARD_OPERATION_IF_MISSING_GRID:
8310
7.52k
            factory_ctx->operationContext->setGridAvailabilityUse(
8311
7.52k
                CoordinateOperationContext::GridAvailabilityUse::
8312
7.52k
                    DISCARD_OPERATION_IF_MISSING_GRID);
8313
7.52k
            break;
8314
8315
0
        case PROJ_GRID_AVAILABILITY_IGNORED:
8316
0
            factory_ctx->operationContext->setGridAvailabilityUse(
8317
0
                CoordinateOperationContext::GridAvailabilityUse::
8318
0
                    IGNORE_GRID_AVAILABILITY);
8319
0
            break;
8320
8321
0
        case PROJ_GRID_AVAILABILITY_KNOWN_AVAILABLE:
8322
0
            factory_ctx->operationContext->setGridAvailabilityUse(
8323
0
                CoordinateOperationContext::GridAvailabilityUse::
8324
0
                    KNOWN_AVAILABLE);
8325
0
            break;
8326
7.52k
        }
8327
7.52k
    } catch (const std::exception &e) {
8328
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8329
0
    }
8330
7.52k
}
8331
8332
// ---------------------------------------------------------------------------
8333
8334
/** \brief Set whether PROJ alternative grid names should be substituted to
8335
 * the official authority names.
8336
 *
8337
 * The default is true.
8338
 *
8339
 * @param ctx PROJ context, or NULL for default context
8340
 * @param factory_ctx Operation factory context. must not be NULL
8341
 * @param usePROJNames whether PROJ alternative grid names should be used
8342
 */
8343
void proj_operation_factory_context_set_use_proj_alternative_grid_names(
8344
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8345
0
    int usePROJNames) {
8346
0
    SANITIZE_CTX(ctx);
8347
0
    if (!factory_ctx) {
8348
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8349
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8350
0
        return;
8351
0
    }
8352
0
    try {
8353
0
        factory_ctx->operationContext->setUsePROJAlternativeGridNames(
8354
0
            usePROJNames != 0);
8355
0
    } catch (const std::exception &e) {
8356
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8357
0
    }
8358
0
}
8359
8360
// ---------------------------------------------------------------------------
8361
8362
/** \brief Set whether an intermediate pivot CRS can be used for researching
8363
 * coordinate operations between a source and target CRS.
8364
 *
8365
 * Concretely if in the database there is an operation from A to C
8366
 * (or C to A), and another one from C to B (or B to C), but no direct
8367
 * operation between A and B, setting this parameter to true, allow
8368
 * chaining both operations.
8369
 *
8370
 * The current implementation is limited to researching one intermediate
8371
 * step.
8372
 *
8373
 * By default, with the IF_NO_DIRECT_TRANSFORMATION strategy, all potential
8374
 * C candidates will be used if there is no direct transformation.
8375
 *
8376
 * @param ctx PROJ context, or NULL for default context
8377
 * @param factory_ctx Operation factory context. must not be NULL
8378
 * @param use whether and how intermediate CRS may be used.
8379
 */
8380
void proj_operation_factory_context_set_allow_use_intermediate_crs(
8381
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8382
0
    PROJ_INTERMEDIATE_CRS_USE use) {
8383
0
    SANITIZE_CTX(ctx);
8384
0
    if (!factory_ctx) {
8385
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8386
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8387
0
        return;
8388
0
    }
8389
0
    try {
8390
0
        switch (use) {
8391
0
        case PROJ_INTERMEDIATE_CRS_USE_ALWAYS:
8392
0
            factory_ctx->operationContext->setAllowUseIntermediateCRS(
8393
0
                CoordinateOperationContext::IntermediateCRSUse::ALWAYS);
8394
0
            break;
8395
8396
0
        case PROJ_INTERMEDIATE_CRS_USE_IF_NO_DIRECT_TRANSFORMATION:
8397
0
            factory_ctx->operationContext->setAllowUseIntermediateCRS(
8398
0
                CoordinateOperationContext::IntermediateCRSUse::
8399
0
                    IF_NO_DIRECT_TRANSFORMATION);
8400
0
            break;
8401
8402
0
        case PROJ_INTERMEDIATE_CRS_USE_NEVER:
8403
0
            factory_ctx->operationContext->setAllowUseIntermediateCRS(
8404
0
                CoordinateOperationContext::IntermediateCRSUse::NEVER);
8405
0
            break;
8406
0
        }
8407
0
    } catch (const std::exception &e) {
8408
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8409
0
    }
8410
0
}
8411
8412
// ---------------------------------------------------------------------------
8413
8414
/** \brief Restrict the potential pivot CRSs that can be used when trying to
8415
 * build a coordinate operation between two CRS that have no direct operation.
8416
 *
8417
 * @param ctx PROJ context, or NULL for default context
8418
 * @param factory_ctx Operation factory context. must not be NULL
8419
 * @param list_of_auth_name_codes an array of strings NLL terminated,
8420
 * with the format { "auth_name1", "code1", "auth_name2", "code2", ... NULL }
8421
 */
8422
void proj_operation_factory_context_set_allowed_intermediate_crs(
8423
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx,
8424
0
    const char *const *list_of_auth_name_codes) {
8425
0
    SANITIZE_CTX(ctx);
8426
0
    if (!factory_ctx) {
8427
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8428
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8429
0
        return;
8430
0
    }
8431
0
    try {
8432
0
        std::vector<std::pair<std::string, std::string>> pivots;
8433
0
        for (auto iter = list_of_auth_name_codes; iter && iter[0] && iter[1];
8434
0
             iter += 2) {
8435
0
            pivots.emplace_back(std::pair<std::string, std::string>(
8436
0
                std::string(iter[0]), std::string(iter[1])));
8437
0
        }
8438
0
        factory_ctx->operationContext->setIntermediateCRS(pivots);
8439
0
    } catch (const std::exception &e) {
8440
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8441
0
    }
8442
0
}
8443
8444
// ---------------------------------------------------------------------------
8445
8446
/** \brief Set whether transformations that are superseded (but not deprecated)
8447
 * should be discarded.
8448
 *
8449
 * @param ctx PROJ context, or NULL for default context
8450
 * @param factory_ctx Operation factory context. must not be NULL
8451
 * @param discard superseded crs or not
8452
 */
8453
void proj_operation_factory_context_set_discard_superseded(
8454
0
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, int discard) {
8455
0
    SANITIZE_CTX(ctx);
8456
0
    if (!factory_ctx) {
8457
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8458
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8459
0
        return;
8460
0
    }
8461
0
    try {
8462
0
        factory_ctx->operationContext->setDiscardSuperseded(discard != 0);
8463
0
    } catch (const std::exception &e) {
8464
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8465
0
    }
8466
0
}
8467
8468
// ---------------------------------------------------------------------------
8469
8470
/** \brief Set whether ballpark transformations are allowed.
8471
 *
8472
 * @param ctx PROJ context, or NULL for default context
8473
 * @param factory_ctx Operation factory context. must not be NULL
8474
 * @param allow set to TRUE to allow ballpark transformations.
8475
 * @since 7.1
8476
 */
8477
void proj_operation_factory_context_set_allow_ballpark_transformations(
8478
4.97k
    PJ_CONTEXT *ctx, PJ_OPERATION_FACTORY_CONTEXT *factory_ctx, int allow) {
8479
4.97k
    SANITIZE_CTX(ctx);
8480
4.97k
    if (!factory_ctx) {
8481
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8482
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8483
0
        return;
8484
0
    }
8485
4.97k
    try {
8486
4.97k
        factory_ctx->operationContext->setAllowBallparkTransformations(allow !=
8487
4.97k
                                                                       0);
8488
4.97k
    } catch (const std::exception &e) {
8489
0
        proj_log_error(ctx, __FUNCTION__, e.what());
8490
0
    }
8491
4.97k
}
8492
8493
// ---------------------------------------------------------------------------
8494
8495
//! @cond Doxygen_Suppress
8496
/** \brief Opaque object representing a set of operation results. */
8497
struct PJ_OPERATION_LIST : PJ_OBJ_LIST {
8498
8499
    PJ *source_crs;
8500
    PJ *target_crs;
8501
    bool hasPreparedOperation = false;
8502
    std::vector<PJCoordOperation> preparedOperations{};
8503
8504
    explicit PJ_OPERATION_LIST(PJ_CONTEXT *ctx, const PJ *source_crsIn,
8505
                               const PJ *target_crsIn,
8506
                               std::vector<IdentifiedObjectNNPtr> &&objectsIn);
8507
    ~PJ_OPERATION_LIST() override;
8508
8509
    PJ_OPERATION_LIST(const PJ_OPERATION_LIST &) = delete;
8510
    PJ_OPERATION_LIST &operator=(const PJ_OPERATION_LIST &) = delete;
8511
8512
    const std::vector<PJCoordOperation> &getPreparedOperations(PJ_CONTEXT *ctx);
8513
};
8514
8515
// ---------------------------------------------------------------------------
8516
8517
PJ_OPERATION_LIST::PJ_OPERATION_LIST(
8518
    PJ_CONTEXT *ctx, const PJ *source_crsIn, const PJ *target_crsIn,
8519
    std::vector<IdentifiedObjectNNPtr> &&objectsIn)
8520
6.89k
    : PJ_OBJ_LIST(std::move(objectsIn)),
8521
6.89k
      source_crs(proj_clone(ctx, source_crsIn)),
8522
6.89k
      target_crs(proj_clone(ctx, target_crsIn)) {}
8523
8524
// ---------------------------------------------------------------------------
8525
8526
6.89k
PJ_OPERATION_LIST::~PJ_OPERATION_LIST() {
8527
6.89k
    auto tmpCtxt = proj_context_create();
8528
6.89k
    proj_assign_context(source_crs, tmpCtxt);
8529
6.89k
    proj_assign_context(target_crs, tmpCtxt);
8530
6.89k
    proj_destroy(source_crs);
8531
6.89k
    proj_destroy(target_crs);
8532
6.89k
    proj_context_destroy(tmpCtxt);
8533
6.89k
}
8534
8535
// ---------------------------------------------------------------------------
8536
8537
const std::vector<PJCoordOperation> &
8538
0
PJ_OPERATION_LIST::getPreparedOperations(PJ_CONTEXT *ctx) {
8539
0
    if (!hasPreparedOperation) {
8540
0
        hasPreparedOperation = true;
8541
0
        preparedOperations =
8542
0
            pj_create_prepared_operations(ctx, source_crs, target_crs, this);
8543
0
    }
8544
0
    return preparedOperations;
8545
0
}
8546
8547
//! @endcond
8548
8549
// ---------------------------------------------------------------------------
8550
8551
/** \brief Find a list of CoordinateOperation from source_crs to target_crs.
8552
 *
8553
 * The operations are sorted with the most relevant ones first: by
8554
 * descending
8555
 * area (intersection of the transformation area with the area of interest,
8556
 * or intersection of the transformation with the area of use of the CRS),
8557
 * and
8558
 * by increasing accuracy. Operations with unknown accuracy are sorted last,
8559
 * whatever their area.
8560
 *
8561
 * Starting with PROJ 9.1, vertical transformations are only done if both
8562
 * source CRS and target CRS are 3D CRS or Compound CRS with a vertical
8563
 * component. You may need to use proj_crs_promote_to_3D().
8564
 *
8565
 * @param ctx PROJ context, or NULL for default context
8566
 * @param source_crs source CRS or CoordinateMetadata. Must not be NULL.
8567
 * @param target_crs target CRS or CoordinateMetadata. Must not be NULL.
8568
 * @param operationContext Search context. Must not be NULL.
8569
 * @return a result set that must be unreferenced with
8570
 * proj_list_destroy(), or NULL in case of error.
8571
 */
8572
PJ_OBJ_LIST *
8573
proj_create_operations(PJ_CONTEXT *ctx, const PJ *source_crs,
8574
                       const PJ *target_crs,
8575
7.52k
                       const PJ_OPERATION_FACTORY_CONTEXT *operationContext) {
8576
7.52k
    SANITIZE_CTX(ctx);
8577
7.52k
    if (!source_crs || !target_crs || !operationContext) {
8578
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8579
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8580
0
        return nullptr;
8581
0
    }
8582
7.52k
    auto sourceCRS = std::dynamic_pointer_cast<CRS>(source_crs->iso_obj);
8583
7.52k
    CoordinateMetadataPtr sourceCoordinateMetadata;
8584
7.52k
    if (!sourceCRS) {
8585
233
        sourceCoordinateMetadata =
8586
233
            std::dynamic_pointer_cast<CoordinateMetadata>(source_crs->iso_obj);
8587
233
        if (!sourceCoordinateMetadata) {
8588
197
            proj_log_error(ctx, __FUNCTION__,
8589
197
                           "source_crs is not a CRS or a CoordinateMetadata");
8590
197
            return nullptr;
8591
197
        }
8592
36
        if (!sourceCoordinateMetadata->coordinateEpoch().has_value()) {
8593
0
            sourceCRS = sourceCoordinateMetadata->crs().as_nullable();
8594
0
            sourceCoordinateMetadata.reset();
8595
0
        }
8596
36
    }
8597
7.32k
    auto targetCRS = std::dynamic_pointer_cast<CRS>(target_crs->iso_obj);
8598
7.32k
    CoordinateMetadataPtr targetCoordinateMetadata;
8599
7.32k
    if (!targetCRS) {
8600
289
        targetCoordinateMetadata =
8601
289
            std::dynamic_pointer_cast<CoordinateMetadata>(target_crs->iso_obj);
8602
289
        if (!targetCoordinateMetadata) {
8603
278
            proj_log_error(ctx, __FUNCTION__,
8604
278
                           "target_crs is not a CRS or a CoordinateMetadata");
8605
278
            return nullptr;
8606
278
        }
8607
11
        if (!targetCoordinateMetadata->coordinateEpoch().has_value()) {
8608
2
            targetCRS = targetCoordinateMetadata->crs().as_nullable();
8609
2
            targetCoordinateMetadata.reset();
8610
2
        }
8611
11
    }
8612
8613
7.04k
    try {
8614
7.04k
        auto factory = CoordinateOperationFactory::create();
8615
7.04k
        std::vector<IdentifiedObjectNNPtr> objects;
8616
7.04k
        auto ops = sourceCoordinateMetadata != nullptr
8617
7.04k
                       ? (targetCoordinateMetadata != nullptr
8618
34
                              ? factory->createOperations(
8619
1
                                    NN_NO_CHECK(sourceCoordinateMetadata),
8620
1
                                    NN_NO_CHECK(targetCoordinateMetadata),
8621
1
                                    operationContext->operationContext)
8622
34
                              : factory->createOperations(
8623
33
                                    NN_NO_CHECK(sourceCoordinateMetadata),
8624
33
                                    NN_NO_CHECK(targetCRS),
8625
33
                                    operationContext->operationContext))
8626
7.04k
                   : targetCoordinateMetadata != nullptr
8627
7.01k
                       ? factory->createOperations(
8628
8
                             NN_NO_CHECK(sourceCRS),
8629
8
                             NN_NO_CHECK(targetCoordinateMetadata),
8630
8
                             operationContext->operationContext)
8631
7.01k
                       : factory->createOperations(
8632
7.00k
                             NN_NO_CHECK(sourceCRS), NN_NO_CHECK(targetCRS),
8633
7.00k
                             operationContext->operationContext);
8634
27.8k
        for (const auto &op : ops) {
8635
27.8k
            objects.emplace_back(op);
8636
27.8k
        }
8637
7.04k
        return new PJ_OPERATION_LIST(ctx, source_crs, target_crs,
8638
7.04k
                                     std::move(objects));
8639
7.04k
    } catch (const std::exception &e) {
8640
156
        proj_log_error(ctx, __FUNCTION__, e.what());
8641
156
        return nullptr;
8642
156
    }
8643
7.04k
}
8644
8645
// ---------------------------------------------------------------------------
8646
8647
/** Return the index of the operation that would be the most appropriate to
8648
 * transform the specified coordinates.
8649
 *
8650
 * This operation may use resources that are not locally available, depending
8651
 * on the search criteria used by proj_create_operations().
8652
 *
8653
 * This could be done by using proj_create_operations() with a punctual bounding
8654
 * box, but this function is faster when one needs to evaluate on many points
8655
 * with the same (source_crs, target_crs) tuple.
8656
 *
8657
 * @param ctx PROJ context, or NULL for default context
8658
 * @param operations List of operations returned by proj_create_operations()
8659
 * @param direction Direction into which to transform the point.
8660
 * @param coord Coordinate to transform
8661
 * @return the index in operations that would be used to transform coord. Or -1
8662
 * in case of error, or no match.
8663
 *
8664
 * @since 7.1
8665
 */
8666
int proj_get_suggested_operation(PJ_CONTEXT *ctx, PJ_OBJ_LIST *operations,
8667
                                 // cppcheck-suppress passedByValue
8668
0
                                 PJ_DIRECTION direction, PJ_COORD coord) {
8669
0
    SANITIZE_CTX(ctx);
8670
0
    auto opList = dynamic_cast<PJ_OPERATION_LIST *>(operations);
8671
0
    if (opList == nullptr) {
8672
0
        proj_log_error(ctx, __FUNCTION__,
8673
0
                       "operations is not a list of operations");
8674
0
        return -1;
8675
0
    }
8676
8677
    // Special case:
8678
    // proj_create_crs_to_crs_from_pj() always use the unique operation
8679
    // if there's a single one
8680
0
    if (opList->objects.size() == 1) {
8681
0
        return 0;
8682
0
    }
8683
8684
0
    int iExcluded[2] = {-1, -1};
8685
0
    const auto &preparedOps = opList->getPreparedOperations(ctx);
8686
0
    int idx = pj_get_suggested_operation(ctx, preparedOps, iExcluded,
8687
0
                                         /* skipNonInstantiable= */ false,
8688
0
                                         direction, coord);
8689
0
    if (idx >= 0) {
8690
0
        idx = preparedOps[idx].idxInOriginalList;
8691
0
    }
8692
0
    return idx;
8693
0
}
8694
8695
// ---------------------------------------------------------------------------
8696
8697
/** \brief Return the number of objects in the result set
8698
 *
8699
 * @param result Object of type PJ_OBJ_LIST (must not be NULL)
8700
 */
8701
36.9k
int proj_list_get_count(const PJ_OBJ_LIST *result) {
8702
36.9k
    if (!result) {
8703
0
        return 0;
8704
0
    }
8705
36.9k
    return static_cast<int>(result->objects.size());
8706
36.9k
}
8707
8708
// ---------------------------------------------------------------------------
8709
8710
/** \brief Return an object from the result set
8711
 *
8712
 * The returned object must be unreferenced with proj_destroy() after
8713
 * use.
8714
 * It should be used by at most one thread at a time.
8715
 *
8716
 * @param ctx PROJ context, or NULL for default context
8717
 * @param result Object of type PJ_OBJ_LIST (must not be NULL)
8718
 * @param index Index
8719
 * @return a new object that must be unreferenced with proj_destroy(),
8720
 * or nullptr in case of error.
8721
 */
8722
8723
28.8k
PJ *proj_list_get(PJ_CONTEXT *ctx, const PJ_OBJ_LIST *result, int index) {
8724
28.8k
    SANITIZE_CTX(ctx);
8725
28.8k
    if (!result) {
8726
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8727
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8728
0
        return nullptr;
8729
0
    }
8730
28.8k
    if (index < 0 || index >= proj_list_get_count(result)) {
8731
0
        proj_log_error(ctx, __FUNCTION__, "Invalid index");
8732
0
        return nullptr;
8733
0
    }
8734
28.8k
    return pj_obj_create(ctx, result->objects[index]);
8735
28.8k
}
8736
8737
// ---------------------------------------------------------------------------
8738
8739
/** \brief Drops a reference on the result set.
8740
 *
8741
 * This method should be called one and exactly one for each function
8742
 * returning a PJ_OBJ_LIST*
8743
 *
8744
 * @param result Object, or NULL.
8745
 */
8746
6.89k
void proj_list_destroy(PJ_OBJ_LIST *result) { delete result; }
8747
8748
// ---------------------------------------------------------------------------
8749
8750
/** \brief Return the accuracy (in metre) of a coordinate operation.
8751
 *
8752
 * @param ctx PROJ context, or NULL for default context
8753
 * @param coordoperation Coordinate operation. Must not be NULL.
8754
 * @return the accuracy, or a negative value if unknown or in case of error.
8755
 */
8756
double proj_coordoperation_get_accuracy(PJ_CONTEXT *ctx,
8757
13.1k
                                        const PJ *coordoperation) {
8758
13.1k
    SANITIZE_CTX(ctx);
8759
13.1k
    if (!coordoperation) {
8760
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8761
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8762
0
        return -1;
8763
0
    }
8764
13.1k
    auto co = dynamic_cast<const CoordinateOperation *>(
8765
13.1k
        coordoperation->iso_obj.get());
8766
13.1k
    if (!co) {
8767
0
        proj_log_error(ctx, __FUNCTION__,
8768
0
                       "Object is not a CoordinateOperation");
8769
0
        return -1;
8770
0
    }
8771
13.1k
    const auto &accuracies = co->coordinateOperationAccuracies();
8772
13.1k
    if (accuracies.empty()) {
8773
9.56k
        return -1;
8774
9.56k
    }
8775
3.59k
    try {
8776
3.59k
        return c_locale_stod(accuracies[0]->value());
8777
3.59k
    } catch (const std::exception &) {
8778
0
    }
8779
0
    return -1;
8780
3.59k
}
8781
8782
// ---------------------------------------------------------------------------
8783
8784
/** \brief Returns the datum of a SingleCRS.
8785
 *
8786
 * If that function returns NULL, @see proj_crs_get_datum_ensemble() to
8787
 * potentially get a DatumEnsemble instead.
8788
 *
8789
 * The returned object must be unreferenced with proj_destroy() after
8790
 * use.
8791
 * It should be used by at most one thread at a time.
8792
 *
8793
 * @param ctx PROJ context, or NULL for default context
8794
 * @param crs Object of type SingleCRS (must not be NULL)
8795
 * @return Object that must be unreferenced with proj_destroy(), or NULL
8796
 * in case of error (or if there is no datum)
8797
 */
8798
0
PJ *proj_crs_get_datum(PJ_CONTEXT *ctx, const PJ *crs) {
8799
0
    SANITIZE_CTX(ctx);
8800
0
    if (!crs) {
8801
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8802
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8803
0
        return nullptr;
8804
0
    }
8805
0
    auto l_crs = dynamic_cast<const SingleCRS *>(crs->iso_obj.get());
8806
0
    if (!l_crs) {
8807
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleCRS");
8808
0
        return nullptr;
8809
0
    }
8810
0
    const auto &datum = l_crs->datum();
8811
0
    if (!datum) {
8812
0
        return nullptr;
8813
0
    }
8814
0
    return pj_obj_create(ctx, NN_NO_CHECK(datum));
8815
0
}
8816
8817
// ---------------------------------------------------------------------------
8818
8819
/** \brief Returns the datum ensemble of a SingleCRS.
8820
 *
8821
 * If that function returns NULL, @see proj_crs_get_datum() to
8822
 * potentially get a Datum instead.
8823
 *
8824
 * The returned object must be unreferenced with proj_destroy() after
8825
 * use.
8826
 * It should be used by at most one thread at a time.
8827
 *
8828
 * @param ctx PROJ context, or NULL for default context
8829
 * @param crs Object of type SingleCRS (must not be NULL)
8830
 * @return Object that must be unreferenced with proj_destroy(), or NULL
8831
 * in case of error (or if there is no datum ensemble)
8832
 *
8833
 * @since 7.2
8834
 */
8835
0
PJ *proj_crs_get_datum_ensemble(PJ_CONTEXT *ctx, const PJ *crs) {
8836
0
    SANITIZE_CTX(ctx);
8837
0
    if (!crs) {
8838
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8839
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8840
0
        return nullptr;
8841
0
    }
8842
0
    auto l_crs = dynamic_cast<const SingleCRS *>(crs->iso_obj.get());
8843
0
    if (!l_crs) {
8844
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleCRS");
8845
0
        return nullptr;
8846
0
    }
8847
0
    const auto &datumEnsemble = l_crs->datumEnsemble();
8848
0
    if (!datumEnsemble) {
8849
0
        return nullptr;
8850
0
    }
8851
0
    return pj_obj_create(ctx, NN_NO_CHECK(datumEnsemble));
8852
0
}
8853
8854
// ---------------------------------------------------------------------------
8855
8856
/** \brief Returns the number of members of a datum ensemble.
8857
 *
8858
 * @param ctx PROJ context, or NULL for default context
8859
 * @param datum_ensemble Object of type DatumEnsemble (must not be NULL)
8860
 *
8861
 * @since 7.2
8862
 */
8863
int proj_datum_ensemble_get_member_count(PJ_CONTEXT *ctx,
8864
0
                                         const PJ *datum_ensemble) {
8865
0
    SANITIZE_CTX(ctx);
8866
0
    if (!datum_ensemble) {
8867
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8868
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8869
0
        return 0;
8870
0
    }
8871
0
    auto l_datum_ensemble =
8872
0
        dynamic_cast<const DatumEnsemble *>(datum_ensemble->iso_obj.get());
8873
0
    if (!l_datum_ensemble) {
8874
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a DatumEnsemble");
8875
0
        return 0;
8876
0
    }
8877
0
    return static_cast<int>(l_datum_ensemble->datums().size());
8878
0
}
8879
8880
// ---------------------------------------------------------------------------
8881
8882
/** \brief Returns the positional accuracy of the datum ensemble.
8883
 *
8884
 * @param ctx PROJ context, or NULL for default context
8885
 * @param datum_ensemble Object of type DatumEnsemble (must not be NULL)
8886
 * @return the accuracy, or -1 in case of error.
8887
 *
8888
 * @since 7.2
8889
 */
8890
double proj_datum_ensemble_get_accuracy(PJ_CONTEXT *ctx,
8891
0
                                        const PJ *datum_ensemble) {
8892
0
    SANITIZE_CTX(ctx);
8893
0
    if (!datum_ensemble) {
8894
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8895
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8896
0
        return -1;
8897
0
    }
8898
0
    auto l_datum_ensemble =
8899
0
        dynamic_cast<const DatumEnsemble *>(datum_ensemble->iso_obj.get());
8900
0
    if (!l_datum_ensemble) {
8901
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a DatumEnsemble");
8902
0
        return -1;
8903
0
    }
8904
0
    const auto &accuracy = l_datum_ensemble->positionalAccuracy();
8905
0
    try {
8906
0
        return c_locale_stod(accuracy->value());
8907
0
    } catch (const std::exception &) {
8908
0
    }
8909
0
    return -1;
8910
0
}
8911
8912
// ---------------------------------------------------------------------------
8913
8914
/** \brief Returns a member from a datum ensemble.
8915
 *
8916
 * The returned object must be unreferenced with proj_destroy() after
8917
 * use.
8918
 * It should be used by at most one thread at a time.
8919
 *
8920
 * @param ctx PROJ context, or NULL for default context
8921
 * @param datum_ensemble Object of type DatumEnsemble (must not be NULL)
8922
 * @param member_index Index of the datum member to extract (between 0 and
8923
 * proj_datum_ensemble_get_member_count()-1)
8924
 * @return Object that must be unreferenced with proj_destroy(), or NULL
8925
 * in case of error (or if there is no datum ensemble)
8926
 *
8927
 * @since 7.2
8928
 */
8929
PJ *proj_datum_ensemble_get_member(PJ_CONTEXT *ctx, const PJ *datum_ensemble,
8930
0
                                   int member_index) {
8931
0
    SANITIZE_CTX(ctx);
8932
0
    if (!datum_ensemble) {
8933
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8934
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8935
0
        return nullptr;
8936
0
    }
8937
0
    auto l_datum_ensemble =
8938
0
        dynamic_cast<const DatumEnsemble *>(datum_ensemble->iso_obj.get());
8939
0
    if (!l_datum_ensemble) {
8940
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a DatumEnsemble");
8941
0
        return nullptr;
8942
0
    }
8943
0
    if (member_index < 0 ||
8944
0
        member_index >= static_cast<int>(l_datum_ensemble->datums().size())) {
8945
0
        proj_log_error(ctx, __FUNCTION__, "Invalid member_index");
8946
0
        return nullptr;
8947
0
    }
8948
0
    return pj_obj_create(ctx, l_datum_ensemble->datums()[member_index]);
8949
0
}
8950
8951
// ---------------------------------------------------------------------------
8952
8953
/** \brief Returns a datum for a SingleCRS.
8954
 *
8955
 * If the SingleCRS has a datum, then this datum is returned.
8956
 * Otherwise, the SingleCRS has a datum ensemble, and this datum ensemble is
8957
 * returned as a regular datum instead of a datum ensemble.
8958
 *
8959
 * The returned object must be unreferenced with proj_destroy() after
8960
 * use.
8961
 * It should be used by at most one thread at a time.
8962
 *
8963
 * @param ctx PROJ context, or NULL for default context
8964
 * @param crs Object of type SingleCRS (must not be NULL)
8965
 * @return Object that must be unreferenced with proj_destroy(), or NULL
8966
 * in case of error (or if there is no datum)
8967
 *
8968
 * @since 7.2
8969
 */
8970
2.54k
PJ *proj_crs_get_datum_forced(PJ_CONTEXT *ctx, const PJ *crs) {
8971
2.54k
    SANITIZE_CTX(ctx);
8972
2.54k
    if (!crs) {
8973
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
8974
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
8975
0
        return nullptr;
8976
0
    }
8977
2.54k
    auto l_crs = dynamic_cast<const SingleCRS *>(crs->iso_obj.get());
8978
2.54k
    if (!l_crs) {
8979
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleCRS");
8980
0
        return nullptr;
8981
0
    }
8982
2.54k
    const auto &datum = l_crs->datum();
8983
2.54k
    if (datum) {
8984
2.53k
        return pj_obj_create(ctx, NN_NO_CHECK(datum));
8985
2.53k
    }
8986
6
    const auto &datumEnsemble = l_crs->datumEnsemble();
8987
6
    assert(datumEnsemble);
8988
6
    auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
8989
6
    try {
8990
6
        return pj_obj_create(ctx, datumEnsemble->asDatum(dbContext));
8991
6
    } catch (const std::exception &e) {
8992
0
        proj_log_debug(ctx, __FUNCTION__, e.what());
8993
0
        return nullptr;
8994
0
    }
8995
6
}
8996
8997
// ---------------------------------------------------------------------------
8998
8999
/** \brief Returns the frame reference epoch of a dynamic geodetic or vertical
9000
 * reference frame.
9001
 *
9002
 * @param ctx PROJ context, or NULL for default context
9003
 * @param datum Object of type DynamicGeodeticReferenceFrame or
9004
 * DynamicVerticalReferenceFrame (must not be NULL)
9005
 * @return the frame reference epoch as decimal year, or -1 in case of error.
9006
 *
9007
 * @since 7.2
9008
 */
9009
double proj_dynamic_datum_get_frame_reference_epoch(PJ_CONTEXT *ctx,
9010
0
                                                    const PJ *datum) {
9011
0
    SANITIZE_CTX(ctx);
9012
0
    if (!datum) {
9013
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9014
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9015
0
        return -1;
9016
0
    }
9017
0
    auto dgrf = dynamic_cast<const DynamicGeodeticReferenceFrame *>(
9018
0
        datum->iso_obj.get());
9019
0
    auto dvrf = dynamic_cast<const DynamicVerticalReferenceFrame *>(
9020
0
        datum->iso_obj.get());
9021
0
    if (!dgrf && !dvrf) {
9022
0
        proj_log_error(ctx, __FUNCTION__,
9023
0
                       "Object is not a "
9024
0
                       "DynamicGeodeticReferenceFrame or "
9025
0
                       "DynamicVerticalReferenceFrame");
9026
0
        return -1;
9027
0
    }
9028
0
    const auto &frameReferenceEpoch =
9029
0
        dgrf ? dgrf->frameReferenceEpoch() : dvrf->frameReferenceEpoch();
9030
0
    return frameReferenceEpoch.value();
9031
0
}
9032
9033
// ---------------------------------------------------------------------------
9034
9035
/** \brief Returns the coordinate system of a SingleCRS.
9036
 *
9037
 * The returned object must be unreferenced with proj_destroy() after
9038
 * use.
9039
 * It should be used by at most one thread at a time.
9040
 *
9041
 * @param ctx PROJ context, or NULL for default context
9042
 * @param crs Object of type SingleCRS (must not be NULL)
9043
 * @return Object that must be unreferenced with proj_destroy(), or NULL
9044
 * in case of error.
9045
 */
9046
6.58k
PJ *proj_crs_get_coordinate_system(PJ_CONTEXT *ctx, const PJ *crs) {
9047
6.58k
    SANITIZE_CTX(ctx);
9048
6.58k
    if (!crs) {
9049
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9050
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9051
0
        return nullptr;
9052
0
    }
9053
6.58k
    auto l_crs = dynamic_cast<const SingleCRS *>(crs->iso_obj.get());
9054
6.58k
    if (!l_crs) {
9055
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a SingleCRS");
9056
0
        return nullptr;
9057
0
    }
9058
6.58k
    return pj_obj_create(ctx, l_crs->coordinateSystem());
9059
6.58k
}
9060
9061
// ---------------------------------------------------------------------------
9062
9063
/** \brief Returns the type of the coordinate system.
9064
 *
9065
 * @param ctx PROJ context, or NULL for default context
9066
 * @param cs Object of type CoordinateSystem (must not be NULL)
9067
 * @return type, or PJ_CS_TYPE_UNKNOWN in case of error.
9068
 */
9069
0
PJ_COORDINATE_SYSTEM_TYPE proj_cs_get_type(PJ_CONTEXT *ctx, const PJ *cs) {
9070
0
    SANITIZE_CTX(ctx);
9071
0
    if (!cs) {
9072
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9073
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9074
0
        return PJ_CS_TYPE_UNKNOWN;
9075
0
    }
9076
0
    auto l_cs = dynamic_cast<const CoordinateSystem *>(cs->iso_obj.get());
9077
0
    if (!l_cs) {
9078
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateSystem");
9079
0
        return PJ_CS_TYPE_UNKNOWN;
9080
0
    }
9081
0
    if (dynamic_cast<const CartesianCS *>(l_cs)) {
9082
0
        return PJ_CS_TYPE_CARTESIAN;
9083
0
    }
9084
0
    if (dynamic_cast<const EllipsoidalCS *>(l_cs)) {
9085
0
        return PJ_CS_TYPE_ELLIPSOIDAL;
9086
0
    }
9087
0
    if (dynamic_cast<const VerticalCS *>(l_cs)) {
9088
0
        return PJ_CS_TYPE_VERTICAL;
9089
0
    }
9090
0
    if (dynamic_cast<const SphericalCS *>(l_cs)) {
9091
0
        return PJ_CS_TYPE_SPHERICAL;
9092
0
    }
9093
0
    if (dynamic_cast<const OrdinalCS *>(l_cs)) {
9094
0
        return PJ_CS_TYPE_ORDINAL;
9095
0
    }
9096
0
    if (dynamic_cast<const ParametricCS *>(l_cs)) {
9097
0
        return PJ_CS_TYPE_PARAMETRIC;
9098
0
    }
9099
0
    if (dynamic_cast<const DateTimeTemporalCS *>(l_cs)) {
9100
0
        return PJ_CS_TYPE_DATETIMETEMPORAL;
9101
0
    }
9102
0
    if (dynamic_cast<const TemporalCountCS *>(l_cs)) {
9103
0
        return PJ_CS_TYPE_TEMPORALCOUNT;
9104
0
    }
9105
0
    if (dynamic_cast<const TemporalMeasureCS *>(l_cs)) {
9106
0
        return PJ_CS_TYPE_TEMPORALMEASURE;
9107
0
    }
9108
0
    return PJ_CS_TYPE_UNKNOWN;
9109
0
}
9110
9111
// ---------------------------------------------------------------------------
9112
9113
/** \brief Returns the number of axis of the coordinate system.
9114
 *
9115
 * @param ctx PROJ context, or NULL for default context
9116
 * @param cs Object of type CoordinateSystem (must not be NULL)
9117
 * @return number of axis, or -1 in case of error.
9118
 */
9119
0
int proj_cs_get_axis_count(PJ_CONTEXT *ctx, const PJ *cs) {
9120
0
    SANITIZE_CTX(ctx);
9121
0
    if (!cs) {
9122
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9123
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9124
0
        return -1;
9125
0
    }
9126
0
    auto l_cs = dynamic_cast<const CoordinateSystem *>(cs->iso_obj.get());
9127
0
    if (!l_cs) {
9128
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateSystem");
9129
0
        return -1;
9130
0
    }
9131
0
    return static_cast<int>(l_cs->axisList().size());
9132
0
}
9133
9134
// ---------------------------------------------------------------------------
9135
9136
/** \brief Returns information on an axis
9137
 *
9138
 * @param ctx PROJ context, or NULL for default context
9139
 * @param cs Object of type CoordinateSystem (must not be NULL)
9140
 * @param index Index of the coordinate system (between 0 and
9141
 * proj_cs_get_axis_count() - 1)
9142
 * @param out_name Pointer to a string value to store the axis name. or NULL
9143
 * @param out_abbrev Pointer to a string value to store the axis abbreviation.
9144
 * or NULL
9145
 * @param out_direction Pointer to a string value to store the axis direction.
9146
 * or NULL
9147
 * @param out_unit_conv_factor Pointer to a double value to store the axis
9148
 * unit conversion factor. or NULL
9149
 * @param out_unit_name Pointer to a string value to store the axis
9150
 * unit name. or NULL
9151
 * @param out_unit_auth_name Pointer to a string value to store the axis
9152
 * unit authority name. or NULL
9153
 * @param out_unit_code Pointer to a string value to store the axis
9154
 * unit code. or NULL
9155
 * @return TRUE in case of success
9156
 */
9157
int proj_cs_get_axis_info(PJ_CONTEXT *ctx, const PJ *cs, int index,
9158
                          const char **out_name, const char **out_abbrev,
9159
                          const char **out_direction,
9160
                          double *out_unit_conv_factor,
9161
                          const char **out_unit_name,
9162
                          const char **out_unit_auth_name,
9163
9.20k
                          const char **out_unit_code) {
9164
9.20k
    SANITIZE_CTX(ctx);
9165
9.20k
    if (!cs) {
9166
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9167
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9168
0
        return false;
9169
0
    }
9170
9.20k
    auto l_cs = dynamic_cast<const CoordinateSystem *>(cs->iso_obj.get());
9171
9.20k
    if (!l_cs) {
9172
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateSystem");
9173
0
        return false;
9174
0
    }
9175
9.20k
    const auto &axisList = l_cs->axisList();
9176
9.20k
    if (index < 0 || static_cast<size_t>(index) >= axisList.size()) {
9177
0
        proj_log_error(ctx, __FUNCTION__, "Invalid index");
9178
0
        return false;
9179
0
    }
9180
9.20k
    const auto &axis = axisList[index];
9181
9.20k
    if (out_name) {
9182
0
        *out_name = axis->nameStr().c_str();
9183
0
    }
9184
9.20k
    if (out_abbrev) {
9185
0
        *out_abbrev = axis->abbreviation().c_str();
9186
0
    }
9187
9.20k
    if (out_direction) {
9188
9.20k
        *out_direction = axis->direction().toString().c_str();
9189
9.20k
    }
9190
9.20k
    if (out_unit_conv_factor) {
9191
9.20k
        *out_unit_conv_factor = axis->unit().conversionToSI();
9192
9.20k
    }
9193
9.20k
    if (out_unit_name) {
9194
0
        *out_unit_name = axis->unit().name().c_str();
9195
0
    }
9196
9.20k
    if (out_unit_auth_name) {
9197
0
        *out_unit_auth_name = axis->unit().codeSpace().c_str();
9198
0
    }
9199
9.20k
    if (out_unit_code) {
9200
0
        *out_unit_code = axis->unit().code().c_str();
9201
0
    }
9202
9.20k
    return true;
9203
9.20k
}
9204
9205
// ---------------------------------------------------------------------------
9206
9207
/** \brief Returns a PJ* object whose axis order is the one expected for
9208
 * visualization purposes.
9209
 *
9210
 * The input object must be either:
9211
 * <ul>
9212
 * <li>a coordinate operation, that has been created with
9213
 *     proj_create_crs_to_crs(). If the axis order of its source or target CRS
9214
 *     is northing,easting, then an axis swap operation will be inserted.</li>
9215
 * <li>or a CRS. The axis order of geographic CRS will be longitude, latitude
9216
 *     [,height], and the one of projected CRS, or of engineering CRS with a
9217
 *     Cartesian coordinate system, will be easting, northing [, height]</li>
9218
 * </ul>
9219
 *
9220
 * @param ctx PROJ context, or NULL for default context
9221
 * @param obj Object of type CRS, or CoordinateOperation created with
9222
 * proj_create_crs_to_crs() (must not be NULL)
9223
 * @return a new PJ* object to free with proj_destroy() in case of success, or
9224
 * nullptr in case of error
9225
 */
9226
0
PJ *proj_normalize_for_visualization(PJ_CONTEXT *ctx, const PJ *obj) {
9227
9228
0
    SANITIZE_CTX(ctx);
9229
0
    if (!obj->alternativeCoordinateOperations.empty()) {
9230
0
        try {
9231
0
            auto pjNew = std::unique_ptr<PJ>(pj_new());
9232
0
            if (!pjNew)
9233
0
                return nullptr;
9234
0
            pjNew->ctx = ctx;
9235
0
            pjNew->descr = "Set of coordinate operations";
9236
0
            pjNew->left = obj->left;
9237
0
            pjNew->right = obj->right;
9238
0
            pjNew->copyStateFrom(*obj);
9239
9240
0
            for (const auto &alt : obj->alternativeCoordinateOperations) {
9241
0
                auto co = dynamic_cast<const CoordinateOperation *>(
9242
0
                    alt.pj->iso_obj.get());
9243
0
                if (co) {
9244
0
                    double minxSrc = alt.minxSrc;
9245
0
                    double minySrc = alt.minySrc;
9246
0
                    double maxxSrc = alt.maxxSrc;
9247
0
                    double maxySrc = alt.maxySrc;
9248
0
                    double minxDst = alt.minxDst;
9249
0
                    double minyDst = alt.minyDst;
9250
0
                    double maxxDst = alt.maxxDst;
9251
0
                    double maxyDst = alt.maxyDst;
9252
9253
0
                    auto l_sourceCRS = co->sourceCRS();
9254
0
                    auto l_targetCRS = co->targetCRS();
9255
0
                    if (l_sourceCRS && l_targetCRS) {
9256
0
                        const bool swapSource =
9257
0
                            l_sourceCRS
9258
0
                                ->mustAxisOrderBeSwitchedForVisualization();
9259
0
                        if (swapSource) {
9260
0
                            std::swap(minxSrc, minySrc);
9261
0
                            std::swap(maxxSrc, maxySrc);
9262
0
                        }
9263
0
                        const bool swapTarget =
9264
0
                            l_targetCRS
9265
0
                                ->mustAxisOrderBeSwitchedForVisualization();
9266
0
                        if (swapTarget) {
9267
0
                            std::swap(minxDst, minyDst);
9268
0
                            std::swap(maxxDst, maxyDst);
9269
0
                        }
9270
0
                    }
9271
0
                    ctx->forceOver = alt.pj->over != 0;
9272
0
                    auto pjNormalized =
9273
0
                        pj_obj_create(ctx, co->normalizeForVisualization());
9274
0
                    ctx->forceOver = false;
9275
9276
0
                    pjNormalized->copyStateFrom(*(alt.pj));
9277
9278
0
                    pjNew->alternativeCoordinateOperations.emplace_back(
9279
0
                        alt.idxInOriginalList, minxSrc, minySrc, maxxSrc,
9280
0
                        maxySrc, minxDst, minyDst, maxxDst, maxyDst,
9281
0
                        pjNormalized, co->nameStr(), alt.accuracy,
9282
0
                        alt.pseudoArea, alt.areaName.c_str(),
9283
0
                        alt.pjSrcGeocentricToLonLat,
9284
0
                        alt.pjDstGeocentricToLonLat);
9285
0
                }
9286
0
            }
9287
0
            return pjNew.release();
9288
0
        } catch (const std::exception &e) {
9289
0
            ctx->forceOver = false;
9290
0
            proj_log_debug(ctx, __FUNCTION__, e.what());
9291
0
            return nullptr;
9292
0
        }
9293
0
    }
9294
9295
0
    auto crs = dynamic_cast<const CRS *>(obj->iso_obj.get());
9296
0
    if (crs) {
9297
0
        try {
9298
0
            return pj_obj_create(ctx, crs->normalizeForVisualization());
9299
0
        } catch (const std::exception &e) {
9300
0
            proj_log_debug(ctx, __FUNCTION__, e.what());
9301
0
            return nullptr;
9302
0
        }
9303
0
    }
9304
9305
0
    auto co = dynamic_cast<const CoordinateOperation *>(obj->iso_obj.get());
9306
0
    if (!co) {
9307
0
        proj_log_error(ctx, __FUNCTION__,
9308
0
                       "Object is not a CoordinateOperation "
9309
0
                       "created with "
9310
0
                       "proj_create_crs_to_crs");
9311
0
        return nullptr;
9312
0
    }
9313
0
    try {
9314
0
        ctx->forceOver = obj->over != 0;
9315
0
        auto pjNormalized = pj_obj_create(ctx, co->normalizeForVisualization());
9316
0
        pjNormalized->over = obj->over;
9317
0
        ctx->forceOver = false;
9318
0
        return pjNormalized;
9319
0
    } catch (const std::exception &e) {
9320
0
        ctx->forceOver = false;
9321
0
        proj_log_debug(ctx, __FUNCTION__, e.what());
9322
0
        return nullptr;
9323
0
    }
9324
0
}
9325
9326
// ---------------------------------------------------------------------------
9327
9328
/** \brief Returns a PJ* coordinate operation object which represents the
9329
 * inverse operation of the specified coordinate operation.
9330
 *
9331
 * @param ctx PROJ context, or NULL for default context
9332
 * @param obj Object of type CoordinateOperation (must not be NULL)
9333
 * @return a new PJ* object to free with proj_destroy() in case of success, or
9334
 * nullptr in case of error
9335
 * @since 6.3
9336
 */
9337
0
PJ *proj_coordoperation_create_inverse(PJ_CONTEXT *ctx, const PJ *obj) {
9338
9339
0
    SANITIZE_CTX(ctx);
9340
0
    if (!obj) {
9341
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9342
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9343
0
        return nullptr;
9344
0
    }
9345
0
    auto co = dynamic_cast<const CoordinateOperation *>(obj->iso_obj.get());
9346
0
    if (!co) {
9347
0
        proj_log_error(ctx, __FUNCTION__,
9348
0
                       "Object is not a CoordinateOperation");
9349
0
        return nullptr;
9350
0
    }
9351
0
    try {
9352
0
        return pj_obj_create(ctx, co->inverse());
9353
0
    } catch (const std::exception &e) {
9354
0
        proj_log_debug(ctx, __FUNCTION__, e.what());
9355
0
        return nullptr;
9356
0
    }
9357
0
}
9358
9359
// ---------------------------------------------------------------------------
9360
9361
/** \brief Returns the number of steps of a concatenated operation.
9362
 *
9363
 * The input object must be a concatenated operation.
9364
 *
9365
 * @param ctx PROJ context, or NULL for default context
9366
 * @param concatoperation Concatenated operation (must not be NULL)
9367
 * @return the number of steps, or 0 in case of error.
9368
 */
9369
int proj_concatoperation_get_step_count(PJ_CONTEXT *ctx,
9370
0
                                        const PJ *concatoperation) {
9371
0
    SANITIZE_CTX(ctx);
9372
0
    if (!concatoperation) {
9373
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9374
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9375
0
        return false;
9376
0
    }
9377
0
    auto l_co = dynamic_cast<const ConcatenatedOperation *>(
9378
0
        concatoperation->iso_obj.get());
9379
0
    if (!l_co) {
9380
0
        proj_log_error(ctx, __FUNCTION__,
9381
0
                       "Object is not a ConcatenatedOperation");
9382
0
        return false;
9383
0
    }
9384
0
    return static_cast<int>(l_co->operations().size());
9385
0
}
9386
// ---------------------------------------------------------------------------
9387
9388
/** \brief Returns a step of a concatenated operation.
9389
 *
9390
 * The input object must be a concatenated operation.
9391
 *
9392
 * The returned object must be unreferenced with proj_destroy() after
9393
 * use.
9394
 * It should be used by at most one thread at a time.
9395
 *
9396
 * @param ctx PROJ context, or NULL for default context
9397
 * @param concatoperation Concatenated operation (must not be NULL)
9398
 * @param i_step Index of the step to extract. Between 0 and
9399
 *               proj_concatoperation_get_step_count()-1
9400
 * @return Object that must be unreferenced with proj_destroy(), or NULL
9401
 * in case of error.
9402
 */
9403
PJ *proj_concatoperation_get_step(PJ_CONTEXT *ctx, const PJ *concatoperation,
9404
0
                                  int i_step) {
9405
0
    SANITIZE_CTX(ctx);
9406
0
    if (!concatoperation) {
9407
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9408
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9409
0
        return nullptr;
9410
0
    }
9411
0
    auto l_co = dynamic_cast<const ConcatenatedOperation *>(
9412
0
        concatoperation->iso_obj.get());
9413
0
    if (!l_co) {
9414
0
        proj_log_error(ctx, __FUNCTION__,
9415
0
                       "Object is not a ConcatenatedOperation");
9416
0
        return nullptr;
9417
0
    }
9418
0
    const auto &steps = l_co->operations();
9419
0
    if (i_step < 0 || static_cast<size_t>(i_step) >= steps.size()) {
9420
0
        proj_log_error(ctx, __FUNCTION__, "Invalid step index");
9421
0
        return nullptr;
9422
0
    }
9423
0
    return pj_obj_create(ctx, steps[i_step]);
9424
0
}
9425
// ---------------------------------------------------------------------------
9426
9427
/** \brief Opaque object representing an insertion session. */
9428
struct PJ_INSERT_SESSION {
9429
    //! @cond Doxygen_Suppress
9430
    PJ_CONTEXT *ctx = nullptr;
9431
    //!  @endcond
9432
};
9433
9434
// ---------------------------------------------------------------------------
9435
9436
/** \brief Starts a session for proj_get_insert_statements()
9437
 *
9438
 * Starts a new session for one or several calls to
9439
 * proj_get_insert_statements().
9440
 *
9441
 * An insertion session guarantees that the inserted objects will not create
9442
 * conflicting intermediate objects.
9443
 *
9444
 * The session must be stopped with proj_insert_object_session_destroy().
9445
 *
9446
 * Only one session may be active at a time for a given context.
9447
 *
9448
 * @param ctx PROJ context, or NULL for default context
9449
 * @return the session, or NULL in case of error.
9450
 *
9451
 * @since 8.1
9452
 */
9453
0
PJ_INSERT_SESSION *proj_insert_object_session_create(PJ_CONTEXT *ctx) {
9454
0
    SANITIZE_CTX(ctx);
9455
0
    try {
9456
0
        auto dbContext = getDBcontext(ctx);
9457
0
        dbContext->startInsertStatementsSession();
9458
0
        PJ_INSERT_SESSION *session = new PJ_INSERT_SESSION;
9459
0
        session->ctx = ctx;
9460
0
        return session;
9461
0
    } catch (const std::exception &e) {
9462
0
        proj_log_error(ctx, __FUNCTION__, e.what());
9463
0
        return nullptr;
9464
0
    }
9465
0
}
9466
9467
// ---------------------------------------------------------------------------
9468
9469
/** \brief Stops an insertion session started with
9470
 * proj_insert_object_session_create()
9471
 *
9472
 * @param ctx PROJ context, or NULL for default context
9473
 * @param session The insertion session.
9474
 * @since 8.1
9475
 */
9476
void proj_insert_object_session_destroy(PJ_CONTEXT *ctx,
9477
0
                                        PJ_INSERT_SESSION *session) {
9478
0
    SANITIZE_CTX(ctx);
9479
0
    if (session) {
9480
0
        try {
9481
0
            if (session->ctx != ctx) {
9482
0
                proj_log_error(ctx, __FUNCTION__,
9483
0
                               "proj_insert_object_session_destroy() called "
9484
0
                               "with a context different from the one of "
9485
0
                               "proj_insert_object_session_create()");
9486
0
            } else {
9487
0
                auto dbContext = getDBcontext(ctx);
9488
0
                dbContext->stopInsertStatementsSession();
9489
0
            }
9490
0
        } catch (const std::exception &e) {
9491
0
            proj_log_error(ctx, __FUNCTION__, e.what());
9492
0
        }
9493
0
        delete session;
9494
0
    }
9495
0
}
9496
9497
// ---------------------------------------------------------------------------
9498
9499
/** \brief Suggests a database code for the passed object.
9500
 *
9501
 * Supported type of objects are PrimeMeridian, Ellipsoid, Datum, DatumEnsemble,
9502
 * GeodeticCRS, ProjectedCRS, VerticalCRS, CompoundCRS, BoundCRS, Conversion.
9503
 *
9504
 * @param ctx PROJ context, or NULL for default context
9505
 * @param object Object for which to suggest a code.
9506
 * @param authority Authority name into which the object will be inserted.
9507
 * @param numeric_code Whether the code should be numeric, or derived from the
9508
 * object name.
9509
 * @param options NULL terminated list of options, or NULL.
9510
 *                No options are supported currently.
9511
 * @return the suggested code, that is guaranteed to not conflict with an
9512
 * existing one (to be freed with proj_string_destroy),
9513
 * or nullptr in case of error.
9514
 *
9515
 * @since 8.1
9516
 */
9517
char *proj_suggests_code_for(PJ_CONTEXT *ctx, const PJ *object,
9518
                             const char *authority, int numeric_code,
9519
0
                             const char *const *options) {
9520
0
    SANITIZE_CTX(ctx);
9521
0
    (void)options;
9522
9523
0
    if (!object || !authority) {
9524
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9525
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9526
0
        return nullptr;
9527
0
    }
9528
0
    auto identifiedObject =
9529
0
        std::dynamic_pointer_cast<IdentifiedObject>(object->iso_obj);
9530
0
    if (!identifiedObject) {
9531
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9532
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a IdentifiedObject");
9533
0
        return nullptr;
9534
0
    }
9535
9536
0
    try {
9537
0
        auto dbContext = getDBcontext(ctx);
9538
0
        return pj_strdup(dbContext
9539
0
                             ->suggestsCodeFor(NN_NO_CHECK(identifiedObject),
9540
0
                                               std::string(authority),
9541
0
                                               numeric_code != FALSE)
9542
0
                             .c_str());
9543
0
    } catch (const std::exception &e) {
9544
0
        proj_log_error(ctx, __FUNCTION__, e.what());
9545
0
    }
9546
0
    return nullptr;
9547
0
}
9548
9549
// ---------------------------------------------------------------------------
9550
9551
/** \brief Free a string.
9552
 *
9553
 * Only to be used with functions that document using this function.
9554
 *
9555
 * @param str String to free.
9556
 *
9557
 * @since 8.1
9558
 */
9559
0
void proj_string_destroy(char *str) { free(str); }
9560
9561
// ---------------------------------------------------------------------------
9562
9563
/** \brief Returns SQL statements needed to insert the passed object into the
9564
 * database.
9565
 *
9566
 * proj_insert_object_session_create() may have been called previously.
9567
 *
9568
 * It is strongly recommended that new objects should not be added in common
9569
 * registries, such as "EPSG", "ESRI", "IAU", etc. Users should use a custom
9570
 * authority name instead. If a new object should be
9571
 * added to the official EPSG registry, users are invited to follow the
9572
 * procedure explained at https://epsg.org/dataset-change-requests.html.
9573
 *
9574
 * Combined with proj_context_get_database_structure(), users can create
9575
 * auxiliary databases, instead of directly modifying the main proj.db database.
9576
 * Those auxiliary databases can be specified through
9577
 * proj_context_set_database_path() or the PROJ_AUX_DB environment variable.
9578
 *
9579
 * @param ctx PROJ context, or NULL for default context
9580
 * @param session The insertion session. May be NULL if a single object must be
9581
 *                inserted.
9582
 * @param object The object to insert into the database. Currently only
9583
 *               PrimeMeridian, Ellipsoid, Datum, GeodeticCRS, ProjectedCRS,
9584
 *               VerticalCRS, CompoundCRS or BoundCRS are supported.
9585
 * @param authority Authority name into which the object will be inserted.
9586
 *                  Must not be NULL.
9587
 * @param code Code with which the object will be inserted.Must not be NULL.
9588
 * @param numeric_codes Whether intermediate objects that can be created should
9589
 *                      use numeric codes (true), or may be alphanumeric (false)
9590
 * @param allowed_authorities NULL terminated list of authority names, or NULL.
9591
 *                            Authorities to which intermediate objects are
9592
 *                            allowed to refer to. "authority" will be
9593
 *                            implicitly added to it. Note that unit,
9594
 *                            coordinate systems, projection methods and
9595
 *                            parameters will in any case be allowed to refer
9596
 *                            to EPSG.
9597
 *                            If NULL, allowed_authorities defaults to
9598
 *                            {"EPSG", "PROJ", nullptr}
9599
 * @param options NULL terminated list of options, or NULL.
9600
 *                No options are supported currently.
9601
 *
9602
 * @return a list of insert statements (to be freed with
9603
 *         proj_string_list_destroy()), or NULL in case of error.
9604
 * @since 8.1
9605
 */
9606
PROJ_STRING_LIST proj_get_insert_statements(
9607
    PJ_CONTEXT *ctx, PJ_INSERT_SESSION *session, const PJ *object,
9608
    const char *authority, const char *code, int numeric_codes,
9609
0
    const char *const *allowed_authorities, const char *const *options) {
9610
0
    SANITIZE_CTX(ctx);
9611
0
    (void)options;
9612
9613
0
    struct TempSessionHolder {
9614
0
      private:
9615
0
        PJ_CONTEXT *m_ctx;
9616
0
        PJ_INSERT_SESSION *m_tempSession;
9617
0
        TempSessionHolder(const TempSessionHolder &) = delete;
9618
0
        TempSessionHolder &operator=(const TempSessionHolder &) = delete;
9619
9620
0
      public:
9621
0
        TempSessionHolder(PJ_CONTEXT *ctx, PJ_INSERT_SESSION *session)
9622
0
            : m_ctx(ctx),
9623
0
              m_tempSession(session ? nullptr
9624
0
                                    : proj_insert_object_session_create(ctx)) {}
9625
9626
0
        ~TempSessionHolder() {
9627
0
            if (m_tempSession) {
9628
0
                proj_insert_object_session_destroy(m_ctx, m_tempSession);
9629
0
            }
9630
0
        }
9631
9632
0
        inline PJ_INSERT_SESSION *GetTempSession() const {
9633
0
            return m_tempSession;
9634
0
        }
9635
0
    };
9636
9637
0
    try {
9638
0
        TempSessionHolder oHolder(ctx, session);
9639
0
        if (!session) {
9640
0
            session = oHolder.GetTempSession();
9641
0
            if (!session) {
9642
0
                return nullptr;
9643
0
            }
9644
0
        }
9645
9646
0
        if (!object || !authority || !code) {
9647
0
            proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9648
0
            proj_log_error(ctx, __FUNCTION__, "missing required input");
9649
0
            return nullptr;
9650
0
        }
9651
0
        auto identifiedObject =
9652
0
            std::dynamic_pointer_cast<IdentifiedObject>(object->iso_obj);
9653
0
        if (!identifiedObject) {
9654
0
            proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9655
0
            proj_log_error(ctx, __FUNCTION__,
9656
0
                           "Object is not a IdentifiedObject");
9657
0
            return nullptr;
9658
0
        }
9659
9660
0
        auto dbContext = getDBcontext(ctx);
9661
0
        std::vector<std::string> allowedAuthorities{"EPSG", "PROJ"};
9662
0
        if (allowed_authorities) {
9663
0
            allowedAuthorities.clear();
9664
0
            for (auto iter = allowed_authorities; *iter; ++iter) {
9665
0
                allowedAuthorities.emplace_back(*iter);
9666
0
            }
9667
0
        }
9668
0
        auto statements = dbContext->getInsertStatementsFor(
9669
0
            NN_NO_CHECK(identifiedObject), authority, code,
9670
0
            numeric_codes != FALSE, allowedAuthorities);
9671
0
        return to_string_list(std::move(statements));
9672
0
    } catch (const std::exception &e) {
9673
0
        proj_log_error(ctx, __FUNCTION__, e.what());
9674
0
    }
9675
0
    return nullptr;
9676
0
}
9677
9678
// ---------------------------------------------------------------------------
9679
9680
/** \brief Returns a list of geoid models available for that crs
9681
 *
9682
 * The list includes the geoid models connected directly with the crs,
9683
 * or via "Height Depth Reversal" or "Change of Vertical Unit" transformations.
9684
 * The returned list is NULL terminated and must be freed with
9685
 * proj_string_list_destroy().
9686
 *
9687
 * @param ctx Context, or NULL for default context.
9688
 * @param auth_name Authority name (must not be NULL)
9689
 * @param code Object code (must not be NULL)
9690
 * @param options should be set to NULL for now
9691
 * @return list of geoid models names (to be freed with
9692
 * proj_string_list_destroy()), or NULL in case of error.
9693
 * @since 8.1
9694
 */
9695
PROJ_STRING_LIST
9696
proj_get_geoid_models_from_database(PJ_CONTEXT *ctx, const char *auth_name,
9697
                                    const char *code,
9698
0
                                    const char *const *options) {
9699
0
    SANITIZE_CTX(ctx);
9700
0
    if (!auth_name || !code) {
9701
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9702
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9703
0
        return nullptr;
9704
0
    }
9705
0
    (void)options;
9706
0
    try {
9707
0
        const std::string codeStr(code);
9708
0
        auto factory = AuthorityFactory::create(getDBcontext(ctx), auth_name);
9709
0
        auto geoidModels = factory->getGeoidModels(codeStr);
9710
0
        return to_string_list(std::move(geoidModels));
9711
0
    } catch (const std::exception &e) {
9712
0
        proj_log_error(ctx, __FUNCTION__, e.what());
9713
0
    }
9714
0
    return nullptr;
9715
0
}
9716
9717
// ---------------------------------------------------------------------------
9718
9719
/** \brief Instantiate a CoordinateMetadata object
9720
 *
9721
 * @since 9.4
9722
 */
9723
9724
PJ *proj_coordinate_metadata_create(PJ_CONTEXT *ctx, const PJ *crs,
9725
0
                                    double epoch) {
9726
0
    SANITIZE_CTX(ctx);
9727
0
    if (!crs) {
9728
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9729
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9730
0
        return nullptr;
9731
0
    }
9732
0
    auto crsCast = std::dynamic_pointer_cast<CRS>(crs->iso_obj);
9733
0
    if (!crsCast) {
9734
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CRS");
9735
0
        return nullptr;
9736
0
    }
9737
0
    try {
9738
0
        auto dbContext = getDBcontextNoException(ctx, __FUNCTION__);
9739
0
        return pj_obj_create(ctx, CoordinateMetadata::create(
9740
0
                                      NN_NO_CHECK(crsCast), epoch, dbContext));
9741
0
    } catch (const std::exception &e) {
9742
0
        proj_log_debug(ctx, __FUNCTION__, e.what());
9743
0
        return nullptr;
9744
0
    }
9745
0
}
9746
9747
// ---------------------------------------------------------------------------
9748
9749
/** \brief Return the coordinate epoch associated with a CoordinateMetadata.
9750
 *
9751
 * It may return a NaN value if there is no associated coordinate epoch.
9752
 *
9753
 * @since 9.2
9754
 */
9755
0
double proj_coordinate_metadata_get_epoch(PJ_CONTEXT *ctx, const PJ *obj) {
9756
0
    SANITIZE_CTX(ctx);
9757
0
    if (!obj) {
9758
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9759
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9760
0
        return std::numeric_limits<double>::quiet_NaN();
9761
0
    }
9762
0
    auto ptr = obj->iso_obj.get();
9763
0
    auto coordinateMetadata = dynamic_cast<const CoordinateMetadata *>(ptr);
9764
0
    if (coordinateMetadata) {
9765
0
        if (coordinateMetadata->coordinateEpoch().has_value()) {
9766
0
            return coordinateMetadata->coordinateEpochAsDecimalYear();
9767
0
        }
9768
0
        return std::numeric_limits<double>::quiet_NaN();
9769
0
    }
9770
0
    proj_log_error(ctx, __FUNCTION__, "Object is not a CoordinateMetadata");
9771
0
    return std::numeric_limits<double>::quiet_NaN();
9772
0
}
9773
9774
// ---------------------------------------------------------------------------
9775
9776
/** \brief Return whether a CRS has an associated PointMotionOperation
9777
 *
9778
 * @since 9.4
9779
 */
9780
0
int proj_crs_has_point_motion_operation(PJ_CONTEXT *ctx, const PJ *crs) {
9781
0
    SANITIZE_CTX(ctx);
9782
0
    if (!crs) {
9783
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
9784
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
9785
0
        return false;
9786
0
    }
9787
0
    auto l_crs = dynamic_cast<const CRS *>(crs->iso_obj.get());
9788
0
    if (!l_crs) {
9789
0
        proj_log_error(ctx, __FUNCTION__, "Object is not a CRS");
9790
0
        return false;
9791
0
    }
9792
0
    auto geodeticCRS = l_crs->extractGeodeticCRS();
9793
0
    if (!geodeticCRS)
9794
0
        return false;
9795
0
    try {
9796
0
        auto factory =
9797
0
            AuthorityFactory::create(getDBcontext(ctx), std::string());
9798
0
        return !factory
9799
0
                    ->getPointMotionOperationsFor(NN_NO_CHECK(geodeticCRS),
9800
0
                                                  false)
9801
0
                    .empty();
9802
0
    } catch (const std::exception &e) {
9803
0
        proj_log_error(ctx, __FUNCTION__, e.what());
9804
0
    }
9805
0
    return false;
9806
0
}
9807
9808
// ---------------------------------------------------------------------------
9809
9810
static UnitOfMeasure createScaleUnit(const char *name, double convFactor,
9811
                                     const char *unit_auth_name = nullptr,
9812
0
                                     const char *unit_code = nullptr) {
9813
0
    return name == nullptr
9814
0
               ? UnitOfMeasure::SCALE_UNITY
9815
0
               : UnitOfMeasure(name, convFactor, UnitOfMeasure::Type::SCALE,
9816
0
                               unit_auth_name ? unit_auth_name : "",
9817
0
                               unit_code ? unit_code : "");
9818
0
}
9819
9820
// ---------------------------------------------------------------------------
9821
9822
/** \brief Instantiate a conversion with method Affine Parametric, assuming
9823
 * it works in linear coordinate space
9824
 *
9825
 * This method is defined as
9826
 * <a href="https://epsg.org/coord-operation-method_9624/index.html">
9827
 * EPSG:9624</a>.
9828
 *
9829
 * Use this method for a CRS whose coordinate system has 2 axis.
9830
 *
9831
 * The returned object must be unreferenced with proj_destroy() after
9832
 * use.
9833
 * It should be used by at most one thread at a time.
9834
 *
9835
 * @param ctx PROJ context, or NULL for default context
9836
 * @param name Conversion name, or nullptr
9837
 * @param A0 translation term for output first axis
9838
 * @param A0_unit_name Name of the linear unit for A0. Or NULL for metre.
9839
 * @param A0_unit_conv_factor Conversion factor to metre for A0. Note: this is
9840
 * indicative only, and not taken into account in coordinate conversion
9841
 * @param A1 coefficient term for output first axis taking that is multiplied
9842
 * with the value along the source first axis
9843
 * @param A1_unit_name Name of the scale unit for A1. Or NULL for unity.
9844
 * @param A1_unit_conv_factor Conversion factor to unity for A1. Note: this is
9845
 * indicative only, and not taken into account in coordinate conversion
9846
 * @param A2 coefficient term for output first axis taking that is multiplied
9847
 * with the value along the source second axis
9848
 * @param A2_unit_name Name of the scale unit for A2. Or NULL for unity.
9849
 * @param A2_unit_conv_factor Conversion factor to unity for A2. Note: this is
9850
 * indicative only, and not taken into account in coordinate conversion
9851
 * @param B0 translation term for output second axis
9852
 * @param B0_unit_name Name of the linear unit for B0. Or NULL for metre.
9853
 * @param B0_unit_conv_factor Conversion factor to metre for B0. Note: this is
9854
 * indicative only, and not taken into account in coordinate conversion
9855
 * @param B1 coefficient term for output second axis taking that is multiplied
9856
 * with the value along the source first axis
9857
 * @param B1_unit_name Name of the scale unit for B1. Or NULL for unity.
9858
 * @param B1_unit_conv_factor Conversion factor to unity for B1. Note: this is
9859
 * indicative only, and not taken into account in coordinate conversion
9860
 * @param B2 coefficient term for output second axis taking that is multiplied
9861
 * with the value along the source second axis
9862
 * @param B2_unit_name Name of the scale unit for B2. Or NULL for unity.
9863
 * @param B2_unit_conv_factor Conversion factor to unity for B2. Note: this is
9864
 * indicative only, and not taken into account in coordinate conversion
9865
 * @return Object that must be unreferenced with proj_destroy(), or NULL
9866
 * in case of error.
9867
 *
9868
 * @since 9.8
9869
 */
9870
/* clang-format off */
9871
PJ PROJ_DLL *proj_create_linear_affine_parametric_conversion(
9872
    PJ_CONTEXT *ctx, const char* name,
9873
    double A0, const char *A0_unit_name, double A0_unit_conv_factor,
9874
    double A1, const char *A1_unit_name, double A1_unit_conv_factor,
9875
    double A2, const char *A2_unit_name, double A2_unit_conv_factor,
9876
    double B0, const char *B0_unit_name, double B0_unit_conv_factor,
9877
    double B1, const char *B1_unit_name, double B1_unit_conv_factor,
9878
    double B2, const char *B2_unit_name, double B2_unit_conv_factor)
9879
/* clang-format on */
9880
0
{
9881
0
    SANITIZE_CTX(ctx);
9882
0
    try {
9883
0
        auto conv = Conversion::createAffineParametric(
9884
0
            createPropertyMapName(name),
9885
0
            Length(A0, createLinearUnit(A0_unit_name, A0_unit_conv_factor)),
9886
0
            Scale(A1, createScaleUnit(A1_unit_name, A1_unit_conv_factor)),
9887
0
            Scale(A2, createScaleUnit(A2_unit_name, A2_unit_conv_factor)),
9888
0
            Length(B0, createLinearUnit(B0_unit_name, B0_unit_conv_factor)),
9889
0
            Scale(B1, createScaleUnit(B1_unit_name, B1_unit_conv_factor)),
9890
0
            Scale(B2, createScaleUnit(B2_unit_name, B2_unit_conv_factor)));
9891
0
        return pj_obj_create(ctx, conv);
9892
0
    } catch (const std::exception &e) {
9893
0
        proj_log_error(ctx, __FUNCTION__, e.what());
9894
0
    }
9895
0
    return nullptr;
9896
0
}
9897
9898
// ---------------------------------------------------------------------------
9899
9900
/** \brief Instantiate a conversion with method 3D Affine Parametric, assuming
9901
 * it works in linear coordinate space
9902
 *
9903
 * Use this method for a CRS whose coordinate system has 3 axis.
9904
 *
9905
 * The returned object must be unreferenced with proj_destroy() after
9906
 * use.
9907
 * It should be used by at most one thread at a time.
9908
 *
9909
 * @param ctx PROJ context, or NULL for default context
9910
 * @param name Conversion name, or nullptr
9911
 * @param A0 translation term for output first axis
9912
 * @param A0_unit_name Name of the linear unit for A0. Or NULL for metre.
9913
 * @param A0_unit_conv_factor Conversion factor to metre for A0. Note: this is
9914
 * indicative only, and not taken into account in coordinate conversion
9915
 * @param A1 coefficient term for output first axis taking that is multiplied
9916
 * with the value along the source first axis
9917
 * @param A1_unit_name Name of the scale unit for A1. Or NULL for unity.
9918
 * @param A1_unit_conv_factor Conversion factor to unity for A1. Note: this is
9919
 * indicative only, and not taken into account in coordinate conversion
9920
 * @param A2 coefficient term for output first axis taking that is multiplied
9921
 * with the value along the source second axis
9922
 * @param A2_unit_name Name of the scale unit for A2. Or NULL for unity.
9923
 * @param A2_unit_conv_factor Conversion factor to unity for A2. Note: this is
9924
 * indicative only, and not taken into account in coordinate conversion
9925
 * @param A3 coefficient term for output first axis taking that is multiplied
9926
 * with the value along the source third axis
9927
 * @param A3_unit_name Name of the scale unit for A3. Or NULL for unity.
9928
 * @param A3_unit_conv_factor Conversion factor to unity for A3. Note: this is
9929
 * indicative only, and not taken into account in coordinate conversion
9930
 * @param B0 translation term for output second axis
9931
 * @param B0_unit_name Name of the linear unit for B0. Or NULL for metre.
9932
 * @param B0_unit_conv_factor Conversion factor to metre for B0. Note: this is
9933
 * indicative only, and not taken into account in coordinate conversion
9934
 * @param B1 coefficient term for output second axis taking that is multiplied
9935
 * with the value along the source first axis
9936
 * @param B1_unit_name Name of the scale unit for B1. Or NULL for unity.
9937
 * @param B1_unit_conv_factor Conversion factor to unity for B1. Note: this is
9938
 * indicative only, and not taken into account in coordinate conversion
9939
 * @param B2 coefficient term for output second axis taking that is multiplied
9940
 * with the value along the source second axis
9941
 * @param B2_unit_name Name of the scale unit for B2. Or NULL for unity.
9942
 * @param B2_unit_conv_factor Conversion factor to unity for B2. Note: this is
9943
 * indicative only, and not taken into account in coordinate conversion
9944
 * @param B3 coefficient term for output second axis taking that is multiplied
9945
 * with the value along the source third axis
9946
 * @param B3_unit_name Name of the scale unit for B3. Or NULL for unity.
9947
 * @param B3_unit_conv_factor Conversion factor to unity for B3. Note: this is
9948
 * indicative only, and not taken into account in coordinate conversion
9949
 * @param C0 translation term for output third axis
9950
 * @param C0_unit_name Name of the linear unit for C0. Or NULL for metre.
9951
 * @param C0_unit_conv_factor Conversion factor to metre for C0. Note: this is
9952
 * indicative only, and not taken into account in coordinate conversion
9953
 * @param C1 coefficient term for output third axis taking that is multiplied
9954
 * with the value along the source first axis
9955
 * @param C1_unit_name Name of the scale unit for C1. Or NULL for unity.
9956
 * @param C1_unit_conv_factor Conversion factor to unity for C1. Note: this is
9957
 * indicative only, and not taken into account in coordinate conversion
9958
 * @param C2 coefficient term for output third axis taking that is multiplied
9959
 * with the value along the source second axis
9960
 * @param C2_unit_name Name of the scale unit for C2. Or NULL for unity.
9961
 * @param C2_unit_conv_factor Conversion factor to unity for C2. Note: this is
9962
 * indicative only, and not taken into account in coordinate conversion
9963
 * @param C3 coefficient term for output third axis taking that is multiplied
9964
 * with the value along the source third axis
9965
 * @param C3_unit_name Name of the scale unit for C3. Or NULL for unity.
9966
 * @param C3_unit_conv_factor Conversion factor to unity for C3. Note: this is
9967
 * indicative only, and not taken into account in coordinate conversion
9968
 * @return Object that must be unreferenced with proj_destroy(), or NULL
9969
 * in case of error.
9970
 *
9971
 * @since 9.9
9972
 */
9973
/* clang-format off */
9974
PJ PROJ_DLL *proj_create_linear_3D_affine_parametric_conversion(
9975
    PJ_CONTEXT *ctx, const char* name,
9976
    double A0, const char *A0_unit_name, double A0_unit_conv_factor,
9977
    double A1, const char *A1_unit_name, double A1_unit_conv_factor,
9978
    double A2, const char *A2_unit_name, double A2_unit_conv_factor,
9979
    double A3, const char *A3_unit_name, double A3_unit_conv_factor,
9980
    double B0, const char *B0_unit_name, double B0_unit_conv_factor,
9981
    double B1, const char *B1_unit_name, double B1_unit_conv_factor,
9982
    double B2, const char *B2_unit_name, double B2_unit_conv_factor,
9983
    double B3, const char *B3_unit_name, double B3_unit_conv_factor,
9984
    double C0, const char *C0_unit_name, double C0_unit_conv_factor,
9985
    double C1, const char *C1_unit_name, double C1_unit_conv_factor,
9986
    double C2, const char *C2_unit_name, double C2_unit_conv_factor,
9987
    double C3, const char *C3_unit_name, double C3_unit_conv_factor)
9988
/* clang-format on */
9989
0
{
9990
0
    SANITIZE_CTX(ctx);
9991
0
    try {
9992
0
        auto conv = Conversion::createAffineParametric(
9993
0
            createPropertyMapName(name),
9994
0
            Length(A0, createLinearUnit(A0_unit_name, A0_unit_conv_factor)),
9995
0
            Scale(A1, createScaleUnit(A1_unit_name, A1_unit_conv_factor)),
9996
0
            Scale(A2, createScaleUnit(A2_unit_name, A2_unit_conv_factor)),
9997
0
            Scale(A3, createScaleUnit(A3_unit_name, A3_unit_conv_factor)),
9998
0
            Length(B0, createLinearUnit(B0_unit_name, B0_unit_conv_factor)),
9999
0
            Scale(B1, createScaleUnit(B1_unit_name, B1_unit_conv_factor)),
10000
0
            Scale(B2, createScaleUnit(B2_unit_name, B2_unit_conv_factor)),
10001
0
            Scale(B3, createScaleUnit(B3_unit_name, B3_unit_conv_factor)),
10002
0
            Length(C0, createLinearUnit(C0_unit_name, C0_unit_conv_factor)),
10003
0
            Scale(C1, createScaleUnit(C1_unit_name, C1_unit_conv_factor)),
10004
0
            Scale(C2, createScaleUnit(C2_unit_name, C2_unit_conv_factor)),
10005
0
            Scale(C3, createScaleUnit(C3_unit_name, C3_unit_conv_factor)));
10006
0
        return pj_obj_create(ctx, conv);
10007
0
    } catch (const std::exception &e) {
10008
0
        proj_log_error(ctx, __FUNCTION__, e.what());
10009
0
    }
10010
0
    return nullptr;
10011
0
}
10012
10013
// ---------------------------------------------------------------------------
10014
10015
/** \brief Instantiate a DerivedProjectedCRS
10016
 *
10017
 * The returned object must be unreferenced with proj_destroy() after
10018
 * use.
10019
 * It should be used by at most one thread at a time.
10020
 *
10021
 * @param ctx PROJ context, or NULL for default context
10022
 * @param crs_name CRS name. Or NULL
10023
 * @param base_proj_crs Base ProjectedCRS. Must not be NULL.
10024
 * @param deriving_conversion Conversion. Must not be NULL.
10025
 * @param coordinate_system Cartesian coordinate system. Must not be NULL.
10026
 *
10027
 * @return Object that must be unreferenced with
10028
 * proj_destroy(), or NULL in case of error.
10029
 *
10030
 * @since 9.8
10031
 */
10032
10033
PJ *proj_create_derived_projected_crs(PJ_CONTEXT *ctx, const char *crs_name,
10034
                                      const PJ *base_proj_crs,
10035
                                      const PJ *deriving_conversion,
10036
0
                                      const PJ *coordinate_system) {
10037
0
    SANITIZE_CTX(ctx);
10038
0
    if (!base_proj_crs || !deriving_conversion || !coordinate_system) {
10039
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
10040
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
10041
0
        return nullptr;
10042
0
    }
10043
0
    auto baseProjCRS =
10044
0
        std::dynamic_pointer_cast<ProjectedCRS>(base_proj_crs->iso_obj);
10045
0
    if (!baseProjCRS) {
10046
0
        return nullptr;
10047
0
    }
10048
0
    auto conv =
10049
0
        std::dynamic_pointer_cast<Conversion>(deriving_conversion->iso_obj);
10050
0
    if (!conv) {
10051
0
        return nullptr;
10052
0
    }
10053
0
    auto cs =
10054
0
        std::dynamic_pointer_cast<CartesianCS>(coordinate_system->iso_obj);
10055
0
    if (!cs) {
10056
0
        return nullptr;
10057
0
    }
10058
0
    try {
10059
0
        return pj_obj_create(ctx, DerivedProjectedCRS::create(
10060
0
                                      createPropertyMapName(crs_name),
10061
0
                                      NN_NO_CHECK(baseProjCRS),
10062
0
                                      NN_NO_CHECK(conv), NN_NO_CHECK(cs)));
10063
0
    } catch (const std::exception &e) {
10064
0
        proj_log_error(ctx, __FUNCTION__, e.what());
10065
0
    }
10066
0
    return nullptr;
10067
0
}
10068
10069
// ---------------------------------------------------------------------------
10070
10071
/** \brief Add a deriving conversion to a horizontal CRS
10072
 *
10073
 * This is for now restricting when the horizontal CRS is a ProjectedCRS,
10074
 * a CompoundCRS with a ProjectedCRS, or a BoundCRS of a ProjectedCRS.
10075
 *
10076
 * The returned object must be unreferenced with proj_destroy() after
10077
 * use.
10078
 * It should be used by at most one thread at a time.
10079
 *
10080
 * @param ctx PROJ context, or NULL for default context
10081
 * @param crs_name Name of the derived projected CRS. Or NULL
10082
 * @param base_crs ProjectedCRS, CompoundCRS with a ProjectedCRS, or
10083
 * BoundCRS of a ProjectedCRS. Must not be NULL.
10084
 * @param deriving_conversion Conversion. Must not be NULL.
10085
 * @param coordinate_system Cartesian coordinate system. Must not be NULL.
10086
 *
10087
 * @return Object that must be unreferenced with
10088
 * proj_destroy(), or NULL in case of error.
10089
 *
10090
 * @since 9.8
10091
 */
10092
PJ *proj_crs_add_horizontal_derived_conversion(PJ_CONTEXT *ctx,
10093
                                               const char *crs_name,
10094
                                               const PJ *base_crs,
10095
                                               const PJ *deriving_conversion,
10096
0
                                               const PJ *coordinate_system) {
10097
0
    SANITIZE_CTX(ctx);
10098
0
    if (!base_crs || !deriving_conversion || !coordinate_system) {
10099
0
        proj_context_errno_set(ctx, PROJ_ERR_OTHER_API_MISUSE);
10100
0
        proj_log_error(ctx, __FUNCTION__, "missing required input");
10101
0
        return nullptr;
10102
0
    }
10103
10104
0
    CRSPtr baseCRS = std::dynamic_pointer_cast<CRS>(base_crs->iso_obj);
10105
0
    if (!baseCRS)
10106
0
        return nullptr;
10107
0
    auto conv =
10108
0
        std::dynamic_pointer_cast<Conversion>(deriving_conversion->iso_obj);
10109
0
    if (!conv) {
10110
0
        return nullptr;
10111
0
    }
10112
0
    auto cs =
10113
0
        std::dynamic_pointer_cast<CartesianCS>(coordinate_system->iso_obj);
10114
0
    if (!cs) {
10115
0
        return nullptr;
10116
0
    }
10117
10118
0
    auto compoundCRS = std::dynamic_pointer_cast<CompoundCRS>(baseCRS);
10119
0
    CRSPtr verticalCRS;
10120
0
    if (compoundCRS) {
10121
0
        const auto &components = compoundCRS->componentReferenceSystems();
10122
0
        if (components.size() == 2) {
10123
0
            baseCRS = components[0];
10124
0
            verticalCRS = components[1];
10125
0
        }
10126
0
    }
10127
10128
0
    auto boundCRS = std::dynamic_pointer_cast<BoundCRS>(baseCRS);
10129
0
    if (boundCRS) {
10130
0
        baseCRS = boundCRS->baseCRS();
10131
0
    }
10132
10133
0
    auto baseProjCRS = std::dynamic_pointer_cast<ProjectedCRS>(baseCRS);
10134
0
    if (!baseProjCRS) {
10135
0
        return nullptr;
10136
0
    }
10137
10138
0
    try {
10139
0
        CRSNNPtr retCRS = DerivedProjectedCRS::create(
10140
0
            createPropertyMapName(crs_name), NN_NO_CHECK(baseProjCRS),
10141
0
            NN_NO_CHECK(conv), NN_NO_CHECK(cs));
10142
10143
0
        if (boundCRS) {
10144
0
            retCRS = BoundCRS::create(retCRS, boundCRS->hubCRS(),
10145
0
                                      boundCRS->transformation());
10146
0
        }
10147
0
        if (verticalCRS) {
10148
0
            retCRS = CompoundCRS::create(
10149
0
                createPropertyMapName(
10150
0
                    (retCRS->nameStr() + " + " + verticalCRS->nameStr())
10151
0
                        .c_str()),
10152
0
                {retCRS, NN_NO_CHECK(verticalCRS)});
10153
0
        }
10154
0
        return pj_obj_create(ctx, retCRS);
10155
0
    } catch (const std::exception &e) {
10156
0
        proj_log_error(ctx, __FUNCTION__, e.what());
10157
0
    }
10158
0
    return nullptr;
10159
0
}