Shapes
GIS made easy, a lightweight header-only planar geometry library for Modern C++
polygon.hpp
1 #pragma once
2 
3 #include <ciso646>
4 #include <vector>
5 #include <set>
6 #include <sstream>
7 #include <iomanip>
8 #include <utility>
9 #include <simo/geom/geometry.hpp>
10 #include <simo/geom/linearring.hpp>
11 #include <simo/geom/detail/bounds.hpp>
12 
13 namespace simo
14 {
15 namespace shapes
16 {
17 
18 template <typename T, typename AllocatorType = std::allocator<T>>
19 class basic_polygon : public std::vector<T, AllocatorType>, public basic_geometry<basic_polygon<T>>
20 {
21  public:
22  using base_type = std::vector<T, AllocatorType>;
23 
24  using point_type = typename T::point_type;
25  using point_iterator = typename std::vector<T>::iterator;
26  using point_const_iterator = typename std::vector<T>::const_iterator;
27 
28  using coord_type = typename T::coord_type;
29  using coord_iterator = typename std::vector<coord_type>::iterator;
30  using coord_const_iterator = typename std::vector<coord_type>::const_iterator;
31 
33  : base_type() {}
34 
35  basic_polygon(point_iterator first, point_iterator last)
36  : base_type(first, last)
37  {
38  }
39 
40  basic_polygon(point_const_iterator first, point_const_iterator last)
41  : base_type(first, last)
42  {
43  }
44 
45  basic_polygon(std::initializer_list<T> init)
46  : base_type(init.begin(), init.end()) {}
47 
48 
49  explicit basic_polygon(coord_const_iterator first, coord_const_iterator last)
50  {
52  size_t n = this->ndim();
53  this->reserve(std::distance(first, last));
54  for (auto it = first; it != last; it += n)
55  {
56  this->emplace_back(it, it + n);
57  }
58  }
59 
60  explicit basic_polygon(coord_iterator first, coord_iterator last)
61  {
63  size_t n = this->ndim();
64  this->reserve(std::distance(first, last));
65  for (auto it = first; it != last; it += n)
66  {
67  this->emplace_back(it, it + n);
68  }
69  }
70 
71  template <typename CoordIterator, typename OffsetIterator>
72  basic_polygon(CoordIterator coord_first, CoordIterator coord_last, OffsetIterator offset_first, OffsetIterator offset_last)
73  {
74  if (std::distance(coord_first, coord_last) > 0)
75  {
76  auto n = this->ndim();
77  this->reserve((coord_last - coord_first) / n);
78  size_t lo = 0;
79  for (auto it = offset_first; it != offset_last; ++it)
80  {
81  size_t hi = *it;
82  this->emplace_back(coord_first + lo, coord_first + hi);
83  lo = hi;
84  }
85  }
86  }
87 
88  // operators
89 
90  friend bool operator==(const basic_polygon<T>& lhs, const basic_polygon<T>& rhs)
91  {
92  if (lhs.size() != rhs.size())
93  {
94  return false;
95  }
96  for (size_t i = 0; i < lhs.size(); ++i)
97  {
98  if (lhs[i] != rhs[i])
99  {
100  return false;
101  }
102  }
103  return true;
104  }
105 
106  friend bool operator!=(const basic_polygon<T>& lhs, const basic_polygon<T>& rhs)
107  {
108  return not operator==(lhs, rhs);
109  }
110 
111  std::vector<std::tuple<double, double>> xy() const
112  {
113  std::vector<std::tuple<double, double>> res;
114  res.reserve(this->size());
115  for (const auto& p : *this)
116  {
117  res.emplace_back(p.x, p.y);
118  }
119  return res;
120  }
121 
122  T& exterior()
123  {
124  return *this->begin();
125  }
126 
127  T& interiors(size_t pos)
128  {
129  assert(this->begin() + pos + 1 < this->end());
130  return *(this->begin() + pos + 1);
131  }
132 
133  private:
135  friend class basic_geometry<basic_polygon<T>>;
136 
138  geometry_type geom_type_() const noexcept
139  {
141  {
142  return geometry_type::POLYGONZ;
143  }
145  {
146  return geometry_type::POLYGONM;
147  }
149  {
150  return geometry_type::POLYGONZM;
151  }
152  return geometry_type::POLYGON;
153  }
154 
156  bool is_closed_() const noexcept
157  {
158  if (this->empty())
159  {
160  return true;
161  }
162  return *this[0] == *this[this->size() - 1];
163  }
164 
166  void throw_for_invalid_() const
167  {
169  }
170 
172  bounds_t bounds_() const
173  {
174  bounds_t res{};
175  for (const auto& r : *this)
176  {
177  auto r_bound = r.bounds();
178  res.extend(r_bound);
179  }
180  return res;
181  }
182 
184  std::string tagged_text_() const noexcept
185  {
186  return "Polygon";
187  }
188 
189  // json
190 
192  static basic_polygon<T> from_json_(const std::string& json)
193  {
194  try
195  {
196  auto j = nlohmann::json::parse(json);
197  auto geom_type = j.at("type").get<std::string>();
198  if (geom_type != "Polygon")
199  {
200  throw exceptions::parse_error("invalid geometry type: " + std::string(geom_type));
201  }
202  const auto& rings = j.at("coordinates");
203  std::vector<T> res;
204  res.reserve(rings.size());
205  std::vector<point_type> points;
206  for (const auto& ring : rings)
207  {
208  if (not ring.empty())
209  {
210  const auto& coords = ring.get<std::vector<std::vector<double>>>();
211  points.reserve(coords.size());
212  std::for_each(std::begin(coords), std::end(coords),
213  [&points](const std::vector<double>& coord) {
214  points.emplace_back(coord.begin(), coord.end());
215  });
216  res.emplace_back(points.begin(), points.end());
217  }
218  points.clear();
219  }
220  return basic_polygon<T>(res.begin(), res.end());
221  }
222  catch (const nlohmann::json::exception& e)
223  {
224  throw exceptions::parse_error("invalid json: " + std::string(e.what()));
225  }
226  catch (const exceptions::geometry_error& e)
227  {
228  throw exceptions::parse_error("invalid geometry: " + std::string(e.what()));
229  }
230  }
231 
233  std::string json_(std::int32_t precision = -1) const
234  {
235  std::stringstream ss;
236  if (precision >= 0)
237  {
238  ss << std::setprecision(precision);
239  }
240  ss << "{\"type\":\"Polygon\",\"coordinates\":[";
241  int i = 0;
242  for (const auto& ls : *this)
243  {
244  if (i > 0)
245  {
246  ss << ",";
247  }
248  ss << "[";
249  for (size_t j = 0; j < ls.size(); ++j)
250  {
251  if (j > 0)
252  {
253  ss << ",";
254  }
255  ss << "[";
256  const auto& p = ls[j];
257  for (size_t k = 0; k < p.size(); ++k)
258  {
259  if (k > 0)
260  {
261  ss << ",";
262  }
263  ss << p.coords[k];
264  }
265  ss << "]";
266  ++i;
267  }
268  ss << "]";
269  ++i;
270  }
271  ss << "]}";
272  return ss.str();
273  }
274 
275  // wkt
276 
278  static basic_polygon<T> from_wkt_(const std::string& wkt)
279  {
280  wkt_reader reader{};
281  auto result = reader.read(wkt);
282  const auto& data = result.data;
283  if (not utils::is_polygon(data.geom_type))
284  {
285  throw exceptions::parse_error("invalid wkt string");
286  }
287  return basic_polygon<T>(result.data.coords.begin(), result.data.coords.end(), result.data.offsets.begin(),
288  result.data.offsets.end());
289  }
290 
292  std::string wkt_(std::int32_t precision = -1) const
293  {
294  std::stringstream ss;
295  if (precision >= 0)
296  {
297  ss << std::setprecision(precision);
298  }
299  ss << "POLYGON";
300  if (this->has_z())
301  {
302  ss << "Z";
303  }
304  if (this->has_m())
305  {
306  ss << "M";
307  }
308  ss << "(";
309  int i = 0;
310  for (const auto& ls : *this)
311  {
312  if (i > 0)
313  {
314  ss << ",";
315  }
316  ss << "(";
317  for (size_t j = 0; j < ls.size(); ++j)
318  {
319  if (j > 0)
320  {
321  ss << ",";
322  }
323  const auto& p = ls[j];
324  for (size_t k = 0; k < p.size(); ++k)
325  {
326  if (k > 0)
327  {
328  ss << " ";
329  }
330  ss << p.coords[k];
331  }
332  }
333  ss << ")";
334  ++i;
335  }
336  ss << ")";
337  return ss.str();
338  }
339 };
340 
341 template <typename>
342 struct is_basic_polygon : std::false_type
343 {};
344 
345 template <typename T>
347 {};
348 
349 template <typename>
350 struct is_basic_polygon_z : std::false_type
351 {};
352 
353 template <typename T>
355 {};
356 
357 template <typename>
358 struct is_basic_polygon_m : std::false_type
359 {};
360 
361 template <typename T>
363 {};
364 
365 template <typename>
366 struct is_basic_polygon_zm : std::false_type
367 {};
368 
369 template <typename T>
371 {};
372 
373 } // namespace shapes
374 } // namespace simo
geometry_type geom_type() const noexcept
Returns the geometry type.
Definition: geometry.hpp:35
Base class for all geometries.
Definition: geometry.hpp:26
wkt_data data
the parser result data
Definition: wkt_parser.hpp:44
wkt_result read(const std::string &wkt)
parse the given wkt string
Definition: wkt_reader.hpp:47
Exception thrown when a geometry error is found.
Definition: exceptions.hpp:121
const char * what() const noexceptoverride
Returns the exception reason.
Definition: exceptions.hpp:39
Represents an axis-aligned bounding box.
Definition: bounds.hpp:18
bool has_z() const noexcept
Whether the geometry has the z-coordinate.
Definition: geometry.hpp:214
bounds_t & extend(double x, double y)
Extends the bounds to contain the given point.
Definition: bounds.hpp:69
Exception thrown when an error has been found while parsing.
Definition: exceptions.hpp:67
std::string json(std::int32_t precision=-1) const
Dumps the geojson representation of the geometry.
Definition: geometry.hpp:255
bool has_m() const noexcept
Whether the geometry has the m-coordinate (measurement coordinate)
Definition: geometry.hpp:226
std::string wkt(std::int32_t precision=-1) const
Dumps the wkt representation of the geometry.
Definition: geometry.hpp:283
basic_polygon(coord_const_iterator first, coord_const_iterator last)
Definition: polygon.hpp:49
size_t ndim() const noexcept
Returns the number of dimensions of the geometry.
Definition: geometry.hpp:142
basic_polygon(coord_iterator first, coord_iterator last)
Definition: polygon.hpp:60