From 90a7cf87a32f6787e57e3852ac9d3015d8112621 Mon Sep 17 00:00:00 2001 From: "Thiago Marcos P. Santos" Date: Wed, 18 Oct 2017 13:11:31 -0700 Subject: Bump Mapbox GL Native mapbox-gl-native @ 10f7af19ce1ec61f37459f9cd75e2a0c89a0c790 --- deps/geojson/0.4.0/include/mapbox/geojson.hpp | 42 -- .../0.4.0/include/mapbox/geojson/rapidjson.hpp | 32 -- deps/geojson/0.4.0/include/mapbox/geojson_impl.hpp | 447 ---------------- deps/geojson/0.4.2/include/mapbox/geojson.hpp | 42 ++ .../0.4.2/include/mapbox/geojson/rapidjson.hpp | 32 ++ deps/geojson/0.4.2/include/mapbox/geojson_impl.hpp | 453 ++++++++++++++++ deps/geojsonvt/6.2.1/include/mapbox/geojsonvt.hpp | 244 --------- .../6.2.1/include/mapbox/geojsonvt/clip.hpp | 251 --------- .../6.2.1/include/mapbox/geojsonvt/convert.hpp | 110 ---- .../6.2.1/include/mapbox/geojsonvt/simplify.hpp | 72 --- .../6.2.1/include/mapbox/geojsonvt/tile.hpp | 207 -------- .../6.2.1/include/mapbox/geojsonvt/types.hpp | 156 ------ .../6.2.1/include/mapbox/geojsonvt/wrap.hpp | 46 -- deps/geojsonvt/6.3.0/include/mapbox/geojsonvt.hpp | 271 ++++++++++ .../6.3.0/include/mapbox/geojsonvt/clip.hpp | 251 +++++++++ .../6.3.0/include/mapbox/geojsonvt/convert.hpp | 110 ++++ .../6.3.0/include/mapbox/geojsonvt/simplify.hpp | 72 +++ .../6.3.0/include/mapbox/geojsonvt/tile.hpp | 207 ++++++++ .../6.3.0/include/mapbox/geojsonvt/types.hpp | 156 ++++++ .../6.3.0/include/mapbox/geojsonvt/wrap.hpp | 46 ++ include/mbgl/actor/message.hpp | 26 + include/mbgl/map/map.hpp | 19 +- include/mbgl/renderer/renderer.hpp | 2 + include/mbgl/renderer/renderer_backend.hpp | 2 + include/mbgl/storage/default_file_source.hpp | 3 +- include/mbgl/storage/file_source.hpp | 6 +- include/mbgl/storage/offline.hpp | 6 +- include/mbgl/storage/online_file_source.hpp | 3 + include/mbgl/storage/resource.hpp | 43 +- include/mbgl/storage/response.hpp | 1 - include/mbgl/style/conversion/geojson_options.hpp | 10 + .../style/conversion/make_property_setters.hpp | 7 +- include/mbgl/style/layers/custom_layer.hpp | 16 + include/mbgl/style/layers/symbol_layer.hpp | 22 +- include/mbgl/style/sources/geojson_source.hpp | 1 + include/mbgl/style/types.hpp | 8 +- include/mbgl/tile/tile_id.hpp | 263 +++++++++ include/mbgl/tile/tile_necessity.hpp | 15 + include/mbgl/util/constants.hpp | 1 - include/mbgl/util/geometry.hpp | 6 + include/mbgl/util/image.hpp | 26 + include/mbgl/util/projection.hpp | 22 +- include/mbgl/util/thread.hpp | 161 ++++++ mapbox-gl-native.pro | 20 +- platform/default/default_file_source.cpp | 93 ++-- platform/default/file_source_request.cpp | 37 ++ platform/default/mbgl/gl/headless_backend.cpp | 4 + platform/default/mbgl/gl/headless_backend.hpp | 1 + platform/default/mbgl/gl/headless_frontend.cpp | 8 +- platform/default/mbgl/gl/headless_frontend.hpp | 5 +- platform/default/mbgl/map/map_snapshotter.cpp | 76 +++ platform/default/mbgl/map/map_snapshotter.hpp | 40 ++ .../default/mbgl/storage/file_source_request.hpp | 31 ++ platform/default/mbgl/storage/offline.cpp | 35 +- platform/default/mbgl/storage/offline_download.cpp | 4 +- platform/default/mbgl/storage/offline_download.hpp | 2 +- platform/default/online_file_source.cpp | 31 +- platform/qt/include/qmapboxgl.hpp | 6 + platform/qt/src/qmapboxgl.cpp | 42 +- platform/qt/src/qmapboxgl_p.hpp | 9 +- src/mbgl/algorithm/generate_clip_ids_impl.hpp | 2 +- src/mbgl/algorithm/update_renderables.hpp | 25 +- src/mbgl/annotation/annotation_manager.cpp | 38 +- src/mbgl/annotation/annotation_manager.hpp | 11 +- src/mbgl/annotation/annotation_tile.cpp | 3 - src/mbgl/annotation/annotation_tile.hpp | 2 - src/mbgl/annotation/render_annotation_source.cpp | 4 +- src/mbgl/annotation/render_annotation_source.hpp | 4 +- src/mbgl/geometry/feature_index.cpp | 97 ++-- src/mbgl/geometry/feature_index.hpp | 9 +- src/mbgl/gl/context.cpp | 21 +- src/mbgl/gl/context.hpp | 8 +- src/mbgl/gl/renderbuffer.hpp | 16 +- src/mbgl/gl/value.cpp | 6 +- src/mbgl/gl/value.hpp | 2 +- src/mbgl/layout/symbol_layout.cpp | 5 +- src/mbgl/layout/symbol_projection.cpp | 72 ++- src/mbgl/map/map.cpp | 161 ++++-- src/mbgl/map/transform.cpp | 26 + src/mbgl/map/transform.hpp | 8 + src/mbgl/map/transform_state.cpp | 11 +- src/mbgl/map/transform_state.hpp | 5 +- src/mbgl/map/zoom_history.hpp | 2 +- src/mbgl/programs/attributes.hpp | 2 +- src/mbgl/programs/line_program.cpp | 2 +- src/mbgl/programs/line_program.hpp | 3 +- src/mbgl/renderer/image_manager.cpp | 21 +- src/mbgl/renderer/image_manager.hpp | 8 +- src/mbgl/renderer/layers/render_custom_layer.cpp | 8 +- src/mbgl/renderer/layers/render_custom_layer.hpp | 5 + .../layers/render_fill_extrusion_layer.cpp | 194 ++++--- .../layers/render_fill_extrusion_layer.hpp | 4 + src/mbgl/renderer/layers/render_fill_layer.cpp | 4 +- src/mbgl/renderer/paint_parameters.cpp | 17 +- src/mbgl/renderer/paint_parameters.hpp | 8 +- src/mbgl/renderer/render_pass.hpp | 1 + src/mbgl/renderer/render_source.hpp | 3 +- src/mbgl/renderer/render_static_data.hpp | 5 +- src/mbgl/renderer/render_tile.hpp | 1 + src/mbgl/renderer/renderer.cpp | 12 +- src/mbgl/renderer/renderer_impl.cpp | 586 +++++++++++++++++---- src/mbgl/renderer/renderer_impl.hpp | 70 ++- .../renderer/sources/render_geojson_source.cpp | 24 +- .../renderer/sources/render_geojson_source.hpp | 4 +- src/mbgl/renderer/sources/render_image_source.cpp | 2 +- src/mbgl/renderer/sources/render_image_source.hpp | 4 +- src/mbgl/renderer/sources/render_raster_source.cpp | 2 +- src/mbgl/renderer/sources/render_raster_source.hpp | 4 +- src/mbgl/renderer/sources/render_vector_source.cpp | 4 +- src/mbgl/renderer/sources/render_vector_source.hpp | 4 +- src/mbgl/renderer/tile_pyramid.cpp | 47 +- src/mbgl/renderer/tile_pyramid.hpp | 8 +- src/mbgl/renderer/update_parameters.hpp | 4 - src/mbgl/shaders/line.cpp | 9 +- src/mbgl/shaders/line_pattern.cpp | 9 +- src/mbgl/shaders/line_sdf.cpp | 9 +- src/mbgl/storage/resource.cpp | 22 +- src/mbgl/style/function/categorical_stops.cpp | 2 +- src/mbgl/style/function/identity_stops.cpp | 4 +- src/mbgl/style/image_impl.hpp | 1 + src/mbgl/style/layers/custom_layer.cpp | 11 +- src/mbgl/style/layers/custom_layer_impl.cpp | 2 + src/mbgl/style/layers/custom_layer_impl.hpp | 2 + src/mbgl/style/layers/symbol_layer.cpp | 34 +- src/mbgl/style/layers/symbol_layer_properties.hpp | 14 +- src/mbgl/style/observer.hpp | 3 +- src/mbgl/style/sources/geojson_source_impl.cpp | 2 +- src/mbgl/style/sources/image_source.cpp | 2 +- src/mbgl/style/style_impl.cpp | 17 +- src/mbgl/style/types.cpp | 20 +- src/mbgl/text/glyph_manager.cpp | 11 +- src/mbgl/text/glyph_manager.hpp | 2 +- src/mbgl/text/placement_config.hpp | 6 +- src/mbgl/text/shaping.cpp | 106 ++-- src/mbgl/text/shaping.hpp | 7 +- src/mbgl/tile/geojson_tile.cpp | 97 +--- src/mbgl/tile/geojson_tile.hpp | 2 - src/mbgl/tile/geojson_tile_data.hpp | 94 ++++ src/mbgl/tile/geometry_tile.cpp | 61 ++- src/mbgl/tile/geometry_tile.hpp | 31 +- src/mbgl/tile/geometry_tile_worker.cpp | 30 +- src/mbgl/tile/geometry_tile_worker.hpp | 3 +- src/mbgl/tile/raster_tile.cpp | 26 +- src/mbgl/tile/raster_tile.hpp | 13 +- src/mbgl/tile/raster_tile_worker.cpp | 8 +- src/mbgl/tile/raster_tile_worker.hpp | 2 +- src/mbgl/tile/tile.cpp | 4 +- src/mbgl/tile/tile.hpp | 18 +- src/mbgl/tile/tile_id_hash.cpp | 29 + src/mbgl/tile/tile_loader.hpp | 12 +- src/mbgl/tile/tile_loader_impl.hpp | 61 ++- src/mbgl/tile/vector_tile.cpp | 8 +- src/mbgl/tile/vector_tile.hpp | 7 +- src/mbgl/util/constants.cpp | 2 - src/mbgl/util/http_timeout.cpp | 2 + src/mbgl/util/i18n.cpp | 2 +- src/mbgl/util/mapbox.cpp | 6 +- src/mbgl/util/mapbox.hpp | 4 +- src/mbgl/util/math.hpp | 4 +- src/mbgl/util/offscreen_texture.cpp | 32 +- src/mbgl/util/offscreen_texture.hpp | 11 +- src/mbgl/util/tile_cover.cpp | 25 +- src/mbgl/util/tile_cover.hpp | 5 +- 163 files changed, 4242 insertions(+), 2611 deletions(-) delete mode 100644 deps/geojson/0.4.0/include/mapbox/geojson.hpp delete mode 100644 deps/geojson/0.4.0/include/mapbox/geojson/rapidjson.hpp delete mode 100644 deps/geojson/0.4.0/include/mapbox/geojson_impl.hpp create mode 100644 deps/geojson/0.4.2/include/mapbox/geojson.hpp create mode 100644 deps/geojson/0.4.2/include/mapbox/geojson/rapidjson.hpp create mode 100644 deps/geojson/0.4.2/include/mapbox/geojson_impl.hpp delete mode 100644 deps/geojsonvt/6.2.1/include/mapbox/geojsonvt.hpp delete mode 100644 deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/clip.hpp delete mode 100644 deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/convert.hpp delete mode 100644 deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/simplify.hpp delete mode 100644 deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/tile.hpp delete mode 100644 deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/types.hpp delete mode 100644 deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/wrap.hpp create mode 100644 deps/geojsonvt/6.3.0/include/mapbox/geojsonvt.hpp create mode 100644 deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/clip.hpp create mode 100644 deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/convert.hpp create mode 100644 deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/simplify.hpp create mode 100644 deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/tile.hpp create mode 100644 deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/types.hpp create mode 100644 deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/wrap.hpp create mode 100644 include/mbgl/tile/tile_id.hpp create mode 100644 include/mbgl/tile/tile_necessity.hpp create mode 100644 include/mbgl/util/thread.hpp create mode 100644 platform/default/file_source_request.cpp create mode 100644 platform/default/mbgl/map/map_snapshotter.cpp create mode 100644 platform/default/mbgl/map/map_snapshotter.hpp create mode 100644 platform/default/mbgl/storage/file_source_request.hpp create mode 100644 src/mbgl/tile/geojson_tile_data.hpp create mode 100644 src/mbgl/tile/tile_id_hash.cpp diff --git a/deps/geojson/0.4.0/include/mapbox/geojson.hpp b/deps/geojson/0.4.0/include/mapbox/geojson.hpp deleted file mode 100644 index 4b62751e15..0000000000 --- a/deps/geojson/0.4.0/include/mapbox/geojson.hpp +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include -#include - -namespace mapbox { -namespace geojson { - -using value = mapbox::geometry::value; -using null_value_t = mapbox::geometry::null_value_t; -using identifier = mapbox::geometry::identifier; -using point = mapbox::geometry::point; -using multi_point = mapbox::geometry::multi_point; -using line_string = mapbox::geometry::line_string; -using linear_ring = mapbox::geometry::linear_ring; -using multi_line_string = mapbox::geometry::multi_line_string; -using polygon = mapbox::geometry::polygon; -using multi_polygon = mapbox::geometry::multi_polygon; -using geometry = mapbox::geometry::geometry; -using geometry_collection = mapbox::geometry::geometry_collection; -using feature = mapbox::geometry::feature; -using feature_collection = mapbox::geometry::feature_collection; - -// Parse inputs of known types. Instantiations are provided for geometry, feature, and -// feature_collection. -template -T parse(const std::string &); - -// Parse any GeoJSON type. -using geojson = mapbox::util::variant; -geojson parse(const std::string &); - -// Stringify inputs of known types. Instantiations are provided for geometry, feature, and -// feature_collection. -template -std::string stringify(const T &); - -// Stringify any GeoJSON type. -std::string stringify(const geojson &); - -} // namespace geojson -} // namespace mapbox diff --git a/deps/geojson/0.4.0/include/mapbox/geojson/rapidjson.hpp b/deps/geojson/0.4.0/include/mapbox/geojson/rapidjson.hpp deleted file mode 100644 index 360eefd010..0000000000 --- a/deps/geojson/0.4.0/include/mapbox/geojson/rapidjson.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include -#include - -namespace mapbox { -namespace geojson { - -// Use the CrtAllocator, because the MemoryPoolAllocator is broken on ARM -// https://github.com/miloyip/rapidjson/issues/200, 301, 388 -using rapidjson_allocator = rapidjson::CrtAllocator; -using rapidjson_document = rapidjson::GenericDocument, rapidjson_allocator>; -using rapidjson_value = rapidjson::GenericValue, rapidjson_allocator>; - -// Convert inputs of known types. Instantiations are provided for geometry, feature, and -// feature_collection. -template -T convert(const rapidjson_value &); - -// Convert any GeoJSON type. -geojson convert(const rapidjson_value &); - -// Convert back to rapidjson value. Instantiations are provided for geometry, feature, and -// feature_collection. -template -rapidjson_value convert(const T &, rapidjson_allocator&); - -// Convert any GeoJSON type. -rapidjson_value convert(const geojson &, rapidjson_allocator&); - -} // namespace geojson -} // namespace mapbox diff --git a/deps/geojson/0.4.0/include/mapbox/geojson_impl.hpp b/deps/geojson/0.4.0/include/mapbox/geojson_impl.hpp deleted file mode 100644 index 2ffd420dbb..0000000000 --- a/deps/geojson/0.4.0/include/mapbox/geojson_impl.hpp +++ /dev/null @@ -1,447 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include - -namespace mapbox { -namespace geojson { - -using error = std::runtime_error; -using prop_map = std::unordered_map; - -template -T convert(const rapidjson_value &json); - -template <> -point convert(const rapidjson_value &json) { - if (json.Size() < 2) - throw error("coordinates array must have at least 2 numbers"); - - return point{ json[0].GetDouble(), json[1].GetDouble() }; -} - -template -Cont convert(const rapidjson_value &json) { - Cont points; - auto size = json.Size(); - points.reserve(size); - - for (auto &element : json.GetArray()) { - points.push_back(convert(element)); - } - return points; -} - -template <> -geometry convert(const rapidjson_value &json) { - if (!json.IsObject()) - throw error("Geometry must be an object"); - - const auto &json_end = json.MemberEnd(); - - const auto &type_itr = json.FindMember("type"); - if (type_itr == json_end) - throw error("Geometry must have a type property"); - - const auto &type = type_itr->value; - - if (type == "GeometryCollection") { - const auto &geometries_itr = json.FindMember("geometries"); - if (geometries_itr == json_end) - throw error("GeometryCollection must have a geometries property"); - - const auto &json_geometries = geometries_itr->value; - - if (!json_geometries.IsArray()) - throw error("GeometryCollection geometries property must be an array"); - - return geometry{ convert(json_geometries) }; - } - - const auto &coords_itr = json.FindMember("coordinates"); - - if (coords_itr == json_end) - throw error(std::string(type.GetString()) + " geometry must have a coordinates property"); - - const auto &json_coords = coords_itr->value; - if (!json_coords.IsArray()) - throw error("coordinates property must be an array"); - - if (type == "Point") - return geometry{ convert(json_coords) }; - if (type == "MultiPoint") - return geometry{ convert(json_coords) }; - if (type == "LineString") - return geometry{ convert(json_coords) }; - if (type == "MultiLineString") - return geometry{ convert(json_coords) }; - if (type == "Polygon") - return geometry{ convert(json_coords) }; - if (type == "MultiPolygon") - return geometry{ convert(json_coords) }; - - throw error(std::string(type.GetString()) + " not yet implemented"); -} - -template <> -value convert(const rapidjson_value &json); - -template <> -prop_map convert(const rapidjson_value &json) { - if (!json.IsObject()) - throw error("properties must be an object"); - - prop_map result; - for (auto &member : json.GetObject()) { - result.emplace(std::string(member.name.GetString(), member.name.GetStringLength()), - convert(member.value)); - } - return result; -} - -template <> -value convert(const rapidjson_value &json) { - switch (json.GetType()) { - case rapidjson::kNullType: - return ::mapbox::geometry::null_value_t{}; - case rapidjson::kFalseType: - return false; - case rapidjson::kTrueType: - return true; - case rapidjson::kObjectType: - return convert(json); - case rapidjson::kArrayType: - return convert>(json); - case rapidjson::kStringType: - return std::string(json.GetString(), json.GetStringLength()); - default: - assert(json.GetType() == rapidjson::kNumberType); - if (json.IsUint64()) - return std::uint64_t(json.GetUint64()); - if (json.IsInt64()) - return std::int64_t(json.GetInt64()); - return json.GetDouble(); - } -} - -template <> -identifier convert(const rapidjson_value &json) { - switch (json.GetType()) { - case rapidjson::kStringType: - return std::string(json.GetString(), json.GetStringLength()); - case rapidjson::kNumberType: - if (json.IsUint64()) - return std::uint64_t(json.GetUint64()); - if (json.IsInt64()) - return std::int64_t(json.GetInt64()); - return json.GetDouble(); - default: - throw error("Feature id must be a string or number"); - } -} - -template <> -feature convert(const rapidjson_value &json) { - if (!json.IsObject()) - throw error("Feature must be an object"); - - auto const &json_end = json.MemberEnd(); - auto const &type_itr = json.FindMember("type"); - - if (type_itr == json_end) - throw error("Feature must have a type property"); - if (type_itr->value != "Feature") - throw error("Feature type must be Feature"); - - auto const &geom_itr = json.FindMember("geometry"); - - if (geom_itr == json_end) - throw error("Feature must have a geometry property"); - - feature result{ convert(geom_itr->value) }; - - auto const &id_itr = json.FindMember("id"); - if (id_itr != json_end) { - result.id = convert(id_itr->value); - } - - auto const &prop_itr = json.FindMember("properties"); - - if (prop_itr == json_end) - throw error("Feature must have a properties property"); - - const auto &json_props = prop_itr->value; - if (!json_props.IsNull()) { - result.properties = convert(json_props); - } - - return result; -} - -template <> -geojson convert(const rapidjson_value &json) { - if (!json.IsObject()) - throw error("GeoJSON must be an object"); - - const auto &type_itr = json.FindMember("type"); - const auto &json_end = json.MemberEnd(); - - if (type_itr == json_end) - throw error("GeoJSON must have a type property"); - - const auto &type = type_itr->value; - - if (type == "FeatureCollection") { - const auto &features_itr = json.FindMember("features"); - if (features_itr == json_end) - throw error("FeatureCollection must have features property"); - - const auto &json_features = features_itr->value; - - if (!json_features.IsArray()) - throw error("FeatureCollection features property must be an array"); - - feature_collection collection; - - const auto &size = json_features.Size(); - collection.reserve(size); - - for (auto &feature_obj : json_features.GetArray()) { - collection.push_back(convert(feature_obj)); - } - - return geojson{ collection }; - } - - if (type == "Feature") - return geojson{ convert(json) }; - - return geojson{ convert(json) }; -} - -template -T parse(const std::string &json) { - rapidjson_document d; - d.Parse(json.c_str()); - return convert(d); -} - -template <> -geometry parse(const std::string &); -template <> -feature parse(const std::string &); -template <> -feature_collection parse(const std::string &); - -geojson parse(const std::string &json) { - return parse(json); -} - -geojson convert(const rapidjson_value &json) { - return convert(json); -} - -template <> -rapidjson_value convert(const geometry&, rapidjson_allocator&); - -template <> -rapidjson_value convert(const feature&, rapidjson_allocator&); - -template <> -rapidjson_value convert(const feature_collection&, rapidjson_allocator&); - -struct to_type { -public: - const char * operator()(const point&) { - return "Point"; - } - - const char * operator()(const line_string&) { - return "LineString"; - } - - const char * operator()(const polygon&) { - return "Polygon"; - } - - const char * operator()(const multi_point&) { - return "MultiPoint"; - } - - const char * operator()(const multi_line_string&) { - return "MultiLineString"; - } - - const char * operator()(const multi_polygon&) { - return "MultiPolygon"; - } - - const char * operator()(const geometry_collection&) { - return "GeometryCollection"; - } -}; - -struct to_coordinates_or_geometries { - rapidjson_allocator& allocator; - - // Handles line_string, polygon, multi_point, multi_line_string, multi_polygon, and geometry_collection. - template - rapidjson_value operator()(const std::vector& vector) { - rapidjson_value result; - result.SetArray(); - for (std::size_t i = 0; i < vector.size(); ++i) { - result.PushBack(operator()(vector[i]), allocator); - } - return result; - } - - rapidjson_value operator()(const point& element) { - rapidjson_value result; - result.SetArray(); - result.PushBack(element.x, allocator); - result.PushBack(element.y, allocator); - return result; - } - - rapidjson_value operator()(const geometry& element) { - return convert(element, allocator); - } -}; - -struct to_value { - rapidjson_allocator& allocator; - - rapidjson_value operator()(null_value_t) { - rapidjson_value result; - result.SetNull(); - return result; - } - - rapidjson_value operator()(bool t) { - rapidjson_value result; - result.SetBool(t); - return result; - } - - rapidjson_value operator()(int64_t t) { - rapidjson_value result; - result.SetInt64(t); - return result; - } - - rapidjson_value operator()(uint64_t t) { - rapidjson_value result; - result.SetUint64(t); - return result; - } - - rapidjson_value operator()(double t) { - rapidjson_value result; - result.SetDouble(t); - return result; - } - - rapidjson_value operator()(const std::string& t) { - rapidjson_value result; - result.SetString(t.data(), rapidjson::SizeType(t.size()), allocator); - return result; - } - - rapidjson_value operator()(const std::vector& array) { - rapidjson_value result; - result.SetArray(); - for (const auto& item : array) { - result.PushBack(value::visit(item, *this), allocator); - } - return result; - } - - rapidjson_value operator()(const std::unordered_map& map) { - rapidjson_value result; - result.SetObject(); - for (const auto& property : map) { - result.AddMember( - rapidjson::GenericStringRef { - property.first.data(), - rapidjson::SizeType(property.first.size()) - }, - value::visit(property.second, *this), - allocator); - } - return result; - } -}; - -template <> -rapidjson_value convert(const geometry& element, rapidjson_allocator& allocator) { - rapidjson_value result(rapidjson::kObjectType); - - result.AddMember( - "type", - rapidjson::GenericStringRef { geometry::visit(element, to_type()) }, - allocator); - - result.AddMember( - rapidjson::GenericStringRef { element.is() ? "geometries" : "coordinates" }, - geometry::visit(element, to_coordinates_or_geometries { allocator }), - allocator); - - return result; -} - -template <> -rapidjson_value convert(const feature& element, rapidjson_allocator& allocator) { - rapidjson_value result(rapidjson::kObjectType); - result.AddMember("type", "Feature", allocator); - - if (element.id) { - result.AddMember("id", identifier::visit(*element.id, to_value { allocator }), allocator); - } - - result.AddMember("geometry", convert(element.geometry, allocator), allocator); - result.AddMember("properties", to_value { allocator }(element.properties), allocator); - - return result; -} - -template <> -rapidjson_value convert(const feature_collection& collection, rapidjson_allocator& allocator) { - rapidjson_value result(rapidjson::kObjectType); - result.AddMember("type", "FeatureCollection", allocator); - - rapidjson_value features(rapidjson::kArrayType); - for (const auto& element : collection) { - features.PushBack(convert(element, allocator), allocator); - } - result.AddMember("features", features, allocator); - - return result; -} - -rapidjson_value convert(const geojson& element, rapidjson_allocator& allocator) { - return geojson::visit(element, [&] (const auto& alternative) { - return convert(alternative, allocator); - }); -} - -template -std::string stringify(const T& t) { - rapidjson_allocator allocator; - rapidjson::StringBuffer buffer; - rapidjson::Writer writer(buffer); - convert(t, allocator).Accept(writer); - return buffer.GetString(); -} - -std::string stringify(const geojson& element) { - return geojson::visit(element, [] (const auto& alternative) { - return stringify(alternative); - }); -} - -} // namespace geojson -} // namespace mapbox diff --git a/deps/geojson/0.4.2/include/mapbox/geojson.hpp b/deps/geojson/0.4.2/include/mapbox/geojson.hpp new file mode 100644 index 0000000000..4b62751e15 --- /dev/null +++ b/deps/geojson/0.4.2/include/mapbox/geojson.hpp @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +namespace mapbox { +namespace geojson { + +using value = mapbox::geometry::value; +using null_value_t = mapbox::geometry::null_value_t; +using identifier = mapbox::geometry::identifier; +using point = mapbox::geometry::point; +using multi_point = mapbox::geometry::multi_point; +using line_string = mapbox::geometry::line_string; +using linear_ring = mapbox::geometry::linear_ring; +using multi_line_string = mapbox::geometry::multi_line_string; +using polygon = mapbox::geometry::polygon; +using multi_polygon = mapbox::geometry::multi_polygon; +using geometry = mapbox::geometry::geometry; +using geometry_collection = mapbox::geometry::geometry_collection; +using feature = mapbox::geometry::feature; +using feature_collection = mapbox::geometry::feature_collection; + +// Parse inputs of known types. Instantiations are provided for geometry, feature, and +// feature_collection. +template +T parse(const std::string &); + +// Parse any GeoJSON type. +using geojson = mapbox::util::variant; +geojson parse(const std::string &); + +// Stringify inputs of known types. Instantiations are provided for geometry, feature, and +// feature_collection. +template +std::string stringify(const T &); + +// Stringify any GeoJSON type. +std::string stringify(const geojson &); + +} // namespace geojson +} // namespace mapbox diff --git a/deps/geojson/0.4.2/include/mapbox/geojson/rapidjson.hpp b/deps/geojson/0.4.2/include/mapbox/geojson/rapidjson.hpp new file mode 100644 index 0000000000..360eefd010 --- /dev/null +++ b/deps/geojson/0.4.2/include/mapbox/geojson/rapidjson.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include +#include + +namespace mapbox { +namespace geojson { + +// Use the CrtAllocator, because the MemoryPoolAllocator is broken on ARM +// https://github.com/miloyip/rapidjson/issues/200, 301, 388 +using rapidjson_allocator = rapidjson::CrtAllocator; +using rapidjson_document = rapidjson::GenericDocument, rapidjson_allocator>; +using rapidjson_value = rapidjson::GenericValue, rapidjson_allocator>; + +// Convert inputs of known types. Instantiations are provided for geometry, feature, and +// feature_collection. +template +T convert(const rapidjson_value &); + +// Convert any GeoJSON type. +geojson convert(const rapidjson_value &); + +// Convert back to rapidjson value. Instantiations are provided for geometry, feature, and +// feature_collection. +template +rapidjson_value convert(const T &, rapidjson_allocator&); + +// Convert any GeoJSON type. +rapidjson_value convert(const geojson &, rapidjson_allocator&); + +} // namespace geojson +} // namespace mapbox diff --git a/deps/geojson/0.4.2/include/mapbox/geojson_impl.hpp b/deps/geojson/0.4.2/include/mapbox/geojson_impl.hpp new file mode 100644 index 0000000000..48d81e4114 --- /dev/null +++ b/deps/geojson/0.4.2/include/mapbox/geojson_impl.hpp @@ -0,0 +1,453 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +#include + +namespace mapbox { +namespace geojson { + +using error = std::runtime_error; +using prop_map = std::unordered_map; + +template +T convert(const rapidjson_value &json); + +template <> +point convert(const rapidjson_value &json) { + if (json.Size() < 2) + throw error("coordinates array must have at least 2 numbers"); + + return point{ json[0].GetDouble(), json[1].GetDouble() }; +} + +template +Cont convert(const rapidjson_value &json) { + Cont points; + auto size = json.Size(); + points.reserve(size); + + for (auto &element : json.GetArray()) { + points.push_back(convert(element)); + } + return points; +} + +template <> +geometry convert(const rapidjson_value &json) { + if (!json.IsObject()) + throw error("Geometry must be an object"); + + const auto &json_end = json.MemberEnd(); + + const auto &type_itr = json.FindMember("type"); + if (type_itr == json_end) + throw error("Geometry must have a type property"); + + const auto &type = type_itr->value; + + if (type == "GeometryCollection") { + const auto &geometries_itr = json.FindMember("geometries"); + if (geometries_itr == json_end) + throw error("GeometryCollection must have a geometries property"); + + const auto &json_geometries = geometries_itr->value; + + if (!json_geometries.IsArray()) + throw error("GeometryCollection geometries property must be an array"); + + return geometry{ convert(json_geometries) }; + } + + const auto &coords_itr = json.FindMember("coordinates"); + + if (coords_itr == json_end) + throw error(std::string(type.GetString()) + " geometry must have a coordinates property"); + + const auto &json_coords = coords_itr->value; + if (!json_coords.IsArray()) + throw error("coordinates property must be an array"); + + if (type == "Point") + return geometry{ convert(json_coords) }; + if (type == "MultiPoint") + return geometry{ convert(json_coords) }; + if (type == "LineString") + return geometry{ convert(json_coords) }; + if (type == "MultiLineString") + return geometry{ convert(json_coords) }; + if (type == "Polygon") + return geometry{ convert(json_coords) }; + if (type == "MultiPolygon") + return geometry{ convert(json_coords) }; + + throw error(std::string(type.GetString()) + " not yet implemented"); +} + +template <> +value convert(const rapidjson_value &json); + +template <> +prop_map convert(const rapidjson_value &json) { + if (!json.IsObject()) + throw error("properties must be an object"); + + prop_map result; + for (auto &member : json.GetObject()) { + result.emplace(std::string(member.name.GetString(), member.name.GetStringLength()), + convert(member.value)); + } + return result; +} + +template <> +value convert(const rapidjson_value &json) { + switch (json.GetType()) { + case rapidjson::kNullType: + return ::mapbox::geometry::null_value_t{}; + case rapidjson::kFalseType: + return false; + case rapidjson::kTrueType: + return true; + case rapidjson::kObjectType: + return convert(json); + case rapidjson::kArrayType: + return convert>(json); + case rapidjson::kStringType: + return std::string(json.GetString(), json.GetStringLength()); + default: + assert(json.GetType() == rapidjson::kNumberType); + if (json.IsUint64()) + return std::uint64_t(json.GetUint64()); + if (json.IsInt64()) + return std::int64_t(json.GetInt64()); + return json.GetDouble(); + } +} + +template <> +identifier convert(const rapidjson_value &json) { + switch (json.GetType()) { + case rapidjson::kStringType: + return std::string(json.GetString(), json.GetStringLength()); + case rapidjson::kNumberType: + if (json.IsUint64()) + return std::uint64_t(json.GetUint64()); + if (json.IsInt64()) + return std::int64_t(json.GetInt64()); + return json.GetDouble(); + default: + throw error("Feature id must be a string or number"); + } +} + +template <> +feature convert(const rapidjson_value &json) { + if (!json.IsObject()) + throw error("Feature must be an object"); + + auto const &json_end = json.MemberEnd(); + auto const &type_itr = json.FindMember("type"); + + if (type_itr == json_end) + throw error("Feature must have a type property"); + if (type_itr->value != "Feature") + throw error("Feature type must be Feature"); + + auto const &geom_itr = json.FindMember("geometry"); + + if (geom_itr == json_end) + throw error("Feature must have a geometry property"); + + feature result{ convert(geom_itr->value) }; + + auto const &id_itr = json.FindMember("id"); + if (id_itr != json_end) { + result.id = convert(id_itr->value); + } + + auto const &prop_itr = json.FindMember("properties"); + if (prop_itr != json_end) { + const auto &json_props = prop_itr->value; + if (!json_props.IsNull()) { + result.properties = convert(json_props); + } + } + + return result; +} + +template <> +geojson convert(const rapidjson_value &json) { + if (!json.IsObject()) + throw error("GeoJSON must be an object"); + + const auto &type_itr = json.FindMember("type"); + const auto &json_end = json.MemberEnd(); + + if (type_itr == json_end) + throw error("GeoJSON must have a type property"); + + const auto &type = type_itr->value; + + if (type == "FeatureCollection") { + const auto &features_itr = json.FindMember("features"); + if (features_itr == json_end) + throw error("FeatureCollection must have features property"); + + const auto &json_features = features_itr->value; + + if (!json_features.IsArray()) + throw error("FeatureCollection features property must be an array"); + + feature_collection collection; + + const auto &size = json_features.Size(); + collection.reserve(size); + + for (auto &feature_obj : json_features.GetArray()) { + collection.push_back(convert(feature_obj)); + } + + return geojson{ collection }; + } + + if (type == "Feature") + return geojson{ convert(json) }; + + return geojson{ convert(json) }; +} + +template +T parse(const std::string &json) { + rapidjson_document d; + d.Parse(json.c_str()); + if (d.HasParseError()) { + std::stringstream message; + message << d.GetErrorOffset() << " - " << rapidjson::GetParseError_En(d.GetParseError()); + throw error(message.str()); + } + return convert(d); +} + +template <> +geometry parse(const std::string &); +template <> +feature parse(const std::string &); +template <> +feature_collection parse(const std::string &); + +geojson parse(const std::string &json) { + return parse(json); +} + +geojson convert(const rapidjson_value &json) { + return convert(json); +} + +template <> +rapidjson_value convert(const geometry&, rapidjson_allocator&); + +template <> +rapidjson_value convert(const feature&, rapidjson_allocator&); + +template <> +rapidjson_value convert(const feature_collection&, rapidjson_allocator&); + +struct to_type { +public: + const char * operator()(const point&) { + return "Point"; + } + + const char * operator()(const line_string&) { + return "LineString"; + } + + const char * operator()(const polygon&) { + return "Polygon"; + } + + const char * operator()(const multi_point&) { + return "MultiPoint"; + } + + const char * operator()(const multi_line_string&) { + return "MultiLineString"; + } + + const char * operator()(const multi_polygon&) { + return "MultiPolygon"; + } + + const char * operator()(const geometry_collection&) { + return "GeometryCollection"; + } +}; + +struct to_coordinates_or_geometries { + rapidjson_allocator& allocator; + + // Handles line_string, polygon, multi_point, multi_line_string, multi_polygon, and geometry_collection. + template + rapidjson_value operator()(const std::vector& vector) { + rapidjson_value result; + result.SetArray(); + for (std::size_t i = 0; i < vector.size(); ++i) { + result.PushBack(operator()(vector[i]), allocator); + } + return result; + } + + rapidjson_value operator()(const point& element) { + rapidjson_value result; + result.SetArray(); + result.PushBack(element.x, allocator); + result.PushBack(element.y, allocator); + return result; + } + + rapidjson_value operator()(const geometry& element) { + return convert(element, allocator); + } +}; + +struct to_value { + rapidjson_allocator& allocator; + + rapidjson_value operator()(null_value_t) { + rapidjson_value result; + result.SetNull(); + return result; + } + + rapidjson_value operator()(bool t) { + rapidjson_value result; + result.SetBool(t); + return result; + } + + rapidjson_value operator()(int64_t t) { + rapidjson_value result; + result.SetInt64(t); + return result; + } + + rapidjson_value operator()(uint64_t t) { + rapidjson_value result; + result.SetUint64(t); + return result; + } + + rapidjson_value operator()(double t) { + rapidjson_value result; + result.SetDouble(t); + return result; + } + + rapidjson_value operator()(const std::string& t) { + rapidjson_value result; + result.SetString(t.data(), rapidjson::SizeType(t.size()), allocator); + return result; + } + + rapidjson_value operator()(const std::vector& array) { + rapidjson_value result; + result.SetArray(); + for (const auto& item : array) { + result.PushBack(value::visit(item, *this), allocator); + } + return result; + } + + rapidjson_value operator()(const std::unordered_map& map) { + rapidjson_value result; + result.SetObject(); + for (const auto& property : map) { + result.AddMember( + rapidjson::GenericStringRef { + property.first.data(), + rapidjson::SizeType(property.first.size()) + }, + value::visit(property.second, *this), + allocator); + } + return result; + } +}; + +template <> +rapidjson_value convert(const geometry& element, rapidjson_allocator& allocator) { + rapidjson_value result(rapidjson::kObjectType); + + result.AddMember( + "type", + rapidjson::GenericStringRef { geometry::visit(element, to_type()) }, + allocator); + + result.AddMember( + rapidjson::GenericStringRef { element.is() ? "geometries" : "coordinates" }, + geometry::visit(element, to_coordinates_or_geometries { allocator }), + allocator); + + return result; +} + +template <> +rapidjson_value convert(const feature& element, rapidjson_allocator& allocator) { + rapidjson_value result(rapidjson::kObjectType); + result.AddMember("type", "Feature", allocator); + + if (element.id) { + result.AddMember("id", identifier::visit(*element.id, to_value { allocator }), allocator); + } + + result.AddMember("geometry", convert(element.geometry, allocator), allocator); + result.AddMember("properties", to_value { allocator }(element.properties), allocator); + + return result; +} + +template <> +rapidjson_value convert(const feature_collection& collection, rapidjson_allocator& allocator) { + rapidjson_value result(rapidjson::kObjectType); + result.AddMember("type", "FeatureCollection", allocator); + + rapidjson_value features(rapidjson::kArrayType); + for (const auto& element : collection) { + features.PushBack(convert(element, allocator), allocator); + } + result.AddMember("features", features, allocator); + + return result; +} + +rapidjson_value convert(const geojson& element, rapidjson_allocator& allocator) { + return geojson::visit(element, [&] (const auto& alternative) { + return convert(alternative, allocator); + }); +} + +template +std::string stringify(const T& t) { + rapidjson_allocator allocator; + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + convert(t, allocator).Accept(writer); + return buffer.GetString(); +} + +std::string stringify(const geojson& element) { + return geojson::visit(element, [] (const auto& alternative) { + return stringify(alternative); + }); +} + +} // namespace geojson +} // namespace mapbox diff --git a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt.hpp b/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt.hpp deleted file mode 100644 index b758be3929..0000000000 --- a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt.hpp +++ /dev/null @@ -1,244 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -#include -#include -#include -#include - -namespace mapbox { -namespace geojsonvt { - -using geometry = mapbox::geometry::geometry; -using feature = mapbox::geometry::feature; -using feature_collection = mapbox::geometry::feature_collection; -using geometry_collection = mapbox::geometry::geometry_collection; -using geojson = mapbox::util::variant; - -struct ToFeatureCollection { - feature_collection operator()(const feature_collection& value) const { - return value; - } - feature_collection operator()(const feature& value) const { - return { value }; - } - feature_collection operator()(const geometry& value) const { - return { { value } }; - } -}; - -struct Options { - // max zoom to preserve detail on - uint8_t maxZoom = 18; - - // max zoom in the tile index - uint8_t indexMaxZoom = 5; - - // max number of points per tile in the tile index - uint32_t indexMaxPoints = 100000; - - // whether to tile solid square tiles further - bool solidChildren = false; - - // simplification tolerance (higher means simpler) - double tolerance = 3; - - // tile extent - uint16_t extent = 4096; - - // tile buffer on each side - uint16_t buffer = 64; -}; - -const Tile empty_tile{}; - -inline uint64_t toID(uint8_t z, uint32_t x, uint32_t y) { - return (((1ull << z) * y + x) * 32) + z; -} - -class GeoJSONVT { -public: - const Options options; - - GeoJSONVT(const mapbox::geometry::feature_collection& features_, - const Options& options_ = Options()) - : options(options_) { - - const uint32_t z2 = std::pow(2, options.maxZoom); - - auto converted = detail::convert(features_, (options.tolerance / options.extent) / z2); - auto features = detail::wrap(converted, double(options.buffer) / options.extent); - - splitTile(features, 0, 0, 0); - } - - GeoJSONVT(const geojson& geojson_, const Options& options_ = Options()) - : GeoJSONVT(geojson::visit(geojson_, ToFeatureCollection{}), options_) { - } - - std::map stats; - uint32_t total = 0; - - const Tile& getTile(const uint8_t z, const uint32_t x_, const uint32_t y) { - - if (z > options.maxZoom) - throw std::runtime_error("Requested zoom higher than maxZoom: " + std::to_string(z)); - - const uint32_t z2 = std::pow(2, z); - const uint32_t x = ((x_ % z2) + z2) % z2; // wrap tile x coordinate - const uint64_t id = toID(z, x, y); - - auto it = tiles.find(id); - if (it != tiles.end()) - return it->second.tile; - - it = findParent(z, x, y); - - if (it == tiles.end()) - throw std::runtime_error("Parent tile not found"); - - // if we found a parent tile containing the original geometry, we can drill down from it - const auto& parent = it->second; - - // parent tile is a solid clipped square, return it instead since it's identical - if (parent.is_solid) - return parent.tile; - - // drill down parent tile up to the requested one - splitTile(parent.source_features, parent.z, parent.x, parent.y, z, x, y); - - it = tiles.find(id); - if (it != tiles.end()) - return it->second.tile; - - it = findParent(z, x, y); - if (it == tiles.end()) - throw std::runtime_error("Parent tile not found"); - - // drilling stopped because parent was a solid square; return it instead - if (it->second.is_solid) - return it->second.tile; - - // otherwise it was an empty tile - return empty_tile; - } - - const std::unordered_map& getInternalTiles() const { - return tiles; - } - -private: - std::unordered_map tiles; - - std::unordered_map::iterator - findParent(const uint8_t z, const uint32_t x, const uint32_t y) { - uint8_t z0 = z; - uint32_t x0 = x; - uint32_t y0 = y; - - const auto end = tiles.end(); - auto parent = end; - - while ((parent == end) && (z0 != 0)) { - z0--; - x0 = x0 / 2; - y0 = y0 / 2; - parent = tiles.find(toID(z0, x0, y0)); - } - - return parent; - } - - void splitTile(const detail::vt_features& features, - const uint8_t z, - const uint32_t x, - const uint32_t y, - const uint8_t cz = 0, - const uint32_t cx = 0, - const uint32_t cy = 0) { - - const double z2 = 1u << z; - const uint64_t id = toID(z, x, y); - - auto it = tiles.find(id); - - if (it == tiles.end()) { - const double tolerance = - (z == options.maxZoom ? 0 : options.tolerance / (z2 * options.extent)); - - it = tiles - .emplace(id, detail::InternalTile{ features, z, x, y, options.extent, - options.buffer, tolerance }) - .first; - stats[z] = (stats.count(z) ? stats[z] + 1 : 1); - total++; - // printf("tile z%i-%i-%i\n", z, x, y); - } - - auto& tile = it->second; - - if (features.empty()) - return; - - // stop tiling if the tile is solid clipped square - if (!options.solidChildren && tile.is_solid) - return; - - // if it's the first-pass tiling - if (cz == 0u) { - // stop tiling if we reached max zoom, or if the tile is too simple - if (z == options.indexMaxZoom || tile.tile.num_points <= options.indexMaxPoints) { - tile.source_features = features; - return; - } - - } else { // drilldown to a specific tile; - // stop tiling if we reached base zoom - if (z == options.maxZoom) - return; - - // stop tiling if it's our target tile zoom - if (z == cz) { - tile.source_features = features; - return; - } - - // stop tiling if it's not an ancestor of the target tile - const double m = 1u << (cz - z); - if (x != static_cast(std::floor(cx / m)) || - y != static_cast(std::floor(cy / m))) { - tile.source_features = features; - return; - } - } - - const double p = 0.5 * options.buffer / options.extent; - const auto& min = tile.bbox.min; - const auto& max = tile.bbox.max; - - const auto left = detail::clip<0>(features, (x - p) / z2, (x + 0.5 + p) / z2, min.x, max.x); - - splitTile(detail::clip<1>(left, (y - p) / z2, (y + 0.5 + p) / z2, min.y, max.y), z + 1, - x * 2, y * 2, cz, cx, cy); - splitTile(detail::clip<1>(left, (y + 0.5 - p) / z2, (y + 1 + p) / z2, min.y, max.y), z + 1, - x * 2, y * 2 + 1, cz, cx, cy); - - const auto right = - detail::clip<0>(features, (x + 0.5 - p) / z2, (x + 1 + p) / z2, min.x, max.x); - - splitTile(detail::clip<1>(right, (y - p) / z2, (y + 0.5 + p) / z2, min.y, max.y), z + 1, - x * 2 + 1, y * 2, cz, cx, cy); - splitTile(detail::clip<1>(right, (y + 0.5 - p) / z2, (y + 1 + p) / z2, min.y, max.y), z + 1, - x * 2 + 1, y * 2 + 1, cz, cx, cy); - - // if we sliced further down, no need to keep source geometry - tile.source_features = {}; - } -}; - -} // namespace geojsonvt -} // namespace mapbox diff --git a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/clip.hpp b/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/clip.hpp deleted file mode 100644 index 212e2979d0..0000000000 --- a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/clip.hpp +++ /dev/null @@ -1,251 +0,0 @@ -#pragma once - -#include - -namespace mapbox { -namespace geojsonvt { -namespace detail { - -template -class clipper { -public: - const double k1; - const double k2; - - vt_geometry operator()(const vt_point& point) const { - return point; - } - - vt_geometry operator()(const vt_multi_point& points) const { - vt_multi_point part; - for (const auto& p : points) { - const double ak = get(p); - if (ak >= k1 && ak <= k2) - part.push_back(p); - } - return part; - } - - vt_geometry operator()(const vt_line_string& line) const { - vt_multi_line_string parts; - clipLine(line, parts); - if (parts.size() == 1) - return parts[0]; - else - return parts; - } - - vt_geometry operator()(const vt_multi_line_string& lines) const { - vt_multi_line_string parts; - for (const auto& line : lines) { - clipLine(line, parts); - } - if (parts.size() == 1) - return parts[0]; - else - return parts; - } - - vt_geometry operator()(const vt_polygon& polygon) const { - vt_polygon result; - for (const auto& ring : polygon) { - const auto new_ring = clipRing(ring); - if (!new_ring.empty()) - result.push_back(new_ring); - } - return result; - } - - vt_geometry operator()(const vt_multi_polygon& polygons) const { - vt_multi_polygon result; - for (const auto& polygon : polygons) { - vt_polygon p; - for (const auto& ring : polygon) { - const auto new_ring = clipRing(ring); - if (!new_ring.empty()) - p.push_back(new_ring); - } - if (!p.empty()) - result.push_back(p); - } - return result; - } - - vt_geometry operator()(const vt_geometry_collection& geometries) const { - vt_geometry_collection result; - for (const auto& geometry : geometries) { - vt_geometry::visit(geometry, - [&](const auto& g) { result.push_back(this->operator()(g)); }); - } - return result; - } - -private: - vt_line_string newSlice(vt_multi_line_string& parts, vt_line_string& slice, double dist) const { - if (!slice.empty()) { - slice.dist = dist; - parts.push_back(std::move(slice)); - } - return {}; - } - - void clipLine(const vt_line_string& line, vt_multi_line_string& slices) const { - - const double dist = line.dist; - const size_t len = line.size(); - - if (len < 2) - return; - - vt_line_string slice; - - for (size_t i = 0; i < (len - 1); ++i) { - const auto& a = line[i]; - const auto& b = line[i + 1]; - const double ak = get(a); - const double bk = get(b); - - if (ak < k1) { - if (bk > k2) { // ---|-----|--> - slice.push_back(intersect(a, b, k1)); - slice.push_back(intersect(a, b, k2)); - slice = newSlice(slices, slice, dist); - - } else if (bk >= k1) { // ---|--> | - slice.push_back(intersect(a, b, k1)); - if (i == len - 2) - slice.push_back(b); // last point - } - } else if (ak > k2) { - if (bk < k1) { // <--|-----|--- - slice.push_back(intersect(a, b, k2)); - slice.push_back(intersect(a, b, k1)); - slice = newSlice(slices, slice, dist); - - } else if (bk <= k2) { // | <--|--- - slice.push_back(intersect(a, b, k2)); - if (i == len - 2) - slice.push_back(b); // last point - } - } else { - slice.push_back(a); - - if (bk < k1) { // <--|--- | - slice.push_back(intersect(a, b, k1)); - slice = newSlice(slices, slice, dist); - - } else if (bk > k2) { // | ---|--> - slice.push_back(intersect(a, b, k2)); - slice = newSlice(slices, slice, dist); - - } else if (i == len - 2) { // | --> | - slice.push_back(b); - } - } - } - - // add the final slice - newSlice(slices, slice, dist); - } - - vt_linear_ring clipRing(const vt_linear_ring& ring) const { - const size_t len = ring.size(); - - vt_linear_ring slice; - slice.area = ring.area; - - if (len < 2) - return slice; - - for (size_t i = 0; i < (len - 1); ++i) { - const auto& a = ring[i]; - const auto& b = ring[i + 1]; - const double ak = get(a); - const double bk = get(b); - - if (ak < k1) { - if (bk >= k1) { - slice.push_back(intersect(a, b, k1)); // ---|--> | - if (bk > k2) // ---|-----|--> - slice.push_back(intersect(a, b, k2)); - else if (i == len - 2) - slice.push_back(b); // last point - } - } else if (ak > k2) { - if (bk <= k2) { // | <--|--- - slice.push_back(intersect(a, b, k2)); - if (bk < k1) // <--|-----|--- - slice.push_back(intersect(a, b, k1)); - else if (i == len - 2) - slice.push_back(b); // last point - } - } else { - slice.push_back(a); - if (bk < k1) // <--|--- | - slice.push_back(intersect(a, b, k1)); - else if (bk > k2) // | ---|--> - slice.push_back(intersect(a, b, k2)); - // | --> | - } - } - - // close the polygon if its endpoints are not the same after clipping - if (!slice.empty()) { - const auto& first = slice.front(); - const auto& last = slice.back(); - if (first != last) { - slice.push_back(first); - } - } - - return slice; - } -}; - -/* clip features between two axis-parallel lines: - * | | - * ___|___ | / - * / | \____|____/ - * | | - */ - -template -inline vt_features clip(const vt_features& features, - const double k1, - const double k2, - const double minAll, - const double maxAll) { - - if (minAll >= k1 && maxAll <= k2) // trivial accept - return features; - - if (minAll > k2 || maxAll < k1) // trivial reject - return {}; - - vt_features clipped; - - for (const auto& feature : features) { - const auto& geom = feature.geometry; - const auto& props = feature.properties; - const auto& id = feature.id; - - const double min = get(feature.bbox.min); - const double max = get(feature.bbox.max); - - if (min >= k1 && max <= k2) { // trivial accept - clipped.push_back(feature); - - } else if (min > k2 || max < k1) { // trivial reject - continue; - - } else { - clipped.emplace_back(vt_geometry::visit(geom, clipper{ k1, k2 }), props, id); - } - } - - return clipped; -} - -} // namespace detail -} // namespace geojsonvt -} // namespace mapbox diff --git a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/convert.hpp b/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/convert.hpp deleted file mode 100644 index ba28f449b3..0000000000 --- a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/convert.hpp +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once - -#include -#include -#include - -#include -#include - -namespace mapbox { -namespace geojsonvt { -namespace detail { - -struct project { - const double tolerance; - using result_type = vt_geometry; - - vt_point operator()(const geometry::point& p) { - const double sine = std::sin(p.y * M_PI / 180); - const double x = p.x / 360 + 0.5; - const double y = - std::max(std::min(0.5 - 0.25 * std::log((1 + sine) / (1 - sine)) / M_PI, 1.0), 0.0); - return { x, y, 0.0 }; - } - - vt_line_string operator()(const geometry::line_string& points) { - vt_line_string result; - const size_t len = points.size(); - - if (len == 0) - return result; - - result.reserve(len); - - for (const auto& p : points) { - result.push_back(operator()(p)); - } - - for (size_t i = 0; i < len - 1; ++i) { - const auto& a = result[i]; - const auto& b = result[i + 1]; - // use Manhattan distance instead of Euclidian to avoid expensive square root - // computation - result.dist += std::abs(b.x - a.x) + std::abs(b.y - a.y); - } - - simplify(result, tolerance); - - return result; - } - - vt_linear_ring operator()(const geometry::linear_ring& ring) { - vt_linear_ring result; - const size_t len = ring.size(); - - if (len == 0) - return result; - - result.reserve(len); - - for (const auto& p : ring) { - result.push_back(operator()(p)); - } - - double area = 0.0; - - for (size_t i = 0; i < len - 1; ++i) { - const auto& a = result[i]; - const auto& b = result[i + 1]; - area += a.x * b.y - b.x * a.y; - } - result.area = std::abs(area / 2); - - simplify(result, tolerance); - - return result; - } - - vt_geometry operator()(const geometry::geometry& geometry) { - return geometry::geometry::visit(geometry, project{ tolerance }); - } - - // Handles polygon, multi_*, geometry_collection. - template - auto operator()(const T& vector) { - typename vt_geometry_type::type result; - result.reserve(vector.size()); - for (const auto& e : vector) { - result.push_back(operator()(e)); - } - return result; - } -}; - -inline vt_features convert(const geometry::feature_collection& features, - const double tolerance) { - vt_features projected; - projected.reserve(features.size()); - for (const auto& feature : features) { - projected.emplace_back( - geometry::geometry::visit(feature.geometry, project{ tolerance }), - feature.properties, - feature.id); - } - return projected; -} - -} // namespace detail -} // namespace geojsonvt -} // namespace mapbox diff --git a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/simplify.hpp b/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/simplify.hpp deleted file mode 100644 index be0165b0f1..0000000000 --- a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/simplify.hpp +++ /dev/null @@ -1,72 +0,0 @@ -#pragma once - -#include - -namespace mapbox { -namespace geojsonvt { -namespace detail { - -// square distance from a point to a segment -inline double getSqSegDist(const vt_point& p, const vt_point& a, const vt_point& b) { - double x = a.x; - double y = a.y; - double dx = b.x - a.x; - double dy = b.y - a.y; - - if ((dx != 0.0) || (dy != 0.0)) { - - const double t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / (dx * dx + dy * dy); - - if (t > 1) { - x = b.x; - y = b.y; - - } else if (t > 0) { - x += dx * t; - y += dy * t; - } - } - - dx = p.x - x; - dy = p.y - y; - - return dx * dx + dy * dy; -} - -// calculate simplification data using optimized Douglas-Peucker algorithm -inline void simplify(std::vector& points, size_t first, size_t last, double sqTolerance) { - double maxSqDist = sqTolerance; - size_t index = 0; - - for (auto i = first + 1; i < last; i++) { - const double sqDist = getSqSegDist(points[i], points[first], points[last]); - - if (sqDist > maxSqDist) { - index = i; - maxSqDist = sqDist; - } - } - - if (maxSqDist > sqTolerance) { - // save the point importance in squared pixels as a z coordinate - points[index].z = maxSqDist; - if (index - first > 1) - simplify(points, first, index, sqTolerance); - if (last - index > 1) - simplify(points, index, last, sqTolerance); - } -} - -inline void simplify(std::vector& points, double tolerance) { - const size_t len = points.size(); - - // always retain the endpoints (1 is the max value) - points[0].z = 1.0; - points[len - 1].z = 1.0; - - simplify(points, 0, len - 1, tolerance * tolerance); -} - -} // namespace detail -} // namespace geojsonvt -} // namespace mapbox diff --git a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/tile.hpp b/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/tile.hpp deleted file mode 100644 index ccb230bb9c..0000000000 --- a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/tile.hpp +++ /dev/null @@ -1,207 +0,0 @@ -#pragma once - -#include -#include -#include - -namespace mapbox { -namespace geojsonvt { - -struct Tile { - mapbox::geometry::feature_collection features; - uint32_t num_points = 0; - uint32_t num_simplified = 0; -}; - -namespace detail { - -class InternalTile { -public: - const uint8_t z; - const uint32_t x; - const uint32_t y; - - vt_features source_features; - bool is_solid = false; - mapbox::geometry::box bbox = { { 2, 1 }, { -1, 0 } }; - - Tile tile; - - InternalTile(const vt_features& source, - const uint8_t z_, - const uint32_t x_, - const uint32_t y_, - const uint16_t extent_, - const uint16_t buffer, - const double tolerance_) - : z(z_), - x(x_), - y(y_), - z2(std::pow(2, z)), - extent(extent_), - tolerance(tolerance_), - sq_tolerance(tolerance_ * tolerance_) { - - for (const auto& feature : source) { - const auto& geom = feature.geometry; - const auto& props = feature.properties; - const auto& id = feature.id; - - tile.num_points += feature.num_points; - - vt_geometry::visit(geom, [&](const auto& g) { - // `this->` is a workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61636 - this->addFeature(g, props, id); - }); - - bbox.min.x = std::min(feature.bbox.min.x, bbox.min.x); - bbox.min.y = std::min(feature.bbox.min.y, bbox.min.y); - bbox.max.x = std::max(feature.bbox.max.x, bbox.max.x); - bbox.max.y = std::max(feature.bbox.max.y, bbox.max.y); - } - - is_solid = isSolid(buffer); - } - -private: - const double z2; - const uint16_t extent; - const double tolerance; - const double sq_tolerance; - - bool isSolid(const uint16_t buffer) { - if (tile.features.size() != 1) - return false; - - const auto& geom = tile.features.front().geometry; - if (!geom.is>()) - return false; - - const auto& rings = geom.get>(); - if (rings.size() > 1) - return false; - - const auto& ring = rings.front(); - if (ring.size() != 5) - return false; - - const int16_t min = -static_cast(buffer); - const int16_t max = static_cast(extent + buffer); - for (const auto& p : ring) { - if ((p.x != min && p.x != max) || (p.y != min && p.y != max)) - return false; - } - - return true; - } - - void addFeature(const vt_point& point, const property_map& props, const optional& id) { - tile.features.push_back({ transform(point), props, id }); - } - - void addFeature(const vt_line_string& line, const property_map& props, const optional& id) { - const auto new_line = transform(line); - if (!new_line.empty()) - tile.features.push_back({ std::move(new_line), props, id }); - } - - void addFeature(const vt_polygon& polygon, const property_map& props, const optional& id) { - const auto new_polygon = transform(polygon); - if (!new_polygon.empty()) - tile.features.push_back({ std::move(new_polygon), props, id }); - } - - void addFeature(const vt_geometry_collection& collection, const property_map& props, const optional& id) { - for (const auto& geom : collection) { - vt_geometry::visit(geom, [&](const auto& g) { - // `this->` is a workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61636 - this->addFeature(g, props, id); - }); - } - } - - template - void addFeature(const T& multi, const property_map& props, const optional& id) { - const auto new_multi = transform(multi); - - switch (new_multi.size()) { - case 0: - break; - case 1: - tile.features.push_back({ std::move(new_multi[0]), props, id }); - break; - default: - tile.features.push_back({ std::move(new_multi), props, id }); - break; - } - } - - mapbox::geometry::point transform(const vt_point& p) { - ++tile.num_simplified; - return { static_cast(::round((p.x * z2 - x) * extent)), - static_cast(::round((p.y * z2 - y) * extent)) }; - } - - mapbox::geometry::multi_point transform(const vt_multi_point& points) { - mapbox::geometry::multi_point result; - result.reserve(points.size()); - for (const auto& p : points) { - result.push_back(transform(p)); - } - return result; - } - - mapbox::geometry::line_string transform(const vt_line_string& line) { - mapbox::geometry::line_string result; - if (line.dist > tolerance) { - for (const auto& p : line) { - if (p.z > sq_tolerance) - result.push_back(transform(p)); - } - } - return result; - } - - mapbox::geometry::linear_ring transform(const vt_linear_ring& ring) { - mapbox::geometry::linear_ring result; - if (ring.area > sq_tolerance) { - for (const auto& p : ring) { - if (p.z > sq_tolerance) - result.push_back(transform(p)); - } - } - return result; - } - - mapbox::geometry::multi_line_string transform(const vt_multi_line_string& lines) { - mapbox::geometry::multi_line_string result; - for (const auto& line : lines) { - if (line.dist > tolerance) - result.push_back(transform(line)); - } - return result; - } - - mapbox::geometry::polygon transform(const vt_polygon& rings) { - mapbox::geometry::polygon result; - for (const auto& ring : rings) { - if (ring.area > sq_tolerance) - result.push_back(transform(ring)); - } - return result; - } - - mapbox::geometry::multi_polygon transform(const vt_multi_polygon& polygons) { - mapbox::geometry::multi_polygon result; - for (const auto& polygon : polygons) { - const auto p = transform(polygon); - if (!p.empty()) - result.push_back(std::move(p)); - } - return result; - } -}; - -} // namespace detail -} // namespace geojsonvt -} // namespace mapbox diff --git a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/types.hpp b/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/types.hpp deleted file mode 100644 index b54357a7b9..0000000000 --- a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/types.hpp +++ /dev/null @@ -1,156 +0,0 @@ -#pragma once - -#include -#include - -#include -#include -#include -#include - -namespace mapbox { -namespace geojsonvt { -namespace detail { - -struct vt_point : mapbox::geometry::point { - double z = 0.0; // simplification tolerance - - vt_point(double x_, double y_, double z_) : mapbox::geometry::point(x_, y_), z(z_) { - } - - vt_point(double x_, double y_) : vt_point(x_, y_, 0.0) { - } -}; - -template -inline double get(const T&); - -template <> -inline double get<0>(const vt_point& p) { - return p.x; -} -template <> -inline double get<1>(const vt_point& p) { - return p.y; -} -template <> -inline double get<0>(const mapbox::geometry::point& p) { - return p.x; -} -template <> -inline double get<1>(const mapbox::geometry::point& p) { - return p.y; -} - -template -inline vt_point intersect(const vt_point&, const vt_point&, const double); - -template <> -inline vt_point intersect<0>(const vt_point& a, const vt_point& b, const double x) { - const double y = (x - a.x) * (b.y - a.y) / (b.x - a.x) + a.y; - return { x, y, 1.0 }; -} -template <> -inline vt_point intersect<1>(const vt_point& a, const vt_point& b, const double y) { - const double x = (y - a.y) * (b.x - a.x) / (b.y - a.y) + a.x; - return { x, y, 1.0 }; -} - -using vt_multi_point = std::vector; - -struct vt_line_string : std::vector { - using container_type = std::vector; - using container_type::container_type; - double dist = 0.0; // line length -}; - -struct vt_linear_ring : std::vector { - using container_type = std::vector; - using container_type::container_type; - double area = 0.0; // polygon ring area -}; - -using vt_multi_line_string = std::vector; -using vt_polygon = std::vector; -using vt_multi_polygon = std::vector; - -struct vt_geometry_collection; - -using vt_geometry = mapbox::util::variant; - -struct vt_geometry_collection : std::vector {}; - -using property_map = mapbox::geometry::property_map; -using identifier = mapbox::geometry::identifier; - -template -using optional = std::experimental::optional; - -template -struct vt_geometry_type; - -template <> -struct vt_geometry_type> { - using type = vt_point; -}; -template <> -struct vt_geometry_type> { - using type = vt_line_string; -}; -template <> -struct vt_geometry_type> { - using type = vt_polygon; -}; -template <> -struct vt_geometry_type> { - using type = vt_multi_point; -}; -template <> -struct vt_geometry_type> { - using type = vt_multi_line_string; -}; -template <> -struct vt_geometry_type> { - using type = vt_multi_polygon; -}; -template <> -struct vt_geometry_type> { - using type = vt_geometry; -}; -template <> -struct vt_geometry_type> { - using type = vt_geometry_collection; -}; - -struct vt_feature { - vt_geometry geometry; - property_map properties; - optional id; - - mapbox::geometry::box bbox = { { 2, 1 }, { -1, 0 } }; - uint32_t num_points = 0; - - vt_feature(const vt_geometry& geom, const property_map& props, const optional& id_) - : geometry(geom), properties(props), id(id_) { - - mapbox::geometry::for_each_point(geom, [&](const vt_point& p) { - bbox.min.x = std::min(p.x, bbox.min.x); - bbox.min.y = std::min(p.y, bbox.min.y); - bbox.max.x = std::max(p.x, bbox.max.x); - bbox.max.y = std::max(p.y, bbox.max.y); - ++num_points; - }); - } -}; - -using vt_features = std::vector; - -} // namespace detail -} // namespace geojsonvt -} // namespace mapbox diff --git a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/wrap.hpp b/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/wrap.hpp deleted file mode 100644 index 495ccc5800..0000000000 --- a/deps/geojsonvt/6.2.1/include/mapbox/geojsonvt/wrap.hpp +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include -#include - -namespace mapbox { -namespace geojsonvt { -namespace detail { - -inline void shiftCoords(vt_features& features, double offset) { - for (auto& feature : features) { - mapbox::geometry::for_each_point(feature.geometry, - [offset](vt_point& point) { point.x += offset; }); - feature.bbox.min.x += offset; - feature.bbox.max.x += offset; - } -} - -inline vt_features wrap(const vt_features& features, double buffer) { - // left world copy - auto left = clip<0>(features, -1 - buffer, buffer, -1, 2); - // right world copy - auto right = clip<0>(features, 1 - buffer, 2 + buffer, -1, 2); - - if (left.empty() && right.empty()) - return features; - - // center world copy - auto merged = clip<0>(features, -buffer, 1 + buffer, -1, 2); - - if (!left.empty()) { - // merge left into center - shiftCoords(left, 1.0); - merged.insert(merged.begin(), left.begin(), left.end()); - } - if (!right.empty()) { - // merge right into center - shiftCoords(right, -1.0); - merged.insert(merged.end(), right.begin(), right.end()); - } - return merged; -} - -} // namespace detail -} // namespace geojsonvt -} // namespace mapbox diff --git a/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt.hpp b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt.hpp new file mode 100644 index 0000000000..ee1bbbca8d --- /dev/null +++ b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt.hpp @@ -0,0 +1,271 @@ +#pragma once + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace mapbox { +namespace geojsonvt { + +using geometry = mapbox::geometry::geometry; +using feature = mapbox::geometry::feature; +using feature_collection = mapbox::geometry::feature_collection; +using geometry_collection = mapbox::geometry::geometry_collection; +using geojson = mapbox::util::variant; + +struct ToFeatureCollection { + feature_collection operator()(const feature_collection& value) const { + return value; + } + feature_collection operator()(const feature& value) const { + return { value }; + } + feature_collection operator()(const geometry& value) const { + return { { value } }; + } +}; + +struct TileOptions { + // simplification tolerance (higher means simpler) + double tolerance = 3; + + // tile extent + uint16_t extent = 4096; + + // tile buffer on each side + uint16_t buffer = 64; +}; + +struct Options : TileOptions { + // max zoom to preserve detail on + uint8_t maxZoom = 18; + + // max zoom in the tile index + uint8_t indexMaxZoom = 5; + + // max number of points per tile in the tile index + uint32_t indexMaxPoints = 100000; + + // whether to tile solid square tiles further + bool solidChildren = false; +}; + +const Tile empty_tile{}; + +inline uint64_t toID(uint8_t z, uint32_t x, uint32_t y) { + return (((1ull << z) * y + x) * 32) + z; +} + +inline const Tile geoJSONToTile(const geojson& geojson_, + uint8_t z, + uint32_t x, + uint32_t y, + const TileOptions& options = TileOptions(), + bool wrap = false, + bool clip = false) { + + const auto features_ = geojson::visit(geojson_, ToFeatureCollection{}); + auto z2 = 1u << z; + auto tolerance = (options.tolerance / options.extent) / z2; + auto features = detail::convert(features_, tolerance); + if (wrap) { + features = detail::wrap(features, double(options.buffer) / options.extent); + } + if (clip) { + const double p = options.buffer / options.extent; + + const auto left = detail::clip<0>(features, (x - p) / z2, (x + 1 + p) / z2, -1, 2); + features = detail::clip<1>(left, (y - p) / z2, (y + 1 + p) / z2, -1, 2); + } + return detail::InternalTile({ features, z, x, y, options.extent, options.buffer, tolerance }) + .tile; +} + +class GeoJSONVT { +public: + const Options options; + + GeoJSONVT(const mapbox::geometry::feature_collection& features_, + const Options& options_ = Options()) + : options(options_) { + + const uint32_t z2 = 1u << options.maxZoom; + + auto converted = detail::convert(features_, (options.tolerance / options.extent) / z2); + auto features = detail::wrap(converted, double(options.buffer) / options.extent); + + splitTile(features, 0, 0, 0); + } + + GeoJSONVT(const geojson& geojson_, const Options& options_ = Options()) + : GeoJSONVT(geojson::visit(geojson_, ToFeatureCollection{}), options_) { + } + + std::map stats; + uint32_t total = 0; + + const Tile& getTile(const uint8_t z, const uint32_t x_, const uint32_t y) { + + if (z > options.maxZoom) + throw std::runtime_error("Requested zoom higher than maxZoom: " + std::to_string(z)); + + const uint32_t z2 = 1u << z; + const uint32_t x = ((x_ % z2) + z2) % z2; // wrap tile x coordinate + const uint64_t id = toID(z, x, y); + + auto it = tiles.find(id); + if (it != tiles.end()) + return it->second.tile; + + it = findParent(z, x, y); + + if (it == tiles.end()) + throw std::runtime_error("Parent tile not found"); + + // if we found a parent tile containing the original geometry, we can drill down from it + const auto& parent = it->second; + + // parent tile is a solid clipped square, return it instead since it's identical + if (parent.is_solid) + return parent.tile; + + // drill down parent tile up to the requested one + splitTile(parent.source_features, parent.z, parent.x, parent.y, z, x, y); + + it = tiles.find(id); + if (it != tiles.end()) + return it->second.tile; + + it = findParent(z, x, y); + if (it == tiles.end()) + throw std::runtime_error("Parent tile not found"); + + // drilling stopped because parent was a solid square; return it instead + if (it->second.is_solid) + return it->second.tile; + + // otherwise it was an empty tile + return empty_tile; + } + + const std::unordered_map& getInternalTiles() const { + return tiles; + } + +private: + std::unordered_map tiles; + + std::unordered_map::iterator + findParent(const uint8_t z, const uint32_t x, const uint32_t y) { + uint8_t z0 = z; + uint32_t x0 = x; + uint32_t y0 = y; + + const auto end = tiles.end(); + auto parent = end; + + while ((parent == end) && (z0 != 0)) { + z0--; + x0 = x0 / 2; + y0 = y0 / 2; + parent = tiles.find(toID(z0, x0, y0)); + } + + return parent; + } + + void splitTile(const detail::vt_features& features, + const uint8_t z, + const uint32_t x, + const uint32_t y, + const uint8_t cz = 0, + const uint32_t cx = 0, + const uint32_t cy = 0) { + + const double z2 = 1u << z; + const uint64_t id = toID(z, x, y); + + auto it = tiles.find(id); + + if (it == tiles.end()) { + const double tolerance = + (z == options.maxZoom ? 0 : options.tolerance / (z2 * options.extent)); + + it = tiles + .emplace(id, detail::InternalTile{ features, z, x, y, options.extent, + options.buffer, tolerance }) + .first; + stats[z] = (stats.count(z) ? stats[z] + 1 : 1); + total++; + // printf("tile z%i-%i-%i\n", z, x, y); + } + + auto& tile = it->second; + + if (features.empty()) + return; + + // stop tiling if the tile is solid clipped square + if (!options.solidChildren && tile.is_solid) + return; + + // if it's the first-pass tiling + if (cz == 0u) { + // stop tiling if we reached max zoom, or if the tile is too simple + if (z == options.indexMaxZoom || tile.tile.num_points <= options.indexMaxPoints) { + tile.source_features = features; + return; + } + + } else { // drilldown to a specific tile; + // stop tiling if we reached base zoom + if (z == options.maxZoom) + return; + + // stop tiling if it's our target tile zoom + if (z == cz) { + tile.source_features = features; + return; + } + + // stop tiling if it's not an ancestor of the target tile + const double m = 1u << (cz - z); + if (x != static_cast(std::floor(cx / m)) || + y != static_cast(std::floor(cy / m))) { + tile.source_features = features; + return; + } + } + + const double p = 0.5 * options.buffer / options.extent; + const auto& min = tile.bbox.min; + const auto& max = tile.bbox.max; + + const auto left = detail::clip<0>(features, (x - p) / z2, (x + 0.5 + p) / z2, min.x, max.x); + + splitTile(detail::clip<1>(left, (y - p) / z2, (y + 0.5 + p) / z2, min.y, max.y), z + 1, + x * 2, y * 2, cz, cx, cy); + splitTile(detail::clip<1>(left, (y + 0.5 - p) / z2, (y + 1 + p) / z2, min.y, max.y), z + 1, + x * 2, y * 2 + 1, cz, cx, cy); + + const auto right = + detail::clip<0>(features, (x + 0.5 - p) / z2, (x + 1 + p) / z2, min.x, max.x); + + splitTile(detail::clip<1>(right, (y - p) / z2, (y + 0.5 + p) / z2, min.y, max.y), z + 1, + x * 2 + 1, y * 2, cz, cx, cy); + splitTile(detail::clip<1>(right, (y + 0.5 - p) / z2, (y + 1 + p) / z2, min.y, max.y), z + 1, + x * 2 + 1, y * 2 + 1, cz, cx, cy); + + // if we sliced further down, no need to keep source geometry + tile.source_features = {}; + } +}; + +} // namespace geojsonvt +} // namespace mapbox diff --git a/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/clip.hpp b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/clip.hpp new file mode 100644 index 0000000000..212e2979d0 --- /dev/null +++ b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/clip.hpp @@ -0,0 +1,251 @@ +#pragma once + +#include + +namespace mapbox { +namespace geojsonvt { +namespace detail { + +template +class clipper { +public: + const double k1; + const double k2; + + vt_geometry operator()(const vt_point& point) const { + return point; + } + + vt_geometry operator()(const vt_multi_point& points) const { + vt_multi_point part; + for (const auto& p : points) { + const double ak = get(p); + if (ak >= k1 && ak <= k2) + part.push_back(p); + } + return part; + } + + vt_geometry operator()(const vt_line_string& line) const { + vt_multi_line_string parts; + clipLine(line, parts); + if (parts.size() == 1) + return parts[0]; + else + return parts; + } + + vt_geometry operator()(const vt_multi_line_string& lines) const { + vt_multi_line_string parts; + for (const auto& line : lines) { + clipLine(line, parts); + } + if (parts.size() == 1) + return parts[0]; + else + return parts; + } + + vt_geometry operator()(const vt_polygon& polygon) const { + vt_polygon result; + for (const auto& ring : polygon) { + const auto new_ring = clipRing(ring); + if (!new_ring.empty()) + result.push_back(new_ring); + } + return result; + } + + vt_geometry operator()(const vt_multi_polygon& polygons) const { + vt_multi_polygon result; + for (const auto& polygon : polygons) { + vt_polygon p; + for (const auto& ring : polygon) { + const auto new_ring = clipRing(ring); + if (!new_ring.empty()) + p.push_back(new_ring); + } + if (!p.empty()) + result.push_back(p); + } + return result; + } + + vt_geometry operator()(const vt_geometry_collection& geometries) const { + vt_geometry_collection result; + for (const auto& geometry : geometries) { + vt_geometry::visit(geometry, + [&](const auto& g) { result.push_back(this->operator()(g)); }); + } + return result; + } + +private: + vt_line_string newSlice(vt_multi_line_string& parts, vt_line_string& slice, double dist) const { + if (!slice.empty()) { + slice.dist = dist; + parts.push_back(std::move(slice)); + } + return {}; + } + + void clipLine(const vt_line_string& line, vt_multi_line_string& slices) const { + + const double dist = line.dist; + const size_t len = line.size(); + + if (len < 2) + return; + + vt_line_string slice; + + for (size_t i = 0; i < (len - 1); ++i) { + const auto& a = line[i]; + const auto& b = line[i + 1]; + const double ak = get(a); + const double bk = get(b); + + if (ak < k1) { + if (bk > k2) { // ---|-----|--> + slice.push_back(intersect(a, b, k1)); + slice.push_back(intersect(a, b, k2)); + slice = newSlice(slices, slice, dist); + + } else if (bk >= k1) { // ---|--> | + slice.push_back(intersect(a, b, k1)); + if (i == len - 2) + slice.push_back(b); // last point + } + } else if (ak > k2) { + if (bk < k1) { // <--|-----|--- + slice.push_back(intersect(a, b, k2)); + slice.push_back(intersect(a, b, k1)); + slice = newSlice(slices, slice, dist); + + } else if (bk <= k2) { // | <--|--- + slice.push_back(intersect(a, b, k2)); + if (i == len - 2) + slice.push_back(b); // last point + } + } else { + slice.push_back(a); + + if (bk < k1) { // <--|--- | + slice.push_back(intersect(a, b, k1)); + slice = newSlice(slices, slice, dist); + + } else if (bk > k2) { // | ---|--> + slice.push_back(intersect(a, b, k2)); + slice = newSlice(slices, slice, dist); + + } else if (i == len - 2) { // | --> | + slice.push_back(b); + } + } + } + + // add the final slice + newSlice(slices, slice, dist); + } + + vt_linear_ring clipRing(const vt_linear_ring& ring) const { + const size_t len = ring.size(); + + vt_linear_ring slice; + slice.area = ring.area; + + if (len < 2) + return slice; + + for (size_t i = 0; i < (len - 1); ++i) { + const auto& a = ring[i]; + const auto& b = ring[i + 1]; + const double ak = get(a); + const double bk = get(b); + + if (ak < k1) { + if (bk >= k1) { + slice.push_back(intersect(a, b, k1)); // ---|--> | + if (bk > k2) // ---|-----|--> + slice.push_back(intersect(a, b, k2)); + else if (i == len - 2) + slice.push_back(b); // last point + } + } else if (ak > k2) { + if (bk <= k2) { // | <--|--- + slice.push_back(intersect(a, b, k2)); + if (bk < k1) // <--|-----|--- + slice.push_back(intersect(a, b, k1)); + else if (i == len - 2) + slice.push_back(b); // last point + } + } else { + slice.push_back(a); + if (bk < k1) // <--|--- | + slice.push_back(intersect(a, b, k1)); + else if (bk > k2) // | ---|--> + slice.push_back(intersect(a, b, k2)); + // | --> | + } + } + + // close the polygon if its endpoints are not the same after clipping + if (!slice.empty()) { + const auto& first = slice.front(); + const auto& last = slice.back(); + if (first != last) { + slice.push_back(first); + } + } + + return slice; + } +}; + +/* clip features between two axis-parallel lines: + * | | + * ___|___ | / + * / | \____|____/ + * | | + */ + +template +inline vt_features clip(const vt_features& features, + const double k1, + const double k2, + const double minAll, + const double maxAll) { + + if (minAll >= k1 && maxAll <= k2) // trivial accept + return features; + + if (minAll > k2 || maxAll < k1) // trivial reject + return {}; + + vt_features clipped; + + for (const auto& feature : features) { + const auto& geom = feature.geometry; + const auto& props = feature.properties; + const auto& id = feature.id; + + const double min = get(feature.bbox.min); + const double max = get(feature.bbox.max); + + if (min >= k1 && max <= k2) { // trivial accept + clipped.push_back(feature); + + } else if (min > k2 || max < k1) { // trivial reject + continue; + + } else { + clipped.emplace_back(vt_geometry::visit(geom, clipper{ k1, k2 }), props, id); + } + } + + return clipped; +} + +} // namespace detail +} // namespace geojsonvt +} // namespace mapbox diff --git a/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/convert.hpp b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/convert.hpp new file mode 100644 index 0000000000..ba28f449b3 --- /dev/null +++ b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/convert.hpp @@ -0,0 +1,110 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace mapbox { +namespace geojsonvt { +namespace detail { + +struct project { + const double tolerance; + using result_type = vt_geometry; + + vt_point operator()(const geometry::point& p) { + const double sine = std::sin(p.y * M_PI / 180); + const double x = p.x / 360 + 0.5; + const double y = + std::max(std::min(0.5 - 0.25 * std::log((1 + sine) / (1 - sine)) / M_PI, 1.0), 0.0); + return { x, y, 0.0 }; + } + + vt_line_string operator()(const geometry::line_string& points) { + vt_line_string result; + const size_t len = points.size(); + + if (len == 0) + return result; + + result.reserve(len); + + for (const auto& p : points) { + result.push_back(operator()(p)); + } + + for (size_t i = 0; i < len - 1; ++i) { + const auto& a = result[i]; + const auto& b = result[i + 1]; + // use Manhattan distance instead of Euclidian to avoid expensive square root + // computation + result.dist += std::abs(b.x - a.x) + std::abs(b.y - a.y); + } + + simplify(result, tolerance); + + return result; + } + + vt_linear_ring operator()(const geometry::linear_ring& ring) { + vt_linear_ring result; + const size_t len = ring.size(); + + if (len == 0) + return result; + + result.reserve(len); + + for (const auto& p : ring) { + result.push_back(operator()(p)); + } + + double area = 0.0; + + for (size_t i = 0; i < len - 1; ++i) { + const auto& a = result[i]; + const auto& b = result[i + 1]; + area += a.x * b.y - b.x * a.y; + } + result.area = std::abs(area / 2); + + simplify(result, tolerance); + + return result; + } + + vt_geometry operator()(const geometry::geometry& geometry) { + return geometry::geometry::visit(geometry, project{ tolerance }); + } + + // Handles polygon, multi_*, geometry_collection. + template + auto operator()(const T& vector) { + typename vt_geometry_type::type result; + result.reserve(vector.size()); + for (const auto& e : vector) { + result.push_back(operator()(e)); + } + return result; + } +}; + +inline vt_features convert(const geometry::feature_collection& features, + const double tolerance) { + vt_features projected; + projected.reserve(features.size()); + for (const auto& feature : features) { + projected.emplace_back( + geometry::geometry::visit(feature.geometry, project{ tolerance }), + feature.properties, + feature.id); + } + return projected; +} + +} // namespace detail +} // namespace geojsonvt +} // namespace mapbox diff --git a/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/simplify.hpp b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/simplify.hpp new file mode 100644 index 0000000000..be0165b0f1 --- /dev/null +++ b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/simplify.hpp @@ -0,0 +1,72 @@ +#pragma once + +#include + +namespace mapbox { +namespace geojsonvt { +namespace detail { + +// square distance from a point to a segment +inline double getSqSegDist(const vt_point& p, const vt_point& a, const vt_point& b) { + double x = a.x; + double y = a.y; + double dx = b.x - a.x; + double dy = b.y - a.y; + + if ((dx != 0.0) || (dy != 0.0)) { + + const double t = ((p.x - a.x) * dx + (p.y - a.y) * dy) / (dx * dx + dy * dy); + + if (t > 1) { + x = b.x; + y = b.y; + + } else if (t > 0) { + x += dx * t; + y += dy * t; + } + } + + dx = p.x - x; + dy = p.y - y; + + return dx * dx + dy * dy; +} + +// calculate simplification data using optimized Douglas-Peucker algorithm +inline void simplify(std::vector& points, size_t first, size_t last, double sqTolerance) { + double maxSqDist = sqTolerance; + size_t index = 0; + + for (auto i = first + 1; i < last; i++) { + const double sqDist = getSqSegDist(points[i], points[first], points[last]); + + if (sqDist > maxSqDist) { + index = i; + maxSqDist = sqDist; + } + } + + if (maxSqDist > sqTolerance) { + // save the point importance in squared pixels as a z coordinate + points[index].z = maxSqDist; + if (index - first > 1) + simplify(points, first, index, sqTolerance); + if (last - index > 1) + simplify(points, index, last, sqTolerance); + } +} + +inline void simplify(std::vector& points, double tolerance) { + const size_t len = points.size(); + + // always retain the endpoints (1 is the max value) + points[0].z = 1.0; + points[len - 1].z = 1.0; + + simplify(points, 0, len - 1, tolerance * tolerance); +} + +} // namespace detail +} // namespace geojsonvt +} // namespace mapbox diff --git a/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/tile.hpp b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/tile.hpp new file mode 100644 index 0000000000..ccb230bb9c --- /dev/null +++ b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/tile.hpp @@ -0,0 +1,207 @@ +#pragma once + +#include +#include +#include + +namespace mapbox { +namespace geojsonvt { + +struct Tile { + mapbox::geometry::feature_collection features; + uint32_t num_points = 0; + uint32_t num_simplified = 0; +}; + +namespace detail { + +class InternalTile { +public: + const uint8_t z; + const uint32_t x; + const uint32_t y; + + vt_features source_features; + bool is_solid = false; + mapbox::geometry::box bbox = { { 2, 1 }, { -1, 0 } }; + + Tile tile; + + InternalTile(const vt_features& source, + const uint8_t z_, + const uint32_t x_, + const uint32_t y_, + const uint16_t extent_, + const uint16_t buffer, + const double tolerance_) + : z(z_), + x(x_), + y(y_), + z2(std::pow(2, z)), + extent(extent_), + tolerance(tolerance_), + sq_tolerance(tolerance_ * tolerance_) { + + for (const auto& feature : source) { + const auto& geom = feature.geometry; + const auto& props = feature.properties; + const auto& id = feature.id; + + tile.num_points += feature.num_points; + + vt_geometry::visit(geom, [&](const auto& g) { + // `this->` is a workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61636 + this->addFeature(g, props, id); + }); + + bbox.min.x = std::min(feature.bbox.min.x, bbox.min.x); + bbox.min.y = std::min(feature.bbox.min.y, bbox.min.y); + bbox.max.x = std::max(feature.bbox.max.x, bbox.max.x); + bbox.max.y = std::max(feature.bbox.max.y, bbox.max.y); + } + + is_solid = isSolid(buffer); + } + +private: + const double z2; + const uint16_t extent; + const double tolerance; + const double sq_tolerance; + + bool isSolid(const uint16_t buffer) { + if (tile.features.size() != 1) + return false; + + const auto& geom = tile.features.front().geometry; + if (!geom.is>()) + return false; + + const auto& rings = geom.get>(); + if (rings.size() > 1) + return false; + + const auto& ring = rings.front(); + if (ring.size() != 5) + return false; + + const int16_t min = -static_cast(buffer); + const int16_t max = static_cast(extent + buffer); + for (const auto& p : ring) { + if ((p.x != min && p.x != max) || (p.y != min && p.y != max)) + return false; + } + + return true; + } + + void addFeature(const vt_point& point, const property_map& props, const optional& id) { + tile.features.push_back({ transform(point), props, id }); + } + + void addFeature(const vt_line_string& line, const property_map& props, const optional& id) { + const auto new_line = transform(line); + if (!new_line.empty()) + tile.features.push_back({ std::move(new_line), props, id }); + } + + void addFeature(const vt_polygon& polygon, const property_map& props, const optional& id) { + const auto new_polygon = transform(polygon); + if (!new_polygon.empty()) + tile.features.push_back({ std::move(new_polygon), props, id }); + } + + void addFeature(const vt_geometry_collection& collection, const property_map& props, const optional& id) { + for (const auto& geom : collection) { + vt_geometry::visit(geom, [&](const auto& g) { + // `this->` is a workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61636 + this->addFeature(g, props, id); + }); + } + } + + template + void addFeature(const T& multi, const property_map& props, const optional& id) { + const auto new_multi = transform(multi); + + switch (new_multi.size()) { + case 0: + break; + case 1: + tile.features.push_back({ std::move(new_multi[0]), props, id }); + break; + default: + tile.features.push_back({ std::move(new_multi), props, id }); + break; + } + } + + mapbox::geometry::point transform(const vt_point& p) { + ++tile.num_simplified; + return { static_cast(::round((p.x * z2 - x) * extent)), + static_cast(::round((p.y * z2 - y) * extent)) }; + } + + mapbox::geometry::multi_point transform(const vt_multi_point& points) { + mapbox::geometry::multi_point result; + result.reserve(points.size()); + for (const auto& p : points) { + result.push_back(transform(p)); + } + return result; + } + + mapbox::geometry::line_string transform(const vt_line_string& line) { + mapbox::geometry::line_string result; + if (line.dist > tolerance) { + for (const auto& p : line) { + if (p.z > sq_tolerance) + result.push_back(transform(p)); + } + } + return result; + } + + mapbox::geometry::linear_ring transform(const vt_linear_ring& ring) { + mapbox::geometry::linear_ring result; + if (ring.area > sq_tolerance) { + for (const auto& p : ring) { + if (p.z > sq_tolerance) + result.push_back(transform(p)); + } + } + return result; + } + + mapbox::geometry::multi_line_string transform(const vt_multi_line_string& lines) { + mapbox::geometry::multi_line_string result; + for (const auto& line : lines) { + if (line.dist > tolerance) + result.push_back(transform(line)); + } + return result; + } + + mapbox::geometry::polygon transform(const vt_polygon& rings) { + mapbox::geometry::polygon result; + for (const auto& ring : rings) { + if (ring.area > sq_tolerance) + result.push_back(transform(ring)); + } + return result; + } + + mapbox::geometry::multi_polygon transform(const vt_multi_polygon& polygons) { + mapbox::geometry::multi_polygon result; + for (const auto& polygon : polygons) { + const auto p = transform(polygon); + if (!p.empty()) + result.push_back(std::move(p)); + } + return result; + } +}; + +} // namespace detail +} // namespace geojsonvt +} // namespace mapbox diff --git a/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/types.hpp b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/types.hpp new file mode 100644 index 0000000000..b54357a7b9 --- /dev/null +++ b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/types.hpp @@ -0,0 +1,156 @@ +#pragma once + +#include +#include + +#include +#include +#include +#include + +namespace mapbox { +namespace geojsonvt { +namespace detail { + +struct vt_point : mapbox::geometry::point { + double z = 0.0; // simplification tolerance + + vt_point(double x_, double y_, double z_) : mapbox::geometry::point(x_, y_), z(z_) { + } + + vt_point(double x_, double y_) : vt_point(x_, y_, 0.0) { + } +}; + +template +inline double get(const T&); + +template <> +inline double get<0>(const vt_point& p) { + return p.x; +} +template <> +inline double get<1>(const vt_point& p) { + return p.y; +} +template <> +inline double get<0>(const mapbox::geometry::point& p) { + return p.x; +} +template <> +inline double get<1>(const mapbox::geometry::point& p) { + return p.y; +} + +template +inline vt_point intersect(const vt_point&, const vt_point&, const double); + +template <> +inline vt_point intersect<0>(const vt_point& a, const vt_point& b, const double x) { + const double y = (x - a.x) * (b.y - a.y) / (b.x - a.x) + a.y; + return { x, y, 1.0 }; +} +template <> +inline vt_point intersect<1>(const vt_point& a, const vt_point& b, const double y) { + const double x = (y - a.y) * (b.x - a.x) / (b.y - a.y) + a.x; + return { x, y, 1.0 }; +} + +using vt_multi_point = std::vector; + +struct vt_line_string : std::vector { + using container_type = std::vector; + using container_type::container_type; + double dist = 0.0; // line length +}; + +struct vt_linear_ring : std::vector { + using container_type = std::vector; + using container_type::container_type; + double area = 0.0; // polygon ring area +}; + +using vt_multi_line_string = std::vector; +using vt_polygon = std::vector; +using vt_multi_polygon = std::vector; + +struct vt_geometry_collection; + +using vt_geometry = mapbox::util::variant; + +struct vt_geometry_collection : std::vector {}; + +using property_map = mapbox::geometry::property_map; +using identifier = mapbox::geometry::identifier; + +template +using optional = std::experimental::optional; + +template +struct vt_geometry_type; + +template <> +struct vt_geometry_type> { + using type = vt_point; +}; +template <> +struct vt_geometry_type> { + using type = vt_line_string; +}; +template <> +struct vt_geometry_type> { + using type = vt_polygon; +}; +template <> +struct vt_geometry_type> { + using type = vt_multi_point; +}; +template <> +struct vt_geometry_type> { + using type = vt_multi_line_string; +}; +template <> +struct vt_geometry_type> { + using type = vt_multi_polygon; +}; +template <> +struct vt_geometry_type> { + using type = vt_geometry; +}; +template <> +struct vt_geometry_type> { + using type = vt_geometry_collection; +}; + +struct vt_feature { + vt_geometry geometry; + property_map properties; + optional id; + + mapbox::geometry::box bbox = { { 2, 1 }, { -1, 0 } }; + uint32_t num_points = 0; + + vt_feature(const vt_geometry& geom, const property_map& props, const optional& id_) + : geometry(geom), properties(props), id(id_) { + + mapbox::geometry::for_each_point(geom, [&](const vt_point& p) { + bbox.min.x = std::min(p.x, bbox.min.x); + bbox.min.y = std::min(p.y, bbox.min.y); + bbox.max.x = std::max(p.x, bbox.max.x); + bbox.max.y = std::max(p.y, bbox.max.y); + ++num_points; + }); + } +}; + +using vt_features = std::vector; + +} // namespace detail +} // namespace geojsonvt +} // namespace mapbox diff --git a/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/wrap.hpp b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/wrap.hpp new file mode 100644 index 0000000000..495ccc5800 --- /dev/null +++ b/deps/geojsonvt/6.3.0/include/mapbox/geojsonvt/wrap.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include +#include + +namespace mapbox { +namespace geojsonvt { +namespace detail { + +inline void shiftCoords(vt_features& features, double offset) { + for (auto& feature : features) { + mapbox::geometry::for_each_point(feature.geometry, + [offset](vt_point& point) { point.x += offset; }); + feature.bbox.min.x += offset; + feature.bbox.max.x += offset; + } +} + +inline vt_features wrap(const vt_features& features, double buffer) { + // left world copy + auto left = clip<0>(features, -1 - buffer, buffer, -1, 2); + // right world copy + auto right = clip<0>(features, 1 - buffer, 2 + buffer, -1, 2); + + if (left.empty() && right.empty()) + return features; + + // center world copy + auto merged = clip<0>(features, -buffer, 1 + buffer, -1, 2); + + if (!left.empty()) { + // merge left into center + shiftCoords(left, 1.0); + merged.insert(merged.begin(), left.begin(), left.end()); + } + if (!right.empty()) { + // merge right into center + shiftCoords(right, -1.0); + merged.insert(merged.end(), right.begin(), right.end()); + } + return merged; +} + +} // namespace detail +} // namespace geojsonvt +} // namespace mapbox diff --git a/include/mbgl/actor/message.hpp b/include/mbgl/actor/message.hpp index 406de425d4..0a20993352 100644 --- a/include/mbgl/actor/message.hpp +++ b/include/mbgl/actor/message.hpp @@ -64,6 +64,32 @@ public: std::promise promise; }; +template +class AskMessageImpl : public Message { +public: + AskMessageImpl(std::promise promise_, Object& object_, MemberFn memberFn_, ArgsTuple argsTuple_) + : object(object_), + memberFn(memberFn_), + argsTuple(std::move(argsTuple_)), + promise(std::move(promise_)) { + } + + void operator()() override { + ask(std::make_index_sequence::value>()); + promise.set_value(); + } + + template + void ask(std::index_sequence) { + (object.*memberFn)(std::move(std::get(argsTuple))...); + } + + Object& object; + MemberFn memberFn; + ArgsTuple argsTuple; + std::promise promise; +}; + namespace actor { template diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 7d6678dc93..5ba23a76dd 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -42,7 +43,8 @@ public: // Register a callback that will get called (on the render thread) when all resources have // been loaded and a complete render occurs. using StillImageCallback = std::function; - void renderStill(StillImageCallback callback); + void renderStill(StillImageCallback); + void renderStill(const CameraOptions&, MapDebugOptions, StillImageCallback); // Triggers a repaint. void triggerRepaint(); @@ -65,6 +67,10 @@ public: void jumpTo(const CameraOptions&); void easeTo(const CameraOptions&, const AnimationOptions&); void flyTo(const CameraOptions&, const AnimationOptions&); + CameraOptions cameraForLatLngBounds(const LatLngBounds&, const EdgeInsets&, optional bearing = {}) const; + CameraOptions cameraForLatLngs(const std::vector&, const EdgeInsets&, optional bearing = {}) const; + CameraOptions cameraForGeometry(const Geometry&, const EdgeInsets&, optional bearing = {}) const; + LatLngBounds latLngBoundsForCamera(const CameraOptions&) const; // Position void moveBy(const ScreenCoordinate&, const AnimationOptions& = {}); @@ -81,9 +87,6 @@ public: double getZoom() const; void setLatLngZoom(const LatLng&, double zoom, const AnimationOptions& = {}); void setLatLngZoom(const LatLng&, double zoom, const EdgeInsets&, const AnimationOptions& = {}); - CameraOptions cameraForLatLngBounds(const LatLngBounds&, const EdgeInsets&) const; - CameraOptions cameraForLatLngs(const std::vector&, const EdgeInsets&) const; - LatLngBounds latLngBoundsForCamera(const CameraOptions&) const; void resetZoom(); // Bounds @@ -124,6 +127,14 @@ public: void setViewportMode(ViewportMode); ViewportMode getViewportMode() const; + // Projection mode + void setAxonometric(bool); + bool getAxonometric() const; + void setXSkew(double ySkew); + double getXSkew() const; + void setYSkew(double ySkew); + double getYSkew() const; + // Size void setSize(Size); Size getSize() const; diff --git a/include/mbgl/renderer/renderer.hpp b/include/mbgl/renderer/renderer.hpp index 95828a1b79..be8abb2c29 100644 --- a/include/mbgl/renderer/renderer.hpp +++ b/include/mbgl/renderer/renderer.hpp @@ -28,6 +28,8 @@ public: const optional programCacheDir = {}); ~Renderer(); + void markContextLost(); + void setObserver(RendererObserver*); void render(const UpdateParameters&); diff --git a/include/mbgl/renderer/renderer_backend.hpp b/include/mbgl/renderer/renderer_backend.hpp index f7d19a1791..295838c71b 100644 --- a/include/mbgl/renderer/renderer_backend.hpp +++ b/include/mbgl/renderer/renderer_backend.hpp @@ -35,6 +35,8 @@ public: // set to the current state. virtual void bind() = 0; + virtual Size getFramebufferSize() const = 0; + protected: // Called with the name of an OpenGL extension that should be loaded. RendererBackend implementations // must call the API-specific version that obtains the function pointer for this function, diff --git a/include/mbgl/storage/default_file_source.hpp b/include/mbgl/storage/default_file_source.hpp index 9911e0ce67..b9c8de5052 100644 --- a/include/mbgl/storage/default_file_source.hpp +++ b/include/mbgl/storage/default_file_source.hpp @@ -34,7 +34,7 @@ public: uint64_t maximumCacheSize = util::DEFAULT_MAX_CACHE_SIZE); ~DefaultFileSource() override; - bool supportsOptionalRequests() const override { + bool supportsCacheOnlyRequests() const override { return true; } @@ -140,6 +140,7 @@ public: void resume(); // For testing only. + void setOnlineStatus(bool); void put(const Resource&, const Response&); class Impl; diff --git a/include/mbgl/storage/file_source.hpp b/include/mbgl/storage/file_source.hpp index 404c683fdb..0709a1c245 100644 --- a/include/mbgl/storage/file_source.hpp +++ b/include/mbgl/storage/file_source.hpp @@ -24,11 +24,11 @@ public: // not be executed. virtual std::unique_ptr request(const Resource&, Callback) = 0; - // When a file source supports optional requests, it must return true. - // Optional requests are requests that aren't as urgent, but could be useful, e.g. + // When a file source supports consulting a local cache only, it must return true. + // Cache-only requests are requests that aren't as urgent, but could be useful, e.g. // to cover part of the map while loading. The FileSource should only do cheap actions to // retrieve the data, e.g. load it from a cache, but not from the internet. - virtual bool supportsOptionalRequests() const { + virtual bool supportsCacheOnlyRequests() const { return false; } }; diff --git a/include/mbgl/storage/offline.hpp b/include/mbgl/storage/offline.hpp index 818cfe2ba5..ef4a499e83 100644 --- a/include/mbgl/storage/offline.hpp +++ b/include/mbgl/storage/offline.hpp @@ -30,13 +30,15 @@ public: OfflineTilePyramidRegionDefinition(std::string, LatLngBounds, double, double, float); /* Private */ - std::vector tileCover(SourceType, uint16_t tileSize, const Range& zoomRange) const; - + std::vector tileCover(style::SourceType, uint16_t tileSize, const Range& zoomRange) const; + uint64_t tileCount(style::SourceType, uint16_t tileSize, const Range& zoomRange) const; const std::string styleURL; const LatLngBounds bounds; const double minZoom; const double maxZoom; const float pixelRatio; +private: + Range coveringZoomRange(style::SourceType, uint16_t tileSize, const Range& zoomRange) const; }; /* diff --git a/include/mbgl/storage/online_file_source.hpp b/include/mbgl/storage/online_file_source.hpp index ffd75662e6..28d70ce544 100644 --- a/include/mbgl/storage/online_file_source.hpp +++ b/include/mbgl/storage/online_file_source.hpp @@ -24,6 +24,9 @@ public: std::unique_ptr request(const Resource&, Callback) override; + // For testing only. + void setOnlineStatus(bool); + private: friend class OnlineFileRequest; diff --git a/include/mbgl/storage/resource.hpp b/include/mbgl/storage/resource.hpp index 5d44f4869f..318fa389f4 100644 --- a/include/mbgl/storage/resource.hpp +++ b/include/mbgl/storage/resource.hpp @@ -4,6 +4,8 @@ #include #include #include +#include +#include #include @@ -30,18 +32,28 @@ public: int8_t z; }; - enum Necessity : bool { - Optional = false, - Required = true, + enum class LoadingMethod : uint8_t { + None = 0b00, + Cache = 0b01, + Network = 0b10, + + CacheOnly = Cache, + NetworkOnly = Network, + All = Cache | Network, }; - Resource(Kind kind_, std::string url_, optional tileData_ = {}, Necessity necessity_ = Required) + Resource(Kind kind_, + std::string url_, + optional tileData_ = {}, + LoadingMethod loadingMethod_ = LoadingMethod::All) : kind(kind_), - necessity(necessity_), + loadingMethod(loadingMethod_), url(std::move(url_)), tileData(std::move(tileData_)) { } + bool hasLoadingMethod(LoadingMethod method); + static Resource style(const std::string& url); static Resource source(const std::string& url); static Resource tile(const std::string& urlTemplate, @@ -50,7 +62,7 @@ public: int32_t y, int8_t z, Tileset::Scheme scheme, - Necessity = Required); + LoadingMethod = LoadingMethod::All); static Resource glyphs(const std::string& urlTemplate, const FontStack& fontStack, const std::pair& glyphRange); @@ -59,7 +71,7 @@ public: static Resource image(const std::string& url); Kind kind; - Necessity necessity; + LoadingMethod loadingMethod; std::string url; // Includes auxiliary data if this is a tile request. @@ -71,4 +83,21 @@ public: std::shared_ptr priorData; }; + +MBGL_CONSTEXPR Resource::LoadingMethod operator|(Resource::LoadingMethod a, Resource::LoadingMethod b) { + return Resource::LoadingMethod(mbgl::underlying_type(a) | mbgl::underlying_type(b)); +} + +MBGL_CONSTEXPR Resource::LoadingMethod& operator|=(Resource::LoadingMethod& a, Resource::LoadingMethod b) { + return (a = a | b); +} + +MBGL_CONSTEXPR Resource::LoadingMethod operator&(Resource::LoadingMethod a, Resource::LoadingMethod b) { + return Resource::LoadingMethod(mbgl::underlying_type(a) & mbgl::underlying_type(b)); +} + +inline bool Resource::hasLoadingMethod(Resource::LoadingMethod method) { + return (loadingMethod & method) != Resource::LoadingMethod::None; +} + } // namespace mbgl diff --git a/include/mbgl/storage/response.hpp b/include/mbgl/storage/response.hpp index 711f008e83..508400141b 100644 --- a/include/mbgl/storage/response.hpp +++ b/include/mbgl/storage/response.hpp @@ -2,7 +2,6 @@ #include #include -#include #include #include diff --git a/include/mbgl/style/conversion/geojson_options.hpp b/include/mbgl/style/conversion/geojson_options.hpp index 19383d90ce..1c9c18250c 100644 --- a/include/mbgl/style/conversion/geojson_options.hpp +++ b/include/mbgl/style/conversion/geojson_options.hpp @@ -14,6 +14,16 @@ struct Converter { optional operator()(const V& value, Error& error) const { GeoJSONOptions options; + const auto minzoomValue = objectMember(value, "minzoom"); + if (minzoomValue) { + if (toNumber(*minzoomValue)) { + options.minzoom = static_cast(*toNumber(*minzoomValue)); + } else { + error = { "GeoJSON source minzoom value must be a number" }; + return {}; + } + } + const auto maxzoomValue = objectMember(value, "maxzoom"); if (maxzoomValue) { if (toNumber(*maxzoomValue)) { diff --git a/include/mbgl/style/conversion/make_property_setters.hpp b/include/mbgl/style/conversion/make_property_setters.hpp index 9252297d75..59b0e7be32 100644 --- a/include/mbgl/style/conversion/make_property_setters.hpp +++ b/include/mbgl/style/conversion/make_property_setters.hpp @@ -45,17 +45,18 @@ auto makeLayoutPropertySetters() { result["icon-padding"] = &setProperty, &SymbolLayer::setIconPadding>; result["icon-keep-upright"] = &setProperty, &SymbolLayer::setIconKeepUpright>; result["icon-offset"] = &setProperty>, &SymbolLayer::setIconOffset>; + result["icon-anchor"] = &setProperty, &SymbolLayer::setIconAnchor>; result["icon-pitch-alignment"] = &setProperty, &SymbolLayer::setIconPitchAlignment>; result["text-pitch-alignment"] = &setProperty, &SymbolLayer::setTextPitchAlignment>; result["text-rotation-alignment"] = &setProperty, &SymbolLayer::setTextRotationAlignment>; result["text-field"] = &setProperty, &SymbolLayer::setTextField>; result["text-font"] = &setProperty>, &SymbolLayer::setTextFont>; result["text-size"] = &setProperty, &SymbolLayer::setTextSize>; - result["text-max-width"] = &setProperty, &SymbolLayer::setTextMaxWidth>; + result["text-max-width"] = &setProperty, &SymbolLayer::setTextMaxWidth>; result["text-line-height"] = &setProperty, &SymbolLayer::setTextLineHeight>; - result["text-letter-spacing"] = &setProperty, &SymbolLayer::setTextLetterSpacing>; + result["text-letter-spacing"] = &setProperty, &SymbolLayer::setTextLetterSpacing>; result["text-justify"] = &setProperty, &SymbolLayer::setTextJustify>; - result["text-anchor"] = &setProperty, &SymbolLayer::setTextAnchor>; + result["text-anchor"] = &setProperty, &SymbolLayer::setTextAnchor>; result["text-max-angle"] = &setProperty, &SymbolLayer::setTextMaxAngle>; result["text-rotate"] = &setProperty, &SymbolLayer::setTextRotate>; result["text-padding"] = &setProperty, &SymbolLayer::setTextPadding>; diff --git a/include/mbgl/style/layers/custom_layer.hpp b/include/mbgl/style/layers/custom_layer.hpp index 79a353b047..bf3387f95b 100644 --- a/include/mbgl/style/layers/custom_layer.hpp +++ b/include/mbgl/style/layers/custom_layer.hpp @@ -38,6 +38,14 @@ struct CustomLayerRenderParameters { */ using CustomLayerRenderFunction = void (*)(void* context, const CustomLayerRenderParameters&); +/** + * Called when the system has destroyed the underlying GL context. The + * `CustomLayerDeinitializeFunction` will not be called in this case, however + * `CustomLayerInitializeFunction` will be called instead to prepare for a new render. + * + */ +using CustomLayerContextLostFunction = void (*)(void* context); + /** * Destroy any GL state needed by the custom layer, and deallocate context, if necessary. This * method is called once, from the main thread, at a point when the GL context is active. @@ -51,8 +59,16 @@ public: CustomLayer(const std::string& id, CustomLayerInitializeFunction, CustomLayerRenderFunction, + CustomLayerContextLostFunction, CustomLayerDeinitializeFunction, void* context); + + CustomLayer(const std::string& id, + CustomLayerInitializeFunction, + CustomLayerRenderFunction, + CustomLayerDeinitializeFunction, + void* context); + ~CustomLayer() final; // Visibility diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp index 6e355c0057..a72baa0b4e 100644 --- a/include/mbgl/style/layers/symbol_layer.hpp +++ b/include/mbgl/style/layers/symbol_layer.hpp @@ -98,6 +98,10 @@ public: DataDrivenPropertyValue> getIconOffset() const; void setIconOffset(DataDrivenPropertyValue>); + static DataDrivenPropertyValue getDefaultIconAnchor(); + DataDrivenPropertyValue getIconAnchor() const; + void setIconAnchor(DataDrivenPropertyValue); + static PropertyValue getDefaultIconPitchAlignment(); PropertyValue getIconPitchAlignment() const; void setIconPitchAlignment(PropertyValue); @@ -122,25 +126,25 @@ public: DataDrivenPropertyValue getTextSize() const; void setTextSize(DataDrivenPropertyValue); - static PropertyValue getDefaultTextMaxWidth(); - PropertyValue getTextMaxWidth() const; - void setTextMaxWidth(PropertyValue); + static DataDrivenPropertyValue getDefaultTextMaxWidth(); + DataDrivenPropertyValue getTextMaxWidth() const; + void setTextMaxWidth(DataDrivenPropertyValue); static PropertyValue getDefaultTextLineHeight(); PropertyValue getTextLineHeight() const; void setTextLineHeight(PropertyValue); - static PropertyValue getDefaultTextLetterSpacing(); - PropertyValue getTextLetterSpacing() const; - void setTextLetterSpacing(PropertyValue); + static DataDrivenPropertyValue getDefaultTextLetterSpacing(); + DataDrivenPropertyValue getTextLetterSpacing() const; + void setTextLetterSpacing(DataDrivenPropertyValue); static DataDrivenPropertyValue getDefaultTextJustify(); DataDrivenPropertyValue getTextJustify() const; void setTextJustify(DataDrivenPropertyValue); - static DataDrivenPropertyValue getDefaultTextAnchor(); - DataDrivenPropertyValue getTextAnchor() const; - void setTextAnchor(DataDrivenPropertyValue); + static DataDrivenPropertyValue getDefaultTextAnchor(); + DataDrivenPropertyValue getTextAnchor() const; + void setTextAnchor(DataDrivenPropertyValue); static PropertyValue getDefaultTextMaxAngle(); PropertyValue getTextMaxAngle() const; diff --git a/include/mbgl/style/sources/geojson_source.hpp b/include/mbgl/style/sources/geojson_source.hpp index 2dcfec51aa..5bdf1ef957 100644 --- a/include/mbgl/style/sources/geojson_source.hpp +++ b/include/mbgl/style/sources/geojson_source.hpp @@ -12,6 +12,7 @@ namespace style { struct GeoJSONOptions { // GeoJSON-VT options + uint8_t minzoom = 0; uint8_t maxzoom = 18; uint16_t buffer = 128; double tolerance = 0.375; diff --git a/include/mbgl/style/types.hpp b/include/mbgl/style/types.hpp index 44b16f16e7..2ed95f08b8 100644 --- a/include/mbgl/style/types.hpp +++ b/include/mbgl/style/types.hpp @@ -4,7 +4,9 @@ namespace mbgl { -// TODO: should be in public source.hpp header and style namespace +namespace style { + +// TODO: should be in public source.hpp header enum class SourceType : uint8_t { Vector, Raster, @@ -14,8 +16,6 @@ enum class SourceType : uint8_t { Image }; -namespace style { - enum class VisibilityType : bool { Visible, None, @@ -68,7 +68,7 @@ enum class TextJustifyType : uint8_t { Right }; -enum class TextAnchorType : uint8_t { +enum class SymbolAnchorType : uint8_t { Center, Left, Right, diff --git a/include/mbgl/tile/tile_id.hpp b/include/mbgl/tile/tile_id.hpp new file mode 100644 index 0000000000..0457dd3a07 --- /dev/null +++ b/include/mbgl/tile/tile_id.hpp @@ -0,0 +1,263 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +class OverscaledTileID; +class CanonicalTileID; +class UnwrappedTileID; + +// Has integer z/x/y coordinates +// All tiles must be derived from 0/0/0 (=no tiles outside of the main tile pyramid) +// Used for requesting data; represents data tiles that exist out there. +// z is never larger than the source's maxzoom +class CanonicalTileID { +public: + CanonicalTileID(uint8_t z, uint32_t x, uint32_t y); + bool operator==(const CanonicalTileID&) const; + bool operator!=(const CanonicalTileID&) const; + bool operator<(const CanonicalTileID&) const; + bool isChildOf(const CanonicalTileID&) const; + CanonicalTileID scaledTo(uint8_t z) const; + std::array children() const; + + const uint8_t z; + const uint32_t x; + const uint32_t y; +}; + +::std::ostream& operator<<(::std::ostream& os, const CanonicalTileID& rhs); +namespace util { +std::string toString(const CanonicalTileID&); +} // namespace util + +// Has integer z/x/y coordinates +// overscaledZ describes the zoom level this tile is intented to represent, e.g. when parsing data +// z is never larger than the source's maxzoom +// z/x/y describe the +class OverscaledTileID { +public: + OverscaledTileID(uint8_t overscaledZ, int16_t wrap, CanonicalTileID); + OverscaledTileID(uint8_t overscaledZ, int16_t wrap, uint8_t z, uint32_t x, uint32_t y); + OverscaledTileID(uint8_t z, uint32_t x, uint32_t y); + explicit OverscaledTileID(const CanonicalTileID&); + explicit OverscaledTileID(CanonicalTileID&&); + bool operator==(const OverscaledTileID&) const; + bool operator!=(const OverscaledTileID&) const; + bool operator<(const OverscaledTileID&) const; + bool isChildOf(const OverscaledTileID&) const; + uint32_t overscaleFactor() const; + OverscaledTileID scaledTo(uint8_t z) const; + UnwrappedTileID toUnwrapped() const; + + const uint8_t overscaledZ; + const int16_t wrap; + const CanonicalTileID canonical; +}; + +::std::ostream& operator<<(::std::ostream& os, const OverscaledTileID& rhs); +namespace util { +std::string toString(const OverscaledTileID&); +} // namespace util + +// Has integer z/x/y coordinates +// wrap describes tiles that are left/right of the main tile pyramid, e.g. when wrapping the world +// Used for describing what position tiles are getting rendered at (= calc the matrix) +// z is never larger than the source's maxzoom +class UnwrappedTileID { +public: + UnwrappedTileID(uint8_t z, int64_t x, int64_t y); + UnwrappedTileID(int16_t wrap, CanonicalTileID); + bool operator==(const UnwrappedTileID&) const; + bool operator!=(const UnwrappedTileID&) const; + bool operator<(const UnwrappedTileID&) const; + bool isChildOf(const UnwrappedTileID&) const; + std::array children() const; + OverscaledTileID overscaleTo(uint8_t z) const; + float pixelsToTileUnits(float pixelValue, float zoom) const; + + const int16_t wrap; + const CanonicalTileID canonical; +}; + +::std::ostream& operator<<(::std::ostream& os, const UnwrappedTileID& rhs); +namespace util { +std::string toString(const UnwrappedTileID&); +} // namespace util + +inline CanonicalTileID::CanonicalTileID(uint8_t z_, uint32_t x_, uint32_t y_) : z(z_), x(x_), y(y_) { + assert(z <= 32); + assert(x < (1ull << z)); + assert(y < (1ull << z)); +} + +inline bool CanonicalTileID::operator==(const CanonicalTileID& rhs) const { + return z == rhs.z && x == rhs.x && y == rhs.y; +} + +inline bool CanonicalTileID::operator!=(const CanonicalTileID& rhs) const { + return z != rhs.z || x != rhs.x || y != rhs.y; +} + +inline bool CanonicalTileID::operator<(const CanonicalTileID& rhs) const { + return std::tie(z, x, y) < std::tie(rhs.z, rhs.x, rhs.y); +} + +inline bool CanonicalTileID::isChildOf(const CanonicalTileID& parent) const { + // We're first testing for z == 0, to avoid a 32 bit shift, which is undefined. + return parent.z == 0 || + (parent.z < z && parent.x == (x >> (z - parent.z)) && parent.y == (y >> (z - parent.z))); +} + +inline CanonicalTileID CanonicalTileID::scaledTo(uint8_t targetZ) const { + if (targetZ <= z) { + return { targetZ, x >> (z - targetZ), y >> (z - targetZ) }; // parent or same + } else { + return { targetZ, x << (targetZ - z), y << (targetZ - z) }; // child + } +} + +inline std::array CanonicalTileID::children() const { + const uint8_t childZ = z + 1; + const uint32_t childX = x * 2; + const uint32_t childY = y * 2; + return { { + { childZ, childX, childY }, + { childZ, childX, childY + 1 }, + { childZ, childX + 1, childY }, + { childZ, childX + 1, childY + 1 }, + } }; +} + +inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, int16_t wrap_, CanonicalTileID canonical_) + : overscaledZ(overscaledZ_), wrap(wrap_), canonical(std::move(canonical_)) { + assert(overscaledZ >= canonical.z); +} + +inline OverscaledTileID::OverscaledTileID(uint8_t overscaledZ_, int16_t wrap_, uint8_t z, uint32_t x, uint32_t y) + : overscaledZ(overscaledZ_), wrap(wrap_), canonical(z, x, y) { + assert(overscaledZ >= canonical.z); +} + +inline OverscaledTileID::OverscaledTileID(uint8_t z, uint32_t x, uint32_t y) + : overscaledZ(z), wrap(0), canonical(z, x, y) { +} + +inline OverscaledTileID::OverscaledTileID(const CanonicalTileID& canonical_) + : overscaledZ(canonical_.z), wrap(0), canonical(canonical_) { + assert(overscaledZ >= canonical.z); +} + +inline OverscaledTileID::OverscaledTileID(CanonicalTileID&& canonical_) + : overscaledZ(canonical_.z), wrap(0), canonical(std::forward(canonical_)) { + assert(overscaledZ >= canonical.z); +} + +inline bool OverscaledTileID::operator==(const OverscaledTileID& rhs) const { + return overscaledZ == rhs.overscaledZ && wrap == rhs.wrap &&canonical == rhs.canonical; +} + +inline bool OverscaledTileID::operator!=(const OverscaledTileID& rhs) const { + return overscaledZ != rhs.overscaledZ || wrap != rhs.wrap || canonical != rhs.canonical; +} + +inline bool OverscaledTileID::operator<(const OverscaledTileID& rhs) const { + return std::tie(overscaledZ, wrap, canonical) < std::tie(rhs.overscaledZ, rhs.wrap, rhs.canonical); +} + +inline uint32_t OverscaledTileID::overscaleFactor() const { + return 1u << (overscaledZ - canonical.z); +} + +inline bool OverscaledTileID::isChildOf(const OverscaledTileID& rhs) const { + return overscaledZ > rhs.overscaledZ && + (canonical == rhs.canonical || canonical.isChildOf(rhs.canonical)); +} + +inline OverscaledTileID OverscaledTileID::scaledTo(uint8_t z) const { + return { z, wrap, z >= canonical.z ? canonical : canonical.scaledTo(z) }; +} + +inline UnwrappedTileID OverscaledTileID::toUnwrapped() const { + return { wrap, canonical }; +} + +inline UnwrappedTileID::UnwrappedTileID(uint8_t z_, int64_t x_, int64_t y_) + : wrap((x_ < 0 ? x_ - (1ll << z_) + 1 : x_) / (1ll << z_)), + canonical( + z_, + static_cast(x_ - wrap * (1ll << z_)), + y_ < 0 ? 0 : std::min(static_cast(y_), static_cast(1ull << z_) - 1)) { +} + +inline UnwrappedTileID::UnwrappedTileID(int16_t wrap_, CanonicalTileID canonical_) + : wrap(wrap_), canonical(std::move(canonical_)) { +} + +inline bool UnwrappedTileID::operator==(const UnwrappedTileID& rhs) const { + return wrap == rhs.wrap && canonical == rhs.canonical; +} + +inline bool UnwrappedTileID::operator!=(const UnwrappedTileID& rhs) const { + return wrap != rhs.wrap || canonical != rhs.canonical; +} + +inline bool UnwrappedTileID::operator<(const UnwrappedTileID& rhs) const { + return std::tie(wrap, canonical) < std::tie(rhs.wrap, rhs.canonical); +} + +inline bool UnwrappedTileID::isChildOf(const UnwrappedTileID& parent) const { + return wrap == parent.wrap && canonical.isChildOf(parent.canonical); +} + +inline std::array UnwrappedTileID::children() const { + const uint8_t childZ = canonical.z + 1; + const uint32_t childX = canonical.x * 2; + const uint32_t childY = canonical.y * 2; + return { { + { wrap, { childZ, childX, childY } }, + { wrap, { childZ, childX, childY + 1 } }, + { wrap, { childZ, childX + 1, childY } }, + { wrap, { childZ, childX + 1, childY + 1 } }, + } }; +} + +inline OverscaledTileID UnwrappedTileID::overscaleTo(const uint8_t overscaledZ) const { + assert(overscaledZ >= canonical.z); + return { overscaledZ, wrap, canonical }; +} + +inline float UnwrappedTileID::pixelsToTileUnits(const float pixelValue, const float zoom) const { + return pixelValue * (util::EXTENT / (util::tileSize * std::pow(2, zoom - canonical.z))); +} + +} // namespace mbgl + +namespace std { + +template <> +struct hash { + size_t operator()(const mbgl::CanonicalTileID& id) const; +}; + +template <> +struct hash { + size_t operator()(const mbgl::UnwrappedTileID& id) const; +}; + +template <> +struct hash { + size_t operator()(const mbgl::OverscaledTileID& id) const; +}; + +} // namespace std + diff --git a/include/mbgl/tile/tile_necessity.hpp b/include/mbgl/tile/tile_necessity.hpp new file mode 100644 index 0000000000..e51bf51d10 --- /dev/null +++ b/include/mbgl/tile/tile_necessity.hpp @@ -0,0 +1,15 @@ +#pragma once + +namespace mbgl { + +// Tiles can have two states: optional or required. +// - optional means that only low-cost actions should be taken to obtain the data +// (e.g. load from cache, but accept stale data) +// - required means that every effort should be taken to obtain the data (e.g. load +// from internet and keep the data fresh if it expires) +enum class TileNecessity : bool { + Optional = false, + Required = true, +}; + +} // namespace mbgl diff --git a/include/mbgl/util/constants.hpp b/include/mbgl/util/constants.hpp index eb5c201793..d5e55065c4 100644 --- a/include/mbgl/util/constants.hpp +++ b/include/mbgl/util/constants.hpp @@ -61,7 +61,6 @@ extern const bool tileParseWarnings; extern const bool styleParseWarnings; extern const bool spriteWarnings; extern const bool renderWarnings; -extern const bool renderTree; extern const bool labelTextMissingWarning; extern const bool missingFontStackWarning; extern const bool missingFontFaceWarning; diff --git a/include/mbgl/util/geometry.hpp b/include/mbgl/util/geometry.hpp index 6dc16bc514..a28c59a47d 100644 --- a/include/mbgl/util/geometry.hpp +++ b/include/mbgl/util/geometry.hpp @@ -2,6 +2,7 @@ #include #include +#include namespace mbgl { @@ -58,4 +59,9 @@ struct ToFeatureType { FeatureType operator()(const mapbox::geometry::geometry_collection &) const { return FeatureType::Unknown; } }; +template +auto forEachPoint(const Geometry& g, F f) { + mapbox::geometry::for_each_point(g, f); +} + } // namespace mbgl diff --git a/include/mbgl/util/image.hpp b/include/mbgl/util/image.hpp index cb28f3da8d..4887058f79 100644 --- a/include/mbgl/util/image.hpp +++ b/include/mbgl/util/image.hpp @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -91,6 +92,31 @@ public: operator=(std::move(newImage)); } + // Clears the rect area specified by `pt` and `size` from `dstImage`. + static void clear(Image& dstImg, const Point& pt, const Size& size) { + if (size.isEmpty()) { + return; + } + + if (!dstImg.valid()) { + throw std::invalid_argument("invalid destination for image clear"); + } + + if (size.width > dstImg.size.width || + size.height > dstImg.size.height || + pt.x > dstImg.size.width - size.width || + pt.y > dstImg.size.height - size.height) { + throw std::out_of_range("out of range destination coordinates for image clear"); + } + + uint8_t* dstData = dstImg.data.get(); + + for (uint32_t y = 0; y < size.height; y++) { + const std::size_t dstOffset = (pt.y + y) * dstImg.stride() + pt.x * channels; + std::memset(dstData + dstOffset, 0, size.width * channels); + } + } + // Copy image data within `rect` from `src` to the rectangle of the same size at `pt` // in `dst`. If the specified bounds exceed the bounds of the source or destination, // throw `std::out_of_range`. Must not be used to move data within a single Image. diff --git a/include/mbgl/util/projection.hpp b/include/mbgl/util/projection.hpp index 3cc1146513..f64502c5bc 100644 --- a/include/mbgl/util/projection.hpp +++ b/include/mbgl/util/projection.hpp @@ -75,10 +75,7 @@ public: } static Point project(const LatLng& latLng, double scale) { - return Point { - util::LONGITUDE_MAX + latLng.longitude(), - util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + latLng.latitude() * M_PI / util::DEGREES_MAX)) - } * worldSize(scale) / util::DEGREES_MAX; + return project_(latLng, worldSize(scale)); } static LatLng unproject(const Point& p, double scale, LatLng::WrapMode wrapMode = LatLng::Unwrapped) { @@ -89,6 +86,23 @@ public: wrapMode }; } + + // Project lat, lon to point in a zoom-dependent world size + static Point project(const LatLng& point, uint8_t zoom, uint16_t tileSize) { + const double t2z = tileSize * std::pow(2, zoom); + Point pt = project_(point, t2z); + // Flip y coordinate + auto x = std::round(std::min(pt.x, t2z)); + auto y = std::round(std::min(t2z - pt.y, t2z)); + return { x, y }; + } +private: + static Point project_(const LatLng& latLng, double worldSize) { + return Point { + util::LONGITUDE_MAX + latLng.longitude(), + util::LONGITUDE_MAX - util::RAD2DEG * std::log(std::tan(M_PI / 4 + latLng.latitude() * M_PI / util::DEGREES_MAX)) + } * worldSize / util::DEGREES_MAX; + } }; } // namespace mbgl diff --git a/include/mbgl/util/thread.hpp b/include/mbgl/util/thread.hpp new file mode 100644 index 0000000000..672eebf6db --- /dev/null +++ b/include/mbgl/util/thread.hpp @@ -0,0 +1,161 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mbgl { +namespace util { + +// Manages a thread with `Object`. + +// Upon creation of this object, it launches a thread and creates an object of type `Object` +// in that thread. When the `Thread<>` object is destructed, the destructor waits +// for thread termination. The `Thread<>` constructor blocks until the thread and +// the `Object` are fully created, so after the object creation, it's safe to obtain the +// `Object` stored in this thread. The thread created will always have low priority on +// the platforms that support setting thread priority. +// +// The following properties make this class different from `ThreadPool`: +// +// - Only one thread is created. +// - `Object` will live in a single thread, providing thread affinity. +// - It is safe to use `ThreadLocal` in an `Object` managed by `Thread<>` +// - A `RunLoop` is created for the `Object` thread. +// - `Object` can use `Timer` and do asynchronous I/O, like wait for sockets events. +// +template +class Thread : public Scheduler { +public: + template + Thread(const std::string& name, Args&&... args) { + std::promise running; + + thread = std::thread([&] { + platform::setCurrentThreadName(name); + platform::makeThreadLowPriority(); + + util::RunLoop loop_(util::RunLoop::Type::New); + loop = &loop_; + + object = std::make_unique>(*this, std::forward(args)...); + running.set_value(); + + loop->run(); + loop = nullptr; + }); + + running.get_future().get(); + } + + ~Thread() override { + if (paused) { + resume(); + } + + std::promise joinable; + + // Kill the actor, so we don't get more + // messages posted on this scheduler after + // we delete the RunLoop. + loop->invoke([&] { + object.reset(); + joinable.set_value(); + }); + + joinable.get_future().get(); + + loop->stop(); + thread.join(); + } + + // Returns a non-owning reference to `Object` that + // can be used to send messages to `Object`. It is safe + // to the non-owning reference to outlive this object + // and be used after the `Thread<>` gets destroyed. + ActorRef> actor() const { + return object->self(); + } + + // Pauses the `Object` thread. It will prevent the object to wake + // up from events such as timers and file descriptor I/O. Messages + // sent to a paused `Object` will be queued and only processed after + // `resume()` is called. + void pause() { + MBGL_VERIFY_THREAD(tid); + + assert(!paused); + + paused = std::make_unique>(); + resumed = std::make_unique>(); + + auto pausing = paused->get_future(); + + loop->invoke([this] { + auto resuming = resumed->get_future(); + paused->set_value(); + resuming.get(); + }); + + pausing.get(); + } + + // Resumes the `Object` thread previously paused by `pause()`. + void resume() { + MBGL_VERIFY_THREAD(tid); + + assert(paused); + + resumed->set_value(); + + resumed.reset(); + paused.reset(); + } + +private: + MBGL_STORE_THREAD(tid); + + void schedule(std::weak_ptr mailbox) override { + { + std::lock_guard lock(mutex); + queue.push(mailbox); + } + + loop->invoke([this] { receive(); }); + } + + void receive() { + std::unique_lock lock(mutex); + + auto mailbox = queue.front(); + queue.pop(); + lock.unlock(); + + Mailbox::maybeReceive(mailbox); + } + + std::mutex mutex; + std::queue> queue; + std::thread thread; + std::unique_ptr> object; + + std::unique_ptr> paused; + std::unique_ptr> resumed; + + util::RunLoop* loop = nullptr; +}; + +} // namespace util +} // namespace mbgl diff --git a/mapbox-gl-native.pro b/mapbox-gl-native.pro index 0cf5d8de11..9f9f411477 100644 --- a/mapbox-gl-native.pro +++ b/mapbox-gl-native.pro @@ -60,6 +60,8 @@ qtConfig(system-zlib) { TR_EXCLUDE += $$PWD/* qtConfig(icu) { + QMAKE_USE_PRIVATE += icu + SOURCES += \ platform/default/bidi.cpp } else { @@ -153,7 +155,6 @@ SOURCES += \ src/mbgl/renderer/render_light.cpp \ src/mbgl/renderer/render_source.cpp \ src/mbgl/renderer/render_static_data.cpp \ - src/mbgl/renderer/render_style.cpp \ src/mbgl/renderer/render_tile.cpp \ src/mbgl/renderer/renderer.cpp \ src/mbgl/renderer/renderer_backend.cpp \ @@ -185,7 +186,6 @@ SOURCES += \ src/mbgl/sprite/sprite_loader.cpp \ src/mbgl/sprite/sprite_loader_worker.cpp \ src/mbgl/sprite/sprite_parser.cpp \ - src/mbgl/storage/file_source_request.cpp \ src/mbgl/storage/network_status.cpp \ src/mbgl/storage/resource.cpp \ src/mbgl/storage/resource_transform.cpp \ @@ -254,6 +254,7 @@ SOURCES += \ src/mbgl/tile/raster_tile_worker.cpp \ src/mbgl/tile/tile.cpp \ src/mbgl/tile/tile_cache.cpp \ + src/mbgl/tile/tile_id_hash.cpp \ src/mbgl/tile/tile_id_io.cpp \ src/mbgl/tile/vector_tile.cpp \ src/mbgl/tile/vector_tile_data.cpp \ @@ -292,14 +293,15 @@ SOURCES += \ src/parsedate/parsedate.c \ platform/default/asset_file_source.cpp \ platform/default/default_file_source.cpp \ + platform/default/file_source_request.cpp \ platform/default/local_file_source.cpp \ - platform/default/online_file_source.cpp \ + platform/default/logging_stderr.cpp \ platform/default/mbgl/storage/offline.cpp \ platform/default/mbgl/storage/offline_database.cpp \ platform/default/mbgl/storage/offline_download.cpp \ - platform/default/logging_stderr.cpp \ + platform/default/mbgl/util/default_thread_pool.cpp \ platform/default/mbgl/util/shared_thread_pool.cpp \ - platform/default/mbgl/util/default_thread_pool.cpp + platform/default/online_file_source.cpp HEADERS += \ platform/qt/include/qmapbox.hpp \ @@ -323,10 +325,10 @@ INCLUDEPATH += \ deps/cheap-ruler/2.5.3/include \ deps/earcut/0.12.3 \ deps/earcut/0.12.3/include \ - deps/geojson/0.4.0 \ - deps/geojson/0.4.0/include \ - deps/geojsonvt/6.2.1 \ - deps/geojsonvt/6.2.1/include \ + deps/geojson/0.4.2 \ + deps/geojson/0.4.2/include \ + deps/geojsonvt/6.3.0 \ + deps/geojsonvt/6.3.0/include \ deps/geometry/0.9.2 \ deps/geometry/0.9.2/include \ deps/kdbush/0.1.1-1 \ diff --git a/platform/default/default_file_source.cpp b/platform/default/default_file_source.cpp index 9c8a38a308..608b782ab9 100644 --- a/platform/default/default_file_source.cpp +++ b/platform/default/default_file_source.cpp @@ -28,10 +28,15 @@ namespace mbgl { class DefaultFileSource::Impl { public: - Impl(ActorRef, std::shared_ptr assetFileSource_, const std::string& cachePath, uint64_t maximumCacheSize) + Impl(ActorRef self, std::shared_ptr assetFileSource_, const std::string& cachePath, uint64_t maximumCacheSize) : assetFileSource(assetFileSource_) - , localFileSource(std::make_unique()) - , offlineDatabase(cachePath, maximumCacheSize) { + , localFileSource(std::make_unique()) { + // Initialize the Database asynchronously so as to not block Actor creation. + self.invoke(&Impl::initializeOfflineDatabase, cachePath, maximumCacheSize); + } + + void initializeOfflineDatabase(std::string cachePath, uint64_t maximumCacheSize) { + offlineDatabase = std::make_unique(cachePath, maximumCacheSize); } void setAPIBaseURL(const std::string& url) { @@ -56,7 +61,7 @@ public: void listRegions(std::function>)> callback) { try { - callback({}, offlineDatabase.listRegions()); + callback({}, offlineDatabase->listRegions()); } catch (...) { callback(std::current_exception(), {}); } @@ -66,7 +71,7 @@ public: const OfflineRegionMetadata& metadata, std::function)> callback) { try { - callback({}, offlineDatabase.createRegion(definition, metadata)); + callback({}, offlineDatabase->createRegion(definition, metadata)); } catch (...) { callback(std::current_exception(), {}); } @@ -76,7 +81,7 @@ public: const OfflineRegionMetadata& metadata, std::function)> callback) { try { - callback({}, offlineDatabase.updateMetadata(regionID, metadata)); + callback({}, offlineDatabase->updateMetadata(regionID, metadata)); } catch (...) { callback(std::current_exception(), {}); } @@ -93,7 +98,7 @@ public: void deleteRegion(OfflineRegion&& region, std::function callback) { try { downloads.erase(region.getID()); - offlineDatabase.deleteRegion(std::move(region)); + offlineDatabase->deleteRegion(std::move(region)); callback({}); } catch (...) { callback(std::current_exception()); @@ -121,43 +126,43 @@ public: tasks[req] = localFileSource->request(resource, callback); } else { // Try the offline database - Resource revalidation = resource; - - const bool hasPrior = resource.priorEtag || resource.priorModified || resource.priorExpires; - if (!hasPrior || resource.necessity == Resource::Optional) { - auto offlineResponse = offlineDatabase.get(resource); - - if (resource.necessity == Resource::Optional && !offlineResponse) { - // Ensure there's always a response that we can send, so the caller knows that - // there's no optional data available in the cache. - offlineResponse.emplace(); - offlineResponse->noContent = true; - offlineResponse->error = std::make_unique( - Response::Error::Reason::NotFound, "Not found in offline database"); - } - - if (offlineResponse) { - revalidation.priorModified = offlineResponse->modified; - revalidation.priorExpires = offlineResponse->expires; - revalidation.priorEtag = offlineResponse->etag; + if (resource.hasLoadingMethod(Resource::LoadingMethod::Cache)) { + auto offlineResponse = offlineDatabase->get(resource); + + if (resource.loadingMethod == Resource::LoadingMethod::CacheOnly) { + if (!offlineResponse) { + // Ensure there's always a response that we can send, so the caller knows that + // there's no optional data available in the cache, when it's the only place + // we're supposed to load from. + offlineResponse.emplace(); + offlineResponse->noContent = true; + offlineResponse->error = std::make_unique( + Response::Error::Reason::NotFound, "Not found in offline database"); + } else if (!offlineResponse->isUsable()) { + // Don't return resources the server requested not to show when they're stale. + // Even if we can't directly use the response, we may still use it to send a + // conditional HTTP request, which is why we're saving it above. + offlineResponse->error = std::make_unique( + Response::Error::Reason::NotFound, "Cached resource is unusable"); + } + callback(*offlineResponse); + } else if (offlineResponse) { + // Copy over the fields so that we can use them when making a refresh request. + resource.priorModified = offlineResponse->modified; + resource.priorExpires = offlineResponse->expires; + resource.priorEtag = offlineResponse->etag; + resource.priorData = offlineResponse->data; - // Don't return resources the server requested not to show when they're stale. - // Even if we can't directly use the response, we may still use it to send a - // conditional HTTP request. if (offlineResponse->isUsable()) { callback(*offlineResponse); - } else { - // Since we can't return the data immediately, we'll have to hold on so that - // we can return it later in case we get a 304 Not Modified response. - revalidation.priorData = offlineResponse->data; } } } // Get from the online file source - if (resource.necessity == Resource::Required) { - tasks[req] = onlineFileSource.request(revalidation, [=] (Response onlineResponse) mutable { - this->offlineDatabase.put(revalidation, onlineResponse); + if (resource.hasLoadingMethod(Resource::LoadingMethod::Network)) { + tasks[req] = onlineFileSource.request(resource, [=] (Response onlineResponse) mutable { + this->offlineDatabase->put(resource, onlineResponse); callback(onlineResponse); }); } @@ -169,11 +174,15 @@ public: } void setOfflineMapboxTileCountLimit(uint64_t limit) { - offlineDatabase.setOfflineMapboxTileCountLimit(limit); + offlineDatabase->setOfflineMapboxTileCountLimit(limit); + } + + void setOnlineStatus(const bool status) { + onlineFileSource.setOnlineStatus(status); } void put(const Resource& resource, const Response& response) { - offlineDatabase.put(resource, response); + offlineDatabase->put(resource, response); } private: @@ -183,13 +192,13 @@ private: return *it->second; } return *downloads.emplace(regionID, - std::make_unique(regionID, offlineDatabase.getRegionDefinition(regionID), offlineDatabase, onlineFileSource)).first->second; + std::make_unique(regionID, offlineDatabase->getRegionDefinition(regionID), *offlineDatabase, onlineFileSource)).first->second; } // shared so that destruction is done on the creating thread const std::shared_ptr assetFileSource; const std::unique_ptr localFileSource; - OfflineDatabase offlineDatabase; + std::unique_ptr offlineDatabase; OnlineFileSource onlineFileSource; std::unordered_map> tasks; std::unordered_map> downloads; @@ -298,6 +307,10 @@ void DefaultFileSource::resume() { // For testing only: +void DefaultFileSource::setOnlineStatus(const bool status) { + impl->actor().invoke(&Impl::setOnlineStatus, status); +} + void DefaultFileSource::put(const Resource& resource, const Response& response) { impl->actor().invoke(&Impl::put, resource, response); } diff --git a/platform/default/file_source_request.cpp b/platform/default/file_source_request.cpp new file mode 100644 index 0000000000..09ea8cc32a --- /dev/null +++ b/platform/default/file_source_request.cpp @@ -0,0 +1,37 @@ +#include + +#include +#include + +namespace mbgl { + +FileSourceRequest::FileSourceRequest(FileSource::Callback&& callback) + : responseCallback(callback) + , mailbox(std::make_shared(*Scheduler::GetCurrent())) { +} + +FileSourceRequest::~FileSourceRequest() { + if (cancelCallback) { + cancelCallback(); + } + + mailbox->close(); +} + +void FileSourceRequest::onCancel(std::function&& callback) { + cancelCallback = std::move(callback); +} + +void FileSourceRequest::setResponse(const Response& response) { + // Copy, because calling the callback will sometimes self + // destroy this object. We cannot move because this method + // can be called more than one. + auto callback = responseCallback; + callback(response); +} + +ActorRef FileSourceRequest::actor() { + return ActorRef(*this, mailbox); +} + +} // namespace mbgl diff --git a/platform/default/mbgl/gl/headless_backend.cpp b/platform/default/mbgl/gl/headless_backend.cpp index fe77b80985..edf637a560 100644 --- a/platform/default/mbgl/gl/headless_backend.cpp +++ b/platform/default/mbgl/gl/headless_backend.cpp @@ -64,6 +64,10 @@ void HeadlessBackend::bind() { context_.viewport = { 0, 0, size }; } +Size HeadlessBackend::getFramebufferSize() const { + return size; +} + void HeadlessBackend::updateAssumedState() { // no-op } diff --git a/platform/default/mbgl/gl/headless_backend.hpp b/platform/default/mbgl/gl/headless_backend.hpp index 8d86ea8c47..66f861e213 100644 --- a/platform/default/mbgl/gl/headless_backend.hpp +++ b/platform/default/mbgl/gl/headless_backend.hpp @@ -15,6 +15,7 @@ public: ~HeadlessBackend() override; void bind() override; + Size getFramebufferSize() const override; void updateAssumedState() override; void setSize(Size); diff --git a/platform/default/mbgl/gl/headless_frontend.cpp b/platform/default/mbgl/gl/headless_frontend.cpp index ad03706be7..5d2932258a 100644 --- a/platform/default/mbgl/gl/headless_frontend.cpp +++ b/platform/default/mbgl/gl/headless_frontend.cpp @@ -5,11 +5,11 @@ namespace mbgl { -HeadlessFrontend::HeadlessFrontend(float pixelRatio_, FileSource& fileSource, Scheduler& scheduler) - : HeadlessFrontend({ 256, 256 }, pixelRatio_, fileSource, scheduler) { +HeadlessFrontend::HeadlessFrontend(float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional programCacheDir) + : HeadlessFrontend({ 256, 256 }, pixelRatio_, fileSource, scheduler, programCacheDir) { } -HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fileSource, Scheduler& scheduler) +HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fileSource, Scheduler& scheduler, const optional programCacheDir) : size(size_), pixelRatio(pixelRatio_), backend({ static_cast(size.width * pixelRatio), @@ -20,7 +20,7 @@ HeadlessFrontend::HeadlessFrontend(Size size_, float pixelRatio_, FileSource& fi renderer->render(*updateParameters); } }), - renderer(std::make_unique(backend, pixelRatio, fileSource, scheduler)) { + renderer(std::make_unique(backend, pixelRatio, fileSource, scheduler, GLContextMode::Unique, programCacheDir)) { } HeadlessFrontend::~HeadlessFrontend() = default; diff --git a/platform/default/mbgl/gl/headless_frontend.hpp b/platform/default/mbgl/gl/headless_frontend.hpp index 18d0d2527b..33503bc13b 100644 --- a/platform/default/mbgl/gl/headless_frontend.hpp +++ b/platform/default/mbgl/gl/headless_frontend.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include @@ -16,8 +17,8 @@ class Map; class HeadlessFrontend : public RendererFrontend { public: - HeadlessFrontend(float pixelRatio_, FileSource&, Scheduler&); - HeadlessFrontend(Size, float pixelRatio_, FileSource&, Scheduler&); + HeadlessFrontend(float pixelRatio_, FileSource&, Scheduler&, const optional programCacheDir = {}); + HeadlessFrontend(Size, float pixelRatio_, FileSource&, Scheduler&, const optional programCacheDir = {}); ~HeadlessFrontend() override; void reset() override; diff --git a/platform/default/mbgl/map/map_snapshotter.cpp b/platform/default/mbgl/map/map_snapshotter.cpp new file mode 100644 index 0000000000..95c46344fe --- /dev/null +++ b/platform/default/mbgl/map/map_snapshotter.cpp @@ -0,0 +1,76 @@ +#include + +#include +#include +#include +#include +#include +#include + +namespace mbgl { + +class MapSnapshotter::Impl { +public: + Impl(FileSource&, + Scheduler&, + const std::string& styleURL, + const Size&, + const float pixelRatio, + const CameraOptions&, + const optional region, + const optional programCacheDir); + + void snapshot(ActorRef); + +private: + HeadlessFrontend frontend; + Map map; +}; + +MapSnapshotter::Impl::Impl(FileSource& fileSource, + Scheduler& scheduler, + const std::string& styleURL, + const Size& size, + const float pixelRatio, + const CameraOptions& cameraOptions, + const optional region, + const optional programCacheDir) + : frontend(size, pixelRatio, fileSource, scheduler, programCacheDir) + , map(frontend, MapObserver::nullObserver(), size, pixelRatio, fileSource, scheduler, MapMode::Still) { + + map.getStyle().loadURL(styleURL); + + map.jumpTo(cameraOptions); + + // Set region, if specified + if (region) { + mbgl::EdgeInsets insets = { 0, 0, 0, 0 }; + std::vector latLngs = { region->southwest(), region->northeast() }; + map.jumpTo(map.cameraForLatLngs(latLngs, insets)); + } +} + +void MapSnapshotter::Impl::snapshot(ActorRef callback) { + map.renderStill([this, callback = std::move(callback)] (std::exception_ptr error) mutable { + callback.invoke(&MapSnapshotter::Callback::operator(), error, error ? PremultipliedImage() : frontend.readStillImage()); + }); +} + +MapSnapshotter::MapSnapshotter(FileSource& fileSource, + Scheduler& scheduler, + const std::string& styleURL, + const Size& size, + const float pixelRatio, + const CameraOptions& cameraOptions, + const optional region, + const optional programCacheDir) + : impl(std::make_unique>("Map Snapshotter", fileSource, scheduler, styleURL, size, pixelRatio, cameraOptions, region, programCacheDir)) { +} + +MapSnapshotter::~MapSnapshotter() = default; + +void MapSnapshotter::snapshot(ActorRef callback) { + impl->actor().invoke(&Impl::snapshot, std::move(callback)); +} + +} // namespace mbgl diff --git a/platform/default/mbgl/map/map_snapshotter.hpp b/platform/default/mbgl/map/map_snapshotter.hpp new file mode 100644 index 0000000000..0afa848fd8 --- /dev/null +++ b/platform/default/mbgl/map/map_snapshotter.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include + +namespace mbgl { + +template class ActorRef; +struct CameraOptions; +class FileSource; +class Size; +class LatLngBounds; + +class MapSnapshotter { +public: + MapSnapshotter(FileSource& fileSource, + Scheduler& scheduler, + const std::string& styleURL, + const Size&, + const float pixelRatio, + const CameraOptions&, + const optional region, + const optional cacheDir = {}); + + ~MapSnapshotter(); + + using Callback = std::function; + void snapshot(ActorRef); + +private: + class Impl; + std::unique_ptr> impl; +}; + +} // namespace mbgl diff --git a/platform/default/mbgl/storage/file_source_request.hpp b/platform/default/mbgl/storage/file_source_request.hpp new file mode 100644 index 0000000000..6bd0d44df6 --- /dev/null +++ b/platform/default/mbgl/storage/file_source_request.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include +#include +#include + +#include +#include + +namespace mbgl { + +class Mailbox; + +class FileSourceRequest : public AsyncRequest { +public: + FileSourceRequest(FileSource::Callback&& callback); + ~FileSourceRequest() final; + + void onCancel(std::function&& callback); + void setResponse(const Response& res); + + ActorRef actor(); + +private: + FileSource::Callback responseCallback = nullptr; + std::function cancelCallback = nullptr; + + std::shared_ptr mailbox; +}; + +} // namespace mbgl diff --git a/platform/default/mbgl/storage/offline.cpp b/platform/default/mbgl/storage/offline.cpp index fd2d47819b..7670790be9 100644 --- a/platform/default/mbgl/storage/offline.cpp +++ b/platform/default/mbgl/storage/offline.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include @@ -23,18 +24,12 @@ OfflineTilePyramidRegionDefinition::OfflineTilePyramidRegionDefinition( } } -std::vector OfflineTilePyramidRegionDefinition::tileCover(SourceType type, uint16_t tileSize, const Range& zoomRange) const { - double minZ = std::max(util::coveringZoomLevel(minZoom, type, tileSize), zoomRange.min); - double maxZ = std::min(util::coveringZoomLevel(maxZoom, type, tileSize), zoomRange.max); - - assert(minZ >= 0); - assert(maxZ >= 0); - assert(minZ < std::numeric_limits::max()); - assert(maxZ < std::numeric_limits::max()); +std::vector OfflineTilePyramidRegionDefinition::tileCover(style::SourceType type, uint16_t tileSize, const Range& zoomRange) const { + const Range clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange); std::vector result; - for (uint8_t z = minZ; z <= maxZ; z++) { + for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { for (const auto& tile : util::tileCover(bounds, z)) { result.emplace_back(tile.canonical); } @@ -43,6 +38,28 @@ std::vector OfflineTilePyramidRegionDefinition::tileCover(Sourc return result; } +uint64_t OfflineTilePyramidRegionDefinition::tileCount(style::SourceType type, uint16_t tileSize, const Range& zoomRange) const { + + const Range clampedZoomRange = coveringZoomRange(type, tileSize, zoomRange); + unsigned long result = 0;; + for (uint8_t z = clampedZoomRange.min; z <= clampedZoomRange.max; z++) { + result += util::tileCount(bounds, z, tileSize); + } + + return result; +} + +Range OfflineTilePyramidRegionDefinition::coveringZoomRange(style::SourceType type, uint16_t tileSize, const Range& zoomRange) const { + double minZ = std::max(util::coveringZoomLevel(minZoom, type, tileSize), zoomRange.min); + double maxZ = std::min(util::coveringZoomLevel(maxZoom, type, tileSize), zoomRange.max); + + assert(minZ >= 0); + assert(maxZ >= 0); + assert(minZ < std::numeric_limits::max()); + assert(maxZ < std::numeric_limits::max()); + return { static_cast(minZ), static_cast(maxZ) }; +} + OfflineRegionDefinition decodeOfflineRegionDefinition(const std::string& region) { rapidjson::GenericDocument, rapidjson::CrtAllocator> doc; doc.Parse<0>(region.c_str()); diff --git a/platform/default/mbgl/storage/offline_download.cpp b/platform/default/mbgl/storage/offline_download.cpp index 7f0001f64b..ff61114888 100644 --- a/platform/default/mbgl/storage/offline_download.cpp +++ b/platform/default/mbgl/storage/offline_download.cpp @@ -80,7 +80,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const { auto handleTiledSource = [&] (const variant& urlOrTileset, const uint16_t tileSize) { if (urlOrTileset.is()) { result.requiredResourceCount += - definition.tileCover(type, tileSize, urlOrTileset.get().zoomRange).size(); + definition.tileCount(type, tileSize, urlOrTileset.get().zoomRange); } else { result.requiredResourceCount += 1; const auto& url = urlOrTileset.get(); @@ -90,7 +90,7 @@ OfflineRegionStatus OfflineDownload::getStatus() const { optional tileset = style::conversion::convertJSON(*sourceResponse->data, error); if (tileset) { result.requiredResourceCount += - definition.tileCover(type, tileSize, (*tileset).zoomRange).size(); + definition.tileCount(type, tileSize, (*tileset).zoomRange); } } else { result.requiredResourceCountIsPrecise = false; diff --git a/platform/default/mbgl/storage/offline_download.hpp b/platform/default/mbgl/storage/offline_download.hpp index c978ded931..437f221c11 100644 --- a/platform/default/mbgl/storage/offline_download.hpp +++ b/platform/default/mbgl/storage/offline_download.hpp @@ -60,7 +60,7 @@ private: std::deque resourcesRemaining; void queueResource(Resource); - void queueTiles(SourceType, uint16_t tileSize, const Tileset&); + void queueTiles(style::SourceType, uint16_t tileSize, const Tileset&); }; } // namespace mbgl diff --git a/platform/default/online_file_source.cpp b/platform/default/online_file_source.cpp index 1c594f87a0..d685109b95 100644 --- a/platform/default/online_file_source.cpp +++ b/platform/default/online_file_source.cpp @@ -117,13 +117,24 @@ public: } void activateRequest(OnlineFileRequest* request) { - activeRequests.insert(request); - request->request = httpFileSource.request(request->resource, [=] (Response response) { + auto callback = [=](Response response) { activeRequests.erase(request); - activatePendingRequest(); request->request.reset(); request->completed(response); - }); + activatePendingRequest(); + }; + + activeRequests.insert(request); + + if (online) { + request->request = httpFileSource.request(request->resource, callback); + } else { + Response response; + response.error = std::make_unique(Response::Error::Reason::Connection, + "Online connectivity is disabled."); + callback(response); + } + assert(pendingRequestsMap.size() == pendingRequestsList.size()); } @@ -153,6 +164,11 @@ public: resourceTransform = std::move(transform); } + void setOnlineStatus(const bool status) { + online = status; + networkIsReachableAgain(); + } + private: void networkIsReachableAgain() { for (auto& request : allRequests) { @@ -178,6 +194,7 @@ private: std::unordered_map::iterator> pendingRequestsMap; std::unordered_set activeRequests; + bool online = true; HTTPFileSource httpFileSource; util::AsyncTask reachability { std::bind(&Impl::networkIsReachableAgain, this) }; }; @@ -389,4 +406,10 @@ ActorRef OnlineFileRequest::actor() { return ActorRef(*this, mailbox); } +// For testing only: + +void OnlineFileSource::setOnlineStatus(const bool status) { + impl->setOnlineStatus(status); +} + } // namespace mbgl diff --git a/platform/qt/include/qmapboxgl.hpp b/platform/qt/include/qmapboxgl.hpp index e2fb283989..ebbd949921 100644 --- a/platform/qt/include/qmapboxgl.hpp +++ b/platform/qt/include/qmapboxgl.hpp @@ -10,6 +10,8 @@ #include #include +#include + class QMapboxGLPrivate; // This header follows the Qt coding style: https://wiki.qt.io/Qt_Coding_Style @@ -59,6 +61,9 @@ public: QString apiBaseUrl() const; void setApiBaseUrl(const QString &); + std::function resourceTransform() const; + void setResourceTransform(const std::function &); + private: GLContextMode m_contextMode; ConstrainMode m_constrainMode; @@ -69,6 +74,7 @@ private: QString m_assetPath; QString m_accessToken; QString m_apiBaseUrl; + std::function m_resourceTransform; }; struct Q_DECL_EXPORT QMapboxGLCameraOptions { diff --git a/platform/qt/src/qmapboxgl.cpp b/platform/qt/src/qmapboxgl.cpp index c7c4cf1e4a..074ef280aa 100644 --- a/platform/qt/src/qmapboxgl.cpp +++ b/platform/qt/src/qmapboxgl.cpp @@ -29,6 +29,7 @@ #include #include #include +#include #if QT_VERSION >= 0x050000 #include @@ -361,6 +362,27 @@ void QMapboxGLSettings::setApiBaseUrl(const QString& url) m_apiBaseUrl = url; } +/*! + Returns resource transformation callback used to transform requested URLs. +*/ +std::function QMapboxGLSettings::resourceTransform() const +{ + return m_resourceTransform; +} + +/*! + Sets the resource transformation callback. + + When given, resource transformation callback will be used to transform the + requested resource URLs before they are requested from internet. This can be + used add or remove custom parameters, or reroute certain requests to other + servers or endpoints. +*/ +void QMapboxGLSettings::setResourceTransform(const std::function &transform) +{ + m_resourceTransform = transform; +} + /*! \class QMapboxGL \brief The QMapboxGL class is a Qt wrapper for the Mapbox GL Native engine. @@ -1499,13 +1521,25 @@ QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settin settings.cacheDatabaseMaximumSize())) , threadPool(mbgl::sharedThreadPool()) { + // Setup resource transform if needed + if (settings.resourceTransform()) { + m_resourceTransform = + std::make_unique< mbgl::Actor >( *mbgl::Scheduler::GetCurrent(), + [callback = settings.resourceTransform()] + (mbgl::Resource::Kind , const std::string&& url_) -> std::string { + return callback(std::move(url_)); + } + ); + fileSourceObj->setResourceTransform(m_resourceTransform->self()); + } + // Setup and connect the renderer frontend frontend = std::make_unique( std::make_unique(*this, pixelRatio, *fileSourceObj, *threadPool, static_cast(settings.contextMode())), *this); connect(frontend.get(), SIGNAL(updated()), this, SLOT(invalidate())); - + mapObj = std::make_unique( *frontend, *this, sanitizedSize(size), @@ -1528,18 +1562,18 @@ QMapboxGLPrivate::~QMapboxGLPrivate() { } -mbgl::Size QMapboxGLPrivate::framebufferSize() const { +mbgl::Size QMapboxGLPrivate::getFramebufferSize() const { return sanitizedSize(fbSize); } void QMapboxGLPrivate::updateAssumedState() { assumeFramebufferBinding(fbObject); - assumeViewport(0, 0, framebufferSize()); + assumeViewport(0, 0, getFramebufferSize()); } void QMapboxGLPrivate::bind() { setFramebufferBinding(fbObject); - setViewport(0, 0, framebufferSize()); + setViewport(0, 0, getFramebufferSize()); } void QMapboxGLPrivate::invalidate() diff --git a/platform/qt/src/qmapboxgl_p.hpp b/platform/qt/src/qmapboxgl_p.hpp index 7b0dd8c192..5e12b44a20 100644 --- a/platform/qt/src/qmapboxgl_p.hpp +++ b/platform/qt/src/qmapboxgl_p.hpp @@ -3,15 +3,19 @@ #include "qmapboxgl.hpp" #include "qmapboxgl_renderer_frontend_p.hpp" +#include #include #include #include #include #include +#include #include #include +#include + class QMapboxGLPrivate : public QObject, public mbgl::RendererBackend, public mbgl::MapObserver { Q_OBJECT @@ -20,10 +24,10 @@ public: explicit QMapboxGLPrivate(QMapboxGL *, const QMapboxGLSettings &, const QSize &size, qreal pixelRatio); virtual ~QMapboxGLPrivate(); - mbgl::Size framebufferSize() const; // mbgl::RendererBackend implementation. void bind() final; + mbgl::Size getFramebufferSize() const final; void updateAssumedState() final; void activate() final {} void deactivate() final {} @@ -68,4 +72,7 @@ signals: void needsRendering(); void mapChanged(QMapboxGL::MapChange); void copyrightsChanged(const QString ©rightsHtml); + +private: + std::unique_ptr< mbgl::Actor > m_resourceTransform; }; diff --git a/src/mbgl/algorithm/generate_clip_ids_impl.hpp b/src/mbgl/algorithm/generate_clip_ids_impl.hpp index db62214220..6a316dcdad 100644 --- a/src/mbgl/algorithm/generate_clip_ids_impl.hpp +++ b/src/mbgl/algorithm/generate_clip_ids_impl.hpp @@ -17,7 +17,7 @@ void ClipIDGenerator::update(std::vector> ren const auto end = renderables.end(); for (auto it = renderables.begin(); it != end; it++) { auto& renderable = it->get(); - if (!renderable.used) { + if (!renderable.used || !renderable.needsClipping) { continue; } diff --git a/src/mbgl/algorithm/update_renderables.hpp b/src/mbgl/algorithm/update_renderables.hpp index 0c2266ff47..c583b6b2b6 100644 --- a/src/mbgl/algorithm/update_renderables.hpp +++ b/src/mbgl/algorithm/update_renderables.hpp @@ -1,8 +1,8 @@ #pragma once #include +#include #include -#include #include @@ -40,15 +40,15 @@ void updateRenderables(GetTileFn getTile, // if (source has the tile and bucket is loaded) { if (tile->isRenderable()) { - retainTile(*tile, Resource::Necessity::Required); + retainTile(*tile, TileNecessity::Required); renderTile(idealRenderTileID, *tile); } else { // We are now attempting to load child and parent tiles. - bool parentHasTriedOptional = tile->hasTriedOptional(); + bool parentHasTriedOptional = tile->hasTriedCache(); bool parentIsLoaded = tile->isLoaded(); // The tile isn't loaded yet, but retain it anyway because it's an ideal tile. - retainTile(*tile, Resource::Necessity::Required); + retainTile(*tile, TileNecessity::Required); covered = true; overscaledZ = dataTileZoom + 1; if (overscaledZ > zoomRange.max) { @@ -56,7 +56,7 @@ void updateRenderables(GetTileFn getTile, const auto childDataTileID = idealDataTileID.scaledTo(overscaledZ); tile = getTile(childDataTileID); if (tile && tile->isRenderable()) { - retainTile(*tile, Resource::Necessity::Optional); + retainTile(*tile, TileNecessity::Optional); renderTile(idealRenderTileID, *tile); } else { covered = false; @@ -67,7 +67,7 @@ void updateRenderables(GetTileFn getTile, const OverscaledTileID childDataTileID(overscaledZ, idealRenderTileID.wrap, childTileID); tile = getTile(childDataTileID); if (tile && tile->isRenderable()) { - retainTile(*tile, Resource::Necessity::Optional); + retainTile(*tile, TileNecessity::Optional); renderTile(childDataTileID.toUnwrapped(), *tile); } else { // At least one child tile doesn't exist, so we are going to look for @@ -97,12 +97,19 @@ void updateRenderables(GetTileFn getTile, } if (tile) { - retainTile(*tile, parentIsLoaded ? Resource::Necessity::Required - : Resource::Necessity::Optional); + if (!parentIsLoaded) { + // We haven't completed loading the child, so we only do an optional + // (cache) request in an attempt to quickly load data that we can show. + retainTile(*tile, TileNecessity::Optional); + } else { + // Now that we've checked the child and know for sure that we can't load + // it, we attempt to load the parent from the network. + retainTile(*tile, TileNecessity::Required); + } // Save the current values, since they're the parent of the next iteration // of the parent tile ascent loop. - parentHasTriedOptional = tile->hasTriedOptional(); + parentHasTriedOptional = tile->hasTriedCache(); parentIsLoaded = tile->isLoaded(); if (tile->isRenderable()) { diff --git a/src/mbgl/annotation/annotation_manager.cpp b/src/mbgl/annotation/annotation_manager.cpp index dbf8387ae0..1f2d01e9eb 100644 --- a/src/mbgl/annotation/annotation_manager.cpp +++ b/src/mbgl/annotation/annotation_manager.cpp @@ -39,19 +39,22 @@ AnnotationID AnnotationManager::addAnnotation(const Annotation& annotation, cons Annotation::visit(annotation, [&] (const auto& annotation_) { this->add(id, annotation_, maxZoom); }); + dirty = true; return id; } -Update AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) { +bool AnnotationManager::updateAnnotation(const AnnotationID& id, const Annotation& annotation, const uint8_t maxZoom) { std::lock_guard lock(mutex); - return Annotation::visit(annotation, [&] (const auto& annotation_) { - return this->update(id, annotation_, maxZoom); + Annotation::visit(annotation, [&] (const auto& annotation_) { + this->update(id, annotation_, maxZoom); }); + return dirty; } void AnnotationManager::removeAnnotation(const AnnotationID& id) { std::lock_guard lock(mutex); remove(id); + dirty = true; } void AnnotationManager::add(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t) { @@ -72,49 +75,45 @@ void AnnotationManager::add(const AnnotationID& id, const FillAnnotation& annota impl.updateStyle(*style.get().impl); } -Update AnnotationManager::update(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t maxZoom) { - Update result = Update::Nothing; - +void AnnotationManager::update(const AnnotationID& id, const SymbolAnnotation& annotation, const uint8_t maxZoom) { auto it = symbolAnnotations.find(id); if (it == symbolAnnotations.end()) { assert(false); // Attempt to update a non-existent symbol annotation - return result; + return; } const SymbolAnnotation& existing = it->second->annotation; if (existing.geometry != annotation.geometry || existing.icon != annotation.icon) { - result |= Update::AnnotationData; + dirty = true; remove(id); add(id, annotation, maxZoom); } - - return result; } -Update AnnotationManager::update(const AnnotationID& id, const LineAnnotation& annotation, const uint8_t maxZoom) { +void AnnotationManager::update(const AnnotationID& id, const LineAnnotation& annotation, const uint8_t maxZoom) { auto it = shapeAnnotations.find(id); if (it == shapeAnnotations.end()) { assert(false); // Attempt to update a non-existent shape annotation - return Update::Nothing; + return; } shapeAnnotations.erase(it); add(id, annotation, maxZoom); - return Update::AnnotationData; + dirty = true; } -Update AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) { +void AnnotationManager::update(const AnnotationID& id, const FillAnnotation& annotation, const uint8_t maxZoom) { auto it = shapeAnnotations.find(id); if (it == shapeAnnotations.end()) { assert(false); // Attempt to update a non-existent shape annotation - return Update::Nothing; + return; } shapeAnnotations.erase(it); add(id, annotation, maxZoom); - return Update::AnnotationData; + dirty = true; } void AnnotationManager::remove(const AnnotationID& id) { @@ -187,8 +186,11 @@ void AnnotationManager::updateStyle() { void AnnotationManager::updateData() { std::lock_guard lock(mutex); - for (auto& tile : tiles) { - tile->setData(getTileData(tile->id.canonical)); + if (dirty) { + for (auto& tile : tiles) { + tile->setData(getTileData(tile->id.canonical)); + } + dirty = false; } } diff --git a/src/mbgl/annotation/annotation_manager.hpp b/src/mbgl/annotation/annotation_manager.hpp index dee823bc0f..a028a8f1ba 100644 --- a/src/mbgl/annotation/annotation_manager.hpp +++ b/src/mbgl/annotation/annotation_manager.hpp @@ -3,7 +3,6 @@ #include #include #include -#include #include #include @@ -30,7 +29,7 @@ public: ~AnnotationManager(); AnnotationID addAnnotation(const Annotation&, const uint8_t maxZoom); - Update updateAnnotation(const AnnotationID&, const Annotation&, const uint8_t maxZoom); + bool updateAnnotation(const AnnotationID&, const Annotation&, const uint8_t maxZoom); void removeAnnotation(const AnnotationID&); void addImage(std::unique_ptr); @@ -53,9 +52,9 @@ private: void add(const AnnotationID&, const LineAnnotation&, const uint8_t); void add(const AnnotationID&, const FillAnnotation&, const uint8_t); - Update update(const AnnotationID&, const SymbolAnnotation&, const uint8_t); - Update update(const AnnotationID&, const LineAnnotation&, const uint8_t); - Update update(const AnnotationID&, const FillAnnotation&, const uint8_t); + void update(const AnnotationID&, const SymbolAnnotation&, const uint8_t); + void update(const AnnotationID&, const LineAnnotation&, const uint8_t); + void update(const AnnotationID&, const FillAnnotation&, const uint8_t); void remove(const AnnotationID&); @@ -67,6 +66,8 @@ private: std::mutex mutex; + bool dirty = false; + AnnotationID nextID = 0; using SymbolAnnotationTree = boost::geometry::index::rtree, boost::geometry::index::rstar<16, 4>>; diff --git a/src/mbgl/annotation/annotation_tile.cpp b/src/mbgl/annotation/annotation_tile.cpp index 0596d60f4f..d405418a45 100644 --- a/src/mbgl/annotation/annotation_tile.cpp +++ b/src/mbgl/annotation/annotation_tile.cpp @@ -19,9 +19,6 @@ AnnotationTile::~AnnotationTile() { annotationManager.removeTile(*this); } -void AnnotationTile::setNecessity(Necessity) { -} - class AnnotationTileFeatureData { public: AnnotationTileFeatureData(const AnnotationID id_, diff --git a/src/mbgl/annotation/annotation_tile.hpp b/src/mbgl/annotation/annotation_tile.hpp index 88505c50e3..a4d1e66802 100644 --- a/src/mbgl/annotation/annotation_tile.hpp +++ b/src/mbgl/annotation/annotation_tile.hpp @@ -14,8 +14,6 @@ public: AnnotationTile(const OverscaledTileID&, const TileParameters&); ~AnnotationTile() override; - void setNecessity(Necessity) final; - private: AnnotationManager& annotationManager; }; diff --git a/src/mbgl/annotation/render_annotation_source.cpp b/src/mbgl/annotation/render_annotation_source.cpp index c2e6191d1d..34fb576727 100644 --- a/src/mbgl/annotation/render_annotation_source.cpp +++ b/src/mbgl/annotation/render_annotation_source.cpp @@ -60,9 +60,9 @@ std::vector> RenderAnnotationSource::getRende std::unordered_map> RenderAnnotationSource::queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, - const RenderStyle& style, + const std::vector& layers, const RenderedQueryOptions& options) const { - return tilePyramid.queryRenderedFeatures(geometry, transformState, style, options); + return tilePyramid.queryRenderedFeatures(geometry, transformState, layers, options); } std::vector RenderAnnotationSource::querySourceFeatures(const SourceQueryOptions&) const { diff --git a/src/mbgl/annotation/render_annotation_source.hpp b/src/mbgl/annotation/render_annotation_source.hpp index 4000c4b04a..6209c943f1 100644 --- a/src/mbgl/annotation/render_annotation_source.hpp +++ b/src/mbgl/annotation/render_annotation_source.hpp @@ -26,7 +26,7 @@ public: std::unordered_map> queryRenderedFeatures(const ScreenLineString& geometry, const TransformState& transformState, - const RenderStyle& style, + const std::vector& layers, const RenderedQueryOptions& options) const final; std::vector @@ -43,7 +43,7 @@ private: template <> inline bool RenderSource::is() const { - return baseImpl->type == SourceType::Annotations; + return baseImpl->type == style::SourceType::Annotations; } } // namespace mbgl diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp index 4c4e985369..1adb933e44 100644 --- a/src/mbgl/geometry/feature_index.cpp +++ b/src/mbgl/geometry/feature_index.cpp @@ -1,5 +1,4 @@ #include -#include #include #include #include @@ -9,7 +8,7 @@ #include #include #include -#include +#include #include @@ -32,19 +31,6 @@ void FeatureIndex::insert(const GeometryCollection& geometries, } } -static bool vectorContains(const std::vector& vector, const std::string& s) { - return std::find(vector.begin(), vector.end(), s) != vector.end(); -} - -static bool vectorsIntersect(const std::vector& vectorA, const std::vector& vectorB) { - for (const auto& a : vectorA) { - if (vectorContains(vectorB, a)) { - return true; - } - } - return false; -} - static bool topDown(const IndexedSubfeature& a, const IndexedSubfeature& b) { return a.sortIndex > b.sortIndex; } @@ -53,36 +39,6 @@ static bool topDownSymbols(const IndexedSubfeature& a, const IndexedSubfeature& return a.sortIndex < b.sortIndex; } -static int16_t getAdditionalQueryRadius(const RenderedQueryOptions& queryOptions, - const RenderStyle& style, - const GeometryTile& tile, - const float pixelsToTileUnits) { - - // Determine the additional radius needed factoring in property functions - float additionalRadius = 0; - auto getQueryRadius = [&](const RenderLayer& layer) { - auto bucket = tile.getBucket(*layer.baseImpl); - if (bucket) { - additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(layer) * pixelsToTileUnits); - } - }; - - if (queryOptions.layerIDs) { - for (const auto& layerID : *queryOptions.layerIDs) { - const RenderLayer* layer = style.getRenderLayer(layerID); - if (layer) { - getQueryRadius(*layer); - } - } - } else { - for (const RenderLayer* layer : style.getRenderLayers()) { - getQueryRadius(*layer); - } - } - - return std::min(util::EXTENT, additionalRadius); -} - void FeatureIndex::query( std::unordered_map>& result, const GeometryCoordinates& queryGeometry, @@ -92,13 +48,13 @@ void FeatureIndex::query( const RenderedQueryOptions& queryOptions, const GeometryTileData& geometryTileData, const CanonicalTileID& tileID, - const RenderStyle& style, + const std::vector& layers, const CollisionTile* collisionTile, - const GeometryTile& tile) const { + const float additionalQueryRadius) const { // Determine query radius const float pixelsToTileUnits = util::EXTENT / tileSize / scale; - const int16_t additionalRadius = getAdditionalQueryRadius(queryOptions, style, tile, pixelsToTileUnits); + const int16_t additionalRadius = std::min(util::EXTENT, additionalQueryRadius * pixelsToTileUnits); // Query the grid index mapbox::geometry::box box = mapbox::geometry::envelope(queryGeometry); @@ -113,7 +69,7 @@ void FeatureIndex::query( if (indexedFeature.sortIndex == previousSortIndex) continue; previousSortIndex = indexedFeature.sortIndex; - addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID, style, bearing, pixelsToTileUnits); + addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID, layers, bearing, pixelsToTileUnits); } // Query symbol features, if they've been placed. @@ -124,7 +80,7 @@ void FeatureIndex::query( std::vector symbolFeatures = collisionTile->queryRenderedSymbols(queryGeometry, scale); std::sort(symbolFeatures.begin(), symbolFeatures.end(), topDownSymbols); for (const auto& symbolFeature : symbolFeatures) { - addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID, style, bearing, pixelsToTileUnits); + addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID, layers, bearing, pixelsToTileUnits); } } @@ -135,30 +91,39 @@ void FeatureIndex::addFeature( const RenderedQueryOptions& options, const GeometryTileData& geometryTileData, const CanonicalTileID& tileID, - const RenderStyle& style, + const std::vector& layers, const float bearing, const float pixelsToTileUnits) const { - auto& layerIDs = bucketLayerIDs.at(indexedFeature.bucketName); - if (options.layerIDs && !vectorsIntersect(layerIDs, *options.layerIDs)) { - return; - } - - auto sourceLayer = geometryTileData.getLayer(indexedFeature.sourceLayerName); - assert(sourceLayer); + auto getRenderLayer = [&] (const std::string& layerID) -> const RenderLayer* { + for (const auto& layer : layers) { + if (layer->getID() == layerID) { + return layer; + } + } + return nullptr; + }; - auto geometryTileFeature = sourceLayer->getFeature(indexedFeature.index); - assert(geometryTileFeature); + // Lazily calculated. + std::unique_ptr sourceLayer; + std::unique_ptr geometryTileFeature; - for (const auto& layerID : layerIDs) { - if (options.layerIDs && !vectorContains(*options.layerIDs, layerID)) { + for (const std::string& layerID : bucketLayerIDs.at(indexedFeature.bucketName)) { + const RenderLayer* renderLayer = getRenderLayer(layerID); + if (!renderLayer) { continue; } - auto renderLayer = style.getRenderLayer(layerID); - if (!renderLayer || - (!renderLayer->is() && - !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits))) { + if (!geometryTileFeature) { + sourceLayer = geometryTileData.getLayer(indexedFeature.sourceLayerName); + assert(sourceLayer); + + geometryTileFeature = sourceLayer->getFeature(indexedFeature.index); + assert(geometryTileFeature); + } + + if (!renderLayer->is() && + !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits)) { continue; } diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp index 83f339a9de..2ae7da33df 100644 --- a/src/mbgl/geometry/feature_index.hpp +++ b/src/mbgl/geometry/feature_index.hpp @@ -11,9 +11,8 @@ namespace mbgl { -class GeometryTile; class RenderedQueryOptions; -class RenderStyle; +class RenderLayer; class CollisionTile; class CanonicalTileID; @@ -42,9 +41,9 @@ public: const RenderedQueryOptions& options, const GeometryTileData&, const CanonicalTileID&, - const RenderStyle&, + const std::vector&, const CollisionTile*, - const GeometryTile& tile) const; + const float additionalQueryRadius) const; static optional translateQueryGeometry( const GeometryCoordinates& queryGeometry, @@ -63,7 +62,7 @@ private: const RenderedQueryOptions& options, const GeometryTileData&, const CanonicalTileID&, - const RenderStyle&, + const std::vector&, const float bearing, const float pixelsToTileUnits) const; diff --git a/src/mbgl/gl/context.cpp b/src/mbgl/gl/context.cpp index e18f1e0bcf..d0c538efb0 100644 --- a/src/mbgl/gl/context.cpp +++ b/src/mbgl/gl/context.cpp @@ -87,7 +87,9 @@ static_assert(std::is_same::value, "OpenGL type mis Context::Context() = default; Context::~Context() { - reset(); + if (cleanupOnDestruction) { + reset(); + } } void Context::initializeExtensions(const std::function& getProcAddress) { @@ -457,6 +459,9 @@ Framebuffer Context::createFramebuffer(const Texture& color) { Framebuffer Context::createFramebuffer(const Texture& color, const Renderbuffer& depthTarget) { + if (color.size != depthTarget.size) { + throw std::runtime_error("Renderbuffer size mismatch"); + } auto fbo = createFramebuffer(); bindFramebuffer = fbo; MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color.texture, 0)); @@ -481,7 +486,7 @@ Context::createTexture(const Size size, const void* data, TextureFormat format, void Context::updateTexture( TextureID id, const Size size, const void* data, TextureFormat format, TextureUnit unit) { - activeTexture = unit; + activeTextureUnit = unit; texture[unit] = id; MBGL_CHECK_ERROR(glTexImage2D(GL_TEXTURE_2D, 0, static_cast(format), size.width, size.height, 0, static_cast(format), GL_UNSIGNED_BYTE, @@ -495,7 +500,7 @@ void Context::bindTexture(Texture& obj, TextureWrap wrapX, TextureWrap wrapY) { if (filter != obj.filter || mipmap != obj.mipmap || wrapX != obj.wrapX || wrapY != obj.wrapY) { - activeTexture = unit; + activeTextureUnit = unit; texture[unit] = obj.texture; if (filter != obj.filter || mipmap != obj.mipmap) { @@ -526,7 +531,7 @@ void Context::bindTexture(Texture& obj, } else if (texture[unit] != obj.texture) { // We are checking first to avoid setting the active texture without a subsequent // texture bind. - activeTexture = unit; + activeTextureUnit = unit; texture[unit] = obj.texture; } } @@ -558,7 +563,7 @@ void Context::setDirtyState() { clearStencil.setDirty(); program.setDirty(); lineWidth.setDirty(); - activeTexture.setDirty(); + activeTextureUnit.setDirty(); pixelStorePack.setDirty(); pixelStoreUnpack.setDirty(); #if not MBGL_USE_GLES2 @@ -709,8 +714,10 @@ void Context::performCleanup() { if (!abandonedTextures.empty()) { for (const auto id : abandonedTextures) { - if (activeTexture == id) { - activeTexture.setDirty(); + for (auto& binding : texture) { + if (binding == id) { + binding.setDirty(); + } } } MBGL_CHECK_ERROR(glDeleteTextures(int(abandonedTextures.size()), abandonedTextures.data())); diff --git a/src/mbgl/gl/context.hpp b/src/mbgl/gl/context.hpp index 9923567276..528113cbba 100644 --- a/src/mbgl/gl/context.hpp +++ b/src/mbgl/gl/context.hpp @@ -190,7 +190,13 @@ public: return vertexArray.get(); } + void setCleanupOnDestruction(bool cleanup) { + cleanupOnDestruction = cleanup; + } + private: + bool cleanupOnDestruction = true; + std::unique_ptr debugging; std::unique_ptr vertexArray; #if MBGL_HAS_BINARY_PROGRAMS @@ -198,7 +204,7 @@ private: #endif public: - State activeTexture; + State activeTextureUnit; State bindFramebuffer; State viewport; State scissorTest; diff --git a/src/mbgl/gl/renderbuffer.hpp b/src/mbgl/gl/renderbuffer.hpp index cc8ff13268..0592557a7f 100644 --- a/src/mbgl/gl/renderbuffer.hpp +++ b/src/mbgl/gl/renderbuffer.hpp @@ -9,9 +9,23 @@ namespace gl { template class Renderbuffer { public: + Renderbuffer(Size size_, UniqueRenderbuffer renderbuffer_, bool dirty_ = false) + : size(std::move(size_)), renderbuffer(std::move(renderbuffer_)), dirty(dirty_) { + } + using type = std::integral_constant; Size size; - gl::UniqueRenderbuffer renderbuffer; + UniqueRenderbuffer renderbuffer; + + void shouldClear(bool clear) { + dirty = clear; + } + bool needsClearing() { + return dirty; + } + +private: + bool dirty; }; } // namespace gl diff --git a/src/mbgl/gl/value.cpp b/src/mbgl/gl/value.cpp index 89014fe6bc..092403af0d 100644 --- a/src/mbgl/gl/value.cpp +++ b/src/mbgl/gl/value.cpp @@ -242,13 +242,13 @@ LineWidth::Type LineWidth::Get() { return lineWidth; } -const constexpr ActiveTexture::Type ActiveTexture::Default; +const constexpr ActiveTextureUnit::Type ActiveTextureUnit::Default; -void ActiveTexture::Set(const Type& value) { +void ActiveTextureUnit::Set(const Type& value) { MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0 + value)); } -ActiveTexture::Type ActiveTexture::Get() { +ActiveTextureUnit::Type ActiveTextureUnit::Get() { GLint activeTexture; MBGL_CHECK_ERROR(glGetIntegerv(GL_ACTIVE_TEXTURE, &activeTexture)); return static_cast(activeTexture - GL_TEXTURE0); diff --git a/src/mbgl/gl/value.hpp b/src/mbgl/gl/value.hpp index 19e9af194f..7b85a5ff4b 100644 --- a/src/mbgl/gl/value.hpp +++ b/src/mbgl/gl/value.hpp @@ -165,7 +165,7 @@ struct LineWidth { static Type Get(); }; -struct ActiveTexture { +struct ActiveTextureUnit { using Type = TextureUnit; static const constexpr Type Default = 0; static void Set(const Type&); diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index 229b8f2ee2..2c90b69b08 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -205,11 +205,11 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph const Shaping result = getShaping( /* string */ text, /* maxWidth: ems */ layout.get() != SymbolPlacementType::Line ? - layout.get() * oneEm : 0, + layout.evaluate(zoom, feature) * oneEm : 0, /* lineHeight: ems */ layout.get() * oneEm, /* anchor */ layout.evaluate(zoom, feature), /* justify */ layout.evaluate(zoom, feature), - /* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.get() * oneEm : 0.0f, + /* spacing: ems */ util::i18n::allowsLetterSpacing(*feature.text) ? layout.evaluate(zoom, feature) * oneEm : 0.0f, /* translate */ Point(layout.evaluate(zoom, feature)[0] * oneEm, layout.evaluate(zoom, feature)[1] * oneEm), /* verticalHeight */ oneEm, /* writingMode */ writingMode, @@ -233,6 +233,7 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph shapedIcon = PositionedIcon::shapeIcon( imagePositions.at(*feature.icon), layout.evaluate(zoom, feature), + layout.evaluate(zoom, feature), layout.evaluate(zoom, feature) * util::DEG2RAD); if (image->second->sdf) { sdfIcons = true; diff --git a/src/mbgl/layout/symbol_projection.cpp b/src/mbgl/layout/symbol_projection.cpp index 8ccd6a0410..279d251f8f 100644 --- a/src/mbgl/layout/symbol_projection.cpp +++ b/src/mbgl/layout/symbol_projection.cpp @@ -94,10 +94,12 @@ namespace mbgl { } - Point project(const Point& point, const mat4& matrix) { + typedef std::pair,float> PointAndCameraDistance; + + PointAndCameraDistance project(const Point& point, const mat4& matrix) { vec4 pos = {{ point.x, point.y, 0, 1 }}; matrix::transformMat4(pos, pos, matrix); - return { static_cast(pos[0] / pos[3]), static_cast(pos[1] / pos[3]) }; + return {{ static_cast(pos[0] / pos[3]), static_cast(pos[1] / pos[3]) }, pos[3] }; } float evaluateSizeForFeature(const ZoomEvaluatedSize& zoomEvaluatedSize, const PlacedSymbol& placedSymbol) { @@ -150,9 +152,20 @@ namespace mbgl { NotEnoughRoom, NeedsFlipping }; + + Point projectTruncatedLineSegment(const Point& previousTilePoint, const Point& currentTilePoint, const Point& previousProjectedPoint, const float minimumLength, const mat4& projectionMatrix) { + // We are assuming "previousTilePoint" won't project to a point within one unit of the camera plane + // If it did, that would mean our label extended all the way out from within the viewport to a (very distant) + // point near the plane of the camera. We wouldn't be able to render the label anyway once it crossed the + // plane of the camera. + const Point projectedUnitVertex = project(previousTilePoint + util::unit(previousTilePoint - currentTilePoint), projectionMatrix).first; + const Point projectedUnitSegment = previousProjectedPoint - projectedUnitVertex; + + return previousProjectedPoint + (projectedUnitSegment * (minimumLength / util::mag(projectedUnitSegment))); + } optional placeGlyphAlongLine(const float offsetX, const float lineOffsetX, const float lineOffsetY, const bool flip, - Point anchorPoint, const uint16_t anchorSegment, const GeometryCoordinates& line, const mat4& labelPlaneMatrix) { + const Point& projectedAnchorPoint, const Point& tileAnchorPoint, const uint16_t anchorSegment, const GeometryCoordinates& line, const mat4& labelPlaneMatrix) { const float combinedOffsetX = flip ? offsetX - lineOffsetX : @@ -172,8 +185,8 @@ namespace mbgl { int32_t currentIndex = dir > 0 ? anchorSegment : anchorSegment + 1; - Point current = anchorPoint; - Point prev = anchorPoint; + Point current = projectedAnchorPoint; + Point prev = projectedAnchorPoint; float distanceToPrev = 0.0; float currentSegmentDistance = 0.0; const float absOffsetX = std::abs(combinedOffsetX); @@ -185,7 +198,18 @@ namespace mbgl { if (currentIndex < 0 || currentIndex >= static_cast(line.size())) return {}; prev = current; - current = project(convertPoint(line.at(currentIndex)), labelPlaneMatrix); + PointAndCameraDistance projection = project(convertPoint(line.at(currentIndex)), labelPlaneMatrix); + if (projection.second > 0) { + current = projection.first; + } else { + // The vertex is behind the plane of the camera, so we can't project it + // Instead, we'll create a vertex along the line that's far enough to include the glyph + const Point previousTilePoint = distanceToPrev == 0 ? + tileAnchorPoint : + convertPoint(line.at(currentIndex - dir)); + const Point currentTilePoint = convertPoint(line.at(currentIndex)); + current = projectTruncatedLineSegment(previousTilePoint, currentTilePoint, prev, absOffsetX - distanceToPrev + 1, labelPlaneMatrix); + } distanceToPrev += currentSegmentDistance; currentSegmentDistance = util::dist(prev, current); @@ -211,29 +235,28 @@ namespace mbgl { const mat4& posMatrix, const mat4& labelPlaneMatrix, const mat4& glCoordMatrix, - gl::VertexVector& dynamicVertexArray) { + gl::VertexVector& dynamicVertexArray, + const Point& projectedAnchorPoint) { const float fontScale = fontSize / 24.0; const float lineOffsetX = symbol.lineOffset[0] * fontSize; const float lineOffsetY = symbol.lineOffset[1] * fontSize; - const Point anchorPoint = project(symbol.anchorPoint, labelPlaneMatrix); - std::vector placedGlyphs; if (symbol.glyphOffsets.size() > 1) { const float firstGlyphOffset = symbol.glyphOffsets.front(); const float lastGlyphOffset = symbol.glyphOffsets.back(); - optional firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); + optional firstPlacedGlyph = placeGlyphAlongLine(fontScale * firstGlyphOffset, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); if (!firstPlacedGlyph) return PlacementResult::NotEnoughRoom; - optional lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); + optional lastPlacedGlyph = placeGlyphAlongLine(fontScale * lastGlyphOffset, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); if (!lastPlacedGlyph) return PlacementResult::NotEnoughRoom; - const Point firstPoint = project(firstPlacedGlyph->point, glCoordMatrix); - const Point lastPoint = project(lastPlacedGlyph->point, glCoordMatrix); + const Point firstPoint = project(firstPlacedGlyph->point, glCoordMatrix).first; + const Point lastPoint = project(lastPlacedGlyph->point, glCoordMatrix).first; if (keepUpright && !flip && (symbol.useVerticalMode ? firstPoint.y < lastPoint.y : firstPoint.x > lastPoint.x)) { @@ -244,7 +267,7 @@ namespace mbgl { for (size_t glyphIndex = 1; glyphIndex < symbol.glyphOffsets.size() - 1; glyphIndex++) { const float glyphOffsetX = symbol.glyphOffsets[glyphIndex]; // Since first and last glyph fit on the line, we're sure that the rest of the glyphs can be placed - auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); + auto placedGlyph = placeGlyphAlongLine(glyphOffsetX * fontScale, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); placedGlyphs.push_back(*placedGlyph); } placedGlyphs.push_back(*lastPlacedGlyph); @@ -252,15 +275,23 @@ namespace mbgl { // Only a single glyph to place // So, determine whether to flip based on projected angle of the line segment it's on if (keepUpright && !flip) { - const Point a = project(convertPoint(symbol.line.at(symbol.segment)), posMatrix); - const Point b = project(convertPoint(symbol.line.at(symbol.segment + 1)), posMatrix); + const Point a = project(symbol.anchorPoint, posMatrix).first; + const Point tileSegmentEnd = convertPoint(symbol.line.at(symbol.segment + 1)); + const PointAndCameraDistance projectedVertex = project(tileSegmentEnd, posMatrix); + // We know the anchor will be in the viewport, but the end of the line segment may be + // behind the plane of the camera, in which case we can use a point at any arbitrary (closer) + // point on the segment. + const Point b = (projectedVertex.second > 0) ? + projectedVertex.first : + projectTruncatedLineSegment(symbol.anchorPoint,tileSegmentEnd, a, 1, posMatrix); + if (symbol.useVerticalMode ? b.y > a.y : b.x < a.x) { return PlacementResult::NeedsFlipping; } } assert(symbol.glyphOffsets.size() == 1); // We are relying on SymbolInstance.hasText filtering out symbols without any glyphs at all const float glyphOffsetX = symbol.glyphOffsets.front(); - optional singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetX, lineOffsetX, lineOffsetY, flip, anchorPoint, symbol.segment, + optional singleGlyph = placeGlyphAlongLine(fontScale * glyphOffsetX, lineOffsetX, lineOffsetY, flip, projectedAnchorPoint, symbol.anchorPoint, symbol.segment, symbol.line, labelPlaneMatrix); if (!singleGlyph) return PlacementResult::NotEnoughRoom; @@ -275,6 +306,7 @@ namespace mbgl { return PlacementResult::OK; } + void reprojectLineLabels(gl::VertexVector& dynamicVertexArray, const std::vector& placedSymbols, const mat4& posMatrix, const style::SymbolPropertyValues& values, const RenderTile& tile, const SymbolSizeBinder& sizeBinder, const TransformState& state, const FrameHistory& frameHistory) { @@ -311,12 +343,14 @@ namespace mbgl { const float pitchScaledFontSize = values.pitchAlignment == style::AlignmentType::Map ? fontSize * perspectiveRatio : fontSize / perspectiveRatio; + + const Point anchorPoint = project(placedSymbol.anchorPoint, labelPlaneMatrix).first; - PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray); + PlacementResult placeUnflipped = placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, false /*unflipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint); if (placeUnflipped == PlacementResult::NotEnoughRoom || (placeUnflipped == PlacementResult::NeedsFlipping && - placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray) == PlacementResult::NotEnoughRoom)) { + placeGlyphsAlongLine(placedSymbol, pitchScaledFontSize, true /*flipped*/, values.keepUpright, posMatrix, labelPlaneMatrix, glCoordMatrix, dynamicVertexArray, anchorPoint) == PlacementResult::NotEnoughRoom)) { hideGlyphs(placedSymbol.glyphOffsets.size(), dynamicVertexArray); } } diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index d5a5923e6c..378bd40ab7 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -50,7 +50,7 @@ public: // StyleObserver void onSourceChanged(style::Source&) override; - void onUpdate(Update) override; + void onUpdate() override; void onStyleLoading() override; void onStyleLoaded() override; void onStyleError(std::exception_ptr) override; @@ -166,11 +166,18 @@ void Map::renderStill(StillImageCallback callback) { impl->stillImageRequest = std::make_unique(std::move(callback)); - impl->onUpdate(Update::Repaint); + impl->onUpdate(); +} + +void Map::renderStill(const CameraOptions& camera, MapDebugOptions debugOptions, StillImageCallback callback) { + impl->cameraMutated = true; + impl->debugOptions = debugOptions; + impl->transform.jumpTo(camera); + renderStill(std::move(callback)); } void Map::triggerRepaint() { - impl->onUpdate(Update::Repaint); + impl->onUpdate(); } #pragma mark - Map::Impl RendererObserver @@ -194,8 +201,11 @@ void Map::Impl::onDidFinishRenderingFrame(RenderMode renderMode, bool needsRepai observer.onDidFinishRenderingFrame(MapObserver::RenderMode(renderMode)); if (needsRepaint || transform.inTransition()) { - onUpdate(Update::Repaint); + onUpdate(); } + } else if (stillImageRequest && rendererFullyLoaded) { + auto request = std::move(stillImageRequest); + request->callback(nullptr); } } @@ -206,9 +216,6 @@ void Map::Impl::onDidFinishRenderingMap() { loading = false; observer.onDidFinishLoadingMap(); } - } else if (stillImageRequest) { - auto request = std::move(stillImageRequest); - request->callback(nullptr); } }; @@ -233,12 +240,12 @@ void Map::setStyle(std::unique_ptr