Shapes
GIS made easy, a lightweight header-only planar geometry library for Modern C++
multipolygon.hpp
1 #pragma once
2 
3 #include <ciso646>
4 #include <vector>
5 #include <set>
6 #include <sstream>
7 #include <iterator>
8 #include <iomanip>
9 #include <simo/geom/detail/geometry.hpp>
10 #include <simo/geom/detail/bounds.hpp>
11 
12 namespace simo
13 {
14 namespace shapes
15 {
16 
17 template <typename T, typename AllocatorType = std::allocator<T>>
19  : public std::vector<T, AllocatorType>,
20  public basic_geometry<basic_multipolygon<T>>
21 {
22  public:
23  using base_type = std::vector<T, AllocatorType>;
24 
25  using polygon_type = T;
26  using polygon_iterator = typename std::vector<polygon_type>::iterator;
27  using polygon_const_iterator = typename std::vector<polygon_type>::const_iterator;
28 
29  using point_type = typename T::point_type;
30  using point_iterator = typename std::vector<point_type>::iterator;
31  using point_const_iterator = typename std::vector<point_type>::const_iterator;
32 
33  using coord_type = typename T::coord_type;
34  using coord_iterator = typename std::vector<coord_type>::iterator;
35  using coord_const_iterator = typename std::vector<coord_type>::const_iterator;
36 
38  : base_type() {}
39 
40  basic_multipolygon(polygon_iterator first, polygon_iterator last)
41  : base_type(first, last)
42  {
43  }
44 
45  basic_multipolygon(polygon_const_iterator first, polygon_const_iterator last)
46  : base_type(first, last)
47  {
48  }
49 
50  basic_multipolygon(std::initializer_list<T> init)
51  : base_type(init.begin(), init.end()) {}
52 
53  template <typename CoordIterator, typename OffsetIterator>
54  basic_multipolygon(CoordIterator coord_first, CoordIterator coord_last, OffsetIterator offset_first, OffsetIterator offset_last)
55  {
56  if (std::distance(coord_first, coord_last) > 0)
57  {
58  auto n = this->ndim();
59  this->reserve((coord_last - coord_first) / n);
60  size_t lo = 0;
61  for (auto it = offset_first; it != offset_last; ++it)
62  {
63  size_t hi = *it;
64  this->emplace_back(coord_first + lo, coord_first + hi);
65  lo = hi;
66  }
67  }
68  }
69 
70  // operators
71 
72  friend bool operator==(const basic_multipolygon<T>& lhs, const basic_multipolygon<T>& rhs)
73  {
74  if (lhs.size() != rhs.size())
75  {
76  return false;
77  }
78  for (size_t i = 0; i < lhs.size(); ++i)
79  {
80  if (lhs[i] != rhs[i])
81  {
82  return false;
83  }
84  }
85  return true;
86  }
87 
88  friend bool operator!=(const basic_multipolygon<T>& lhs, const basic_multipolygon<T>& rhs)
89  {
90  return not operator==(lhs, rhs);
91  }
92 
93  std::vector<std::tuple<double, double>> xy() const
94  {
95  std::vector<std::tuple<double, double>> res;
96  res.reserve(this->size());
97  for (const auto& p : *this)
98  {
99  res.emplace_back(p.x, p.y);
100  }
101  return res;
102  }
103 
104  private:
107 
109  geometry_type geom_type_() const noexcept
110  {
112  {
113  return geometry_type::MULTIPOLYGONZ;
114  }
116  {
117  return geometry_type::MULTIPOLYGONM;
118  }
120  {
121  return geometry_type::MULTIPOLYGONZM;
122  }
123  return geometry_type::MULTIPOLYGON;
124  }
125 
127  bool is_closed_() const noexcept
128  {
129  if (this->empty())
130  {
131  return true;
132  }
133  return *this[0] == *this[this->size() - 1];
134  }
135 
137  void throw_for_invalid_() const
138  {
139  for (const auto& ls : *this)
140  {
141  ls.throw_for_invalid();
142  }
143  }
144 
146  bounds_t bounds_() const
147  {
148  bounds_t res{};
149  for (const auto& p : *this)
150  {
151  res.extend(p.x, p.y);
152  }
153  return res;
154  }
155 
156  // json
157 
159  static basic_multipolygon<T> from_json_(const std::string& json)
160  {
161  try
162  {
163  auto j = nlohmann::json::parse(json);
164  auto geom_type = j.at("type").get<std::string>();
165  if (geom_type != "Multipolygon")
166  {
167  throw exceptions::parse_error("invalid geometry type: " + std::string(geom_type));
168  }
169  const auto& polygons = j.at("coordinates");
170  std::vector<T> res;
171  res.reserve(polygons.size());
172  std::vector<point_type> points;
173  for (const auto& polygon : polygons)
174  {
175  if (not polygon.empty())
176  {
177  const auto& coords = polygon.get<std::vector<std::vector<double>>>();
178  points.reserve(coords.size());
179  std::for_each(std::begin(coords), std::end(coords),
180  [&points](const std::vector<double>& coord) {
181  points.emplace_back(coord.begin(), coord.end());
182  });
183  res.emplace_back(points.begin(), points.end());
184  }
185  points.clear();
186  }
187  return basic_multipolygon<T>(res.begin(), res.end());
188  }
189  catch (const nlohmann::json::exception& e)
190  {
191  throw exceptions::parse_error("invalid json: " + std::string(e.what()));
192  }
193  catch (const exceptions::geometry_error& e)
194  {
195  throw exceptions::parse_error("invalid geometry: " + std::string(e.what()));
196  }
197  }
198 
200  std::string json_(std::int32_t precision = -1) const
201  {
202  std::stringstream ss;
203  if (precision >= 0)
204  {
205  ss << std::setprecision(precision);
206  }
207  ss << "{\"type\":\"Multipolygon\",\"coordinates\":[";
208  for (size_t polygon_index = 0; polygon_index < this->size(); ++polygon_index)
209  {
210  const auto& pg = (*this)[polygon_index];
211  if (polygon_index > 0)
212  {
213  ss << ",";
214  }
215  ss << "[";
216  for (size_t ring_index = 0; ring_index < pg.size(); ++ring_index)
217  {
218  const auto& ring = pg[ring_index];
219  if (ring_index > 0)
220  {
221  ss << ",";
222  }
223  ss << "[";
224  for (size_t point_index = 0; point_index < ring.size(); ++point_index)
225  {
226  const auto& p = ring[point_index];
227  if (point_index > 0)
228  {
229  ss << ",";
230  }
231  ss << "[";
232  for (size_t coord_index = 0; coord_index < p.size(); ++coord_index)
233  {
234  if (coord_index > 0)
235  {
236  ss << ",";
237  }
238  ss << p.coords[coord_index];
239  }
240  ss << "]";
241  }
242  ss << "]";
243  }
244  ss << "]";
245  }
246  ss << "]}";
247  return ss.str();
248  }
249 
250  // wkt
251 
253  static basic_multipolygon<T> from_wkt_(const std::string& wkt)
254  {
255  wkt_reader reader{};
256  auto result = reader.read(wkt);
257  const auto& data = result.data;
258  if (not utils::is_multipolygon(data.geom_type))
259  {
260  throw exceptions::parse_error("invalid wkt string");
261  }
262  return basic_multipolygon<T>(data.coords.begin(), data.coords.end(), data.offsets.begin(), data.offsets.end());
263  }
264 
266  std::string wkt_(std::int32_t precision = -1) const
267  {
268  std::stringstream ss;
269  if (precision >= 0)
270  {
271  ss << std::setprecision(precision);
272  }
273  ss << "MULTIPOLYGON";
274  if (this->has_z())
275  {
276  ss << "Z";
277  }
278  if (this->has_m())
279  {
280  ss << "M";
281  }
282  size_t polygon_index = 0;
283  ss << "(";
284  for (const auto& pg : *this)
285  {
286  if (polygon_index > 0)
287  {
288  ss << ",";
289  }
290  ss << "(";
291  size_t ring_index = 0;
292  for (const auto& ring : pg)
293  {
294  if (ring_index > 0)
295  {
296  ss << ",";
297  }
298  ss << "(";
299  size_t point_index = 0;
300  for (const auto& p : ring)
301  {
302  if (point_index > 0)
303  {
304  ss << ",";
305  }
306  for (size_t k = 0; k < p.ndim(); ++k)
307  {
308  if (k > 0)
309  {
310  ss << " ";
311  }
312  ss << p.coords[k];
313  }
314  ++point_index;
315  }
316  ss << ")";
317  ++ring_index;
318  }
319  ss << ")";
320  ++polygon_index;
321  }
322  ss << ")";
323  return ss.str();
324  }
325 };
326 
327 template <typename>
328 struct is_basic_multipolygon : std::false_type
329 {};
330 
331 template <typename T>
333 {};
334 
335 template <typename>
336 struct is_basic_multipolygon_z : std::false_type
337 {};
338 
339 template <typename T>
341 {};
342 
343 template <typename>
344 struct is_basic_multipolygon_m : std::false_type
345 {};
346 
347 template <typename T>
349 {};
350 
351 template <typename>
352 struct is_basic_multipolygon_zm : std::false_type
353 {};
354 
355 template <typename T>
357 {};
358 
359 } // namespace shapes
360 } // 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
size_t ndim() const noexcept
Returns the number of dimensions of the geometry.
Definition: geometry.hpp:142