Skip to content

Commit b8ca7be

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 bb7df5b commit b8ca7be

File tree

5 files changed

+106
-30
lines changed

5 files changed

+106
-30
lines changed

README.md

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,17 @@ Affiliation: [ETH Zurich, Autonomous Systems Lab](https://asl.ethz.ch/)<br />**
1515
Install the dependencies. This package depends on GDAL, to read georeferenced images and DEM files.
1616

1717
Pull in dependencies using rosdep
18-
```
18+
```bash
1919
source /opt/ros/humble/setup.bash
2020
rosdep update
2121
# Assuming the package is cloned in the src folder of a ROS workspace...
2222
rosdep install --from-paths src --ignore-src -y
2323
```
2424

2525
Build the package
26+
27+
```bash
28+
colcon build --mixin release --packages-up-to grid_map_geo
2629
```
2730
colcon build --cmake-args -DCMAKE_BUILD_TYPE=Release --packages-up-to grid_map_geo
2831
```
@@ -65,8 +68,22 @@ ros2 launch grid_map_geo load_tif_launch.xml
6568
## Running the package
6669

6770
The default launch file can be run as the following command.
68-
```
71+
```bash
6972
source install/setup.bash
7073
ros2 launch grid_map_geo load_tif_launch.xml
7174
```
7275

76+
To debug the map publisher in GDB:
77+
78+
```bash
79+
colcon build --mixin debug --packages-up-to grid_map_geo --symlink-install
80+
source install/setup.bash
81+
82+
# To debug the node under GDB
83+
ros2 run --prefix 'gdb -ex run --args' \
84+
grid_map_geo map_publisher --ros-args \
85+
-p gdal_dataset_path:=install/grid_map_geo/share/grid_map_geo/resources/ap_srtm1.vrt
86+
87+
# To debug from the launch file
88+
ros2 launch grid_map_geo load_vrt_launch.xml
89+
```

include/grid_map_geo/grid_map_geo.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,14 @@ class GridMapGeo {
9797
*/
9898
bool Load(const std::string& map_path) { Load(map_path, ""); }
9999

100+
/**
101+
* @brief Helper function for setting maximum map size. Set to 0 to disable bounds check.
102+
*
103+
* @param pixels_x Maximum number of raster pixels in the X direction
104+
* @param pixels_y Maximum number of raster pixels in the Y direction
105+
*/
106+
void setMaxMapSizePixels(const int pixels_x, const int pixels_y);
107+
100108
/**
101109
* @brief Helper function for loading terrain from path
102110
*
@@ -183,5 +191,10 @@ class GridMapGeo {
183191
Location maporigin_;
184192
std::string frame_id_{""};
185193
std::string coordinate_name_{""};
194+
195+
private:
196+
// Set default map size occupying 4MB RAM assuming 32 bit height precision.
197+
int max_raster_x_size_{1024};
198+
int max_raster_y_size_{1024};
186199
};
187200
#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: 46 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,15 @@ GridMapGeo::GridMapGeo(const std::string &frame_id) { frame_id_ = frame_id; }
6262

6363
GridMapGeo::~GridMapGeo() {}
6464

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+
6574
bool GridMapGeo::Load(const std::string &map_path, const std::string color_map_path) {
6675
bool loaded = initializeFromGdalDataset(map_path);
6776
if (!color_map_path.empty()) { // Load color layer if the color path is nonempty
@@ -106,20 +115,30 @@ bool GridMapGeo::initializeFromGdalDataset(const std::string &path) {
106115
coordinate_name_ = spatial_ref->GetAttrValue("geogcs");
107116

108117
// Get image metadata
109-
unsigned width = dataset->GetRasterXSize();
110-
unsigned height = dataset->GetRasterYSize();
118+
const auto raster_width = dataset->GetRasterXSize();
119+
const auto raster_height = dataset->GetRasterYSize();
120+
121+
if (raster_width > max_raster_x_size_ || raster_height > max_raster_y_size_) {
122+
std::cout << "Raster too big. Using a submap of size " << max_raster_x_size_ << "x" << max_raster_y_size_
123+
<< std::endl;
124+
}
111125
double resolution = pixelSizeX;
112-
std::cout << __LINE__ << "RasterX: " << width << " RasterY: " << height << " pixelSizeX: " << pixelSizeX << std::endl;
126+
std::cout << "RasterX: " << raster_width << " RasterY: " << raster_height << " pixelSizeX: " << pixelSizeX
127+
<< std::endl;
128+
129+
// Limit grid map size to not exceed memory limitations
130+
const auto grid_width = std::min(raster_width, max_raster_x_size_);
131+
const auto grid_height = std::min(raster_height, max_raster_y_size_);
113132

114133
// pixelSizeY is negative because the origin of the image is the north-east corner and positive
115134
// Y pixel coordinates go towards the south
116-
const double lengthX = resolution * width;
117-
const double lengthY = resolution * height;
135+
const double lengthX = resolution * grid_width;
136+
const double lengthY = resolution * grid_height;
118137
grid_map::Length length(lengthX, lengthY);
119-
std::cout << __LINE__ << "GMLX: " << lengthX << " GMLY: " << lengthY << std::endl;
138+
std::cout << "GMLX: " << lengthX << " GMLY: " << lengthY << std::endl;
120139

121-
double mapcenter_e = originX + pixelSizeX * width * 0.5;
122-
double mapcenter_n = originY + pixelSizeY * height * 0.5;
140+
double mapcenter_e = originX + pixelSizeX * grid_width * 0.5;
141+
double mapcenter_n = originY + pixelSizeY * grid_height * 0.5;
123142
maporigin_.espg = ESPG::CH1903_LV03;
124143
maporigin_.position = Eigen::Vector3d(mapcenter_e, mapcenter_n, 0.0);
125144

@@ -128,20 +147,26 @@ bool GridMapGeo::initializeFromGdalDataset(const std::string &path) {
128147
grid_map_.setGeometry(length, resolution, position);
129148
grid_map_.setFrameId(frame_id_);
130149
grid_map_.add("elevation");
131-
GDALRasterBand *elevationBand = dataset->GetRasterBand(1);
132150

133-
// std::vector<float> data(width * height, 0.0f);
134-
// elevationBand->RasterIO(GF_Read, 0, 0, width, height, &data[0], width, height, GDT_Float32, 0, 0);
151+
const auto raster_count = dataset->GetRasterCount();
152+
assert(raster_count == 1); // expect only elevation data, otherwise it's the wrong dataset.
153+
GDALRasterBand *elevationBand = dataset->GetRasterBand(1);
135154

136-
// grid_map::Matrix &layer_elevation = grid_map_["elevation"];
137-
// for (grid_map::GridMapIterator iterator(grid_map_); !iterator.isPastEnd(); ++iterator) {
138-
// const grid_map::Index gridMapIndex = *iterator;
139-
// // TODO: This may be wrong if the pixelSizeY > 0
140-
// int x = width - 1 - gridMapIndex(0);
141-
// int y = gridMapIndex(1);
155+
std::vector<float> data(grid_width * grid_height, 0.0f);
156+
const auto raster_err = elevationBand->RasterIO(GF_Read, 0, 0, grid_width, grid_height, &data[0], grid_width,
157+
grid_height, GDT_Float32, 0, 0);
158+
if (raster_err != CPLE_None) {
159+
return false;
160+
}
161+
grid_map::Matrix &layer_elevation = grid_map_["elevation"];
162+
for (grid_map::GridMapIterator iterator(grid_map_); !iterator.isPastEnd(); ++iterator) {
163+
const grid_map::Index gridMapIndex = *iterator;
164+
// TODO: This may be wrong if the pixelSizeY > 0
165+
int x = grid_width - 1 - gridMapIndex(0);
166+
int y = gridMapIndex(1);
142167

143-
// layer_elevation(x, y) = data[gridMapIndex(0) + width * gridMapIndex(1)];
144-
// }
168+
layer_elevation(x, y) = data[gridMapIndex(0) + grid_width * gridMapIndex(1)];
169+
}
145170

146171
return true;
147172
}
@@ -171,7 +196,7 @@ bool GridMapGeo::addColorFromGeotiff(const std::string &path) {
171196
unsigned width = dataset->GetRasterXSize();
172197
unsigned height = dataset->GetRasterYSize();
173198
double resolution = pixelSizeX;
174-
std::cout << __LINE__ << "Width: " << width << " Height: " << height << " Resolution: " << resolution << std::endl;
199+
std::cout << "Width: " << width << " Height: " << height << " Resolution: " << resolution << std::endl;
175200

176201
// pixelSizeY is negative because the origin of the image is the north-east corner and positive
177202
// Y pixel coordinates go towards the south
@@ -232,7 +257,7 @@ bool GridMapGeo::addLayerFromGeotiff(const std::string &layer_name, const std::s
232257
unsigned width = dataset->GetRasterXSize();
233258
unsigned height = dataset->GetRasterYSize();
234259
double resolution = pixelSizeX;
235-
std::cout << __LINE__ << "Width: " << width << " Height: " << height << " Resolution: " << resolution << std::endl;
260+
std::cout << "Width: " << width << " Height: " << height << " Resolution: " << resolution << std::endl;
236261

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

src/map_publisher.cpp

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@
3737
* @author Jaeyoung Lim <[email protected]>
3838
*/
3939

40+
#include <algorithm>
41+
#include <limits>
42+
4043
#include "geometry_msgs/msg/transform_stamped.hpp"
4144
#include "grid_map_geo/grid_map_geo.hpp"
4245
#include "grid_map_msgs/msg/grid_map.h"
@@ -50,18 +53,36 @@ using namespace std::chrono_literals;
5053
class MapPublisher : public rclcpp::Node {
5154
public:
5255
MapPublisher() : Node("map_publisher") {
53-
std::string file_path = this->declare_parameter("tif_path", ".");
54-
std::string color_path = this->declare_parameter("tif_color_path", ".");
55-
std::string frame_id = this->declare_parameter("frame_id", "map");
56-
5756
original_map_pub_ = this->create_publisher<grid_map_msgs::msg::GridMap>("elevation_map", 1);
57+
const std::string frame_id = this->declare_parameter("frame_id", "map");
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 =
63+
"Maximum number of raster pixels able to be loaded. \
64+
Useful when working with large raster datasets to limit memory usage. \
65+
Set to 0 to disable limits (may cause runtime crash).";
66+
rcl_interfaces::msg::IntegerRange max_map_descriptor_int_range;
67+
68+
max_map_descriptor_int_range.from_value = 0;
69+
max_map_descriptor_int_range.to_value = std::numeric_limits<int>::max();
70+
max_map_descriptor.integer_range.push_back(max_map_descriptor_int_range);
71+
72+
const uint16_t max_map_width =
73+
std::clamp(this->declare_parameter("max_map_width", 1024, max_map_descriptor),
74+
max_map_descriptor_int_range.from_value, max_map_descriptor_int_range.to_value);
75+
const uint16_t max_map_height =
76+
std::clamp(this->declare_parameter("max_map_height", 1024, max_map_descriptor),
77+
max_map_descriptor_int_range.from_value, max_map_descriptor_int_range.to_value);
5878

59-
RCLCPP_INFO_STREAM(get_logger(), "file_path " << file_path);
79+
RCLCPP_INFO_STREAM(get_logger(), "file_path " << gdal_dataset_path);
6080
RCLCPP_INFO_STREAM(get_logger(), "color_path " << color_path);
6181
tf_broadcaster_ = std::make_shared<tf2_ros::StaticTransformBroadcaster>(this);
6282

6383
map_ = std::make_shared<GridMapGeo>(frame_id);
64-
map_->Load(file_path, color_path);
84+
map_->setMaxMapSizePixels(max_map_width, max_map_height);
85+
map_->Load(gdal_dataset_path, color_path);
6586
auto timer_callback = [this]() -> void {
6687
auto msg = grid_map::GridMapRosConverter::toMessage(map_->getGridMap());
6788
if (msg) {

0 commit comments

Comments
 (0)