diff --git a/libs/MeshKernel/CMakeLists.txt b/libs/MeshKernel/CMakeLists.txt index e210c0800..93f10084b 100644 --- a/libs/MeshKernel/CMakeLists.txt +++ b/libs/MeshKernel/CMakeLists.txt @@ -48,6 +48,7 @@ set( ${SRC_DIR}/MeshEdgeLength.cpp ${SRC_DIR}/Mesh1D.cpp ${SRC_DIR}/Mesh2D.cpp + ${SRC_DIR}/Mesh2DFaceBounds.cpp ${SRC_DIR}/Mesh2DGenerateGlobal.cpp ${SRC_DIR}/Mesh2DIntersections.cpp ${SRC_DIR}/Mesh2DToCurvilinear.cpp @@ -182,6 +183,7 @@ set( ${DOMAIN_INC_DIR}/MeshEdgeLength.hpp ${DOMAIN_INC_DIR}/Mesh1D.hpp ${DOMAIN_INC_DIR}/Mesh2D.hpp + ${DOMAIN_INC_DIR}/Mesh2DFaceBounds.hpp ${DOMAIN_INC_DIR}/Mesh2DGenerateGlobal.hpp ${DOMAIN_INC_DIR}/Mesh2DIntersections.hpp ${DOMAIN_INC_DIR}/Mesh2DToCurvilinear.hpp diff --git a/libs/MeshKernel/include/MeshKernel/Definitions.hpp b/libs/MeshKernel/include/MeshKernel/Definitions.hpp index 1608910ea..503c86dd8 100644 --- a/libs/MeshKernel/include/MeshKernel/Definitions.hpp +++ b/libs/MeshKernel/include/MeshKernel/Definitions.hpp @@ -159,7 +159,8 @@ namespace meshkernel Orthogonality = 0, EdgeLength = 1, FaceCircumcenter = 2, - NetlinkContourPolygon = 3 + NetlinkContourPolygon = 3, + FaceBounds = 4 }; /// \brief Describe how the circumcentre should be computed. diff --git a/libs/MeshKernel/include/MeshKernel/Mesh.hpp b/libs/MeshKernel/include/MeshKernel/Mesh.hpp index 31a351b15..eeacb859f 100644 --- a/libs/MeshKernel/include/MeshKernel/Mesh.hpp +++ b/libs/MeshKernel/include/MeshKernel/Mesh.hpp @@ -369,6 +369,9 @@ namespace meshkernel /// is false then the edge is in-valid. bool IsValidEdge(const UInt edgeId) const; + /// @brief Indicate if the face-id is a valid face + bool IsValidFace(const UInt faceId) const; + /// @brief Apply the reset node action void CommitAction(const ResetNodeAction& undoAction); diff --git a/libs/MeshKernel/include/MeshKernel/Mesh2DFaceBounds.hpp b/libs/MeshKernel/include/MeshKernel/Mesh2DFaceBounds.hpp new file mode 100644 index 000000000..d7121f3a0 --- /dev/null +++ b/libs/MeshKernel/include/MeshKernel/Mesh2DFaceBounds.hpp @@ -0,0 +1,53 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2026. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#pragma once + +#include +#include + +#include "MeshKernel/Constants.hpp" +#include "MeshKernel/Definitions.hpp" +#include "MeshKernel/Mesh.hpp" +#include "MeshKernel/Point.hpp" + +namespace meshkernel::algo +{ + + /// @brief Compute the nodes for each face in the grid, will be saved as facebounds + class Mesh2DFaceBounds + { + public: + /// @brief Compute the nodes for each face in the grid + static std::vector Compute(const Mesh& mesh); + + private: + /// @brief Compute the face bounds for a single face + static void ComputeBoundsForFace(const Mesh& mesh, UInt faceId, std::span faceBounds); + }; + +} // namespace meshkernel::algo diff --git a/libs/MeshKernel/src/Mesh.cpp b/libs/MeshKernel/src/Mesh.cpp index c659220fa..496631828 100644 --- a/libs/MeshKernel/src/Mesh.cpp +++ b/libs/MeshKernel/src/Mesh.cpp @@ -1190,6 +1190,25 @@ bool Mesh::IsValidEdge(const UInt edgeId) const m_nodes[m_edges[edgeId].first].IsValid() && m_nodes[m_edges[edgeId].second].IsValid(); } +bool Mesh::IsValidFace(const UInt faceId) const +{ + if (faceId >= GetNumFaces()) + { + throw ConstraintError("The face index is out of bounds. {} >= {}.", faceId, GetNumFaces()); + } + + for (UInt edgeId : m_facesEdges[faceId]) + { + + if (!IsValidEdge(edgeId)) + { + return false; + } + } + + return true; +} + void Mesh::BuildTree(Location location, const BoundingBox& boundingBox) { switch (location) diff --git a/libs/MeshKernel/src/Mesh2DFaceBounds.cpp b/libs/MeshKernel/src/Mesh2DFaceBounds.cpp new file mode 100644 index 000000000..7251164f0 --- /dev/null +++ b/libs/MeshKernel/src/Mesh2DFaceBounds.cpp @@ -0,0 +1,72 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2026. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#include "MeshKernel/Mesh2DFaceBounds.hpp" + +#include + +#include "MeshKernel/Constants.hpp" +#include "MeshKernel/MeshFaceCenters.hpp" +#include "MeshKernel/Operations.hpp" + +std::vector meshkernel::algo::Mesh2DFaceBounds::Compute(const Mesh& mesh) +{ + std::vector meshFaceBounds(constants::geometric::maximumNumberOfNodesPerFace * mesh.GetNumFaces()); + +#pragma omp parallel for + for (int face = 0; face < static_cast(mesh.GetNumFaces()); ++face) + { + const UInt pointCount = constants::geometric::maximumNumberOfNodesPerFace * static_cast(face); + + std::span faceBounds(meshFaceBounds.data() + pointCount, meshFaceBounds.data() + pointCount + constants::geometric::maximumNumberOfNodesPerFace); + ComputeBoundsForFace(mesh, static_cast(face), faceBounds); + } + + return meshFaceBounds; +} + +void meshkernel::algo::Mesh2DFaceBounds::ComputeBoundsForFace(const Mesh& mesh, UInt faceId, std::span faceBounds) +{ + if (mesh.IsValidFace(faceId)) + { + UInt numNodes = static_cast(mesh.m_numFacesNodes[faceId]); + + for (UInt i = 0; i < numNodes; ++i) + { + faceBounds[i] = mesh.Node(mesh.m_facesNodes[faceId][i]); + } + + for (UInt i = numNodes; i < constants::geometric::maximumNumberOfNodesPerFace; ++i) + { + faceBounds[i] = Point{constants::missing::doubleValue, constants::missing::doubleValue}; + } + } + else + { + std::ranges::fill(faceBounds, Point{constants::missing::doubleValue, constants::missing::doubleValue}); + } +} diff --git a/libs/MeshKernel/tests/src/Mesh2DTest.cpp b/libs/MeshKernel/tests/src/Mesh2DTest.cpp index e9c5da3f3..b0717f25e 100644 --- a/libs/MeshKernel/tests/src/Mesh2DTest.cpp +++ b/libs/MeshKernel/tests/src/Mesh2DTest.cpp @@ -34,6 +34,7 @@ #include "MeshKernel/Entities.hpp" #include "MeshKernel/Mesh.hpp" #include "MeshKernel/Mesh2D.hpp" +#include "MeshKernel/Mesh2DFaceBounds.hpp" #include "MeshKernel/Mesh2DIntersections.hpp" #include "MeshKernel/Mesh2DToCurvilinear.hpp" #include "MeshKernel/MeshEdgeLength.hpp" @@ -1862,3 +1863,29 @@ TEST(Mesh2D, ComputeEdgeNetlinkContourPolygonsSpherical) ++expectedNodeCount; } } + +TEST(Mesh2D, ComputeMesh2DFaceBounds) +{ + std::vector meshPoints{{10.0, 0.0}, {20.0, 10.0}, {10.0, 20.0}, {0.0, 10.0}, {30.0, 20.0}, {0.0, 30.0}, {20.0, 30.0}, {30.0, 30.0}}; + std::vector meshEdges{{0, 1}, {1, 2}, {2, 3}, {3, 0}, {1, 4}, {3, 5}, {5, 6}, {6, 7}, {2, 5}, {2, 6}, {4, 7}}; + + meshkernel::Mesh2D mesh(meshEdges, meshPoints, meshkernel::Projection::cartesian); + mesh.Administrate(); + + meshkernel::algo::Mesh2DFaceBounds mesh2dFaceBounds; + + auto faceBoundPoints(mesh2dFaceBounds.Compute(mesh)); + + std::vector expectedXs{10.0, 20.0, 0.0, -999.0, -999.0, -999.0, 10.0, 0.0, 0.0, -999.0, -999.0, -999.0, 10.0, 20.0, 10.0, 0.0, -999.0, -999.0, 20.0, 30.0, 30.0, 20.0, 10.0, -999.0}; + std::vector expectedYs{20.0, 30.0, 30.0, -999.0, -999.0, -999.0, 20.0, 30.0, 10.0, -999.0, -999.0, -999.0, 0.0, 10.0, 20.0, 10.0, -999.0, -999.0, 10.0, 20.0, 30.0, 30.0, 20.0, -999.0}; + + constexpr double tolerance = 1.0e-10; + + ASSERT_EQ(faceBoundPoints.size(), expectedXs.size()); + + for (size_t i = 0; i < faceBoundPoints.size(); ++i) + { + EXPECT_NEAR(faceBoundPoints[i].x, expectedXs[i], tolerance); + EXPECT_NEAR(faceBoundPoints[i].y, expectedYs[i], tolerance); + } +} diff --git a/libs/MeshKernelApi/CMakeLists.txt b/libs/MeshKernelApi/CMakeLists.txt index 92c2cde91..56beb6522 100644 --- a/libs/MeshKernelApi/CMakeLists.txt +++ b/libs/MeshKernelApi/CMakeLists.txt @@ -28,6 +28,7 @@ set(SRC_LIST ${SRC_DIR}/PropertyCalculator.cpp ${SRC_DIR}/EdgeLengthPropertyCalculator.cpp ${SRC_DIR}/FaceCircumcenterPropertyCalculator.cpp + ${SRC_DIR}/Mesh2DFaceBoundsPropertyCalculator.cpp ${SRC_DIR}/NetlinkContourPolygonPropertyCalculator.cpp ${SRC_DIR}/OrthogonalityPropertyCalculator.cpp ${SRC_DIR}/InterpolatedSamplePropertyCalculator.cpp @@ -65,6 +66,7 @@ set( ${DOMAIN_INC_DIR}/PropertyCalculator.hpp ${DOMAIN_INC_DIR}/EdgeLengthPropertyCalculator.hpp ${DOMAIN_INC_DIR}/FaceCircumcenterPropertyCalculator.hpp + ${DOMAIN_INC_DIR}/Mesh2DFaceBoundsPropertyCalculator.hpp ${DOMAIN_INC_DIR}/NetlinkContourPolygonPropertyCalculator.hpp ${DOMAIN_INC_DIR}/OrthogonalityPropertyCalculator.hpp ${DOMAIN_INC_DIR}/InterpolatedSamplePropertyCalculator.hpp diff --git a/libs/MeshKernelApi/include/MeshKernelApi/Mesh2DFaceBoundsPropertyCalculator.hpp b/libs/MeshKernelApi/include/MeshKernelApi/Mesh2DFaceBoundsPropertyCalculator.hpp new file mode 100644 index 000000000..5671e7a4b --- /dev/null +++ b/libs/MeshKernelApi/include/MeshKernelApi/Mesh2DFaceBoundsPropertyCalculator.hpp @@ -0,0 +1,56 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2026. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#pragma once + +#include "MeshKernel/Definitions.hpp" +#include "MeshKernel/Parameters.hpp" +#include "MeshKernel/SampleInterpolator.hpp" + +#include "MeshKernelApi/GeometryList.hpp" +#include "MeshKernelApi/PropertyCalculator.hpp" + +namespace meshkernelapi +{ + + /// @brief Calculator for the face bounds values for a mesh + class Mesh2DFaceBoundsPropertyCalculator : public PropertyCalculator + { + public: + /// @brief Determine is the calculator can compute the desired results correctly. + /// + /// This has a default of checking that the mesh2d is valid and the location is at faces + bool IsValid(const MeshKernelState& state, const meshkernel::Location location) const override; + + /// @brief Calculate the face bounds values for a mesh + void Calculate(const MeshKernelState& state, const meshkernel::Location location, const GeometryList& geometryList) const override; + + /// @brief Determine the size of the face bounds array (for all faces) required + int Size(const MeshKernelState& state, const meshkernel::Location location) const override; + }; + +} // namespace meshkernelapi diff --git a/libs/MeshKernelApi/include/MeshKernelApi/MeshKernel.hpp b/libs/MeshKernelApi/include/MeshKernelApi/MeshKernel.hpp index 44cf15f29..34d57f7f0 100644 --- a/libs/MeshKernelApi/include/MeshKernelApi/MeshKernel.hpp +++ b/libs/MeshKernelApi/include/MeshKernelApi/MeshKernel.hpp @@ -1250,6 +1250,11 @@ namespace meshkernelapi /// @returns Error code MKERNEL_API int mkernel_mesh2d_get_netlink_contour_polygon_property_type(int& type); + /// @brief Gets an int indicating the face bounds property type for mesh2d + /// @param[out] type The int indicating the face bounds property type + /// @returns Error code + MKERNEL_API int mkernel_mesh2d_get_face_bounds_property_type(int& type); + /// @brief Gets the Mesh2D inner boundary polygons data /// /// @param[in] meshKernelId The id of the mesh state diff --git a/libs/MeshKernelApi/src/Mesh2DFaceBoundsPropertyCalculator.cpp b/libs/MeshKernelApi/src/Mesh2DFaceBoundsPropertyCalculator.cpp new file mode 100644 index 000000000..0a67a6db8 --- /dev/null +++ b/libs/MeshKernelApi/src/Mesh2DFaceBoundsPropertyCalculator.cpp @@ -0,0 +1,70 @@ +//---- GPL --------------------------------------------------------------------- +// +// Copyright (C) Stichting Deltares, 2011-2026. +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation version 3. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// +// contact: delft3d.support@deltares.nl +// Stichting Deltares +// P.O. Box 177 +// 2600 MH Delft, The Netherlands +// +// All indications and logos of, and references to, "Delft3D" and "Deltares" +// are registered trademarks of Stichting Deltares, and remain the property of +// Stichting Deltares. All rights reserved. +// +//------------------------------------------------------------------------------ + +#include "MeshKernelApi/Mesh2DFaceBoundsPropertyCalculator.hpp" +#include "MeshKernelApi/PropertyCalculator.hpp" +#include "MeshKernelApi/State.hpp" + +#include "MeshKernel/Mesh2DFaceBounds.hpp" + +bool meshkernelapi::Mesh2DFaceBoundsPropertyCalculator::IsValid(const MeshKernelState& state, const meshkernel::Location location) const +{ + return state.m_mesh2d != nullptr && state.m_mesh2d->GetNumNodes() > 0 && location == meshkernel::Location::Faces; +} + +void meshkernelapi::Mesh2DFaceBoundsPropertyCalculator::Calculate(const MeshKernelState& state, const meshkernel::Location location, const GeometryList& geometryList) const +{ + if (geometryList.num_coordinates < static_cast(meshkernel::constants::geometric::maximumNumberOfNodesPerFace * state.m_mesh2d->GetNumFaces())) + { + throw meshkernel::ConstraintError("GeometryList with wrong dimensions, {} must be greater than or equal to {}", + geometryList.num_coordinates, Size(state, location)); + } + + std::vector faceBounds(meshkernel::algo::Mesh2DFaceBounds::Compute(*state.m_mesh2d)); + + size_t size = static_cast(Size(state, location)); + std::span xCoord(geometryList.coordinates_x, size); + std::span yCoord(geometryList.coordinates_y, size); + + for (size_t i = 0; i < faceBounds.size(); ++i) + { + xCoord[i] = faceBounds[i].x; + yCoord[i] = faceBounds[i].y; + } +} + +int meshkernelapi::Mesh2DFaceBoundsPropertyCalculator::Size(const MeshKernelState& state, const meshkernel::Location location) const +{ + int size = -1; + + if (location == meshkernel::Location::Faces) + { + size = meshkernel::constants::geometric::maximumNumberOfNodesPerFace * static_cast(state.m_mesh2d->GetNumFaces()); + } + + return size; +} diff --git a/libs/MeshKernelApi/src/MeshKernel.cpp b/libs/MeshKernelApi/src/MeshKernel.cpp index 4164cf4d5..7d3f9504d 100644 --- a/libs/MeshKernelApi/src/MeshKernel.cpp +++ b/libs/MeshKernelApi/src/MeshKernel.cpp @@ -107,6 +107,7 @@ #include "MeshKernelApi/EdgeLengthPropertyCalculator.hpp" #include "MeshKernelApi/FaceCircumcenterPropertyCalculator.hpp" #include "MeshKernelApi/InterpolatedSamplePropertyCalculator.hpp" +#include "MeshKernelApi/Mesh2DFaceBoundsPropertyCalculator.hpp" #include "MeshKernelApi/NetlinkContourPolygonPropertyCalculator.hpp" #include "MeshKernelApi/OrthogonalityPropertyCalculator.hpp" #include "MeshKernelApi/PropertyCalculator.hpp" @@ -144,7 +145,7 @@ namespace meshkernelapi int GeneratePropertyId() { // The current property id, initialised with a value equal to the last enum in Mesh2D:::Property enum values - static int currentPropertyId = static_cast(meshkernel::Property::NetlinkContourPolygon); + static int currentPropertyId = static_cast(meshkernel::Property::FaceBounds); // Increment and return the current property id value. return ++currentPropertyId; @@ -166,6 +167,9 @@ namespace meshkernelapi propertyId = static_cast(meshkernel::Property::NetlinkContourPolygon); propertyMap.emplace(propertyId, std::make_shared()); + propertyId = static_cast(meshkernel::Property::FaceBounds); + propertyMap.emplace(propertyId, std::make_shared()); + return propertyMap; } @@ -1025,6 +1029,13 @@ namespace meshkernelapi return lastExitCode; } + MKERNEL_API int mkernel_mesh2d_get_face_bounds_property_type(int& type) + { + lastExitCode = meshkernel::ExitCode::Success; + type = static_cast(meshkernel::Property::FaceBounds); + return lastExitCode; + } + MKERNEL_API int mkernel_mesh1d_get_dimensions(int meshKernelId, Mesh1D& mesh1d) { lastExitCode = meshkernel::ExitCode::Success; diff --git a/libs/MeshKernelApi/src/NetlinkContourPolygonPropertyCalculator.cpp b/libs/MeshKernelApi/src/NetlinkContourPolygonPropertyCalculator.cpp index e5bc66078..df60b1167 100644 --- a/libs/MeshKernelApi/src/NetlinkContourPolygonPropertyCalculator.cpp +++ b/libs/MeshKernelApi/src/NetlinkContourPolygonPropertyCalculator.cpp @@ -39,7 +39,7 @@ bool meshkernelapi::NetlinkContourPolygonPropertyCalculator::IsValid(const MeshK void meshkernelapi::NetlinkContourPolygonPropertyCalculator::Calculate(const MeshKernelState& state, const meshkernel::Location location, const GeometryList& geometryList) const { - if (static_cast(geometryList.num_coordinates) < state.m_mesh2d->GetNumEdges()) + if (static_cast(geometryList.num_coordinates) < 4u * state.m_mesh2d->GetNumEdges()) { throw meshkernel::ConstraintError("GeometryList with wrong dimensions, {} must be greater than or equal to {}", geometryList.num_coordinates, Size(state, location)); diff --git a/libs/MeshKernelApi/tests/src/Mesh2DTests.cpp b/libs/MeshKernelApi/tests/src/Mesh2DTests.cpp index 75d183834..9e5391ac4 100644 --- a/libs/MeshKernelApi/tests/src/Mesh2DTests.cpp +++ b/libs/MeshKernelApi/tests/src/Mesh2DTests.cpp @@ -1825,3 +1825,57 @@ TEST(Mesh2DTests, Mesh2D_ShouldComputeNetlinkPolygon) errorCode = meshkernelapi::mkernel_mesh2d_get_property_dimension(meshkernelId, propertyId, nodesLocation, dimension); ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); } + +TEST(Mesh2DTests, Mesh2D_ShouldComputeFaceBounds) +{ + int meshkernelId = 0; + int errorCode = meshkernelapi::mkernel_allocate_state(0, meshkernelId); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + errorCode = GenerateUnstructuredMesh(meshkernelId); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + int dimension = -1; + int propertyId = -1; + errorCode = meshkernelapi::mkernel_mesh2d_get_face_bounds_property_type(propertyId); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + // Execute + int facesLocation = static_cast(meshkernel::Location::Faces); + errorCode = meshkernelapi::mkernel_mesh2d_get_property_dimension(meshkernelId, propertyId, facesLocation, dimension); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + ASSERT_EQ(dimension, 6 * 6); // 2x3 quadrilateral elements => 6 * 6 nodes (the maximum number of nodes an element can have is 6) + + std::vector xCoords(dimension); + std::vector yCoords(dimension); + + meshkernelapi::GeometryList polygons{}; + + polygons.num_coordinates = dimension; + polygons.coordinates_x = xCoords.data(); + polygons.coordinates_y = yCoords.data(); + + errorCode = meshkernelapi::mkernel_mesh2d_get_property(meshkernelId, propertyId, facesLocation, polygons); + ASSERT_EQ(meshkernel::ExitCode::Success, errorCode); + + std::vector expectedX{0.0, 1.0, 1.0, 0.0, -999.0, -999.0, 0.0, 1.0, 1.0, 0.0, -999.0, -999.0, 1.0, 2.0, 2.0, 1.0, -999.0, -999.0, 1.0, 2.0, 2.0, 1.0, -999.0, -999.0, 2.0, 3.0, 3.0, 2.0, -999.0, -999.0, 2.0, 3.0, 3.0, 2.0, -999.0, -999.0}; + std::vector expectedY{0.0, 0.0, 1.0, 1.0, -999.0, -999.0, 1.0, 1.0, 2.0, 2.0, -999.0, -999.0, 0.0, 0.0, 1.0, 1.0, -999.0, -999.0, 1.0, 1.0, 2.0, 2.0, -999.0, -999.0, 0.0, 0.0, 1.0, 1.0, -999.0, -999.0, 1.0, 1.0, 2.0, 2.0, -999.0, -999.0}; + + constexpr double tolerance = 1.0e-10; + + for (size_t i = 0; i < xCoords.size(); ++i) + { + EXPECT_NEAR(xCoords[i], expectedX[i], tolerance); + EXPECT_NEAR(yCoords[i], expectedY[i], tolerance); + } + + // Try to access the dimension for an invalid location id (faces in this case) for netlink contours, should return non-success error code + int edgesLocation = static_cast(meshkernel::Location::Edges); + errorCode = meshkernelapi::mkernel_mesh2d_get_property_dimension(meshkernelId, propertyId, edgesLocation, dimension); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); + + // Try to access the dimension for an invalid location id (node in this case) for netlink contours, should return non-success error code + int nodesLocation = static_cast(meshkernel::Location::Nodes); + errorCode = meshkernelapi::mkernel_mesh2d_get_property_dimension(meshkernelId, propertyId, nodesLocation, dimension); + ASSERT_EQ(meshkernel::ExitCode::MeshKernelErrorCode, errorCode); +}