Skip to content

Commit de851ae

Browse files
committed
Add limits for max map size
* And instructions for debugging with gdb * GDAL segfaults in RasterIo() with too large of a dataset Signed-off-by: Ryan Friedman <[email protected]>
1 parent f73364b commit de851ae

File tree

5 files changed

+133
-50
lines changed

5 files changed

+133
-50
lines changed

README.md

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
# grid_map_geo
2+
23
[![Build Test](https://github.com/ethz-asl/grid_map_geo/actions/workflows/build_test.yml/badge.svg)](https://github.com/ethz-asl/grid_map_geo/actions/workflows/build_test.yml)
34

45
This package provides a georeferenced extension to the elevation map [grid_map](https://github.com/ANYbotics/grid_map) using [GDAL](https://gdal.org/), library for raster and vector geospatial data formats
@@ -8,24 +9,45 @@ This package provides a georeferenced extension to the elevation map [grid_map](
89

910
**Authors: Jaeyoung Lim<br />
1011
Affiliation: [ETH Zurich, Autonomous Systems Lab](https://asl.ethz.ch/)<br />**
12+
1113
## Setup
14+
1215
Install the dependencies. This package depends on gdal, to read georeferenced images and DEM files.
1316

1417
Pull in dependencies using rosdep
15-
```
18+
```bash
1619
source /opt/ros/humble/setup.bash
1720
rosdep update
1821
# Assuming the package is cloned in the src folder of a ROS workspace...
1922
rosdep install --from-paths src --ignore-src -y
2023
```
2124

2225
Build the package
26+
27+
```bash
28+
colcon build --mixin release --packages-up-to grid_map_geo
2329
```
24-
colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release -DCATKIN_ENABLE_TESTING=False --packages-up-to grid_map_geo
25-
```
30+
2631
## Running the package
27-
The default launch file can be run as the following command.
28-
```
32+
33+
The default launch file can be run as the following command.
34+
35+
```bash
2936
source install/setup.bash
3037
ros2 launch grid_map_geo load_tif_launch.xml
3138
```
39+
40+
To debug the map publisher in GDB:
41+
42+
```bash
43+
colcon build --mixin debug --packages-up-to grid_map_geo --symlink-install
44+
source install/setup.bash
45+
46+
# To debug the node under GDB
47+
ros2 run --prefix 'gdb -ex run --args' \
48+
grid_map_geo map_publisher --ros-args \
49+
-p gdal_dataset_path:=install/grid_map_geo/share/grid_map_geo/resources/ap_srtm1.vrt
50+
51+
# To debug from the launch file
52+
ros2 launch grid_map_geo load_vrt_launch.xml
53+
```

include/grid_map_geo/grid_map_geo.hpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,14 @@ class GridMapGeo {
9191
*/
9292
void setAltitudeOrigin(const double altitude) { localorigin_altitude_ = altitude; };
9393

94+
/**
95+
* @brief Helper function for setting maximum map size. Set to 0 to disable bounds check.
96+
*
97+
* @param pixels_x Maximum number of raster pixels in the X direction
98+
* @param pixels_y Maximum number of raster pixels in the Y direction
99+
*/
100+
void setMaxMapSizePixels(const int pixels_x, const int pixels_y);
101+
94102
/**
95103
* @brief Helper function for loading terrain from path
96104
*
@@ -178,5 +186,12 @@ class GridMapGeo {
178186
double localorigin_n_{177416.56};
179187
double localorigin_altitude_{0.0};
180188
Location maporigin_;
189+
190+
private:
191+
192+
// Set default map size occupying 4MB RAM assuming 32 bit height precision.
193+
int max_raster_x_size_ {1024};
194+
int max_raster_y_size_ {1024};
195+
181196
};
182197
#endif

launch/load_vrt_launch.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<arg name="rviz" default="false"/>
33
<node pkg="tf2_ros" exec="static_transform_publisher" name="world_map" args="--frame-id world --child-frame-id map"/>
44

5-
<node pkg="grid_map_geo" exec="map_publisher" name="map_publisher" output="screen">
5+
<node pkg="grid_map_geo" exec="map_publisher" name="map_publisher" output="screen" launch-prefix="gdb -ex run --args">
66
<param name="gdal_dataset_path" value="$(find-pkg-share grid_map_geo)/resources/ap_srtm1.vrt"/>
77
</node>
88

src/grid_map_geo.cpp

Lines changed: 63 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,17 @@ GridMapGeo::GridMapGeo() {}
6161

6262
GridMapGeo::~GridMapGeo() {}
6363

64+
65+
void GridMapGeo::setMaxMapSizePixels(const int pixels_x, const int pixels_y) {
66+
// Use int type to match GDAL , but validate it's a positive value.
67+
assert(pixels_x >= 0);
68+
assert(pixels_y >= 0);
69+
70+
max_raster_x_size_ = pixels_x;
71+
max_raster_y_size_ = pixels_y;
72+
}
73+
74+
6475
bool GridMapGeo::Load(const std::string &map_path, bool algin_terrain, const std::string color_map_path) {
6576
bool loaded = initializeFromGdalDataset(map_path, algin_terrain);
6677
if (!color_map_path.empty()) { // Load color layer if the color path is nonempty
@@ -101,20 +112,29 @@ bool GridMapGeo::initializeFromGdalDataset(const std::string &path, bool align_t
101112
}
102113

103114
// Get image metadata
104-
unsigned width = dataset->GetRasterXSize();
105-
unsigned height = dataset->GetRasterYSize();
115+
const auto raster_width = dataset->GetRasterXSize();
116+
const auto raster_height = dataset->GetRasterYSize();
117+
118+
if (raster_width > max_raster_x_size_ || raster_height > max_raster_y_size_) {
119+
std::cout << "Raster too big. Using a submap of size " << max_raster_x_size_ << "x" << max_raster_y_size_ << std::endl;
120+
}
106121
double resolution = pixelSizeX;
107-
std::cout << __LINE__ << "RasterX: " << width << " RasterY: " << height << " pixelSizeX: " << pixelSizeX << std::endl;
122+
std::cout << "RasterX: " << raster_width << " RasterY: " << raster_height << " pixelSizeX: " << pixelSizeX << std::endl;
123+
124+
125+
// Limit grid map size to not exceed memory limitations
126+
const auto grid_width = std::min(raster_width, max_raster_x_size_);
127+
const auto grid_height = std::min(raster_height, max_raster_y_size_);
108128

109129
// pixelSizeY is negative because the origin of the image is the north-east corner and positive
110130
// Y pixel coordinates go towards the south
111-
const double lengthX = resolution * width;
112-
const double lengthY = resolution * height;
131+
const double lengthX = resolution * grid_width;
132+
const double lengthY = resolution * grid_height;
113133
grid_map::Length length(lengthX, lengthY);
114-
std::cout << __LINE__ << "GMLX: " << lengthX << " GMLY: " << lengthY << std::endl;
134+
std::cout << "GMLX: " << lengthX << " GMLY: " << lengthY << std::endl;
115135

116-
double mapcenter_e = originX + pixelSizeX * width * 0.5;
117-
double mapcenter_n = originY + pixelSizeY * height * 0.5;
136+
double mapcenter_e = originX + pixelSizeX * grid_width * 0.5;
137+
double mapcenter_n = originY + pixelSizeY * grid_height * 0.5;
118138
maporigin_.espg = ESPG::CH1903_LV03;
119139
maporigin_.position = Eigen::Vector3d(mapcenter_e, mapcenter_n, 0.0);
120140

@@ -136,39 +156,41 @@ bool GridMapGeo::initializeFromGdalDataset(const std::string &path, bool align_t
136156
// }
137157

138158
grid_map_.setGeometry(length, resolution, position);
139-
// /// TODO: Use TF for geocoordinates
140-
// grid_map_.setFrameId("map");
141-
// grid_map_.add("elevation");
142-
// const auto raster_count = dataset->GetRasterCount();
143-
// assert(raster_count == 2);
144-
// std::cout << __LINE__ << "Raster Count: " << raster_count << std::endl;
145-
// GDALRasterBand *elevationBand = dataset->GetRasterBand(1);
146-
147-
// std::vector<float> data(width * height, 0.0f);
148-
// elevationBand->RasterIO(GF_Read, 0, 0, width, height, &data[0], width, height, GDT_Float32, 0, 0);
149-
150-
// grid_map::Matrix &layer_elevation = grid_map_["elevation"];
151-
// for (grid_map::GridMapIterator iterator(grid_map_); !iterator.isPastEnd(); ++iterator) {
152-
// const grid_map::Index gridMapIndex = *iterator;
153-
// // TODO: This may be wrong if the pixelSizeY > 0
154-
// int x = width - 1 - gridMapIndex(0);
155-
// int y = gridMapIndex(1);
156-
157-
// layer_elevation(x, y) = data[gridMapIndex(0) + width * gridMapIndex(1)];
158-
// }
159+
/// TODO: Use TF for geocoordinates
160+
grid_map_.setFrameId("map");
161+
grid_map_.add("elevation");
159162

160-
// /// TODO: This is a workaround with the problem of gdal 3 not translating altitude correctly.
161-
// /// This section just levels the current position to the ground
162-
// double altitude{0.0};
163-
// if (grid_map_.isInside(Eigen::Vector2d(0.0, 0.0))) {
164-
// altitude = grid_map_.atPosition("elevation", Eigen::Vector2d(0.0, 0.0));
165-
// }
163+
const auto raster_count = dataset->GetRasterCount();
164+
assert(raster_count == 1); // expect only elevation data, otherwise it's the wrong dataset.
165+
GDALRasterBand *elevationBand = dataset->GetRasterBand(1);
166166

167-
// Eigen::Translation3d meshlab_translation(0.0, 0.0, -altitude);
168-
// Eigen::AngleAxisd meshlab_rotation(Eigen::AngleAxisd::Identity());
169-
// Eigen::Isometry3d transform = meshlab_translation * meshlab_rotation; // Apply affine transformation.
170-
// grid_map_ = grid_map_.getTransformedMap(transform, "elevation", grid_map_.getFrameId(), true);
171-
return false;
167+
std::vector<float> data(grid_width * grid_height, 0.0f);
168+
const auto raster_err = elevationBand->RasterIO(GF_Read, 0, 0, grid_width, grid_height, &data[0], grid_width, grid_height, GDT_Float32, 0, 0);
169+
if (raster_err != CPLE_None ) {
170+
return false;
171+
}
172+
grid_map::Matrix &layer_elevation = grid_map_["elevation"];
173+
for (grid_map::GridMapIterator iterator(grid_map_); !iterator.isPastEnd(); ++iterator) {
174+
const grid_map::Index gridMapIndex = *iterator;
175+
// TODO: This may be wrong if the pixelSizeY > 0
176+
int x = grid_width - 1 - gridMapIndex(0);
177+
int y = gridMapIndex(1);
178+
179+
layer_elevation(x, y) = data[gridMapIndex(0) + grid_width * gridMapIndex(1)];
180+
}
181+
182+
/// TODO: This is a workaround with the problem of gdal 3 not translating altitude correctly.
183+
/// This section just levels the current position to the ground
184+
double altitude{0.0};
185+
if (grid_map_.isInside(Eigen::Vector2d(0.0, 0.0))) {
186+
altitude = grid_map_.atPosition("elevation", Eigen::Vector2d(0.0, 0.0));
187+
}
188+
189+
Eigen::Translation3d meshlab_translation(0.0, 0.0, -altitude);
190+
Eigen::AngleAxisd meshlab_rotation(Eigen::AngleAxisd::Identity());
191+
Eigen::Isometry3d transform = meshlab_translation * meshlab_rotation; // Apply affine transformation.
192+
grid_map_ = grid_map_.getTransformedMap(transform, "elevation", grid_map_.getFrameId(), true);
193+
return true;
172194
}
173195

174196
bool GridMapGeo::addColorFromGeotiff(const std::string &path) {
@@ -196,7 +218,7 @@ bool GridMapGeo::addColorFromGeotiff(const std::string &path) {
196218
unsigned width = dataset->GetRasterXSize();
197219
unsigned height = dataset->GetRasterYSize();
198220
double resolution = pixelSizeX;
199-
std::cout << __LINE__ << "Width: " << width << " Height: " << height << " Resolution: " << resolution << std::endl;
221+
std::cout << "Width: " << width << " Height: " << height << " Resolution: " << resolution << std::endl;
200222

201223
// pixelSizeY is negative because the origin of the image is the north-east corner and positive
202224
// Y pixel coordinates go towards the south
@@ -257,7 +279,7 @@ bool GridMapGeo::addLayerFromGeotiff(const std::string &layer_name, const std::s
257279
unsigned width = dataset->GetRasterXSize();
258280
unsigned height = dataset->GetRasterYSize();
259281
double resolution = pixelSizeX;
260-
std::cout << __LINE__ << "Width: " << width << " Height: " << height << " Resolution: " << resolution << std::endl;
282+
std::cout << "Width: " << width << " Height: " << height << " Resolution: " << resolution << std::endl;
261283

262284
// pixelSizeY is negative because the origin of the image is the north-east corner and positive
263285
// Y pixel coordinates go towards the south

src/map_publisher.cpp

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,46 @@
4545
#include "grid_map_msgs/msg/grid_map.h"
4646
#include "grid_map_ros/GridMapRosConverter.hpp"
4747

48+
#include <algorithm>
49+
#include <limits>
50+
4851
using namespace std::chrono_literals;
4952

5053
class MapPublisher : public rclcpp::Node {
5154
public:
5255
MapPublisher() : Node("map_publisher") {
5356
original_map_pub_ = this->create_publisher<grid_map_msgs::msg::GridMap>("elevation_map", 1);
5457

55-
std::string gdal_dataset_path = this->declare_parameter("gdal_dataset_path", ".");
56-
std::string color_path = this->declare_parameter("tif_color_path", ".");
58+
const std::string gdal_dataset_path = this->declare_parameter("gdal_dataset_path", ".");
59+
const std::string color_path = this->declare_parameter("tif_color_path", ".");
60+
rcl_interfaces::msg::ParameterDescriptor max_map_descriptor;
61+
max_map_descriptor.read_only = true;
62+
max_map_descriptor.description = "Maximum number of raster pixels able to be loaded. \
63+
Useful when working with large raster datasets to limit memory usage. \
64+
Set to 0 to disable limits (may cause runtime crash).";
65+
rcl_interfaces::msg::IntegerRange max_map_descriptor_int_range;
66+
67+
max_map_descriptor_int_range.from_value = 0;
68+
max_map_descriptor_int_range.to_value = std::numeric_limits<int>::max();
69+
max_map_descriptor.integer_range.push_back(max_map_descriptor_int_range);
70+
71+
const uint16_t max_map_width = std::clamp(
72+
this->declare_parameter("max_map_width", 1024, max_map_descriptor),
73+
max_map_descriptor_int_range.from_value,
74+
max_map_descriptor_int_range.to_value);
75+
const uint16_t max_map_height = std::clamp(
76+
this->declare_parameter("max_map_height", 1024, max_map_descriptor),
77+
max_map_descriptor_int_range.from_value,
78+
max_map_descriptor_int_range.to_value);
79+
5780

5881
RCLCPP_INFO_STREAM(get_logger(), "gdal_dataset_path: '" << gdal_dataset_path << "'");
5982
RCLCPP_INFO_STREAM(get_logger(), "color_path: '" << color_path << "'");
6083

6184
map_ = std::make_shared<GridMapGeo>();
85+
map_->setMaxMapSizePixels(max_map_width, max_map_height);
6286
map_->Load(gdal_dataset_path, false, color_path);
63-
timer_ = this->create_wall_timer(5s, std::bind(&MapPublisher::timer_callback, this));
87+
// timer_ = this->create_wall_timer(5s, std::bind(&MapPublisher::timer_callback, this));
6488
}
6589
private:
6690
void timer_callback() {

0 commit comments

Comments
 (0)