Shapes
GIS made easy, a lightweight header-only planar geometry library for Modern C++
multilinestring.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_multilinestring<T>>
21 {
22  public:
23  using base_type = std::vector<T, AllocatorType>;
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  using coord_type = typename T::coord_type;
28  using coord_iterator = typename std::vector<coord_type>::iterator;
29  using coord_const_iterator = typename std::vector<coord_type>::const_iterator;
30 
32  : base_type() {}
33 
34  basic_multilinestring(point_iterator first, point_iterator last)
35  : base_type(first, last)
36  {
37  }
38 
39  basic_multilinestring(point_const_iterator first, point_const_iterator last)
40  : base_type(first, last)
41  {
42  }
43 
44  basic_multilinestring(std::initializer_list<T> init)
45  : base_type(init.begin(), init.end()) {}
46 
47  template <typename CoordIterator, typename OffsetIterator>
48  basic_multilinestring(CoordIterator coord_first, CoordIterator coord_last, OffsetIterator offset_first, OffsetIterator offset_last)
49  {
50  if (std::distance(coord_first, coord_last) > 0)
51  {
52  auto n = this->ndim();
53  this->reserve((coord_last - coord_first) / n);
54  size_t lo = 0;
55  for (auto it = offset_first; it != offset_last; ++it)
56  {
57  size_t hi = *it;
58  this->emplace_back(coord_first + lo, coord_first + hi);
59  lo = hi;
60  }
61  }
62  }
63 
64  // operators
65 
66  friend bool operator==(const basic_multilinestring<T>& lhs, const basic_multilinestring<T>& rhs)
67  {
68  if (lhs.size() != rhs.size())
69  {
70  return false;
71  }
72  for (size_t i = 0; i < lhs.size(); ++i)
73  {
74  if (lhs[i] != rhs[i])
75  {
76  return false;
77  }
78  }
79  return true;
80  }
81 
82  friend bool operator!=(const basic_multilinestring<T>& lhs, const basic_multilinestring<T>& rhs)
83  {
84  return not operator==(lhs, rhs);
85  }
86 
87  std::vector<std::tuple<double, double>> xy() const
88  {
89  std::vector<std::tuple<double, double>> res;
90  res.reserve(this->size());
91  for (const auto& p : *this)
92  {
93  res.emplace_back(p.x, p.y);
94  }
95  return res;
96  }
97 
98  private:
101 
103  geometry_type geom_type_() const noexcept
104  {
106  {
107  return geometry_type::MULTILINESTRINGZ;
108  }
110  {
111  return geometry_type::MULTILINESTRINGM;
112  }
114  {
115  return geometry_type::MULTILINESTRINGZM;
116  }
117  return geometry_type::MULTILINESTRING;
118  }
119 
121  bool is_closed_() const noexcept
122  {
123  if (this->empty())
124  {
125  return true;
126  }
127  return *this[0] == *this[this->size() - 1];
128  }
129 
131  void throw_for_invalid_() const
132  {
133  for (const auto& ls : *this)
134  {
135  ls.throw_for_invalid();
136  }
137  }
138 
140  bounds_t bounds_() const
141  {
142  bounds_t res{};
143  for (const auto& p : *this)
144  {
145  res.extend(p.x, p.y);
146  }
147  return res;
148  }
149 
150  // json
151 
153  static basic_multilinestring<T> from_json_(const std::string& json)
154  {
155  try
156  {
157  auto j = nlohmann::json::parse(json);
158  auto geom_type = j.at("type").get<std::string>();
159  if (geom_type != "MultiLineString")
160  {
161  throw exceptions::parse_error("invalid geometry type: " + std::string(geom_type));
162  }
163  const auto& linestrings = j.at("coordinates");
164  std::vector<T> res;
165  res.reserve(linestrings.size());
166  std::vector<point_type> points;
167  for (const auto& linestring : linestrings)
168  {
169  if (not linestring.empty())
170  {
171  const auto& coords = linestring.get<std::vector<std::vector<double>>>();
172  points.reserve(coords.size());
173  std::for_each(std::begin(coords), std::end(coords),
174  [&points](const std::vector<double>& coord) {
175  points.emplace_back(coord.begin(), coord.end());
176  });
177  res.emplace_back(points.begin(), points.end());
178  }
179  points.clear();
180  }
181  return basic_multilinestring<T>(res.begin(), res.end());
182  }
183  catch (const nlohmann::json::exception& e)
184  {
185  throw exceptions::parse_error("invalid json: " + std::string(e.what()));
186  }
187  catch (const exceptions::geometry_error& e)
188  {
189  throw exceptions::parse_error("invalid geometry: " + std::string(e.what()));
190  }
191  }
192 
194  std::string json_(std::int32_t precision = -1) const
195  {
196  std::stringstream ss;
197  if (precision >= 0)
198  {
199  ss << std::setprecision(precision);
200  }
201  ss << "{\"type\":\"MultiLineString\",\"coordinates\":[";
202  int i = 0;
203  for (const auto& ls : *this)
204  {
205  if (i > 0)
206  {
207  ss << ",";
208  }
209  ss << "[";
210  for (size_t j = 0; j < ls.size(); ++j)
211  {
212  if (j > 0)
213  {
214  ss << ",";
215  }
216  ss << "[";
217  const auto& p = ls[j];
218  for (size_t k = 0; k < p.size(); ++k)
219  {
220  if (k > 0)
221  {
222  ss << ",";
223  }
224  ss << p.coords[k];
225  }
226  ss << "]";
227  ++i;
228  }
229  ss << "]";
230  ++i;
231  }
232  ss << "]}";
233  return ss.str();
234  }
235 
236  // wkt
237 
239  static basic_multilinestring<T> from_wkt_(const std::string& wkt)
240  {
241  wkt_reader reader{};
242  auto result = reader.read(wkt);
243  const auto& data = result.data;
244  if (not utils::is_multilinestring(data.geom_type))
245  {
246  throw exceptions::parse_error("invalid wkt string");
247  }
248  return basic_multilinestring<T>(result.data.coords.begin(), result.data.coords.end(),
249  result.data.offsets.begin(), result.data.offsets.end());
250  }
251 
253  std::string wkt_(std::int32_t precision = -1) const
254  {
255  std::stringstream ss;
256  if (precision >= 0)
257  {
258  ss << std::setprecision(precision);
259  }
260  ss << "MULTILINESTRING";
261  if (this->has_z())
262  {
263  ss << "Z";
264  }
265  if (this->has_m())
266  {
267  ss << "M";
268  }
269  ss << "(";
270  int i = 0;
271  for (const auto& ls : *this)
272  {
273  if (i > 0)
274  {
275  ss << ",";
276  }
277  ss << "(";
278  for (size_t j = 0; j < ls.size(); ++j)
279  {
280  if (j > 0)
281  {
282  ss << ",";
283  }
284  const auto& p = ls[j];
285  for (size_t k = 0; k < p.size(); ++k)
286  {
287  if (k > 0)
288  {
289  ss << " ";
290  }
291  ss << p.coords[k];
292  }
293  }
294  ss << ")";
295  ++i;
296  }
297  ss << ")";
298  return ss.str();
299  }
300 };
301 
302 template <typename>
303 struct is_basic_multilinestring : std::false_type
304 {};
305 
306 template <typename T>
308 {};
309 
310 template <typename>
311 struct is_basic_multilinestring_z : std::false_type
312 {};
313 
314 template <typename T>
316 {};
317 
318 template <typename>
319 struct is_basic_multilinestring_m : std::false_type
320 {};
321 
322 template <typename T>
324 {};
325 
326 template <typename>
327 struct is_basic_multilinestring_zm : std::false_type
328 {};
329 
330 template <typename T>
332 {};
333 
334 } // namespace shapes
335 } // 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