Coverage Report

Created: 2026-04-10 07:04

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