Skip to content

Commit 06f338d

Browse files
authored
平面直角座標で記されたgmlファイルをインポートできる (#269)
* epsgによる判定 projectなしのconvert処理追加 * geo reference test * mesh extractor unf test * include fix * code fix * unit test code update * コメント削除 * mac build error fix * コメント修正 build error 修正 例外チェック * refactoring : isPolarCoordinate -> usePolarConversion * bool isPolarCoordinateSystem() に変更 * C# unit test fix * タブの修正 * ファイル名のパース処理変更
1 parent dfee250 commit 06f338d

13 files changed

Lines changed: 275 additions & 10 deletions

File tree

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<core:CityModel xmlns:brid="http://www.opengis.net/citygml/bridge/2.0" xmlns:wtr="http://www.opengis.net/citygml/waterbody/2.0" xmlns:tran="http://www.opengis.net/citygml/transportation/2.0" xmlns:gml="http://www.opengis.net/gml" xmlns:frn="http://www.opengis.net/citygml/cityfurniture/2.0" xmlns:veg="http://www.opengis.net/citygml/vegetation/2.0" xmlns:sch="http://www.ascc.net/xml/schematron" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:tun="http://www.opengis.net/citygml/tunnel/2.0" xmlns:tex="http://www.opengis.net/citygml/texturedsurface/2.0" xmlns:app="http://www.opengis.net/citygml/appearance/2.0" xmlns:gen="http://www.opengis.net/citygml/generics/2.0" xmlns:dem="http://www.opengis.net/citygml/relief/2.0" xmlns:luse="http://www.opengis.net/citygml/landuse/2.0" xmlns:uro="https://www.geospatial.jp/iur/uro/3.1" xmlns:xAL="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0" xmlns:bldg="http://www.opengis.net/citygml/building/2.0" xmlns:smil20="http://www.w3.org/2001/SMIL20/" xmlns:pbase="http://www.opengis.net/citygml/profiles/base/2.0" xmlns:smil20lang="http://www.w3.org/2001/SMIL20/Language" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:core="http://www.opengis.net/citygml/2.0" xmlns:grp="http://www.opengis.net/citygml/cityobjectgroup/2.0" xsi:schemaLocation="https://www.geospatial.jp/iur/urf/3.1 ../../schemas/iur/urf/3.1/urbanFunction.xsd https://www.geospatial.jp/iur/uro/3.1 ../../schemas/iur/uro/3.1/urbanObject.xsd http://www.opengis.net/citygml/2.0 http://schemas.opengis.net/citygml/2.0/cityGMLBase.xsd http://www.opengis.net/citygml/landuse/2.0 http://schemas.opengis.net/citygml/landuse/2.0/landUse.xsd http://www.opengis.net/citygml/building/2.0 http://schemas.opengis.net/citygml/building/2.0/building.xsd http://www.opengis.net/citygml/transportation/2.0 http://schemas.opengis.net/citygml/transportation/2.0/transportation.xsd http://www.opengis.net/citygml/generics/2.0 http://schemas.opengis.net/citygml/generics/2.0/generics.xsd http://www.opengis.net/citygml/relief/2.0 http://schemas.opengis.net/citygml/relief/2.0/relief.xsd http://www.opengis.net/citygml/cityobjectgroup/2.0 http://schemas.opengis.net/citygml/cityobjectgroup/2.0/cityObjectGroup.xsd http://www.opengis.net/gml http://schemas.opengis.net/gml/3.1.1/base/gml.xsd http://www.opengis.net/citygml/appearance/2.0 http://schemas.opengis.net/citygml/appearance/2.0/appearance.xsd">
3+
<gml:boundedBy>
4+
<gml:Envelope srsName="http://www.opengis.net/def/crs/EPSG/0/10169" srsDimension="3">
5+
<gml:lowerCorner>156561.10929971817 21007.437699998685 0</gml:lowerCorner>
6+
<gml:upperCorner>157517.0072997175 22010.374800001056 225.3813</gml:upperCorner>
7+
</gml:Envelope>
8+
</gml:boundedBy>
9+
10+
<core:cityObjectMember>
11+
<uro:Manhole gml:id="unf_f15bce0a-e6b5-41c2-a4ea-140f966686c3">
12+
<gml:name>雨水マンホール</gml:name>
13+
<core:creationDate>2025-03-21</core:creationDate>
14+
<frn:lod2Geometry>
15+
<gml:Solid srsName="http://www.opengis.net/def/crs/EPSG/0/10169" srsDimension="3">
16+
<gml:exterior>
17+
<gml:CompositeSurface>
18+
<gml:surfaceMember>
19+
<gml:Polygon gml:id="poly-7a90fd2f-c13c-4824-ab37-9cc81d19ad51">
20+
<gml:exterior>
21+
<gml:LinearRing>
22+
<gml:posList>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</gml:posList>
23+
</gml:LinearRing>
24+
</gml:exterior>
25+
</gml:Polygon>
26+
</gml:surfaceMember>
27+
<gml:surfaceMember>
28+
<gml:Polygon gml:id="poly-47a82d4d-d696-4dd1-b7b3-5cbe9336f141">
29+
<gml:exterior>
30+
<gml:LinearRing>
31+
<gml:posList>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</gml:posList>
32+
</gml:LinearRing>
33+
</gml:exterior>
34+
</gml:Polygon>
35+
</gml:surfaceMember>
36+
<gml:surfaceMember>
37+
<gml:Polygon gml:id="poly-6f56700e-61de-439a-aefa-c04c1eb4aaeb">
38+
<gml:exterior>
39+
<gml:LinearRing>
40+
<gml:posList>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</gml:posList>
41+
</gml:LinearRing>
42+
</gml:exterior>
43+
</gml:Polygon>
44+
</gml:surfaceMember>
45+
<gml:surfaceMember>
46+
<gml:Polygon gml:id="poly-4f0fbdad-b948-4122-95c2-b8f2656630c3">
47+
<gml:exterior>
48+
<gml:LinearRing>
49+
<gml:posList>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</gml:posList>
50+
</gml:LinearRing>
51+
</gml:exterior>
52+
</gml:Polygon>
53+
</gml:surfaceMember>
54+
<gml:surfaceMember>
55+
<gml:Polygon gml:id="poly-f2f88f9b-3671-4722-a933-bd81c52b94fa">
56+
<gml:exterior>
57+
<gml:LinearRing>
58+
<gml:posList>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</gml:posList>
59+
</gml:LinearRing>
60+
</gml:exterior>
61+
</gml:Polygon>
62+
</gml:surfaceMember>
63+
<gml:surfaceMember>
64+
<gml:Polygon gml:id="poly-e378d104-8bd1-41b0-aa2f-b62e9f63cf1e">
65+
<gml:exterior>
66+
<gml:LinearRing>
67+
<gml:posList>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</gml:posList>
68+
</gml:LinearRing>
69+
</gml:exterior>
70+
</gml:Polygon>
71+
</gml:surfaceMember>
72+
</gml:CompositeSurface>
73+
</gml:exterior>
74+
</gml:Solid>
75+
</frn:lod2Geometry>
76+
<uro:frnDataQualityAttribute>
77+
<uro:DataQualityAttribute>
78+
<uro:geometrySrcDescLod0 codeSpace="../../codelists/DataQualityAttribute_geometrySrcDesc.xml">500</uro:geometrySrcDescLod0>
79+
<uro:geometrySrcDescLod1 codeSpace="../../codelists/DataQualityAttribute_geometrySrcDesc.xml">999</uro:geometrySrcDescLod1>
80+
<uro:geometrySrcDescLod2 codeSpace="../../codelists/DataQualityAttribute_geometrySrcDesc.xml">500</uro:geometrySrcDescLod2>
81+
<uro:geometrySrcDescLod3 codeSpace="../../codelists/DataQualityAttribute_geometrySrcDesc.xml">999</uro:geometrySrcDescLod3>
82+
<uro:thematicSrcDesc codeSpace="../../codelists/DataQualityAttribute_thematicSrcDesc.xml">500</uro:thematicSrcDesc>
83+
</uro:DataQualityAttribute>
84+
</uro:frnDataQualityAttribute>
85+
<uro:year>2008</uro:year>
86+
</uro:Manhole>
87+
</core:cityObjectMember>
88+
</core:CityModel>

include/plateau/dataset/gml_file.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ namespace plateau::dataset {
2121
const std::string& getPath() const;
2222
void setPath(const std::string& path);
2323
MeshCode getMeshCode() const;
24+
double getEpsg() const;
25+
bool isPolarCoordinateSystem() const;
2426
const std::string& getFeatureType() const;
2527
PredefinedCityModelPackage getPackage() const;
2628
std::string getAppearanceDirectoryPath() const;
@@ -63,6 +65,7 @@ namespace plateau::dataset {
6365
std::string path_;
6466
std::string code_;
6567
std::string feature_type_;
68+
std::string epsg_;
6669
bool is_valid_;
6770
bool is_local_;
6871
int max_lod_;

include/plateau/geometry/geo_coordinate.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,7 @@ namespace plateau::geometry {
1616
* EPSGコードが 6697 のとき、それは
1717
* 「日本測地系2011における経緯度座標系と東京湾平均海面を基準とする標高の複合座標参照系」
1818
* になります。
19-
* TODO
20-
* EPSGコードの判別と、それによって処理を変える機能は未実装です。
19+
* EPSGコードが 10162 ~ 10174 の場合は平面直角座標系となります。
2120
*/
2221
struct GeoCoordinate {
2322
double latitude;
@@ -95,8 +94,8 @@ namespace plateau::geometry {
9594

9695
static Extent all() {
9796
return {
98-
GeoCoordinate(-90, -180, -9999),
99-
GeoCoordinate(90, 180, 9999)
97+
GeoCoordinate(-9999999, -9999999, -9999),
98+
GeoCoordinate(9999999, 9999999, 9999)
10099
};
101100
}
102101
};

include/plateau/geometry/geo_reference.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,15 @@ namespace plateau::geometry {
2020
/**
2121
* 緯度・経度・高さで表現される座標を平面直角座標系に変換します。
2222
* 座標軸変換を含みます。
23-
*/
23+
*/
2424
TVec3d project(const GeoCoordinate& point) const;
2525
TVec3d project(const TVec3d& lat_lon) const;
2626
/// project の座標軸変換をしない版です。座標軸は ENU → ENU であるとします。 reference_point_ は ENUに変換されます。
2727
TVec3d projectWithoutAxisConvert(const TVec3d& lat_lon) const;
2828
TVec3d convertAxisToENU(const TVec3d& vertex) const;
29+
TVec3d convert(const TVec3d& lat_lon, const bool convert_axis = true, const bool project = true) const;
2930
static TVec3d convertAxisFromENUTo(CoordinateSystem axis, const TVec3d& vertex);
30-
static TVec3d convertAxisToENU(CoordinateSystem axis, const TVec3d& vertex);
31+
static TVec3d convertAxisToENU(CoordinateSystem axis, const TVec3d& vertex);
3132

3233
GeoCoordinate unproject(const TVec3d& point) const;
3334

src/dataset/gml_file.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,25 @@ namespace plateau::dataset {
5151
return MeshCode(code_);
5252
}
5353

54+
double GmlFile::getEpsg() const {
55+
try {
56+
return epsg_.empty() ? 6697 : std::stod(epsg_);
57+
}
58+
catch (const std::exception&) {
59+
return 6697;
60+
}
61+
}
62+
63+
bool GmlFile::isPolarCoordinateSystem() const {
64+
double epsg = getEpsg();
65+
// 平面直角座標系の区分についてはこちらを参照してください :
66+
// https://www.mlit.go.jp/plateaudocument/toc9/toc9_08/toc9_08_04/
67+
if (epsg >= 10162 && epsg <= 10174) {
68+
return false;
69+
}
70+
return true;
71+
}
72+
5473
const std::string& GmlFile::getFeatureType() const {
5574
return feature_type_;
5675
}
@@ -113,6 +132,7 @@ namespace plateau::dataset {
113132
try {
114133
code_ = filename_parts.empty() ? "" : filename_parts.at(0);
115134
feature_type_ = filename_parts.size() <= 1 ? "" : filename_parts.at(1);
135+
epsg_ = filename_parts.size() <= 2 ? "" : filename_parts.at(2);
116136
is_valid_ = true;
117137
}
118138
catch (...) {

src/geometry/geo_reference.cpp

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,23 @@ namespace plateau::geometry {
2424
return converted_point;
2525
}
2626

27+
TVec3d GeoReference::convert(const TVec3d& lat_lon, const bool convert_axis, const bool project) const {
28+
//平面直角座標変換、座標軸変換をフラグに応じてスキップします。
29+
TVec3d point = lat_lon;
30+
// 平面直角座標系に変換
31+
if (project)
32+
PolarToPlaneCartesian().project(point, zone_id_);
33+
if (!convert_axis) {
34+
// 座標軸変換をしない場合
35+
TVec3 converted_point = point / unit_scale_ - convertAxisToENU(coordinate_system_, reference_point_);
36+
return converted_point;
37+
}
38+
// 座標軸変換をする場合
39+
TVec3 converted_point = convertAxisFromENUTo(coordinate_system_, point);
40+
converted_point = converted_point / unit_scale_ - reference_point_;
41+
return converted_point;
42+
}
43+
2744
TVec3d GeoReference::convertAxisToENU(const TVec3d& vertex) const {
2845
return convertAxisToENU(getCoordinateSystem(), vertex);
2946
}

src/polygon_mesh/mesh_factory.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
#include "plateau/polygon_mesh/mesh_merger.h"
1111
#include "plateau/polygon_mesh/mesh_extractor.h"
12+
#include "plateau/dataset/gml_file.h"
1213

1314

1415
namespace plateau::polygonMesh {
@@ -33,6 +34,8 @@ namespace plateau::polygonMesh {
3334
const Polygon& polygon, const std::string& gml_path,
3435
const GeoReference& geo_reference, Mesh& out_mesh) {
3536

37+
const auto& gml = plateau::dataset::GmlFile(gml_path);
38+
3639
// マージ対象の情報を取得します。ここでの頂点は極座標です。
3740
const auto& vertices_lat_lon = polygon.getVertices();
3841
const auto& in_indices = polygon.getIndices();
@@ -53,7 +56,7 @@ namespace plateau::polygonMesh {
5356
auto& out_vertices = out_mesh.getVertices();
5457
out_vertices.reserve(vertices_lat_lon.size());
5558
for (const auto& lat_lon : vertices_lat_lon) {
56-
auto xyz = geo_reference.projectWithoutAxisConvert(lat_lon);
59+
auto xyz = geo_reference.convert(lat_lon, false, gml.isPolarCoordinateSystem());
5760
out_vertices.push_back(xyz);
5861
}
5962
assert(out_vertices.size() == vertices_lat_lon.size());

src/polygon_mesh/polygon_mesh_utils.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#include "plateau/polygon_mesh/mesh.h"
33
#include "plateau/geometry/geo_reference.h"
44
#include "citygml/citymodel.h"
5+
#include <plateau/dataset/gml_file.h>
56

67
namespace plateau::polygonMesh {
78
using namespace citygml;
@@ -59,8 +60,9 @@ namespace plateau::polygonMesh {
5960
if (!envelope.validBounds()) {
6061
return TVec3d{0, 0, 0};
6162
}
63+
const auto& gml = plateau::dataset::GmlFile(city_model.getGmlPath());
6264
auto city_center = (envelope.getLowerBound() + envelope.getUpperBound()) / 2.0;
63-
return geometry::GeoReference(coordinate_zone_id).project(city_center);
65+
return geometry::GeoReference(coordinate_zone_id).convert(city_center, true, gml.isPolarCoordinateSystem());
6466
}
6567

6668
/**

test/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,9 @@ add_executable(plateau_test
4848
"test_texture_image_base.cpp"
4949
"test_map_zoom_level_searcher.cpp"
5050
"test_height_map_aligner.cpp"
51-
"test_heightmap_mesh_generator.cpp")
51+
"test_heightmap_mesh_generator.cpp"
52+
"test_geo_reference.cpp"
53+
)
5254

5355
add_subdirectory("${CMAKE_CURRENT_SOURCE_DIR}/test_granularity_convert")
5456

test/test_geo_reference.cpp

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#include <gtest/gtest.h>
2+
#include <plateau/geometry/geo_coordinate.h>
3+
#include <plateau/geometry/geo_reference.h>
4+
#include "../src/geometry/polar_to_plane_cartesian.cpp"
5+
6+
namespace plateau::geometry {
7+
class GeoReferenceTest : public ::testing::Test {
8+
protected:
9+
void SetUp() override {
10+
}
11+
void TearDown() override {
12+
}
13+
14+
// テスト設定
15+
int zone_id = 9;
16+
TVec3d ref_point = TVec3d(0, 0, 0);
17+
float unit_scale = 1.0;
18+
CoordinateSystem coordinate = CoordinateSystem::EUN;
19+
GeoReference ref = GeoReference(zone_id, ref_point, unit_scale, coordinate);
20+
TVec3d base_point = TVec3d(100, 100, 0);
21+
};
22+
23+
TEST_F(GeoReferenceTest, ConvertAxisProject) { // NOLINT
24+
// 平面直角座標変換・座標軸変換を行う
25+
TVec3d converted = ref.convert(base_point, true, true);
26+
TVec3d projected = ref.project(base_point);
27+
28+
// Expected
29+
TVec3d position = base_point;
30+
PolarToPlaneCartesian().project(position, zone_id);
31+
TVec3 expected_point = GeoReference::convertAxisFromENUTo(coordinate, position);
32+
expected_point = expected_point / unit_scale - ref_point;
33+
34+
ASSERT_EQ(expected_point, converted);
35+
ASSERT_EQ(expected_point, projected);
36+
}
37+
38+
TEST_F(GeoReferenceTest, ConvertProjectOnly) { // NOLINT
39+
// 平面直角座標変換を行う・座標軸変換を行わない
40+
TVec3d converted = ref.convert(base_point, false, true);
41+
TVec3d projected = ref.projectWithoutAxisConvert(base_point);
42+
43+
// Expected
44+
TVec3d position = base_point;
45+
PolarToPlaneCartesian().project(position, zone_id);
46+
TVec3 expected_point = position / unit_scale - GeoReference::convertAxisToENU(coordinate, ref_point);
47+
48+
ASSERT_EQ(expected_point, converted);
49+
ASSERT_EQ(expected_point, projected);
50+
}
51+
52+
TEST_F(GeoReferenceTest, ConvertAxisOnly) { // NOLINT
53+
// 平面直角座標変換を行わない・座標軸変換を行う
54+
TVec3d point = ref.convert(base_point, true, false);
55+
56+
// Expected
57+
TVec3 expected_point = GeoReference::convertAxisFromENUTo(coordinate, base_point);
58+
expected_point = expected_point / unit_scale - ref_point;
59+
60+
ASSERT_EQ(expected_point, point);
61+
}
62+
63+
TEST_F(GeoReferenceTest, ConvertOnly) { // NOLINT
64+
// 平面直角座標変換・座標軸変換を行わない
65+
TVec3d point = ref.convert(base_point, false, false);
66+
67+
// Expected
68+
TVec3 expected_point = base_point / unit_scale - GeoReference::convertAxisToENU(coordinate, ref_point);
69+
70+
ASSERT_EQ(expected_point, point);
71+
}
72+
73+
// fetch のテストは test_dataset.cpp にあります。
74+
75+
}

0 commit comments

Comments
 (0)