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