Shapes
GIS made easy, a lightweight header-only planar geometry library for Modern C++
multipoint.hpp
1 #pragma once
2 
3 #include <ciso646>
4 #include <vector>
5 #include <set>
6 #include <sstream>
7 #include <iomanip>
8 #include <simo/geom/detail/geometry.hpp>
9 #include <simo/geom/detail/bounds.hpp>
10 
11 namespace simo
12 {
13 namespace shapes
14 {
15 
16 template <typename T, typename AllocatorType = std::allocator<T>>
17 class basic_multipoint : public std::vector<T, AllocatorType>, public basic_geometry<basic_multipoint<T>>
18 {
19  public:
20  using base_type = std::vector<T, AllocatorType>;
21 
22  using point_type = typename T::point_type;
23  using point_iterator = typename std::vector<T>::iterator;
24  using point_const_iterator = typename std::vector<T>::const_iterator;
25 
26  using coord_type = typename T::coord_type;
27  using coord_iterator = typename std::vector<coord_type>::iterator;
28  using coord_const_iterator = typename std::vector<coord_type>::const_iterator;
29 
30  inline basic_multipoint()
31  : base_type() {}
32 
33  basic_multipoint(std::initializer_list<T> init)
34  : base_type(init.begin(), init.end()) {}
35 
36  explicit basic_multipoint(coord_const_iterator first, coord_const_iterator last)
37  {
39  size_t n = this->ndim();
40  this->reserve(std::distance(first, last));
41  for (auto it = first; it != last; it += n)
42  {
43  this->emplace_back(it, it + n);
44  }
45  }
46 
47  explicit basic_multipoint(coord_iterator first, coord_iterator last)
48  {
50  size_t n = this->ndim();
51  this->reserve(std::distance(first, last));
52  for (auto it = first; it != last; it += n)
53  {
54  this->emplace_back(it, it + n);
55  }
56  }
57 
58  basic_multipoint(point_iterator first, point_iterator last)
59  : base_type(first, last)
60  {
61  }
62 
63  basic_multipoint(point_const_iterator first, point_const_iterator last)
64  : base_type(first, last)
65  {
66  }
67 
68  // operators
69 
77  friend bool operator==(const basic_multipoint<T>& lhs, const basic_multipoint<T>& rhs)
78  {
79  if (lhs.size() != rhs.size())
80  {
81  return false;
82  }
83  for (size_t i = 0; i < lhs.size(); ++i)
84  {
85  if (lhs[i] != rhs[i])
86  {
87  return false;
88  }
89  }
90  return true;
91  }
92 
100  friend bool operator!=(const basic_multipoint<T>& lhs, const basic_multipoint<T>& rhs)
101  {
102  return not operator==(lhs, rhs);
103  }
104 
105  std::vector<std::tuple<double, double>> xy() const
106  {
107  std::vector<std::tuple<double, double>> res;
108  res.reserve(this->size());
109  for (const auto& p : *this)
110  {
111  res.emplace_back(p.x, p.y);
112  }
113  return res;
114  }
115 
116  // polyline
117 
118  static basic_multipoint<T> from_polyline(const std::string& polyline, std::int32_t precision = 5)
119  {
120  static_assert(is_basic_point<T>::value, "must contain XY points");
121 
122  auto coords = polyline::decode(polyline, precision);
123  return {coords.begin(), coords.end()};
124  }
125 
126  std::string polyline(std::int32_t precision = 5) const
127  {
128  static_assert(is_basic_point<T>::value, "must contain XY points");
129 
130  std::string res;
131  res.reserve(this->size() * 6);
132  double prev_lng = 0;
133  double prev_lat = 0;
134  for (const auto& p : *this)
135  {
136  res += polyline::encode(p.lat - prev_lat, precision);
137  res += polyline::encode(p.lng - prev_lng, precision);
138  prev_lat = p.lat;
139  prev_lng = p.lng;
140  }
141  return res;
142  }
143 
144  private:
147 
149  geometry_type geom_type_() const noexcept
150  {
152  {
153  return geometry_type::MULTIPOINTZ;
154  }
156  {
157  return geometry_type::MULTIPOINTM;
158  }
160  {
161  return geometry_type::MULTIPOINTZM;
162  }
163  return geometry_type::MULTIPOINT;
164  }
165 
167  bool is_closed_() const noexcept
168  {
169  if (this->empty())
170  {
171  return true;
172  }
173  return *this[0] == *this[this->size() - 1];
174  }
175 
177  void throw_for_invalid_() const
178  {
179  // do nothing
180  }
181 
183  bounds_t bounds_() const
184  {
185  bounds_t res{};
186  for (const auto& p : *this)
187  {
188  res.extend(p.x, p.y);
189  }
190  return res;
191  }
192 
193  // json
194 
196  static basic_multipoint<T> from_json_(const std::string& json)
197  {
198  try
199  {
200  auto j = nlohmann::json::parse(json);
201  auto geom_type = j.at("type").get<std::string>();
202  if (geom_type != "MultiPoint")
203  {
204  throw exceptions::parse_error("invalid geometry type: " + std::string(geom_type));
205  }
206  const auto& coords = j.at("coordinates").get<std::vector<std::vector<double>>>();
207  std::vector<point_type> res;
208  res.reserve(coords.size());
209  std::for_each(std::begin(coords), std::end(coords), [&](const std::vector<double>& coord) {
210  res.emplace_back(coord.begin(), coord.end());
211  });
212  return basic_multipoint<T>(res.begin(), res.end());
213  }
214  catch (const nlohmann::json::exception& e)
215  {
216  throw exceptions::parse_error("invalid json: " + std::string(e.what()));
217  }
218  catch (const exceptions::geometry_error& e)
219  {
220  throw exceptions::parse_error("invalid geometry: " + std::string(e.what()));
221  }
222  }
223 
225  std::string json_(std::int32_t precision = -1) const
226  {
227  std::stringstream ss;
228  if (precision >= 0)
229  {
230  ss << std::setprecision(precision);
231  }
232  ss << "{\"type\":\"MultiPoint\",\"coordinates\":[";
233  int i = 0;
234  for (const auto& p : *this)
235  {
236  if (i > 0)
237  {
238  ss << ",";
239  }
240  ss << "[";
241  for (size_t j = 0; j < p.size(); ++j)
242  {
243  if (j > 0)
244  {
245  ss << ",";
246  }
247  ss << p.coords[j];
248  }
249  ss << "]";
250  ++i;
251  }
252  ss << "]}";
253  return ss.str();
254  }
255 
256  // wkt
257 
259  static basic_multipoint<T> from_wkt_(const std::string& wkt)
260  {
261  wkt_reader reader{};
262  auto result = reader.read(wkt);
263  const auto& data = result.data;
264  if (not utils::is_multipoint(data.geom_type))
265  {
266  throw exceptions::parse_error("invalid wkt string");
267  }
268  return basic_multipoint<T>(result.data.coords.begin(), result.data.coords.end());
269  }
270 
272  std::string wkt_(std::int32_t precision = -1) const
273  {
274  std::stringstream ss;
275  if (precision >= 0)
276  {
277  ss << std::setprecision(precision);
278  }
279  ss << "MULTIPOINT";
280  if (this->has_z())
281  {
282  ss << "Z";
283  }
284  if (this->has_m())
285  {
286  ss << "M";
287  }
288  ss << "(";
289  int i = 0;
290  for (const auto& p : *this)
291  {
292  if (i > 0)
293  {
294  ss << ",";
295  }
296  ss << "(";
297  for (size_t j = 0; j < p.size(); ++j)
298  {
299  if (j > 0)
300  {
301  ss << " ";
302  }
303  ss << p.coords[j];
304  }
305  ss << ")";
306  ++i;
307  }
308  ss << ")";
309  return ss.str();
310  }
311 };
312 
313 template <typename>
314 struct is_basic_multipoint : std::false_type
315 {};
316 
317 template <typename T>
319 {};
320 
321 template <typename>
322 struct is_basic_multipoint_z : std::false_type
323 {};
324 
325 template <typename T>
327 {};
328 
329 template <typename>
330 struct is_basic_multipoint_m : std::false_type
331 {};
332 
333 template <typename T>
335 {};
336 
337 template <typename>
338 struct is_basic_multipoint_zm : std::false_type
339 {};
340 
341 template <typename T>
343 {};
344 
345 } // namespace shapes
346 } // 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
basic_multipoint(coord_const_iterator first, coord_const_iterator last)
Definition: multipoint.hpp:36
friend bool operator!=(const basic_multipoint< T > &lhs, const basic_multipoint< T > &rhs)
Definition: multipoint.hpp:100
const char * what() const noexceptoverride
Returns the exception reason.
Definition: exceptions.hpp:39
Represents an axis-aligned bounding box.
Definition: bounds.hpp:18
friend bool operator==(const basic_multipoint< T > &lhs, const basic_multipoint< T > &rhs)
Definition: multipoint.hpp:77
bool has_z() const noexcept
Whether the geometry has the z-coordinate.
Definition: geometry.hpp:214
basic_multipoint(coord_iterator first, coord_iterator last)
Definition: multipoint.hpp:47
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