aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmake/ProjUtilities.cmake3
-rw-r--r--docs/source/development/bindings.rst6
-rw-r--r--include/proj/coordinatesystem.hpp8
-rw-r--r--scripts/reference_exported_symbols.txt2
-rw-r--r--src/4D_api.cpp4
-rw-r--r--src/bin_proj.cmake2
-rw-r--r--src/iso19111/c_api.cpp54
-rw-r--r--src/iso19111/coordinatesystem.cpp22
-rw-r--r--src/iso19111/io.cpp72
-rw-r--r--src/lib_proj.cmake1
-rw-r--r--src/proj.h4
-rw-r--r--src/proj_experimental.h16
-rw-r--r--src/proj_internal.h2
-rw-r--r--test/unit/test_c_api.cpp155
14 files changed, 320 insertions, 31 deletions
diff --git a/cmake/ProjUtilities.cmake b/cmake/ProjUtilities.cmake
index 1a2b6052..d63cedac 100644
--- a/cmake/ProjUtilities.cmake
+++ b/cmake/ProjUtilities.cmake
@@ -89,7 +89,8 @@ endfunction()
#
# Generates output name for given target depending on platform and version.
-# For instance, on Windows, libraries get ABI version suffix proj_X_Y.{dll|lib}.
+# For instance, on Windows, dynamic link libraries get ABI version suffix
+# proj_X_Y.dll.
#
function(proj_target_output_name TARGET_NAME OUTPUT_NAME)
diff --git a/docs/source/development/bindings.rst b/docs/source/development/bindings.rst
index 27d3fddc..c339bfdc 100644
--- a/docs/source/development/bindings.rst
+++ b/docs/source/development/bindings.rst
@@ -18,6 +18,12 @@ Ruby
`proj4rb <https://github.com/cfis/proj4rb>`_:
Bindings for PROJ in ruby
+Rust
+=======
+
+`proj <https://github.com/georust/proj>`_:
+Rust bindings for the latest stable version of PROJ
+
TCL
========
`proj4tcl <http://wiki.tcl.tk/41270>`_:
diff --git a/include/proj/coordinatesystem.hpp b/include/proj/coordinatesystem.hpp
index 573f18e2..20c3383c 100644
--- a/include/proj/coordinatesystem.hpp
+++ b/include/proj/coordinatesystem.hpp
@@ -352,19 +352,27 @@ class PROJ_GCC_DLL EllipsoidalCS final : public CoordinateSystem {
create(const util::PropertyMap &properties,
const CoordinateSystemAxisNNPtr &axis1,
const CoordinateSystemAxisNNPtr &axis2);
+
PROJ_DLL static EllipsoidalCSNNPtr
create(const util::PropertyMap &properties,
const CoordinateSystemAxisNNPtr &axis1,
const CoordinateSystemAxisNNPtr &axis2,
const CoordinateSystemAxisNNPtr &axis3);
+
PROJ_DLL static EllipsoidalCSNNPtr
createLatitudeLongitude(const common::UnitOfMeasure &unit);
+
PROJ_DLL static EllipsoidalCSNNPtr createLatitudeLongitudeEllipsoidalHeight(
const common::UnitOfMeasure &angularUnit,
const common::UnitOfMeasure &linearUnit);
+
PROJ_DLL static EllipsoidalCSNNPtr
createLongitudeLatitude(const common::UnitOfMeasure &unit);
+ PROJ_DLL static EllipsoidalCSNNPtr createLongitudeLatitudeEllipsoidalHeight(
+ const common::UnitOfMeasure &angularUnit,
+ const common::UnitOfMeasure &linearUnit);
+
//! @cond Doxygen_Suppress
/** \brief Typical axis order. */
diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt
index 71de2d0e..c6937d18 100644
--- a/scripts/reference_exported_symbols.txt
+++ b/scripts/reference_exported_symbols.txt
@@ -202,6 +202,7 @@ osgeo::proj::cs::DateTimeTemporalCS::create(osgeo::proj::util::PropertyMap const
osgeo::proj::cs::DateTimeTemporalCS::~DateTimeTemporalCS()
osgeo::proj::cs::EllipsoidalCS::createLatitudeLongitudeEllipsoidalHeight(osgeo::proj::common::UnitOfMeasure const&, osgeo::proj::common::UnitOfMeasure const&)
osgeo::proj::cs::EllipsoidalCS::createLatitudeLongitude(osgeo::proj::common::UnitOfMeasure const&)
+osgeo::proj::cs::EllipsoidalCS::createLongitudeLatitudeEllipsoidalHeight(osgeo::proj::common::UnitOfMeasure const&, osgeo::proj::common::UnitOfMeasure const&)
osgeo::proj::cs::EllipsoidalCS::createLongitudeLatitude(osgeo::proj::common::UnitOfMeasure const&)
osgeo::proj::cs::EllipsoidalCS::create(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::cs::CoordinateSystemAxis> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::cs::CoordinateSystemAxis> > const&)
osgeo::proj::cs::EllipsoidalCS::create(osgeo::proj::util::PropertyMap const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::cs::CoordinateSystemAxis> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::cs::CoordinateSystemAxis> > const&, dropbox::oxygen::nn<std::shared_ptr<osgeo::proj::cs::CoordinateSystemAxis> > const&)
@@ -878,6 +879,7 @@ proj_create_crs_to_crs
proj_create_crs_to_crs_from_pj
proj_create_cs
proj_create_ellipsoidal_2D_cs
+proj_create_ellipsoidal_3D_cs
proj_create_engineering_crs
proj_create_from_database
proj_create_from_name
diff --git a/src/4D_api.cpp b/src/4D_api.cpp
index 3a9582e6..ffef8d81 100644
--- a/src/4D_api.cpp
+++ b/src/4D_api.cpp
@@ -946,7 +946,7 @@ static PJ* add_coord_op_to_list(PJ* op,
}
/*****************************************************************************/
-static PJ* create_operation_to_base_geog_crs(PJ_CONTEXT* ctx, PJ* crs) {
+static PJ* create_operation_to_base_geog_crs(PJ_CONTEXT* ctx, const PJ* crs) {
/*****************************************************************************/
// Create a geographic 2D long-lat degrees CRS that is related to the
// CRS
@@ -1052,7 +1052,7 @@ PJ *proj_create_crs_to_crs (PJ_CONTEXT *ctx, const char *source_crs, const char
}
/*****************************************************************************/
-PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, PJ *source_crs, PJ *target_crs, PJ_AREA *area, const char* const *) {
+PJ *proj_create_crs_to_crs_from_pj (PJ_CONTEXT *ctx, const PJ *source_crs, const PJ *target_crs, PJ_AREA *area, const char* const *) {
/******************************************************************************
Create a transformation pipeline between two known coordinate reference
systems.
diff --git a/src/bin_proj.cmake b/src/bin_proj.cmake
index ce282fc6..3826ac8d 100644
--- a/src/bin_proj.cmake
+++ b/src/bin_proj.cmake
@@ -9,7 +9,7 @@ source_group("Source Files\\Bin" FILES ${PROJ_SRC})
add_executable(binproj ${PROJ_SRC})
set_target_properties(binproj
PROPERTIES
- OUTPUT_NAME proj)
+ RUNTIME_OUTPUT_NAME proj)
target_link_libraries(binproj ${PROJ_LIBRARIES})
target_compile_options(binproj PRIVATE ${PROJ_CXX_WARN_FLAGS})
diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp
index 53cfa31e..45eb16d1 100644
--- a/src/iso19111/c_api.cpp
+++ b/src/iso19111/c_api.cpp
@@ -3763,6 +3763,58 @@ PJ *proj_create_ellipsoidal_2D_cs(PJ_CONTEXT *ctx,
// ---------------------------------------------------------------------------
+/** \brief Instantiate a Ellipsoidal 3D
+ *
+ * The returned object must be unreferenced with proj_destroy() after
+ * use.
+ * It should be used by at most one thread at a time.
+ *
+ * @param ctx PROJ context, or NULL for default context
+ * @param type Coordinate system type.
+ * @param horizontal_angular_unit_name Horizontal angular unit name.
+ * @param horizontal_angular_unit_conv_factor Horizontal angular unit conversion
+ * factor to SI.
+ * @param vertical_linear_unit_name Vertical linear unit name.
+ * @param vertical_linear_unit_conv_factor Vertical linear unit conversion
+ * factor to SI.
+ *
+ * @return Object that must be unreferenced with
+ * proj_destroy(), or NULL in case of error.
+ * @since 7.0
+ */
+
+PJ *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) {
+ try {
+ switch (type) {
+ case PJ_ELLPS3D_LONGITUDE_LATITUDE_HEIGHT:
+ return pj_obj_create(
+ ctx, EllipsoidalCS::createLongitudeLatitudeEllipsoidalHeight(
+ createAngularUnit(horizontal_angular_unit_name,
+ horizontal_angular_unit_conv_factor),
+ createLinearUnit(vertical_linear_unit_name,
+ vertical_linear_unit_conv_factor)));
+
+ case PJ_ELLPS3D_LATITUDE_LONGITUDE_HEIGHT:
+ return pj_obj_create(
+ ctx, EllipsoidalCS::createLatitudeLongitudeEllipsoidalHeight(
+ createAngularUnit(horizontal_angular_unit_name,
+ horizontal_angular_unit_conv_factor),
+ createLinearUnit(vertical_linear_unit_name,
+ vertical_linear_unit_conv_factor)));
+ }
+ } catch (const std::exception &e) {
+ proj_log_error(ctx, __FUNCTION__, e.what());
+ }
+ return nullptr;
+}
+
+// ---------------------------------------------------------------------------
+
/** \brief Instantiate a ProjectedCRS
*
* The returned object must be unreferenced with proj_destroy() after
@@ -6039,6 +6091,7 @@ PJ *proj_create_conversion_equal_earth(PJ_CONTEXT *ctx, double center_long,
int proj_coordoperation_is_instantiable(PJ_CONTEXT *ctx,
const PJ *coordoperation) {
+ SANITIZE_CTX(ctx);
assert(coordoperation);
auto op = dynamic_cast<const CoordinateOperation *>(
coordoperation->iso_obj.get());
@@ -6081,6 +6134,7 @@ int proj_coordoperation_is_instantiable(PJ_CONTEXT *ctx,
int proj_coordoperation_has_ballpark_transformation(PJ_CONTEXT *ctx,
const PJ *coordoperation) {
+ SANITIZE_CTX(ctx);
assert(coordoperation);
auto op = dynamic_cast<const CoordinateOperation *>(
coordoperation->iso_obj.get());
diff --git a/src/iso19111/coordinatesystem.cpp b/src/iso19111/coordinatesystem.cpp
index c58fcaaa..6769b486 100644
--- a/src/iso19111/coordinatesystem.cpp
+++ b/src/iso19111/coordinatesystem.cpp
@@ -789,6 +789,28 @@ EllipsoidalCS::createLongitudeLatitude(const common::UnitOfMeasure &unit) {
// ---------------------------------------------------------------------------
+/** \brief Instantiate a EllipsoidalCS with a Longitude (first), Latitude
+ * (second) axis and ellipsoidal height (third) axis.
+ *
+ * @param angularUnit Angular unit of the latitude and longitude axes.
+ * @param linearUnit Linear unit of the ellipsoidal height axis.
+ * @return a new EllipsoidalCS.
+ * @since 7.0
+ */
+EllipsoidalCSNNPtr EllipsoidalCS::createLongitudeLatitudeEllipsoidalHeight(
+ const common::UnitOfMeasure &angularUnit,
+ const common::UnitOfMeasure &linearUnit) {
+ return EllipsoidalCS::create(
+ util::PropertyMap(), CoordinateSystemAxis::createLONG_EAST(angularUnit),
+ CoordinateSystemAxis::createLAT_NORTH(angularUnit),
+ CoordinateSystemAxis::create(
+ util::PropertyMap().set(IdentifiedObject::NAME_KEY,
+ AxisName::Ellipsoidal_height),
+ AxisAbbreviation::h, AxisDirection::UP, linearUnit));
+}
+
+// ---------------------------------------------------------------------------
+
//! @cond Doxygen_Suppress
/** \brief Return the axis order in an enumerated way. */
EllipsoidalCS::AxisOrder EllipsoidalCS::axisOrder() const {
diff --git a/src/iso19111/io.cpp b/src/iso19111/io.cpp
index 097bc5b8..a86982d4 100644
--- a/src/iso19111/io.cpp
+++ b/src/iso19111/io.cpp
@@ -8638,6 +8638,21 @@ static const metadata::ExtentPtr &getExtent(const crs::CRS *crs) {
//! @endcond
+namespace {
+struct PJContextHolder {
+ PJ_CONTEXT *ctx_;
+ bool bFree_;
+
+ PJContextHolder(PJ_CONTEXT *ctx, bool bFree) : ctx_(ctx), bFree_(bFree) {}
+ ~PJContextHolder() {
+ if (bFree_)
+ proj_context_destroy(ctx_);
+ }
+ PJContextHolder(const PJContextHolder &) = delete;
+ PJContextHolder &operator=(const PJContextHolder &) = delete;
+};
+} // namespace
+
// ---------------------------------------------------------------------------
/** \brief Instantiate a sub-class of BaseObject from a PROJ string.
@@ -8652,8 +8667,10 @@ PROJStringParser::createFromPROJString(const std::string &projString) {
// In some abnormal situations involving init=epsg:XXXX syntax, we could
// have infinite loop
- if (d->ctx_ && d->ctx_->curStringInCreateFromPROJString == projString) {
- throw ParsingException("invalid PROJ string");
+ if (d->ctx_ &&
+ d->ctx_->projStringParserCreateFromPROJStringRecursionCounter == 2) {
+ throw ParsingException(
+ "Infinite recursion in PROJStringParser::createFromPROJString()");
}
d->steps_.clear();
@@ -8701,27 +8718,38 @@ PROJStringParser::createFromPROJString(const std::string &projString) {
const std::string &stepName = d->steps_[0].name;
if (ci_starts_with(stepName, "epsg:") ||
ci_starts_with(stepName, "IGNF:")) {
+
+ /* We create a new context so as to avoid messing up with the */
+ /* errorno of the main context, when trying to find the likely */
+ /* missing epsg file */
+ auto ctx = proj_context_create();
+ if (!ctx) {
+ throw ParsingException("out of memory");
+ }
+ PJContextHolder contextHolder(ctx, true);
+ if (d->ctx_) {
+ ctx->set_search_paths(d->ctx_->search_paths);
+ ctx->file_finder = d->ctx_->file_finder;
+ ctx->file_finder_legacy = d->ctx_->file_finder_legacy;
+ ctx->file_finder_user_data = d->ctx_->file_finder_user_data;
+ }
+
bool usePROJ4InitRules = d->usePROJ4InitRules_;
if (!usePROJ4InitRules) {
- PJ_CONTEXT *ctx = proj_context_create();
- if (ctx) {
- usePROJ4InitRules = proj_context_get_use_proj4_init_rules(
- ctx, FALSE) == TRUE;
- proj_context_destroy(ctx);
- }
+ usePROJ4InitRules =
+ proj_context_get_use_proj4_init_rules(ctx, FALSE) == TRUE;
}
if (!usePROJ4InitRules) {
throw ParsingException("init=epsg:/init=IGNF: syntax not "
"supported in non-PROJ4 emulation mode");
}
- PJ_CONTEXT *ctx = proj_context_create();
char unused[256];
std::string initname(stepName);
initname.resize(initname.find(':'));
int file_found =
pj_find_file(ctx, initname.c_str(), unused, sizeof(unused));
- proj_context_destroy(ctx);
+
if (!file_found) {
auto obj = createFromUserInput(stepName, d->dbContext_, true);
auto crs = dynamic_cast<CRS *>(obj.get());
@@ -8790,19 +8818,19 @@ PROJStringParser::createFromPROJString(const std::string &projString) {
}
}
- paralist *init = pj_mkparam(("init=" + d->steps_[0].name).c_str());
- if (!init) {
+ auto ctx = d->ctx_ ? d->ctx_ : proj_context_create();
+ if (!ctx) {
throw ParsingException("out of memory");
}
- PJ_CONTEXT *ctx = d->ctx_ ? d->ctx_ : proj_context_create();
- if (!ctx) {
- pj_dealloc(init);
+ PJContextHolder contextHolder(ctx, ctx != d->ctx_);
+
+ paralist *init = pj_mkparam(("init=" + d->steps_[0].name).c_str());
+ if (!init) {
throw ParsingException("out of memory");
}
+ ctx->projStringParserCreateFromPROJStringRecursionCounter++;
paralist *list = pj_expand_init(ctx, init);
- if (ctx != d->ctx_) {
- proj_context_destroy(ctx);
- }
+ ctx->projStringParserCreateFromPROJStringRecursionCounter--;
if (!list) {
pj_dealloc(init);
throw ParsingException("cannot expand " + projString);
@@ -8933,18 +8961,14 @@ PROJStringParser::createFromPROJString(const std::string &projString) {
proj_log_func(pj_context, &logger, Logger::log);
proj_context_use_proj4_init_rules(pj_context, d->usePROJ4InitRules_);
}
- if (d->ctx_) {
- d->ctx_->curStringInCreateFromPROJString = projString;
- }
+ pj_context->projStringParserCreateFromPROJStringRecursionCounter++;
auto log_level = proj_log_level(pj_context, PJ_LOG_NONE);
auto pj = pj_create_internal(
pj_context, (projString.find("type=crs") != std::string::npos
? projString + " +disable_grid_presence_check"
: projString)
.c_str());
- if (d->ctx_) {
- d->ctx_->curStringInCreateFromPROJString.clear();
- }
+ pj_context->projStringParserCreateFromPROJStringRecursionCounter--;
valid = pj != nullptr;
proj_log_level(pj_context, log_level);
diff --git a/src/lib_proj.cmake b/src/lib_proj.cmake
index bad60324..d1d6f766 100644
--- a/src/lib_proj.cmake
+++ b/src/lib_proj.cmake
@@ -405,6 +405,7 @@ if(WIN32)
PROPERTIES
VERSION "${${PROJECT_INTERN_NAME}_BUILD_VERSION}"
OUTPUT_NAME "${PROJ_CORE_TARGET_OUTPUT_NAME}"
+ ARCHIVE_OUTPUT_NAME "${PROJ_CORE_TARGET}"
CLEAN_DIRECT_OUTPUT 1)
elseif(BUILD_FRAMEWORKS_AND_BUNDLE)
set_target_properties(${PROJ_CORE_TARGET}
diff --git a/src/proj.h b/src/proj.h
index 6e6d63b4..a01c8a36 100644
--- a/src/proj.h
+++ b/src/proj.h
@@ -358,8 +358,8 @@ PJ PROJ_DLL *proj_create (PJ_CONTEXT *ctx, const char *definition);
PJ PROJ_DLL *proj_create_argv (PJ_CONTEXT *ctx, int argc, char **argv);
PJ PROJ_DLL *proj_create_crs_to_crs(PJ_CONTEXT *ctx, const char *source_crs, const char *target_crs, PJ_AREA *area);
PJ PROJ_DLL *proj_create_crs_to_crs_from_pj(PJ_CONTEXT *ctx,
- PJ *source_crs,
- PJ *target_crs,
+ const PJ *source_crs,
+ const PJ *target_crs,
PJ_AREA *area,
const char* const *options);
PJ PROJ_DLL *proj_normalize_for_visualization(PJ_CONTEXT *ctx, const PJ* obj);
diff --git a/src/proj_experimental.h b/src/proj_experimental.h
index 0e97ac9f..5a96203c 100644
--- a/src/proj_experimental.h
+++ b/src/proj_experimental.h
@@ -130,6 +130,22 @@ PJ PROJ_DLL *proj_create_ellipsoidal_2D_cs(PJ_CONTEXT *ctx,
const char* unit_name,
double unit_conv_factor);
+/** Type of Ellipsoidal 3D coordinate system. */
+typedef enum
+{
+ /* Longitude-Latitude-Height(up) */
+ PJ_ELLPS3D_LONGITUDE_LATITUDE_HEIGHT,
+ /* Latitude-Longitude-Height(up) */
+ PJ_ELLPS3D_LATITUDE_LONGITUDE_HEIGHT,
+} PJ_ELLIPSOIDAL_CS_3D_TYPE;
+
+PJ PROJ_DLL *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);
+
PJ_OBJ_LIST PROJ_DLL *proj_query_geodetic_crs_from_datum(
PJ_CONTEXT *ctx,
const char *crs_auth_name,
diff --git a/src/proj_internal.h b/src/proj_internal.h
index 761746c1..4a126e98 100644
--- a/src/proj_internal.h
+++ b/src/proj_internal.h
@@ -706,7 +706,7 @@ struct projCtx_t {
const char* (*file_finder) (PJ_CONTEXT *, const char*, void* user_data) = nullptr;
void* file_finder_user_data = nullptr;
- std::string curStringInCreateFromPROJString{};
+ int projStringParserCreateFromPROJStringRecursionCounter = 0; // to avoid potential infinite recursion in PROJStringParser::createFromPROJString()
projCtx_t() = default;
projCtx_t(const projCtx_t&);
diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp
index ace66a0c..fd129c80 100644
--- a/test/unit/test_c_api.cpp
+++ b/test/unit/test_c_api.cpp
@@ -3737,4 +3737,159 @@ TEST_F(CApi, proj_create_crs_to_crs_from_pj) {
"+step +proj=utm +zone=31 +ellps=WGS84");
}
+// ---------------------------------------------------------------------------
+
+static void
+check_axis_is_latitude(PJ_CONTEXT *ctx, PJ *cs, int axis_number,
+ const char *unit_name = "degree",
+ double unit_conv_factor = 0.017453292519943295,
+ const char *auth = "EPSG", const char *code = "9122") {
+
+ const char *name = nullptr;
+ const char *abbrev = nullptr;
+ const char *direction = nullptr;
+ double unitConvFactor = 0.0;
+ const char *unitName = nullptr;
+ const char *unitAuthority = nullptr;
+ const char *unitCode = nullptr;
+
+ EXPECT_TRUE(proj_cs_get_axis_info(ctx, cs, axis_number, &name, &abbrev,
+ &direction, &unitConvFactor, &unitName,
+ &unitAuthority, &unitCode));
+ ASSERT_NE(name, nullptr);
+ ASSERT_NE(abbrev, nullptr);
+ ASSERT_NE(direction, nullptr);
+ ASSERT_NE(unitName, nullptr);
+ if (auth) {
+ ASSERT_NE(unitAuthority, nullptr);
+ ASSERT_NE(unitCode, nullptr);
+ }
+ EXPECT_EQ(std::string(name), "Latitude");
+ EXPECT_EQ(std::string(abbrev), "lat");
+ EXPECT_EQ(std::string(direction), "north");
+ EXPECT_EQ(unitConvFactor, unit_conv_factor) << unitConvFactor;
+ EXPECT_EQ(std::string(unitName), unit_name);
+ if (auth) {
+ EXPECT_EQ(std::string(unitAuthority), auth);
+ EXPECT_EQ(std::string(unitCode), code);
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+static void
+check_axis_is_longitude(PJ_CONTEXT *ctx, PJ *cs, int axis_number,
+ const char *unit_name = "degree",
+ double unit_conv_factor = 0.017453292519943295,
+ const char *auth = "EPSG", const char *code = "9122") {
+
+ const char *name = nullptr;
+ const char *abbrev = nullptr;
+ const char *direction = nullptr;
+ double unitConvFactor = 0.0;
+ const char *unitName = nullptr;
+ const char *unitAuthority = nullptr;
+ const char *unitCode = nullptr;
+
+ EXPECT_TRUE(proj_cs_get_axis_info(ctx, cs, axis_number, &name, &abbrev,
+ &direction, &unitConvFactor, &unitName,
+ &unitAuthority, &unitCode));
+ ASSERT_NE(name, nullptr);
+ ASSERT_NE(abbrev, nullptr);
+ ASSERT_NE(direction, nullptr);
+ ASSERT_NE(unitName, nullptr);
+ if (auth) {
+ ASSERT_NE(unitAuthority, nullptr);
+ ASSERT_NE(unitCode, nullptr);
+ }
+ EXPECT_EQ(std::string(name), "Longitude");
+ EXPECT_EQ(std::string(abbrev), "lon");
+ EXPECT_EQ(std::string(direction), "east");
+ EXPECT_EQ(unitConvFactor, unit_conv_factor) << unitConvFactor;
+ EXPECT_EQ(std::string(unitName), unit_name);
+ if (auth) {
+ EXPECT_EQ(std::string(unitAuthority), auth);
+ EXPECT_EQ(std::string(unitCode), code);
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+static void check_axis_is_height(PJ_CONTEXT *ctx, PJ *cs, int axis_number,
+ const char *unit_name = "metre",
+ double unit_conv_factor = 1,
+ const char *auth = "EPSG",
+ const char *code = "9001") {
+
+ const char *name = nullptr;
+ const char *abbrev = nullptr;
+ const char *direction = nullptr;
+ double unitConvFactor = 0.0;
+ const char *unitName = nullptr;
+ const char *unitAuthority = nullptr;
+ const char *unitCode = nullptr;
+
+ EXPECT_TRUE(proj_cs_get_axis_info(ctx, cs, axis_number, &name, &abbrev,
+ &direction, &unitConvFactor, &unitName,
+ &unitAuthority, &unitCode));
+ ASSERT_NE(name, nullptr);
+ ASSERT_NE(abbrev, nullptr);
+ ASSERT_NE(direction, nullptr);
+ ASSERT_NE(unitName, nullptr);
+ if (auth) {
+ ASSERT_NE(unitAuthority, nullptr);
+ ASSERT_NE(unitCode, nullptr);
+ }
+ EXPECT_EQ(std::string(name), "Ellipsoidal height");
+ EXPECT_EQ(std::string(abbrev), "h");
+ EXPECT_EQ(std::string(direction), "up");
+ EXPECT_EQ(unitConvFactor, unit_conv_factor) << unitConvFactor;
+ EXPECT_EQ(std::string(unitName), unit_name);
+ if (auth) {
+ EXPECT_EQ(std::string(unitAuthority), auth);
+ EXPECT_EQ(std::string(unitCode), code);
+ }
+}
+
+// ---------------------------------------------------------------------------
+
+TEST_F(CApi, proj_create_ellipsoidal_3D_cs) {
+
+ {
+ auto cs = proj_create_ellipsoidal_3D_cs(
+ m_ctxt, PJ_ELLPS3D_LATITUDE_LONGITUDE_HEIGHT, nullptr, 0, nullptr,
+ 0);
+ ObjectKeeper keeper_cs(cs);
+ ASSERT_NE(cs, nullptr);
+
+ EXPECT_EQ(proj_cs_get_type(m_ctxt, cs), PJ_CS_TYPE_ELLIPSOIDAL);
+
+ EXPECT_EQ(proj_cs_get_axis_count(m_ctxt, cs), 3);
+
+ check_axis_is_latitude(m_ctxt, cs, 0);
+
+ check_axis_is_longitude(m_ctxt, cs, 1);
+
+ check_axis_is_height(m_ctxt, cs, 2);
+ }
+
+ {
+ auto cs = proj_create_ellipsoidal_3D_cs(
+ m_ctxt, PJ_ELLPS3D_LONGITUDE_LATITUDE_HEIGHT, "foo", 0.5, "bar",
+ 0.6);
+ ObjectKeeper keeper_cs(cs);
+ ASSERT_NE(cs, nullptr);
+
+ EXPECT_EQ(proj_cs_get_type(m_ctxt, cs), PJ_CS_TYPE_ELLIPSOIDAL);
+
+ EXPECT_EQ(proj_cs_get_axis_count(m_ctxt, cs), 3);
+
+ check_axis_is_longitude(m_ctxt, cs, 0, "foo", 0.5, nullptr, nullptr);
+
+ check_axis_is_latitude(m_ctxt, cs, 1, "foo", 0.5, nullptr, nullptr);
+
+ check_axis_is_height(m_ctxt, cs, 2, "bar", 0.6, nullptr, nullptr);
+ }
+}
+
} // namespace