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> ¶meters,
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 ¶meters = 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 ¶m = parameters[index];
7075 const auto ¶m_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