diff --git "a/data/\346\227\245\346\234\254\350\252\236\343\203\221\343\202\271\343\203\206\343\202\271\343\203\210/udx/unf/08EE763_unf_10169_sewer_op.gml" "b/data/\346\227\245\346\234\254\350\252\236\343\203\221\343\202\271\343\203\206\343\202\271\343\203\210/udx/unf/08EE763_unf_10169_sewer_op.gml" new file mode 100644 index 00000000..36783690 --- /dev/null +++ "b/data/\346\227\245\346\234\254\350\252\236\343\203\221\343\202\271\343\203\206\343\202\271\343\203\210/udx/unf/08EE763_unf_10169_sewer_op.gml" @@ -0,0 +1,88 @@ + + + + + 156561.10929971817 21007.437699998685 0 + 157517.0072997175 22010.374800001056 225.3813 + + + + + + 雨水マンホール + 2025-03-21 + + + + + + + + + 157110.9434 21547.8443 136.489 157111.4651 21547.5479 136.489 157111.4651 21547.5479 135.139 157110.9434 21547.8443 135.139 157110.9434 21547.8443 136.489 + + + + + + + + + 157111.7615 21548.0695 135.139 157111.4651 21547.5479 135.139 157111.4651 21547.5479 136.489 157111.7615 21548.0695 136.489 157111.7615 21548.0695 135.139 + + + + + + + + + 157111.7615 21548.0695 136.489 157111.4651 21547.5479 136.489 157110.9434 21547.8443 136.489 157111.2398 21548.366 136.489 157111.7615 21548.0695 136.489 + + + + + + + + + 157110.9434 21547.8443 135.139 157111.4651 21547.5479 135.139 157111.7615 21548.0695 135.139 157111.2398 21548.366 135.139 157110.9434 21547.8443 135.139 + + + + + + + + + 157111.2398 21548.366 135.139 157111.7615 21548.0695 135.139 157111.7615 21548.0695 136.489 157111.2398 21548.366 136.489 157111.2398 21548.366 135.139 + + + + + + + + + 157110.9434 21547.8443 135.139 157111.2398 21548.366 135.139 157111.2398 21548.366 136.489 157110.9434 21547.8443 136.489 157110.9434 21547.8443 135.139 + + + + + + + + + + + 500 + 999 + 500 + 999 + 500 + + + 2008 + + + diff --git a/include/plateau/dataset/gml_file.h b/include/plateau/dataset/gml_file.h index dbf83846..e9954d30 100644 --- a/include/plateau/dataset/gml_file.h +++ b/include/plateau/dataset/gml_file.h @@ -21,6 +21,8 @@ namespace plateau::dataset { const std::string& getPath() const; void setPath(const std::string& path); MeshCode getMeshCode() const; + double getEpsg() const; + bool isPolarCoordinateSystem() const; const std::string& getFeatureType() const; PredefinedCityModelPackage getPackage() const; std::string getAppearanceDirectoryPath() const; @@ -63,6 +65,7 @@ namespace plateau::dataset { std::string path_; std::string code_; std::string feature_type_; + std::string epsg_; bool is_valid_; bool is_local_; int max_lod_; diff --git a/include/plateau/geometry/geo_coordinate.h b/include/plateau/geometry/geo_coordinate.h index 9d0156fe..1dc4cf7b 100644 --- a/include/plateau/geometry/geo_coordinate.h +++ b/include/plateau/geometry/geo_coordinate.h @@ -16,8 +16,7 @@ namespace plateau::geometry { * EPSGコードが 6697 のとき、それは * 「日本測地系2011における経緯度座標系と東京湾平均海面を基準とする標高の複合座標参照系」 * になります。 - * TODO - * EPSGコードの判別と、それによって処理を変える機能は未実装です。 + * EPSGコードが 10162 ~ 10174 の場合は平面直角座標系となります。 */ struct GeoCoordinate { double latitude; @@ -95,8 +94,8 @@ namespace plateau::geometry { static Extent all() { return { - GeoCoordinate(-90, -180, -9999), - GeoCoordinate(90, 180, 9999) + GeoCoordinate(-9999999, -9999999, -9999), + GeoCoordinate(9999999, 9999999, 9999) }; } }; diff --git a/include/plateau/geometry/geo_reference.h b/include/plateau/geometry/geo_reference.h index fceaab06..52ca6680 100644 --- a/include/plateau/geometry/geo_reference.h +++ b/include/plateau/geometry/geo_reference.h @@ -20,14 +20,15 @@ namespace plateau::geometry { /** * 緯度・経度・高さで表現される座標を平面直角座標系に変換します。 * 座標軸変換を含みます。 - */ + */ TVec3d project(const GeoCoordinate& point) const; TVec3d project(const TVec3d& lat_lon) const; /// project の座標軸変換をしない版です。座標軸は ENU → ENU であるとします。 reference_point_ は ENUに変換されます。 TVec3d projectWithoutAxisConvert(const TVec3d& lat_lon) const; TVec3d convertAxisToENU(const TVec3d& vertex) const; + TVec3d convert(const TVec3d& lat_lon, const bool convert_axis = true, const bool project = true) const; static TVec3d convertAxisFromENUTo(CoordinateSystem axis, const TVec3d& vertex); - static TVec3d convertAxisToENU(CoordinateSystem axis, const TVec3d& vertex); + static TVec3d convertAxisToENU(CoordinateSystem axis, const TVec3d& vertex); GeoCoordinate unproject(const TVec3d& point) const; diff --git a/src/dataset/gml_file.cpp b/src/dataset/gml_file.cpp index 26c238b7..5549fa00 100644 --- a/src/dataset/gml_file.cpp +++ b/src/dataset/gml_file.cpp @@ -51,6 +51,25 @@ namespace plateau::dataset { return MeshCode(code_); } + double GmlFile::getEpsg() const { + try { + return epsg_.empty() ? 6697 : std::stod(epsg_); + } + catch (const std::exception&) { + return 6697; + } + } + + bool GmlFile::isPolarCoordinateSystem() const { + double epsg = getEpsg(); + // 平面直角座標系の区分についてはこちらを参照してください : + // https://www.mlit.go.jp/plateaudocument/toc9/toc9_08/toc9_08_04/ + if (epsg >= 10162 && epsg <= 10174) { + return false; + } + return true; + } + const std::string& GmlFile::getFeatureType() const { return feature_type_; } @@ -113,6 +132,7 @@ namespace plateau::dataset { try { code_ = filename_parts.empty() ? "" : filename_parts.at(0); feature_type_ = filename_parts.size() <= 1 ? "" : filename_parts.at(1); + epsg_ = filename_parts.size() <= 2 ? "" : filename_parts.at(2); is_valid_ = true; } catch (...) { diff --git a/src/geometry/geo_reference.cpp b/src/geometry/geo_reference.cpp index 6bdfe5b5..584c32f8 100644 --- a/src/geometry/geo_reference.cpp +++ b/src/geometry/geo_reference.cpp @@ -24,6 +24,23 @@ namespace plateau::geometry { return converted_point; } + TVec3d GeoReference::convert(const TVec3d& lat_lon, const bool convert_axis, const bool project) const { + //平面直角座標変換、座標軸変換をフラグに応じてスキップします。 + TVec3d point = lat_lon; + // 平面直角座標系に変換 + if (project) + PolarToPlaneCartesian().project(point, zone_id_); + if (!convert_axis) { + // 座標軸変換をしない場合 + TVec3 converted_point = point / unit_scale_ - convertAxisToENU(coordinate_system_, reference_point_); + return converted_point; + } + // 座標軸変換をする場合 + TVec3 converted_point = convertAxisFromENUTo(coordinate_system_, point); + converted_point = converted_point / unit_scale_ - reference_point_; + return converted_point; + } + TVec3d GeoReference::convertAxisToENU(const TVec3d& vertex) const { return convertAxisToENU(getCoordinateSystem(), vertex); } diff --git a/src/polygon_mesh/mesh_factory.cpp b/src/polygon_mesh/mesh_factory.cpp index 93616ee2..f03579fd 100644 --- a/src/polygon_mesh/mesh_factory.cpp +++ b/src/polygon_mesh/mesh_factory.cpp @@ -9,6 +9,7 @@ #include "plateau/polygon_mesh/mesh_merger.h" #include "plateau/polygon_mesh/mesh_extractor.h" +#include "plateau/dataset/gml_file.h" namespace plateau::polygonMesh { @@ -33,6 +34,8 @@ namespace plateau::polygonMesh { const Polygon& polygon, const std::string& gml_path, const GeoReference& geo_reference, Mesh& out_mesh) { + const auto& gml = plateau::dataset::GmlFile(gml_path); + // マージ対象の情報を取得します。ここでの頂点は極座標です。 const auto& vertices_lat_lon = polygon.getVertices(); const auto& in_indices = polygon.getIndices(); @@ -53,7 +56,7 @@ namespace plateau::polygonMesh { auto& out_vertices = out_mesh.getVertices(); out_vertices.reserve(vertices_lat_lon.size()); for (const auto& lat_lon : vertices_lat_lon) { - auto xyz = geo_reference.projectWithoutAxisConvert(lat_lon); + auto xyz = geo_reference.convert(lat_lon, false, gml.isPolarCoordinateSystem()); out_vertices.push_back(xyz); } assert(out_vertices.size() == vertices_lat_lon.size()); diff --git a/src/polygon_mesh/polygon_mesh_utils.cpp b/src/polygon_mesh/polygon_mesh_utils.cpp index 230ec302..4ff9f853 100644 --- a/src/polygon_mesh/polygon_mesh_utils.cpp +++ b/src/polygon_mesh/polygon_mesh_utils.cpp @@ -2,6 +2,7 @@ #include "plateau/polygon_mesh/mesh.h" #include "plateau/geometry/geo_reference.h" #include "citygml/citymodel.h" +#include namespace plateau::polygonMesh { using namespace citygml; @@ -59,8 +60,9 @@ namespace plateau::polygonMesh { if (!envelope.validBounds()) { return TVec3d{0, 0, 0}; } + const auto& gml = plateau::dataset::GmlFile(city_model.getGmlPath()); auto city_center = (envelope.getLowerBound() + envelope.getUpperBound()) / 2.0; - return geometry::GeoReference(coordinate_zone_id).project(city_center); + return geometry::GeoReference(coordinate_zone_id).convert(city_center, true, gml.isPolarCoordinateSystem()); } /** diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 3efef245..2b7eee80 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -48,7 +48,9 @@ add_executable(plateau_test "test_texture_image_base.cpp" "test_map_zoom_level_searcher.cpp" "test_height_map_aligner.cpp" - "test_heightmap_mesh_generator.cpp") + "test_heightmap_mesh_generator.cpp" + "test_geo_reference.cpp" + ) add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/test_granularity_convert") diff --git a/test/test_geo_reference.cpp b/test/test_geo_reference.cpp new file mode 100644 index 00000000..3b1c81b6 --- /dev/null +++ b/test/test_geo_reference.cpp @@ -0,0 +1,75 @@ +#include +#include +#include +#include "../src/geometry/polar_to_plane_cartesian.cpp" + +namespace plateau::geometry { + class GeoReferenceTest : public ::testing::Test { + protected: + void SetUp() override { + } + void TearDown() override { + } + + // テスト設定 + int zone_id = 9; + TVec3d ref_point = TVec3d(0, 0, 0); + float unit_scale = 1.0; + CoordinateSystem coordinate = CoordinateSystem::EUN; + GeoReference ref = GeoReference(zone_id, ref_point, unit_scale, coordinate); + TVec3d base_point = TVec3d(100, 100, 0); + }; + + TEST_F(GeoReferenceTest, ConvertAxisProject) { // NOLINT + // 平面直角座標変換・座標軸変換を行う + TVec3d converted = ref.convert(base_point, true, true); + TVec3d projected = ref.project(base_point); + + // Expected + TVec3d position = base_point; + PolarToPlaneCartesian().project(position, zone_id); + TVec3 expected_point = GeoReference::convertAxisFromENUTo(coordinate, position); + expected_point = expected_point / unit_scale - ref_point; + + ASSERT_EQ(expected_point, converted); + ASSERT_EQ(expected_point, projected); + } + + TEST_F(GeoReferenceTest, ConvertProjectOnly) { // NOLINT + // 平面直角座標変換を行う・座標軸変換を行わない + TVec3d converted = ref.convert(base_point, false, true); + TVec3d projected = ref.projectWithoutAxisConvert(base_point); + + // Expected + TVec3d position = base_point; + PolarToPlaneCartesian().project(position, zone_id); + TVec3 expected_point = position / unit_scale - GeoReference::convertAxisToENU(coordinate, ref_point); + + ASSERT_EQ(expected_point, converted); + ASSERT_EQ(expected_point, projected); + } + + TEST_F(GeoReferenceTest, ConvertAxisOnly) { // NOLINT + // 平面直角座標変換を行わない・座標軸変換を行う + TVec3d point = ref.convert(base_point, true, false); + + // Expected + TVec3 expected_point = GeoReference::convertAxisFromENUTo(coordinate, base_point); + expected_point = expected_point / unit_scale - ref_point; + + ASSERT_EQ(expected_point, point); + } + + TEST_F(GeoReferenceTest, ConvertOnly) { // NOLINT + // 平面直角座標変換・座標軸変換を行わない + TVec3d point = ref.convert(base_point, false, false); + + // Expected + TVec3 expected_point = base_point / unit_scale - GeoReference::convertAxisToENU(coordinate, ref_point); + + ASSERT_EQ(expected_point, point); + } + + // fetch のテストは test_dataset.cpp にあります。 + +} diff --git a/test/test_gml_file.cpp b/test/test_gml_file.cpp index c31bef27..1acb51a1 100644 --- a/test/test_gml_file.cpp +++ b/test/test_gml_file.cpp @@ -13,6 +13,16 @@ namespace plateau::dataset { ASSERT_EQ(PredefinedCityModelPackage::Building, UdxSubFolder::getPackage("bldg")); } + TEST_F(GmlFileTest, get_epsg) { // NOLINT + auto info1 = GmlFile(std::string("foobar/udx/unf/08EE751_unf_10169_water_op.gml")); + ASSERT_EQ(10169, info1.getEpsg()); + ASSERT_FALSE(info1.isPolarCoordinateSystem()); + + auto info2 = GmlFile(std::string("foobar/udx/bldg/53392546_bldg_6697_2_op.gml")); + ASSERT_EQ(6697, info2.getEpsg()); + ASSERT_TRUE(info2.isPolarCoordinateSystem()); + } + // fetch のテストは test_dataset.cpp にあります。 } diff --git a/test/test_mesh_extractor.cpp b/test/test_mesh_extractor.cpp index e0c05d6f..2dad32aa 100644 --- a/test/test_mesh_extractor.cpp +++ b/test/test_mesh_extractor.cpp @@ -8,6 +8,7 @@ #include "../src/polygon_mesh/area_mesh_factory.h" #include #include +#include using namespace citygml; using namespace plateau::geometry; @@ -204,6 +205,50 @@ namespace plateau::polygonMesh { } } + // Epsgが6697以外(10162~10174)の場合、平面直角座標変換を行わない + TEST_F(MeshExtractorTest, no_coordinate_transformation_during_conversion) { // NOLINT + + const std::string gml_path = u8"../data/日本語パステスト/udx/unf/08EE763_unf_10169_sewer_op.gml"; + ParserParams params; + params.tesselate = true; + MeshExtractOptions mesh_extract_options = MeshExtractOptions(); + mesh_extract_options.min_lod = 0; + mesh_extract_options.max_lod = 2; + mesh_extract_options.mesh_granularity = MeshGranularity::PerPrimaryFeatureObject; + mesh_extract_options.grid_count_of_side = 5; + mesh_extract_options.exclude_city_object_outside_extent = true; + mesh_extract_options.exclude_polygons_outside_extent = false; + mesh_extract_options.coordinate_zone_id = 8; + mesh_extract_options.unit_scale = 1.0; + mesh_extract_options.mesh_axes = CoordinateSystem::ENU; + const std::shared_ptr city_model = load(gml_path, params); + + const auto& gml = plateau::dataset::GmlFile((city_model->getGmlPath())); + ASSERT_FALSE(gml.isPolarCoordinateSystem()); + + auto model = MeshExtractor::extract(*city_model, mesh_extract_options); + const auto& lod_node = model->getRootNodeAt(0); + const auto& first_model_node = lod_node.getChildAt(0); + const auto& mesh = first_model_node.getMesh(); + + ASSERT_TRUE(mesh->hasVertices()); + const auto& vertices = mesh->getVertices(); + ASSERT_TRUE(vertices.size() > 0); + + // CityModel Vertices + const auto& root_city_object = city_model->getRootCityObject(0); + const auto& root_geometry = root_city_object.getGeometry(0).getGeometry(0); + const auto& city_model_polygon = root_geometry.getPolygon(0); + const auto& city_model_vertices = city_model_polygon->getVertices(); + + // 変換されないのでscale:1でENUであればCityModelと座標が同じになる + for (int i = 0; i < city_model_vertices.size(); i++) { + const auto& city_model_vertex = city_model_vertices[i]; + const auto& vertex = vertices[i]; + ASSERT_EQ(vertex, city_model_vertex); + } + } + void MeshExtractorTest::testExtractFromCWrapper() const { const CityModelHandle* city_model_handle; diff --git a/wrappers/csharp/LibPLATEAU.NET/CSharpPLATEAU.Test/Dataset/DatasetAccessorTest.cs b/wrappers/csharp/LibPLATEAU.NET/CSharpPLATEAU.Test/Dataset/DatasetAccessorTest.cs index 9523495b..4a429e2f 100644 --- a/wrappers/csharp/LibPLATEAU.NET/CSharpPLATEAU.Test/Dataset/DatasetAccessorTest.cs +++ b/wrappers/csharp/LibPLATEAU.NET/CSharpPLATEAU.Test/Dataset/DatasetAccessorTest.cs @@ -66,7 +66,7 @@ public void GetPackagesLocal() using var datasetSource = DatasetSource.Create(new DatasetSourceConfigLocal(TestDataPathLocal)); using var accessor = datasetSource.Accessor; Console.WriteLine(Path.GetFullPath(accessor.GetGmlFiles(PredefinedCityModelPackage.Building).At(0).Path)); - var expected = PredefinedCityModelPackage.Building | PredefinedCityModelPackage.Road; + var expected = PredefinedCityModelPackage.Building | PredefinedCityModelPackage.Road | PredefinedCityModelPackage.UndergroundFacility; Assert.AreEqual(expected, accessor.Packages); }