@@ -52,22 +52,22 @@ template <typename T> T read(char* buff, int& head, tag_little_endian) {
52
52
class shp_reader {
53
53
void skip (int n_bytes) { head_ += n_bytes; } // move head_ n_bytes forward
54
54
55
- struct sf_header_t {
55
+ struct sf_header_t_ {
56
56
static constexpr int size = 100 ; // the size (in number of bytes) of the header
57
57
int file_code; // file code: 9994
58
58
int file_length; // total length of the file in 8-bit words
59
59
int version; // shapefile version: 1000
60
60
std::array<double , 8 > bbox; // x_min, y_min, x_max, y_max, z_min, z_max, m_min, m_max
61
61
int shape_type;
62
62
};
63
- struct sf_point_t {
63
+ struct sf_point_t_ {
64
64
int shape_type;
65
65
int record_number;
66
66
double x, y, z, m; // point coordinates (z and m optional)
67
67
68
- sf_point_t () = default ;
69
- sf_point_t (double x_, double y_) : x(x_), y(y_) { }
70
- sf_point_t (int record_number_, shp_reader& file) :
68
+ sf_point_t_ () = default ;
69
+ sf_point_t_ (double x_, double y_) : x(x_), y(y_) { }
70
+ sf_point_t_ (int record_number_, shp_reader& file) :
71
71
shape_type (file.shape_type()), record_number(record_number_) {
72
72
// point specific content
73
73
x = read <double >(file.buff_ , file.head_ , LittleEndian);
@@ -78,8 +78,16 @@ class shp_reader {
78
78
m = read <double >(file.buff_ , file.head_ , LittleEndian);
79
79
}
80
80
}
81
+ friend bool operator ==(const sf_point_t_& lhs, const sf_point_t_& rhs) {
82
+ if (lhs.shape_type != rhs.shape_type ) { return false ; }
83
+ bool equal = (lhs.x == rhs.x ) && (lhs.y == rhs.y );
84
+ if (lhs.shape_type == shape_t ::PointM) { equal &= (lhs.m == rhs.m ); }
85
+ if (lhs.shape_type == shape_t ::PointZ) { equal &= ((lhs.m == rhs.m ) && (lhs.z == rhs.z )); }
86
+ return equal;
87
+ }
88
+ friend bool operator !=(const sf_point_t_& lhs, const sf_point_t_& rhs) { return !(lhs == rhs); }
81
89
};
82
- struct sf_multipoint_t {
90
+ struct sf_multipoint_t_ {
83
91
private:
84
92
void read_zm_block (int n_points, std::array<double , 2 >& range, std::vector<double > points, shp_reader& file) {
85
93
points.resize (n_points);
@@ -95,8 +103,8 @@ class shp_reader {
95
103
int n_points; // number of points in the record
96
104
std::vector<double > x, y, z, m; // points coordinates (z and m optional)
97
105
98
- sf_multipoint_t () = default ;
99
- sf_multipoint_t (int record_number_, shp_reader& file) :
106
+ sf_multipoint_t_ () = default ;
107
+ sf_multipoint_t_ (int record_number_, shp_reader& file) :
100
108
shape_type (file.shape_type()), record_number(record_number_) {
101
109
// multipoint specific content
102
110
for (int i = 0 ; i < 4 ; ++i) { bbox[i] = read <double >(file.buff_ , file.head_ , LittleEndian); }
@@ -114,10 +122,10 @@ class shp_reader {
114
122
}
115
123
}
116
124
};
117
- struct sf_polygon_t {
125
+ struct sf_polygon_t_ {
118
126
private:
119
127
void
120
- read_zm_block (int n_points, std::array<double , 2 >& range, std::vector<sf_point_t >& points, shp_reader& file) {
128
+ read_zm_block (int n_points, std::array<double , 2 >& range, std::vector<sf_point_t_ >& points, shp_reader& file) {
121
129
range[0 ] = read <double >(file.buff_ , file.head_ , LittleEndian);
122
130
range[1 ] = read <double >(file.buff_ , file.head_ , LittleEndian);
123
131
for (int i = 0 ; i < n_points; ++i) {
@@ -137,10 +145,10 @@ class shp_reader {
137
145
int n_rings; // number of closed polygons in the record
138
146
int n_points; // overall number of points
139
147
std::vector<int > ring_begin, ring_end; // first and last points in each ring, as offsets in points vector
140
- std::vector<sf_point_t > points; // points coordinates (z and m optional)
148
+ std::vector<sf_point_t_ > points; // points coordinates (z and m optional)
141
149
142
- sf_polygon_t () = default ;
143
- sf_polygon_t (int record_number_, shp_reader& file) :
150
+ sf_polygon_t_ () = default ;
151
+ sf_polygon_t_ (int record_number_, shp_reader& file) :
144
152
shape_type (file.shape_type()), record_number(record_number_) {
145
153
// polygon specific content
146
154
for (int i = 0 ; i < 4 ; ++i) { bbox[i] = read <double >(file.buff_ , file.head_ , LittleEndian); }
@@ -175,17 +183,17 @@ class shp_reader {
175
183
}
176
184
// iterator over rings
177
185
class ring_iterator {
178
- const sf_polygon_t * polygon_;
186
+ const sf_polygon_t_ * polygon_;
179
187
int index_;
180
- std::vector<sf_point_t >::const_iterator it;
188
+ std::vector<sf_point_t_ >::const_iterator it;
181
189
public:
182
- ring_iterator (const sf_polygon_t * polygon, int index) : polygon_(polygon), index_(index) {
190
+ ring_iterator (const sf_polygon_t_ * polygon, int index) : polygon_(polygon), index_(index) {
183
191
it = polygon_->points .begin ();
184
192
}
185
- std::vector<sf_point_t >::const_iterator begin () const { return it + polygon_->ring_begin [index_]; }
186
- std::vector<sf_point_t >::const_iterator end () const { return it + polygon_->ring_end [index_]; }
187
- const sf_point_t * operator ->() { return it.operator ->(); }
188
- const sf_point_t & operator *() { return *it; };
193
+ std::vector<sf_point_t_ >::const_iterator begin () const { return it + polygon_->ring_begin [index_]; }
194
+ std::vector<sf_point_t_ >::const_iterator end () const { return it + polygon_->ring_end [index_]; }
195
+ const sf_point_t_ * operator ->() { return it.operator ->(); }
196
+ const sf_point_t_ & operator *() { return *it; };
189
197
ring_iterator& operator ++() {
190
198
index_++;
191
199
return *this ;
@@ -194,31 +202,41 @@ class shp_reader {
194
202
return it1.index_ != it2.index_ ;
195
203
}
196
204
int n_nodes () const { return polygon_->ring_end [index_] - polygon_->ring_begin [index_]; }
197
- // RowMajor expansion of polygon coordinates
198
- Eigen::Matrix<double , Dynamic, Dynamic> nodes () {
199
- int n_rows = n_nodes ();
205
+ // RowMajor expansion of ring coordinates (already discards last point, as it coincides with end point)
206
+ Eigen::Matrix<double , Dynamic, Dynamic> nodes () const {
207
+ int n_rows = n_nodes () - ( end () != polygon_-> points . end () ? 0 : 1 ) ;
200
208
int n_cols =
201
209
2 +
202
210
(polygon_->shape_type == shape_t ::PointM ? 1 : (polygon_->shape_type == shape_t ::PointZ ? 2 : 0 ));
203
211
Eigen::Matrix<double , Dynamic, Dynamic> coords (n_rows, n_cols);
204
212
int row = 0 ;
205
- for (auto jt = begin (), ht = end (); jt != ht; ++jt) {
213
+ for (auto jt = begin (), ht = end (); jt != ht && row < n_rows ; ++jt) {
206
214
coords (row, 0 ) = jt->x ;
207
215
coords (row, 1 ) = jt->y ;
208
216
if (polygon_->shape_type == shape_t ::PointM) { coords (row, 2 ) = jt->m ; }
209
- if (polygon_->shape_type == shape_t ::PointZ) { coords (row, 3 ) = jt->z ; }
210
- row++;
217
+ if (polygon_->shape_type == shape_t ::PointZ) {
218
+ coords (row, 2 ) = jt->m ;
219
+ coords (row, 3 ) = jt->z ;
220
+ }
221
+ row++;
211
222
}
212
223
return coords;
213
224
}
214
225
};
215
226
ring_iterator begin () const { return ring_iterator (this , 0 ); }
216
227
ring_iterator end () const { return ring_iterator (this , n_rings); }
228
+ // provides all nodes coordinates, orgnanized by rings
229
+ std::vector<Eigen::Matrix<double , Dynamic, Dynamic>> nodes () const {
230
+ std::vector<Eigen::Matrix<double , Dynamic, Dynamic>> nodes_;
231
+ nodes_.reserve (n_rings);
232
+ for (ring_iterator it = begin (); it != end (); ++it) { nodes_.push_back (it.nodes ()); }
233
+ return nodes_;
234
+ }
217
235
};
218
236
// a polyline has the same layout of a polygon, where rings are not necessarily closed (and can self-intersect)
219
- struct sf_polyline_t : public sf_polygon_t {
220
- sf_polyline_t () = default ;
221
- sf_polyline_t (int record_number_, shp_reader& file) : sf_polygon_t (record_number_, file) { }
237
+ struct sf_polyline_t_ : public sf_polygon_t_ {
238
+ sf_polyline_t_ () = default ;
239
+ sf_polyline_t_ (int record_number_, shp_reader& file) : sf_polygon_t (record_number_, file) { }
222
240
};
223
241
224
242
template <typename T> void read_into (T& container) {
@@ -232,15 +250,19 @@ class shp_reader {
232
250
233
251
std::string file_name_;
234
252
int n_records_;
235
- sf_header_t header_; // shapefile header
253
+ sf_header_t_ header_; // shapefile header
236
254
int head_ = 0 ; // currently pointed byte in buff_
237
255
char * buff_ = nullptr ; // loaded binary data
238
256
// only one of the following containers is active (by shapefile specification)
239
- std::vector<sf_point_t > points_;
240
- std::vector<sf_polyline_t > polylines_;
241
- std::vector<sf_polygon_t > polygons_;
242
- std::vector<sf_multipoint_t > multipoints_;
257
+ std::vector<sf_point_t_ > points_;
258
+ std::vector<sf_polyline_t_ > polylines_;
259
+ std::vector<sf_polygon_t_ > polygons_;
260
+ std::vector<sf_multipoint_t_ > multipoints_;
243
261
public:
262
+ using sf_point_t = sf_point_t_;
263
+ using sf_polyline_t = sf_polyline_t_;
264
+ using sf_polygon_t = sf_polygon_t_;
265
+ using sf_multipoint_t = sf_multipoint_t_;
244
266
// supported shapefile format
245
267
// all the non-null shapes in a shapefile are required to be of the same shape type (cit. Shapefile standard)
246
268
enum shape_t {
@@ -287,27 +309,24 @@ class shp_reader {
287
309
int shape_type () const { return header_.shape_type ; }
288
310
std::array<double , 4 > bbox () const { return {header_.bbox [0 ], header_.bbox [1 ], header_.bbox [2 ], header_.bbox [3 ]}; }
289
311
int n_records () const { return n_records_; }
290
- const sf_header_t & header () const { return header_; }
312
+ const sf_header_t_ & header () const { return header_; }
291
313
int n_points () const { return points_.size (); }
292
314
const sf_point_t & point (int index) const {
293
315
fdapde_assert (
294
316
shape_type () == shape_t ::Point || shape_type () == shape_t ::PointZ || shape_type () == shape_t ::PointM);
295
317
return points_[index ];
296
318
}
297
- int n_polylines () const { return polylines_.size (); }
298
319
const sf_polyline_t & polyline (int index) const {
299
320
fdapde_assert (
300
321
shape_type () == shape_t ::PolyLine || shape_type () == shape_t ::PolyLineZ ||
301
322
shape_type () == shape_t ::PolyLineM);
302
323
return polylines_[index ];
303
324
}
304
- int n_polygons () const { return polygons_.size (); }
305
325
const sf_polygon_t & polygon (int index) const {
306
326
fdapde_assert (
307
327
shape_type () == shape_t ::Polygon || shape_type () == shape_t ::PolygonZ || shape_type () == shape_t ::PolygonM);
308
328
return polygons_[index ];
309
329
}
310
- int n_multipoints () const { return multipoints_.size (); }
311
330
const sf_multipoint_t & multipoint (int index) const {
312
331
fdapde_assert (
313
332
shape_type () == shape_t ::MultiPoint || shape_type () == shape_t ::MultiPointZ ||
@@ -417,7 +436,14 @@ class ShapeFile {
417
436
std::string gcs_ = " UNDEFINED" ; // geographic coordinate system (GCS)
418
437
std::string filename_;
419
438
public:
420
- ShapeFile (std::string filename) : filename_(filename) {
439
+ ShapeFile (std::string filename) : filename_() {
440
+ std::filesystem::path filepath (filename);
441
+ if (!std::filesystem::exists (filepath)) { throw std::runtime_error (" File " + filename + " not found." ); }
442
+ if (filepath.extension () == " .shp" ) {
443
+ filename_ = filepath.parent_path () / filepath.stem ();
444
+ } else {
445
+ throw std::runtime_error (filename + " is not a .shp file." );
446
+ }
421
447
// load geometric features and associated data
422
448
shp_ = shp_reader (filename_ + " .shp" );
423
449
dbf_ = dbf_reader (filename_ + " .dbf" );
@@ -433,10 +459,20 @@ class ShapeFile {
433
459
gcs_ = line.substr (i, j - i);
434
460
}
435
461
}
436
- // getters
462
+ // observers
437
463
const shp_reader& shp () const { return shp_; }
438
464
template <typename T> std::vector<T> get_as (std::string colname) const { return dbf_.get_as <T>(colname); }
439
465
std::vector<std::pair<std::string, char >> field_descriptors () const { return dbf_.field_descriptors (); }
466
+ int shape_type () const { return shp_.shape_type (); }
467
+ int n_records () const { return shp_.n_records (); }
468
+ // geometry
469
+ const auto & point (int index) const { return shp_.point (index ); }
470
+ const auto & polyline (int index) const { return shp_.polyline (index ); }
471
+ const auto & polygon (int index) const { return shp_.polygon (index ); }
472
+ const auto & multipoint (int index) const { return shp_.multipoint (index ); }
473
+ // accessor
474
+ template <typename T> std::vector<T> get (std::string colname) const { return dbf_.get_as <T>(colname); }
475
+ // output stream
440
476
friend std::ostream& operator <<(std::ostream& os, const ShapeFile& sf) {
441
477
os << " file: " << sf.filename_ << std::endl;
442
478
std::string shape_type;
0 commit comments