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