|
1 | 1 | #include "plateau/dataset/standard_map_grid.h" |
| 2 | +#include <algorithm> |
2 | 3 | #include "plateau/geometry/geo_coordinate.h" |
3 | 4 | #include <stdexcept> |
4 | 5 | #include <utility> |
| 6 | +#include <plateau/geometry/geo_reference.h> |
| 7 | +#include <plateau/polygon_mesh/mesh_extract_options.h> |
| 8 | +#include <iostream> |
5 | 9 |
|
6 | 10 | namespace plateau::dataset { |
7 | 11 |
|
8 | | - StandardMapGrid::StandardMapGrid(std::string code) : code_(std::move(code)), is_valid_(false) { |
9 | | - // コードの形式を検証 |
10 | | - // TODO |
11 | | - is_valid_ = true; |
| 12 | + namespace { |
| 13 | + constexpr int level5000_division_count = 10; |
| 14 | + constexpr int level2500_division_count = 2; |
| 15 | + constexpr int level1000_division_count = 5; |
| 16 | + constexpr int level500_division_count = 10; |
| 17 | + |
| 18 | + // 1セルのサイズ |
| 19 | + constexpr double level50000_cell_column = 30000.0; // 南北30km |
| 20 | + constexpr double level50000_cell_row = 40000.0; // 東西40km |
| 21 | + |
| 22 | + constexpr double level5000_cell_column = level50000_cell_column / level5000_division_count; // 南北4km |
| 23 | + constexpr double level5000_cell_row = level50000_cell_row / level5000_division_count; // 東西3km |
| 24 | + |
| 25 | + constexpr double level2500_cell_column = level5000_cell_column / level2500_division_count; // 南北2km |
| 26 | + constexpr double level2500_cell_row = level5000_cell_row / level2500_division_count; // 東西1.5km |
| 27 | + |
| 28 | + constexpr double level1000_cell_column = level5000_cell_column / level1000_division_count; // 南北800m |
| 29 | + constexpr double level1000_cell_row = level5000_cell_row / level1000_division_count; // 東西600m |
| 30 | + |
| 31 | + constexpr double level500_cell_column = level5000_cell_column / level500_division_count; // 南北400m |
| 32 | + constexpr double level500_cell_row = level5000_cell_row / level500_division_count; // 東西300m |
| 33 | + |
| 34 | + /** |
| 35 | + * 図郭コードの文字列からレベル(詳細度)を返します。 |
| 36 | + */ |
| 37 | + StandardMapGridLevel parseLevel(const std::string& code) { |
| 38 | + if (code.size() == 4) { |
| 39 | + return StandardMapGridLevel::Level50000; |
| 40 | + } |
| 41 | + if (code.size() == 6) { |
| 42 | + return StandardMapGridLevel::Level5000; |
| 43 | + } |
| 44 | + if (code.size() == 7) { |
| 45 | + return StandardMapGridLevel::Level2500; |
| 46 | + } |
| 47 | + if (code.size() == 8) { |
| 48 | + // 末尾がアルファベットであれば1000 |
| 49 | + if (std::isalpha(code.back())) { |
| 50 | + return StandardMapGridLevel::Level1000; |
| 51 | + } |
| 52 | + // 末尾が数字であれば500 |
| 53 | + else { |
| 54 | + return StandardMapGridLevel::Level500; |
| 55 | + } |
| 56 | + } |
| 57 | + |
| 58 | + // サポート対象外 |
| 59 | + return StandardMapGridLevel::Invalid; |
| 60 | + } |
| 61 | + |
| 62 | + /** |
| 63 | + * 計算された平面直角座標をTVec3dのペアに変換します。 |
| 64 | + */ |
| 65 | + std::pair<TVec3d, TVec3d> createExtentPair( |
| 66 | + double min_column, double min_row, |
| 67 | + double max_column, double max_row) { |
| 68 | + // column: 南北方向(緯度):Y軸 |
| 69 | + // row: 東西方向(経度):X軸 |
| 70 | + return {TVec3d(min_row, 0, min_column), TVec3d(max_row, 0, max_column)}; |
| 71 | + } |
| 72 | + |
| 73 | + void calculateSubGridExtent( |
| 74 | + double& min_column, double& min_row, |
| 75 | + double& max_column, double& max_row, |
| 76 | + int column_index, int row_index, |
| 77 | + double cell_column, double cell_row) { |
| 78 | + |
| 79 | + // column座標(南北)の計算 |
| 80 | + min_column = min_column + (column_index * cell_column); |
| 81 | + max_column = min_column + cell_column; |
| 82 | + |
| 83 | + // row座標(東西)の計算 |
| 84 | + min_row = min_row + (row_index * cell_row); |
| 85 | + max_row = min_row + cell_row; |
| 86 | + } |
| 87 | + } |
| 88 | + |
| 89 | + StandardMapGrid::StandardMapGrid(std::string code) : code_(std::move(code)) { |
| 90 | + try { |
| 91 | + is_valid_ = true; |
| 92 | + |
| 93 | + // 図郭コードの文字列が数字とアルファベットからなることをチェックします。 |
| 94 | + if (!std::all_of(code_.begin(), code_.end(), [](char c) |
| 95 | + { |
| 96 | + return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z'); |
| 97 | + })) { |
| 98 | + is_valid_ = false; |
| 99 | + return; |
| 100 | + } |
| 101 | + |
| 102 | + // 図郭コードのレベル(詳細度)をチェックします。 |
| 103 | + level_ = parseLevel(code_); |
| 104 | + if (level_ == StandardMapGridLevel::Invalid) { |
| 105 | + is_valid_ = false; |
| 106 | + return; |
| 107 | + } |
| 108 | + |
| 109 | + // 原点設定 |
| 110 | + coordinate_origin_ = std::stoi(code_.substr(0, 2)); |
| 111 | + |
| 112 | + // 図郭コードのcolumn, row座標を取得します。 |
| 113 | + first_column_ = code_[2]; // 1文字目はcolumn座標(南北) |
| 114 | + first_row_ = code_[3]; // 2文字目はrow座標(東西) |
| 115 | + |
| 116 | + // 文字の範囲チェック |
| 117 | + if (first_column_ < 'A' || first_column_ > 'T' || |
| 118 | + first_row_ < 'A' || first_row_ > 'H') { |
| 119 | + is_valid_ = false; |
| 120 | + return; |
| 121 | + } |
| 122 | + |
| 123 | + if (level_ == StandardMapGridLevel::Level50000) { |
| 124 | + return; |
| 125 | + } |
| 126 | + |
| 127 | + // 基点は左下(90) |
| 128 | + second_column_ = 9 - std::stoi(code_.substr(4, 1)); |
| 129 | + second_row_ = std::stoi(code_.substr(5, 1)); |
| 130 | + |
| 131 | + if (level_ == StandardMapGridLevel::Level5000) { |
| 132 | + return; |
| 133 | + } |
| 134 | + |
| 135 | + if (level_ == StandardMapGridLevel::Level2500) { |
| 136 | + // 基点は左下 |
| 137 | + int third = std::stoi(code_.substr(6, 1)); // 1-4 |
| 138 | + switch (third) { |
| 139 | + case 1: // 左上 |
| 140 | + third_column_ = 1; |
| 141 | + third_row_ = 0; |
| 142 | + break; |
| 143 | + case 2: // 右上 |
| 144 | + third_column_ = 1; |
| 145 | + third_row_ = 1; |
| 146 | + break; |
| 147 | + case 3: // 左下 |
| 148 | + third_column_ = 0; |
| 149 | + third_row_ = 0; |
| 150 | + break; |
| 151 | + case 4: // 右下 |
| 152 | + third_column_ = 0; |
| 153 | + third_row_ = 1; |
| 154 | + break; |
| 155 | + default: |
| 156 | + is_valid_ = false; |
| 157 | + break; |
| 158 | + } |
| 159 | + return; |
| 160 | + } |
| 161 | + |
| 162 | + if (level_ == StandardMapGridLevel::Level1000) { |
| 163 | + // 基点は左下(4A) |
| 164 | + third_column_ = 4 - std::stoi(code_.substr(6, 1)); // 0-4 |
| 165 | + third_row_ = code_.substr(7, 1)[0] - 'A'; // 0-4 (A-E) |
| 166 | + |
| 167 | + // Level1000の範囲チェック |
| 168 | + if (third_column_ < 0 || third_column_ > 4 || |
| 169 | + third_row_ < 0 || third_row_ > 4) { |
| 170 | + is_valid_ = false; |
| 171 | + } |
| 172 | + return; |
| 173 | + } |
| 174 | + |
| 175 | + // Level500 |
| 176 | + // 基点は左下(9A) |
| 177 | + third_column_ = 9 - std::stoi(code_.substr(6, 1)); // 0-9 |
| 178 | + third_row_ = std::stoi(code_.substr(7, 1)); // 0-9 |
| 179 | + |
| 180 | + } catch (const std::exception& e) { |
| 181 | + is_valid_ = false; |
| 182 | + std::cerr << "Failed to parse grid code " << code_ << ": " << e.what() << std::endl; |
| 183 | + } |
12 | 184 | } |
13 | 185 |
|
14 | 186 | std::string StandardMapGrid::get() const { |
15 | 187 | return code_; |
16 | 188 | } |
17 | 189 |
|
| 190 | + std::pair<TVec3d, TVec3d> StandardMapGrid::calculateGridExtent() const { |
| 191 | + // 東西方向のインデックスを計算(東がプラス、西がマイナス) |
| 192 | + int row_index = first_row_ - 'E'; |
| 193 | + |
| 194 | + // 南北方向のインデックスを計算(北がプラス、南がマイナス) |
| 195 | + int column_index = 'J' - first_column_; |
| 196 | + |
| 197 | + // Level50000の計算 |
| 198 | + // column座標(南北)の計算 |
| 199 | + double min_column = column_index * level50000_cell_column; |
| 200 | + double max_column = min_column + level50000_cell_column; |
| 201 | + |
| 202 | + // row座標(東西)の計算 |
| 203 | + double min_row = row_index * level50000_cell_row; |
| 204 | + double max_row = min_row + level50000_cell_row; |
| 205 | + |
| 206 | + if (level_ == StandardMapGridLevel::Level50000) { |
| 207 | + return createExtentPair(min_column, min_row, max_column, max_row); |
| 208 | + } |
| 209 | + |
| 210 | + // Level5000の計算 |
| 211 | + calculateSubGridExtent( |
| 212 | + min_column, min_row, max_column, max_row, |
| 213 | + second_column_, second_row_, |
| 214 | + level5000_cell_column, level5000_cell_row); |
| 215 | + |
| 216 | + if (level_ == StandardMapGridLevel::Level5000) { |
| 217 | + return createExtentPair(min_column, min_row, max_column, max_row); |
| 218 | + } |
| 219 | + |
| 220 | + if (level_ == StandardMapGridLevel::Level2500) { |
| 221 | + |
| 222 | + calculateSubGridExtent( |
| 223 | + min_column, min_row, max_column, max_row, |
| 224 | + third_column_, third_row_, |
| 225 | + level2500_cell_column, level2500_cell_row); |
| 226 | + return createExtentPair(min_column, min_row, max_column, max_row); |
| 227 | + |
| 228 | + } else if (level_ == StandardMapGridLevel::Level1000) { |
| 229 | + |
| 230 | + calculateSubGridExtent( |
| 231 | + min_column, min_row, max_column, max_row, |
| 232 | + third_column_, third_row_, |
| 233 | + level1000_cell_column, level1000_cell_row); |
| 234 | + return createExtentPair(min_column, min_row, max_column, max_row); |
| 235 | + |
| 236 | + } else { // Level500 |
| 237 | + |
| 238 | + calculateSubGridExtent( |
| 239 | + min_column, min_row, max_column, max_row, |
| 240 | + third_column_, third_row_, |
| 241 | + level500_cell_column, level500_cell_row); |
| 242 | + return createExtentPair(min_column, min_row, max_column, max_row); |
| 243 | + } |
| 244 | + } |
| 245 | + |
18 | 246 | geometry::Extent StandardMapGrid::getExtent() const { |
19 | 247 | if (!isValid()) { |
20 | 248 | throw std::runtime_error("Invalid standard map grid code."); |
21 | 249 | } |
22 | | - |
23 | | - // TODO: 図郭コードから緯度経度範囲を計算する実装を追加 |
24 | | - // この実装は図郭コードの仕様に基づいて行う必要があります |
25 | | - return {geometry::GeoCoordinate(0, 0, 0), geometry::GeoCoordinate(0, 0, 0)}; |
| 250 | + |
| 251 | + const auto [planeMin, planeMax] = calculateGridExtent(); |
| 252 | + |
| 253 | + // GeoReferenceを取得 |
| 254 | + plateau::polygonMesh::MeshExtractOptions options; |
| 255 | + options.coordinate_zone_id = coordinate_origin_; |
| 256 | + const auto geo_reference = geometry::GeoReference(options.coordinate_zone_id, options.reference_point, options.unit_scale, options.mesh_axes); |
| 257 | + |
| 258 | + // 平面直角座標系から緯度経度に変換 |
| 259 | + const auto min_coordinate = geo_reference.unproject(planeMin); |
| 260 | + const auto max_coordinate = geo_reference.unproject(planeMax); |
| 261 | + |
| 262 | + return geometry::Extent(min_coordinate, max_coordinate); |
26 | 263 | } |
27 | 264 |
|
28 | 265 | bool StandardMapGrid::isValid() const { |
29 | 266 | return is_valid_; |
30 | 267 | } |
31 | 268 |
|
32 | 269 | std::shared_ptr<GridCode> StandardMapGrid::upper() const { |
33 | | - // 仮実装: 自分自身のコピーを返す |
34 | | - return std::shared_ptr<GridCode>(upperRaw()); |
| 270 | + // 1段階上のレベルの図郭コードに変換 |
| 271 | + auto new_grid_code = std::shared_ptr<GridCode>(upperRaw()); |
| 272 | + return new_grid_code; |
35 | 273 | } |
36 | 274 |
|
37 | 275 | GridCode* StandardMapGrid::upperRaw() const { |
38 | | - // 仮実装: 自分自身のコピーを返す |
39 | | - return new StandardMapGrid(code_); |
| 276 | + if (level_ == StandardMapGridLevel::Level50000) { |
| 277 | + auto* new_grid = new StandardMapGrid(code_); |
| 278 | + new_grid->is_valid_ = false; |
| 279 | + return new_grid; |
| 280 | + } |
| 281 | + |
| 282 | + const std::string upper_code = level_ > StandardMapGridLevel::Level5000 |
| 283 | + ? code_.substr(0, 6) |
| 284 | + : code_.substr(0, 4); |
| 285 | + return new StandardMapGrid(upper_code); |
40 | 286 | } |
41 | 287 |
|
42 | 288 | int StandardMapGrid::getLevel() const { |
43 | | - // 仮実装: 常に1を返す |
44 | | - return 1; |
| 289 | + return (int)level_; |
45 | 290 | } |
46 | 291 |
|
47 | 292 | bool StandardMapGrid::isLargestLevel() const { |
48 | | - // 仮実装: 常にtrueを返す |
49 | | - return true; |
| 293 | + return level_ == StandardMapGridLevel::Level50000; |
50 | 294 | } |
51 | 295 |
|
52 | 296 | bool StandardMapGrid::isSmallerThanNormalGml() const { |
53 | | - // 仮実装 |
54 | | - return false; |
| 297 | + return level_ < StandardMapGridLevel::Level2500; |
55 | 298 | } |
56 | 299 |
|
57 | 300 | bool StandardMapGrid::isNormalGmlLevel() const { |
58 | | - // 仮実装: 常にtrueを返す |
59 | | - return true; |
| 301 | + return level_ == StandardMapGridLevel::Level2500; |
60 | 302 | } |
61 | 303 |
|
62 | 304 | bool StandardMapGrid::operator==(const StandardMapGrid& other) const { |
|
0 commit comments