Coverage Report

Created: 2025-12-31 06:48

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