aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--scripts/reference_exported_symbols.txt2
-rw-r--r--src/iso19111/c_api.cpp59
-rw-r--r--src/proj.h7
-rw-r--r--test/unit/test_c_api.cpp69
4 files changed, 137 insertions, 0 deletions
diff --git a/scripts/reference_exported_symbols.txt b/scripts/reference_exported_symbols.txt
index b759c96a..f3d13bd7 100644
--- a/scripts/reference_exported_symbols.txt
+++ b/scripts/reference_exported_symbols.txt
@@ -762,6 +762,8 @@ proj_as_proj_string
proj_assign_context
proj_as_wkt
proj_clone
+proj_concatoperation_get_step
+proj_concatoperation_get_step_count
proj_context_create
proj_context_delete_cpp_context(projCppContext*)
proj_context_destroy
diff --git a/src/iso19111/c_api.cpp b/src/iso19111/c_api.cpp
index bc1d0bd8..7a77ccfb 100644
--- a/src/iso19111/c_api.cpp
+++ b/src/iso19111/c_api.cpp
@@ -6976,3 +6976,62 @@ PJ *proj_normalize_for_visualization(PJ_CONTEXT *ctx, const PJ *obj) {
return nullptr;
}
}
+
+// ---------------------------------------------------------------------------
+
+/** \brief Returns the number of steps of a concatenated operation.
+ *
+ * The input object must be a concatenated operation.
+ *
+ * @param ctx PROJ context, or NULL for default context
+ * @param concatoperation Concatenated operation (must not be NULL)
+ * @return the number of steps, or 0 in case of error.
+ */
+int proj_concatoperation_get_step_count(PJ_CONTEXT *ctx,
+ const PJ *concatoperation) {
+ SANITIZE_CTX(ctx);
+ assert(concatoperation);
+ auto l_co = dynamic_cast<const ConcatenatedOperation *>(
+ concatoperation->iso_obj.get());
+ if (!l_co) {
+ proj_log_error(ctx, __FUNCTION__,
+ "Object is not a ConcatenatedOperation");
+ return false;
+ }
+ return static_cast<int>(l_co->operations().size());
+}
+// ---------------------------------------------------------------------------
+
+/** \brief Returns a step of a concatenated operation.
+ *
+ * The input object must be a concatenated operation.
+ *
+ * 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 concatoperation Concatenated operation (must not be NULL)
+ * @param i_step Index of the step to extract. Between 0 and
+ * proj_concatoperation_get_step_count()-1
+ * @return Object that must be unreferenced with proj_destroy(), or NULL
+ * in case of error.
+ */
+PJ *proj_concatoperation_get_step(PJ_CONTEXT *ctx, const PJ *concatoperation,
+ int i_step) {
+ SANITIZE_CTX(ctx);
+ assert(concatoperation);
+ auto l_co = dynamic_cast<const ConcatenatedOperation *>(
+ concatoperation->iso_obj.get());
+ if (!l_co) {
+ proj_log_error(ctx, __FUNCTION__,
+ "Object is not a ConcatenatedOperation");
+ return nullptr;
+ }
+ const auto &steps = l_co->operations();
+ if (i_step < 0 || static_cast<size_t>(i_step) >= steps.size()) {
+ proj_log_error(ctx, __FUNCTION__, "Invalid step index");
+ return nullptr;
+ }
+ return pj_obj_create(ctx, steps[i_step]);
+}
diff --git a/src/proj.h b/src/proj.h
index 0282ddfe..25cd981c 100644
--- a/src/proj.h
+++ b/src/proj.h
@@ -1064,6 +1064,13 @@ int PROJ_DLL proj_coordoperation_get_towgs84_values(PJ_CONTEXT *ctx,
int value_count,
int emit_error_if_incompatible);
+int PROJ_DLL proj_concatoperation_get_step_count(PJ_CONTEXT *ctx,
+ const PJ *concatoperation);
+
+PJ PROJ_DLL *proj_concatoperation_get_step(PJ_CONTEXT *ctx,
+ const PJ *concatoperation,
+ int i_step);
+
/**@}*/
#ifdef __cplusplus
diff --git a/test/unit/test_c_api.cpp b/test/unit/test_c_api.cpp
index 99904fd4..82974e13 100644
--- a/test/unit/test_c_api.cpp
+++ b/test/unit/test_c_api.cpp
@@ -3420,4 +3420,73 @@ TEST_F(CApi, proj_get_scope) {
}
}
+// ---------------------------------------------------------------------------
+
+TEST_F(CApi, proj_concatoperation_get_step) {
+ // Test on a non concatenated operation
+ {
+ auto co = proj_create_from_database(m_ctxt, "EPSG", "8048",
+ PJ_CATEGORY_COORDINATE_OPERATION,
+ false, nullptr);
+ ObjectKeeper keeper(co);
+ ASSERT_NE(co, nullptr);
+ ASSERT_NE(proj_get_type(co), PJ_TYPE_CONCATENATED_OPERATION);
+
+ ASSERT_EQ(proj_concatoperation_get_step_count(m_ctxt, co), 0);
+ ASSERT_EQ(proj_concatoperation_get_step(m_ctxt, co, 0), nullptr);
+ }
+ // Test on a concatenated operation
+ {
+ auto ctxt = proj_create_operation_factory_context(m_ctxt, nullptr);
+ ASSERT_NE(ctxt, nullptr);
+ ContextKeeper keeper_ctxt(ctxt);
+
+ // GDA94 / MGA zone 56
+ auto source_crs = proj_create_from_database(
+ m_ctxt, "EPSG", "28356", PJ_CATEGORY_CRS, false, nullptr);
+ ASSERT_NE(source_crs, nullptr);
+ ObjectKeeper keeper_source_crs(source_crs);
+
+ // GDA2020 / MGA zone 56
+ auto target_crs = proj_create_from_database(
+ m_ctxt, "EPSG", "7856", PJ_CATEGORY_CRS, false, nullptr);
+ ASSERT_NE(target_crs, nullptr);
+ ObjectKeeper keeper_target_crs(target_crs);
+
+ proj_operation_factory_context_set_spatial_criterion(
+ m_ctxt, ctxt, PROJ_SPATIAL_CRITERION_PARTIAL_INTERSECTION);
+
+ proj_operation_factory_context_set_grid_availability_use(
+ m_ctxt, ctxt, PROJ_GRID_AVAILABILITY_IGNORED);
+
+ auto res = proj_create_operations(m_ctxt, source_crs, target_crs, ctxt);
+ ASSERT_NE(res, nullptr);
+ ObjListKeeper keeper_res(res);
+
+ ASSERT_GT(proj_list_get_count(res), 0);
+
+ auto op = proj_list_get(m_ctxt, res, 0);
+ ASSERT_NE(op, nullptr);
+ ObjectKeeper keeper_op(op);
+
+ ASSERT_EQ(proj_get_type(op), PJ_TYPE_CONCATENATED_OPERATION);
+ ASSERT_EQ(proj_concatoperation_get_step_count(m_ctxt, op), 3);
+
+ EXPECT_EQ(proj_concatoperation_get_step(m_ctxt, op, -1), nullptr);
+ EXPECT_EQ(proj_concatoperation_get_step(m_ctxt, op, 3), nullptr);
+
+ auto step = proj_concatoperation_get_step(m_ctxt, op, 1);
+ ASSERT_NE(step, nullptr);
+ ObjectKeeper keeper_step(step);
+
+ const char* scope = proj_get_scope(step);
+ EXPECT_NE(scope, nullptr);
+ EXPECT_NE(std::string(scope), std::string());
+
+ const char* remarks = proj_get_remarks(step);
+ EXPECT_NE(remarks, nullptr);
+ EXPECT_NE(std::string(remarks), std::string());
+ }
+}
+
} // namespace