1 #pragma once
2 
3 #include <algorithm>
4 #include <cmath>
5 #include <mapbox/geojsonvt/types.hpp>
6 
7 namespace mapbox {
8 namespace geojsonvt {
9 
10 struct Tile {
11     mapbox::geometry::feature_collection<int16_t> features;
12     uint32_t num_points = 0;
13     uint32_t num_simplified = 0;
14 };
15 
16 namespace detail {
17 
18 class InternalTile {
19 public:
20     const uint16_t extent;
21     const uint8_t z;
22     const uint32_t x;
23     const uint32_t y;
24 
25     const double z2;
26     const double tolerance;
27     const double sq_tolerance;
28 
29     vt_features source_features;
30     mapbox::geometry::box<double> bbox = { { 2, 1 }, { -1, 0 } };
31 
32     Tile tile;
33 
InternalTile(const vt_features & source,const uint8_t z_,const uint32_t x_,const uint32_t y_,const uint16_t extent_,const double tolerance_)34     InternalTile(const vt_features& source,
35                  const uint8_t z_,
36                  const uint32_t x_,
37                  const uint32_t y_,
38                  const uint16_t extent_,
39                  const double tolerance_)
40         : extent(extent_),
41           z(z_),
42           x(x_),
43           y(y_),
44           z2(std::pow(2, z)),
45           tolerance(tolerance_),
46           sq_tolerance(tolerance_ * tolerance_) {
47 
48         for (const auto& feature : source) {
49             const auto& geom = feature.geometry;
50             const auto& props = feature.properties;
51             const auto& id = feature.id;
52 
53             tile.num_points += feature.num_points;
54 
55             vt_geometry::visit(geom, [&](const auto& g) {
56                 // `this->` is a workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61636
57                 this->addFeature(g, props, id);
58             });
59 
60             bbox.min.x = std::min(feature.bbox.min.x, bbox.min.x);
61             bbox.min.y = std::min(feature.bbox.min.y, bbox.min.y);
62             bbox.max.x = std::max(feature.bbox.max.x, bbox.max.x);
63             bbox.max.y = std::max(feature.bbox.max.y, bbox.max.y);
64         }
65     }
66 
67 private:
68     void
addFeature(const vt_point & point,const property_map & props,const optional<identifier> & id)69     addFeature(const vt_point& point, const property_map& props, const optional<identifier>& id) {
70         tile.features.push_back({ transform(point), props, id });
71     }
72 
addFeature(const vt_line_string & line,const property_map & props,const optional<identifier> & id)73     void addFeature(const vt_line_string& line,
74                     const property_map& props,
75                     const optional<identifier>& id) {
76         const auto new_line = transform(line);
77         if (!new_line.empty())
78             tile.features.push_back({ std::move(new_line), props, id });
79     }
80 
addFeature(const vt_polygon & polygon,const property_map & props,const optional<identifier> & id)81     void addFeature(const vt_polygon& polygon,
82                     const property_map& props,
83                     const optional<identifier>& id) {
84         const auto new_polygon = transform(polygon);
85         if (!new_polygon.empty())
86             tile.features.push_back({ std::move(new_polygon), props, id });
87     }
88 
addFeature(const vt_geometry_collection & collection,const property_map & props,const optional<identifier> & id)89     void addFeature(const vt_geometry_collection& collection,
90                     const property_map& props,
91                     const optional<identifier>& id) {
92         for (const auto& geom : collection) {
93             vt_geometry::visit(geom, [&](const auto& g) {
94                 // `this->` is a workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61636
95                 this->addFeature(g, props, id);
96             });
97         }
98     }
99 
100     template <class T>
addFeature(const T & multi,const property_map & props,const optional<identifier> & id)101     void addFeature(const T& multi, const property_map& props, const optional<identifier>& id) {
102         const auto new_multi = transform(multi);
103 
104         switch (new_multi.size()) {
105         case 0:
106             break;
107         case 1:
108             tile.features.push_back({ std::move(new_multi[0]), props, id });
109             break;
110         default:
111             tile.features.push_back({ std::move(new_multi), props, id });
112             break;
113         }
114     }
115 
transform(const vt_point & p)116     mapbox::geometry::point<int16_t> transform(const vt_point& p) {
117         ++tile.num_simplified;
118         return { static_cast<int16_t>(::round((p.x * z2 - x) * extent)),
119                  static_cast<int16_t>(::round((p.y * z2 - y) * extent)) };
120     }
121 
transform(const vt_multi_point & points)122     mapbox::geometry::multi_point<int16_t> transform(const vt_multi_point& points) {
123         mapbox::geometry::multi_point<int16_t> result;
124         result.reserve(points.size());
125         for (const auto& p : points) {
126             result.push_back(transform(p));
127         }
128         return result;
129     }
130 
transform(const vt_line_string & line)131     mapbox::geometry::line_string<int16_t> transform(const vt_line_string& line) {
132         mapbox::geometry::line_string<int16_t> result;
133         if (line.dist > tolerance) {
134             for (const auto& p : line) {
135                 if (p.z > sq_tolerance)
136                     result.push_back(transform(p));
137             }
138         }
139         return result;
140     }
141 
transform(const vt_linear_ring & ring)142     mapbox::geometry::linear_ring<int16_t> transform(const vt_linear_ring& ring) {
143         mapbox::geometry::linear_ring<int16_t> result;
144         if (ring.area > sq_tolerance) {
145             for (const auto& p : ring) {
146                 if (p.z > sq_tolerance)
147                     result.push_back(transform(p));
148             }
149         }
150         return result;
151     }
152 
transform(const vt_multi_line_string & lines)153     mapbox::geometry::multi_line_string<int16_t> transform(const vt_multi_line_string& lines) {
154         mapbox::geometry::multi_line_string<int16_t> result;
155         for (const auto& line : lines) {
156             if (line.dist > tolerance)
157                 result.push_back(transform(line));
158         }
159         return result;
160     }
161 
transform(const vt_polygon & rings)162     mapbox::geometry::polygon<int16_t> transform(const vt_polygon& rings) {
163         mapbox::geometry::polygon<int16_t> result;
164         for (const auto& ring : rings) {
165             if (ring.area > sq_tolerance)
166                 result.push_back(transform(ring));
167         }
168         return result;
169     }
170 
transform(const vt_multi_polygon & polygons)171     mapbox::geometry::multi_polygon<int16_t> transform(const vt_multi_polygon& polygons) {
172         mapbox::geometry::multi_polygon<int16_t> result;
173         for (const auto& polygon : polygons) {
174             const auto p = transform(polygon);
175             if (!p.empty())
176                 result.push_back(std::move(p));
177         }
178         return result;
179     }
180 };
181 
182 } // namespace detail
183 } // namespace geojsonvt
184 } // namespace mapbox
185