TinyRobotics
Loading...
Searching...
No Matches
MapSerializer.h
1
2#pragma once
3
4#include "TinyRobotics/maps/PathSegment.h"
5#include "TinyRobotics/utils/Common.h"
6#include "Serializable.h"
7
8namespace tinyrobotics {
9
10/**
11 * @class GridMapSerializer
12 * @ingroup serialize
13 * @brief Utility class for serializing and deserializing grid maps in CSV
14 * format.
15 *
16 * The GridMapSerializer class provides methods to write a grid map to a Print
17 * object (such as Serial) and to read a grid map from a Stream object. It is
18 * designed to work with map types that provide the following interface:
19 * - getXCount(), getYCount(), getResolution(), cellToWorld(), getCell(),
20 * setCell(), resize(), setResolution()
21 *
22 * The serialization format is:
23 * - Header: "GridMap x:<xCount>, y:<yCount>, resolution=<res>"
24 * - One line per cell: "<wx>,<wy>,<value>" (CSV: world x, world y, value)
25 * - If the value is missing, the line is "<wx>,<wy>,"
26 *
27 * This format is compatible with external tools and spreadsheets for easy
28 * inspection and editing.
29 *
30 * Example:
31 * GridMap x:10, y:10, resolution=0.1
32 * 0,0,0
33 * 0,0.1,1
34 * ...
35 *
36 * @tparam Map The map type to serialize/deserialize (e.g., GridMap)
37 * @tparam StateT The cell state type (e.g., float, int, CellState)
38 * @tparam T The numeric type for coordinates (e.g., float)
39 */
40
41template <typename Map, typename StateT = CellState, typename T = float>
43 public:
44 // write map to output
45 size_t write(Map& map, Print& out) {
46 size_t total = 0;
47 char buf[128];
48 int n =
49 snprintf(buf, sizeof(buf), "GridMap x:%d, y:%d, resolution=%g\n",
50 map.getXCount(), map.getYCount(), (double)map.getResolution());
51 if (n > 0) total += out.print(buf);
52 for (int x = 0; x < map.getXCount(); ++x) {
53 for (int y = 0; y < map.getYCount(); ++y) {
54 Coordinate<T> coord;
55 map.cellToWorld(x, y, coord.x, coord.y);
56 StateT state;
57 if (map.getCell(x, y, state)) {
58 int n = snprintf(buf, sizeof(buf), "%g,%g,%g\n", (double)coord.x,
59 (double)coord.y, (double)state);
60 if (n > 0) total += out.print(buf);
61 } else {
62 int n = snprintf(buf, sizeof(buf), "%g,%g,\n", (double)coord.x,
63 (double)coord.y);
64 if (n > 0) total += out.print(buf);
65 }
66 }
67 }
68 out.flush();
69 return total;
70 }
71
72 // read map from input
73 size_t read(Map& map, Stream& in) {
74 size_t total = 0;
75 int x, y;
76 float res;
77
78 // Read header
79 std::string header;
80 if (!readLine(in, header)) return 0;
81 total += header.length() + 1;
82 if (sscanf(header.c_str(), "GridMap x:%d, y:%d, resolution=%f", &x, &y,
83 &res) != 3)
84 return 0;
85 if (x <= 0 || y <= 0 || res <= 0) return 0;
86 map.resize(x, y);
87 map.setResolution(res);
88
89 // Read cell values
90 int cellCount = x * y;
91 int filled = 0;
92 while (filled < cellCount) {
93 std::string line;
94 if (!readLine(in, line)) return 0;
95 total += line.length() + 1;
96 float wx, wy, value;
97 int parsed = sscanf(line.c_str(), "%f,%f,%f", &wx, &wy, &value);
98 if (parsed < 2) return 0; // must have at least x and y
99 Coordinate<T> coord(static_cast<T>(wx), static_cast<T>(wy));
100 if (parsed == 3) {
101 map.setCell(coord, (StateT)value);
102 }
103 ++filled;
104 }
105 return total;
106 }
107};
108
109/**
110 * @class PathMapSerializer
111 * @ingroup serialize
112 * @brief Utility class for serializing and deserializing path maps (sequences
113 * of segments) in CSV format.
114 *
115 * The PathMapSerializer class provides methods to write a path map (a
116 * sequence of segments, each with a 'from' and 'to' coordinate, a cost, and a
117 * directed flag) to a Print object (such as Serial) and to read a path map
118 * from a Stream object. It is designed to work with map types that provide:
119 * - size(), operator[](size_t), and addSegment(from, to, cost, directed)
120 * - Each segment must have 'from' and 'to' members of type CoordinateT,
121 * plus 'cost' (double/float) and 'directed' (bool)
122 *
123 * The serialization format is:
124 * - One line per segment:
125 * "<from.x>,<from.y>,<to.x>,<to.y>,<cost>,<directed>" (CSV)
126 * - <cost> is a floating-point value, <directed> is 0 (false) or 1 (true)
127 * - If <cost> and <directed> are missing, defaults are used (cost=1.0,
128 * directed=false)
129 *
130 * This format is compatible with external tools and spreadsheets for easy
131 * inspection and editing.
132 *
133 * Example:
134 * 0,0,1,1,2.5,1
135 * 1,1,2,2,1.0,0
136 * ...
137 *
138 * @tparam MapT The map type to serialize/deserialize (must support segment
139 * access and addSegment)
140 * @tparam CoordinateT The coordinate type for segment endpoints (e.g.,
141 * Coordinate<float>)
142 */
143
144template <typename MapT, typename CoordinateT = Coordinate<float>>
145class PathMapSerializer {
146 using T = typename CoordinateT::value_type;
147
148 size_t write(MapT& map, Print& out) {
149 char buf[160];
150 size_t total = 0;
151 size_t len = map.size();
152 for (size_t i = 0; i < len; ++i) {
153 auto& segment = map[i];
154 auto& from = segment.from;
155 auto& to = segment.to;
156 double cost = segment.cost;
157 bool directed = segment.directed;
158 int n = snprintf(buf, sizeof(buf), "%g,%g,%g,%g,%g,%d\n", (double)from.x,
159 (double)from.y, (double)to.x, (double)to.y, cost,
160 directed ? 1 : 0);
161 if (n > 0) total += out.print(buf);
162 }
163 out.flush();
164 return total;
165 }
166
167 size_t read(MapT& map, Stream& in) {
168 size_t total = 0;
169 std::string line;
170 while (readLine(in, line)) {
171 total += line.length() + 1;
172 float fx, fy, tx, ty, cost = 1.0f;
173 int directedInt = 0;
174 int parsed = sscanf(line.c_str(), "%f,%f,%f,%f,%f,%d", &fx, &fy, &tx, &ty,
175 &cost, &directedInt);
176 if (parsed < 4) return 0; // must have at least coordinates
177 CoordinateT from(static_cast<T>(fx), static_cast<T>(fy));
178 CoordinateT to(static_cast<T>(tx), static_cast<T>(ty));
179 double segCost = (parsed >= 5) ? cost : 1.0;
180 bool directed = (parsed == 6) ? (directedInt != 0) : false;
181 PathSegment<CoordinateT> segment{from, to, segCost, directed};
182 map.addSegment(segment);
183 }
184 return total;
185 }
186};
187
188/**
189 * @class PointCloudSerializer
190 * @ingroup serialize
191 * @brief Utility class for serializing and deserializing PointCloud objects,
192 * including points, voxels, and attributes.
193 *
194 * The PointCloudSerializer class provides methods to write a PointCloud to a
195 * Print object (such as Serial) and to read a PointCloud from a Stream object.
196 * The format includes a header with attributes, points, and voxels.
197 *
198 * Format:
199 * #PointCloud voxelSize:<float> is3d:<0|1>
200 * bounds:<minx>,<miny>,<minz>,<maxx>,<maxy>,<maxz> #Points x1,y1,z1
201 * ...
202 * #Voxels
203 * vx1,vy1,vz1
204 * ...
205 *
206 * @tparam PointCloudT The PointCloud type to serialize/deserialize (e.g.,
207 * PointCloud<float>)
208 * @tparam T The numeric type for coordinates (e.g., float)
209 */
210template <typename PointCloudT>
212 using T = typename PointCloudT::value_type;
213
214 public:
215 // Write all attributes, points, and voxels
216 size_t write(const PointCloudT& cloud, Print& out) {
217 size_t total = 0;
218 char buf[128];
219 // Header
220 const auto& b = cloud.bounds();
221 sprintf(buf, "#PointCloud voxelSize:%g is3d:%d bounds:%g,%g,%g,%g,%g,%g\n",
222 (double)cloud.voxelSize(), cloud.is3D() ? 1 : 0, (double)b.min.x,
223 (double)b.min.y, (double)b.min.z, (double)b.max.x, (double)b.max.y,
224 (double)b.max.z);
225 total += out.print(buf);
226 // Points
227 total += out.print("#Points\n");
228 for (const auto& pt : cloud.points()) {
229 sprintf(buf, "%g,%g,%g\n", (double)pt.x, (double)pt.y, (double)pt.z);
230 total += out.print(buf);
231 }
232 // Voxels
233 total += out.print("#Voxels\n");
234 for (auto it = cloud.beginVoxels(); it != cloud.endVoxels(); ++it) {
235 sprintf(buf, "%d,%d,%d\n", it->x, it->y, it->z);
236 total += out.print(buf);
237 }
238 out.flush();
239 return total;
240 }
241
242 // Read all attributes, points, and voxels
243 size_t read(PointCloudT& cloud, Stream& in) {
244 size_t total = 0;
245 std::string line;
246 // Header
247 if (!readLine(in, line)) return 0;
248 total += line.length() + 1;
249 float voxelSize = 0.0f, minx, miny, minz, maxx, maxy, maxz;
250 int is3d = 0;
251 if (sscanf(line.c_str(),
252 "#PointCloud voxelSize:%f is3d:%d bounds:%f,%f,%f,%f,%f,%f",
253 &voxelSize, &is3d, &minx, &miny, &minz, &maxx, &maxy,
254 &maxz) != 8)
255 return 0;
256 cloud.set3D(is3d != 0);
257 cloud.setBounds(minx, miny, minz, maxx, maxy, maxz);
258 cloud.buildVoxelGrid(voxelSize);
259 // Expect #Points
260 if (!readLine(in, line)) return 0;
261 total += line.length() + 1;
262 if (line.find("#Points") != 0) return 0;
263 // Read points
264 while (readLine(in, line)) {
265 total += line.length() + 1;
266 if (line.find("#Voxels") == 0) break;
267 float x, y, z = 0.0f;
268 int parsed = sscanf(line.c_str(), "%f,%f,%f", &x, &y, &z);
269 if (parsed < 2) return 0;
270 cloud.add(static_cast<T>(x), static_cast<T>(y), static_cast<T>(z));
271 }
272 // Read voxels
273 while (readLine(in, line)) {
274 total += line.length() + 1;
275 int vx, vy, vz;
276 if (sscanf(line.c_str(), "%d,%d,%d", &vx, &vy, &vz) == 3) {
277 cloud.addVoxel((float)vx * voxelSize, (float)vy * voxelSize,
278 (float)vz * voxelSize);
279 }
280 }
281 return total;
282 }
283};
284
285} // namespace tinyrobotics
A generic 3D coordinate class for robotics, navigation, and spatial calculations.
Definition: Coordinate.h:57
Utility class for serializing and deserializing grid maps in CSV format.
Definition: MapSerializer.h:42
Utility class for serializing and deserializing path maps (sequences of segments) in CSV format.
Definition: MapSerializer.h:145
Utility class for serializing and deserializing PointCloud objects, including points,...
Definition: MapSerializer.h:211
CellState
Cell state for occupancy grid mapping (e.g., UNKNOWN, FREE, OCCUPIED).
Definition: Common.h:32