From a2c7e00cd283d4122b8cb4fa5fd1d70493068119 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Wed, 20 May 2015 12:29:06 -0700 Subject: [PATCH 01/85] refactor annotation adding for shapes; move to some unordered_maps --- src/mbgl/map/annotation.cpp | 66 ++++++++++++++++++++++++++----------- src/mbgl/map/annotation.hpp | 21 +++++++++++- src/mbgl/map/live_tile.cpp | 3 +- src/mbgl/map/live_tile.hpp | 6 ++-- src/mbgl/map/map.cpp | 5 ++- 5 files changed, 76 insertions(+), 25 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 7a7a0ed6831..0bcd07bed1e 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -12,14 +12,6 @@ namespace mbgl { -enum class AnnotationType : uint8_t { - Point, - Shape -}; - -using AnnotationSegment = std::vector; -using AnnotationSegments = std::vector; - class Annotation : private util::noncopyable { friend class AnnotationManager; public: @@ -86,9 +78,10 @@ vec2 AnnotationManager::projectPoint(const LatLng& point) { } std::pair, AnnotationIDs> -AnnotationManager::addPointAnnotations(const std::vector& points, - const std::vector& symbols, - const MapData& data) { +AnnotationManager::addAnnotations(const AnnotationType type, + const std::vector& segments, + const AnnotationsProperties& properties, + const MapData& data) { std::lock_guard lock(mtx); // We pre-generate tiles to contain each annotation up to the map's max zoom. @@ -99,7 +92,9 @@ AnnotationManager::addPointAnnotations(const std::vector& points, const uint16_t extent = 4096; - std::vector annotationIDs; + AnnotationSegment points = segments[0][0]; // FIXME + + AnnotationIDs annotationIDs; annotationIDs.reserve(points.size()); std::vector affectedTiles; @@ -133,13 +128,24 @@ AnnotationManager::addPointAnnotations(const std::vector& points, const GeometryCollection geometries({ { { { coordinate } } } }); - // at render time we style the annotation according to its {sprite} field - const std::map properties = { - { "sprite", (symbols[i].length() ? symbols[i] : defaultPointAnnotationSymbol) } - }; + std::unordered_map featureProperties; + + if (type == AnnotationType::Point) { + // at render time we style the point according to its {sprite} field + const std::string& symbol = properties.at("symbols")[i]; + if (symbol.length()) { + featureProperties.emplace("sprite", symbol); + } else { + featureProperties.emplace("sprite", defaultPointAnnotationSymbol); + } + } else { + + } auto feature = - std::make_shared(FeatureType::Point, geometries, properties); + std::make_shared(FeatureType::Point, + geometries, + featureProperties); auto tile_it = tiles.find(tileID); if (tile_it != tiles.end()) { @@ -185,6 +191,26 @@ AnnotationManager::addPointAnnotations(const std::vector& points, return std::make_pair(affectedTiles, annotationIDs); } +std::pair, AnnotationIDs> +AnnotationManager::addPointAnnotations(const AnnotationSegment& points, + const AnnotationsProperties& properties, + const MapData& data) { + return addAnnotations(AnnotationType::Point, + { { points } }, + properties, + data); +} + +std::pair, AnnotationIDs> +AnnotationManager::addShapeAnnotations(const std::vector& shapes, + const AnnotationsProperties& properties, + const MapData& data) { + return addAnnotations(AnnotationType::Shape, + shapes, + properties, + data); +} + std::vector AnnotationManager::removeAnnotations(const AnnotationIDs& ids, const MapData& data) { std::lock_guard lock(mtx); @@ -235,8 +261,8 @@ std::vector AnnotationManager::removeAnnotations(const AnnotationIDs& id return affectedTiles; } -std::vector AnnotationManager::getAnnotationsInBounds(const LatLngBounds& queryBounds, - const MapData& data) const { +AnnotationIDs AnnotationManager::getAnnotationsInBounds(const LatLngBounds& queryBounds, + const MapData& data) const { std::lock_guard lock(mtx); const uint8_t z = data.transform.getMaxZoom(); @@ -248,7 +274,7 @@ std::vector AnnotationManager::getAnnotationsInBounds(const LatLngBoun const TileID nwTile(z, swPoint.x * z2, nePoint.y * z2); const TileID seTile(z, nePoint.x * z2, swPoint.y * z2); - std::vector matchingAnnotations; + AnnotationIDs matchingAnnotations; for (auto& tile : tiles) { TileID id = tile.first; diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp index a80b03226fb..96e148e6e1b 100644 --- a/src/mbgl/map/annotation.hpp +++ b/src/mbgl/map/annotation.hpp @@ -22,6 +22,14 @@ class LiveTile; class MapData; using AnnotationIDs = std::vector; +using AnnotationSegment = std::vector; +using AnnotationSegments = std::vector; +using AnnotationsProperties = std::unordered_map>; + +enum class AnnotationType : uint8_t { + Point, + Shape +}; class AnnotationManager : private util::noncopyable { public: @@ -30,7 +38,13 @@ class AnnotationManager : private util::noncopyable { void setDefaultPointAnnotationSymbol(const std::string& symbol); std::pair, AnnotationIDs> addPointAnnotations( - const std::vector&, const std::vector& symbols, const MapData&); + const AnnotationSegment& points, + const AnnotationsProperties& properties, + const MapData&); + std::pair, AnnotationIDs> addShapeAnnotations( + const std::vector& shapes, + const AnnotationsProperties& properties, + const MapData&); std::vector removeAnnotations(const AnnotationIDs&, const MapData&); AnnotationIDs getAnnotationsInBounds(const LatLngBounds&, const MapData&) const; LatLngBounds getBoundsForAnnotations(const AnnotationIDs&) const; @@ -42,6 +56,11 @@ class AnnotationManager : private util::noncopyable { private: inline uint32_t nextID(); static vec2 projectPoint(const LatLng& point); + std::pair, AnnotationIDs> addAnnotations( + const AnnotationType, + const std::vector& segments, + const AnnotationsProperties& properties, + const MapData&); private: mutable std::mutex mtx; diff --git a/src/mbgl/map/live_tile.cpp b/src/mbgl/map/live_tile.cpp index 06337af184f..999fcbc8899 100644 --- a/src/mbgl/map/live_tile.cpp +++ b/src/mbgl/map/live_tile.cpp @@ -3,7 +3,8 @@ namespace mbgl { -LiveTileFeature::LiveTileFeature(FeatureType type_, GeometryCollection geometries_, std::map properties_) +LiveTileFeature::LiveTileFeature(FeatureType type_, GeometryCollection geometries_, + std::unordered_map properties_) : type(type_), properties(properties_), geometries(geometries_) {} diff --git a/src/mbgl/map/live_tile.hpp b/src/mbgl/map/live_tile.hpp index b13b5a1f661..a94ae307f57 100644 --- a/src/mbgl/map/live_tile.hpp +++ b/src/mbgl/map/live_tile.hpp @@ -2,6 +2,7 @@ #define MBGL_MAP_LIVE_TILE #include +#include #include @@ -9,7 +10,8 @@ namespace mbgl { class LiveTileFeature : public GeometryTileFeature { public: - LiveTileFeature(FeatureType, GeometryCollection, std::map properties = {{}}); + LiveTileFeature(FeatureType, GeometryCollection, + std::unordered_map properties = {{}}); FeatureType getType() const override { return type; } mapbox::util::optional getValue(const std::string&) const override; @@ -17,7 +19,7 @@ class LiveTileFeature : public GeometryTileFeature { private: FeatureType type = FeatureType::Unknown; - std::map properties; + std::unordered_map properties; GeometryCollection geometries; }; diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 8514c6ecb28..42e902d2c45 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -7,6 +7,8 @@ #include #include +#include + namespace mbgl { Map::Map(View& view, FileSource& fileSource, MapMode mode) @@ -251,7 +253,8 @@ uint32_t Map::addPointAnnotation(const LatLng& point, const std::string& symbol) } std::vector Map::addPointAnnotations(const std::vector& points, const std::vector& symbols) { - auto result = data->annotationManager.addPointAnnotations(points, symbols, *data); + AnnotationsProperties properties = { { "symbols", symbols } }; + auto result = data->annotationManager.addPointAnnotations(points, properties, *data); context->invoke(&MapContext::updateAnnotationTiles, result.first); return result.second; } From b977a57766c6036d4870cfa66c9233dd21f24736 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Wed, 20 May 2015 14:03:55 -0700 Subject: [PATCH 02/85] basic roughing out of shape paints & feature creation --- include/mbgl/map/map.hpp | 1 + src/mbgl/map/annotation.cpp | 159 +++++++++++++++++--------------- src/mbgl/map/annotation.hpp | 3 +- src/mbgl/map/map.cpp | 6 ++ src/mbgl/style/style_parser.cpp | 48 +++++++++- 5 files changed, 139 insertions(+), 78 deletions(-) diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index e71c5f5b4d6..706b47226d3 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -118,6 +118,7 @@ class Map : private util::noncopyable { uint32_t addPointAnnotation(const LatLng&, const std::string& symbol); std::vector addPointAnnotations(const std::vector&, const std::vector& symbols); + uint32_t addShapeAnnotation(const std::vector&); void removeAnnotation(uint32_t); void removeAnnotations(const std::vector&); std::vector getAnnotationsInBounds(const LatLngBounds&); diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 0bcd07bed1e..22e96b1db1b 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -92,96 +92,108 @@ AnnotationManager::addAnnotations(const AnnotationType type, const uint16_t extent = 4096; - AnnotationSegment points = segments[0][0]; // FIXME - AnnotationIDs annotationIDs; - annotationIDs.reserve(points.size()); + annotationIDs.reserve((type == AnnotationType::Shape ? segments.size() : segments[0][0].size())); std::vector affectedTiles; - for (size_t i = 0; i < points.size(); ++i) { + for (size_t s = 0; s < segments.size(); ++s) { + auto& shape = segments[s]; + const uint32_t annotationID = nextID(); // track the annotation global ID and its geometry - auto anno_it = annotations.emplace( - annotationID, - util::make_unique(AnnotationType::Point, - AnnotationSegments({ { points[i] } }))); + auto anno_it = annotations.emplace(annotationID, + util::make_unique(type, shape)); - const uint8_t maxZoom = data.transform.getMaxZoom(); + for (size_t l = 0; l < segments[s].size(); ++l) { + auto& line = shape[l]; - // side length of map at this zoom - uint32_t z2 = 1 << maxZoom; + for (size_t p = 0; p < line.size(); ++p) { + auto& point = line[p]; - // projection conversion into unit space - const vec2 p = projectPoint(points[i]); + const uint8_t maxZoom = data.transform.getMaxZoom(); - uint32_t x = p.x * z2; - uint32_t y = p.y * z2; + // side length of map at this zoom + uint32_t z2 = 1 << maxZoom; - for (int8_t z = maxZoom; z >= 0; z--) { - affectedTiles.emplace_back(z, x, y); - TileID tileID = affectedTiles.back(); + // projection conversion into unit space + const vec2 pp = projectPoint(point); - // calculate tile coordinate - const Coordinate coordinate(extent * (p.x * z2 - x), extent * (p.y * z2 - y)); + uint32_t x = pp.x * z2; + uint32_t y = pp.y * z2; - const GeometryCollection geometries({ { { { coordinate } } } }); + for (int8_t z = maxZoom; z >= 0; z--) { + affectedTiles.emplace_back(z, x, y); + TileID tileID = affectedTiles.back(); - std::unordered_map featureProperties; + // calculate tile coordinate + const Coordinate coordinate(extent * (pp.x * z2 - x), extent * (pp.y * z2 - y)); - if (type == AnnotationType::Point) { - // at render time we style the point according to its {sprite} field - const std::string& symbol = properties.at("symbols")[i]; - if (symbol.length()) { - featureProperties.emplace("sprite", symbol); - } else { - featureProperties.emplace("sprite", defaultPointAnnotationSymbol); - } - } else { + const GeometryCollection geometries({ { { { coordinate } } } }); - } + std::unordered_map featureProperties; - auto feature = - std::make_shared(FeatureType::Point, - geometries, - featureProperties); - - auto tile_it = tiles.find(tileID); - if (tile_it != tiles.end()) { - // - // We have this tile created already. Add this feature to it. - // - // get point layer & add feature - auto layer = - tile_it->second.second->getMutableLayer(layerID); - layer->addFeature(feature); - // record annotation association with tile - tile_it->second.first.insert(annotationID); - } else { - // - // We need to create a new tile for this feature. - // - // create point layer & add feature - util::ptr layer = std::make_shared(); - layer->addFeature(feature); - // create tile & record annotation association - auto tile_pos = tiles.emplace( - tileID, std::make_pair(std::unordered_set({ annotationID }), - util::make_unique())); - // add point layer to tile - tile_pos.first->second.second->addLayer(layerID, layer); + if (type == AnnotationType::Point) { + // at render time we style the point according to its {sprite} field + const std::string& symbol = properties.at("symbols")[p]; + if (symbol.length()) { + featureProperties.emplace("sprite", symbol); + } else { + featureProperties.emplace("sprite", defaultPointAnnotationSymbol); + } + } else { + + } + + auto feature = + std::make_shared( + (type == AnnotationType::Point ? FeatureType::Point : FeatureType::LineString), + geometries, + featureProperties); + + auto tile_it = tiles.find(tileID); + if (tile_it != tiles.end()) { + // + // We have this tile created already. Add this feature to it. + // + // get point layer & add feature + auto layer = + tile_it->second.second->getMutableLayer( + (type == AnnotationType::Point ? PointLayerID : ShapeLayerID) + ); + layer->addFeature(feature); + // record annotation association with tile + tile_it->second.first.insert(annotationID); + } else { + // + // We need to create a new tile for this feature. + // + // create point layer & add feature + util::ptr layer = std::make_shared(); + layer->addFeature(feature); + // create tile & record annotation association + auto tile_pos = tiles.emplace( + tileID, std::make_pair(std::unordered_set({ annotationID }), + util::make_unique())); + // add point layer to tile + tile_pos.first->second.second->addLayer( + (type == AnnotationType::Point ? PointLayerID : ShapeLayerID), + layer + ); + } + + // Record annotation association with tile and tile feature. This is used to determine stale tiles, + // as well as to remove the feature from the tile upon annotation deletion. + anno_it.first->second->tileFeatures.emplace( + tileID, std::weak_ptr(feature)); + + // get ready for the next-lower zoom number + z2 /= 2; + x /= 2; + y /= 2; + } } - - // Record annotation association with tile and tile feature. This is used to determine stale tiles, - // as well as to remove the feature from the tile upon annotation deletion. - anno_it.first->second->tileFeatures.emplace( - tileID, std::weak_ptr(feature)); - - // get ready for the next-lower zoom number - z2 /= 2; - x /= 2; - y /= 2; } annotationIDs.push_back(annotationID); @@ -248,7 +260,7 @@ std::vector AnnotationManager::removeAnnotations(const AnnotationIDs& id const auto& features_it = annotation->tileFeatures.find(tid); if (features_it != annotation->tileFeatures.end()) { const auto& layer = - tiles[tid].second->getMutableLayer(layerID); + tiles[tid].second->getMutableLayer(PointLayerID); // layer->removeFeature(features_it->second); affectedTiles.push_back(tid); } @@ -334,6 +346,7 @@ const LiveTile* AnnotationManager::getTile(const TileID& id) { return nullptr; } -const std::string AnnotationManager::layerID = "com.mapbox.annotations.points"; +const std::string AnnotationManager::PointLayerID = "com.mapbox.annotations.points"; +const std::string AnnotationManager::ShapeLayerID = "com.mapbox.annotations.shape"; } diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp index 96e148e6e1b..4ce2973a8cc 100644 --- a/src/mbgl/map/annotation.hpp +++ b/src/mbgl/map/annotation.hpp @@ -51,7 +51,8 @@ class AnnotationManager : private util::noncopyable { const LiveTile* getTile(const TileID& id); - static const std::string layerID; + static const std::string PointLayerID; + static const std::string ShapeLayerID; private: inline uint32_t nextID(); diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 42e902d2c45..6d7a6fa5047 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -259,6 +259,12 @@ std::vector Map::addPointAnnotations(const std::vector& points return result.second; } +uint32_t Map::addShapeAnnotation(const std::vector& shape) { + auto result = data->annotationManager.addShapeAnnotations({{ shape }}, {{}}, *data); + context->invoke(&MapContext::updateAnnotationTiles, result.first); + return result.second[0]; +} + void Map::removeAnnotation(uint32_t annotation) { removeAnnotations({ annotation }); } diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index 313fe3df89f..a99c1082d9c 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -37,14 +37,54 @@ void StyleParser::parse(JSVal document) { if (document.HasMember("layers")) { parseLayers(document["layers"]); + + + + + + + const std::string& shapeID = AnnotationManager::ShapeLayerID; + + std::map shapePaints; + rapidjson::Document d1; + rapidjson::Value lineWidth(rapidjson::kObjectType); + lineWidth.AddMember("line-width", 5, d1.GetAllocator()); + parsePaint(lineWidth, shapePaints[ClassID::Default]); + rapidjson::Value lineColor(rapidjson::kObjectType); + lineColor.AddMember("line-color", "#ff0000", d1.GetAllocator()); + parsePaint(lineColor, shapePaints[ClassID::Default]); + util::ptr shapeAnnotations = std::make_shared(shapeID, std::move(shapePaints)); + shapeAnnotations->type = StyleLayerType::Line; + layersMap.emplace(shapeID, std::pair> { JSVal(shapeID), shapeAnnotations }); + layers.emplace_back(shapeAnnotations); + + util::ptr lineBucket = std::make_shared(shapeAnnotations->type); + lineBucket->name = shapeAnnotations->id; + lineBucket->source_layer = shapeAnnotations->id; + + // parse layout? + + util::ptr source1 = std::make_shared(); + sourcesMap.emplace(shapeID, source1); + sources.emplace_back(source1); + source1->info.type = SourceType::Annotations; + lineBucket->source = source1; + shapeAnnotations->bucket = lineBucket; + + + + + + + // create point annotations layer // - const std::string& id = AnnotationManager::layerID; + const std::string& pointID = AnnotationManager::PointLayerID; std::map paints; - util::ptr annotations = std::make_shared(id, std::move(paints)); + util::ptr annotations = std::make_shared(pointID, std::move(paints)); annotations->type = StyleLayerType::Symbol; - layersMap.emplace(id, std::pair> { JSVal(id), annotations }); + layersMap.emplace(pointID, std::pair> { JSVal(pointID), annotations }); layers.emplace_back(annotations); util::ptr pointBucket = std::make_shared(annotations->type); @@ -60,7 +100,7 @@ void StyleParser::parse(JSVal document) { parseLayout(iconOverlap, pointBucket); util::ptr source = std::make_shared(); - sourcesMap.emplace(id, source); + sourcesMap.emplace(pointID, source); sources.emplace_back(source); source->info.type = SourceType::Annotations; pointBucket->source = source; From ed50422f67df0d40729c95a55dadb5eb9b7f2ebe Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Wed, 20 May 2015 15:37:13 -0700 Subject: [PATCH 03/85] split out multiple layers in live tiles --- src/mbgl/map/annotation.cpp | 51 +++++++++++++++------------------ src/mbgl/style/style_parser.cpp | 18 ++++-------- 2 files changed, 28 insertions(+), 41 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 22e96b1db1b..ea3aad96352 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -146,43 +146,38 @@ AnnotationManager::addAnnotations(const AnnotationType type, } + // create tile feature auto feature = std::make_shared( (type == AnnotationType::Point ? FeatureType::Point : FeatureType::LineString), geometries, featureProperties); - auto tile_it = tiles.find(tileID); - if (tile_it != tiles.end()) { - // - // We have this tile created already. Add this feature to it. - // - // get point layer & add feature - auto layer = - tile_it->second.second->getMutableLayer( - (type == AnnotationType::Point ? PointLayerID : ShapeLayerID) - ); - layer->addFeature(feature); - // record annotation association with tile - tile_it->second.first.insert(annotationID); + // check for tile & create if necessary + auto tile_pos = tiles.emplace( + tileID, + std::make_pair( + std::unordered_set({ annotationID }), + util::make_unique() + ) + ); + + // check for annotation layer & create if necessary + util::ptr layer; + auto& layerID = (type == AnnotationType::Point ? PointLayerID : ShapeLayerID); + if (tile_pos.second || tile_pos.first->second.second->getMutableLayer(layerID) == nullptr) { + layer = std::make_shared(); + tile_pos.first->second.second->addLayer(layerID, layer); } else { - // - // We need to create a new tile for this feature. - // - // create point layer & add feature - util::ptr layer = std::make_shared(); - layer->addFeature(feature); - // create tile & record annotation association - auto tile_pos = tiles.emplace( - tileID, std::make_pair(std::unordered_set({ annotationID }), - util::make_unique())); - // add point layer to tile - tile_pos.first->second.second->addLayer( - (type == AnnotationType::Point ? PointLayerID : ShapeLayerID), - layer - ); + layer = tile_pos.first->second.second->getMutableLayer(layerID); + + // associate annotation with tile + tile_pos.first->second.first.insert(annotationID); } + // add feature to layer + layer->addFeature(feature); + // Record annotation association with tile and tile feature. This is used to determine stale tiles, // as well as to remove the feature from the tile upon annotation deletion. anno_it.first->second->tileFeatures.emplace( diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index a99c1082d9c..6da48b9f3e6 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -37,12 +37,8 @@ void StyleParser::parse(JSVal document) { if (document.HasMember("layers")) { parseLayers(document["layers"]); - - - - - - + // create single shape annotations layer (for now) + // const std::string& shapeID = AnnotationManager::ShapeLayerID; std::map shapePaints; @@ -51,7 +47,7 @@ void StyleParser::parse(JSVal document) { lineWidth.AddMember("line-width", 5, d1.GetAllocator()); parsePaint(lineWidth, shapePaints[ClassID::Default]); rapidjson::Value lineColor(rapidjson::kObjectType); - lineColor.AddMember("line-color", "#ff0000", d1.GetAllocator()); + lineColor.AddMember("line-color", "#0000ff", d1.GetAllocator()); parsePaint(lineColor, shapePaints[ClassID::Default]); util::ptr shapeAnnotations = std::make_shared(shapeID, std::move(shapePaints)); shapeAnnotations->type = StyleLayerType::Line; @@ -70,12 +66,8 @@ void StyleParser::parse(JSVal document) { source1->info.type = SourceType::Annotations; lineBucket->source = source1; shapeAnnotations->bucket = lineBucket; - - - - - - + // + // end shape annotations // create point annotations layer // From fb65df242ad92ec655519c7c4a821298f577affc Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 22 May 2015 15:43:50 -0700 Subject: [PATCH 04/85] abstract tile(s) feature creation to handle built-up shapes; move to unordered sets --- src/mbgl/map/annotation.cpp | 222 ++++++++++++++++++++++------------- src/mbgl/map/annotation.hpp | 17 ++- src/mbgl/map/map_context.cpp | 2 +- src/mbgl/map/map_context.hpp | 2 +- src/mbgl/map/source.cpp | 2 +- src/mbgl/map/source.hpp | 3 +- 6 files changed, 156 insertions(+), 92 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index ea3aad96352..7c81fa69c28 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -77,7 +77,7 @@ vec2 AnnotationManager::projectPoint(const LatLng& point) { return { x, y }; } -std::pair, AnnotationIDs> +std::pair, AnnotationIDs> AnnotationManager::addAnnotations(const AnnotationType type, const std::vector& segments, const AnnotationsProperties& properties, @@ -90,115 +90,169 @@ AnnotationManager::addAnnotations(const AnnotationType type, // annotations are added or removed in order to refresh the map render without // touching the base map underneath. - const uint16_t extent = 4096; - AnnotationIDs annotationIDs; - annotationIDs.reserve((type == AnnotationType::Shape ? segments.size() : segments[0][0].size())); + annotationIDs.reserve((type == AnnotationType::Shape ? + segments.size() : // shapes + segments[0][0].size())); // points - std::vector affectedTiles; + std::unordered_set affectedTiles; for (size_t s = 0; s < segments.size(); ++s) { auto& shape = segments[s]; - const uint32_t annotationID = nextID(); + std::vector>> projectedShape; - // track the annotation global ID and its geometry - auto anno_it = annotations.emplace(annotationID, - util::make_unique(type, shape)); + const uint32_t annotationID = nextID(); for (size_t l = 0; l < segments[s].size(); ++l) { auto& line = shape[l]; + projectedShape.push_back({{}}); + auto& projectedLine = projectedShape.back(); + for (size_t p = 0; p < line.size(); ++p) { auto& point = line[p]; - const uint8_t maxZoom = data.transform.getMaxZoom(); - - // side length of map at this zoom - uint32_t z2 = 1 << maxZoom; - // projection conversion into unit space const vec2 pp = projectPoint(point); + projectedLine.push_back(pp); + } - uint32_t x = pp.x * z2; - uint32_t y = pp.y * z2; + projectedShape.push_back(projectedLine); + } - for (int8_t z = maxZoom; z >= 0; z--) { - affectedTiles.emplace_back(z, x, y); - TileID tileID = affectedTiles.back(); + std::unordered_map featureProperties; - // calculate tile coordinate - const Coordinate coordinate(extent * (pp.x * z2 - x), extent * (pp.y * z2 - y)); + if (type == AnnotationType::Point) { + // at render time we style the point according to its {sprite} field + const std::string& symbol = properties.at("symbols")[s]; + if (symbol.length()) { + featureProperties.emplace("sprite", symbol); + } else { + featureProperties.emplace("sprite", defaultPointAnnotationSymbol); + } + } - const GeometryCollection geometries({ { { { coordinate } } } }); + auto featureAffectedTiles = addTileFeature( + annotationID, + shape, + projectedShape, + type, + featureProperties, + data.transform.getMaxZoom() + ); - std::unordered_map featureProperties; + std::copy(featureAffectedTiles.begin(), featureAffectedTiles.end(), std::inserter(affectedTiles, affectedTiles.begin())); - if (type == AnnotationType::Point) { - // at render time we style the point according to its {sprite} field - const std::string& symbol = properties.at("symbols")[p]; - if (symbol.length()) { - featureProperties.emplace("sprite", symbol); - } else { - featureProperties.emplace("sprite", defaultPointAnnotationSymbol); - } - } else { - - } - - // create tile feature - auto feature = - std::make_shared( - (type == AnnotationType::Point ? FeatureType::Point : FeatureType::LineString), - geometries, - featureProperties); - - // check for tile & create if necessary - auto tile_pos = tiles.emplace( - tileID, - std::make_pair( - std::unordered_set({ annotationID }), - util::make_unique() - ) - ); - - // check for annotation layer & create if necessary - util::ptr layer; - auto& layerID = (type == AnnotationType::Point ? PointLayerID : ShapeLayerID); - if (tile_pos.second || tile_pos.first->second.second->getMutableLayer(layerID) == nullptr) { - layer = std::make_shared(); - tile_pos.first->second.second->addLayer(layerID, layer); - } else { - layer = tile_pos.first->second.second->getMutableLayer(layerID); - - // associate annotation with tile - tile_pos.first->second.first.insert(annotationID); - } - - // add feature to layer - layer->addFeature(feature); - - // Record annotation association with tile and tile feature. This is used to determine stale tiles, - // as well as to remove the feature from the tile upon annotation deletion. - anno_it.first->second->tileFeatures.emplace( - tileID, std::weak_ptr(feature)); - - // get ready for the next-lower zoom number - z2 /= 2; - x /= 2; - y /= 2; + annotationIDs.push_back(annotationID); + } + + // Tile:IDs that need refreshed and the annotation identifiers held onto by the client. + return std::make_pair(affectedTiles, annotationIDs); +} + +std::unordered_set +AnnotationManager::addTileFeature(const uint32_t annotationID, + const AnnotationSegments& segments, + const std::vector>>& projectedFeature, + const AnnotationType& type, + const std::unordered_map& properties, + const uint8_t maxZoom) { + + // track the annotation global ID and its original geometry + auto anno_it = annotations.emplace(annotationID, util::make_unique(type, segments)); + + // side length of map at max zoom + uint32_t z2 = 1 << maxZoom; + + const uint16_t extent = 4096; + + uint32_t x = 0; + uint32_t y = 0; + + std::unordered_set affectedTiles; + + for (int8_t z = maxZoom; z >= 0; z--) { + + GeometryCollection geometries; + + if (type == AnnotationType::Point) { + auto& pp = projectedFeature[0][0]; + + x = pp.x * z2; + y = pp.y * z2; + + const Coordinate coordinate(extent * (pp.x * z2 - x), extent * (pp.y * z2 - y)); + + geometries = {{ {{ coordinate }} }}; + } else { + for (size_t l = 0; l < projectedFeature.size(); ++l) { + + std::vector line; + + for (size_t p = 0; p < projectedFeature[l].size(); ++p) { + + auto& pp = projectedFeature[l][p]; + + x = pp.x * z2; + y = pp.y * z2; + + const Coordinate coordinate(extent * (pp.x * z2 - x), extent * (pp.y * z2 - y)); + + line.push_back(coordinate); } + + geometries.push_back(line); } } - annotationIDs.push_back(annotationID); + auto tileID = TileID(z, x, y); + + // create tile feature + auto feature = std::make_shared( + (type == AnnotationType::Point ? FeatureType::Point : FeatureType::LineString), + geometries, + properties + ); + + // check for tile & create if necessary + auto tile_pos = tiles.emplace(tileID, + std::make_pair(std::unordered_set({ annotationID }), + util::make_unique())); + + // check for annotation layer & create if necessary + util::ptr layer; + auto& layerID = (type == AnnotationType::Point ? PointLayerID : ShapeLayerID); + if (tile_pos.second || tile_pos.first->second.second->getMutableLayer(layerID) == nullptr) { + layer = std::make_shared(); + tile_pos.first->second.second->addLayer(layerID, layer); + } else { + layer = tile_pos.first->second.second->getMutableLayer(layerID); + + // associate annotation with tile + tile_pos.first->second.first.insert(annotationID); + } + + // add feature to layer + layer->addFeature(feature); + + // Record annotation association with tile and tile feature. This is used to determine stale tiles, + // as well as to remove the feature from the tile upon annotation deletion. + anno_it.first->second->tileFeatures.emplace(tileID, std::weak_ptr(feature)); + + // track affected tile + affectedTiles.insert(tileID); + + // get ready for the next-lower zoom number + z2 /= 2; + x /= 2; + y /= 2; } - // Tile:IDs that need refreshed and the annotation identifiers held onto by the client. - return std::make_pair(affectedTiles, annotationIDs); + return affectedTiles; } -std::pair, AnnotationIDs> +std::pair, AnnotationIDs> AnnotationManager::addPointAnnotations(const AnnotationSegment& points, const AnnotationsProperties& properties, const MapData& data) { @@ -208,7 +262,7 @@ AnnotationManager::addPointAnnotations(const AnnotationSegment& points, data); } -std::pair, AnnotationIDs> +std::pair, AnnotationIDs> AnnotationManager::addShapeAnnotations(const std::vector& shapes, const AnnotationsProperties& properties, const MapData& data) { @@ -218,11 +272,11 @@ AnnotationManager::addShapeAnnotations(const std::vector& sh data); } -std::vector AnnotationManager::removeAnnotations(const AnnotationIDs& ids, +std::unordered_set AnnotationManager::removeAnnotations(const AnnotationIDs& ids, const MapData& data) { std::lock_guard lock(mtx); - std::vector affectedTiles; + std::unordered_set affectedTiles; std::vector z2s; const uint8_t zoomCount = data.transform.getMaxZoom() + 1; @@ -257,7 +311,7 @@ std::vector AnnotationManager::removeAnnotations(const AnnotationIDs& id const auto& layer = tiles[tid].second->getMutableLayer(PointLayerID); // layer->removeFeature(features_it->second); - affectedTiles.push_back(tid); + affectedTiles.insert(tid); } } annotations.erase(annotationID); diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp index 4ce2973a8cc..76d882e0122 100644 --- a/src/mbgl/map/annotation.hpp +++ b/src/mbgl/map/annotation.hpp @@ -1,6 +1,7 @@ #ifndef MBGL_MAP_ANNOTATIONS #define MBGL_MAP_ANNOTATIONS +#include #include #include #include @@ -19,6 +20,7 @@ namespace mbgl { class Annotation; class Map; class LiveTile; +class LiveTileFeature; class MapData; using AnnotationIDs = std::vector; @@ -37,15 +39,15 @@ class AnnotationManager : private util::noncopyable { ~AnnotationManager(); void setDefaultPointAnnotationSymbol(const std::string& symbol); - std::pair, AnnotationIDs> addPointAnnotations( + std::pair, AnnotationIDs> addPointAnnotations( const AnnotationSegment& points, const AnnotationsProperties& properties, const MapData&); - std::pair, AnnotationIDs> addShapeAnnotations( + std::pair, AnnotationIDs> addShapeAnnotations( const std::vector& shapes, const AnnotationsProperties& properties, const MapData&); - std::vector removeAnnotations(const AnnotationIDs&, const MapData&); + std::unordered_set removeAnnotations(const AnnotationIDs&, const MapData&); AnnotationIDs getAnnotationsInBounds(const LatLngBounds&, const MapData&) const; LatLngBounds getBoundsForAnnotations(const AnnotationIDs&) const; @@ -57,11 +59,18 @@ class AnnotationManager : private util::noncopyable { private: inline uint32_t nextID(); static vec2 projectPoint(const LatLng& point); - std::pair, AnnotationIDs> addAnnotations( + std::pair, AnnotationIDs> addAnnotations( const AnnotationType, const std::vector& segments, const AnnotationsProperties& properties, const MapData&); + std::unordered_set addTileFeature( + const uint32_t annotationID, + const AnnotationSegments&, + const std::vector>>& projectedFeature, + const AnnotationType&, + const std::unordered_map& properties, + const uint8_t maxZoom); private: mutable std::mutex mtx; diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp index f373274ebd0..57115caa253 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -145,7 +145,7 @@ void MapContext::updateTiles() { resourceLoader->update(data, transformState, *glyphAtlas, *spriteAtlas, *texturePool); } -void MapContext::updateAnnotationTiles(const std::vector& ids) { +void MapContext::updateAnnotationTiles(const std::unordered_set& ids) { assert(Environment::currentlyOn(ThreadType::Map)); if (!style) return; for (const auto &source : style->sources) { diff --git a/src/mbgl/map/map_context.hpp b/src/mbgl/map/map_context.hpp index c842454849d..4adcf65aab2 100644 --- a/src/mbgl/map/map_context.hpp +++ b/src/mbgl/map/map_context.hpp @@ -54,7 +54,7 @@ class MapContext : public ResourceLoader::Observer { std::string getStyleJSON() const { return styleJSON; } double getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol); - void updateAnnotationTiles(const std::vector&); + void updateAnnotationTiles(const std::unordered_set&); void setSourceTileCacheSize(size_t size); void onLowMemory(); diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index e8dc3468d34..7c96d06dabf 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -502,7 +502,7 @@ bool Source::update(MapData& data, return allTilesUpdated; } -void Source::invalidateTiles(const std::vector& ids) { +void Source::invalidateTiles(const std::unordered_set& ids) { cache.clear(); for (auto& id : ids) { tiles.erase(id); diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp index 9d3de81b70b..89ecb707add 100644 --- a/src/mbgl/map/source.hpp +++ b/src/mbgl/map/source.hpp @@ -17,6 +17,7 @@ #include #include #include +#include namespace mbgl { @@ -83,7 +84,7 @@ class Source : private util::noncopyable { TexturePool&, bool shouldReparsePartialTiles); - void invalidateTiles(const std::vector&); + void invalidateTiles(const std::unordered_set&); void updateMatrices(const mat4 &projMatrix, const TransformState &transform); void drawClippingMasks(Painter &painter); From 450d4b38e23f4f7a1e0086a45daa03acd68753d1 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 22 May 2015 16:17:04 -0700 Subject: [PATCH 05/85] abstract zoom --- src/mbgl/map/annotation.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 7c81fa69c28..3371058db96 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -97,6 +97,8 @@ AnnotationManager::addAnnotations(const AnnotationType type, std::unordered_set affectedTiles; + const uint8_t maxZoom = data.transform.getMaxZoom(); + for (size_t s = 0; s < segments.size(); ++s) { auto& shape = segments[s]; @@ -139,7 +141,7 @@ AnnotationManager::addAnnotations(const AnnotationType type, projectedShape, type, featureProperties, - data.transform.getMaxZoom() + maxZoom ); std::copy(featureAffectedTiles.begin(), featureAffectedTiles.end(), std::inserter(affectedTiles, affectedTiles.begin())); From 6a82560c170078ac91da8b127a42d3668446ed97 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 22 May 2015 16:17:30 -0700 Subject: [PATCH 06/85] reserve sizes; fix line accumulation --- src/mbgl/map/annotation.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 3371058db96..777a408c0c9 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -103,20 +103,22 @@ AnnotationManager::addAnnotations(const AnnotationType type, auto& shape = segments[s]; std::vector>> projectedShape; + projectedShape.reserve(shape.size()); const uint32_t annotationID = nextID(); - for (size_t l = 0; l < segments[s].size(); ++l) { + for (size_t l = 0; l < shape.size(); ++l) { auto& line = shape[l]; - projectedShape.push_back({{}}); - auto& projectedLine = projectedShape.back(); + std::vector> projectedLine; + projectedLine.reserve(line.size()); for (size_t p = 0; p < line.size(); ++p) { auto& point = line[p]; // projection conversion into unit space - const vec2 pp = projectPoint(point); + const auto pp = projectPoint(point); + projectedLine.push_back(pp); } From 4a0686155796ebaee0231305984c7d1be6eb0af6 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 22 May 2015 16:26:07 -0700 Subject: [PATCH 07/85] better-looking line width --- src/mbgl/style/style_parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index 6da48b9f3e6..3a40156ea07 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -44,7 +44,7 @@ void StyleParser::parse(JSVal document) { std::map shapePaints; rapidjson::Document d1; rapidjson::Value lineWidth(rapidjson::kObjectType); - lineWidth.AddMember("line-width", 5, d1.GetAllocator()); + lineWidth.AddMember("line-width", 2, d1.GetAllocator()); parsePaint(lineWidth, shapePaints[ClassID::Default]); rapidjson::Value lineColor(rapidjson::kObjectType); lineColor.AddMember("line-color", "#0000ff", d1.GetAllocator()); From c54e00d78c749f801b829eef1ef563682895b739 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 22 May 2015 16:32:19 -0700 Subject: [PATCH 08/85] move to fill for now --- src/mbgl/style/style_parser.cpp | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index 3a40156ea07..b61d90d90e1 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -43,20 +43,23 @@ void StyleParser::parse(JSVal document) { std::map shapePaints; rapidjson::Document d1; - rapidjson::Value lineWidth(rapidjson::kObjectType); - lineWidth.AddMember("line-width", 2, d1.GetAllocator()); - parsePaint(lineWidth, shapePaints[ClassID::Default]); - rapidjson::Value lineColor(rapidjson::kObjectType); - lineColor.AddMember("line-color", "#0000ff", d1.GetAllocator()); - parsePaint(lineColor, shapePaints[ClassID::Default]); + rapidjson::Value fillOpacity(rapidjson::kObjectType); + fillOpacity.AddMember("fill-opacity", 0.25, d1.GetAllocator()); + parsePaint(fillOpacity, shapePaints[ClassID::Default]); + rapidjson::Value fillColor(rapidjson::kObjectType); + fillColor.AddMember("fill-color", "#0000ff", d1.GetAllocator()); + parsePaint(fillColor, shapePaints[ClassID::Default]); + rapidjson::Value fillOutlineColor(rapidjson::kObjectType); + fillOutlineColor.AddMember("fill-outline-color", "#ff0000", d1.GetAllocator()); + parsePaint(fillOutlineColor, shapePaints[ClassID::Default]); util::ptr shapeAnnotations = std::make_shared(shapeID, std::move(shapePaints)); - shapeAnnotations->type = StyleLayerType::Line; + shapeAnnotations->type = StyleLayerType::Fill; layersMap.emplace(shapeID, std::pair> { JSVal(shapeID), shapeAnnotations }); layers.emplace_back(shapeAnnotations); - util::ptr lineBucket = std::make_shared(shapeAnnotations->type); - lineBucket->name = shapeAnnotations->id; - lineBucket->source_layer = shapeAnnotations->id; + util::ptr fillBucket = std::make_shared(shapeAnnotations->type); + fillBucket->name = shapeAnnotations->id; + fillBucket->source_layer = shapeAnnotations->id; // parse layout? @@ -64,8 +67,8 @@ void StyleParser::parse(JSVal document) { sourcesMap.emplace(shapeID, source1); sources.emplace_back(source1); source1->info.type = SourceType::Annotations; - lineBucket->source = source1; - shapeAnnotations->bucket = lineBucket; + fillBucket->source = source1; + shapeAnnotations->bucket = fillBucket; // // end shape annotations From 1d1eb840f4bcaaf880aec77cc6ee270802b54f0d Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Tue, 26 May 2015 16:17:21 -0700 Subject: [PATCH 09/85] expose & carry along shape annotation style properties --- include/mbgl/map/map.hpp | 5 ++- .../mbgl/style/style_properties.hpp | 1 - {src => include}/mbgl/style/types.hpp | 0 src/mbgl/map/annotation.cpp | 33 ++++++++++++------- src/mbgl/map/annotation.hpp | 19 +++++++---- src/mbgl/map/map.cpp | 10 ++++-- src/mbgl/style/style_parser.cpp | 9 +++-- src/mbgl/style/style_properties.cpp | 1 + 8 files changed, 53 insertions(+), 25 deletions(-) rename {src => include}/mbgl/style/style_properties.hpp (98%) rename {src => include}/mbgl/style/types.hpp (100%) diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 706b47226d3..858dddb71e8 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,7 @@ #include #include #include +#include namespace mbgl { @@ -118,7 +120,8 @@ class Map : private util::noncopyable { uint32_t addPointAnnotation(const LatLng&, const std::string& symbol); std::vector addPointAnnotations(const std::vector&, const std::vector& symbols); - uint32_t addShapeAnnotation(const std::vector&); + uint32_t addShapeAnnotation(const std::vector&, + const StyleProperties&); void removeAnnotation(uint32_t); void removeAnnotations(const std::vector&); std::vector getAnnotationsInBounds(const LatLngBounds&); diff --git a/src/mbgl/style/style_properties.hpp b/include/mbgl/style/style_properties.hpp similarity index 98% rename from src/mbgl/style/style_properties.hpp rename to include/mbgl/style/style_properties.hpp index 8e8619fb991..9ab528586fa 100644 --- a/src/mbgl/style/style_properties.hpp +++ b/include/mbgl/style/style_properties.hpp @@ -3,7 +3,6 @@ #include #include -#include #include #include diff --git a/src/mbgl/style/types.hpp b/include/mbgl/style/types.hpp similarity index 100% rename from src/mbgl/style/types.hpp rename to include/mbgl/style/types.hpp diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 777a408c0c9..a279fc5262c 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -15,7 +15,7 @@ namespace mbgl { class Annotation : private util::noncopyable { friend class AnnotationManager; public: - Annotation(AnnotationType, const AnnotationSegments&); + Annotation(AnnotationType, const AnnotationSegments&, const StyleProperties&); private: LatLng getPoint() const; @@ -24,13 +24,17 @@ class Annotation : private util::noncopyable { private: const AnnotationType type = AnnotationType::Point; const AnnotationSegments geometry; + const StyleProperties styleProperties; std::unordered_map, TileID::Hash> tileFeatures; const LatLngBounds bounds; }; -Annotation::Annotation(AnnotationType type_, const AnnotationSegments& geometry_) +Annotation::Annotation(AnnotationType type_, + const AnnotationSegments& geometry_, + const StyleProperties& styleProperties_) : type(type_), geometry(geometry_), + styleProperties(styleProperties_), bounds([this] { LatLngBounds bounds_; if (type == AnnotationType::Point) { @@ -80,7 +84,8 @@ vec2 AnnotationManager::projectPoint(const LatLng& point) { std::pair, AnnotationIDs> AnnotationManager::addAnnotations(const AnnotationType type, const std::vector& segments, - const AnnotationsProperties& properties, + const StyleProperties& styleProperties, + const AnnotationsProperties& annotationsProperties, const MapData& data) { std::lock_guard lock(mtx); @@ -129,7 +134,7 @@ AnnotationManager::addAnnotations(const AnnotationType type, if (type == AnnotationType::Point) { // at render time we style the point according to its {sprite} field - const std::string& symbol = properties.at("symbols")[s]; + const std::string& symbol = annotationsProperties.at("symbols")[s]; if (symbol.length()) { featureProperties.emplace("sprite", symbol); } else { @@ -142,6 +147,7 @@ AnnotationManager::addAnnotations(const AnnotationType type, shape, projectedShape, type, + styleProperties, featureProperties, maxZoom ); @@ -160,11 +166,13 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, const AnnotationSegments& segments, const std::vector>>& projectedFeature, const AnnotationType& type, - const std::unordered_map& properties, + const StyleProperties& styleProperties, + const std::unordered_map& featureProperties, const uint8_t maxZoom) { // track the annotation global ID and its original geometry - auto anno_it = annotations.emplace(annotationID, util::make_unique(type, segments)); + auto anno_it = annotations.emplace(annotationID, + util::make_unique(type, segments, styleProperties)); // side length of map at max zoom uint32_t z2 = 1 << maxZoom; @@ -216,7 +224,7 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, auto feature = std::make_shared( (type == AnnotationType::Point ? FeatureType::Point : FeatureType::LineString), geometries, - properties + featureProperties ); // check for tile & create if necessary @@ -258,21 +266,24 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, std::pair, AnnotationIDs> AnnotationManager::addPointAnnotations(const AnnotationSegment& points, - const AnnotationsProperties& properties, + const AnnotationsProperties& annotationsProperties, const MapData& data) { return addAnnotations(AnnotationType::Point, { { points } }, - properties, + defaultStyleProperties(), + annotationsProperties, data); } std::pair, AnnotationIDs> AnnotationManager::addShapeAnnotations(const std::vector& shapes, - const AnnotationsProperties& properties, + const StyleProperties& styleProperties, + const AnnotationsProperties& annotationsProperties, const MapData& data) { return addAnnotations(AnnotationType::Shape, shapes, - properties, + styleProperties, + annotationsProperties, data); } diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp index 76d882e0122..723db0a4426 100644 --- a/src/mbgl/map/annotation.hpp +++ b/src/mbgl/map/annotation.hpp @@ -3,6 +3,8 @@ #include #include +#include +#include #include #include #include @@ -40,12 +42,13 @@ class AnnotationManager : private util::noncopyable { void setDefaultPointAnnotationSymbol(const std::string& symbol); std::pair, AnnotationIDs> addPointAnnotations( - const AnnotationSegment& points, - const AnnotationsProperties& properties, + const AnnotationSegment&, + const AnnotationsProperties&, const MapData&); std::pair, AnnotationIDs> addShapeAnnotations( - const std::vector& shapes, - const AnnotationsProperties& properties, + const std::vector&, + const StyleProperties&, + const AnnotationsProperties&, const MapData&); std::unordered_set removeAnnotations(const AnnotationIDs&, const MapData&); AnnotationIDs getAnnotationsInBounds(const LatLngBounds&, const MapData&) const; @@ -61,15 +64,17 @@ class AnnotationManager : private util::noncopyable { static vec2 projectPoint(const LatLng& point); std::pair, AnnotationIDs> addAnnotations( const AnnotationType, - const std::vector& segments, - const AnnotationsProperties& properties, + const std::vector&, + const StyleProperties&, + const AnnotationsProperties&, const MapData&); std::unordered_set addTileFeature( const uint32_t annotationID, const AnnotationSegments&, const std::vector>>& projectedFeature, const AnnotationType&, - const std::unordered_map& properties, + const StyleProperties&, + const std::unordered_map& featureProperties, const uint8_t maxZoom); private: diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 6d7a6fa5047..eaddc4f80ab 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -259,8 +259,14 @@ std::vector Map::addPointAnnotations(const std::vector& points return result.second; } -uint32_t Map::addShapeAnnotation(const std::vector& shape) { - auto result = data->annotationManager.addShapeAnnotations({{ shape }}, {{}}, *data); +uint32_t Map::addShapeAnnotation(const std::vector& shape, + const StyleProperties& styleProperties) { + auto result = data->annotationManager.addShapeAnnotations( + {{ shape }}, + styleProperties, + {{}}, + *data + ); context->invoke(&MapContext::updateAnnotationTiles, result.first); return result.second[0]; } diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index b61d90d90e1..3c4e0bae807 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -44,14 +44,17 @@ void StyleParser::parse(JSVal document) { std::map shapePaints; rapidjson::Document d1; rapidjson::Value fillOpacity(rapidjson::kObjectType); - fillOpacity.AddMember("fill-opacity", 0.25, d1.GetAllocator()); + fillOpacity.AddMember("fill-opacity", 0.5, d1.GetAllocator()); parsePaint(fillOpacity, shapePaints[ClassID::Default]); rapidjson::Value fillColor(rapidjson::kObjectType); - fillColor.AddMember("fill-color", "#0000ff", d1.GetAllocator()); + fillColor.AddMember("fill-color", "#fff", d1.GetAllocator()); parsePaint(fillColor, shapePaints[ClassID::Default]); rapidjson::Value fillOutlineColor(rapidjson::kObjectType); - fillOutlineColor.AddMember("fill-outline-color", "#ff0000", d1.GetAllocator()); + fillOutlineColor.AddMember("fill-outline-color", "#f00", d1.GetAllocator()); parsePaint(fillOutlineColor, shapePaints[ClassID::Default]); +// rapidjson::Value fillWidth(rapidjson::kObjectType); +// fillWidth.AddMember("line-width", 1, d1.GetAllocator()); +// parsePaint(fillWidth, shapePaints[ClassID::Default]); util::ptr shapeAnnotations = std::make_shared(shapeID, std::move(shapePaints)); shapeAnnotations->type = StyleLayerType::Fill; layersMap.emplace(shapeID, std::pair> { JSVal(shapeID), shapeAnnotations }); diff --git a/src/mbgl/style/style_properties.cpp b/src/mbgl/style/style_properties.cpp index 29730fb85b2..3c5b525c1d4 100644 --- a/src/mbgl/style/style_properties.cpp +++ b/src/mbgl/style/style_properties.cpp @@ -1,4 +1,5 @@ #include +#include namespace mbgl { From 1ae97a4d55a153c2d4d27060ebff934790698205 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Wed, 27 May 2015 15:08:48 -0700 Subject: [PATCH 10/85] basic working runtime shape styling - track ordered shapes in AnnotationManager - add shape source at style parse time - add shape layers & buckets at annotation tile invalidation time - expose Annotation to MapContext for style querying --- src/mbgl/map/annotation.cpp | 41 +++++++------- src/mbgl/map/annotation.hpp | 22 ++++++++ src/mbgl/map/map_context.cpp | 94 +++++++++++++++++++++++++++++++-- src/mbgl/map/map_context.hpp | 4 ++ src/mbgl/map/source.hpp | 1 + src/mbgl/style/style_parser.cpp | 76 +++++++++----------------- 6 files changed, 163 insertions(+), 75 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index a279fc5262c..a47d2823e7c 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -12,29 +12,12 @@ namespace mbgl { -class Annotation : private util::noncopyable { - friend class AnnotationManager; -public: - Annotation(AnnotationType, const AnnotationSegments&, const StyleProperties&); - -private: - LatLng getPoint() const; - LatLngBounds getBounds() const { return bounds; } - -private: - const AnnotationType type = AnnotationType::Point; - const AnnotationSegments geometry; - const StyleProperties styleProperties; - std::unordered_map, TileID::Hash> tileFeatures; - const LatLngBounds bounds; -}; - Annotation::Annotation(AnnotationType type_, const AnnotationSegments& geometry_, const StyleProperties& styleProperties_) - : type(type_), + : styleProperties(styleProperties_), + type(type_), geometry(geometry_), - styleProperties(styleProperties_), bounds([this] { LatLngBounds bounds_; if (type == AnnotationType::Point) { @@ -174,6 +157,10 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, auto anno_it = annotations.emplace(annotationID, util::make_unique(type, segments, styleProperties)); + if (type == AnnotationType::Shape) { + orderedShapeAnnotations.push_back(annotationID); + } + // side length of map at max zoom uint32_t z2 = 1 << maxZoom; @@ -234,7 +221,12 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, // check for annotation layer & create if necessary util::ptr layer; - auto& layerID = (type == AnnotationType::Point ? PointLayerID : ShapeLayerID); + std::string layerID = ""; + if (type == AnnotationType::Point) { + layerID = PointLayerID; + } else { + layerID = ShapeLayerID + "." + std::to_string(annotationID); + } if (tile_pos.second || tile_pos.first->second.second->getMutableLayer(layerID) == nullptr) { layer = std::make_shared(); tile_pos.first->second.second->addLayer(layerID, layer); @@ -337,6 +329,15 @@ std::unordered_set AnnotationManager::removeAnnotations(co return affectedTiles; } +const std::unique_ptr& AnnotationManager::getAnnotationWithID(uint32_t annotationID) const { + std::lock_guard lock(mtx); + + auto anno_it = annotations.find(annotationID); + assert(anno_it != annotations.end()); + + return anno_it->second; +} + AnnotationIDs AnnotationManager::getAnnotationsInBounds(const LatLngBounds& queryBounds, const MapData& data) const { std::lock_guard lock(mtx); diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp index 723db0a4426..66711eb23f6 100644 --- a/src/mbgl/map/annotation.hpp +++ b/src/mbgl/map/annotation.hpp @@ -35,6 +35,25 @@ enum class AnnotationType : uint8_t { Shape }; +class Annotation : private util::noncopyable { + friend class AnnotationManager; +public: + Annotation(AnnotationType, const AnnotationSegments&, const StyleProperties&); + +public: + const StyleProperties styleProperties; + +private: + LatLng getPoint() const; + LatLngBounds getBounds() const { return bounds; } + +private: + const AnnotationType type = AnnotationType::Point; + const AnnotationSegments geometry; + std::unordered_map, TileID::Hash> tileFeatures; + const LatLngBounds bounds; +}; + class AnnotationManager : private util::noncopyable { public: AnnotationManager(); @@ -51,6 +70,8 @@ class AnnotationManager : private util::noncopyable { const AnnotationsProperties&, const MapData&); std::unordered_set removeAnnotations(const AnnotationIDs&, const MapData&); + AnnotationIDs getOrderedShapeAnnotations() const { return orderedShapeAnnotations; } + const std::unique_ptr& getAnnotationWithID(uint32_t) const; AnnotationIDs getAnnotationsInBounds(const LatLngBounds&, const MapData&) const; LatLngBounds getBoundsForAnnotations(const AnnotationIDs&) const; @@ -81,6 +102,7 @@ class AnnotationManager : private util::noncopyable { mutable std::mutex mtx; std::string defaultPointAnnotationSymbol; std::unordered_map> annotations; + std::vector orderedShapeAnnotations; std::unordered_map, std::unique_ptr>, TileID::Hash> tiles; uint32_t nextID_ = 0; }; diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp index 57115caa253..6e2b55b9843 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include @@ -19,6 +20,8 @@ #include #include +#include +#include #include #include @@ -148,12 +151,97 @@ void MapContext::updateTiles() { void MapContext::updateAnnotationTiles(const std::unordered_set& ids) { assert(Environment::currentlyOn(ThreadType::Map)); if (!style) return; + + // grab existing, single shape annotations source + const auto& shapeID = AnnotationManager::ShapeLayerID; + + const auto source_it = std::find_if(style->sources.begin(), style->sources.end(), + [&shapeID](util::ptr source) { + return (source->info.source_id == shapeID); + }); + assert(source_it != style->sources.end()); + source_it->get()->enabled = true; + + // create (if necessary) layers and buckets for each shape + for (const auto &shapeAnnotationID : data.annotationManager.getOrderedShapeAnnotations()) { + const std::string shapeLayerID = shapeID + "." + std::to_string(shapeAnnotationID); + + const auto layer_it = std::find_if(style->layers.begin(), style->layers.end(), + [&shapeLayerID](util::ptr layer) { + return (layer->id == shapeLayerID); + }); + + if (layer_it == style->layers.end()) { + // query shape styling + auto& annotation = data.annotationManager.getAnnotationWithID(shapeAnnotationID); + auto& shapeStyle = annotation->styleProperties; + + // apply shape style properties + ClassProperties classProperties; + + if (shapeStyle.is()) { + // opacity + PropertyValue lineOpacity = ConstantFunction(shapeStyle.get().opacity); + classProperties.set(PropertyKey::LineOpacity, lineOpacity); + + // line width + PropertyValue lineWidth = ConstantFunction(shapeStyle.get().width); + classProperties.set(PropertyKey::LineWidth, lineWidth); + + // stroke color + PropertyValue strokeColor = ConstantFunction(shapeStyle.get().color); + classProperties.set(PropertyKey::LineColor, strokeColor); + } else if (shapeStyle.is()) { + // opacity + PropertyValue fillOpacity = ConstantFunction(shapeStyle.get().opacity); + classProperties.set(PropertyKey::FillOpacity, fillOpacity); + + // fill color + PropertyValue fillColor = ConstantFunction(shapeStyle.get().fill_color); + classProperties.set(PropertyKey::FillColor, fillColor); + + // stroke color + PropertyValue strokeColor = ConstantFunction(shapeStyle.get().stroke_color); + classProperties.set(PropertyKey::FillOutlineColor, strokeColor); + } + + std::map shapePaints; + shapePaints.emplace(ClassID::Default, std::move(classProperties)); + + // create shape layer + util::ptr shapeLayer = std::make_shared(shapeLayerID, std::move(shapePaints)); + shapeLayer->type = (shapeStyle.is() ? StyleLayerType::Line : StyleLayerType::Fill); + style->layers.emplace_back(shapeLayer); + + // create shape bucket & connect to source + util::ptr shapeBucket = std::make_shared(shapeLayer->type); + shapeBucket->name = shapeLayer->id; + shapeBucket->source_layer = shapeLayer->id; + shapeBucket->source = *source_it; + + // connect layer to bucket + shapeLayer->bucket = shapeBucket; + } + } + + // invalidate annotations layer tiles for (const auto &source : style->sources) { if (source->info.type == SourceType::Annotations) { source->invalidateTiles(ids); } } - triggerUpdate(); + + cascadeClasses(); + + triggerUpdate(Update::Classes); +} + +void MapContext::cascadeClasses() { + style->cascade(data.getClasses()); +} + +void MapContext::recalculateClasses(TimePoint now) { + style->recalculate(transformState.getNormalizedZoom(), now); } void MapContext::update() { @@ -171,12 +259,12 @@ void MapContext::update() { } if (updated & static_cast(Update::Classes)) { - style->cascade(data.getClasses()); + cascadeClasses(); } if (updated & static_cast(Update::Classes) || updated & static_cast(Update::Zoom)) { - style->recalculate(transformState.getNormalizedZoom(), now); + recalculateClasses(now); } updateTiles(); diff --git a/src/mbgl/map/map_context.hpp b/src/mbgl/map/map_context.hpp index 4adcf65aab2..6ad3d007227 100644 --- a/src/mbgl/map/map_context.hpp +++ b/src/mbgl/map/map_context.hpp @@ -65,6 +65,10 @@ class MapContext : public ResourceLoader::Observer { private: void updateTiles(); + // Style-related updates. + void cascadeClasses(); + void recalculateClasses(TimePoint); + // Update the state indicated by the accumulated Update flags, then render. void update(); diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp index 89ecb707add..5a8b6e42851 100644 --- a/src/mbgl/map/source.hpp +++ b/src/mbgl/map/source.hpp @@ -47,6 +47,7 @@ class SourceInfo : private util::noncopyable { std::string attribution; std::array center = {{0, 0, 0}}; std::array bounds = {{-180, -90, 180, 90}}; + std::string source_id = ""; void parseTileJSONProperties(const rapidjson::Value&); std::string tileURL(const TileID& id, float pixelRatio) const; diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index 3c4e0bae807..b83ca5b52bc 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -37,58 +37,30 @@ void StyleParser::parse(JSVal document) { if (document.HasMember("layers")) { parseLayers(document["layers"]); - // create single shape annotations layer (for now) - // + // create shape annotations source const std::string& shapeID = AnnotationManager::ShapeLayerID; - std::map shapePaints; - rapidjson::Document d1; - rapidjson::Value fillOpacity(rapidjson::kObjectType); - fillOpacity.AddMember("fill-opacity", 0.5, d1.GetAllocator()); - parsePaint(fillOpacity, shapePaints[ClassID::Default]); - rapidjson::Value fillColor(rapidjson::kObjectType); - fillColor.AddMember("fill-color", "#fff", d1.GetAllocator()); - parsePaint(fillColor, shapePaints[ClassID::Default]); - rapidjson::Value fillOutlineColor(rapidjson::kObjectType); - fillOutlineColor.AddMember("fill-outline-color", "#f00", d1.GetAllocator()); - parsePaint(fillOutlineColor, shapePaints[ClassID::Default]); -// rapidjson::Value fillWidth(rapidjson::kObjectType); -// fillWidth.AddMember("line-width", 1, d1.GetAllocator()); -// parsePaint(fillWidth, shapePaints[ClassID::Default]); - util::ptr shapeAnnotations = std::make_shared(shapeID, std::move(shapePaints)); - shapeAnnotations->type = StyleLayerType::Fill; - layersMap.emplace(shapeID, std::pair> { JSVal(shapeID), shapeAnnotations }); - layers.emplace_back(shapeAnnotations); - - util::ptr fillBucket = std::make_shared(shapeAnnotations->type); - fillBucket->name = shapeAnnotations->id; - fillBucket->source_layer = shapeAnnotations->id; - - // parse layout? - - util::ptr source1 = std::make_shared(); - sourcesMap.emplace(shapeID, source1); - sources.emplace_back(source1); - source1->info.type = SourceType::Annotations; - fillBucket->source = source1; - shapeAnnotations->bucket = fillBucket; - // - // end shape annotations + util::ptr shapeAnnotationsSource = std::make_shared(); + sourcesMap.emplace(shapeID, shapeAnnotationsSource); + sources.emplace_back(shapeAnnotationsSource); + shapeAnnotationsSource->info.type = SourceType::Annotations; + shapeAnnotationsSource->info.source_id = shapeID; // create point annotations layer - // const std::string& pointID = AnnotationManager::PointLayerID; - std::map paints; - util::ptr annotations = std::make_shared(pointID, std::move(paints)); - annotations->type = StyleLayerType::Symbol; - layersMap.emplace(pointID, std::pair> { JSVal(pointID), annotations }); - layers.emplace_back(annotations); + std::map pointPaints; + util::ptr pointAnnotationsLayer = std::make_shared(pointID, std::move(pointPaints)); + pointAnnotationsLayer->type = StyleLayerType::Symbol; + layersMap.emplace(pointID, std::pair> { JSVal(pointID), pointAnnotationsLayer }); + layers.emplace_back(pointAnnotationsLayer); - util::ptr pointBucket = std::make_shared(annotations->type); - pointBucket->name = annotations->id; - pointBucket->source_layer = annotations->id; + // create point annotations symbol bucket + util::ptr pointBucket = std::make_shared(pointAnnotationsLayer->type); + pointBucket->name = pointAnnotationsLayer->id; + pointBucket->source_layer = pointAnnotationsLayer->id; + // build up point annotations style rapidjson::Document d; rapidjson::Value iconImage(rapidjson::kObjectType); iconImage.AddMember("icon-image", "{sprite}", d.GetAllocator()); @@ -97,14 +69,14 @@ void StyleParser::parse(JSVal document) { iconOverlap.AddMember("icon-allow-overlap", true, d.GetAllocator()); parseLayout(iconOverlap, pointBucket); - util::ptr source = std::make_shared(); - sourcesMap.emplace(pointID, source); - sources.emplace_back(source); - source->info.type = SourceType::Annotations; - pointBucket->source = source; - annotations->bucket = pointBucket; - // - // end point annotations + // create point annotations source & connect to bucket & layer + util::ptr pointAnnotationsSource = std::make_shared(); + sourcesMap.emplace(pointID, pointAnnotationsSource); + sources.emplace_back(pointAnnotationsSource); + pointAnnotationsSource->info.type = SourceType::Annotations; + pointAnnotationsSource->info.source_id = pointID; + pointBucket->source = pointAnnotationsSource; + pointAnnotationsLayer->bucket = pointBucket; } if (document.HasMember("sprite")) { From 9722c92db67dd490aea7972b5d2466877cea4f57 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Wed, 27 May 2015 15:11:54 -0700 Subject: [PATCH 11/85] sample point & shape annotation adds at startup --- macosx/main.mm | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/macosx/main.mm b/macosx/main.mm index e5a711753fb..8b44a30089c 100644 --- a/macosx/main.mm +++ b/macosx/main.mm @@ -150,6 +150,27 @@ int main() { map.setStyleURL(newStyle.first); view.setWindowTitle(newStyle.second); + map.addPointAnnotation(mbgl::LatLng(45, -120), "default_marker"); + + __block mbgl::Map& map_ = map; + + dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ + mbgl::FillProperties fillProperties; + fillProperties.fill_color = mbgl::Color({{ 1, 0, 0, 1 }}); + fillProperties.stroke_color = mbgl::Color({{ 0, 0, 0, 1 }}); + fillProperties.opacity = 0.25; + + mbgl::StyleProperties shapeProperties; + shapeProperties.set(fillProperties); + + map_.addShapeAnnotation({ + mbgl::LatLng(44, -122), + mbgl::LatLng(46, -122), + mbgl::LatLng(46, -121), + mbgl::LatLng(44, -122) + }, shapeProperties); + }); + view.run(); [reachability stopNotifier]; From e5db77b2045d9e61aa2559f88a5eb5e34905d7fd Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Wed, 27 May 2015 16:28:07 -0700 Subject: [PATCH 12/85] add missing include for find_if --- src/mbgl/map/map_context.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp index 6e2b55b9843..a4c69dd0571 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -30,6 +30,8 @@ #include #include +#include + namespace mbgl { MapContext::MapContext(uv_loop_t* loop, View& view_, FileSource& fileSource, MapData& data_) From 94f7e2fe1d61c28a407a541cb4af80b32412183d Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Wed, 27 May 2015 16:48:34 -0700 Subject: [PATCH 13/85] track stale annotation tile pre-style load --- macosx/main.mm | 32 ++++++++++++++------------------ src/mbgl/map/annotation.cpp | 10 ++++++++++ src/mbgl/map/annotation.hpp | 4 ++++ src/mbgl/map/map_context.cpp | 10 ++++++++++ 4 files changed, 38 insertions(+), 18 deletions(-) diff --git a/macosx/main.mm b/macosx/main.mm index 8b44a30089c..52e02debe9c 100644 --- a/macosx/main.mm +++ b/macosx/main.mm @@ -152,24 +152,20 @@ int main() { map.addPointAnnotation(mbgl::LatLng(45, -120), "default_marker"); - __block mbgl::Map& map_ = map; - - dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ - mbgl::FillProperties fillProperties; - fillProperties.fill_color = mbgl::Color({{ 1, 0, 0, 1 }}); - fillProperties.stroke_color = mbgl::Color({{ 0, 0, 0, 1 }}); - fillProperties.opacity = 0.25; - - mbgl::StyleProperties shapeProperties; - shapeProperties.set(fillProperties); - - map_.addShapeAnnotation({ - mbgl::LatLng(44, -122), - mbgl::LatLng(46, -122), - mbgl::LatLng(46, -121), - mbgl::LatLng(44, -122) - }, shapeProperties); - }); + mbgl::FillProperties fillProperties; + fillProperties.fill_color = mbgl::Color({{ 1, 0, 0, 1 }}); + fillProperties.stroke_color = mbgl::Color({{ 0, 0, 0, 1 }}); + fillProperties.opacity = 0.25; + + mbgl::StyleProperties shapeProperties; + shapeProperties.set(fillProperties); + + map.addShapeAnnotation({ + mbgl::LatLng(44, -122), + mbgl::LatLng(46, -122), + mbgl::LatLng(46, -121), + mbgl::LatLng(44, -122) + }, shapeProperties); view.run(); diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index a47d2823e7c..53031efb7a9 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -47,6 +47,16 @@ AnnotationManager::~AnnotationManager() { // Annotation so we can't destruct the object with just the header file. } +void AnnotationManager::markStaleTiles(std::unordered_set ids) { + std::lock_guard lock(mtx); + std::copy(ids.begin(), ids.end(), std::inserter(staleTiles, staleTiles.begin())); +} + +std::unordered_set AnnotationManager::resetStaleTiles() { + std::lock_guard lock(mtx); + return std::move(staleTiles); +} + void AnnotationManager::setDefaultPointAnnotationSymbol(const std::string& symbol) { std::lock_guard lock(mtx); defaultPointAnnotationSymbol = symbol; diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp index 66711eb23f6..5df384cecaa 100644 --- a/src/mbgl/map/annotation.hpp +++ b/src/mbgl/map/annotation.hpp @@ -59,6 +59,9 @@ class AnnotationManager : private util::noncopyable { AnnotationManager(); ~AnnotationManager(); + void markStaleTiles(std::unordered_set); + std::unordered_set resetStaleTiles(); + void setDefaultPointAnnotationSymbol(const std::string& symbol); std::pair, AnnotationIDs> addPointAnnotations( const AnnotationSegment&, @@ -104,6 +107,7 @@ class AnnotationManager : private util::noncopyable { std::unordered_map> annotations; std::vector orderedShapeAnnotations; std::unordered_map, std::unique_ptr>, TileID::Hash> tiles; + std::unordered_set staleTiles; uint32_t nextID_ = 0; }; diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp index a4c69dd0571..7fb2b9e4b07 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -142,6 +142,11 @@ void MapContext::loadStyleJSON(const std::string& json, const std::string& base) resourceLoader->setGlyphStore(glyphStore.get()); triggerUpdate(Update::Zoom); + + auto staleTiles = data.annotationManager.resetStaleTiles(); + if (staleTiles.size()) { + updateAnnotationTiles(staleTiles); + } } void MapContext::updateTiles() { @@ -152,6 +157,9 @@ void MapContext::updateTiles() { void MapContext::updateAnnotationTiles(const std::unordered_set& ids) { assert(Environment::currentlyOn(ThreadType::Map)); + + data.annotationManager.markStaleTiles(ids); + if (!style) return; // grab existing, single shape annotations source @@ -236,6 +244,8 @@ void MapContext::updateAnnotationTiles(const std::unordered_set Date: Thu, 28 May 2015 14:49:20 -0700 Subject: [PATCH 14/85] apply round line join for annotations --- src/mbgl/map/map_context.cpp | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp index 7fb2b9e4b07..6be4e7e45a7 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -186,37 +186,37 @@ void MapContext::updateAnnotationTiles(const std::unordered_setstyleProperties; - // apply shape style properties - ClassProperties classProperties; + // apply shape paint properties + ClassProperties paintProperties; if (shapeStyle.is()) { // opacity PropertyValue lineOpacity = ConstantFunction(shapeStyle.get().opacity); - classProperties.set(PropertyKey::LineOpacity, lineOpacity); + paintProperties.set(PropertyKey::LineOpacity, lineOpacity); // line width PropertyValue lineWidth = ConstantFunction(shapeStyle.get().width); - classProperties.set(PropertyKey::LineWidth, lineWidth); + paintProperties.set(PropertyKey::LineWidth, lineWidth); // stroke color PropertyValue strokeColor = ConstantFunction(shapeStyle.get().color); - classProperties.set(PropertyKey::LineColor, strokeColor); + paintProperties.set(PropertyKey::LineColor, strokeColor); } else if (shapeStyle.is()) { // opacity PropertyValue fillOpacity = ConstantFunction(shapeStyle.get().opacity); - classProperties.set(PropertyKey::FillOpacity, fillOpacity); + paintProperties.set(PropertyKey::FillOpacity, fillOpacity); // fill color PropertyValue fillColor = ConstantFunction(shapeStyle.get().fill_color); - classProperties.set(PropertyKey::FillColor, fillColor); + paintProperties.set(PropertyKey::FillColor, fillColor); // stroke color PropertyValue strokeColor = ConstantFunction(shapeStyle.get().stroke_color); - classProperties.set(PropertyKey::FillOutlineColor, strokeColor); + paintProperties.set(PropertyKey::FillOutlineColor, strokeColor); } std::map shapePaints; - shapePaints.emplace(ClassID::Default, std::move(classProperties)); + shapePaints.emplace(ClassID::Default, std::move(paintProperties)); // create shape layer util::ptr shapeLayer = std::make_shared(shapeLayerID, std::move(shapePaints)); @@ -229,6 +229,11 @@ void MapContext::updateAnnotationTiles(const std::unordered_setsource_layer = shapeLayer->id; shapeBucket->source = *source_it; + // apply line layout properties to bucket + if (shapeStyle.is()) { + shapeBucket->layout.set(PropertyKey::LineJoin, ConstantFunction(JoinType::Round)); + } + // connect layer to bucket shapeLayer->bucket = shapeBucket; } From b023d8d217cec3f60aaaa619e02fbb1c8ccac35d Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 28 May 2015 15:07:50 -0700 Subject: [PATCH 15/85] support adding multiple shapes at once --- include/mbgl/map/map.hpp | 18 ++++++++++++------ src/mbgl/map/annotation.cpp | 8 ++++---- src/mbgl/map/annotation.hpp | 8 +++----- src/mbgl/map/map.cpp | 21 ++++++++++++--------- 4 files changed, 31 insertions(+), 24 deletions(-) diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index 858dddb71e8..f989b213a48 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -28,6 +28,10 @@ namespace util { template class Thread; } +using AnnotationIDs = std::vector; +using AnnotationSegment = std::vector; +using AnnotationSegments = std::vector; + class Map : private util::noncopyable { friend class View; @@ -118,14 +122,16 @@ class Map : private util::noncopyable { void setDefaultPointAnnotationSymbol(const std::string&); double getTopOffsetPixelsForAnnotationSymbol(const std::string&); uint32_t addPointAnnotation(const LatLng&, const std::string& symbol); - std::vector addPointAnnotations(const std::vector&, - const std::vector& symbols); - uint32_t addShapeAnnotation(const std::vector&, + AnnotationIDs addPointAnnotations(const AnnotationSegment&, + const std::vector& symbols); + uint32_t addShapeAnnotation(const AnnotationSegments&, const StyleProperties&); + AnnotationIDs addShapeAnnotations(const std::vector&, + const std::vector&); void removeAnnotation(uint32_t); - void removeAnnotations(const std::vector&); - std::vector getAnnotationsInBounds(const LatLngBounds&); - LatLngBounds getBoundsForAnnotations(const std::vector&); + void removeAnnotations(const AnnotationIDs&); + AnnotationIDs getAnnotationsInBounds(const LatLngBounds&); + LatLngBounds getBoundsForAnnotations(const AnnotationIDs&); // Memory void setSourceTileCacheSize(size_t); diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 53031efb7a9..d7ecd2a487a 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -77,7 +77,7 @@ vec2 AnnotationManager::projectPoint(const LatLng& point) { std::pair, AnnotationIDs> AnnotationManager::addAnnotations(const AnnotationType type, const std::vector& segments, - const StyleProperties& styleProperties, + const std::vector& styleProperties, const AnnotationsProperties& annotationsProperties, const MapData& data) { std::lock_guard lock(mtx); @@ -140,7 +140,7 @@ AnnotationManager::addAnnotations(const AnnotationType type, shape, projectedShape, type, - styleProperties, + styleProperties[s], featureProperties, maxZoom ); @@ -272,14 +272,14 @@ AnnotationManager::addPointAnnotations(const AnnotationSegment& points, const MapData& data) { return addAnnotations(AnnotationType::Point, { { points } }, - defaultStyleProperties(), + { defaultStyleProperties() }, annotationsProperties, data); } std::pair, AnnotationIDs> AnnotationManager::addShapeAnnotations(const std::vector& shapes, - const StyleProperties& styleProperties, + const std::vector& styleProperties, const AnnotationsProperties& annotationsProperties, const MapData& data) { return addAnnotations(AnnotationType::Shape, diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp index 5df384cecaa..b6f7be4f638 100644 --- a/src/mbgl/map/annotation.hpp +++ b/src/mbgl/map/annotation.hpp @@ -1,6 +1,7 @@ #ifndef MBGL_MAP_ANNOTATIONS #define MBGL_MAP_ANNOTATIONS +#include #include #include #include @@ -25,9 +26,6 @@ class LiveTile; class LiveTileFeature; class MapData; -using AnnotationIDs = std::vector; -using AnnotationSegment = std::vector; -using AnnotationSegments = std::vector; using AnnotationsProperties = std::unordered_map>; enum class AnnotationType : uint8_t { @@ -69,7 +67,7 @@ class AnnotationManager : private util::noncopyable { const MapData&); std::pair, AnnotationIDs> addShapeAnnotations( const std::vector&, - const StyleProperties&, + const std::vector&, const AnnotationsProperties&, const MapData&); std::unordered_set removeAnnotations(const AnnotationIDs&, const MapData&); @@ -89,7 +87,7 @@ class AnnotationManager : private util::noncopyable { std::pair, AnnotationIDs> addAnnotations( const AnnotationType, const std::vector&, - const StyleProperties&, + const std::vector&, const AnnotationsProperties&, const MapData&); std::unordered_set addTileFeature( diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index eaddc4f80ab..d9a1d11e80c 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -252,23 +252,26 @@ uint32_t Map::addPointAnnotation(const LatLng& point, const std::string& symbol) return addPointAnnotations({ point }, { symbol }).front(); } -std::vector Map::addPointAnnotations(const std::vector& points, const std::vector& symbols) { +AnnotationIDs Map::addPointAnnotations(const AnnotationSegment& points, + const std::vector& symbols) { AnnotationsProperties properties = { { "symbols", symbols } }; auto result = data->annotationManager.addPointAnnotations(points, properties, *data); context->invoke(&MapContext::updateAnnotationTiles, result.first); return result.second; } -uint32_t Map::addShapeAnnotation(const std::vector& shape, +// todo don't penalize adding point and shape anno's at the same time + +uint32_t Map::addShapeAnnotation(const AnnotationSegments& shape, const StyleProperties& styleProperties) { - auto result = data->annotationManager.addShapeAnnotations( - {{ shape }}, - styleProperties, - {{}}, - *data - ); + return addShapeAnnotations({ shape }, { styleProperties }).front(); +} + +AnnotationIDs Map::addShapeAnnotations(const std::vector& shapes, + const std::vector& styleProperties) { + auto result = data->annotationManager.addShapeAnnotations(shapes, styleProperties, {{}}, *data); context->invoke(&MapContext::updateAnnotationTiles, result.first); - return result.second[0]; + return result.second; } void Map::removeAnnotation(uint32_t annotation) { From 12976568c686394aab56b68d4c82c3ae8db9cf15 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 28 May 2015 15:34:29 -0700 Subject: [PATCH 16/85] fix annotation removal --- src/mbgl/map/annotation.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index d7ecd2a487a..3a577bc4e1d 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -290,7 +290,7 @@ AnnotationManager::addShapeAnnotations(const std::vector& sh } std::unordered_set AnnotationManager::removeAnnotations(const AnnotationIDs& ids, - const MapData& data) { + const MapData& data) { std::lock_guard lock(mtx); std::unordered_set affectedTiles; @@ -325,8 +325,12 @@ std::unordered_set AnnotationManager::removeAnnotations(co // remove annotation's features from tile const auto& features_it = annotation->tileFeatures.find(tid); if (features_it != annotation->tileFeatures.end()) { - const auto& layer = - tiles[tid].second->getMutableLayer(PointLayerID); // + util::ptr layer; + if (annotation->type == AnnotationType::Point) { + layer = tiles[tid].second->getMutableLayer(PointLayerID); + } else { + layer = tiles[tid].second->getMutableLayer(ShapeLayerID + "." + std::to_string(annotationID)); + } layer->removeFeature(features_it->second); affectedTiles.insert(tid); } From b63b436ac95aa0a382ae9ecc5cc3dcef1b77e63f Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 28 May 2015 16:21:10 -0700 Subject: [PATCH 17/85] proper deletion cleanup --- src/mbgl/map/annotation.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 3a577bc4e1d..6181697d9e2 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -335,6 +335,12 @@ std::unordered_set AnnotationManager::removeAnnotations(co affectedTiles.insert(tid); } } + + if (annotation->type == AnnotationType::Shape) { + auto shape_it = std::find(orderedShapeAnnotations.begin(), orderedShapeAnnotations.end(), annotationID); + orderedShapeAnnotations.erase(shape_it); + } + annotations.erase(annotationID); } } From 15ea397183afaadd19d32dbcd6476965b1585845 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 29 May 2015 08:55:47 -0700 Subject: [PATCH 18/85] partial fix to tile-spanning features --- src/mbgl/map/annotation.cpp | 94 +++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 41 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 6181697d9e2..b2c6ac8f31b 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -183,7 +183,7 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, for (int8_t z = maxZoom; z >= 0; z--) { - GeometryCollection geometries; + std::unordered_map featureTiles; if (type == AnnotationType::Point) { auto& pp = projectedFeature[0][0]; @@ -193,7 +193,9 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, const Coordinate coordinate(extent * (pp.x * z2 - x), extent * (pp.y * z2 - y)); - geometries = {{ {{ coordinate }} }}; + GeometryCollection geometries = {{ {{ coordinate }} }}; + + featureTiles.emplace(TileID(z, x, y), geometries); } else { for (size_t l = 0; l < projectedFeature.size(); ++l) { @@ -208,55 +210,65 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, const Coordinate coordinate(extent * (pp.x * z2 - x), extent * (pp.y * z2 - y)); - line.push_back(coordinate); - } + auto tile_it = featureTiles.find(TileID(z, x, y)); - geometries.push_back(line); + if (tile_it != featureTiles.end()) { + GeometryCollection& geometries = featureTiles.find(TileID(z, x, y))->second; + if (geometries.size()) { + geometries.back().push_back(coordinate); + } else { + geometries.push_back({{ coordinate }}); + } + } else { + GeometryCollection geometries = {{ {{ coordinate }} }}; + featureTiles.emplace(TileID(z, x, y), geometries); + } + } } } - auto tileID = TileID(z, x, y); + for (auto& featureTile : featureTiles) { + // create tile feature + auto feature = std::make_shared( + (type == AnnotationType::Point ? FeatureType::Point : FeatureType::LineString), + featureTile.second, + featureProperties + ); + + // check for tile & create if necessary + auto tile_pos = tiles.emplace(featureTile.first, + std::make_pair(std::unordered_set({ annotationID }), + util::make_unique())); + + // check for annotation layer & create if necessary + util::ptr layer; + std::string layerID = ""; + if (type == AnnotationType::Point) { + layerID = PointLayerID; + } else { + layerID = ShapeLayerID + "." + std::to_string(annotationID); + } + if (tile_pos.second || tile_pos.first->second.second->getMutableLayer(layerID) == nullptr) { + layer = std::make_shared(); + tile_pos.first->second.second->addLayer(layerID, layer); + } else { + layer = tile_pos.first->second.second->getMutableLayer(layerID); - // create tile feature - auto feature = std::make_shared( - (type == AnnotationType::Point ? FeatureType::Point : FeatureType::LineString), - geometries, - featureProperties - ); + // associate annotation with tile + tile_pos.first->second.first.insert(annotationID); + } - // check for tile & create if necessary - auto tile_pos = tiles.emplace(tileID, - std::make_pair(std::unordered_set({ annotationID }), - util::make_unique())); + // add feature to layer + layer->addFeature(feature); - // check for annotation layer & create if necessary - util::ptr layer; - std::string layerID = ""; - if (type == AnnotationType::Point) { - layerID = PointLayerID; - } else { - layerID = ShapeLayerID + "." + std::to_string(annotationID); - } - if (tile_pos.second || tile_pos.first->second.second->getMutableLayer(layerID) == nullptr) { - layer = std::make_shared(); - tile_pos.first->second.second->addLayer(layerID, layer); - } else { - layer = tile_pos.first->second.second->getMutableLayer(layerID); + // Record annotation association with tile and tile feature. This is used to determine stale tiles, + // as well as to remove the feature from the tile upon annotation deletion. + anno_it.first->second->tileFeatures.emplace(featureTile.first, std::weak_ptr(feature)); - // associate annotation with tile - tile_pos.first->second.first.insert(annotationID); + // track affected tile + affectedTiles.insert(featureTile.first); } - // add feature to layer - layer->addFeature(feature); - - // Record annotation association with tile and tile feature. This is used to determine stale tiles, - // as well as to remove the feature from the tile upon annotation deletion. - anno_it.first->second->tileFeatures.emplace(tileID, std::weak_ptr(feature)); - - // track affected tile - affectedTiles.insert(tileID); - // get ready for the next-lower zoom number z2 /= 2; x /= 2; From 53b558b17f3bdb979324a1cbcacd7a3e7f43d19d Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 29 May 2015 17:20:04 -0700 Subject: [PATCH 19/85] roughing out basic Cocoa types --- gyp/platform-ios.gypi | 9 +++++++++ include/mbgl/ios/MGLMultiPoint.h | 10 ++++++++++ include/mbgl/ios/MGLOverlay.h | 14 ++++++++++++++ include/mbgl/ios/MGLPolygon.h | 15 +++++++++++++++ include/mbgl/ios/MGLPolyline.h | 11 +++++++++++ include/mbgl/ios/MGLShape.h | 10 ++++++++++ include/mbgl/ios/MGLTypes.h | 9 +++++++-- platform/ios/MGLMultiPoint.m | 18 ++++++++++++++++++ platform/ios/MGLPolygon.m | 32 ++++++++++++++++++++++++++++++++ platform/ios/MGLPolyline.m | 32 ++++++++++++++++++++++++++++++++ platform/ios/MGLShape.m | 14 ++++++++++++++ 11 files changed, 172 insertions(+), 2 deletions(-) create mode 100644 include/mbgl/ios/MGLMultiPoint.h create mode 100644 include/mbgl/ios/MGLOverlay.h create mode 100644 include/mbgl/ios/MGLPolygon.h create mode 100644 include/mbgl/ios/MGLPolyline.h create mode 100644 include/mbgl/ios/MGLShape.h create mode 100644 platform/ios/MGLMultiPoint.m create mode 100644 platform/ios/MGLPolygon.m create mode 100644 platform/ios/MGLPolyline.m create mode 100644 platform/ios/MGLShape.m diff --git a/gyp/platform-ios.gypi b/gyp/platform-ios.gypi index 0034831a918..b118674f909 100644 --- a/gyp/platform-ios.gypi +++ b/gyp/platform-ios.gypi @@ -35,6 +35,15 @@ '../platform/ios/MGLUserLocationAnnotationView.m', '../include/mbgl/ios/MGLTypes.h', '../platform/ios/MGLTypes.m', + '../include/mbgl/ios/MGLMultiPoint.h', + '../platform/ios/MGLMultiPoint.m', + '../include/mbgl/ios/MGLOverlay.h', + '../include/mbgl/ios/MGLPolyline.h', + '../platform/ios/MGLPolyline.m', + '../include/mbgl/ios/MGLPolygon.h', + '../platform/ios/MGLPolygon.m', + '../include/mbgl/ios/MGLShape.h', + '../platform/ios/MGLShape.m', '../platform/ios/NSBundle+MGLAdditions.h', '../platform/ios/NSBundle+MGLAdditions.m', '../platform/ios/NSException+MGLAdditions.h', diff --git a/include/mbgl/ios/MGLMultiPoint.h b/include/mbgl/ios/MGLMultiPoint.h new file mode 100644 index 00000000000..d0ec9f5a06a --- /dev/null +++ b/include/mbgl/ios/MGLMultiPoint.h @@ -0,0 +1,10 @@ +#import +#import + +#import "MGLShape.h" + +@interface MGLMultiPoint: MGLShape + +- (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range; + +@end diff --git a/include/mbgl/ios/MGLOverlay.h b/include/mbgl/ios/MGLOverlay.h new file mode 100644 index 00000000000..9bbae9d039d --- /dev/null +++ b/include/mbgl/ios/MGLOverlay.h @@ -0,0 +1,14 @@ +#import +#import + +#import "MGLAnnotation.h" +#import "MGLTypes.h" + +@protocol MGLOverlay + +@property (nonatomic, readonly) CLLocationCoordinate2D coordinate; +@property (nonatomic, readonly) MGLMapBounds overlayBounds; + +- (BOOL)intersectsOverlayBounds:(MGLMapBounds)overlayBounds; + +@end diff --git a/include/mbgl/ios/MGLPolygon.h b/include/mbgl/ios/MGLPolygon.h new file mode 100644 index 00000000000..7fd0bafe759 --- /dev/null +++ b/include/mbgl/ios/MGLPolygon.h @@ -0,0 +1,15 @@ +#import +#import + +#import "MGLMultiPoint.h" + +@interface MGLPolygon: MGLMultiPoint + ++ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords + count:(NSUInteger)count; + +//+ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords +// count:(NSUInteger)count +// interiorPolygons:(NSArray *)interiorPolygons; + +@end diff --git a/include/mbgl/ios/MGLPolyline.h b/include/mbgl/ios/MGLPolyline.h new file mode 100644 index 00000000000..5c42120be76 --- /dev/null +++ b/include/mbgl/ios/MGLPolyline.h @@ -0,0 +1,11 @@ +#import +#import + +#import "MGLMultiPoint.h" + +@interface MGLPolyline: MGLMultiPoint + ++ (instancetype)polylineWithCoordinates:(CLLocationCoordinate2D *)coords + count:(NSUInteger)count; + +@end diff --git a/include/mbgl/ios/MGLShape.h b/include/mbgl/ios/MGLShape.h new file mode 100644 index 00000000000..4b171cbc380 --- /dev/null +++ b/include/mbgl/ios/MGLShape.h @@ -0,0 +1,10 @@ +#import + +#import "MGLAnnotation.h" + +@interface MGLShape : NSObject + +@property (nonatomic, copy) NSString *title; +@property (nonatomic, copy) NSString *subtitle; + +@end diff --git a/include/mbgl/ios/MGLTypes.h b/include/mbgl/ios/MGLTypes.h index d4bc6c2ce2e..75d23f414f9 100644 --- a/include/mbgl/ios/MGLTypes.h +++ b/include/mbgl/ios/MGLTypes.h @@ -1,10 +1,10 @@ #import +#import extern NSString * const MGLErrorDomain; /** The mode used to track the user location on the map. */ -typedef NS_ENUM(NSUInteger, MGLUserTrackingMode) -{ +typedef NS_ENUM(NSUInteger, MGLUserTrackingMode) { /** The map does not follow the user location. */ MGLUserTrackingModeNone = 0, /** The map follows the user location. */ @@ -12,3 +12,8 @@ typedef NS_ENUM(NSUInteger, MGLUserTrackingMode) /** The map follows the user location and rotates when the heading changes. */ MGLUserTrackingModeFollowWithHeading }; + +typedef struct { + CLLocationCoordinate2D sw; + CLLocationCoordinate2D ne; +} MGLMapBounds; diff --git a/platform/ios/MGLMultiPoint.m b/platform/ios/MGLMultiPoint.m new file mode 100644 index 00000000000..986052f1620 --- /dev/null +++ b/platform/ios/MGLMultiPoint.m @@ -0,0 +1,18 @@ +#import "MGLMultiPoint.h" + +@implementation MGLMultiPoint +{ + CLLocationCoordinate2D *_coords; +} + +- (CLLocationCoordinate2D)coordinate +{ + return CLLocationCoordinate2DMake(_coords[0].latitude, _coords[0].longitude); +} + +- (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range +{ + coords = _coords; +} + +@end diff --git a/platform/ios/MGLPolygon.m b/platform/ios/MGLPolygon.m new file mode 100644 index 00000000000..4167fc3f6d2 --- /dev/null +++ b/platform/ios/MGLPolygon.m @@ -0,0 +1,32 @@ +#import "MGLPolygon.h" + +@implementation MGLPolygon +{ + CLLocationCoordinate2D *_coords; +} + ++ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords + count:(NSUInteger)count +{ + return [[self alloc] initWithCoordinates:coords count:count]; +} + +- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords + count:(NSUInteger)count +{ + self = [super init]; + + if (self) + { + _coords = coords; + } + + return self; +} + +- (CLLocationCoordinate2D)coordinate +{ + return CLLocationCoordinate2DMake(_coords[0].latitude, _coords[0].longitude); +} + +@end diff --git a/platform/ios/MGLPolyline.m b/platform/ios/MGLPolyline.m new file mode 100644 index 00000000000..442ad64145d --- /dev/null +++ b/platform/ios/MGLPolyline.m @@ -0,0 +1,32 @@ +#import "MGLPolyline.h" + +@implementation MGLPolyline +{ + CLLocationCoordinate2D *_coords; +} + ++ (instancetype)polylineWithCoordinates:(CLLocationCoordinate2D *)coords + count:(NSUInteger)count +{ + return [[self alloc] initWithCoordinates:coords count:count]; +} + +- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords + count:(NSUInteger)count +{ + self = [super init]; + + if (self) + { + _coords = coords; + } + + return self; +} + +- (CLLocationCoordinate2D)coordinate +{ + return CLLocationCoordinate2DMake(_coords[0].latitude, _coords[0].longitude); +} + +@end diff --git a/platform/ios/MGLShape.m b/platform/ios/MGLShape.m new file mode 100644 index 00000000000..e3d92c38c84 --- /dev/null +++ b/platform/ios/MGLShape.m @@ -0,0 +1,14 @@ +#import "MGLShape.h" + +@implementation MGLShape + +- (CLLocationCoordinate2D)coordinate +{ + [[NSException exceptionWithName:@"MGLAbstractClassException" + reason:@"MGLShape is an abstract class" + userInfo:nil] raise]; + + return CLLocationCoordinate2DMake(MAXFLOAT, MAXFLOAT); +} + +@end From 184768108366eafe39543759dabe2af2b33ea9a1 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 29 May 2015 17:30:58 -0700 Subject: [PATCH 20/85] add shape styling delegate callbacks --- include/mbgl/ios/MGLMapView.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/mbgl/ios/MGLMapView.h b/include/mbgl/ios/MGLMapView.h index 9d7b5c9019b..8b4c827307e 100644 --- a/include/mbgl/ios/MGLMapView.h +++ b/include/mbgl/ios/MGLMapView.h @@ -4,6 +4,9 @@ #import @class MGLUserLocation; +@class MGLPolyline; +@class MGLPolygon; +@class MGLShape; @protocol MGLMapViewDelegate; @protocol MGLAnnotation; @@ -306,6 +309,12 @@ IB_DESIGNABLE * @return The marker symbol to display for the specified annotation or `nil` if you want to display the default symbol. */ - (NSString *)mapView:(MGLMapView *)mapView symbolNameForAnnotation:(id )annotation; +- (CGFloat)alphaForShapeAnnotation:(MGLShape *)annotation; +- (UIColor *)strokeColorForPolylineAnnotation:(MGLPolyline *)annotation; +- (CGFloat)lineWidthForPolylineAnnotation:(MGLPolyline *)annotation; +- (UIColor *)strokeColorForPolygonAnnotation:(MGLPolygon *)annotation; +- (UIColor *)fillColorForPolygonAnnotation:(MGLPolygon *)annotation; + /** Returns a Boolean value indicating whether the annotation is able to display extra information in a callout bubble. * * If the value returned is `YES`, a standard callout bubble is shown when the user taps a selected annotation. The callout uses the title and subtitle text from the associated annotation object. If there is no title text, though, the annotation will not show a callout. The callout also displays any custom callout views returned by the delegate for the left and right callout accessory views. From 129159bdfea31189b6da5349d334b57b471e3284 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 29 May 2015 17:45:28 -0700 Subject: [PATCH 21/85] tighten up shape delegate methods --- include/mbgl/ios/MGLMapView.h | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/include/mbgl/ios/MGLMapView.h b/include/mbgl/ios/MGLMapView.h index 8b4c827307e..fda4052571e 100644 --- a/include/mbgl/ios/MGLMapView.h +++ b/include/mbgl/ios/MGLMapView.h @@ -309,11 +309,10 @@ IB_DESIGNABLE * @return The marker symbol to display for the specified annotation or `nil` if you want to display the default symbol. */ - (NSString *)mapView:(MGLMapView *)mapView symbolNameForAnnotation:(id )annotation; -- (CGFloat)alphaForShapeAnnotation:(MGLShape *)annotation; -- (UIColor *)strokeColorForPolylineAnnotation:(MGLPolyline *)annotation; -- (CGFloat)lineWidthForPolylineAnnotation:(MGLPolyline *)annotation; -- (UIColor *)strokeColorForPolygonAnnotation:(MGLPolygon *)annotation; -- (UIColor *)fillColorForPolygonAnnotation:(MGLPolygon *)annotation; +- (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation; +- (UIColor *)mapView:(MGLMapView *)mapView strokeColorForShapeAnnotation:(MGLShape *)annotation; +- (UIColor *)mapView:(MGLMapView *)mapView fillColorForPolygonAnnotation:(MGLPolygon *)annotation; +- (CGFloat)mapView:(MGLMapView *)mapView lineWidthForPolylineAnnotation:(MGLPolyline *)annotation; /** Returns a Boolean value indicating whether the annotation is able to display extra information in a callout bubble. * From 20a489bbc7850a5afa55e630aec6fe74a8abf4dd Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 29 May 2015 17:45:45 -0700 Subject: [PATCH 22/85] add shape includes to umbrella header --- include/mbgl/ios/MapboxGL.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/mbgl/ios/MapboxGL.h b/include/mbgl/ios/MapboxGL.h index a6d23d32804..1a6dd7f836e 100644 --- a/include/mbgl/ios/MapboxGL.h +++ b/include/mbgl/ios/MapboxGL.h @@ -1,5 +1,10 @@ #import "MGLAccountManager.h" #import "MGLAnnotation.h" #import "MGLMapView.h" +#import "MGLMultiPoint.h" +#import "MGLOverlay.h" +#import "MGLPolygon.h" +#import "MGLPolyline.h" +#import "MGLShape.h" #import "MGLTypes.h" #import "MGLUserLocation.h" From f7b7afedeec88ea0c8e158f81930029793bab9d8 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Sun, 31 May 2015 13:52:03 -0700 Subject: [PATCH 23/85] add concrete MGLPointAnnotation --- gyp/platform-ios.gypi | 2 ++ include/mbgl/ios/MGLMultiPoint.h | 2 +- include/mbgl/ios/MGLPointAnnotation.h | 10 +++++++++ include/mbgl/ios/MGLPolygon.h | 2 +- include/mbgl/ios/MGLPolyline.h | 2 +- include/mbgl/ios/MapboxGL.h | 1 + ios/app/MBXAnnotation.h | 12 ----------- ios/app/MBXAnnotation.m | 30 --------------------------- ios/app/MBXViewController.mm | 8 +++---- ios/app/mapboxgl-app.gypi | 2 -- platform/ios/MGLPointAnnotation.m | 7 +++++++ 11 files changed, 26 insertions(+), 52 deletions(-) create mode 100644 include/mbgl/ios/MGLPointAnnotation.h delete mode 100644 ios/app/MBXAnnotation.h delete mode 100644 ios/app/MBXAnnotation.m create mode 100644 platform/ios/MGLPointAnnotation.m diff --git a/gyp/platform-ios.gypi b/gyp/platform-ios.gypi index b118674f909..316f40ae002 100644 --- a/gyp/platform-ios.gypi +++ b/gyp/platform-ios.gypi @@ -38,6 +38,8 @@ '../include/mbgl/ios/MGLMultiPoint.h', '../platform/ios/MGLMultiPoint.m', '../include/mbgl/ios/MGLOverlay.h', + '../include/mbgl/ios/MGLPointAnnotation.h', + '../platform/ios/MGLPointAnnotation.m', '../include/mbgl/ios/MGLPolyline.h', '../platform/ios/MGLPolyline.m', '../include/mbgl/ios/MGLPolygon.h', diff --git a/include/mbgl/ios/MGLMultiPoint.h b/include/mbgl/ios/MGLMultiPoint.h index d0ec9f5a06a..2c631035d67 100644 --- a/include/mbgl/ios/MGLMultiPoint.h +++ b/include/mbgl/ios/MGLMultiPoint.h @@ -3,7 +3,7 @@ #import "MGLShape.h" -@interface MGLMultiPoint: MGLShape +@interface MGLMultiPoint : MGLShape - (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range; diff --git a/include/mbgl/ios/MGLPointAnnotation.h b/include/mbgl/ios/MGLPointAnnotation.h new file mode 100644 index 00000000000..78308f35317 --- /dev/null +++ b/include/mbgl/ios/MGLPointAnnotation.h @@ -0,0 +1,10 @@ +#import +#import + +#import "MGLShape.h" + +@interface MGLPointAnnotation : MGLShape + +@property (nonatomic, assign) CLLocationCoordinate2D coordinate; + +@end diff --git a/include/mbgl/ios/MGLPolygon.h b/include/mbgl/ios/MGLPolygon.h index 7fd0bafe759..b61f6359199 100644 --- a/include/mbgl/ios/MGLPolygon.h +++ b/include/mbgl/ios/MGLPolygon.h @@ -3,7 +3,7 @@ #import "MGLMultiPoint.h" -@interface MGLPolygon: MGLMultiPoint +@interface MGLPolygon : MGLMultiPoint + (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count; diff --git a/include/mbgl/ios/MGLPolyline.h b/include/mbgl/ios/MGLPolyline.h index 5c42120be76..df682cbc319 100644 --- a/include/mbgl/ios/MGLPolyline.h +++ b/include/mbgl/ios/MGLPolyline.h @@ -3,7 +3,7 @@ #import "MGLMultiPoint.h" -@interface MGLPolyline: MGLMultiPoint +@interface MGLPolyline : MGLMultiPoint + (instancetype)polylineWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count; diff --git a/include/mbgl/ios/MapboxGL.h b/include/mbgl/ios/MapboxGL.h index 1a6dd7f836e..d3b032b9c96 100644 --- a/include/mbgl/ios/MapboxGL.h +++ b/include/mbgl/ios/MapboxGL.h @@ -3,6 +3,7 @@ #import "MGLMapView.h" #import "MGLMultiPoint.h" #import "MGLOverlay.h" +#import "MGLPointAnnotation.h" #import "MGLPolygon.h" #import "MGLPolyline.h" #import "MGLShape.h" diff --git a/ios/app/MBXAnnotation.h b/ios/app/MBXAnnotation.h deleted file mode 100644 index 63c5f9425f0..00000000000 --- a/ios/app/MBXAnnotation.h +++ /dev/null @@ -1,12 +0,0 @@ -#import -#import - -#import - -@interface MBXAnnotation : NSObject - -+ (instancetype)annotationWithLocation:(CLLocationCoordinate2D)coordinate title:(NSString *)title subtitle:(NSString *)subtitle; - -- (instancetype)initWithLocation:(CLLocationCoordinate2D)coordinate title:(NSString *)title subtitle:(NSString *)subtitle; - -@end diff --git a/ios/app/MBXAnnotation.m b/ios/app/MBXAnnotation.m deleted file mode 100644 index f942a8572dd..00000000000 --- a/ios/app/MBXAnnotation.m +++ /dev/null @@ -1,30 +0,0 @@ -#import "MBXAnnotation.h" - -@interface MBXAnnotation () - -@property (nonatomic) CLLocationCoordinate2D coordinate; -@property (nonatomic) NSString *title; -@property (nonatomic) NSString *subtitle; - -@end - -@implementation MBXAnnotation - -+ (instancetype)annotationWithLocation:(CLLocationCoordinate2D)coordinate title:(NSString *)title subtitle:(NSString *)subtitle -{ - return [[self alloc] initWithLocation:coordinate title:title subtitle:subtitle]; -} - -- (instancetype)initWithLocation:(CLLocationCoordinate2D)coordinate title:(NSString *)title subtitle:(NSString *)subtitle -{ - if (self = [super init]) - { - _coordinate = coordinate; - _title = title; - _subtitle = subtitle; - } - - return self; -} - -@end diff --git a/ios/app/MBXViewController.mm b/ios/app/MBXViewController.mm index 9ccda7ac6d7..fb62ec02e14 100644 --- a/ios/app/MBXViewController.mm +++ b/ios/app/MBXViewController.mm @@ -6,8 +6,6 @@ #import -#import "MBXAnnotation.h" - static UIColor *const kTintColor = [UIColor colorWithRed:0.120 green:0.550 blue:0.670 alpha:1.000]; static NSArray *const kStyleNames = @[ @@ -194,9 +192,9 @@ - (void)parseFeaturesAddingCount:(NSUInteger)featuresCount [feature[@"geometry"][@"coordinates"][0] doubleValue]); NSString *title = feature[@"properties"][@"NAME"]; - MBXAnnotation *annotation = [MBXAnnotation annotationWithLocation:coordinate - title:title - subtitle:nil]; + MGLPointAnnotation *annotation = [MGLPointAnnotation new]; + annotation.coordinate = coordinate; + annotation.title = title; [annotations addObject:annotation]; diff --git a/ios/app/mapboxgl-app.gypi b/ios/app/mapboxgl-app.gypi index 73b66dff12d..9807f0891c5 100644 --- a/ios/app/mapboxgl-app.gypi +++ b/ios/app/mapboxgl-app.gypi @@ -29,8 +29,6 @@ './MBXAppDelegate.m', './MBXViewController.h', './MBXViewController.mm', - './MBXAnnotation.h', - './MBXAnnotation.m', '../../platform/darwin/settings_nsuserdefaults.mm', ], diff --git a/platform/ios/MGLPointAnnotation.m b/platform/ios/MGLPointAnnotation.m new file mode 100644 index 00000000000..13fbba1083e --- /dev/null +++ b/platform/ios/MGLPointAnnotation.m @@ -0,0 +1,7 @@ +#import "MGLPointAnnotation.h" + +@implementation MGLPointAnnotation + +@synthesize coordinate; + +@end From a3f32eab237d7befec5c410d91877e149561a4d9 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Sun, 31 May 2015 14:48:44 -0700 Subject: [PATCH 24/85] clean up MGLMultiPoint & descendents --- include/mbgl/ios/MGLMultiPoint.h | 2 + platform/ios/MGLMultiPoint.m | 66 +++++++++++++++++++++++++++++++- platform/ios/MGLPolygon.m | 27 +++---------- platform/ios/MGLPolyline.m | 27 +++---------- 4 files changed, 79 insertions(+), 43 deletions(-) diff --git a/include/mbgl/ios/MGLMultiPoint.h b/include/mbgl/ios/MGLMultiPoint.h index 2c631035d67..acafe6ad2c2 100644 --- a/include/mbgl/ios/MGLMultiPoint.h +++ b/include/mbgl/ios/MGLMultiPoint.h @@ -5,6 +5,8 @@ @interface MGLMultiPoint : MGLShape +@property (nonatomic, readonly) NSUInteger pointCount; + - (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range; @end diff --git a/platform/ios/MGLMultiPoint.m b/platform/ios/MGLMultiPoint.m index 986052f1620..dedc26f3a4a 100644 --- a/platform/ios/MGLMultiPoint.m +++ b/platform/ios/MGLMultiPoint.m @@ -3,16 +3,80 @@ @implementation MGLMultiPoint { CLLocationCoordinate2D *_coords; + size_t _count; +} + +- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords + count:(NSUInteger)count +{ + assert(sizeof(coords) == count * sizeof(CLLocationCoordinate2D)); + + self = [super init]; + + if (self) + { + _count = count; + _coords = malloc(_count * sizeof(CLLocationCoordinate2D)); + + for (NSUInteger i = 0; i < _count; i++) + { + _coords[i] = coords[i]; + } + } + + return self; +} + +- (void)dealloc +{ + free(_coords); } - (CLLocationCoordinate2D)coordinate { + if ([self isMemberOfClass:[MGLMultiPoint class]]) + { + [[NSException exceptionWithName:@"MGLAbstractClassException" + reason:@"MGLMultiPoint is an abstract class" + userInfo:nil] raise]; + } + + assert(_count > 0); + return CLLocationCoordinate2DMake(_coords[0].latitude, _coords[0].longitude); } +- (NSUInteger)pointCount +{ + if ([self isMemberOfClass:[MGLMultiPoint class]]) + { + [[NSException exceptionWithName:@"MGLAbstractClassException" + reason:@"MGLMultiPoint is an abstract class" + userInfo:nil] raise]; + } + + return _count; +} + - (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range { - coords = _coords; + if ([self isMemberOfClass:[MGLMultiPoint class]]) + { + [[NSException exceptionWithName:@"MGLAbstractClassException" + reason:@"MGLMultiPoint is an abstract class" + userInfo:nil] raise]; + } + + assert(sizeof(coords) >= sizeof(range.length * sizeof(CLLocationCoordinate2D))); + assert(range.location + range.length < _count); + + NSUInteger index = 0; + + for (NSUInteger i = range.location; i < range.location + range.length; i++) + { + coords[index] = _coords[i]; + index++; + } } @end diff --git a/platform/ios/MGLPolygon.m b/platform/ios/MGLPolygon.m index 4167fc3f6d2..97c2054a94e 100644 --- a/platform/ios/MGLPolygon.m +++ b/platform/ios/MGLPolygon.m @@ -1,9 +1,12 @@ #import "MGLPolygon.h" +@interface MGLPolygon (MGLMultiPointDescendent) + +- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count; + +@end + @implementation MGLPolygon -{ - CLLocationCoordinate2D *_coords; -} + (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count @@ -11,22 +14,4 @@ + (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords return [[self alloc] initWithCoordinates:coords count:count]; } -- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords - count:(NSUInteger)count -{ - self = [super init]; - - if (self) - { - _coords = coords; - } - - return self; -} - -- (CLLocationCoordinate2D)coordinate -{ - return CLLocationCoordinate2DMake(_coords[0].latitude, _coords[0].longitude); -} - @end diff --git a/platform/ios/MGLPolyline.m b/platform/ios/MGLPolyline.m index 442ad64145d..3e3abc0b9c6 100644 --- a/platform/ios/MGLPolyline.m +++ b/platform/ios/MGLPolyline.m @@ -1,9 +1,12 @@ #import "MGLPolyline.h" +@interface MGLPolyline (MGLMultiPointDescendent) + +- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count; + +@end + @implementation MGLPolyline -{ - CLLocationCoordinate2D *_coords; -} + (instancetype)polylineWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count @@ -11,22 +14,4 @@ + (instancetype)polylineWithCoordinates:(CLLocationCoordinate2D *)coords return [[self alloc] initWithCoordinates:coords count:count]; } -- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords - count:(NSUInteger)count -{ - self = [super init]; - - if (self) - { - _coords = coords; - } - - return self; -} - -- (CLLocationCoordinate2D)coordinate -{ - return CLLocationCoordinate2DMake(_coords[0].latitude, _coords[0].longitude); -} - @end From 1c23ac8aecd89acfe081796d851ae50593d01f87 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Sun, 31 May 2015 16:11:04 -0700 Subject: [PATCH 25/85] flesh out Cocoa shapes API & use in demo app --- ios/app/MBXViewController.mm | 56 +- ios/app/mapboxgl-app.gypi | 3 +- ios/app/{features.geojson => points.geojson} | 0 ios/app/polyline.geojson | 14187 +++++++++++++++++ platform/ios/MGLMapView.mm | 123 +- platform/ios/MGLMultiPoint.m | 4 +- 6 files changed, 14353 insertions(+), 20 deletions(-) rename ios/app/{features.geojson => points.geojson} (100%) create mode 100644 ios/app/polyline.geojson diff --git a/ios/app/MBXViewController.mm b/ios/app/MBXViewController.mm index fb62ec02e14..fb47264f014 100644 --- a/ios/app/MBXViewController.mm +++ b/ios/app/MBXViewController.mm @@ -128,7 +128,8 @@ - (void)showSettings @"Add 100 Points", @"Add 1,000 Points", @"Add 10,000 Points", - @"Remove Points", + @"Add Test Shapes", + @"Remove Annotations", nil]; [sheet showFromBarButtonItem:self.navigationItem.leftBarButtonItem animated:YES]; @@ -165,6 +166,42 @@ - (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSIn [self parseFeaturesAddingCount:10000]; } else if (buttonIndex == actionSheet.firstOtherButtonIndex + 7) + { + CLLocationCoordinate2D polygonCoordinates[4] = + { + CLLocationCoordinate2DMake(44, -122), + CLLocationCoordinate2DMake(46, -122), + CLLocationCoordinate2DMake(46, -121), + CLLocationCoordinate2DMake(44, -122) + }; + + MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:polygonCoordinates count:4]; + + [self.mapView addAnnotation:polygon]; + + NSDictionary *hike = [NSJSONSerialization JSONObjectWithData: + [NSData dataWithContentsOfFile: + [[NSBundle mainBundle] pathForResource:@"polyline" ofType:@"geojson"]] + options:0 + error:nil]; + + NSArray *coordinatePairs = hike[@"features"][0][@"geometry"][@"coordinates"]; + + CLLocationCoordinate2D *polylineCoordinates = (CLLocationCoordinate2D *)malloc([coordinatePairs count] * sizeof(CLLocationCoordinate2D)); + + for (NSArray *coordinatePair in coordinatePairs) + { + polylineCoordinates[[coordinatePairs indexOfObject:coordinatePair]] = CLLocationCoordinate2DMake([coordinatePair[1] doubleValue], [coordinatePair[0] doubleValue]); + } + + MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:polylineCoordinates + count:[coordinatePairs count]]; + + [self.mapView addAnnotation:polyline]; + + free(polylineCoordinates); + } + else if (buttonIndex == actionSheet.firstOtherButtonIndex + 8) { [self.mapView removeAnnotations:self.mapView.annotations]; } @@ -176,7 +213,7 @@ - (void)parseFeaturesAddingCount:(NSUInteger)featuresCount dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^ { - NSData *featuresData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"features" ofType:@"geojson"]]; + NSData *featuresData = [NSData dataWithContentsOfFile:[[NSBundle mainBundle] pathForResource:@"points" ofType:@"geojson"]]; id features = [NSJSONSerialization JSONObjectWithData:featuresData options:0 @@ -277,6 +314,21 @@ - (BOOL)mapView:(__unused MGLMapView *)mapView annotationCanShowCallout:(__unuse return YES; } +- (CGFloat)mapView:(__unused MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation +{ + return ([annotation isKindOfClass:[MGLPolygon class]] ? 0.5 : 1.0); +} + +- (UIColor *)mapView:(__unused MGLMapView *)mapView strokeColorForShapeAnnotation:(MGLShape *)annotation +{ + return ([annotation isKindOfClass:[MGLPolyline class]] ? [UIColor purpleColor] : [UIColor blackColor]); +} + +- (UIColor *)mapView:(__unused MGLMapView *)mapView fillColorForPolygonAnnotation:(__unused MGLPolygon *)annotation +{ + return [UIColor redColor]; +} + - (void)mapView:(__unused MGLMapView *)mapView didChangeUserTrackingMode:(MGLUserTrackingMode)mode animated:(__unused BOOL)animated { UIImage *newButtonImage; diff --git a/ios/app/mapboxgl-app.gypi b/ios/app/mapboxgl-app.gypi index 9807f0891c5..56ac3567c5d 100644 --- a/ios/app/mapboxgl-app.gypi +++ b/ios/app/mapboxgl-app.gypi @@ -10,7 +10,8 @@ 'mac_bundle': 1, 'mac_bundle_resources': [ ' latLngs; - latLngs.reserve(annotations.count); - + std::vector points; std::vector symbols; - symbols.reserve(annotations.count); + + std::vector shapes; + std::vector shapesProperties; BOOL delegateImplementsSymbolLookup = [self.delegate respondsToSelector:@selector(mapView:symbolNameForAnnotation:)]; + BOOL delegateImplementsAlphaForShape = [self.delegate respondsToSelector:@selector(mapView:alphaForShapeAnnotation:)]; + BOOL delegateImplementsStrokeColorForShape = [self.delegate respondsToSelector:@selector(mapView:strokeColorForShapeAnnotation:)]; + BOOL delegateImplementsFillColorForPolygon = [self.delegate respondsToSelector:@selector(mapView:fillColorForPolygonAnnotation:)]; + BOOL delegateImplementsLineWidthForPolyline = [self.delegate respondsToSelector:@selector(mapView:lineWidthForPolylineAnnotation:)]; for (id annotation in annotations) { assert([annotation conformsToProtocol:@protocol(MGLAnnotation)]); - latLngs.push_back(coordinateToLatLng(annotation.coordinate)); + if ([annotation isKindOfClass:[MGLMultiPoint class]]) + { + CGFloat alpha = (delegateImplementsAlphaForShape ? + [self.delegate mapView:self alphaForShapeAnnotation:annotation] : + 1.0); + + UIColor *strokeColor = (delegateImplementsStrokeColorForShape ? + [self.delegate mapView:self strokeColorForShapeAnnotation:annotation] : + [UIColor blackColor]); - NSString *symbolName = nil; + assert(strokeColor); - if (delegateImplementsSymbolLookup) - { - symbolName = [self.delegate mapView:self symbolNameForAnnotation:annotation]; + CGFloat r,g,b,a; + [strokeColor getRed:&r green:&g blue:&b alpha:&a]; + mbgl::Color strokeNativeColor({{ (float)r, (float)g, (float)b, (float)a }}); + + mbgl::StyleProperties shapeProperties; + + if ([annotation isKindOfClass:[MGLPolyline class]]) + { + CGFloat lineWidth = (delegateImplementsLineWidthForPolyline ? + [self.delegate mapView:self lineWidthForPolylineAnnotation:annotation] : + 3.0); + + mbgl::LineProperties lineProperties; + lineProperties.opacity = alpha; + lineProperties.color = strokeNativeColor; + lineProperties.width = lineWidth; + shapeProperties.set(lineProperties); + + } + else if ([annotation isKindOfClass:[MGLPolygon class]]) + { + UIColor *fillColor = (delegateImplementsFillColorForPolygon ? + [self.delegate mapView:self fillColorForPolygonAnnotation:annotation] : + [UIColor blueColor]); + + assert(fillColor); + + [fillColor getRed:&r green:&g blue:&b alpha:&a]; + mbgl::Color fillNativeColor({{ (float)r, (float)g, (float)b, (float)a }}); + + mbgl::FillProperties fillProperties; + fillProperties.opacity = alpha; + fillProperties.stroke_color = strokeNativeColor; + fillProperties.fill_color = fillNativeColor; + shapeProperties.set(fillProperties); + } + else + { + [[NSException exceptionWithName:@"MGLUnknownShapeClassException" + reason:[NSString stringWithFormat:@"%@ is an unknown shape class", [annotation class]] + userInfo:nil] raise]; + } + + shapesProperties.push_back(shapeProperties); + + NSUInteger count = [(MGLMultiPoint *)annotation pointCount]; + + CLLocationCoordinate2D *coordinates = (CLLocationCoordinate2D *)malloc(count * sizeof(CLLocationCoordinate2D)); + [(MGLMultiPoint *)annotation getCoordinates:coordinates range:NSMakeRange(0, count)]; + + mbgl::AnnotationSegment shape; + shape.reserve(count); + + for (NSUInteger i = 0; i < count; i++) + { + shape.push_back(mbgl::LatLng(coordinates[i].latitude, coordinates[i].longitude)); + } + + free(coordinates); + + shapes.push_back({{ shape }}); } + else + { + points.push_back(coordinateToLatLng(annotation.coordinate)); + + NSString *symbolName = nil; - symbols.push_back((symbolName ? [symbolName UTF8String] : "")); + if (delegateImplementsSymbolLookup) + { + symbolName = [self.delegate mapView:self symbolNameForAnnotation:annotation]; + } + + symbols.push_back((symbolName ? [symbolName UTF8String] : "")); + } } - std::vector annotationIDs = _mbglMap->addPointAnnotations(latLngs, symbols); + if (points.size()) + { + std::vector pointAnnotationIDs = _mbglMap->addPointAnnotations(points, symbols); + + for (size_t i = 0; i < pointAnnotationIDs.size(); ++i) + { + [self.annotationIDsByAnnotation setObject:@{ MGLAnnotationIDKey : @(pointAnnotationIDs[i]) } + forKey:annotations[i]]; + } + } - for (size_t i = 0; i < annotationIDs.size(); ++i) + if (shapes.size()) { - [self.annotationIDsByAnnotation setObject:@{ MGLAnnotationIDKey : @(annotationIDs[i]) } - forKey:annotations[i]]; + std::vector shapeAnnotationIDs = _mbglMap->addShapeAnnotations(shapes, shapesProperties); + + for (size_t i = 0; i < shapeAnnotationIDs.size(); ++i) + { + [self.annotationIDsByAnnotation setObject:@{ MGLAnnotationIDKey : @(shapeAnnotationIDs[i]) } + forKey:annotations[i]]; + } } } diff --git a/platform/ios/MGLMultiPoint.m b/platform/ios/MGLMultiPoint.m index dedc26f3a4a..f138a71a665 100644 --- a/platform/ios/MGLMultiPoint.m +++ b/platform/ios/MGLMultiPoint.m @@ -9,8 +9,6 @@ @implementation MGLMultiPoint - (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count { - assert(sizeof(coords) == count * sizeof(CLLocationCoordinate2D)); - self = [super init]; if (self) @@ -68,7 +66,7 @@ - (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range } assert(sizeof(coords) >= sizeof(range.length * sizeof(CLLocationCoordinate2D))); - assert(range.location + range.length < _count); + assert(range.location + range.length <= _count); NSUInteger index = 0; From 5c3448a47824f7ada4ea65a3d60f060734f9033d Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 1 Jun 2015 12:40:12 -0700 Subject: [PATCH 26/85] remove todo comment --- src/mbgl/map/map.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index d9a1d11e80c..f73a00bdb37 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -260,8 +260,6 @@ AnnotationIDs Map::addPointAnnotations(const AnnotationSegment& points, return result.second; } -// todo don't penalize adding point and shape anno's at the same time - uint32_t Map::addShapeAnnotation(const AnnotationSegments& shape, const StyleProperties& styleProperties) { return addShapeAnnotations({ shape }, { styleProperties }).front(); From bf6be3c2207aab29a15cb1764eadece03fc0e722 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 1 Jun 2015 13:08:07 -0700 Subject: [PATCH 27/85] fix annotations bounds search for >+/-85 latitude --- src/mbgl/map/annotation.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index b2c6ac8f31b..6c5b56758e0 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -67,8 +67,11 @@ uint32_t AnnotationManager::nextID() { } vec2 AnnotationManager::projectPoint(const LatLng& point) { + // Clamp to the latitude limits of Mercator. + const double constrainedLatitude = std::fmin(std::fmax(point.latitude, -util::LATITUDE_MAX), util::LATITUDE_MAX); + // Project a coordinate into unit space in a square map. - const double sine = std::sin(point.latitude * M_PI / 180.0); + const double sine = std::sin(constrainedLatitude * M_PI / 180.0); const double x = point.longitude / 360.0 + 0.5; const double y = 0.5 - 0.25 * std::log((1.0 + sine) / (1.0 - sine)) / M_PI; return { x, y }; From 8626f911609de57b77457389788df27ebdb24578 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 1 Jun 2015 13:08:57 -0700 Subject: [PATCH 28/85] avoid dupes in annotations bounds queries --- src/mbgl/map/annotation.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 6c5b56758e0..dca3de4905a 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -386,7 +386,7 @@ AnnotationIDs AnnotationManager::getAnnotationsInBounds(const LatLngBounds& quer const TileID nwTile(z, swPoint.x * z2, nePoint.y * z2); const TileID seTile(z, nePoint.x * z2, swPoint.y * z2); - AnnotationIDs matchingAnnotations; + std::unordered_set matchingAnnotations; for (auto& tile : tiles) { TileID id = tile.first; @@ -396,12 +396,12 @@ AnnotationIDs AnnotationManager::getAnnotationsInBounds(const LatLngBounds& quer // Trivial accept; this tile is completely inside the query bounds, so // we'll return all of its annotations. std::copy(tile.second.first.begin(), tile.second.first.end(), - std::back_inserter(matchingAnnotations)); + std::inserter(matchingAnnotations, matchingAnnotations.begin())); } else { // This tile is intersected by the query bounds. We need to check the // tile's annotations' bounding boxes individually. std::copy_if(tile.second.first.begin(), tile.second.first.end(), - std::back_inserter(matchingAnnotations), + std::inserter(matchingAnnotations, matchingAnnotations.begin()), [&](const uint32_t annotationID) -> bool { const auto it = annotations.find(annotationID); if (it != annotations.end()) { @@ -419,7 +419,7 @@ AnnotationIDs AnnotationManager::getAnnotationsInBounds(const LatLngBounds& quer } } - return matchingAnnotations; + return AnnotationIDs(matchingAnnotations.begin(), matchingAnnotations.end()); } LatLngBounds AnnotationManager::getBoundsForAnnotations(const AnnotationIDs& ids) const { From b51f5e8d44d5562efdd88b11ad0359d2eb157567 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 1 Jun 2015 15:32:02 -0700 Subject: [PATCH 29/85] fix problems with multiple point annotations --- src/mbgl/map/annotation.cpp | 74 +++++++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 27 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index dca3de4905a..7c75afed03e 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -106,8 +106,6 @@ AnnotationManager::addAnnotations(const AnnotationType type, std::vector>> projectedShape; projectedShape.reserve(shape.size()); - const uint32_t annotationID = nextID(); - for (size_t l = 0; l < shape.size(); ++l) { auto& line = shape[l]; @@ -120,37 +118,59 @@ AnnotationManager::addAnnotations(const AnnotationType type, // projection conversion into unit space const auto pp = projectPoint(point); - projectedLine.push_back(pp); - } + if (type == AnnotationType::Point) { + const uint32_t pointAnnotationID = nextID(); - projectedShape.push_back(projectedLine); - } + // at render time we style the point according to its {sprite} field + std::unordered_map pointFeatureProperties; + const std::string& symbol = annotationsProperties.at("symbols")[p]; + if (symbol.length()) { + pointFeatureProperties.emplace("sprite", symbol); + } else { + pointFeatureProperties.emplace("sprite", defaultPointAnnotationSymbol); + } - std::unordered_map featureProperties; + // add individual point tile feature + auto featureAffectedTiles = addTileFeature( + pointAnnotationID, + AnnotationSegments({{ point }}), + std::vector>>({{ pp }}), + AnnotationType::Point, + {{ }}, + pointFeatureProperties, + maxZoom + ); - if (type == AnnotationType::Point) { - // at render time we style the point according to its {sprite} field - const std::string& symbol = annotationsProperties.at("symbols")[s]; - if (symbol.length()) { - featureProperties.emplace("sprite", symbol); - } else { - featureProperties.emplace("sprite", defaultPointAnnotationSymbol); + std::copy(featureAffectedTiles.begin(), featureAffectedTiles.end(), std::inserter(affectedTiles, affectedTiles.begin())); + + annotationIDs.push_back(pointAnnotationID); + } else { + projectedLine.push_back(pp); + } + } + + if (type == AnnotationType::Shape) { + projectedShape.push_back(projectedLine); } } - auto featureAffectedTiles = addTileFeature( - annotationID, - shape, - projectedShape, - type, - styleProperties[s], - featureProperties, - maxZoom - ); + if (type == AnnotationType::Shape) { + const uint32_t shapeAnnotationID = nextID(); + + auto featureAffectedTiles = addTileFeature( + shapeAnnotationID, + shape, + projectedShape, + AnnotationType::Shape, + styleProperties[s], + {{ }}, + maxZoom + ); - std::copy(featureAffectedTiles.begin(), featureAffectedTiles.end(), std::inserter(affectedTiles, affectedTiles.begin())); + std::copy(featureAffectedTiles.begin(), featureAffectedTiles.end(), std::inserter(affectedTiles, affectedTiles.begin())); - annotationIDs.push_back(annotationID); + annotationIDs.push_back(shapeAnnotationID); + } } // Tile:IDs that need refreshed and the annotation identifiers held onto by the client. @@ -286,8 +306,8 @@ AnnotationManager::addPointAnnotations(const AnnotationSegment& points, const AnnotationsProperties& annotationsProperties, const MapData& data) { return addAnnotations(AnnotationType::Point, - { { points } }, - { defaultStyleProperties() }, + {{ points }}, + {{ }}, annotationsProperties, data); } From da013180caa0fcd6075f835316af9fc208d17862 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 1 Jun 2015 16:31:11 -0700 Subject: [PATCH 30/85] allow annotation bounds filtering by type --- include/mbgl/map/map.hpp | 8 +++++++- platform/ios/MGLMapView.mm | 2 +- src/mbgl/map/annotation.cpp | 33 +++++++++++++++++++++++++++++---- src/mbgl/map/annotation.hpp | 7 +------ src/mbgl/map/map.cpp | 4 ++-- 5 files changed, 40 insertions(+), 14 deletions(-) diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index f989b213a48..23463ba74c5 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -28,6 +28,12 @@ namespace util { template class Thread; } +enum class AnnotationType : uint8_t { + Any = 0, + Point = 1 << 0, + Shape = 1 << 1, +}; + using AnnotationIDs = std::vector; using AnnotationSegment = std::vector; using AnnotationSegments = std::vector; @@ -130,7 +136,7 @@ class Map : private util::noncopyable { const std::vector&); void removeAnnotation(uint32_t); void removeAnnotations(const AnnotationIDs&); - AnnotationIDs getAnnotationsInBounds(const LatLngBounds&); + AnnotationIDs getAnnotationsInBounds(const LatLngBounds&, const AnnotationType& = AnnotationType::Any); LatLngBounds getBoundsForAnnotations(const AnnotationIDs&); // Memory diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index cd127d4e008..e1c082c9035 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -1011,7 +1011,7 @@ - (void)handleSingleTapGesture:(UITapGestureRecognizer *)singleTap tapBounds.extend(coordinateToLatLng(coordinate)); // query for nearby annotations - std::vector nearbyAnnotations = _mbglMap->getAnnotationsInBounds(tapBounds); + std::vector nearbyAnnotations = _mbglMap->getAnnotationsInBounds(tapBounds, mbgl::AnnotationType::Point); int32_t newSelectedAnnotationID = -1; diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 7c75afed03e..911c5b7436f 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -20,6 +20,7 @@ Annotation::Annotation(AnnotationType type_, geometry(geometry_), bounds([this] { LatLngBounds bounds_; + assert(type != AnnotationType::Any); if (type == AnnotationType::Point) { bounds_ = { getPoint(), getPoint() }; } else { @@ -85,6 +86,8 @@ AnnotationManager::addAnnotations(const AnnotationType type, const MapData& data) { std::lock_guard lock(mtx); + assert(type != AnnotationType::Any); + // We pre-generate tiles to contain each annotation up to the map's max zoom. // We do this for fast rendering without projection conversions on the fly, as well as // to simplify bounding box queries of annotations later. Tiles get invalidated when @@ -186,6 +189,8 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, const std::unordered_map& featureProperties, const uint8_t maxZoom) { + assert(type != AnnotationType::Any); + // track the annotation global ID and its original geometry auto anno_it = annotations.emplace(annotationID, util::make_unique(type, segments, styleProperties)); @@ -394,7 +399,8 @@ const std::unique_ptr& AnnotationManager::getAnnotationWithID(uint32 } AnnotationIDs AnnotationManager::getAnnotationsInBounds(const LatLngBounds& queryBounds, - const MapData& data) const { + const MapData& data, + const AnnotationType& type) const { std::lock_guard lock(mtx); const uint8_t z = data.transform.getMaxZoom(); @@ -414,9 +420,22 @@ AnnotationIDs AnnotationManager::getAnnotationsInBounds(const LatLngBounds& quer if (id.x >= nwTile.x && id.x <= seTile.x && id.y >= nwTile.y && id.y <= seTile.y) { if (id.x > nwTile.x && id.x < seTile.x && id.y > nwTile.y && id.y < seTile.y) { // Trivial accept; this tile is completely inside the query bounds, so - // we'll return all of its annotations. - std::copy(tile.second.first.begin(), tile.second.first.end(), - std::inserter(matchingAnnotations, matchingAnnotations.begin())); + // we'll return all of its annotations that match type (if specified). + if (type != AnnotationType::Any) { + std::copy_if(tile.second.first.begin(), tile.second.first.end(), + std::inserter(matchingAnnotations, matchingAnnotations.begin()), + [&](const uint32_t annotationID) -> bool { + const auto it = annotations.find(annotationID); + if (it != annotations.end()) { + return (it->second->type == type); + } else { + return false; + } + }); + } else { + std::copy(tile.second.first.begin(), tile.second.first.end(), + std::inserter(matchingAnnotations, matchingAnnotations.begin())); + } } else { // This tile is intersected by the query bounds. We need to check the // tile's annotations' bounding boxes individually. @@ -425,6 +444,12 @@ AnnotationIDs AnnotationManager::getAnnotationsInBounds(const LatLngBounds& quer [&](const uint32_t annotationID) -> bool { const auto it = annotations.find(annotationID); if (it != annotations.end()) { + // check type + if (type != AnnotationType::Any && it->second->type != type) { + return false; + } + + // check bounds const LatLngBounds annoBounds = it->second->getBounds(); return (annoBounds.sw.latitude >= queryBounds.sw.latitude && annoBounds.ne.latitude <= queryBounds.ne.latitude && diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp index b6f7be4f638..c81effdf7fe 100644 --- a/src/mbgl/map/annotation.hpp +++ b/src/mbgl/map/annotation.hpp @@ -28,11 +28,6 @@ class MapData; using AnnotationsProperties = std::unordered_map>; -enum class AnnotationType : uint8_t { - Point, - Shape -}; - class Annotation : private util::noncopyable { friend class AnnotationManager; public: @@ -73,7 +68,7 @@ class AnnotationManager : private util::noncopyable { std::unordered_set removeAnnotations(const AnnotationIDs&, const MapData&); AnnotationIDs getOrderedShapeAnnotations() const { return orderedShapeAnnotations; } const std::unique_ptr& getAnnotationWithID(uint32_t) const; - AnnotationIDs getAnnotationsInBounds(const LatLngBounds&, const MapData&) const; + AnnotationIDs getAnnotationsInBounds(const LatLngBounds&, const MapData&, const AnnotationType& = AnnotationType::Any) const; LatLngBounds getBoundsForAnnotations(const AnnotationIDs&) const; const LiveTile* getTile(const TileID& id); diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index f73a00bdb37..efb2b1cdddf 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -281,8 +281,8 @@ void Map::removeAnnotations(const std::vector& annotations) { context->invoke(&MapContext::updateAnnotationTiles, result); } -std::vector Map::getAnnotationsInBounds(const LatLngBounds& bounds) { - return data->annotationManager.getAnnotationsInBounds(bounds, *data); +std::vector Map::getAnnotationsInBounds(const LatLngBounds& bounds, const AnnotationType& type) { + return data->annotationManager.getAnnotationsInBounds(bounds, *data, type); } LatLngBounds Map::getBoundsForAnnotations(const std::vector& annotations) { From 376ad5ee0240ee7d4ca68633089d0f38002a2b19 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 1 Jun 2015 16:49:21 -0700 Subject: [PATCH 31/85] extend annotation bounds querying for shapes --- include/mbgl/util/geo.hpp | 9 ++++++++- src/mbgl/map/annotation.cpp | 13 ++++++------- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/include/mbgl/util/geo.hpp b/include/mbgl/util/geo.hpp index 6ece6d4de91..03dab078418 100644 --- a/include/mbgl/util/geo.hpp +++ b/include/mbgl/util/geo.hpp @@ -33,12 +33,19 @@ struct LatLngBounds { if (point.longitude > ne.longitude) ne.longitude = point.longitude; } - inline bool contains(const LatLng& point) { + inline bool contains(const LatLng& point) const { return (point.latitude >= sw.latitude && point.latitude <= ne.latitude && point.longitude >= sw.longitude && point.longitude <= ne.longitude); } + + inline bool intersects(const LatLngBounds area) const { + return (area.ne.latitude > sw.latitude && + area.sw.latitude < ne.latitude && + area.ne.longitude > sw.longitude && + area.sw.longitude < ne.longitude); + } }; } diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 911c5b7436f..82dbaabd887 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -450,14 +450,13 @@ AnnotationIDs AnnotationManager::getAnnotationsInBounds(const LatLngBounds& quer } // check bounds - const LatLngBounds annoBounds = it->second->getBounds(); - return (annoBounds.sw.latitude >= queryBounds.sw.latitude && - annoBounds.ne.latitude <= queryBounds.ne.latitude && - annoBounds.sw.longitude >= queryBounds.sw.longitude && - annoBounds.ne.longitude <= queryBounds.ne.longitude); - } else { - return false; + if (it->second->type == AnnotationType::Point) { + return queryBounds.contains(it->second->getPoint()); + } else if (it->second->type == AnnotationType::Shape) { + return queryBounds.intersects(it->second->getBounds()); + } } + return false; }); } } From 2a0d31c868f2d43ed1bceacf6bfdb6d6fa7b46b1 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 1 Jun 2015 17:18:34 -0700 Subject: [PATCH 32/85] implement MGLOverlay & bounds intersection --- include/mbgl/ios/MGLPolygon.h | 3 ++- include/mbgl/ios/MGLPolyline.h | 3 ++- platform/ios/MGLMapView.mm | 4 +-- .../ios/{MGLMultiPoint.m => MGLMultiPoint.mm} | 25 ++++++++++++++++++- platform/ios/MGLPolygon.m | 5 +++- platform/ios/MGLPolyline.m | 5 +++- 6 files changed, 38 insertions(+), 7 deletions(-) rename platform/ios/{MGLMultiPoint.m => MGLMultiPoint.mm} (71%) diff --git a/include/mbgl/ios/MGLPolygon.h b/include/mbgl/ios/MGLPolygon.h index b61f6359199..0e8648ed7a7 100644 --- a/include/mbgl/ios/MGLPolygon.h +++ b/include/mbgl/ios/MGLPolygon.h @@ -2,8 +2,9 @@ #import #import "MGLMultiPoint.h" +#import "MGLOverlay.h" -@interface MGLPolygon : MGLMultiPoint +@interface MGLPolygon : MGLMultiPoint + (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count; diff --git a/include/mbgl/ios/MGLPolyline.h b/include/mbgl/ios/MGLPolyline.h index df682cbc319..f9fccf31f7e 100644 --- a/include/mbgl/ios/MGLPolyline.h +++ b/include/mbgl/ios/MGLPolyline.h @@ -2,8 +2,9 @@ #import #import "MGLMultiPoint.h" +#import "MGLOverlay.h" -@interface MGLPolyline : MGLMultiPoint +@interface MGLPolyline : MGLMultiPoint + (instancetype)polylineWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count; diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index e1c082c9035..6d9b20a4442 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -1682,7 +1682,7 @@ - (void)addAnnotations:(NSArray *)annotations if ([annotation isKindOfClass:[MGLPolyline class]]) { CGFloat lineWidth = (delegateImplementsLineWidthForPolyline ? - [self.delegate mapView:self lineWidthForPolylineAnnotation:annotation] : + [self.delegate mapView:self lineWidthForPolylineAnnotation:(MGLPolyline *)annotation] : 3.0); mbgl::LineProperties lineProperties; @@ -1695,7 +1695,7 @@ - (void)addAnnotations:(NSArray *)annotations else if ([annotation isKindOfClass:[MGLPolygon class]]) { UIColor *fillColor = (delegateImplementsFillColorForPolygon ? - [self.delegate mapView:self fillColorForPolygonAnnotation:annotation] : + [self.delegate mapView:self fillColorForPolygonAnnotation:(MGLPolygon *)annotation] : [UIColor blueColor]); assert(fillColor); diff --git a/platform/ios/MGLMultiPoint.m b/platform/ios/MGLMultiPoint.mm similarity index 71% rename from platform/ios/MGLMultiPoint.m rename to platform/ios/MGLMultiPoint.mm index f138a71a665..fbd9a092d76 100644 --- a/platform/ios/MGLMultiPoint.m +++ b/platform/ios/MGLMultiPoint.mm @@ -1,9 +1,13 @@ #import "MGLMultiPoint.h" +#import "MGLTypes.h" + +#import @implementation MGLMultiPoint { CLLocationCoordinate2D *_coords; size_t _count; + mbgl::LatLngBounds _bounds; } - (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords @@ -14,11 +18,12 @@ - (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords if (self) { _count = count; - _coords = malloc(_count * sizeof(CLLocationCoordinate2D)); + _coords = (CLLocationCoordinate2D*)malloc(_count * sizeof(CLLocationCoordinate2D)); for (NSUInteger i = 0; i < _count; i++) { _coords[i] = coords[i]; + _bounds.extend(mbgl::LatLng(coords[i].latitude, coords[i].longitude)); } } @@ -77,4 +82,22 @@ - (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range } } +- (MGLMapBounds)overlayBounds +{ + return { + CLLocationCoordinate2DMake(_bounds.sw.latitude, _bounds.sw.longitude), + CLLocationCoordinate2DMake(_bounds.ne.longitude, _bounds.ne.longitude) + }; +} + +- (BOOL)intersectsOverlayBounds:(MGLMapBounds)overlayBounds +{ + mbgl::LatLngBounds area( + mbgl::LatLng(overlayBounds.sw.latitude, overlayBounds.sw.longitude), + mbgl::LatLng(overlayBounds.ne.latitude, overlayBounds.ne.longitude) + ); + + return _bounds.intersects(area); +} + @end diff --git a/platform/ios/MGLPolygon.m b/platform/ios/MGLPolygon.m index 97c2054a94e..492980933a7 100644 --- a/platform/ios/MGLPolygon.m +++ b/platform/ios/MGLPolygon.m @@ -1,13 +1,16 @@ #import "MGLPolygon.h" -@interface MGLPolygon (MGLMultiPointDescendent) +@interface MGLMultiPoint (MGLPolygon) - (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count; +- (BOOL)intersectsOverlayBounds:(MGLMapBounds)overlayBounds; @end @implementation MGLPolygon +@synthesize overlayBounds; + + (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count { diff --git a/platform/ios/MGLPolyline.m b/platform/ios/MGLPolyline.m index 3e3abc0b9c6..f80a149a9ef 100644 --- a/platform/ios/MGLPolyline.m +++ b/platform/ios/MGLPolyline.m @@ -1,13 +1,16 @@ #import "MGLPolyline.h" -@interface MGLPolyline (MGLMultiPointDescendent) +@interface MGLMultiPoint (MGLPolyline) - (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count; +- (BOOL)intersectsOverlayBounds:(MGLMapBounds)overlayBounds; @end @implementation MGLPolyline +@synthesize overlayBounds; + + (instancetype)polylineWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count { From e25a561ce1abcc97331a28840ba203bd3997dc24 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 1 Jun 2015 17:25:30 -0700 Subject: [PATCH 33/85] comment out API docs gen for now to try tests --- scripts/ios/package.sh | 46 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/scripts/ios/package.sh b/scripts/ios/package.sh index 35162ce5d16..5da03697934 100755 --- a/scripts/ios/package.sh +++ b/scripts/ios/package.sh @@ -96,26 +96,26 @@ mkdir -p "${OUTPUT}/static/${NAME}.bundle" cp -pv platform/ios/resources/* "${OUTPUT}/static/${NAME}.bundle" cp -prv styles/styles "${OUTPUT}/static/${NAME}.bundle/styles" -step "Creating API Docs..." -if [ -z `which appledoc` ]; then - echo "Unable to find appledoc. Consider installing it from source or Homebrew." - exit 1 -fi -DOCS_OUTPUT="${OUTPUT}/static/Docs" -DOCS_VERSION=$( git tag -l ios\* --sort -v:refname | sed -n '1p' | sed 's/ios-v//' ) -README="/tmp/GL-README.md" -cat ios/README.md > ${README} -echo >> ${README} -echo -n "#" >> ${README} -cat CHANGELOG.md >> ${README} -appledoc \ - --output ${DOCS_OUTPUT} \ - --project-name "Mapbox GL for iOS ${DOCS_VERSION}" \ - --project-company Mapbox \ - --create-html \ - --no-create-docset \ - --no-install-docset \ - --company-id com.mapbox \ - --ignore include/mbgl/ios/private \ - --index-desc ${README} \ - include/mbgl/ios +# step "Creating API Docs..." +# if [ -z `which appledoc` ]; then +# echo "Unable to find appledoc. Consider installing it from source or Homebrew." +# exit 1 +# fi +# DOCS_OUTPUT="${OUTPUT}/static/Docs" +# DOCS_VERSION=$( git tag -l ios\* --sort -v:refname | sed -n '1p' | sed 's/ios-v//' ) +# README="/tmp/GL-README.md" +# cat ios/README.md > ${README} +# echo >> ${README} +# echo -n "#" >> ${README} +# cat CHANGELOG.md >> ${README} +# appledoc \ +# --output ${DOCS_OUTPUT} \ +# --project-name "Mapbox GL for iOS ${DOCS_VERSION}" \ +# --project-company Mapbox \ +# --create-html \ +# --no-create-docset \ +# --no-install-docset \ +# --company-id com.mapbox \ +# --ignore include/mbgl/ios/private \ +# --index-desc ${README} \ +# include/mbgl/ios From 2862ee42880bfbb613a7cda809abb52b458fbed0 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 1 Jun 2015 17:35:57 -0700 Subject: [PATCH 34/85] update GYP for .m -> .mm --- gyp/platform-ios.gypi | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gyp/platform-ios.gypi b/gyp/platform-ios.gypi index 316f40ae002..d1bda88c5a7 100644 --- a/gyp/platform-ios.gypi +++ b/gyp/platform-ios.gypi @@ -36,7 +36,7 @@ '../include/mbgl/ios/MGLTypes.h', '../platform/ios/MGLTypes.m', '../include/mbgl/ios/MGLMultiPoint.h', - '../platform/ios/MGLMultiPoint.m', + '../platform/ios/MGLMultiPoint.mm', '../include/mbgl/ios/MGLOverlay.h', '../include/mbgl/ios/MGLPointAnnotation.h', '../platform/ios/MGLPointAnnotation.m', From 93521b89c83fd6d90519a2a607b18761eb217da1 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 1 Jun 2015 18:44:49 -0700 Subject: [PATCH 35/85] fix temp OS X shape add --- macosx/main.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/macosx/main.mm b/macosx/main.mm index 52e02debe9c..231eba35b60 100644 --- a/macosx/main.mm +++ b/macosx/main.mm @@ -160,12 +160,12 @@ int main() { mbgl::StyleProperties shapeProperties; shapeProperties.set(fillProperties); - map.addShapeAnnotation({ + map.addShapeAnnotation({{ mbgl::LatLng(44, -122), mbgl::LatLng(46, -122), mbgl::LatLng(46, -121), mbgl::LatLng(44, -122) - }, shapeProperties); + }}, shapeProperties); view.run(); From 9506583c0aabec1fbd891bf2e555a901a9962a6d Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 1 Jun 2015 19:34:08 -0700 Subject: [PATCH 36/85] flesh out some basic Cocoa shape API docs --- include/mbgl/ios/MGLAnnotation.h | 2 +- include/mbgl/ios/MGLMapView.h | 21 +++++++++++++++++++++ include/mbgl/ios/MGLMultiPoint.h | 5 +++++ include/mbgl/ios/MGLOverlay.h | 17 +++++++++++++++++ include/mbgl/ios/MGLPointAnnotation.h | 2 ++ include/mbgl/ios/MGLPolygon.h | 9 +++++---- include/mbgl/ios/MGLPolyline.h | 5 +++++ include/mbgl/ios/MGLShape.h | 4 ++++ 8 files changed, 60 insertions(+), 5 deletions(-) diff --git a/include/mbgl/ios/MGLAnnotation.h b/include/mbgl/ios/MGLAnnotation.h index e4907d9b943..edb4cf8c59f 100644 --- a/include/mbgl/ios/MGLAnnotation.h +++ b/include/mbgl/ios/MGLAnnotation.h @@ -1,7 +1,7 @@ #import #import -/** The MGLAnnotation protocol is used to provide annotation-related information to a map view. To use this protocol, you adopt it in any custom objects that store or represent annotation data. Each object then serves as the source of information about a single map annotation and provides critical information, such as the annotation’s location on the map. Annotation objects do not provide the visual representation of the annotation but typically coordinate (in conjunction with the map view’s delegate) the creation of an appropriate objects to handle the display. +/** The `MGLAnnotation` protocol is used to provide annotation-related information to a map view. To use this protocol, you adopt it in any custom objects that store or represent annotation data. Each object then serves as the source of information about a single map annotation and provides critical information, such as the annotation’s location on the map. Annotation objects do not provide the visual representation of the annotation but typically coordinate (in conjunction with the map view’s delegate) the creation of an appropriate objects to handle the display. * * An object that adopts this protocol must implement the `coordinate` property. The other methods of this protocol are optional. */ @protocol MGLAnnotation diff --git a/include/mbgl/ios/MGLMapView.h b/include/mbgl/ios/MGLMapView.h index fda4052571e..97b5a286436 100644 --- a/include/mbgl/ios/MGLMapView.h +++ b/include/mbgl/ios/MGLMapView.h @@ -309,9 +309,28 @@ IB_DESIGNABLE * @return The marker symbol to display for the specified annotation or `nil` if you want to display the default symbol. */ - (NSString *)mapView:(MGLMapView *)mapView symbolNameForAnnotation:(id )annotation; +/** Returns the alpha value to use when rendering a shape annotation. Defaults to `1.0`. +* @param mapView The map view rendering the shape annotation. +* @param annotation The annotation being rendered. +* @return An alpha value between `0` and `1.0`. */ - (CGFloat)mapView:(MGLMapView *)mapView alphaForShapeAnnotation:(MGLShape *)annotation; + +/** Returns the stroke color to use when rendering a shape annotation. Defaults to black. +* @param mapView The map view rendering the shape annotation. +* @param annotation The annotation being rendered. +* @return A color to use for the shape outline. */ - (UIColor *)mapView:(MGLMapView *)mapView strokeColorForShapeAnnotation:(MGLShape *)annotation; + +/** Returns the fill color to use when rendering a polygon annotation. Defaults to blue. +* @param mapView The map view rendering the polygon annotation. +* @param annotation The annotation being rendered. +* @return A color to use for the polygon interior. */ - (UIColor *)mapView:(MGLMapView *)mapView fillColorForPolygonAnnotation:(MGLPolygon *)annotation; + +/** Returns the line width to use when rendering a polyline annotation. Defaults to `3.0`. +* @param mapView The map view rendering the polygon annotation. +* @param annotation The annotation being rendered. +* @return A line width for the polyline. */ - (CGFloat)mapView:(MGLMapView *)mapView lineWidthForPolylineAnnotation:(MGLPolyline *)annotation; /** Returns a Boolean value indicating whether the annotation is able to display extra information in a callout bubble. @@ -381,6 +400,8 @@ IB_DESIGNABLE #pragma mark - Tracking the User Location +/** @name Tracking the User Location */ + /** Tells the delegate that the map view will begin tracking the user’s location. * * This method is called when the value of the showsUserLocation property changes to `YES`. diff --git a/include/mbgl/ios/MGLMultiPoint.h b/include/mbgl/ios/MGLMultiPoint.h index acafe6ad2c2..8da956b5391 100644 --- a/include/mbgl/ios/MGLMultiPoint.h +++ b/include/mbgl/ios/MGLMultiPoint.h @@ -3,10 +3,15 @@ #import "MGLShape.h" +/** The `MGLMultiPoint` class is an abstract superclass used to define shapes composed of multiple points. You should not create instances of this class directly. Instead, you should create instances of the `MGLPolyline` or `MGLPolygon` classes. However, you can use the method and properties of this class to access information about the specific points associated with the line or polygon. */ @interface MGLMultiPoint : MGLShape +/** The number of points associated with the shape. (read-only) */ @property (nonatomic, readonly) NSUInteger pointCount; +/** Retrieves one or more coordinates associated with the shape. +* @param coords On input, you must provide a C array of structures large enough to hold the desired number of coordinates. On output, this structure contains the requested coordinate data. +* @param range The range of points you want. The `location` field indicates the first point you are requesting, with `0` being the first point, `1` being the second point, and so on. The `length` field indicates the number of points you want. The array in _`coords`_ must be large enough to accommodate the number of requested coordinates. */ - (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range; @end diff --git a/include/mbgl/ios/MGLOverlay.h b/include/mbgl/ios/MGLOverlay.h index 9bbae9d039d..8abed8bae28 100644 --- a/include/mbgl/ios/MGLOverlay.h +++ b/include/mbgl/ios/MGLOverlay.h @@ -4,11 +4,28 @@ #import "MGLAnnotation.h" #import "MGLTypes.h" +/** The `MGLOverlay` protocol defines a specific type of annotation that represents both a point and an area on a map. Overlay objects are essentially data objects that contain the geographic data needed to represent the map area. For example, overlays can take the form of common shapes such as rectangles and circles. They can also describe polygons and other complex shapes. +* +* You use overlays to layer more sophisticated content on top of a map view. For example, you could use an overlay to show the boundaries of a national park or trace a bus route along city streets. Mapbox GL defines several concrete classes that conform to this protocol and define standard shapes. +* +* Because overlays are also annotations, they have similar usage pattern to annotations. When added to a map view using the `addOverlay:` method, that view detects whenever the overlay’s defined region intersects the visible portion of the map. At that point, the map view asks its delegate to provide a special overlay view to draw the visual representation of the overlay. If you add an overlay to a map view as an annotation instead, it is treated as an annotation with a single point. */ @protocol MGLOverlay +/* The approximate center point of the overlay area. (required) (read-only) +* +* This point is typically set to the center point of the map’s bounding rectangle. It is used as the anchor point for any callouts displayed for the annotation. */ @property (nonatomic, readonly) CLLocationCoordinate2D coordinate; + +/** The cooordinate rectangle that encompasses the overlay. (required) (read-only) +* +* This property contains the smallest rectangle that completely encompasses the overlay. Implementers of this protocol must set this area when implementing their overlay class, and after setting it, you must not change it. */ @property (nonatomic, readonly) MGLMapBounds overlayBounds; +/** Returns a Boolean indicating whether the specified rectangle intersects the receiver’s shape. +* +* You can implement this method to provide more specific bounds checking for an overlay. If you do not implement it, the bounding rectangle is used to detect intersections. +* @param overlayBounds The rectangle to intersect with the receiver’s area. +* @return `YES` if any part of the map rectangle intersects the receiver’s shape or `NO` if it does not. */ - (BOOL)intersectsOverlayBounds:(MGLMapBounds)overlayBounds; @end diff --git a/include/mbgl/ios/MGLPointAnnotation.h b/include/mbgl/ios/MGLPointAnnotation.h index 78308f35317..d3d1a8f0e9a 100644 --- a/include/mbgl/ios/MGLPointAnnotation.h +++ b/include/mbgl/ios/MGLPointAnnotation.h @@ -3,8 +3,10 @@ #import "MGLShape.h" +/** The `MGLPointAnnotation` class defines a concrete annotation object located at a specified point. You can use this class, rather than define your own, in situations where all you want to do is associate a point on the map with a title. */ @interface MGLPointAnnotation : MGLShape +/** The coordinate point of the annotation, specified as a latitude and longitude. */ @property (nonatomic, assign) CLLocationCoordinate2D coordinate; @end diff --git a/include/mbgl/ios/MGLPolygon.h b/include/mbgl/ios/MGLPolygon.h index 0e8648ed7a7..bd6907dbe74 100644 --- a/include/mbgl/ios/MGLPolygon.h +++ b/include/mbgl/ios/MGLPolygon.h @@ -4,13 +4,14 @@ #import "MGLMultiPoint.h" #import "MGLOverlay.h" +/** The `MGLPolygon` class represents a shape consisting of one or more points that define a closed polygon. The points are connected end-to-end in the order they are provided. The first and last points are connected to each other to create the closed shape. */ @interface MGLPolygon : MGLMultiPoint +/** Creates and returns an `MGLPolygon` object from the specified set of coordinates. +* @param coords The array of coordinates defining the shape. The data in this array is copied to the new object. +* @param count The number of items in the _`coords`_ array. +* @return A new polygon object. */ + (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count; -//+ (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords -// count:(NSUInteger)count -// interiorPolygons:(NSArray *)interiorPolygons; - @end diff --git a/include/mbgl/ios/MGLPolyline.h b/include/mbgl/ios/MGLPolyline.h index f9fccf31f7e..a49fae07c6d 100644 --- a/include/mbgl/ios/MGLPolyline.h +++ b/include/mbgl/ios/MGLPolyline.h @@ -4,8 +4,13 @@ #import "MGLMultiPoint.h" #import "MGLOverlay.h" +/** The `MGLPolyline` class represents a shape consisting of one or more points that define connecting line segments. The points are connected end-to-end in the order they are provided. The first and last points are not connected to each other. */ @interface MGLPolyline : MGLMultiPoint +/** Creates and returns an `MGLPolygon` object from the specified set of coordinates. +* @param coords The array of coordinates defining the shape. The data in this array is copied to the new object. +* @param count The number of items in the _`coords`_ array. +* @return A new polyline object. */ + (instancetype)polylineWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count; diff --git a/include/mbgl/ios/MGLShape.h b/include/mbgl/ios/MGLShape.h index 4b171cbc380..6693d2b3026 100644 --- a/include/mbgl/ios/MGLShape.h +++ b/include/mbgl/ios/MGLShape.h @@ -2,9 +2,13 @@ #import "MGLAnnotation.h" +/** The `MGLShape` class is an abstract class that defines the basic properties for all shape-based annotation objects. This class must be subclassed and cannot be used as is. Subclasses are responsible for defining the geometry of the shape and providing an appropriate value for the coordinate property inherited from the `MGLAnnotation` protocol. */ @interface MGLShape : NSObject +/** The title of the shape annotation. The default value of this property is `nil`. */ @property (nonatomic, copy) NSString *title; + +/** The subtitle of the shape annotation. The default value of this property is `nil`. */ @property (nonatomic, copy) NSString *subtitle; @end From c3656fd27bf6c5f551f8ca59e951f87721b97d58 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 1 Jun 2015 19:34:45 -0700 Subject: [PATCH 37/85] Revert "comment out API docs gen for now to try tests" This reverts commit e25a561ce1abcc97331a28840ba203bd3997dc24. --- scripts/ios/package.sh | 46 +++++++++++++++++++++--------------------- 1 file changed, 23 insertions(+), 23 deletions(-) diff --git a/scripts/ios/package.sh b/scripts/ios/package.sh index 5da03697934..35162ce5d16 100755 --- a/scripts/ios/package.sh +++ b/scripts/ios/package.sh @@ -96,26 +96,26 @@ mkdir -p "${OUTPUT}/static/${NAME}.bundle" cp -pv platform/ios/resources/* "${OUTPUT}/static/${NAME}.bundle" cp -prv styles/styles "${OUTPUT}/static/${NAME}.bundle/styles" -# step "Creating API Docs..." -# if [ -z `which appledoc` ]; then -# echo "Unable to find appledoc. Consider installing it from source or Homebrew." -# exit 1 -# fi -# DOCS_OUTPUT="${OUTPUT}/static/Docs" -# DOCS_VERSION=$( git tag -l ios\* --sort -v:refname | sed -n '1p' | sed 's/ios-v//' ) -# README="/tmp/GL-README.md" -# cat ios/README.md > ${README} -# echo >> ${README} -# echo -n "#" >> ${README} -# cat CHANGELOG.md >> ${README} -# appledoc \ -# --output ${DOCS_OUTPUT} \ -# --project-name "Mapbox GL for iOS ${DOCS_VERSION}" \ -# --project-company Mapbox \ -# --create-html \ -# --no-create-docset \ -# --no-install-docset \ -# --company-id com.mapbox \ -# --ignore include/mbgl/ios/private \ -# --index-desc ${README} \ -# include/mbgl/ios +step "Creating API Docs..." +if [ -z `which appledoc` ]; then + echo "Unable to find appledoc. Consider installing it from source or Homebrew." + exit 1 +fi +DOCS_OUTPUT="${OUTPUT}/static/Docs" +DOCS_VERSION=$( git tag -l ios\* --sort -v:refname | sed -n '1p' | sed 's/ios-v//' ) +README="/tmp/GL-README.md" +cat ios/README.md > ${README} +echo >> ${README} +echo -n "#" >> ${README} +cat CHANGELOG.md >> ${README} +appledoc \ + --output ${DOCS_OUTPUT} \ + --project-name "Mapbox GL for iOS ${DOCS_VERSION}" \ + --project-company Mapbox \ + --create-html \ + --no-create-docset \ + --no-install-docset \ + --company-id com.mapbox \ + --ignore include/mbgl/ios/private \ + --index-desc ${README} \ + include/mbgl/ios From b7e26f323d23750035518d4cd212ca8956f23e8a Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Tue, 2 Jun 2015 09:13:56 -0700 Subject: [PATCH 38/85] only notify delegate on actual annotation deselection --- platform/ios/MGLMapView.mm | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index 5bc182beebe..1cf6e6eb786 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -1957,12 +1957,12 @@ - (void)deselectAnnotation:(id )annotation animated:(BOOL)animate // clean up self.selectedAnnotationCalloutView = nil; self.selectedAnnotation = nil; - } - // notify delegate - if ([self.delegate respondsToSelector:@selector(mapView:didDeselectAnnotation:)]) - { - [self.delegate mapView:self didDeselectAnnotation:annotation]; + // notify delegate + if ([self.delegate respondsToSelector:@selector(mapView:didDeselectAnnotation:)]) + { + [self.delegate mapView:self didDeselectAnnotation:annotation]; + } } } From 820d2da203b184fbe369f3e5beb3d68a51c8a1f1 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Tue, 2 Jun 2015 09:35:52 -0700 Subject: [PATCH 39/85] don't allow Cocoa selection of non-point annotations --- platform/ios/MGLMapView.mm | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/platform/ios/MGLMapView.mm b/platform/ios/MGLMapView.mm index 1cf6e6eb786..6e3d5fe6184 100644 --- a/platform/ios/MGLMapView.mm +++ b/platform/ios/MGLMapView.mm @@ -1842,6 +1842,8 @@ - (void)setSelectedAnnotations:(NSArray *)selectedAnnotations assert([firstAnnotation conformsToProtocol:@protocol(MGLAnnotation)]); + if ([firstAnnotation isKindOfClass:[MGLMultiPoint class]]) return; + if ( ! [self viewportBounds].contains(MGLLatLngFromLocationCoordinate2D(firstAnnotation.coordinate))) return; [self selectAnnotation:firstAnnotation animated:NO]; @@ -1851,6 +1853,8 @@ - (void)selectAnnotation:(id )annotation animated:(BOOL)animated { if ( ! annotation) return; + if ([annotation isKindOfClass:[MGLMultiPoint class]]) return; + if ( ! [self viewportBounds].contains(MGLLatLngFromLocationCoordinate2D(annotation.coordinate))) return; if (annotation == self.selectedAnnotation) return; From d6ced9edce2383fd7cfee0ce333f8b1342041749 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Tue, 2 Jun 2015 10:56:24 -0700 Subject: [PATCH 40/85] more proper feature type --- src/mbgl/map/annotation.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 28250ee4dd4..1a944ad6590 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -255,9 +255,21 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, } for (auto& featureTile : featureTiles) { + // determine feature type + FeatureType featureType; + if (type == AnnotationType::Point) { + featureType = FeatureType::Point; + } else if (styleProperties.is()) { + featureType = FeatureType::LineString; + } else if (styleProperties.is()) { + featureType = FeatureType::Polygon; + } else { + throw std::runtime_error("Invalid feature type"); + } + // create tile feature auto feature = std::make_shared( - (type == AnnotationType::Point ? FeatureType::Point : FeatureType::LineString), + featureType, featureTile.second, featureProperties ); From e9eb7dcb43271e271edf76fc861ef6afc888e886 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 08:59:52 -0700 Subject: [PATCH 41/85] add comment about polygon closure --- platform/ios/MGLPolygon.m | 2 ++ 1 file changed, 2 insertions(+) diff --git a/platform/ios/MGLPolygon.m b/platform/ios/MGLPolygon.m index 492980933a7..95a2dfb5eda 100644 --- a/platform/ios/MGLPolygon.m +++ b/platform/ios/MGLPolygon.m @@ -14,6 +14,8 @@ @implementation MGLPolygon + (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count { + // connect first & last if needed + return [[self alloc] initWithCoordinates:coords count:count]; } From c6fee9a36a80e083ceeda8d5a673b430905abe46 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 09:14:12 -0700 Subject: [PATCH 42/85] add former, untouched GeoJSONVT lib --- src/mbgl/util/geojsonvt.cpp | 848 ++++++++++++++++++++++++++++++++++++ src/mbgl/util/geojsonvt.hpp | 273 ++++++++++++ 2 files changed, 1121 insertions(+) create mode 100644 src/mbgl/util/geojsonvt.cpp create mode 100644 src/mbgl/util/geojsonvt.hpp diff --git a/src/mbgl/util/geojsonvt.cpp b/src/mbgl/util/geojsonvt.cpp new file mode 100644 index 00000000000..ca427ebfb01 --- /dev/null +++ b/src/mbgl/util/geojsonvt.cpp @@ -0,0 +1,848 @@ +#include + +#include +#include +#include + +namespace mapbox { namespace util { namespace geojsonvt { + +std::unordered_map Time::activities; + +#pragma mark - Tile + +Tile Tile::createTile(std::vector &features, uint32_t z2, uint32_t tx, uint32_t ty, double tolerance, uint16_t extent, bool noSimplify) { + + Tile tile; + + for (size_t i = 0; i < features.size(); ++i) { + tile.numFeatures++; + addFeature(tile, features[i], z2, tx, ty, tolerance, extent, noSimplify); + } + + return std::move(tile); +} + +void Tile::addFeature(Tile &tile, ProjectedFeature &feature, uint32_t z2, uint32_t tx, uint32_t ty, double tolerance, uint16_t extent, bool noSimplify) { + + ProjectedGeometryContainer *geom = &(feature.geometry.get()); + ProjectedFeatureType type = feature.type; + std::vector transformed; + double sqTolerance = tolerance * tolerance; + ProjectedGeometryContainer ring; + + if (type == ProjectedFeatureType::Point) { + for (size_t i = 0; i < geom->members.size(); ++i) { + ProjectedPoint *p = &(geom->members[i].get()); + transformed.push_back(transformPoint(*p, z2, tx, ty, extent)); + tile.numPoints++; + tile.numSimplified++; + } + } else { + for (size_t i = 0; i < geom->members.size(); ++i) { + ring = geom->members[i].get(); + + if (!noSimplify && ((type == ProjectedFeatureType::LineString && ring.dist < tolerance) || + (type == ProjectedFeatureType::Polygon && ring.area < sqTolerance))) { + tile.numPoints += ring.members.size(); + continue; + } + + TileRing transformedRing; + + for (size_t j = 0; j < ring.members.size(); ++j) { + ProjectedPoint *p = &(ring.members[j].get()); + if (noSimplify || p->z > sqTolerance) { + TilePoint transformedPoint = transformPoint(*p, z2, tx, ty, extent); + transformedRing.points.push_back(transformedPoint); + tile.numSimplified++; + } + tile.numPoints++; + } + + transformed.push_back(transformedRing); + } + } + + if (transformed.size()) { + tile.features.push_back(TileFeature(transformed, type, Tags(feature.tags))); + } + +} + +TilePoint Tile::transformPoint(const ProjectedPoint &p, uint32_t z2, uint32_t tx, uint32_t ty, uint16_t extent) { + + uint16_t x = extent * (p.x * z2 - tx); + uint16_t y = extent * (p.y * z2 - ty); + + return TilePoint(x, y); +} + +#pragma mark - GeoJSONVT + +GeoJSONVT::GeoJSONVT(const std::string &data, uint8_t baseZoom_, uint8_t maxZoom_, uint32_t maxPoints_, double tolerance_, bool debug_) + : baseZoom(baseZoom_), + maxZoom(maxZoom_), + maxPoints(maxPoints_), + tolerance(tolerance_), + debug(debug_) { + + if (this->debug) { + Time::time("preprocess data"); + } + + uint32_t z2 = 1 << this->baseZoom; + + JSDocument deserializedData; + deserializedData.Parse<0>(data.c_str()); + + if (deserializedData.HasParseError()) { + printf("invalid GeoJSON\n"); + return; + } + + std::vector features = Convert::convert(deserializedData, this->tolerance / (z2 * this->extent)); + + if (this->debug) { + Time::timeEnd("preprocess data"); + Time::time("generate tiles up to z" + std::to_string(maxZoom)); + } + + splitTile(features, 0, 0, 0); + + if (this->debug) { + printf("features: %i, points: %i\n", this->tiles[0].numFeatures, this->tiles[0].numPoints); + Time::timeEnd("generate tiles up to z" + std::to_string(maxZoom)); + printf("tiles generated: %i {\n", this->total); + for (const auto &pair : this->stats) { + printf(" z%i: %i\n", pair.first, pair.second); + } + printf("}\n"); + } +} + +void GeoJSONVT::splitTile(std::vector features_, uint8_t z_, uint32_t x_, uint32_t y_, int8_t cz, int32_t cx, int32_t cy) { + + std::queue stack; + stack.emplace(features_, z_, x_, y_); + + while (stack.size()) { + FeatureStackItem set = stack.front(); + stack.pop(); + std::vector features = std::move(set.features); + uint8_t z = set.z; + uint32_t x = set.x; + uint32_t y = set.y; + + uint32_t z2 = 1 << z; + const uint64_t id = toID(z, x, y); + Tile* tile; + double tileTolerance = (z == this->baseZoom ? 0 : this->tolerance / (z2 * this->extent)); + + if (this->tiles.count(id)) { + tile = &this->tiles[id]; + } else { + if (this->debug) { + Time::time("creation"); + } + + this->tiles[id] = std::move(Tile::createTile(features, z2, x, y, tileTolerance, extent, (z == this->baseZoom))); + tile = &this->tiles[id]; + + if (this->debug) { + printf("tile z%i-%i-%i (features: %i, points: %i, simplified: %i\n", z, x, y, + tile->numFeatures, tile->numPoints, tile->numSimplified); + Time::timeEnd("creation"); + + uint8_t key = z; + this->stats[key] = (this->stats.count(key) ? this->stats[key] + 1 : 1); + this->total++; + } + } + + if ((cz < 0 && (z == this->maxZoom || this->tiles[id].numPoints <= this->maxPoints || + isClippedSquare(tile->features, this->extent, this->buffer))) || z == this->baseZoom || z == cz) { + tile->source = std::vector(features); + continue; + } + + if (cz >= 0) { + tile->source = std::vector(features); + } else { + tile->source = {}; + } + + if (this->debug) { + Time::time("clipping"); + } + + double k1 = 0.5 * this->buffer / this->extent; + double k2 = 0.5 - k1; + double k3 = 0.5 + k1; + double k4 = 1 + k1; + + std::vector tl; + std::vector bl; + std::vector tr; + std::vector br; + std::vector left; + std::vector right; + uint32_t m = 0; + bool goLeft = false; + bool goTop = false; + + if (cz >= 0) { + m = 1 << (cz - z); + goLeft = cx / m - x < 0.5; + goTop = cy / m - y < 0.5; + } + + if (cz < 0 || goLeft) { + left = Clip::clip(features, z2, x - k1, x + k3, 0, intersectX); + } + + if (cz < 0 || !goLeft) { + right = Clip::clip(features, z2, x + k2, x + k4, 0, intersectX); + } + + if (left.size()) { + if (cz < 0 || goTop) { + tl = Clip::clip(left, z2, y - k1, y + k3, 1, intersectY); + } + + if (cz < 0 || !goTop) { + bl = Clip::clip(left, z2, y + k2, y + k4, 1, intersectY); + } + } + + if (right.size()) { + if (cz < 0 || goTop) { + tr = Clip::clip(right, z2, y - k1, y + k3, 1, intersectY); + } + + if (cz < 0 || !goTop) { + br = Clip::clip(right, z2, y + k2, y + k4, 1, intersectY); + } + } + + if (this->debug) { + Time::timeEnd("clipping"); + } + + if (tl.size()) stack.emplace(std::move(tl), z + 1, x * 2, y * 2); + if (bl.size()) stack.emplace(std::move(bl), z + 1, x * 2, y * 2 + 1); + if (tr.size()) stack.emplace(std::move(tr), z + 1, x * 2 + 1, y * 2); + if (br.size()) stack.emplace(std::move(br), z + 1, x * 2 + 1, y * 2 + 1); + } +} + +Tile& GeoJSONVT::getTile(uint8_t z, uint32_t x, uint32_t y) { + + std::lock_guard lock(mtx); + + const uint64_t id = toID(z, x, y); + if (this->tiles.count(id)) { + return this->tiles[id]; + } + + if (this->debug) { + printf("drilling down to z%i-%i-%i\n", z, x, y); + } + + uint8_t z0 = z; + uint32_t x0 = x; + uint32_t y0 = y; + Tile *parent = nullptr; + + while (!parent && z0) { + z0--; + x0 = x0 / 2; + y0 = y0 / 2; + const uint64_t checkID = toID(z0, x0, y0); + if (this->tiles.count(checkID)) { + parent = &this->tiles[checkID]; + } + } + + if (this->debug) { + printf("found parent tile z%i-%i-%i\n", z0, x0, y0); + } + + if (parent->source.size()) { + if (isClippedSquare(parent->features, this->extent, this->buffer)) { + return *parent; + } + + if (this->debug) { + Time::time("drilling down"); + } + + splitTile(parent->source, z0, x0, y0, z, x, y); + + if (this->debug) { + Time::timeEnd("drilling down"); + } + } + + return this->tiles[id]; +} + +bool GeoJSONVT::isClippedSquare(const std::vector &features, uint16_t extent_, uint8_t buffer_) const { + + if (features.size() != 1) { + return false; + } + + const TileFeature feature = features.front(); + + if (feature.type != TileFeatureType::Polygon || feature.geometry.size() > 1) { + return false; + } + + const TileRing *ring = &(feature.geometry.front().get()); + + for (size_t i = 0; i < ring->points.size(); ++i) { + const TilePoint *p = &ring->points[i]; + if ((p->x != -buffer_ && p->x != extent_ + buffer_) || + (p->y != -buffer_ && p->y != extent_ + buffer_)) { + return false; + } + } + + return true; +} + +uint64_t GeoJSONVT::toID(uint8_t z, uint32_t x, uint32_t y) { + + return (((1 << z) * y + x) * 32) + z; +} + +ProjectedPoint GeoJSONVT::intersectX(const ProjectedPoint &a, const ProjectedPoint &b, double x) { + + double r1 = x; + double r2 = (x - a.x) * (b.y - a.y) / (b.x - a.x) + a.y; + double r3 = 1; + + return ProjectedPoint(r1, r2, r3); +} + +ProjectedPoint GeoJSONVT::intersectY(const ProjectedPoint &a, const ProjectedPoint &b, double y) { + + double r1 = (y - a.y) * (b.x - a.x) / (b.y - a.y) + a.x; + double r2 = y; + double r3 = 1; + + return ProjectedPoint(r1, r2, r3); +} + +#pragma mark - Convert + +std::vector Convert::convert(const JSDocument &data, double tolerance) { + + std::vector features; + const JSValue &rawType = data["type"]; + + if (std::string(rawType.GetString()) == "FeatureCollection") { + if (data.HasMember("features")) { + const JSValue &rawFeatures = data["features"]; + if (rawFeatures.IsArray()) { + printf("there are %i total features to convert\n", rawFeatures.Size()); + for (rapidjson::SizeType i = 0; i < rawFeatures.Size(); ++i) { + convertFeature(features, rawFeatures[i], tolerance); + } + } + } + } else if (std::string(data["type"].GetString()) == "Feature") { + convertFeature(features, data, tolerance); + } else { + + /* In this case, we want to pass the entire JSON document as the + * value for key 'geometry' in a new JSON object, like so: + * + * convertFeature(features, ["geometry": data], tolerance); (pseudo-code) + * + * Currently this fails due to lack of a proper copy constructor. + * Maybe use move semantics? */ + +// JSValue feature; +// feature.SetObject(); +// feature.AddMember("geometry", data, data.GetAllocator()); +// convertFeature(features, feature, tolerance); + + } + + return std::move(features); +} + +void Convert::convertFeature(std::vector &features, const JSValue &feature, double tolerance) { + + const JSValue &geom = feature["geometry"]; + const JSValue &rawType = geom["type"]; + std::string type { rawType.GetString(), rawType.GetStringLength() }; + Tags tags; + + if (feature.HasMember("properties") && feature["properties"].IsObject()) { + const JSValue &properties = feature["properties"]; + rapidjson::Value::ConstMemberIterator itr = properties.MemberBegin(); + for (; itr != properties.MemberEnd(); ++itr) { + std::string key { itr->name.GetString(), itr->name.GetStringLength() }; + std::string val { itr->value.GetString(), itr->value.GetStringLength() }; + tags[key] = val; + } + } + + if (type == "Point") { + + std::array coordinates = {{ 0, 0 }}; + if (geom.HasMember("coordinates")) { + const JSValue &rawCoordinates = geom["coordinates"]; + if (rawCoordinates.IsArray()) { + coordinates[0] = rawCoordinates[(rapidjson::SizeType)0].GetDouble(); + coordinates[1] = rawCoordinates[(rapidjson::SizeType)1].GetDouble(); + } + } + ProjectedPoint point = projectPoint(LonLat(coordinates)); + + std::vector members = { point }; + ProjectedGeometryContainer geometry(members); + + features.push_back(create(tags, ProjectedFeatureType::Point, geometry)); + + } else if (type == "MultiPoint") { + + std::vector> coordinatePairs; + std::vector points; + if (geom.HasMember("coordinates")) { + const JSValue &rawCoordinatePairs = geom["coordinates"]; + if (rawCoordinatePairs.IsArray()) { + for (rapidjson::SizeType i = 0; i < rawCoordinatePairs.Size(); ++i) { + std::array coordinates = {{ 0, 0 }}; + const JSValue &rawCoordinates = rawCoordinatePairs[i]; + if (rawCoordinates.IsArray()) { + coordinates[0] = rawCoordinates[(rapidjson::SizeType)0].GetDouble(); + coordinates[1] = rawCoordinates[(rapidjson::SizeType)1].GetDouble(); + } + points.push_back(LonLat(coordinates)); + } + } + } + + ProjectedGeometryContainer geometry = project(points); + + features.push_back(create(tags, ProjectedFeatureType::Point, geometry)); + + } else if (type == "LineString") { + + std::vector> coordinatePairs; + std::vector points; + if (geom.HasMember("coordinates")) { + const JSValue &rawCoordinatePairs = geom["coordinates"]; + if (rawCoordinatePairs.IsArray()) { + for (rapidjson::SizeType i = 0; i < rawCoordinatePairs.Size(); ++i) { + std::array coordinates = {{ 0, 0 }}; + const JSValue &rawCoordinates = rawCoordinatePairs[i]; + if (rawCoordinates.IsArray()) { + coordinates[0] = rawCoordinates[(rapidjson::SizeType)0].GetDouble(); + coordinates[1] = rawCoordinates[(rapidjson::SizeType)1].GetDouble(); + } + points.push_back(LonLat(coordinates)); + } + } + } + + ProjectedGeometryContainer geometry({ project(points, tolerance) }); + + features.push_back(create(tags, ProjectedFeatureType::LineString, geometry)); + + } else if (type == "MultiLineString" || type == "Polygon") { + + ProjectedGeometryContainer rings; + if (geom.HasMember("coordinates")) { + const JSValue &rawLines = geom["coordinates"]; + if (rawLines.IsArray()) { + for (rapidjson::SizeType i = 0; i < rawLines.Size(); ++i) { + const JSValue &rawCoordinatePairs = rawLines[i]; + if (rawCoordinatePairs.IsArray()) { + std::vector points; + for (rapidjson::SizeType j = 0; j < rawCoordinatePairs.Size(); ++j) { + std::array coordinates = {{ 0, 0 }}; + const JSValue &rawCoordinates = rawCoordinatePairs[j]; + if (rawCoordinates.IsArray()) { + coordinates[0] = rawCoordinates[(rapidjson::SizeType)0].GetDouble(); + coordinates[1] = rawCoordinates[(rapidjson::SizeType)1].GetDouble(); + } + points.push_back(LonLat(coordinates)); + } + ProjectedGeometryContainer ring = project(points, tolerance); + rings.members.push_back(ring); + } + } + } + } + + ProjectedFeatureType projectedType = (type == "Polygon" ? + ProjectedFeatureType::Polygon : + ProjectedFeatureType::LineString); + + ProjectedGeometryContainer *geometry = &rings; + + features.push_back(create(tags, projectedType, *geometry)); + + printf("features now has %lu items\n", features.size()); + } + + else if (type == "MultiPolygon") { + + ProjectedGeometryContainer rings; + if (geom.HasMember("coordinates")) { + const JSValue &rawPolygons = geom["coordinates"]; + if (rawPolygons.IsArray()) { + for (rapidjson::SizeType i = 0; i < rawPolygons.Size(); ++i) { + std::vector points; + const JSValue &rawLines = rawPolygons[i]; + for (rapidjson::SizeType j = 0; j < rawLines.Size(); ++j) { + std::array coordinates = {{ 0, 0 }}; + const JSValue &rawCoordinatePairs = rawLines[i]; + if (rawCoordinatePairs.IsArray()) { + coordinates[0] = rawCoordinatePairs[(rapidjson::SizeType)0].GetDouble(); + coordinates[1] = rawCoordinatePairs[(rapidjson::SizeType)1].GetDouble(); + } + points.push_back(LonLat(coordinates)); + } + ProjectedGeometryContainer ring = project(points, tolerance); + rings.members.push_back(ring); + } + } + } + + ProjectedGeometryContainer *geometry = &rings; + + features.push_back(create(tags, ProjectedFeatureType::Polygon, *geometry)); + + } else if (type == "GeometryCollection") { + + if (geom.HasMember("geometries")) { + const JSValue &rawGeometries = geom["geometries"]; + if (rawGeometries.IsArray()) { + for (rapidjson::SizeType i = 0; i < rawGeometries.Size(); ++i) { + convertFeature(features, rawGeometries[i], tolerance); + } + } + } + + } else { + + printf("unsupported GeoJSON type: %s\n", geom["type"].GetString()); + + } +} + +ProjectedFeature Convert::create(Tags tags, ProjectedFeatureType type, ProjectedGeometry geometry) { + + ProjectedFeature feature(geometry, type, tags); + calcBBox(feature); + + return std::move(feature); +} + +ProjectedGeometryContainer Convert::project(const std::vector &lonlats, double tolerance) { + + ProjectedGeometryContainer projected; + for (size_t i = 0; i < lonlats.size(); ++i) { + projected.members.push_back(projectPoint(lonlats[i])); + } + if (tolerance) { + Simplify::simplify(projected, tolerance); + calcSize(projected); + } + + return std::move(projected); +} + +ProjectedPoint Convert::projectPoint(const LonLat &p_) { + + double sine = std::sin(p_.lat * M_PI / 180); + double x = p_.lon / 360 + 0.5; + double y = 0.5 - 0.25 * std::log((1 + sine) / (1 - sine)) / M_PI; + + return ProjectedPoint(x, y, 0); +} + +void Convert::calcSize(ProjectedGeometryContainer &geometryContainer) { + + double area = 0, dist = 0; + ProjectedPoint a, b; + + for (size_t i = 0; i < geometryContainer.members.size() - 1; ++i) { + a = (b.isValid() ? b : geometryContainer.members[i].get()); + b = geometryContainer.members[i + 1].get(); + + area += a.x * b.y - b.x * a.y; + dist += std::abs(b.x - a.x) + std::abs(b.y - a.y); + } + + geometryContainer.area = std::abs(area / 2); + geometryContainer.dist = dist; +} + +void Convert::calcBBox(ProjectedFeature &feature) { + + ProjectedGeometryContainer *geometry = &(feature.geometry.get()); + ProjectedPoint *minPoint = &(feature.minPoint); + ProjectedPoint *maxPoint = &(feature.maxPoint); + + if (feature.type == ProjectedFeatureType::Point) { + calcRingBBox(*minPoint, *maxPoint, *geometry); + } else { + for (size_t i = 0; i < geometry->members.size(); ++i) { + ProjectedGeometryContainer *featureGeometry = &(geometry->members[i].get()); + calcRingBBox(*minPoint, *maxPoint, *featureGeometry); + } + } +} + +void Convert::calcRingBBox(ProjectedPoint &minPoint, ProjectedPoint &maxPoint, const ProjectedGeometryContainer &geometry) { + + for (size_t i = 0; i < geometry.members.size(); ++i) { + const ProjectedPoint *p = &(geometry.members[i].get()); + minPoint.x = std::min(p->x, minPoint.x); + maxPoint.x = std::max(p->x, maxPoint.x); + minPoint.y = std::min(p->y, minPoint.y); + maxPoint.y = std::max(p->y, maxPoint.y); + } +} + +#pragma mark - Simplify + +void Simplify::simplify(ProjectedGeometryContainer &points, double tolerance) { + + const double sqTolerance = tolerance * tolerance; + const size_t len = points.members.size(); + size_t first = 0; + size_t last = len - 1; + std::stack stack; + double maxSqDist = 0; + double sqDist = 0; + size_t index = 0; + + points.members[first].get().z = 1; + points.members[last].get().z = 1; + + while (last) { + + maxSqDist = 0; + + for (size_t i = (first + 1); i < last; ++i) { + sqDist = getSqSegDist(points.members[i].get(), + points.members[first].get(), + points.members[last].get()); + + if (sqDist > maxSqDist) { + index = i; + maxSqDist = sqDist; + } + } + + if (maxSqDist > sqTolerance) { + points.members[index].get().z = maxSqDist; + stack.push(first); + stack.push(index); + stack.push(index); + stack.push(last); + } + + if (stack.size()) { + last = stack.top(); + stack.pop(); + } else { + last = 0; + } + + if (stack.size()) { + first = stack.top(); + stack.pop(); + } else { + first = 0; + } + } +} + +double Simplify::getSqSegDist(const ProjectedPoint &p, const ProjectedPoint &a, const ProjectedPoint &b) { + + double x = a.x; + double y = a.y; + double dx = b.x - a.x; + double dy = b.y - a.y; + + if (dx || dy) { + + 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) { + x += dx * t; + y += dy * t; + } + } + + dx = p.x - x; + dy = p.y - y; + + return dx * dx + dy * dy; +} + +#pragma mark - Clip + +std::vector Clip::clip(const std::vector features, uint32_t scale, double k1, double k2, uint8_t axis, ProjectedPoint (*intersect)(const ProjectedPoint&, const ProjectedPoint&, double)) { + + k1 /= scale; + k2 /= scale; + + std::vector clipped; + + for (size_t i = 0; i < features.size(); ++i) { + + const ProjectedFeature feature = features[i]; + const ProjectedGeometry geometry = feature.geometry; + const ProjectedFeatureType type = feature.type; + double min = 0; + double max = 0; + + if (feature.minPoint.isValid()) { + min = (axis == 0 ? feature.minPoint.x : feature.minPoint.y); + max = (axis == 0 ? feature.maxPoint.x : feature.maxPoint.y); + + if (min >= k1 && max <= k2) { + clipped.push_back(feature); + continue; + } else if (min > k2 || max < k1) { + continue; + } + } + + ProjectedGeometryContainer slices; + + if (type == ProjectedFeatureType::Point) { + slices = clipPoints(geometry.get(), k1, k2, axis); + } else { + slices = clipGeometry(geometry.get(), k1, k2, axis, intersect, (type == ProjectedFeatureType::Polygon)); + } + + if (slices.members.size()) { + clipped.push_back(ProjectedFeature(slices, type, features[i].tags)); + } + } + + return std::move(clipped); +} + +ProjectedGeometryContainer Clip::clipPoints(ProjectedGeometryContainer geometry, double k1, double k2, uint8_t axis) { + + ProjectedGeometryContainer slice; + + for (size_t i = 0; i < geometry.members.size(); ++i) { + ProjectedPoint *a = &(geometry.members[i].get()); + double ak = (axis == 0 ? a->x : a->y); + + if (ak >= k1 && ak <= k2) { + slice.members.push_back(*a); + } + } + + return std::move(slice); +} + +ProjectedGeometryContainer Clip::clipGeometry(ProjectedGeometryContainer geometry, double k1, double k2, uint8_t axis, ProjectedPoint (*intersect)(const ProjectedPoint&, const ProjectedPoint&, double), bool closed) { + + ProjectedGeometryContainer slices; + + for (size_t i = 0; i < geometry.members.size(); ++i) { + + double ak = 0; + double bk = 0; + ProjectedPoint b; + const ProjectedGeometryContainer *points = &(geometry.members[i].get()); + const double area = points->area; + const double dist = points->dist; + const size_t len = points->members.size(); + ProjectedPoint a; + + ProjectedGeometryContainer slice; + + for (size_t j = 0; j < (len - 1); ++j) { + a = (b.isValid() ? b : points->members[j].get()); + b = points->members[j + 1].get(); + ak = (bk ? bk : (axis == 0 ? a.x : a.y)); + bk = (axis == 0 ? b.x : b.y); + + if (ak < k1) { + if (bk > k2) { + slice.members.push_back(intersect(a, b, k1)); + slice.members.push_back(intersect(a, b, k2)); + if (!closed) { + slice = newSlice(slices, slice, area, dist); + } + } else if (bk >= k1) { + slice.members.push_back(intersect(a, b, k1)); + } + } else if (ak > k2) { + if (bk < k1) { + slice.members.push_back(intersect(a, b, k2)); + slice.members.push_back(intersect(a, b, k1)); + if (!closed) { + slice = newSlice(slices, slice, area, dist); + } + } else if (bk <= k2) { + slice.members.push_back(intersect(a, b, k2)); + } + } else { + slice.members.push_back(a); + + if (bk < k1) { + slice.members.push_back(intersect(a, b, k1)); + if (!closed) { + slice = newSlice(slices, slice, area, dist); + } + } else if (bk > k2) { + slice.members.push_back(intersect(a, b, k2)); + if (!closed) { + slice = newSlice(slices, slice, area, dist); + } + } + } + } + + a = points->members[len - 1].get(); + ak = (axis == 0 ? a.x : a.y); + + if (ak >= k1 && ak <= k2) { + slice.members.push_back(a); + } + + if (closed && slice.members.size()) { + const ProjectedPoint *first = &(slice.members[0].get()); + const ProjectedPoint *last = &(slice.members[slice.members.size() - 1].get()); + if (!first->isEqualToPoint(last)) { + slice.members.push_back(ProjectedPoint(first->x, first->y, first->z)); + } + } + + newSlice(slices, slice, area, dist); + } + + return std::move(slices); +} + +ProjectedGeometryContainer Clip::newSlice(ProjectedGeometryContainer &slices, ProjectedGeometryContainer &slice, double area, double dist) { + + if (slice.members.size()) { + slice.area = area; + slice.dist = dist; + slices.members.push_back(slice); + } + + return ProjectedGeometryContainer(); +} + +} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ diff --git a/src/mbgl/util/geojsonvt.hpp b/src/mbgl/util/geojsonvt.hpp new file mode 100644 index 00000000000..64a9238b72e --- /dev/null +++ b/src/mbgl/util/geojsonvt.hpp @@ -0,0 +1,273 @@ +#ifndef MAPBOX_UTIL_GEOJSONVT +#define MAPBOX_UTIL_GEOJSONVT + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace mapbox { namespace util { namespace geojsonvt { + +#pragma mark - + +class Time { +public: + inline static void time(std::string activity) { + Time::activities[activity] = clock(); + } + + inline static void timeEnd(std::string activity) { + printf("%s: %fms\n", activity.c_str(), double(clock() - Time::activities[activity]) / (CLOCKS_PER_SEC / 1000)); + } + +private: + static std::unordered_map activities; +}; + +#pragma mark - + +struct LonLat { + LonLat(std::array coordinates) + : lon(coordinates[0]), lat(coordinates[1]) {} + + LonLat(double lon_, double lat_) + : lon(lon_), lat(lat_) {} + + double lon; + double lat; +}; + +#pragma mark - + +class ProjectedPoint; +class ProjectedGeometryContainer; + +using ProjectedGeometry = mapbox::util::variant; + +#pragma mark - + +class ProjectedPoint { +public: + ProjectedPoint(double x_, double y_, double z_) + : x(x_), y(y_), z(z_) {} + ProjectedPoint() + : x(-1), y(-1), z(-1) {} + + inline bool isValid() const { return (x >= 0 && y >= 0 && z >= 0); } + inline bool isEqualToPoint(const ProjectedPoint *p2) const { return (x == p2->x && y == p2->y && z == p2->z); } + +public: + double x = -1; + double y = -1; + double z = -1; +}; + +#pragma mark - + +using JSDocument = rapidjson::Document; +using JSValue = rapidjson::Value; + +#pragma mark - + +class ProjectedGeometryContainer { +public: + ProjectedGeometryContainer() {} + ProjectedGeometryContainer(std::vector members_) + : members(members_) {} + +public: + std::vector members; + double area = 0; + double dist = 0; +}; + +#pragma mark - + +using Tags = std::map; + +#pragma mark - + +enum class ProjectedFeatureType: uint8_t { + Point = 1, + LineString = 2, + Polygon = 3 +}; + +#pragma mark - + +class ProjectedFeature { +public: + ProjectedFeature(ProjectedGeometry geometry_, ProjectedFeatureType type_, Tags tags_) + : geometry(geometry_), type(type_), tags(tags_) {} + +public: + ProjectedGeometry geometry; + ProjectedFeatureType type; + Tags tags; + ProjectedPoint minPoint = ProjectedPoint(1, 1, 0); + ProjectedPoint maxPoint = ProjectedPoint(0, 0, 0); +}; + +#pragma mark - + +class TilePoint; +class TileRing; + +using TileGeometry = mapbox::util::variant; + +#pragma mark - + +class TilePoint { +public: + TilePoint(uint16_t x_, uint16_t y_) + : x(x_), y(y_) {} + +public: + const uint16_t x = 0; + const uint16_t y = 0; +}; + +#pragma mark - + +class TileRing { +public: + std::vector points; +}; + +#pragma mark - + +typedef ProjectedFeatureType TileFeatureType; + +#pragma mark - + +class TileFeature { +public: + TileFeature(std::vector geometry_, TileFeatureType type_, Tags tags_) + : geometry(geometry_), type(type_), tags(tags_) {} + +public: + std::vector geometry; + TileFeatureType type; + Tags tags; +}; + +#pragma mark - + +class Tile { +public: + static Tile createTile(std::vector &features, uint32_t z2, uint32_t tx, uint32_t ty, double tolerance, uint16_t extent, bool noSimplify); + + static void addFeature(Tile &tile, ProjectedFeature &feature, uint32_t z2, uint32_t tx, uint32_t ty, double tolerance, uint16_t extent, bool noSimplify); + + inline operator bool() const { return this->numPoints > 0; } + +private: + static TilePoint transformPoint(const ProjectedPoint &p, uint32_t z2, uint32_t tx, uint32_t ty, uint16_t extent); + +public: + std::vector features; + uint32_t numPoints = 0; + uint32_t numSimplified = 0; + uint32_t numFeatures = 0; + std::vector source; +}; + +#pragma mark - + +class GeoJSONVT { +public: + GeoJSONVT(const std::string &data, uint8_t baseZoom = 14, uint8_t maxZoom = 4, uint32_t maxPoints = 100, double tolerance = 3, bool debug = true); + + Tile& getTile(uint8_t z, uint32_t x, uint32_t y); + +private: + void splitTile(std::vector features, uint8_t z, uint32_t x, uint32_t y, int8_t cz = -1, int32_t cx = -1, int32_t cy = -1); + + bool isClippedSquare(const std::vector &features, uint16_t extent, uint8_t buffer) const; + + static uint64_t toID(uint8_t z, uint32_t x, uint32_t y); + + static ProjectedPoint intersectX(const ProjectedPoint &a, const ProjectedPoint &b, double x); + + static ProjectedPoint intersectY(const ProjectedPoint &a, const ProjectedPoint &b, double y); + + struct FeatureStackItem { + std::vector features; + uint8_t z; + uint32_t x; + uint32_t y; + + FeatureStackItem(std::vector features_, uint8_t z_, uint32_t x_, uint32_t y_) + : features(features_), z(z_), x(x_), y(y_) {} + }; + +private: + std::mutex mtx; + uint8_t baseZoom; + uint8_t maxZoom; + uint32_t maxPoints; + double tolerance; + bool debug; + uint16_t extent = 4096; + uint8_t buffer = 64; + std::map tiles; + std::map stats; + uint16_t total = 0; +}; + +#pragma mark - + +class Convert { +public: + static std::vector convert(const JSDocument &data, double tolerance); + +private: + static void convertFeature(std::vector &features, const JSValue &feature, double tolerance); + + static ProjectedFeature create(Tags tags, ProjectedFeatureType type, ProjectedGeometry geometry); + + static ProjectedGeometryContainer project(const std::vector &lonlats, double tolerance = 0); + + static ProjectedPoint projectPoint(const LonLat &p); + + static void calcSize(ProjectedGeometryContainer &geometryContainer); + + static void calcBBox(ProjectedFeature &feature); + + static void calcRingBBox(ProjectedPoint &minPoint, ProjectedPoint &maxPoint, const ProjectedGeometryContainer &geometry); +}; + +#pragma mark - + +class Simplify { +public: + static void simplify(ProjectedGeometryContainer &points, double tolerance); + +private: + static double getSqSegDist(const ProjectedPoint &p, const ProjectedPoint &a, const ProjectedPoint &b); +}; + +#pragma mark - + +class Clip { +public: + static std::vector clip(std::vector features, uint32_t scale, double k1, double k2, uint8_t axis, ProjectedPoint (*intersect)(const ProjectedPoint&, const ProjectedPoint&, double)); + +private: + static ProjectedGeometryContainer clipPoints(ProjectedGeometryContainer geometry, double k1, double k2, uint8_t axis); + + static ProjectedGeometryContainer clipGeometry(ProjectedGeometryContainer geometry, double k1, double k2, uint8_t axis, ProjectedPoint (*intersect)(const ProjectedPoint&, const ProjectedPoint&, double), bool closed); + + static ProjectedGeometryContainer newSlice(ProjectedGeometryContainer &slices, ProjectedGeometryContainer &slice, double area, double dist); +}; + +} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ + +#endif // MAPBOX_UTIL_GEOJSONVT From 36967187feae72d72cb2f2a0844c4d1a7de64244 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 09:16:27 -0700 Subject: [PATCH 43/85] GeoJSONVT: split out GeoJSON parsing to allow direct feature passing --- src/mbgl/util/geojsonvt.cpp | 34 ++++++++++++++++++++++------------ src/mbgl/util/geojsonvt.hpp | 4 +++- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/src/mbgl/util/geojsonvt.cpp b/src/mbgl/util/geojsonvt.cpp index ca427ebfb01..f7333292cc7 100644 --- a/src/mbgl/util/geojsonvt.cpp +++ b/src/mbgl/util/geojsonvt.cpp @@ -79,35 +79,45 @@ TilePoint Tile::transformPoint(const ProjectedPoint &p, uint32_t z2, uint32_t tx #pragma mark - GeoJSONVT -GeoJSONVT::GeoJSONVT(const std::string &data, uint8_t baseZoom_, uint8_t maxZoom_, uint32_t maxPoints_, double tolerance_, bool debug_) - : baseZoom(baseZoom_), - maxZoom(maxZoom_), - maxPoints(maxPoints_), - tolerance(tolerance_), - debug(debug_) { +std::vector GeoJSONVT::convertFeatures(const std::string &data, uint8_t baseZoom, double tolerance, bool debug) { - if (this->debug) { + if (debug) { Time::time("preprocess data"); } - uint32_t z2 = 1 << this->baseZoom; + uint32_t z2 = 1 << baseZoom; JSDocument deserializedData; deserializedData.Parse<0>(data.c_str()); if (deserializedData.HasParseError()) { printf("invalid GeoJSON\n"); - return; + return std::vector(); } - std::vector features = Convert::convert(deserializedData, this->tolerance / (z2 * this->extent)); + const uint16_t extent = 4096; - if (this->debug) { + std::vector features = Convert::convert(deserializedData, tolerance / (z2 * extent)); + + if (debug) { Time::timeEnd("preprocess data"); + } + + return features; +} + +GeoJSONVT::GeoJSONVT(const std::vector& features_, uint8_t baseZoom_, uint8_t maxZoom_, uint32_t maxPoints_, double tolerance_, bool debug_) + : baseZoom(baseZoom_), + maxZoom(maxZoom_), + maxPoints(maxPoints_), + tolerance(tolerance_), + debug(debug_) { + + if (this->debug) { Time::time("generate tiles up to z" + std::to_string(maxZoom)); } - splitTile(features, 0, 0, 0); + splitTile(features_, 0, 0, 0); if (this->debug) { printf("features: %i, points: %i\n", this->tiles[0].numFeatures, this->tiles[0].numPoints); diff --git a/src/mbgl/util/geojsonvt.hpp b/src/mbgl/util/geojsonvt.hpp index 64a9238b72e..b68c16d847d 100644 --- a/src/mbgl/util/geojsonvt.hpp +++ b/src/mbgl/util/geojsonvt.hpp @@ -183,7 +183,9 @@ class Tile { class GeoJSONVT { public: - GeoJSONVT(const std::string &data, uint8_t baseZoom = 14, uint8_t maxZoom = 4, uint32_t maxPoints = 100, double tolerance = 3, bool debug = true); + static std::vector convertFeatures(const std::string &data, uint8_t baseZoom = 14, double tolerance = 3, bool debug = true); + + GeoJSONVT(const std::vector& features_, uint8_t baseZoom = 14, uint8_t maxZoom = 4, uint32_t maxPoints = 100, double tolerance = 3, bool debug = true); Tile& getTile(uint8_t z, uint32_t x, uint32_t y); From 7e0c53a977e41aea87d6d861758a12f10d7a7182 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 09:19:54 -0700 Subject: [PATCH 44/85] GeoJSONVT: expose conversion/projection functions for callers --- src/mbgl/util/geojsonvt.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mbgl/util/geojsonvt.hpp b/src/mbgl/util/geojsonvt.hpp index b68c16d847d..f02217c08e7 100644 --- a/src/mbgl/util/geojsonvt.hpp +++ b/src/mbgl/util/geojsonvt.hpp @@ -230,13 +230,13 @@ class Convert { public: static std::vector convert(const JSDocument &data, double tolerance); -private: - static void convertFeature(std::vector &features, const JSValue &feature, double tolerance); - static ProjectedFeature create(Tags tags, ProjectedFeatureType type, ProjectedGeometry geometry); static ProjectedGeometryContainer project(const std::vector &lonlats, double tolerance = 0); +private: + static void convertFeature(std::vector &features, const JSValue &feature, double tolerance); + static ProjectedPoint projectPoint(const LonLat &p); static void calcSize(ProjectedGeometryContainer &geometryContainer); From 1ba430ae0565072304cc38e6d42aad93bd0d672b Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 09:30:13 -0700 Subject: [PATCH 45/85] GeoJSONVT: temporarily(?) get rid of mutex --- src/mbgl/util/geojsonvt.cpp | 2 +- src/mbgl/util/geojsonvt.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mbgl/util/geojsonvt.cpp b/src/mbgl/util/geojsonvt.cpp index f7333292cc7..c700c60e12e 100644 --- a/src/mbgl/util/geojsonvt.cpp +++ b/src/mbgl/util/geojsonvt.cpp @@ -247,7 +247,7 @@ void GeoJSONVT::splitTile(std::vector features_, uint8_t z_, u Tile& GeoJSONVT::getTile(uint8_t z, uint32_t x, uint32_t y) { - std::lock_guard lock(mtx); +// std::lock_guard lock(mtx); const uint64_t id = toID(z, x, y); if (this->tiles.count(id)) { diff --git a/src/mbgl/util/geojsonvt.hpp b/src/mbgl/util/geojsonvt.hpp index f02217c08e7..6c66d0e757c 100644 --- a/src/mbgl/util/geojsonvt.hpp +++ b/src/mbgl/util/geojsonvt.hpp @@ -211,7 +211,7 @@ class GeoJSONVT { }; private: - std::mutex mtx; +// std::mutex mtx; uint8_t baseZoom; uint8_t maxZoom; uint32_t maxPoints; From 9ba634f584780a0b57aee057448595ea289cc186 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 09:32:14 -0700 Subject: [PATCH 46/85] GeoJSONVT: fix rounding bug --- src/mbgl/util/geojsonvt.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mbgl/util/geojsonvt.cpp b/src/mbgl/util/geojsonvt.cpp index c700c60e12e..731fc8a1f80 100644 --- a/src/mbgl/util/geojsonvt.cpp +++ b/src/mbgl/util/geojsonvt.cpp @@ -202,8 +202,8 @@ void GeoJSONVT::splitTile(std::vector features_, uint8_t z_, u if (cz >= 0) { m = 1 << (cz - z); - goLeft = cx / m - x < 0.5; - goTop = cy / m - y < 0.5; + goLeft = double(cx) / double(m) - double(x) < 0.5; + goTop = double(cy) / double(m) - double(y) < 0.5; } if (cz < 0 || goLeft) { From 9f25e6fb1ce511269fab4739fbd6702f4f9b3046 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 09:34:31 -0700 Subject: [PATCH 47/85] split out shape handling to GeoJSONVT shape tilers --- src/mbgl/map/annotation.cpp | 217 ++++++++++++++++++++++-------------- src/mbgl/map/annotation.hpp | 4 + 2 files changed, 136 insertions(+), 85 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 1a944ad6590..1f38be67d8a 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -194,124 +194,171 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, auto anno_it = annotations.emplace(annotationID, std::make_unique(type, segments, styleProperties)); + std::unordered_set affectedTiles; + if (type == AnnotationType::Shape) { + orderedShapeAnnotations.push_back(annotationID); - } - // side length of map at max zoom - uint32_t z2 = 1 << maxZoom; + using namespace mapbox::util::geojsonvt; - const uint16_t extent = 4096; + const uint32_t z2 = 1 << 14; + const double baseTolerance = 3; + const uint16_t extent = 4096; - uint32_t x = 0; - uint32_t y = 0; + const double tolerance = baseTolerance / (z2 * extent); - std::unordered_set affectedTiles; + ProjectedGeometryContainer rings; + + std::vector points; + + for (size_t i = 0; i < segments[0].size(); ++i) { + points.push_back(LonLat(segments[0][i].longitude, segments[0][i].latitude)); + } + + ProjectedFeatureType featureType; + + if (styleProperties.is()) { + featureType = ProjectedFeatureType::LineString; + } else if (styleProperties.is()) { + featureType = ProjectedFeatureType::Polygon; + + if (points.front().lon != points.back().lon || points.front().lat != points.back().lat) { + points.push_back(LonLat(points.front().lon, points.front().lat)); + } + } - for (int8_t z = maxZoom; z >= 0; z--) { + ProjectedGeometryContainer ring = Convert::project(points, tolerance); - std::unordered_map featureTiles; + rings.members.push_back(ring); - if (type == AnnotationType::Point) { - auto& pp = projectedFeature[0][0]; + std::vector features; - x = pp.x * z2; - y = pp.y * z2; + features.push_back(Convert::create(Tags(), + (styleProperties.is() ? + ProjectedFeatureType::LineString : + ProjectedFeatureType::Polygon), + rings)); - const Coordinate coordinate(extent * (pp.x * z2 - x), extent * (pp.y * z2 - y)); + shapeTilers.emplace(annotationID, mapbox::util::geojsonvt::GeoJSONVT(features)); - GeometryCollection geometries = {{ {{ coordinate }} }}; + // FIXME: need to expire tiles - featureTiles.emplace(TileID(z, x, y), geometries); - } else { - for (size_t l = 0; l < projectedFeature.size(); ++l) { + } else { + + // side length of map at max zoom + uint32_t z2 = 1 << maxZoom; + + const uint16_t extent = 4096; + + uint32_t x = 0; + uint32_t y = 0; + + for (int8_t z = maxZoom; z >= 0; z--) { + + std::unordered_map featureTiles; + + if (type == AnnotationType::Point) { + auto& pp = projectedFeature[0][0]; + + x = pp.x * z2; + y = pp.y * z2; + + const Coordinate coordinate(extent * (pp.x * z2 - x), extent * (pp.y * z2 - y)); + + GeometryCollection geometries = {{ {{ coordinate }} }}; + + featureTiles.emplace(TileID(z, x, y), geometries); + } else { + for (size_t l = 0; l < projectedFeature.size(); ++l) { - std::vector line; + std::vector line; - for (size_t p = 0; p < projectedFeature[l].size(); ++p) { + for (size_t p = 0; p < projectedFeature[l].size(); ++p) { - auto& pp = projectedFeature[l][p]; + auto& pp = projectedFeature[l][p]; - x = pp.x * z2; - y = pp.y * z2; + x = pp.x * z2; + y = pp.y * z2; - const Coordinate coordinate(extent * (pp.x * z2 - x), extent * (pp.y * z2 - y)); + const Coordinate coordinate(extent * (pp.x * z2 - x), extent * (pp.y * z2 - y)); - auto tile_it = featureTiles.find(TileID(z, x, y)); + auto tile_it = featureTiles.find(TileID(z, x, y)); - if (tile_it != featureTiles.end()) { - GeometryCollection& geometries = featureTiles.find(TileID(z, x, y))->second; - if (geometries.size()) { - geometries.back().push_back(coordinate); + if (tile_it != featureTiles.end()) { + GeometryCollection& geometries = featureTiles.find(TileID(z, x, y))->second; + if (geometries.size()) { + geometries.back().push_back(coordinate); + } else { + geometries.push_back({{ coordinate }}); + } } else { - geometries.push_back({{ coordinate }}); + GeometryCollection geometries = {{ {{ coordinate }} }}; + featureTiles.emplace(TileID(z, x, y), geometries); } - } else { - GeometryCollection geometries = {{ {{ coordinate }} }}; - featureTiles.emplace(TileID(z, x, y), geometries); } } } - } - - for (auto& featureTile : featureTiles) { - // determine feature type - FeatureType featureType; - if (type == AnnotationType::Point) { - featureType = FeatureType::Point; - } else if (styleProperties.is()) { - featureType = FeatureType::LineString; - } else if (styleProperties.is()) { - featureType = FeatureType::Polygon; - } else { - throw std::runtime_error("Invalid feature type"); - } - // create tile feature - auto feature = std::make_shared( - featureType, - featureTile.second, - featureProperties - ); + for (auto& featureTile : featureTiles) { + // determine feature type + FeatureType featureType; + if (type == AnnotationType::Point) { + featureType = FeatureType::Point; + } else if (styleProperties.is()) { + featureType = FeatureType::LineString; + } else if (styleProperties.is()) { + featureType = FeatureType::Polygon; + } else { + throw std::runtime_error("Invalid feature type"); + } - // check for tile & create if necessary - auto tile_pos = tiles.emplace(featureTile.first, - std::make_pair(std::unordered_set({ annotationID }), - std::make_unique())); + // create tile feature + auto feature = std::make_shared( + featureType, + featureTile.second, + featureProperties + ); + + // check for tile & create if necessary + auto tile_pos = tiles.emplace(featureTile.first, + std::make_pair(std::unordered_set({ annotationID }), + std::make_unique())); + + // check for annotation layer & create if necessary + util::ptr layer; + std::string layerID = ""; + if (type == AnnotationType::Point) { + layerID = PointLayerID; + } else { + layerID = ShapeLayerID + "." + std::to_string(annotationID); + } + if (tile_pos.second || tile_pos.first->second.second->getMutableLayer(layerID) == nullptr) { + layer = std::make_shared(); + tile_pos.first->second.second->addLayer(layerID, layer); + } else { + layer = tile_pos.first->second.second->getMutableLayer(layerID); - // check for annotation layer & create if necessary - util::ptr layer; - std::string layerID = ""; - if (type == AnnotationType::Point) { - layerID = PointLayerID; - } else { - layerID = ShapeLayerID + "." + std::to_string(annotationID); - } - if (tile_pos.second || tile_pos.first->second.second->getMutableLayer(layerID) == nullptr) { - layer = std::make_shared(); - tile_pos.first->second.second->addLayer(layerID, layer); - } else { - layer = tile_pos.first->second.second->getMutableLayer(layerID); + // associate annotation with tile + tile_pos.first->second.first.insert(annotationID); + } - // associate annotation with tile - tile_pos.first->second.first.insert(annotationID); - } + // add feature to layer + layer->addFeature(feature); - // add feature to layer - layer->addFeature(feature); + // Record annotation association with tile and tile feature. This is used to determine stale tiles, + // as well as to remove the feature from the tile upon annotation deletion. + anno_it.first->second->tileFeatures.emplace(featureTile.first, std::weak_ptr(feature)); - // Record annotation association with tile and tile feature. This is used to determine stale tiles, - // as well as to remove the feature from the tile upon annotation deletion. - anno_it.first->second->tileFeatures.emplace(featureTile.first, std::weak_ptr(feature)); + // track affected tile + affectedTiles.insert(featureTile.first); + } - // track affected tile - affectedTiles.insert(featureTile.first); + // get ready for the next-lower zoom number + z2 /= 2; + x /= 2; + y /= 2; } - - // get ready for the next-lower zoom number - z2 /= 2; - x /= 2; - y /= 2; } return affectedTiles; diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp index 3b97c171738..c7a54540144 100644 --- a/src/mbgl/map/annotation.hpp +++ b/src/mbgl/map/annotation.hpp @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -27,6 +28,8 @@ class MapData; using AnnotationsProperties = std::unordered_map>; +using GeoJSONVT = mapbox::util::geojsonvt::GeoJSONVT; + class Annotation : private util::noncopyable { friend class AnnotationManager; public: @@ -99,6 +102,7 @@ class AnnotationManager : private util::noncopyable { std::unordered_map> annotations; std::vector orderedShapeAnnotations; std::unordered_map, std::unique_ptr>, TileID::Hash> tiles; + std::unordered_map shapeTilers; std::unordered_set staleTiles; uint32_t nextID_ = 0; }; From 3239b84d47a91b2e0e6dfda13f067d4e0d642d73 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 09:35:09 -0700 Subject: [PATCH 48/85] build up annotation render tiles at point of need --- src/mbgl/map/annotation.cpp | 76 +++++++++++++++++++++++++++++++++++-- 1 file changed, 72 insertions(+), 4 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 1f38be67d8a..9ffda20ca60 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -541,11 +541,79 @@ LatLngBounds AnnotationManager::getBoundsForAnnotations(const AnnotationIDs& ids const LiveTile* AnnotationManager::getTile(const TileID& id) { std::lock_guard lock(mtx); - const auto tile_it = tiles.find(id); - if (tile_it != tiles.end()) { - return tile_it->second.second.get(); + // look up any existing annotation tile + LiveTile *renderTile = nullptr; + const auto tile_lookup_it = tiles.find(id); + if (tile_lookup_it != tiles.end()) { + // it exists and may have annotations already + renderTile = tile_lookup_it->second.second.get(); + } else if (orderedShapeAnnotations.size()) { + // it needs created, but only for on-demand shapes + renderTile = tiles.emplace(id, + std::make_pair(std::unordered_set(), std::make_unique()) + ).first->second.second.get(); } - return nullptr; + + if (renderTile != nullptr && orderedShapeAnnotations.size()) { + + // create shape tile layers from GeoJSONVT queries + for (auto& tiler_it : shapeTilers) { + const auto annotationID = tiler_it.first; + const std::string layerID = ShapeLayerID + "." + std::to_string(annotationID); + + // check for existing render layer + auto renderLayer = renderTile->getMutableLayer(layerID); + + if (renderLayer == nullptr) { + // we might need to create a tile layer for this shape + const auto& shapeTile = tiler_it.second.getTile(id.z, id.x, id.y); + + if (shapeTile) { + + // shape exists on this tile; let's make a layer + renderLayer = std::make_shared(); + + // convert the features and add to render layer + for (auto& shapeFeature : shapeTile.features) { + + using namespace mapbox::util::geojsonvt; + + FeatureType renderType = FeatureType::Unknown; + + if (shapeFeature.type == TileFeatureType::LineString) { + renderType = FeatureType::LineString; + } else if (shapeFeature.type == TileFeatureType::Polygon) { + renderType = FeatureType::Polygon; + } + + assert(renderType != FeatureType::Unknown); + + std::vector renderLine; + auto& shapeLine = shapeFeature.geometry[0].get(); + for (auto& shapePoint : shapeLine.points) { + renderLine.emplace_back(shapePoint.x, shapePoint.y); + } + + GeometryCollection renderGeometry; + renderGeometry.push_back(renderLine); + auto renderFeature = std::make_shared(renderType, renderGeometry); + + renderLayer->addFeature(renderFeature); + } + + // move the layer to the render tile + renderTile->addLayer(layerID, renderLayer); + + // associate the annotation with the tile + auto tile_update_it = tiles.find(id); + assert(tile_update_it != tiles.end()); + tile_update_it->second.first.insert(annotationID); + } + } + } + } + + return renderTile; } const std::string AnnotationManager::PointLayerID = "com.mapbox.annotations.points"; From eefd44c4dc2995d15d157da02549e9a09e31aaff Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 09:35:33 -0700 Subject: [PATCH 49/85] invalidate all annotation for now --- src/mbgl/map/source.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index d15e1cd06fb..eeb2ec8c5a6 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -512,10 +512,13 @@ bool Source::update(MapData& data, void Source::invalidateTiles(const std::unordered_set& ids) { cache.clear(); - for (auto& id : ids) { - tiles.erase(id); - tile_data.erase(id); - } +// for (auto& id : ids) { +// tiles.erase(id); +// tile_data.erase(id); +// } + (void)ids; + tiles.clear(); + tile_data.clear(); updateTilePtrs(); } From 386567ce2922b1d4b88610d17421184317fd467b Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 09:36:29 -0700 Subject: [PATCH 50/85] add test shapes in iOS demo app --- ios/app/MBXViewController.mm | 51 +- ios/app/mapboxgl-app.gypi | 1 + ios/app/threestates.geojson | 1612 ++++++++++++++++++++++++++++++++++ 3 files changed, 1654 insertions(+), 10 deletions(-) create mode 100644 ios/app/threestates.geojson diff --git a/ios/app/MBXViewController.mm b/ios/app/MBXViewController.mm index fb47264f014..e2530bfa858 100644 --- a/ios/app/MBXViewController.mm +++ b/ios/app/MBXViewController.mm @@ -167,39 +167,70 @@ - (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSIn } else if (buttonIndex == actionSheet.firstOtherButtonIndex + 7) { - CLLocationCoordinate2D polygonCoordinates[4] = + // PNW triangle + // + CLLocationCoordinate2D triangleCoordinates[3] = { CLLocationCoordinate2DMake(44, -122), CLLocationCoordinate2DMake(46, -122), - CLLocationCoordinate2DMake(46, -121), - CLLocationCoordinate2DMake(44, -122) + CLLocationCoordinate2DMake(46, -121) }; - MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:polygonCoordinates count:4]; + MGLPolygon *triangle = [MGLPolygon polygonWithCoordinates:triangleCoordinates count:3]; - [self.mapView addAnnotation:polygon]; + [self.mapView addAnnotation:triangle]; + // Orcas Island hike + // NSDictionary *hike = [NSJSONSerialization JSONObjectWithData: [NSData dataWithContentsOfFile: [[NSBundle mainBundle] pathForResource:@"polyline" ofType:@"geojson"]] options:0 error:nil]; - NSArray *coordinatePairs = hike[@"features"][0][@"geometry"][@"coordinates"]; + NSArray *hikeCoordinatePairs = hike[@"features"][0][@"geometry"][@"coordinates"]; - CLLocationCoordinate2D *polylineCoordinates = (CLLocationCoordinate2D *)malloc([coordinatePairs count] * sizeof(CLLocationCoordinate2D)); + CLLocationCoordinate2D *polylineCoordinates = (CLLocationCoordinate2D *)malloc([hikeCoordinatePairs count] * sizeof(CLLocationCoordinate2D)); - for (NSArray *coordinatePair in coordinatePairs) + for (NSArray *coordinatePair in hikeCoordinatePairs) { - polylineCoordinates[[coordinatePairs indexOfObject:coordinatePair]] = CLLocationCoordinate2DMake([coordinatePair[1] doubleValue], [coordinatePair[0] doubleValue]); + polylineCoordinates[[hikeCoordinatePairs indexOfObject:coordinatePair]] = CLLocationCoordinate2DMake([coordinatePair[1] doubleValue], [coordinatePair[0] doubleValue]); } MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:polylineCoordinates - count:[coordinatePairs count]]; + count:[hikeCoordinatePairs count]]; [self.mapView addAnnotation:polyline]; free(polylineCoordinates); + + // PA/NJ/DE polys + // + NSDictionary *threestates = [NSJSONSerialization JSONObjectWithData: + [NSData dataWithContentsOfFile: + [[NSBundle mainBundle] pathForResource:@"threestates" ofType:@"geojson"]] + options:0 + error:nil]; + + for (NSDictionary *feature in threestates[@"features"]) + { + NSArray *stateCoordinatePairs = feature[@"geometry"][@"coordinates"]; + + while ([stateCoordinatePairs count] == 1) stateCoordinatePairs = stateCoordinatePairs[0]; + + CLLocationCoordinate2D *polygonCoordinates = (CLLocationCoordinate2D *)malloc([stateCoordinatePairs count] * sizeof(CLLocationCoordinate2D)); + + for (NSUInteger i = 0; i < [stateCoordinatePairs count]; i++) + { + polygonCoordinates[i] = CLLocationCoordinate2DMake([stateCoordinatePairs[i][1] doubleValue], [stateCoordinatePairs[i][0] doubleValue]); + } + + MGLPolygon *polygon = [MGLPolygon polygonWithCoordinates:polygonCoordinates count:[stateCoordinatePairs count]]; + + [self.mapView addAnnotation:polygon]; + + free(polygonCoordinates); + } } else if (buttonIndex == actionSheet.firstOtherButtonIndex + 8) { diff --git a/ios/app/mapboxgl-app.gypi b/ios/app/mapboxgl-app.gypi index 56ac3567c5d..0a39c7f7d31 100644 --- a/ios/app/mapboxgl-app.gypi +++ b/ios/app/mapboxgl-app.gypi @@ -12,6 +12,7 @@ ' Date: Thu, 4 Jun 2015 10:00:51 -0700 Subject: [PATCH 51/85] we handle this at the core level --- platform/ios/MGLPolygon.m | 2 -- 1 file changed, 2 deletions(-) diff --git a/platform/ios/MGLPolygon.m b/platform/ios/MGLPolygon.m index 95a2dfb5eda..492980933a7 100644 --- a/platform/ios/MGLPolygon.m +++ b/platform/ios/MGLPolygon.m @@ -14,8 +14,6 @@ @implementation MGLPolygon + (instancetype)polygonWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count { - // connect first & last if needed - return [[self alloc] initWithCoordinates:coords count:count]; } From 60ff8e4cb37e1e803072b7fd4e86303c17aba141 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 10:02:19 -0700 Subject: [PATCH 52/85] add default case to appease pedantic compilers --- src/mbgl/map/annotation.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 9ffda20ca60..d77a1a9a67b 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -218,14 +218,14 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, ProjectedFeatureType featureType; - if (styleProperties.is()) { - featureType = ProjectedFeatureType::LineString; - } else if (styleProperties.is()) { + if (styleProperties.is()) { featureType = ProjectedFeatureType::Polygon; if (points.front().lon != points.back().lon || points.front().lat != points.back().lat) { points.push_back(LonLat(points.front().lon, points.front().lat)); } + } else { + featureType = ProjectedFeatureType::LineString; } ProjectedGeometryContainer ring = Convert::project(points, tolerance); From f9a1c60b6508e29d5752cea49f73f6ed9e4280e0 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 10:20:09 -0700 Subject: [PATCH 53/85] APPARENTLY NOT PEDANTIC ENOUGH --- src/mbgl/map/annotation.cpp | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index d77a1a9a67b..d4022e7cc0e 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -234,11 +234,7 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, std::vector features; - features.push_back(Convert::create(Tags(), - (styleProperties.is() ? - ProjectedFeatureType::LineString : - ProjectedFeatureType::Polygon), - rings)); + features.push_back(Convert::create(Tags(), featureType, rings)); shapeTilers.emplace(annotationID, mapbox::util::geojsonvt::GeoJSONVT(features)); From 6d90a8a0f98cd26653a09e936c747d25932fd96b Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 10:37:20 -0700 Subject: [PATCH 54/85] cast for more pedantic warnings on Linux --- src/mbgl/util/geojsonvt.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mbgl/util/geojsonvt.cpp b/src/mbgl/util/geojsonvt.cpp index 731fc8a1f80..1e93e24ec36 100644 --- a/src/mbgl/util/geojsonvt.cpp +++ b/src/mbgl/util/geojsonvt.cpp @@ -497,7 +497,7 @@ void Convert::convertFeature(std::vector &features, const JSVa features.push_back(create(tags, projectedType, *geometry)); - printf("features now has %lu items\n", features.size()); + printf("features now has %lu items\n", (unsigned long)features.size()); } else if (type == "MultiPolygon") { From 395a5dbb27667ad69e17243828869a417d0b27a9 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 12:32:07 -0700 Subject: [PATCH 55/85] differentiate shapes --- ios/app/MBXViewController.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/app/MBXViewController.mm b/ios/app/MBXViewController.mm index e2530bfa858..627f932965e 100644 --- a/ios/app/MBXViewController.mm +++ b/ios/app/MBXViewController.mm @@ -357,7 +357,7 @@ - (UIColor *)mapView:(__unused MGLMapView *)mapView strokeColorForShapeAnnotatio - (UIColor *)mapView:(__unused MGLMapView *)mapView fillColorForPolygonAnnotation:(__unused MGLPolygon *)annotation { - return [UIColor redColor]; + return (annotation.pointCount > 3 ? [UIColor greenColor] : [UIColor redColor]); } - (void)mapView:(__unused MGLMapView *)mapView didChangeUserTrackingMode:(MGLUserTrackingMode)mode animated:(__unused BOOL)animated From a5909954f63a9202991f19a394482ec2a03a1a41 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 16:17:21 -0700 Subject: [PATCH 56/85] sort points above shapes --- src/mbgl/map/map_context.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp index 26e80c5d8cf..34f3d3c4ee6 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -191,7 +191,9 @@ void MapContext::updateAnnotationTiles(const std::unordered_set shapeLayer = std::make_shared(shapeLayerID, std::move(shapePaints)); shapeLayer->type = (shapeStyle.is() ? StyleLayerType::Line : StyleLayerType::Fill); - style->layers.emplace_back(shapeLayer); + + // add to end of other shape layers just before (last) point layer + style->layers.emplace((style->layers.end() - 2), shapeLayer); // create shape bucket & connect to source util::ptr shapeBucket = std::make_shared(shapeLayer->type); From 56b89c5bdfb6675ad508f9554c10a27dda1f5d83 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 16:30:16 -0700 Subject: [PATCH 57/85] clean up shape tilers on deletion --- src/mbgl/map/annotation.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index d4022e7cc0e..2f38d4715cc 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -431,8 +431,12 @@ std::unordered_set AnnotationManager::removeAnnotations(co } if (annotation->type == AnnotationType::Shape) { + // clear shape from render order auto shape_it = std::find(orderedShapeAnnotations.begin(), orderedShapeAnnotations.end(), annotationID); orderedShapeAnnotations.erase(shape_it); + + // clear shape tiler + shapeTilers.erase(annotationID); } annotations.erase(annotationID); From 9b86c21120423361af14c60d9059b609332e78ff Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 16:55:35 -0700 Subject: [PATCH 58/85] properly query annotation bounds --- include/mbgl/util/geo.hpp | 5 +++++ src/mbgl/map/annotation.cpp | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/mbgl/util/geo.hpp b/include/mbgl/util/geo.hpp index 03dab078418..8f74b0a71d6 100644 --- a/include/mbgl/util/geo.hpp +++ b/include/mbgl/util/geo.hpp @@ -33,6 +33,11 @@ struct LatLngBounds { if (point.longitude > ne.longitude) ne.longitude = point.longitude; } + inline void extend(const LatLngBounds& bounds) { + extend(bounds.sw); + extend(bounds.ne); + } + inline bool contains(const LatLng& point) const { return (point.latitude >= sw.latitude && point.latitude <= ne.latitude && diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 2f38d4715cc..cff6850efa9 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -531,7 +531,7 @@ LatLngBounds AnnotationManager::getBoundsForAnnotations(const AnnotationIDs& ids for (auto id : ids) { const auto annotation_it = annotations.find(id); if (annotation_it != annotations.end()) { - bounds.extend(annotation_it->second->getPoint()); + bounds.extend(annotation_it->second->getBounds()); } } From d1797dc41192db45e98076641a0f50a68b34e9ec Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 17:55:16 -0700 Subject: [PATCH 59/85] fix tile invalidation for shapes addition & deletion --- src/mbgl/map/annotation.cpp | 60 ++++++++++++++++++++----------------- src/mbgl/map/annotation.hpp | 2 +- src/mbgl/map/live_tile.cpp | 4 +++ src/mbgl/map/live_tile.hpp | 1 + src/mbgl/map/source.cpp | 16 +++++----- 5 files changed, 47 insertions(+), 36 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index cff6850efa9..6af86294874 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -159,7 +159,10 @@ AnnotationManager::addAnnotations(const AnnotationType type, if (type == AnnotationType::Shape) { const uint32_t shapeAnnotationID = nextID(); - auto featureAffectedTiles = addTileFeature( + // current shape tiles are on-the-fly, so we don't get any "affected tiles" + // and just expire all annotation tiles for shape adds + + addTileFeature( shapeAnnotationID, shape, projectedShape, @@ -169,8 +172,6 @@ AnnotationManager::addAnnotations(const AnnotationType type, maxZoom ); - std::copy(featureAffectedTiles.begin(), featureAffectedTiles.end(), std::inserter(affectedTiles, affectedTiles.begin())); - annotationIDs.push_back(shapeAnnotationID); } } @@ -238,8 +239,6 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, shapeTilers.emplace(annotationID, mapbox::util::geojsonvt::GeoJSONVT(features)); - // FIXME: need to expire tiles - } else { // side length of map at max zoom @@ -344,7 +343,7 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, // Record annotation association with tile and tile feature. This is used to determine stale tiles, // as well as to remove the feature from the tile upon annotation deletion. - anno_it.first->second->tileFeatures.emplace(featureTile.first, std::weak_ptr(feature)); + anno_it.first->second->tilePointFeatures.emplace(featureTile.first, std::weak_ptr(feature)); // track affected tile affectedTiles.insert(featureTile.first); @@ -406,31 +405,36 @@ std::unordered_set AnnotationManager::removeAnnotations(co const auto& annotation_it = annotations.find(annotationID); if (annotation_it != annotations.end()) { const auto& annotation = annotation_it->second; - // calculate annotation's affected tile for each zoom - for (uint8_t z = 0; z < zoomCount; ++z) { - latLng = annotation->getPoint(); - p = projectPoint(latLng); - x = z2s[z] * p.x; - y = z2s[z] * p.y; - TileID tid(z, x, y); - // erase annotation from tile's list - auto& tileAnnotations = tiles[tid].first; - tileAnnotations.erase(annotationID); - // remove annotation's features from tile - const auto& features_it = annotation->tileFeatures.find(tid); - if (features_it != annotation->tileFeatures.end()) { - util::ptr layer; - if (annotation->type == AnnotationType::Point) { - layer = tiles[tid].second->getMutableLayer(PointLayerID); - } else { - layer = tiles[tid].second->getMutableLayer(ShapeLayerID + "." + std::to_string(annotationID)); + // remove feature(s) from relevant tiles + if (annotation->type == AnnotationType::Point) { + // calculate annotation's affected tile for each zoom + for (uint8_t z = 0; z < zoomCount; ++z) { + latLng = annotation->getPoint(); + p = projectPoint(latLng); + x = z2s[z] * p.x; + y = z2s[z] * p.y; + TileID tid(z, x, y); + // erase annotation from tile's list + auto& tileAnnotations = tiles[tid].first; + tileAnnotations.erase(annotationID); + // remove annotation's features from tile + const auto& features_it = annotation->tilePointFeatures.find(tid); + if (features_it != annotation->tilePointFeatures.end()) { + // points share a layer; remove feature + auto layer = tiles[tid].second->getMutableLayer(PointLayerID); + layer->removeFeature(features_it->second); + affectedTiles.insert(tid); + } + } + } else { + // remove shape layer from tiles if relevant + for (auto tile_it = tiles.begin(); tile_it != tiles.end(); ++tile_it) { + if (tile_it->second.first.count(annotationID)) { + tile_it->second.second->removeLayer(ShapeLayerID + "." + std::to_string(annotationID)); + affectedTiles.insert(tile_it->first); } - layer->removeFeature(features_it->second); - affectedTiles.insert(tid); } - } - if (annotation->type == AnnotationType::Shape) { // clear shape from render order auto shape_it = std::find(orderedShapeAnnotations.begin(), orderedShapeAnnotations.end(), annotationID); orderedShapeAnnotations.erase(shape_it); diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp index c7a54540144..3068f21d153 100644 --- a/src/mbgl/map/annotation.hpp +++ b/src/mbgl/map/annotation.hpp @@ -45,7 +45,7 @@ class Annotation : private util::noncopyable { private: const AnnotationType type = AnnotationType::Point; const AnnotationSegments geometry; - std::unordered_map, TileID::Hash> tileFeatures; + std::unordered_map, TileID::Hash> tilePointFeatures; const LatLngBounds bounds; }; diff --git a/src/mbgl/map/live_tile.cpp b/src/mbgl/map/live_tile.cpp index 999fcbc8899..f7a70f7d46a 100644 --- a/src/mbgl/map/live_tile.cpp +++ b/src/mbgl/map/live_tile.cpp @@ -42,6 +42,10 @@ void LiveTile::addLayer(const std::string& name, util::ptr layer) layers.emplace(name, std::move(layer)); } +void LiveTile::removeLayer(const std::string& name) { + layers.erase(name); +} + util::ptr LiveTile::getLayer(const std::string& name) const { return getMutableLayer(name); } diff --git a/src/mbgl/map/live_tile.hpp b/src/mbgl/map/live_tile.hpp index a94ae307f57..5b4c2d1c166 100644 --- a/src/mbgl/map/live_tile.hpp +++ b/src/mbgl/map/live_tile.hpp @@ -42,6 +42,7 @@ class LiveTile : public GeometryTile { LiveTile(); void addLayer(const std::string&, util::ptr); + void removeLayer(const std::string&); util::ptr getLayer(const std::string&) const override; util::ptr getMutableLayer(const std::string&) const; diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index eeb2ec8c5a6..54464d5b8f6 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -512,13 +512,15 @@ bool Source::update(MapData& data, void Source::invalidateTiles(const std::unordered_set& ids) { cache.clear(); -// for (auto& id : ids) { -// tiles.erase(id); -// tile_data.erase(id); -// } - (void)ids; - tiles.clear(); - tile_data.clear(); + if (ids.size()) { + for (auto& id : ids) { + tiles.erase(id); + tile_data.erase(id); + } + } else { + tiles.clear(); + tile_data.clear(); + } updateTilePtrs(); } From 62f40a1b5e4c9dbb39cb6c62c32b6b68892ffed2 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 18:16:39 -0700 Subject: [PATCH 60/85] adjustments to shape tiler structure & locking --- src/mbgl/map/annotation.cpp | 4 ++-- src/mbgl/map/annotation.hpp | 2 +- src/mbgl/util/geojsonvt.cpp | 2 +- src/mbgl/util/geojsonvt.hpp | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 6af86294874..81bc66c313d 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -237,7 +237,7 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, features.push_back(Convert::create(Tags(), featureType, rings)); - shapeTilers.emplace(annotationID, mapbox::util::geojsonvt::GeoJSONVT(features)); + shapeTilers.emplace(annotationID, std::make_unique(features)); } else { @@ -570,7 +570,7 @@ const LiveTile* AnnotationManager::getTile(const TileID& id) { if (renderLayer == nullptr) { // we might need to create a tile layer for this shape - const auto& shapeTile = tiler_it.second.getTile(id.z, id.x, id.y); + const auto& shapeTile = tiler_it.second->getTile(id.z, id.x, id.y); if (shapeTile) { diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp index 3068f21d153..0c8a69cb126 100644 --- a/src/mbgl/map/annotation.hpp +++ b/src/mbgl/map/annotation.hpp @@ -102,7 +102,7 @@ class AnnotationManager : private util::noncopyable { std::unordered_map> annotations; std::vector orderedShapeAnnotations; std::unordered_map, std::unique_ptr>, TileID::Hash> tiles; - std::unordered_map shapeTilers; + std::unordered_map> shapeTilers; std::unordered_set staleTiles; uint32_t nextID_ = 0; }; diff --git a/src/mbgl/util/geojsonvt.cpp b/src/mbgl/util/geojsonvt.cpp index 1e93e24ec36..219a77a624b 100644 --- a/src/mbgl/util/geojsonvt.cpp +++ b/src/mbgl/util/geojsonvt.cpp @@ -247,7 +247,7 @@ void GeoJSONVT::splitTile(std::vector features_, uint8_t z_, u Tile& GeoJSONVT::getTile(uint8_t z, uint32_t x, uint32_t y) { -// std::lock_guard lock(mtx); + std::lock_guard lock(mtx); const uint64_t id = toID(z, x, y); if (this->tiles.count(id)) { diff --git a/src/mbgl/util/geojsonvt.hpp b/src/mbgl/util/geojsonvt.hpp index 6c66d0e757c..f02217c08e7 100644 --- a/src/mbgl/util/geojsonvt.hpp +++ b/src/mbgl/util/geojsonvt.hpp @@ -211,7 +211,7 @@ class GeoJSONVT { }; private: -// std::mutex mtx; + std::mutex mtx; uint8_t baseZoom; uint8_t maxZoom; uint32_t maxPoints; From 9290460c8ee1d89d08e1cd7c51b1e476ef96034e Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 22:34:27 -0700 Subject: [PATCH 61/85] properly constrain latitude for shape points --- src/mbgl/map/annotation.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 81bc66c313d..f2486cc8f88 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -214,7 +214,8 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, std::vector points; for (size_t i = 0; i < segments[0].size(); ++i) { - points.push_back(LonLat(segments[0][i].longitude, segments[0][i].latitude)); + const double constraintedLatitude = std::fmin(std::fmax(segments[0][i].latitude, -util::LATITUDE_MAX), util::LATITUDE_MAX); + points.push_back(LonLat(segments[0][i].longitude, constraintedLatitude)); } ProjectedFeatureType featureType; From 3ce8045a445b05e11905db739c9938a79323bf2f Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 22:35:47 -0700 Subject: [PATCH 62/85] add note about no holes --- src/mbgl/map/annotation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index f2486cc8f88..36d20a3589a 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -213,7 +213,7 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, std::vector points; - for (size_t i = 0; i < segments[0].size(); ++i) { + for (size_t i = 0; i < segments[0].size(); ++i) { // first segment for now (no holes) const double constraintedLatitude = std::fmin(std::fmax(segments[0][i].latitude, -util::LATITUDE_MAX), util::LATITUDE_MAX); points.push_back(LonLat(segments[0][i].longitude, constraintedLatitude)); } From f3eb68c344db6d7e095d6194766f562f2607b6eb Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 4 Jun 2015 22:42:39 -0700 Subject: [PATCH 63/85] clean up point/shape-only routines --- src/mbgl/map/annotation.cpp | 31 +++++++++---------------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 36d20a3589a..08a02ecfda3 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -103,24 +103,18 @@ AnnotationManager::addAnnotations(const AnnotationType type, const uint8_t maxZoom = data.transform.getMaxZoom(); for (size_t s = 0; s < segments.size(); ++s) { - auto& shape = segments[s]; - std::vector>> projectedShape; - projectedShape.reserve(shape.size()); + if (type == AnnotationType::Point) { - for (size_t l = 0; l < shape.size(); ++l) { - auto& line = shape[l]; + for (size_t l = 0; l < segments[s].size(); ++l) { - std::vector> projectedLine; - projectedLine.reserve(line.size()); + for (size_t p = 0; p < segments[s][l].size(); ++p) { - for (size_t p = 0; p < line.size(); ++p) { - auto& point = line[p]; + auto& point = segments[s][l][p]; - // projection conversion into unit space - const auto pp = projectPoint(point); + // projection conversion into unit space + const auto pp = projectPoint(point); - if (type == AnnotationType::Point) { const uint32_t pointAnnotationID = nextID(); // at render time we style the point according to its {sprite} field @@ -146,17 +140,10 @@ AnnotationManager::addAnnotations(const AnnotationType type, std::copy(featureAffectedTiles.begin(), featureAffectedTiles.end(), std::inserter(affectedTiles, affectedTiles.begin())); annotationIDs.push_back(pointAnnotationID); - } else { - projectedLine.push_back(pp); } } + } else { - if (type == AnnotationType::Shape) { - projectedShape.push_back(projectedLine); - } - } - - if (type == AnnotationType::Shape) { const uint32_t shapeAnnotationID = nextID(); // current shape tiles are on-the-fly, so we don't get any "affected tiles" @@ -164,8 +151,8 @@ AnnotationManager::addAnnotations(const AnnotationType type, addTileFeature( shapeAnnotationID, - shape, - projectedShape, + segments[s], + {{ }}, AnnotationType::Shape, styleProperties[s], {{ }}, From e9fcb6ed3e19cff756ed09b64b37e3db6cccc2b1 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 5 Jun 2015 00:47:37 -0700 Subject: [PATCH 64/85] fix polyline parsing error --- ios/app/MBXViewController.mm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/app/MBXViewController.mm b/ios/app/MBXViewController.mm index 627f932965e..00245828ed3 100644 --- a/ios/app/MBXViewController.mm +++ b/ios/app/MBXViewController.mm @@ -192,9 +192,9 @@ - (void)actionSheet:(UIActionSheet *)actionSheet didDismissWithButtonIndex:(NSIn CLLocationCoordinate2D *polylineCoordinates = (CLLocationCoordinate2D *)malloc([hikeCoordinatePairs count] * sizeof(CLLocationCoordinate2D)); - for (NSArray *coordinatePair in hikeCoordinatePairs) + for (NSUInteger i = 0; i < [hikeCoordinatePairs count]; i++) { - polylineCoordinates[[hikeCoordinatePairs indexOfObject:coordinatePair]] = CLLocationCoordinate2DMake([coordinatePair[1] doubleValue], [coordinatePair[0] doubleValue]); + polylineCoordinates[i] = CLLocationCoordinate2DMake([hikeCoordinatePairs[i][1] doubleValue], [hikeCoordinatePairs[i][0] doubleValue]); } MGLPolyline *polyline = [MGLPolyline polylineWithCoordinates:polylineCoordinates From 0e574c614eeec656c40e3b9070193f123c725d77 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 5 Jun 2015 10:43:38 -0700 Subject: [PATCH 65/85] spacing --- platform/ios/MGLMultiPoint.mm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/platform/ios/MGLMultiPoint.mm b/platform/ios/MGLMultiPoint.mm index fbd9a092d76..589a9e806a8 100644 --- a/platform/ios/MGLMultiPoint.mm +++ b/platform/ios/MGLMultiPoint.mm @@ -18,7 +18,7 @@ - (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords if (self) { _count = count; - _coords = (CLLocationCoordinate2D*)malloc(_count * sizeof(CLLocationCoordinate2D)); + _coords = (CLLocationCoordinate2D *)malloc(_count * sizeof(CLLocationCoordinate2D)); for (NSUInteger i = 0; i < _count; i++) { From e92d1ff99f3b33474b563013b5728d3f33011de7 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 5 Jun 2015 10:44:12 -0700 Subject: [PATCH 66/85] remove bad check --- platform/ios/MGLMultiPoint.mm | 1 - 1 file changed, 1 deletion(-) diff --git a/platform/ios/MGLMultiPoint.mm b/platform/ios/MGLMultiPoint.mm index 589a9e806a8..91c2511c86e 100644 --- a/platform/ios/MGLMultiPoint.mm +++ b/platform/ios/MGLMultiPoint.mm @@ -70,7 +70,6 @@ - (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range userInfo:nil] raise]; } - assert(sizeof(coords) >= sizeof(range.length * sizeof(CLLocationCoordinate2D))); assert(range.location + range.length <= _count); NSUInteger index = 0; From 715be6fa69390684b389c6a609c1bd2d1decf5e0 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 5 Jun 2015 10:44:29 -0700 Subject: [PATCH 67/85] preserve detail to max zoom --- src/mbgl/map/annotation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 08a02ecfda3..c00b25f6109 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -225,7 +225,7 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, features.push_back(Convert::create(Tags(), featureType, rings)); - shapeTilers.emplace(annotationID, std::make_unique(features)); + shapeTilers.emplace(annotationID, std::make_unique(features, maxZoom)); } else { From 9d864ce20da63906f854124782f910781f0186aa Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 5 Jun 2015 10:44:50 -0700 Subject: [PATCH 68/85] simplify constructor --- src/mbgl/util/geojsonvt.hpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/mbgl/util/geojsonvt.hpp b/src/mbgl/util/geojsonvt.hpp index f02217c08e7..0a6302f6d43 100644 --- a/src/mbgl/util/geojsonvt.hpp +++ b/src/mbgl/util/geojsonvt.hpp @@ -55,10 +55,8 @@ using ProjectedGeometry = mapbox::util::variant= 0 && y >= 0 && z >= 0); } inline bool isEqualToPoint(const ProjectedPoint *p2) const { return (x == p2->x && y == p2->y && z == p2->z); } From 81616214ff04c577673de82013c972f9bb4e3b9b Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 5 Jun 2015 20:59:51 -0700 Subject: [PATCH 69/85] use nicer operator overloading --- src/mbgl/util/geojsonvt.cpp | 8 ++++---- src/mbgl/util/geojsonvt.hpp | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mbgl/util/geojsonvt.cpp b/src/mbgl/util/geojsonvt.cpp index 219a77a624b..095bfba012a 100644 --- a/src/mbgl/util/geojsonvt.cpp +++ b/src/mbgl/util/geojsonvt.cpp @@ -583,7 +583,7 @@ void Convert::calcSize(ProjectedGeometryContainer &geometryContainer) { ProjectedPoint a, b; for (size_t i = 0; i < geometryContainer.members.size() - 1; ++i) { - a = (b.isValid() ? b : geometryContainer.members[i].get()); + a = (b ? b : geometryContainer.members[i].get()); b = geometryContainer.members[i + 1].get(); area += a.x * b.y - b.x * a.y; @@ -719,7 +719,7 @@ std::vector Clip::clip(const std::vector fea double min = 0; double max = 0; - if (feature.minPoint.isValid()) { + if (feature.minPoint) { min = (axis == 0 ? feature.minPoint.x : feature.minPoint.y); max = (axis == 0 ? feature.maxPoint.x : feature.maxPoint.y); @@ -781,7 +781,7 @@ ProjectedGeometryContainer Clip::clipGeometry(ProjectedGeometryContainer geometr ProjectedGeometryContainer slice; for (size_t j = 0; j < (len - 1); ++j) { - a = (b.isValid() ? b : points->members[j].get()); + a = (b ? b : points->members[j].get()); b = points->members[j + 1].get(); ak = (bk ? bk : (axis == 0 ? a.x : a.y)); bk = (axis == 0 ? b.x : b.y); @@ -833,7 +833,7 @@ ProjectedGeometryContainer Clip::clipGeometry(ProjectedGeometryContainer geometr if (closed && slice.members.size()) { const ProjectedPoint *first = &(slice.members[0].get()); const ProjectedPoint *last = &(slice.members[slice.members.size() - 1].get()); - if (!first->isEqualToPoint(last)) { + if (first != last) { slice.members.push_back(ProjectedPoint(first->x, first->y, first->z)); } } diff --git a/src/mbgl/util/geojsonvt.hpp b/src/mbgl/util/geojsonvt.hpp index 0a6302f6d43..1ab6ca6ce62 100644 --- a/src/mbgl/util/geojsonvt.hpp +++ b/src/mbgl/util/geojsonvt.hpp @@ -58,8 +58,8 @@ class ProjectedPoint { ProjectedPoint(double x_ = -1, double y_ = -1, double z_ = -1) : x(x_), y(y_), z(z_) {} - inline bool isValid() const { return (x >= 0 && y >= 0 && z >= 0); } - inline bool isEqualToPoint(const ProjectedPoint *p2) const { return (x == p2->x && y == p2->y && z == p2->z); } + inline operator bool() const { return (x >= 0 && y >= 0 && z >= 0); } + inline bool operator!= (const ProjectedPoint& rhs) const { return (x != rhs.x || y != rhs.y || z != rhs.z); } public: double x = -1; From ee9ab21c5272586edf9cd499fe4e6c20e115a039 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Fri, 5 Jun 2015 21:13:43 -0700 Subject: [PATCH 70/85] properly iterate all shape geometries --- src/mbgl/map/annotation.cpp | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index c00b25f6109..47c6c493b81 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -580,14 +580,21 @@ const LiveTile* AnnotationManager::getTile(const TileID& id) { assert(renderType != FeatureType::Unknown); - std::vector renderLine; - auto& shapeLine = shapeFeature.geometry[0].get(); - for (auto& shapePoint : shapeLine.points) { - renderLine.emplace_back(shapePoint.x, shapePoint.y); + GeometryCollection renderGeometry; + + for (auto& shapeGeometry : shapeFeature.geometry) { + + std::vector renderLine; + + auto& shapeRing = shapeGeometry.get(); + + for (auto& shapePoint : shapeRing.points) { + renderLine.emplace_back(shapePoint.x, shapePoint.y); + } + + renderGeometry.push_back(renderLine); } - GeometryCollection renderGeometry; - renderGeometry.push_back(renderLine); auto renderFeature = std::make_shared(renderType, renderGeometry); renderLayer->addFeature(renderFeature); From 6345fcff5a998c56cd2d842fc87b07a8bf2fed94 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Sun, 7 Jun 2015 15:58:34 -0700 Subject: [PATCH 71/85] tile points can be (slightly, up to buffer distance) negative --- src/mbgl/util/geojsonvt.cpp | 4 ++-- src/mbgl/util/geojsonvt.hpp | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/mbgl/util/geojsonvt.cpp b/src/mbgl/util/geojsonvt.cpp index 095bfba012a..690dfa47a20 100644 --- a/src/mbgl/util/geojsonvt.cpp +++ b/src/mbgl/util/geojsonvt.cpp @@ -71,8 +71,8 @@ void Tile::addFeature(Tile &tile, ProjectedFeature &feature, uint32_t z2, uint32 TilePoint Tile::transformPoint(const ProjectedPoint &p, uint32_t z2, uint32_t tx, uint32_t ty, uint16_t extent) { - uint16_t x = extent * (p.x * z2 - tx); - uint16_t y = extent * (p.y * z2 - ty); + int16_t x = extent * (p.x * z2 - tx); + int16_t y = extent * (p.y * z2 - ty); return TilePoint(x, y); } diff --git a/src/mbgl/util/geojsonvt.hpp b/src/mbgl/util/geojsonvt.hpp index 1ab6ca6ce62..529785dfd00 100644 --- a/src/mbgl/util/geojsonvt.hpp +++ b/src/mbgl/util/geojsonvt.hpp @@ -124,12 +124,12 @@ using TileGeometry = mapbox::util::variant Date: Sun, 7 Jun 2015 15:58:48 -0700 Subject: [PATCH 72/85] fix bug with min/max point validity --- src/mbgl/util/geojsonvt.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mbgl/util/geojsonvt.hpp b/src/mbgl/util/geojsonvt.hpp index 529785dfd00..c55e6786d84 100644 --- a/src/mbgl/util/geojsonvt.hpp +++ b/src/mbgl/util/geojsonvt.hpp @@ -109,8 +109,8 @@ class ProjectedFeature { ProjectedGeometry geometry; ProjectedFeatureType type; Tags tags; - ProjectedPoint minPoint = ProjectedPoint(1, 1, 0); - ProjectedPoint maxPoint = ProjectedPoint(0, 0, 0); + ProjectedPoint minPoint = ProjectedPoint(); + ProjectedPoint maxPoint = ProjectedPoint(); }; #pragma mark - From 43ccb9b99b8e755c7ddce106b38078d682230a2a Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Sun, 7 Jun 2015 16:32:56 -0700 Subject: [PATCH 73/85] turn off debug logging for GeoJSONVT --- src/mbgl/util/geojsonvt.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mbgl/util/geojsonvt.hpp b/src/mbgl/util/geojsonvt.hpp index c55e6786d84..d141e40bd25 100644 --- a/src/mbgl/util/geojsonvt.hpp +++ b/src/mbgl/util/geojsonvt.hpp @@ -181,9 +181,9 @@ class Tile { class GeoJSONVT { public: - static std::vector convertFeatures(const std::string &data, uint8_t baseZoom = 14, double tolerance = 3, bool debug = true); + static std::vector convertFeatures(const std::string &data, uint8_t baseZoom = 14, double tolerance = 3, bool debug = false); - GeoJSONVT(const std::vector& features_, uint8_t baseZoom = 14, uint8_t maxZoom = 4, uint32_t maxPoints = 100, double tolerance = 3, bool debug = true); + GeoJSONVT(const std::vector& features_, uint8_t baseZoom = 14, uint8_t maxZoom = 4, uint32_t maxPoints = 100, double tolerance = 3, bool debug = false); Tile& getTile(uint8_t z, uint32_t x, uint32_t y); From fe2d84caabca053076e3b2fe3cb9f5b66c38f838 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Sun, 7 Jun 2015 17:32:02 -0700 Subject: [PATCH 74/85] remove OS X test annotations --- macosx/main.mm | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/macosx/main.mm b/macosx/main.mm index ece4343b853..4a786f57723 100644 --- a/macosx/main.mm +++ b/macosx/main.mm @@ -151,23 +151,6 @@ int main() { map.setStyleURL(newStyle.first); view.setWindowTitle(newStyle.second); - map.addPointAnnotation(mbgl::LatLng(45, -120), "default_marker"); - - mbgl::FillProperties fillProperties; - fillProperties.fill_color = mbgl::Color({{ 1, 0, 0, 1 }}); - fillProperties.stroke_color = mbgl::Color({{ 0, 0, 0, 1 }}); - fillProperties.opacity = 0.25; - - mbgl::StyleProperties shapeProperties; - shapeProperties.set(fillProperties); - - map.addShapeAnnotation({{ - mbgl::LatLng(44, -122), - mbgl::LatLng(46, -122), - mbgl::LatLng(46, -121), - mbgl::LatLng(44, -122) - }}, shapeProperties); - view.run(); [reachability stopNotifier]; From c638825ddb91c03e645a9303f6932bd2f004a9ac Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Sun, 7 Jun 2015 17:38:40 -0700 Subject: [PATCH 75/85] rename method --- src/mbgl/map/map_context.cpp | 4 ++-- src/mbgl/map/map_context.hpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp index 98ddbef213a..767584ad323 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -234,7 +234,7 @@ void MapContext::cascadeClasses() { style->cascade(data.getClasses()); } -void MapContext::recalculateClasses(TimePoint now) { +void MapContext::recalculateStyle(TimePoint now) { style->recalculate(transformState.getNormalizedZoom(), now); } @@ -258,7 +258,7 @@ void MapContext::update() { if (updated & static_cast(Update::Classes) || updated & static_cast(Update::Zoom)) { - recalculateClasses(now); + recalculateStyle(now); } updateTiles(); diff --git a/src/mbgl/map/map_context.hpp b/src/mbgl/map/map_context.hpp index 0b51cc58c84..16fdea18200 100644 --- a/src/mbgl/map/map_context.hpp +++ b/src/mbgl/map/map_context.hpp @@ -65,7 +65,7 @@ class MapContext : public Style::Observer { // Style-related updates. void cascadeClasses(); - void recalculateClasses(TimePoint); + void recalculateStyle(TimePoint); // Update the state indicated by the accumulated Update flags, then render. void update(); From 38927c7bf06ff7a6f5f6751bb7291aa7f69f0a5f Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Sun, 7 Jun 2015 17:48:33 -0700 Subject: [PATCH 76/85] MGLBounds -> MGLCoordinateBounds --- include/mbgl/ios/MGLOverlay.h | 4 ++-- include/mbgl/ios/MGLTypes.h | 2 +- platform/ios/MGLMultiPoint.mm | 4 ++-- platform/ios/MGLPolygon.m | 2 +- platform/ios/MGLPolyline.m | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/include/mbgl/ios/MGLOverlay.h b/include/mbgl/ios/MGLOverlay.h index 8abed8bae28..686aefc2beb 100644 --- a/include/mbgl/ios/MGLOverlay.h +++ b/include/mbgl/ios/MGLOverlay.h @@ -19,13 +19,13 @@ /** The cooordinate rectangle that encompasses the overlay. (required) (read-only) * * This property contains the smallest rectangle that completely encompasses the overlay. Implementers of this protocol must set this area when implementing their overlay class, and after setting it, you must not change it. */ -@property (nonatomic, readonly) MGLMapBounds overlayBounds; +@property (nonatomic, readonly) MGLCoordinateBounds overlayBounds; /** Returns a Boolean indicating whether the specified rectangle intersects the receiver’s shape. * * You can implement this method to provide more specific bounds checking for an overlay. If you do not implement it, the bounding rectangle is used to detect intersections. * @param overlayBounds The rectangle to intersect with the receiver’s area. * @return `YES` if any part of the map rectangle intersects the receiver’s shape or `NO` if it does not. */ -- (BOOL)intersectsOverlayBounds:(MGLMapBounds)overlayBounds; +- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds; @end diff --git a/include/mbgl/ios/MGLTypes.h b/include/mbgl/ios/MGLTypes.h index 75d23f414f9..5b11cc71120 100644 --- a/include/mbgl/ios/MGLTypes.h +++ b/include/mbgl/ios/MGLTypes.h @@ -16,4 +16,4 @@ typedef NS_ENUM(NSUInteger, MGLUserTrackingMode) { typedef struct { CLLocationCoordinate2D sw; CLLocationCoordinate2D ne; -} MGLMapBounds; +} MGLCoordinateBounds; diff --git a/platform/ios/MGLMultiPoint.mm b/platform/ios/MGLMultiPoint.mm index 91c2511c86e..ef8a18e4936 100644 --- a/platform/ios/MGLMultiPoint.mm +++ b/platform/ios/MGLMultiPoint.mm @@ -81,7 +81,7 @@ - (void)getCoordinates:(CLLocationCoordinate2D *)coords range:(NSRange)range } } -- (MGLMapBounds)overlayBounds +- (MGLCoordinateBounds)overlayBounds { return { CLLocationCoordinate2DMake(_bounds.sw.latitude, _bounds.sw.longitude), @@ -89,7 +89,7 @@ - (MGLMapBounds)overlayBounds }; } -- (BOOL)intersectsOverlayBounds:(MGLMapBounds)overlayBounds +- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds { mbgl::LatLngBounds area( mbgl::LatLng(overlayBounds.sw.latitude, overlayBounds.sw.longitude), diff --git a/platform/ios/MGLPolygon.m b/platform/ios/MGLPolygon.m index 492980933a7..313a41876f1 100644 --- a/platform/ios/MGLPolygon.m +++ b/platform/ios/MGLPolygon.m @@ -3,7 +3,7 @@ @interface MGLMultiPoint (MGLPolygon) - (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count; -- (BOOL)intersectsOverlayBounds:(MGLMapBounds)overlayBounds; +- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds; @end diff --git a/platform/ios/MGLPolyline.m b/platform/ios/MGLPolyline.m index f80a149a9ef..8b9b312d044 100644 --- a/platform/ios/MGLPolyline.m +++ b/platform/ios/MGLPolyline.m @@ -3,7 +3,7 @@ @interface MGLMultiPoint (MGLPolyline) - (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count; -- (BOOL)intersectsOverlayBounds:(MGLMapBounds)overlayBounds; +- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds; @end From 2c9a886587bbfbedacc10c6cb83f20cd04c378c9 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Sun, 7 Jun 2015 18:07:08 -0700 Subject: [PATCH 77/85] move to annotation style value type getter --- src/mbgl/map/annotation.cpp | 4 ++-- src/mbgl/map/annotation.hpp | 2 +- src/mbgl/map/map_context.cpp | 3 +-- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index f7986da1822..7f5266a8e3c 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -439,13 +439,13 @@ std::unordered_set AnnotationManager::removeAnnotations(co return affectedTiles; } -const std::unique_ptr& AnnotationManager::getAnnotationWithID(uint32_t annotationID) const { +const StyleProperties AnnotationManager::getAnnotationStyleProperties(uint32_t annotationID) const { std::lock_guard lock(mtx); auto anno_it = annotations.find(annotationID); assert(anno_it != annotations.end()); - return anno_it->second; + return anno_it->second->styleProperties; } AnnotationIDs AnnotationManager::getAnnotationsInBounds(const LatLngBounds& queryBounds, diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp index 0c8a69cb126..8c04f454f41 100644 --- a/src/mbgl/map/annotation.hpp +++ b/src/mbgl/map/annotation.hpp @@ -69,7 +69,7 @@ class AnnotationManager : private util::noncopyable { const MapData&); std::unordered_set removeAnnotations(const AnnotationIDs&, const MapData&); AnnotationIDs getOrderedShapeAnnotations() const { return orderedShapeAnnotations; } - const std::unique_ptr& getAnnotationWithID(uint32_t) const; + const StyleProperties getAnnotationStyleProperties(uint32_t) const; AnnotationIDs getAnnotationsInBounds(const LatLngBounds&, const MapData&, const AnnotationType& = AnnotationType::Any) const; LatLngBounds getBoundsForAnnotations(const AnnotationIDs&) const; diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp index 767584ad323..bd288a5cdac 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -158,8 +158,7 @@ void MapContext::updateAnnotationTiles(const std::unordered_setlayers.end()) { // query shape styling - auto& annotation = data.annotationManager.getAnnotationWithID(shapeAnnotationID); - auto& shapeStyle = annotation->styleProperties; + auto& shapeStyle = data.annotationManager.getAnnotationStyleProperties(shapeAnnotationID); // apply shape paint properties ClassProperties paintProperties; From 74db485fb50a6ecccbab29d359c05b7a3fbd748b Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 11 Jun 2015 14:10:36 -0700 Subject: [PATCH 78/85] properly use max zoom --- src/mbgl/map/annotation.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 7f5266a8e3c..5d403eae923 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -190,7 +190,7 @@ AnnotationManager::addTileFeature(const uint32_t annotationID, using namespace mapbox::util::geojsonvt; - const uint32_t z2 = 1 << 14; + const uint32_t z2 = 1 << maxZoom; const double baseTolerance = 3; const uint16_t extent = 4096; From c39de8a442dec100cd99bd41b9cae3a3dabda1e2 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 11 Jun 2015 14:17:08 -0700 Subject: [PATCH 79/85] move to split GeoJSONVT --- src/mbgl/map/annotation.cpp | 1 + src/mbgl/util/geojsonvt.cpp | 587 +-------------------------- src/mbgl/util/geojsonvt.hpp | 224 +--------- src/mbgl/util/geojsonvt_clip.cpp | 156 +++++++ src/mbgl/util/geojsonvt_clip.hpp | 24 ++ src/mbgl/util/geojsonvt_convert.cpp | 285 +++++++++++++ src/mbgl/util/geojsonvt_convert.hpp | 32 ++ src/mbgl/util/geojsonvt_simplify.cpp | 86 ++++ src/mbgl/util/geojsonvt_simplify.hpp | 18 + src/mbgl/util/geojsonvt_tile.cpp | 72 ++++ src/mbgl/util/geojsonvt_tile.hpp | 29 ++ src/mbgl/util/geojsonvt_types.hpp | 140 +++++++ src/mbgl/util/geojsonvt_util.hpp | 24 ++ 13 files changed, 876 insertions(+), 802 deletions(-) create mode 100644 src/mbgl/util/geojsonvt_clip.cpp create mode 100644 src/mbgl/util/geojsonvt_clip.hpp create mode 100644 src/mbgl/util/geojsonvt_convert.cpp create mode 100644 src/mbgl/util/geojsonvt_convert.hpp create mode 100644 src/mbgl/util/geojsonvt_simplify.cpp create mode 100644 src/mbgl/util/geojsonvt_simplify.hpp create mode 100644 src/mbgl/util/geojsonvt_tile.cpp create mode 100644 src/mbgl/util/geojsonvt_tile.hpp create mode 100644 src/mbgl/util/geojsonvt_types.hpp create mode 100644 src/mbgl/util/geojsonvt_util.hpp diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 5d403eae923..6c59df6321b 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include diff --git a/src/mbgl/util/geojsonvt.cpp b/src/mbgl/util/geojsonvt.cpp index 690dfa47a20..e6e485aef1c 100644 --- a/src/mbgl/util/geojsonvt.cpp +++ b/src/mbgl/util/geojsonvt.cpp @@ -1,82 +1,14 @@ -#include +#include "geojsonvt.hpp" +#include "geojsonvt_clip.hpp" +#include "geojsonvt_convert.hpp" +#include "geojsonvt_util.hpp" #include -#include -#include namespace mapbox { namespace util { namespace geojsonvt { std::unordered_map Time::activities; -#pragma mark - Tile - -Tile Tile::createTile(std::vector &features, uint32_t z2, uint32_t tx, uint32_t ty, double tolerance, uint16_t extent, bool noSimplify) { - - Tile tile; - - for (size_t i = 0; i < features.size(); ++i) { - tile.numFeatures++; - addFeature(tile, features[i], z2, tx, ty, tolerance, extent, noSimplify); - } - - return std::move(tile); -} - -void Tile::addFeature(Tile &tile, ProjectedFeature &feature, uint32_t z2, uint32_t tx, uint32_t ty, double tolerance, uint16_t extent, bool noSimplify) { - - ProjectedGeometryContainer *geom = &(feature.geometry.get()); - ProjectedFeatureType type = feature.type; - std::vector transformed; - double sqTolerance = tolerance * tolerance; - ProjectedGeometryContainer ring; - - if (type == ProjectedFeatureType::Point) { - for (size_t i = 0; i < geom->members.size(); ++i) { - ProjectedPoint *p = &(geom->members[i].get()); - transformed.push_back(transformPoint(*p, z2, tx, ty, extent)); - tile.numPoints++; - tile.numSimplified++; - } - } else { - for (size_t i = 0; i < geom->members.size(); ++i) { - ring = geom->members[i].get(); - - if (!noSimplify && ((type == ProjectedFeatureType::LineString && ring.dist < tolerance) || - (type == ProjectedFeatureType::Polygon && ring.area < sqTolerance))) { - tile.numPoints += ring.members.size(); - continue; - } - - TileRing transformedRing; - - for (size_t j = 0; j < ring.members.size(); ++j) { - ProjectedPoint *p = &(ring.members[j].get()); - if (noSimplify || p->z > sqTolerance) { - TilePoint transformedPoint = transformPoint(*p, z2, tx, ty, extent); - transformedRing.points.push_back(transformedPoint); - tile.numSimplified++; - } - tile.numPoints++; - } - - transformed.push_back(transformedRing); - } - } - - if (transformed.size()) { - tile.features.push_back(TileFeature(transformed, type, Tags(feature.tags))); - } - -} - -TilePoint Tile::transformPoint(const ProjectedPoint &p, uint32_t z2, uint32_t tx, uint32_t ty, uint16_t extent) { - - int16_t x = extent * (p.x * z2 - tx); - int16_t y = extent * (p.y * z2 - ty); - - return TilePoint(x, y); -} - #pragma mark - GeoJSONVT std::vector GeoJSONVT::convertFeatures(const std::string &data, uint8_t baseZoom, double tolerance, bool debug) { @@ -344,515 +276,4 @@ ProjectedPoint GeoJSONVT::intersectY(const ProjectedPoint &a, const ProjectedPoi return ProjectedPoint(r1, r2, r3); } -#pragma mark - Convert - -std::vector Convert::convert(const JSDocument &data, double tolerance) { - - std::vector features; - const JSValue &rawType = data["type"]; - - if (std::string(rawType.GetString()) == "FeatureCollection") { - if (data.HasMember("features")) { - const JSValue &rawFeatures = data["features"]; - if (rawFeatures.IsArray()) { - printf("there are %i total features to convert\n", rawFeatures.Size()); - for (rapidjson::SizeType i = 0; i < rawFeatures.Size(); ++i) { - convertFeature(features, rawFeatures[i], tolerance); - } - } - } - } else if (std::string(data["type"].GetString()) == "Feature") { - convertFeature(features, data, tolerance); - } else { - - /* In this case, we want to pass the entire JSON document as the - * value for key 'geometry' in a new JSON object, like so: - * - * convertFeature(features, ["geometry": data], tolerance); (pseudo-code) - * - * Currently this fails due to lack of a proper copy constructor. - * Maybe use move semantics? */ - -// JSValue feature; -// feature.SetObject(); -// feature.AddMember("geometry", data, data.GetAllocator()); -// convertFeature(features, feature, tolerance); - - } - - return std::move(features); -} - -void Convert::convertFeature(std::vector &features, const JSValue &feature, double tolerance) { - - const JSValue &geom = feature["geometry"]; - const JSValue &rawType = geom["type"]; - std::string type { rawType.GetString(), rawType.GetStringLength() }; - Tags tags; - - if (feature.HasMember("properties") && feature["properties"].IsObject()) { - const JSValue &properties = feature["properties"]; - rapidjson::Value::ConstMemberIterator itr = properties.MemberBegin(); - for (; itr != properties.MemberEnd(); ++itr) { - std::string key { itr->name.GetString(), itr->name.GetStringLength() }; - std::string val { itr->value.GetString(), itr->value.GetStringLength() }; - tags[key] = val; - } - } - - if (type == "Point") { - - std::array coordinates = {{ 0, 0 }}; - if (geom.HasMember("coordinates")) { - const JSValue &rawCoordinates = geom["coordinates"]; - if (rawCoordinates.IsArray()) { - coordinates[0] = rawCoordinates[(rapidjson::SizeType)0].GetDouble(); - coordinates[1] = rawCoordinates[(rapidjson::SizeType)1].GetDouble(); - } - } - ProjectedPoint point = projectPoint(LonLat(coordinates)); - - std::vector members = { point }; - ProjectedGeometryContainer geometry(members); - - features.push_back(create(tags, ProjectedFeatureType::Point, geometry)); - - } else if (type == "MultiPoint") { - - std::vector> coordinatePairs; - std::vector points; - if (geom.HasMember("coordinates")) { - const JSValue &rawCoordinatePairs = geom["coordinates"]; - if (rawCoordinatePairs.IsArray()) { - for (rapidjson::SizeType i = 0; i < rawCoordinatePairs.Size(); ++i) { - std::array coordinates = {{ 0, 0 }}; - const JSValue &rawCoordinates = rawCoordinatePairs[i]; - if (rawCoordinates.IsArray()) { - coordinates[0] = rawCoordinates[(rapidjson::SizeType)0].GetDouble(); - coordinates[1] = rawCoordinates[(rapidjson::SizeType)1].GetDouble(); - } - points.push_back(LonLat(coordinates)); - } - } - } - - ProjectedGeometryContainer geometry = project(points); - - features.push_back(create(tags, ProjectedFeatureType::Point, geometry)); - - } else if (type == "LineString") { - - std::vector> coordinatePairs; - std::vector points; - if (geom.HasMember("coordinates")) { - const JSValue &rawCoordinatePairs = geom["coordinates"]; - if (rawCoordinatePairs.IsArray()) { - for (rapidjson::SizeType i = 0; i < rawCoordinatePairs.Size(); ++i) { - std::array coordinates = {{ 0, 0 }}; - const JSValue &rawCoordinates = rawCoordinatePairs[i]; - if (rawCoordinates.IsArray()) { - coordinates[0] = rawCoordinates[(rapidjson::SizeType)0].GetDouble(); - coordinates[1] = rawCoordinates[(rapidjson::SizeType)1].GetDouble(); - } - points.push_back(LonLat(coordinates)); - } - } - } - - ProjectedGeometryContainer geometry({ project(points, tolerance) }); - - features.push_back(create(tags, ProjectedFeatureType::LineString, geometry)); - - } else if (type == "MultiLineString" || type == "Polygon") { - - ProjectedGeometryContainer rings; - if (geom.HasMember("coordinates")) { - const JSValue &rawLines = geom["coordinates"]; - if (rawLines.IsArray()) { - for (rapidjson::SizeType i = 0; i < rawLines.Size(); ++i) { - const JSValue &rawCoordinatePairs = rawLines[i]; - if (rawCoordinatePairs.IsArray()) { - std::vector points; - for (rapidjson::SizeType j = 0; j < rawCoordinatePairs.Size(); ++j) { - std::array coordinates = {{ 0, 0 }}; - const JSValue &rawCoordinates = rawCoordinatePairs[j]; - if (rawCoordinates.IsArray()) { - coordinates[0] = rawCoordinates[(rapidjson::SizeType)0].GetDouble(); - coordinates[1] = rawCoordinates[(rapidjson::SizeType)1].GetDouble(); - } - points.push_back(LonLat(coordinates)); - } - ProjectedGeometryContainer ring = project(points, tolerance); - rings.members.push_back(ring); - } - } - } - } - - ProjectedFeatureType projectedType = (type == "Polygon" ? - ProjectedFeatureType::Polygon : - ProjectedFeatureType::LineString); - - ProjectedGeometryContainer *geometry = &rings; - - features.push_back(create(tags, projectedType, *geometry)); - - printf("features now has %lu items\n", (unsigned long)features.size()); - } - - else if (type == "MultiPolygon") { - - ProjectedGeometryContainer rings; - if (geom.HasMember("coordinates")) { - const JSValue &rawPolygons = geom["coordinates"]; - if (rawPolygons.IsArray()) { - for (rapidjson::SizeType i = 0; i < rawPolygons.Size(); ++i) { - std::vector points; - const JSValue &rawLines = rawPolygons[i]; - for (rapidjson::SizeType j = 0; j < rawLines.Size(); ++j) { - std::array coordinates = {{ 0, 0 }}; - const JSValue &rawCoordinatePairs = rawLines[i]; - if (rawCoordinatePairs.IsArray()) { - coordinates[0] = rawCoordinatePairs[(rapidjson::SizeType)0].GetDouble(); - coordinates[1] = rawCoordinatePairs[(rapidjson::SizeType)1].GetDouble(); - } - points.push_back(LonLat(coordinates)); - } - ProjectedGeometryContainer ring = project(points, tolerance); - rings.members.push_back(ring); - } - } - } - - ProjectedGeometryContainer *geometry = &rings; - - features.push_back(create(tags, ProjectedFeatureType::Polygon, *geometry)); - - } else if (type == "GeometryCollection") { - - if (geom.HasMember("geometries")) { - const JSValue &rawGeometries = geom["geometries"]; - if (rawGeometries.IsArray()) { - for (rapidjson::SizeType i = 0; i < rawGeometries.Size(); ++i) { - convertFeature(features, rawGeometries[i], tolerance); - } - } - } - - } else { - - printf("unsupported GeoJSON type: %s\n", geom["type"].GetString()); - - } -} - -ProjectedFeature Convert::create(Tags tags, ProjectedFeatureType type, ProjectedGeometry geometry) { - - ProjectedFeature feature(geometry, type, tags); - calcBBox(feature); - - return std::move(feature); -} - -ProjectedGeometryContainer Convert::project(const std::vector &lonlats, double tolerance) { - - ProjectedGeometryContainer projected; - for (size_t i = 0; i < lonlats.size(); ++i) { - projected.members.push_back(projectPoint(lonlats[i])); - } - if (tolerance) { - Simplify::simplify(projected, tolerance); - calcSize(projected); - } - - return std::move(projected); -} - -ProjectedPoint Convert::projectPoint(const LonLat &p_) { - - double sine = std::sin(p_.lat * M_PI / 180); - double x = p_.lon / 360 + 0.5; - double y = 0.5 - 0.25 * std::log((1 + sine) / (1 - sine)) / M_PI; - - return ProjectedPoint(x, y, 0); -} - -void Convert::calcSize(ProjectedGeometryContainer &geometryContainer) { - - double area = 0, dist = 0; - ProjectedPoint a, b; - - for (size_t i = 0; i < geometryContainer.members.size() - 1; ++i) { - a = (b ? b : geometryContainer.members[i].get()); - b = geometryContainer.members[i + 1].get(); - - area += a.x * b.y - b.x * a.y; - dist += std::abs(b.x - a.x) + std::abs(b.y - a.y); - } - - geometryContainer.area = std::abs(area / 2); - geometryContainer.dist = dist; -} - -void Convert::calcBBox(ProjectedFeature &feature) { - - ProjectedGeometryContainer *geometry = &(feature.geometry.get()); - ProjectedPoint *minPoint = &(feature.minPoint); - ProjectedPoint *maxPoint = &(feature.maxPoint); - - if (feature.type == ProjectedFeatureType::Point) { - calcRingBBox(*minPoint, *maxPoint, *geometry); - } else { - for (size_t i = 0; i < geometry->members.size(); ++i) { - ProjectedGeometryContainer *featureGeometry = &(geometry->members[i].get()); - calcRingBBox(*minPoint, *maxPoint, *featureGeometry); - } - } -} - -void Convert::calcRingBBox(ProjectedPoint &minPoint, ProjectedPoint &maxPoint, const ProjectedGeometryContainer &geometry) { - - for (size_t i = 0; i < geometry.members.size(); ++i) { - const ProjectedPoint *p = &(geometry.members[i].get()); - minPoint.x = std::min(p->x, minPoint.x); - maxPoint.x = std::max(p->x, maxPoint.x); - minPoint.y = std::min(p->y, minPoint.y); - maxPoint.y = std::max(p->y, maxPoint.y); - } -} - -#pragma mark - Simplify - -void Simplify::simplify(ProjectedGeometryContainer &points, double tolerance) { - - const double sqTolerance = tolerance * tolerance; - const size_t len = points.members.size(); - size_t first = 0; - size_t last = len - 1; - std::stack stack; - double maxSqDist = 0; - double sqDist = 0; - size_t index = 0; - - points.members[first].get().z = 1; - points.members[last].get().z = 1; - - while (last) { - - maxSqDist = 0; - - for (size_t i = (first + 1); i < last; ++i) { - sqDist = getSqSegDist(points.members[i].get(), - points.members[first].get(), - points.members[last].get()); - - if (sqDist > maxSqDist) { - index = i; - maxSqDist = sqDist; - } - } - - if (maxSqDist > sqTolerance) { - points.members[index].get().z = maxSqDist; - stack.push(first); - stack.push(index); - stack.push(index); - stack.push(last); - } - - if (stack.size()) { - last = stack.top(); - stack.pop(); - } else { - last = 0; - } - - if (stack.size()) { - first = stack.top(); - stack.pop(); - } else { - first = 0; - } - } -} - -double Simplify::getSqSegDist(const ProjectedPoint &p, const ProjectedPoint &a, const ProjectedPoint &b) { - - double x = a.x; - double y = a.y; - double dx = b.x - a.x; - double dy = b.y - a.y; - - if (dx || dy) { - - 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) { - x += dx * t; - y += dy * t; - } - } - - dx = p.x - x; - dy = p.y - y; - - return dx * dx + dy * dy; -} - -#pragma mark - Clip - -std::vector Clip::clip(const std::vector features, uint32_t scale, double k1, double k2, uint8_t axis, ProjectedPoint (*intersect)(const ProjectedPoint&, const ProjectedPoint&, double)) { - - k1 /= scale; - k2 /= scale; - - std::vector clipped; - - for (size_t i = 0; i < features.size(); ++i) { - - const ProjectedFeature feature = features[i]; - const ProjectedGeometry geometry = feature.geometry; - const ProjectedFeatureType type = feature.type; - double min = 0; - double max = 0; - - if (feature.minPoint) { - min = (axis == 0 ? feature.minPoint.x : feature.minPoint.y); - max = (axis == 0 ? feature.maxPoint.x : feature.maxPoint.y); - - if (min >= k1 && max <= k2) { - clipped.push_back(feature); - continue; - } else if (min > k2 || max < k1) { - continue; - } - } - - ProjectedGeometryContainer slices; - - if (type == ProjectedFeatureType::Point) { - slices = clipPoints(geometry.get(), k1, k2, axis); - } else { - slices = clipGeometry(geometry.get(), k1, k2, axis, intersect, (type == ProjectedFeatureType::Polygon)); - } - - if (slices.members.size()) { - clipped.push_back(ProjectedFeature(slices, type, features[i].tags)); - } - } - - return std::move(clipped); -} - -ProjectedGeometryContainer Clip::clipPoints(ProjectedGeometryContainer geometry, double k1, double k2, uint8_t axis) { - - ProjectedGeometryContainer slice; - - for (size_t i = 0; i < geometry.members.size(); ++i) { - ProjectedPoint *a = &(geometry.members[i].get()); - double ak = (axis == 0 ? a->x : a->y); - - if (ak >= k1 && ak <= k2) { - slice.members.push_back(*a); - } - } - - return std::move(slice); -} - -ProjectedGeometryContainer Clip::clipGeometry(ProjectedGeometryContainer geometry, double k1, double k2, uint8_t axis, ProjectedPoint (*intersect)(const ProjectedPoint&, const ProjectedPoint&, double), bool closed) { - - ProjectedGeometryContainer slices; - - for (size_t i = 0; i < geometry.members.size(); ++i) { - - double ak = 0; - double bk = 0; - ProjectedPoint b; - const ProjectedGeometryContainer *points = &(geometry.members[i].get()); - const double area = points->area; - const double dist = points->dist; - const size_t len = points->members.size(); - ProjectedPoint a; - - ProjectedGeometryContainer slice; - - for (size_t j = 0; j < (len - 1); ++j) { - a = (b ? b : points->members[j].get()); - b = points->members[j + 1].get(); - ak = (bk ? bk : (axis == 0 ? a.x : a.y)); - bk = (axis == 0 ? b.x : b.y); - - if (ak < k1) { - if (bk > k2) { - slice.members.push_back(intersect(a, b, k1)); - slice.members.push_back(intersect(a, b, k2)); - if (!closed) { - slice = newSlice(slices, slice, area, dist); - } - } else if (bk >= k1) { - slice.members.push_back(intersect(a, b, k1)); - } - } else if (ak > k2) { - if (bk < k1) { - slice.members.push_back(intersect(a, b, k2)); - slice.members.push_back(intersect(a, b, k1)); - if (!closed) { - slice = newSlice(slices, slice, area, dist); - } - } else if (bk <= k2) { - slice.members.push_back(intersect(a, b, k2)); - } - } else { - slice.members.push_back(a); - - if (bk < k1) { - slice.members.push_back(intersect(a, b, k1)); - if (!closed) { - slice = newSlice(slices, slice, area, dist); - } - } else if (bk > k2) { - slice.members.push_back(intersect(a, b, k2)); - if (!closed) { - slice = newSlice(slices, slice, area, dist); - } - } - } - } - - a = points->members[len - 1].get(); - ak = (axis == 0 ? a.x : a.y); - - if (ak >= k1 && ak <= k2) { - slice.members.push_back(a); - } - - if (closed && slice.members.size()) { - const ProjectedPoint *first = &(slice.members[0].get()); - const ProjectedPoint *last = &(slice.members[slice.members.size() - 1].get()); - if (first != last) { - slice.members.push_back(ProjectedPoint(first->x, first->y, first->z)); - } - } - - newSlice(slices, slice, area, dist); - } - - return std::move(slices); -} - -ProjectedGeometryContainer Clip::newSlice(ProjectedGeometryContainer &slices, ProjectedGeometryContainer &slice, double area, double dist) { - - if (slice.members.size()) { - slice.area = area; - slice.dist = dist; - slices.members.push_back(slice); - } - - return ProjectedGeometryContainer(); -} - } /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ diff --git a/src/mbgl/util/geojsonvt.hpp b/src/mbgl/util/geojsonvt.hpp index d141e40bd25..a9bd1934710 100644 --- a/src/mbgl/util/geojsonvt.hpp +++ b/src/mbgl/util/geojsonvt.hpp @@ -1,184 +1,16 @@ #ifndef MAPBOX_UTIL_GEOJSONVT #define MAPBOX_UTIL_GEOJSONVT -#include -#include +#include "geojsonvt_tile.hpp" +#include "geojsonvt_types.hpp" + #include -#include -#include -#include #include - -#include - -#include +#include +#include namespace mapbox { namespace util { namespace geojsonvt { -#pragma mark - - -class Time { -public: - inline static void time(std::string activity) { - Time::activities[activity] = clock(); - } - - inline static void timeEnd(std::string activity) { - printf("%s: %fms\n", activity.c_str(), double(clock() - Time::activities[activity]) / (CLOCKS_PER_SEC / 1000)); - } - -private: - static std::unordered_map activities; -}; - -#pragma mark - - -struct LonLat { - LonLat(std::array coordinates) - : lon(coordinates[0]), lat(coordinates[1]) {} - - LonLat(double lon_, double lat_) - : lon(lon_), lat(lat_) {} - - double lon; - double lat; -}; - -#pragma mark - - -class ProjectedPoint; -class ProjectedGeometryContainer; - -using ProjectedGeometry = mapbox::util::variant; - -#pragma mark - - -class ProjectedPoint { -public: - ProjectedPoint(double x_ = -1, double y_ = -1, double z_ = -1) - : x(x_), y(y_), z(z_) {} - - inline operator bool() const { return (x >= 0 && y >= 0 && z >= 0); } - inline bool operator!= (const ProjectedPoint& rhs) const { return (x != rhs.x || y != rhs.y || z != rhs.z); } - -public: - double x = -1; - double y = -1; - double z = -1; -}; - -#pragma mark - - -using JSDocument = rapidjson::Document; -using JSValue = rapidjson::Value; - -#pragma mark - - -class ProjectedGeometryContainer { -public: - ProjectedGeometryContainer() {} - ProjectedGeometryContainer(std::vector members_) - : members(members_) {} - -public: - std::vector members; - double area = 0; - double dist = 0; -}; - -#pragma mark - - -using Tags = std::map; - -#pragma mark - - -enum class ProjectedFeatureType: uint8_t { - Point = 1, - LineString = 2, - Polygon = 3 -}; - -#pragma mark - - -class ProjectedFeature { -public: - ProjectedFeature(ProjectedGeometry geometry_, ProjectedFeatureType type_, Tags tags_) - : geometry(geometry_), type(type_), tags(tags_) {} - -public: - ProjectedGeometry geometry; - ProjectedFeatureType type; - Tags tags; - ProjectedPoint minPoint = ProjectedPoint(); - ProjectedPoint maxPoint = ProjectedPoint(); -}; - -#pragma mark - - -class TilePoint; -class TileRing; - -using TileGeometry = mapbox::util::variant; - -#pragma mark - - -class TilePoint { -public: - TilePoint(int16_t x_, int16_t y_) - : x(x_), y(y_) {} - -public: - const int16_t x = 0; - const int16_t y = 0; -}; - -#pragma mark - - -class TileRing { -public: - std::vector points; -}; - -#pragma mark - - -typedef ProjectedFeatureType TileFeatureType; - -#pragma mark - - -class TileFeature { -public: - TileFeature(std::vector geometry_, TileFeatureType type_, Tags tags_) - : geometry(geometry_), type(type_), tags(tags_) {} - -public: - std::vector geometry; - TileFeatureType type; - Tags tags; -}; - -#pragma mark - - -class Tile { -public: - static Tile createTile(std::vector &features, uint32_t z2, uint32_t tx, uint32_t ty, double tolerance, uint16_t extent, bool noSimplify); - - static void addFeature(Tile &tile, ProjectedFeature &feature, uint32_t z2, uint32_t tx, uint32_t ty, double tolerance, uint16_t extent, bool noSimplify); - - inline operator bool() const { return this->numPoints > 0; } - -private: - static TilePoint transformPoint(const ProjectedPoint &p, uint32_t z2, uint32_t tx, uint32_t ty, uint16_t extent); - -public: - std::vector features; - uint32_t numPoints = 0; - uint32_t numSimplified = 0; - uint32_t numFeatures = 0; - std::vector source; -}; - -#pragma mark - - class GeoJSONVT { public: static std::vector convertFeatures(const std::string &data, uint8_t baseZoom = 14, double tolerance = 3, bool debug = false); @@ -222,52 +54,6 @@ class GeoJSONVT { uint16_t total = 0; }; -#pragma mark - - -class Convert { -public: - static std::vector convert(const JSDocument &data, double tolerance); - - static ProjectedFeature create(Tags tags, ProjectedFeatureType type, ProjectedGeometry geometry); - - static ProjectedGeometryContainer project(const std::vector &lonlats, double tolerance = 0); - -private: - static void convertFeature(std::vector &features, const JSValue &feature, double tolerance); - - static ProjectedPoint projectPoint(const LonLat &p); - - static void calcSize(ProjectedGeometryContainer &geometryContainer); - - static void calcBBox(ProjectedFeature &feature); - - static void calcRingBBox(ProjectedPoint &minPoint, ProjectedPoint &maxPoint, const ProjectedGeometryContainer &geometry); -}; - -#pragma mark - - -class Simplify { -public: - static void simplify(ProjectedGeometryContainer &points, double tolerance); - -private: - static double getSqSegDist(const ProjectedPoint &p, const ProjectedPoint &a, const ProjectedPoint &b); -}; - -#pragma mark - - -class Clip { -public: - static std::vector clip(std::vector features, uint32_t scale, double k1, double k2, uint8_t axis, ProjectedPoint (*intersect)(const ProjectedPoint&, const ProjectedPoint&, double)); - -private: - static ProjectedGeometryContainer clipPoints(ProjectedGeometryContainer geometry, double k1, double k2, uint8_t axis); - - static ProjectedGeometryContainer clipGeometry(ProjectedGeometryContainer geometry, double k1, double k2, uint8_t axis, ProjectedPoint (*intersect)(const ProjectedPoint&, const ProjectedPoint&, double), bool closed); - - static ProjectedGeometryContainer newSlice(ProjectedGeometryContainer &slices, ProjectedGeometryContainer &slice, double area, double dist); -}; - } /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ #endif // MAPBOX_UTIL_GEOJSONVT diff --git a/src/mbgl/util/geojsonvt_clip.cpp b/src/mbgl/util/geojsonvt_clip.cpp new file mode 100644 index 00000000000..52a954cdf4b --- /dev/null +++ b/src/mbgl/util/geojsonvt_clip.cpp @@ -0,0 +1,156 @@ +#include "geojsonvt_clip.hpp" + +namespace mapbox { namespace util { namespace geojsonvt { + +std::vector Clip::clip(const std::vector features, uint32_t scale, double k1, double k2, uint8_t axis, ProjectedPoint (*intersect)(const ProjectedPoint&, const ProjectedPoint&, double)) { + + k1 /= scale; + k2 /= scale; + + std::vector clipped; + + for (size_t i = 0; i < features.size(); ++i) { + + const ProjectedFeature feature = features[i]; + const ProjectedGeometry geometry = feature.geometry; + const ProjectedFeatureType type = feature.type; + double min = 0; + double max = 0; + + if (feature.minPoint) { + min = (axis == 0 ? feature.minPoint.x : feature.minPoint.y); + max = (axis == 0 ? feature.maxPoint.x : feature.maxPoint.y); + + if (min >= k1 && max <= k2) { + clipped.push_back(feature); + continue; + } else if (min > k2 || max < k1) { + continue; + } + } + + ProjectedGeometryContainer slices; + + if (type == ProjectedFeatureType::Point) { + slices = clipPoints(geometry.get(), k1, k2, axis); + } else { + slices = clipGeometry(geometry.get(), k1, k2, axis, intersect, (type == ProjectedFeatureType::Polygon)); + } + + if (slices.members.size()) { + clipped.push_back(ProjectedFeature(slices, type, features[i].tags)); + } + } + + return std::move(clipped); +} + +ProjectedGeometryContainer Clip::clipPoints(ProjectedGeometryContainer geometry, double k1, double k2, uint8_t axis) { + + ProjectedGeometryContainer slice; + + for (size_t i = 0; i < geometry.members.size(); ++i) { + ProjectedPoint *a = &(geometry.members[i].get()); + double ak = (axis == 0 ? a->x : a->y); + + if (ak >= k1 && ak <= k2) { + slice.members.push_back(*a); + } + } + + return std::move(slice); +} + +ProjectedGeometryContainer Clip::clipGeometry(ProjectedGeometryContainer geometry, double k1, double k2, uint8_t axis, ProjectedPoint (*intersect)(const ProjectedPoint&, const ProjectedPoint&, double), bool closed) { + + ProjectedGeometryContainer slices; + + for (size_t i = 0; i < geometry.members.size(); ++i) { + + double ak = 0; + double bk = 0; + ProjectedPoint b; + const ProjectedGeometryContainer *points = &(geometry.members[i].get()); + const double area = points->area; + const double dist = points->dist; + const size_t len = points->members.size(); + ProjectedPoint a; + + ProjectedGeometryContainer slice; + + for (size_t j = 0; j < (len - 1); ++j) { + a = (b ? b : points->members[j].get()); + b = points->members[j + 1].get(); + ak = (bk ? bk : (axis == 0 ? a.x : a.y)); + bk = (axis == 0 ? b.x : b.y); + + if (ak < k1) { + if (bk > k2) { + slice.members.push_back(intersect(a, b, k1)); + slice.members.push_back(intersect(a, b, k2)); + if (!closed) { + slice = newSlice(slices, slice, area, dist); + } + } else if (bk >= k1) { + slice.members.push_back(intersect(a, b, k1)); + } + } else if (ak > k2) { + if (bk < k1) { + slice.members.push_back(intersect(a, b, k2)); + slice.members.push_back(intersect(a, b, k1)); + if (!closed) { + slice = newSlice(slices, slice, area, dist); + } + } else if (bk <= k2) { + slice.members.push_back(intersect(a, b, k2)); + } + } else { + slice.members.push_back(a); + + if (bk < k1) { + slice.members.push_back(intersect(a, b, k1)); + if (!closed) { + slice = newSlice(slices, slice, area, dist); + } + } else if (bk > k2) { + slice.members.push_back(intersect(a, b, k2)); + if (!closed) { + slice = newSlice(slices, slice, area, dist); + } + } + } + } + + a = points->members[len - 1].get(); + ak = (axis == 0 ? a.x : a.y); + + if (ak >= k1 && ak <= k2) { + slice.members.push_back(a); + } + + if (closed && slice.members.size()) { + const ProjectedPoint *first = &(slice.members[0].get()); + const ProjectedPoint *last = &(slice.members[slice.members.size() - 1].get()); + if (first != last) { + slice.members.push_back(ProjectedPoint(first->x, first->y, first->z)); + } + } + + newSlice(slices, slice, area, dist); + } + + return std::move(slices); +} + +ProjectedGeometryContainer Clip::newSlice(ProjectedGeometryContainer &slices, ProjectedGeometryContainer &slice, double area, double dist) { + + if (slice.members.size()) { + slice.area = area; + slice.dist = dist; + slices.members.push_back(slice); + } + + return ProjectedGeometryContainer(); +} + +} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ diff --git a/src/mbgl/util/geojsonvt_clip.hpp b/src/mbgl/util/geojsonvt_clip.hpp new file mode 100644 index 00000000000..d314297b288 --- /dev/null +++ b/src/mbgl/util/geojsonvt_clip.hpp @@ -0,0 +1,24 @@ +#ifndef MAPBOX_UTIL_GEOJSONVT_CLIP +#define MAPBOX_UTIL_GEOJSONVT_CLIP + +#include "geojsonvt_types.hpp" + +#include + +namespace mapbox { namespace util { namespace geojsonvt { + +class Clip { +public: + static std::vector clip(std::vector features, uint32_t scale, double k1, double k2, uint8_t axis, ProjectedPoint (*intersect)(const ProjectedPoint&, const ProjectedPoint&, double)); + +private: + static ProjectedGeometryContainer clipPoints(ProjectedGeometryContainer geometry, double k1, double k2, uint8_t axis); + + static ProjectedGeometryContainer clipGeometry(ProjectedGeometryContainer geometry, double k1, double k2, uint8_t axis, ProjectedPoint (*intersect)(const ProjectedPoint&, const ProjectedPoint&, double), bool closed); + + static ProjectedGeometryContainer newSlice(ProjectedGeometryContainer &slices, ProjectedGeometryContainer &slice, double area, double dist); +}; + +} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ + +#endif // MAPBOX_UTIL_GEOJSONVT_CLIP diff --git a/src/mbgl/util/geojsonvt_convert.cpp b/src/mbgl/util/geojsonvt_convert.cpp new file mode 100644 index 00000000000..f2001e534e9 --- /dev/null +++ b/src/mbgl/util/geojsonvt_convert.cpp @@ -0,0 +1,285 @@ +#include "geojsonvt_convert.hpp" +#include "geojsonvt_simplify.hpp" + +#include +#include +#include + +namespace mapbox { namespace util { namespace geojsonvt { + +std::vector Convert::convert(const JSDocument &data, double tolerance) { + + std::vector features; + const JSValue &rawType = data["type"]; + + if (std::string(rawType.GetString()) == "FeatureCollection") { + if (data.HasMember("features")) { + const JSValue &rawFeatures = data["features"]; + if (rawFeatures.IsArray()) { + printf("there are %i total features to convert\n", rawFeatures.Size()); + for (rapidjson::SizeType i = 0; i < rawFeatures.Size(); ++i) { + convertFeature(features, rawFeatures[i], tolerance); + } + } + } + } else if (std::string(data["type"].GetString()) == "Feature") { + convertFeature(features, data, tolerance); + } else { + + /* In this case, we want to pass the entire JSON document as the + * value for key 'geometry' in a new JSON object, like so: + * + * convertFeature(features, ["geometry": data], tolerance); (pseudo-code) + * + * Currently this fails due to lack of a proper copy constructor. + * Maybe use move semantics? */ + +// JSValue feature; +// feature.SetObject(); +// feature.AddMember("geometry", data, data.GetAllocator()); +// convertFeature(features, feature, tolerance); + + } + + return std::move(features); +} + +void Convert::convertFeature(std::vector &features, const JSValue &feature, double tolerance) { + + const JSValue &geom = feature["geometry"]; + const JSValue &rawType = geom["type"]; + std::string type { rawType.GetString(), rawType.GetStringLength() }; + Tags tags; + + if (feature.HasMember("properties") && feature["properties"].IsObject()) { + const JSValue &properties = feature["properties"]; + rapidjson::Value::ConstMemberIterator itr = properties.MemberBegin(); + for (; itr != properties.MemberEnd(); ++itr) { + std::string key { itr->name.GetString(), itr->name.GetStringLength() }; + std::string val { itr->value.GetString(), itr->value.GetStringLength() }; + tags[key] = val; + } + } + + if (type == "Point") { + + std::array coordinates = {{ 0, 0 }}; + if (geom.HasMember("coordinates")) { + const JSValue &rawCoordinates = geom["coordinates"]; + if (rawCoordinates.IsArray()) { + coordinates[0] = rawCoordinates[(rapidjson::SizeType)0].GetDouble(); + coordinates[1] = rawCoordinates[(rapidjson::SizeType)1].GetDouble(); + } + } + ProjectedPoint point = projectPoint(LonLat(coordinates)); + + std::vector members = { point }; + ProjectedGeometryContainer geometry(members); + + features.push_back(create(tags, ProjectedFeatureType::Point, geometry)); + + } else if (type == "MultiPoint") { + + std::vector> coordinatePairs; + std::vector points; + if (geom.HasMember("coordinates")) { + const JSValue &rawCoordinatePairs = geom["coordinates"]; + if (rawCoordinatePairs.IsArray()) { + for (rapidjson::SizeType i = 0; i < rawCoordinatePairs.Size(); ++i) { + std::array coordinates = {{ 0, 0 }}; + const JSValue &rawCoordinates = rawCoordinatePairs[i]; + if (rawCoordinates.IsArray()) { + coordinates[0] = rawCoordinates[(rapidjson::SizeType)0].GetDouble(); + coordinates[1] = rawCoordinates[(rapidjson::SizeType)1].GetDouble(); + } + points.push_back(LonLat(coordinates)); + } + } + } + + ProjectedGeometryContainer geometry = project(points); + + features.push_back(create(tags, ProjectedFeatureType::Point, geometry)); + + } else if (type == "LineString") { + + std::vector> coordinatePairs; + std::vector points; + if (geom.HasMember("coordinates")) { + const JSValue &rawCoordinatePairs = geom["coordinates"]; + if (rawCoordinatePairs.IsArray()) { + for (rapidjson::SizeType i = 0; i < rawCoordinatePairs.Size(); ++i) { + std::array coordinates = {{ 0, 0 }}; + const JSValue &rawCoordinates = rawCoordinatePairs[i]; + if (rawCoordinates.IsArray()) { + coordinates[0] = rawCoordinates[(rapidjson::SizeType)0].GetDouble(); + coordinates[1] = rawCoordinates[(rapidjson::SizeType)1].GetDouble(); + } + points.push_back(LonLat(coordinates)); + } + } + } + + ProjectedGeometryContainer geometry({ project(points, tolerance) }); + + features.push_back(create(tags, ProjectedFeatureType::LineString, geometry)); + + } else if (type == "MultiLineString" || type == "Polygon") { + + ProjectedGeometryContainer rings; + if (geom.HasMember("coordinates")) { + const JSValue &rawLines = geom["coordinates"]; + if (rawLines.IsArray()) { + for (rapidjson::SizeType i = 0; i < rawLines.Size(); ++i) { + const JSValue &rawCoordinatePairs = rawLines[i]; + if (rawCoordinatePairs.IsArray()) { + std::vector points; + for (rapidjson::SizeType j = 0; j < rawCoordinatePairs.Size(); ++j) { + std::array coordinates = {{ 0, 0 }}; + const JSValue &rawCoordinates = rawCoordinatePairs[j]; + if (rawCoordinates.IsArray()) { + coordinates[0] = rawCoordinates[(rapidjson::SizeType)0].GetDouble(); + coordinates[1] = rawCoordinates[(rapidjson::SizeType)1].GetDouble(); + } + points.push_back(LonLat(coordinates)); + } + ProjectedGeometryContainer ring = project(points, tolerance); + rings.members.push_back(ring); + } + } + } + } + + ProjectedFeatureType projectedType = (type == "Polygon" ? + ProjectedFeatureType::Polygon : + ProjectedFeatureType::LineString); + + ProjectedGeometryContainer *geometry = &rings; + + features.push_back(create(tags, projectedType, *geometry)); + + printf("features now has %lu items\n", (unsigned long)features.size()); + } + + else if (type == "MultiPolygon") { + + ProjectedGeometryContainer rings; + if (geom.HasMember("coordinates")) { + const JSValue &rawPolygons = geom["coordinates"]; + if (rawPolygons.IsArray()) { + for (rapidjson::SizeType i = 0; i < rawPolygons.Size(); ++i) { + std::vector points; + const JSValue &rawLines = rawPolygons[i]; + for (rapidjson::SizeType j = 0; j < rawLines.Size(); ++j) { + std::array coordinates = {{ 0, 0 }}; + const JSValue &rawCoordinatePairs = rawLines[i]; + if (rawCoordinatePairs.IsArray()) { + coordinates[0] = rawCoordinatePairs[(rapidjson::SizeType)0].GetDouble(); + coordinates[1] = rawCoordinatePairs[(rapidjson::SizeType)1].GetDouble(); + } + points.push_back(LonLat(coordinates)); + } + ProjectedGeometryContainer ring = project(points, tolerance); + rings.members.push_back(ring); + } + } + } + + ProjectedGeometryContainer *geometry = &rings; + + features.push_back(create(tags, ProjectedFeatureType::Polygon, *geometry)); + + } else if (type == "GeometryCollection") { + + if (geom.HasMember("geometries")) { + const JSValue &rawGeometries = geom["geometries"]; + if (rawGeometries.IsArray()) { + for (rapidjson::SizeType i = 0; i < rawGeometries.Size(); ++i) { + convertFeature(features, rawGeometries[i], tolerance); + } + } + } + + } else { + + printf("unsupported GeoJSON type: %s\n", geom["type"].GetString()); + + } +} + +ProjectedFeature Convert::create(Tags tags, ProjectedFeatureType type, ProjectedGeometry geometry) { + + ProjectedFeature feature(geometry, type, tags); + calcBBox(feature); + + return std::move(feature); +} + +ProjectedGeometryContainer Convert::project(const std::vector &lonlats, double tolerance) { + + ProjectedGeometryContainer projected; + for (size_t i = 0; i < lonlats.size(); ++i) { + projected.members.push_back(projectPoint(lonlats[i])); + } + if (tolerance) { + Simplify::simplify(projected, tolerance); + calcSize(projected); + } + + return std::move(projected); +} + +ProjectedPoint Convert::projectPoint(const LonLat &p_) { + + double sine = std::sin(p_.lat * M_PI / 180); + double x = p_.lon / 360 + 0.5; + double y = 0.5 - 0.25 * std::log((1 + sine) / (1 - sine)) / M_PI; + + return ProjectedPoint(x, y, 0); +} + +void Convert::calcSize(ProjectedGeometryContainer &geometryContainer) { + + double area = 0, dist = 0; + ProjectedPoint a, b; + + for (size_t i = 0; i < geometryContainer.members.size() - 1; ++i) { + a = (b ? b : geometryContainer.members[i].get()); + b = geometryContainer.members[i + 1].get(); + + area += a.x * b.y - b.x * a.y; + dist += std::abs(b.x - a.x) + std::abs(b.y - a.y); + } + + geometryContainer.area = std::abs(area / 2); + geometryContainer.dist = dist; +} + +void Convert::calcBBox(ProjectedFeature &feature) { + + ProjectedGeometryContainer *geometry = &(feature.geometry.get()); + ProjectedPoint *minPoint = &(feature.minPoint); + ProjectedPoint *maxPoint = &(feature.maxPoint); + + if (feature.type == ProjectedFeatureType::Point) { + calcRingBBox(*minPoint, *maxPoint, *geometry); + } else { + for (size_t i = 0; i < geometry->members.size(); ++i) { + ProjectedGeometryContainer *featureGeometry = &(geometry->members[i].get()); + calcRingBBox(*minPoint, *maxPoint, *featureGeometry); + } + } +} + +void Convert::calcRingBBox(ProjectedPoint &minPoint, ProjectedPoint &maxPoint, const ProjectedGeometryContainer &geometry) { + + for (size_t i = 0; i < geometry.members.size(); ++i) { + const ProjectedPoint *p = &(geometry.members[i].get()); + minPoint.x = std::min(p->x, minPoint.x); + maxPoint.x = std::max(p->x, maxPoint.x); + minPoint.y = std::min(p->y, minPoint.y); + maxPoint.y = std::max(p->y, maxPoint.y); + } +} + +} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ diff --git a/src/mbgl/util/geojsonvt_convert.hpp b/src/mbgl/util/geojsonvt_convert.hpp new file mode 100644 index 00000000000..8861be13f05 --- /dev/null +++ b/src/mbgl/util/geojsonvt_convert.hpp @@ -0,0 +1,32 @@ +#ifndef MAPBOX_UTIL_GEOJSONVT_CONVERT +#define MAPBOX_UTIL_GEOJSONVT_CONVERT + +#include "geojsonvt_types.hpp" + +#include + +namespace mapbox { namespace util { namespace geojsonvt { + +class Convert { +public: + static std::vector convert(const JSDocument &data, double tolerance); + + static ProjectedFeature create(Tags tags, ProjectedFeatureType type, ProjectedGeometry geometry); + + static ProjectedGeometryContainer project(const std::vector &lonlats, double tolerance = 0); + +private: + static void convertFeature(std::vector &features, const JSValue &feature, double tolerance); + + static ProjectedPoint projectPoint(const LonLat &p); + + static void calcSize(ProjectedGeometryContainer &geometryContainer); + + static void calcBBox(ProjectedFeature &feature); + + static void calcRingBBox(ProjectedPoint &minPoint, ProjectedPoint &maxPoint, const ProjectedGeometryContainer &geometry); +}; + +} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ + +#endif // MAPBOX_UTIL_GEOJSONVT_CONVERT diff --git a/src/mbgl/util/geojsonvt_simplify.cpp b/src/mbgl/util/geojsonvt_simplify.cpp new file mode 100644 index 00000000000..e09297d56fb --- /dev/null +++ b/src/mbgl/util/geojsonvt_simplify.cpp @@ -0,0 +1,86 @@ +#include "geojsonvt_simplify.hpp" + +#include + +namespace mapbox { namespace util { namespace geojsonvt { + +void Simplify::simplify(ProjectedGeometryContainer &points, double tolerance) { + + const double sqTolerance = tolerance * tolerance; + const size_t len = points.members.size(); + size_t first = 0; + size_t last = len - 1; + std::stack stack; + double maxSqDist = 0; + double sqDist = 0; + size_t index = 0; + + points.members[first].get().z = 1; + points.members[last].get().z = 1; + + while (last) { + + maxSqDist = 0; + + for (size_t i = (first + 1); i < last; ++i) { + sqDist = getSqSegDist(points.members[i].get(), + points.members[first].get(), + points.members[last].get()); + + if (sqDist > maxSqDist) { + index = i; + maxSqDist = sqDist; + } + } + + if (maxSqDist > sqTolerance) { + points.members[index].get().z = maxSqDist; + stack.push(first); + stack.push(index); + stack.push(index); + stack.push(last); + } + + if (stack.size()) { + last = stack.top(); + stack.pop(); + } else { + last = 0; + } + + if (stack.size()) { + first = stack.top(); + stack.pop(); + } else { + first = 0; + } + } +} + +double Simplify::getSqSegDist(const ProjectedPoint &p, const ProjectedPoint &a, const ProjectedPoint &b) { + + double x = a.x; + double y = a.y; + double dx = b.x - a.x; + double dy = b.y - a.y; + + if (dx || dy) { + + 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) { + x += dx * t; + y += dy * t; + } + } + + dx = p.x - x; + dy = p.y - y; + + return dx * dx + dy * dy; +} + +} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ diff --git a/src/mbgl/util/geojsonvt_simplify.hpp b/src/mbgl/util/geojsonvt_simplify.hpp new file mode 100644 index 00000000000..7b73337eb7f --- /dev/null +++ b/src/mbgl/util/geojsonvt_simplify.hpp @@ -0,0 +1,18 @@ +#ifndef MAPBOX_UTIL_GEOJSONVT_SIMPLIFY +#define MAPBOX_UTIL_GEOJSONVT_SIMPLIFY + +#include "geojsonvt_types.hpp" + +namespace mapbox { namespace util { namespace geojsonvt { + +class Simplify { +public: + static void simplify(ProjectedGeometryContainer &points, double tolerance); + +private: + static double getSqSegDist(const ProjectedPoint &p, const ProjectedPoint &a, const ProjectedPoint &b); +}; + +} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ + +#endif // MAPBOX_UTIL_GEOJSONVT_SIMPLIFY diff --git a/src/mbgl/util/geojsonvt_tile.cpp b/src/mbgl/util/geojsonvt_tile.cpp new file mode 100644 index 00000000000..d77a2de88f7 --- /dev/null +++ b/src/mbgl/util/geojsonvt_tile.cpp @@ -0,0 +1,72 @@ +#include "geojsonvt_tile.hpp" + +namespace mapbox { namespace util { namespace geojsonvt { + +Tile Tile::createTile(std::vector &features, uint32_t z2, uint32_t tx, uint32_t ty, double tolerance, uint16_t extent, bool noSimplify) { + + Tile tile; + + for (size_t i = 0; i < features.size(); ++i) { + tile.numFeatures++; + addFeature(tile, features[i], z2, tx, ty, tolerance, extent, noSimplify); + } + + return std::move(tile); +} + +void Tile::addFeature(Tile &tile, ProjectedFeature &feature, uint32_t z2, uint32_t tx, uint32_t ty, double tolerance, uint16_t extent, bool noSimplify) { + + ProjectedGeometryContainer *geom = &(feature.geometry.get()); + ProjectedFeatureType type = feature.type; + std::vector transformed; + double sqTolerance = tolerance * tolerance; + ProjectedGeometryContainer ring; + + if (type == ProjectedFeatureType::Point) { + for (size_t i = 0; i < geom->members.size(); ++i) { + ProjectedPoint *p = &(geom->members[i].get()); + transformed.push_back(transformPoint(*p, z2, tx, ty, extent)); + tile.numPoints++; + tile.numSimplified++; + } + } else { + for (size_t i = 0; i < geom->members.size(); ++i) { + ring = geom->members[i].get(); + + if (!noSimplify && ((type == ProjectedFeatureType::LineString && ring.dist < tolerance) || + (type == ProjectedFeatureType::Polygon && ring.area < sqTolerance))) { + tile.numPoints += ring.members.size(); + continue; + } + + TileRing transformedRing; + + for (size_t j = 0; j < ring.members.size(); ++j) { + ProjectedPoint *p = &(ring.members[j].get()); + if (noSimplify || p->z > sqTolerance) { + TilePoint transformedPoint = transformPoint(*p, z2, tx, ty, extent); + transformedRing.points.push_back(transformedPoint); + tile.numSimplified++; + } + tile.numPoints++; + } + + transformed.push_back(transformedRing); + } + } + + if (transformed.size()) { + tile.features.push_back(TileFeature(transformed, type, Tags(feature.tags))); + } + +} + +TilePoint Tile::transformPoint(const ProjectedPoint &p, uint32_t z2, uint32_t tx, uint32_t ty, uint16_t extent) { + + int16_t x = extent * (p.x * z2 - tx); + int16_t y = extent * (p.y * z2 - ty); + + return TilePoint(x, y); +} + +} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ diff --git a/src/mbgl/util/geojsonvt_tile.hpp b/src/mbgl/util/geojsonvt_tile.hpp new file mode 100644 index 00000000000..d0df6ff2d98 --- /dev/null +++ b/src/mbgl/util/geojsonvt_tile.hpp @@ -0,0 +1,29 @@ +#ifndef MAPBOX_UTIL_GEOJSONVT_TILE +#define MAPBOX_UTIL_GEOJSONVT_TILE + +#include "geojsonvt_types.hpp" + +namespace mapbox { namespace util { namespace geojsonvt { + +class Tile { +public: + static Tile createTile(std::vector &features, uint32_t z2, uint32_t tx, uint32_t ty, double tolerance, uint16_t extent, bool noSimplify); + + static void addFeature(Tile &tile, ProjectedFeature &feature, uint32_t z2, uint32_t tx, uint32_t ty, double tolerance, uint16_t extent, bool noSimplify); + + inline operator bool() const { return this->numPoints > 0; } + +private: + static TilePoint transformPoint(const ProjectedPoint &p, uint32_t z2, uint32_t tx, uint32_t ty, uint16_t extent); + +public: + std::vector features; + uint32_t numPoints = 0; + uint32_t numSimplified = 0; + uint32_t numFeatures = 0; + std::vector source; +}; + +} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ + +#endif // MAPBOX_UTIL_GEOJSONVT_TILE diff --git a/src/mbgl/util/geojsonvt_types.hpp b/src/mbgl/util/geojsonvt_types.hpp new file mode 100644 index 00000000000..650e7561daa --- /dev/null +++ b/src/mbgl/util/geojsonvt_types.hpp @@ -0,0 +1,140 @@ +#ifndef MAPBOX_UTIL_GEOJSONVT_TYPES +#define MAPBOX_UTIL_GEOJSONVT_TYPES + +#include + +#include + +#include +#include +#include +#include + +namespace mapbox { namespace util { namespace geojsonvt { + +struct LonLat { + LonLat(std::array coordinates) + : lon(coordinates[0]), lat(coordinates[1]) {} + + LonLat(double lon_, double lat_) + : lon(lon_), lat(lat_) {} + + double lon; + double lat; +}; + +#pragma mark - + +class ProjectedPoint; +class ProjectedGeometryContainer; + +using ProjectedGeometry = mapbox::util::variant; + +#pragma mark - + +class ProjectedPoint { +public: + ProjectedPoint(double x_ = -1, double y_ = -1, double z_ = -1) + : x(x_), y(y_), z(z_) {} + + inline operator bool() const { return (x >= 0 && y >= 0 && z >= 0); } + inline bool operator!= (const ProjectedPoint& rhs) const { return (x != rhs.x || y != rhs.y || z != rhs.z); } + +public: + double x = -1; + double y = -1; + double z = -1; +}; + +#pragma mark - + +using JSDocument = rapidjson::Document; +using JSValue = rapidjson::Value; + +#pragma mark - + +class ProjectedGeometryContainer { +public: + ProjectedGeometryContainer() {} + ProjectedGeometryContainer(std::vector members_) + : members(members_) {} + +public: + std::vector members; + double area = 0; + double dist = 0; +}; + +#pragma mark - + +using Tags = std::map; + +#pragma mark - + +enum class ProjectedFeatureType: uint8_t { + Point = 1, + LineString = 2, + Polygon = 3 +}; + +#pragma mark - + +class ProjectedFeature { +public: + ProjectedFeature(ProjectedGeometry geometry_, ProjectedFeatureType type_, Tags tags_) + : geometry(geometry_), type(type_), tags(tags_) {} + +public: + ProjectedGeometry geometry; + ProjectedFeatureType type; + Tags tags; + ProjectedPoint minPoint = ProjectedPoint(); + ProjectedPoint maxPoint = ProjectedPoint(); +}; + +#pragma mark - + +class TilePoint; +class TileRing; + +using TileGeometry = mapbox::util::variant; + +#pragma mark - + +class TilePoint { +public: + TilePoint(int16_t x_, int16_t y_) + : x(x_), y(y_) {} + +public: + const int16_t x = 0; + const int16_t y = 0; +}; + +#pragma mark - + +class TileRing { +public: + std::vector points; +}; + +#pragma mark - + +typedef ProjectedFeatureType TileFeatureType; + +#pragma mark - + +class TileFeature { +public: + TileFeature(std::vector geometry_, TileFeatureType type_, Tags tags_) + : geometry(geometry_), type(type_), tags(tags_) {} + +public: + std::vector geometry; + TileFeatureType type; + Tags tags; +}; + +} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ + +#endif // MAPBOX_UTIL_GEOJSONVT_TYPES diff --git a/src/mbgl/util/geojsonvt_util.hpp b/src/mbgl/util/geojsonvt_util.hpp new file mode 100644 index 00000000000..cfc2136b4e8 --- /dev/null +++ b/src/mbgl/util/geojsonvt_util.hpp @@ -0,0 +1,24 @@ +#ifndef MAPBOX_UTIL_GEOJSONVT_UTIL +#define MAPBOX_UTIL_GEOJSONVT_UTIL + +#include + +namespace mapbox { namespace util { namespace geojsonvt { + +class Time { +public: + inline static void time(std::string activity) { + Time::activities[activity] = clock(); + } + + inline static void timeEnd(std::string activity) { + printf("%s: %fms\n", activity.c_str(), double(clock() - Time::activities[activity]) / (CLOCKS_PER_SEC / 1000)); + } + +private: + static std::unordered_map activities; +}; + +} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ + +#endif // MAPBOX_UTIL_GEOJSONVT_UTIL From a6da5389022165f1b7b8aa9cf264540012192185 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Thu, 11 Jun 2015 14:17:24 -0700 Subject: [PATCH 80/85] remove debug log line --- src/mbgl/util/geojsonvt_convert.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/mbgl/util/geojsonvt_convert.cpp b/src/mbgl/util/geojsonvt_convert.cpp index f2001e534e9..9d4357bb749 100644 --- a/src/mbgl/util/geojsonvt_convert.cpp +++ b/src/mbgl/util/geojsonvt_convert.cpp @@ -157,8 +157,6 @@ void Convert::convertFeature(std::vector &features, const JSVa ProjectedGeometryContainer *geometry = &rings; features.push_back(create(tags, projectedType, *geometry)); - - printf("features now has %lu items\n", (unsigned long)features.size()); } else if (type == "MultiPolygon") { From 9ed12f4f65bc497ba7abcd4c50ccb56a89779b74 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 15 Jun 2015 16:26:11 -0700 Subject: [PATCH 81/85] move to external, submodule geojsonvt --- .gitmodules | 3 + src/mbgl/map/annotation.cpp | 2 +- src/mbgl/map/annotation.hpp | 2 +- src/mbgl/util/geojsonvt | 1 + src/mbgl/util/geojsonvt.cpp | 279 -------------------------- src/mbgl/util/geojsonvt.hpp | 59 ------ src/mbgl/util/geojsonvt_clip.cpp | 156 --------------- src/mbgl/util/geojsonvt_clip.hpp | 24 --- src/mbgl/util/geojsonvt_convert.cpp | 283 --------------------------- src/mbgl/util/geojsonvt_convert.hpp | 32 --- src/mbgl/util/geojsonvt_simplify.cpp | 86 -------- src/mbgl/util/geojsonvt_simplify.hpp | 18 -- src/mbgl/util/geojsonvt_tile.cpp | 72 ------- src/mbgl/util/geojsonvt_tile.hpp | 29 --- src/mbgl/util/geojsonvt_types.hpp | 140 ------------- src/mbgl/util/geojsonvt_util.hpp | 24 --- 16 files changed, 6 insertions(+), 1204 deletions(-) create mode 160000 src/mbgl/util/geojsonvt delete mode 100644 src/mbgl/util/geojsonvt.cpp delete mode 100644 src/mbgl/util/geojsonvt.hpp delete mode 100644 src/mbgl/util/geojsonvt_clip.cpp delete mode 100644 src/mbgl/util/geojsonvt_clip.hpp delete mode 100644 src/mbgl/util/geojsonvt_convert.cpp delete mode 100644 src/mbgl/util/geojsonvt_convert.hpp delete mode 100644 src/mbgl/util/geojsonvt_simplify.cpp delete mode 100644 src/mbgl/util/geojsonvt_simplify.hpp delete mode 100644 src/mbgl/util/geojsonvt_tile.cpp delete mode 100644 src/mbgl/util/geojsonvt_tile.hpp delete mode 100644 src/mbgl/util/geojsonvt_types.hpp delete mode 100644 src/mbgl/util/geojsonvt_util.hpp diff --git a/.gitmodules b/.gitmodules index 6a4b1d5162e..9bd44721abb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,3 +17,6 @@ [submodule "platform/ios/vendor/SMCalloutView"] path = platform/ios/vendor/SMCalloutView url = https://github.com/nfarina/calloutview.git +[submodule "src/mbgl/util/geojsonvt"] + path = src/mbgl/util/geojsonvt + url = https://github.com/mapbox/geojson-vt-cpp diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 6c59df6321b..c9462645932 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp index 8c04f454f41..213ff608a05 100644 --- a/src/mbgl/map/annotation.hpp +++ b/src/mbgl/map/annotation.hpp @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/mbgl/util/geojsonvt b/src/mbgl/util/geojsonvt new file mode 160000 index 00000000000..ed99a6290fa --- /dev/null +++ b/src/mbgl/util/geojsonvt @@ -0,0 +1 @@ +Subproject commit ed99a6290fa42107a982e7f3675aae49d29026b1 diff --git a/src/mbgl/util/geojsonvt.cpp b/src/mbgl/util/geojsonvt.cpp deleted file mode 100644 index e6e485aef1c..00000000000 --- a/src/mbgl/util/geojsonvt.cpp +++ /dev/null @@ -1,279 +0,0 @@ -#include "geojsonvt.hpp" -#include "geojsonvt_clip.hpp" -#include "geojsonvt_convert.hpp" -#include "geojsonvt_util.hpp" - -#include - -namespace mapbox { namespace util { namespace geojsonvt { - -std::unordered_map Time::activities; - -#pragma mark - GeoJSONVT - -std::vector GeoJSONVT::convertFeatures(const std::string &data, uint8_t baseZoom, double tolerance, bool debug) { - - if (debug) { - Time::time("preprocess data"); - } - - uint32_t z2 = 1 << baseZoom; - - JSDocument deserializedData; - deserializedData.Parse<0>(data.c_str()); - - if (deserializedData.HasParseError()) { - printf("invalid GeoJSON\n"); - return std::vector(); - } - - const uint16_t extent = 4096; - - std::vector features = Convert::convert(deserializedData, tolerance / (z2 * extent)); - - if (debug) { - Time::timeEnd("preprocess data"); - } - - return features; -} - -GeoJSONVT::GeoJSONVT(const std::vector& features_, uint8_t baseZoom_, uint8_t maxZoom_, uint32_t maxPoints_, double tolerance_, bool debug_) - : baseZoom(baseZoom_), - maxZoom(maxZoom_), - maxPoints(maxPoints_), - tolerance(tolerance_), - debug(debug_) { - - if (this->debug) { - Time::time("generate tiles up to z" + std::to_string(maxZoom)); - } - - splitTile(features_, 0, 0, 0); - - if (this->debug) { - printf("features: %i, points: %i\n", this->tiles[0].numFeatures, this->tiles[0].numPoints); - Time::timeEnd("generate tiles up to z" + std::to_string(maxZoom)); - printf("tiles generated: %i {\n", this->total); - for (const auto &pair : this->stats) { - printf(" z%i: %i\n", pair.first, pair.second); - } - printf("}\n"); - } -} - -void GeoJSONVT::splitTile(std::vector features_, uint8_t z_, uint32_t x_, uint32_t y_, int8_t cz, int32_t cx, int32_t cy) { - - std::queue stack; - stack.emplace(features_, z_, x_, y_); - - while (stack.size()) { - FeatureStackItem set = stack.front(); - stack.pop(); - std::vector features = std::move(set.features); - uint8_t z = set.z; - uint32_t x = set.x; - uint32_t y = set.y; - - uint32_t z2 = 1 << z; - const uint64_t id = toID(z, x, y); - Tile* tile; - double tileTolerance = (z == this->baseZoom ? 0 : this->tolerance / (z2 * this->extent)); - - if (this->tiles.count(id)) { - tile = &this->tiles[id]; - } else { - if (this->debug) { - Time::time("creation"); - } - - this->tiles[id] = std::move(Tile::createTile(features, z2, x, y, tileTolerance, extent, (z == this->baseZoom))); - tile = &this->tiles[id]; - - if (this->debug) { - printf("tile z%i-%i-%i (features: %i, points: %i, simplified: %i\n", z, x, y, - tile->numFeatures, tile->numPoints, tile->numSimplified); - Time::timeEnd("creation"); - - uint8_t key = z; - this->stats[key] = (this->stats.count(key) ? this->stats[key] + 1 : 1); - this->total++; - } - } - - if ((cz < 0 && (z == this->maxZoom || this->tiles[id].numPoints <= this->maxPoints || - isClippedSquare(tile->features, this->extent, this->buffer))) || z == this->baseZoom || z == cz) { - tile->source = std::vector(features); - continue; - } - - if (cz >= 0) { - tile->source = std::vector(features); - } else { - tile->source = {}; - } - - if (this->debug) { - Time::time("clipping"); - } - - double k1 = 0.5 * this->buffer / this->extent; - double k2 = 0.5 - k1; - double k3 = 0.5 + k1; - double k4 = 1 + k1; - - std::vector tl; - std::vector bl; - std::vector tr; - std::vector br; - std::vector left; - std::vector right; - uint32_t m = 0; - bool goLeft = false; - bool goTop = false; - - if (cz >= 0) { - m = 1 << (cz - z); - goLeft = double(cx) / double(m) - double(x) < 0.5; - goTop = double(cy) / double(m) - double(y) < 0.5; - } - - if (cz < 0 || goLeft) { - left = Clip::clip(features, z2, x - k1, x + k3, 0, intersectX); - } - - if (cz < 0 || !goLeft) { - right = Clip::clip(features, z2, x + k2, x + k4, 0, intersectX); - } - - if (left.size()) { - if (cz < 0 || goTop) { - tl = Clip::clip(left, z2, y - k1, y + k3, 1, intersectY); - } - - if (cz < 0 || !goTop) { - bl = Clip::clip(left, z2, y + k2, y + k4, 1, intersectY); - } - } - - if (right.size()) { - if (cz < 0 || goTop) { - tr = Clip::clip(right, z2, y - k1, y + k3, 1, intersectY); - } - - if (cz < 0 || !goTop) { - br = Clip::clip(right, z2, y + k2, y + k4, 1, intersectY); - } - } - - if (this->debug) { - Time::timeEnd("clipping"); - } - - if (tl.size()) stack.emplace(std::move(tl), z + 1, x * 2, y * 2); - if (bl.size()) stack.emplace(std::move(bl), z + 1, x * 2, y * 2 + 1); - if (tr.size()) stack.emplace(std::move(tr), z + 1, x * 2 + 1, y * 2); - if (br.size()) stack.emplace(std::move(br), z + 1, x * 2 + 1, y * 2 + 1); - } -} - -Tile& GeoJSONVT::getTile(uint8_t z, uint32_t x, uint32_t y) { - - std::lock_guard lock(mtx); - - const uint64_t id = toID(z, x, y); - if (this->tiles.count(id)) { - return this->tiles[id]; - } - - if (this->debug) { - printf("drilling down to z%i-%i-%i\n", z, x, y); - } - - uint8_t z0 = z; - uint32_t x0 = x; - uint32_t y0 = y; - Tile *parent = nullptr; - - while (!parent && z0) { - z0--; - x0 = x0 / 2; - y0 = y0 / 2; - const uint64_t checkID = toID(z0, x0, y0); - if (this->tiles.count(checkID)) { - parent = &this->tiles[checkID]; - } - } - - if (this->debug) { - printf("found parent tile z%i-%i-%i\n", z0, x0, y0); - } - - if (parent->source.size()) { - if (isClippedSquare(parent->features, this->extent, this->buffer)) { - return *parent; - } - - if (this->debug) { - Time::time("drilling down"); - } - - splitTile(parent->source, z0, x0, y0, z, x, y); - - if (this->debug) { - Time::timeEnd("drilling down"); - } - } - - return this->tiles[id]; -} - -bool GeoJSONVT::isClippedSquare(const std::vector &features, uint16_t extent_, uint8_t buffer_) const { - - if (features.size() != 1) { - return false; - } - - const TileFeature feature = features.front(); - - if (feature.type != TileFeatureType::Polygon || feature.geometry.size() > 1) { - return false; - } - - const TileRing *ring = &(feature.geometry.front().get()); - - for (size_t i = 0; i < ring->points.size(); ++i) { - const TilePoint *p = &ring->points[i]; - if ((p->x != -buffer_ && p->x != extent_ + buffer_) || - (p->y != -buffer_ && p->y != extent_ + buffer_)) { - return false; - } - } - - return true; -} - -uint64_t GeoJSONVT::toID(uint8_t z, uint32_t x, uint32_t y) { - - return (((1 << z) * y + x) * 32) + z; -} - -ProjectedPoint GeoJSONVT::intersectX(const ProjectedPoint &a, const ProjectedPoint &b, double x) { - - double r1 = x; - double r2 = (x - a.x) * (b.y - a.y) / (b.x - a.x) + a.y; - double r3 = 1; - - return ProjectedPoint(r1, r2, r3); -} - -ProjectedPoint GeoJSONVT::intersectY(const ProjectedPoint &a, const ProjectedPoint &b, double y) { - - double r1 = (y - a.y) * (b.x - a.x) / (b.y - a.y) + a.x; - double r2 = y; - double r3 = 1; - - return ProjectedPoint(r1, r2, r3); -} - -} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ diff --git a/src/mbgl/util/geojsonvt.hpp b/src/mbgl/util/geojsonvt.hpp deleted file mode 100644 index a9bd1934710..00000000000 --- a/src/mbgl/util/geojsonvt.hpp +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef MAPBOX_UTIL_GEOJSONVT -#define MAPBOX_UTIL_GEOJSONVT - -#include "geojsonvt_tile.hpp" -#include "geojsonvt_types.hpp" - -#include -#include -#include -#include - -namespace mapbox { namespace util { namespace geojsonvt { - -class GeoJSONVT { -public: - static std::vector convertFeatures(const std::string &data, uint8_t baseZoom = 14, double tolerance = 3, bool debug = false); - - GeoJSONVT(const std::vector& features_, uint8_t baseZoom = 14, uint8_t maxZoom = 4, uint32_t maxPoints = 100, double tolerance = 3, bool debug = false); - - Tile& getTile(uint8_t z, uint32_t x, uint32_t y); - -private: - void splitTile(std::vector features, uint8_t z, uint32_t x, uint32_t y, int8_t cz = -1, int32_t cx = -1, int32_t cy = -1); - - bool isClippedSquare(const std::vector &features, uint16_t extent, uint8_t buffer) const; - - static uint64_t toID(uint8_t z, uint32_t x, uint32_t y); - - static ProjectedPoint intersectX(const ProjectedPoint &a, const ProjectedPoint &b, double x); - - static ProjectedPoint intersectY(const ProjectedPoint &a, const ProjectedPoint &b, double y); - - struct FeatureStackItem { - std::vector features; - uint8_t z; - uint32_t x; - uint32_t y; - - FeatureStackItem(std::vector features_, uint8_t z_, uint32_t x_, uint32_t y_) - : features(features_), z(z_), x(x_), y(y_) {} - }; - -private: - std::mutex mtx; - uint8_t baseZoom; - uint8_t maxZoom; - uint32_t maxPoints; - double tolerance; - bool debug; - uint16_t extent = 4096; - uint8_t buffer = 64; - std::map tiles; - std::map stats; - uint16_t total = 0; -}; - -} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ - -#endif // MAPBOX_UTIL_GEOJSONVT diff --git a/src/mbgl/util/geojsonvt_clip.cpp b/src/mbgl/util/geojsonvt_clip.cpp deleted file mode 100644 index 52a954cdf4b..00000000000 --- a/src/mbgl/util/geojsonvt_clip.cpp +++ /dev/null @@ -1,156 +0,0 @@ -#include "geojsonvt_clip.hpp" - -namespace mapbox { namespace util { namespace geojsonvt { - -std::vector Clip::clip(const std::vector features, uint32_t scale, double k1, double k2, uint8_t axis, ProjectedPoint (*intersect)(const ProjectedPoint&, const ProjectedPoint&, double)) { - - k1 /= scale; - k2 /= scale; - - std::vector clipped; - - for (size_t i = 0; i < features.size(); ++i) { - - const ProjectedFeature feature = features[i]; - const ProjectedGeometry geometry = feature.geometry; - const ProjectedFeatureType type = feature.type; - double min = 0; - double max = 0; - - if (feature.minPoint) { - min = (axis == 0 ? feature.minPoint.x : feature.minPoint.y); - max = (axis == 0 ? feature.maxPoint.x : feature.maxPoint.y); - - if (min >= k1 && max <= k2) { - clipped.push_back(feature); - continue; - } else if (min > k2 || max < k1) { - continue; - } - } - - ProjectedGeometryContainer slices; - - if (type == ProjectedFeatureType::Point) { - slices = clipPoints(geometry.get(), k1, k2, axis); - } else { - slices = clipGeometry(geometry.get(), k1, k2, axis, intersect, (type == ProjectedFeatureType::Polygon)); - } - - if (slices.members.size()) { - clipped.push_back(ProjectedFeature(slices, type, features[i].tags)); - } - } - - return std::move(clipped); -} - -ProjectedGeometryContainer Clip::clipPoints(ProjectedGeometryContainer geometry, double k1, double k2, uint8_t axis) { - - ProjectedGeometryContainer slice; - - for (size_t i = 0; i < geometry.members.size(); ++i) { - ProjectedPoint *a = &(geometry.members[i].get()); - double ak = (axis == 0 ? a->x : a->y); - - if (ak >= k1 && ak <= k2) { - slice.members.push_back(*a); - } - } - - return std::move(slice); -} - -ProjectedGeometryContainer Clip::clipGeometry(ProjectedGeometryContainer geometry, double k1, double k2, uint8_t axis, ProjectedPoint (*intersect)(const ProjectedPoint&, const ProjectedPoint&, double), bool closed) { - - ProjectedGeometryContainer slices; - - for (size_t i = 0; i < geometry.members.size(); ++i) { - - double ak = 0; - double bk = 0; - ProjectedPoint b; - const ProjectedGeometryContainer *points = &(geometry.members[i].get()); - const double area = points->area; - const double dist = points->dist; - const size_t len = points->members.size(); - ProjectedPoint a; - - ProjectedGeometryContainer slice; - - for (size_t j = 0; j < (len - 1); ++j) { - a = (b ? b : points->members[j].get()); - b = points->members[j + 1].get(); - ak = (bk ? bk : (axis == 0 ? a.x : a.y)); - bk = (axis == 0 ? b.x : b.y); - - if (ak < k1) { - if (bk > k2) { - slice.members.push_back(intersect(a, b, k1)); - slice.members.push_back(intersect(a, b, k2)); - if (!closed) { - slice = newSlice(slices, slice, area, dist); - } - } else if (bk >= k1) { - slice.members.push_back(intersect(a, b, k1)); - } - } else if (ak > k2) { - if (bk < k1) { - slice.members.push_back(intersect(a, b, k2)); - slice.members.push_back(intersect(a, b, k1)); - if (!closed) { - slice = newSlice(slices, slice, area, dist); - } - } else if (bk <= k2) { - slice.members.push_back(intersect(a, b, k2)); - } - } else { - slice.members.push_back(a); - - if (bk < k1) { - slice.members.push_back(intersect(a, b, k1)); - if (!closed) { - slice = newSlice(slices, slice, area, dist); - } - } else if (bk > k2) { - slice.members.push_back(intersect(a, b, k2)); - if (!closed) { - slice = newSlice(slices, slice, area, dist); - } - } - } - } - - a = points->members[len - 1].get(); - ak = (axis == 0 ? a.x : a.y); - - if (ak >= k1 && ak <= k2) { - slice.members.push_back(a); - } - - if (closed && slice.members.size()) { - const ProjectedPoint *first = &(slice.members[0].get()); - const ProjectedPoint *last = &(slice.members[slice.members.size() - 1].get()); - if (first != last) { - slice.members.push_back(ProjectedPoint(first->x, first->y, first->z)); - } - } - - newSlice(slices, slice, area, dist); - } - - return std::move(slices); -} - -ProjectedGeometryContainer Clip::newSlice(ProjectedGeometryContainer &slices, ProjectedGeometryContainer &slice, double area, double dist) { - - if (slice.members.size()) { - slice.area = area; - slice.dist = dist; - slices.members.push_back(slice); - } - - return ProjectedGeometryContainer(); -} - -} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ diff --git a/src/mbgl/util/geojsonvt_clip.hpp b/src/mbgl/util/geojsonvt_clip.hpp deleted file mode 100644 index d314297b288..00000000000 --- a/src/mbgl/util/geojsonvt_clip.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef MAPBOX_UTIL_GEOJSONVT_CLIP -#define MAPBOX_UTIL_GEOJSONVT_CLIP - -#include "geojsonvt_types.hpp" - -#include - -namespace mapbox { namespace util { namespace geojsonvt { - -class Clip { -public: - static std::vector clip(std::vector features, uint32_t scale, double k1, double k2, uint8_t axis, ProjectedPoint (*intersect)(const ProjectedPoint&, const ProjectedPoint&, double)); - -private: - static ProjectedGeometryContainer clipPoints(ProjectedGeometryContainer geometry, double k1, double k2, uint8_t axis); - - static ProjectedGeometryContainer clipGeometry(ProjectedGeometryContainer geometry, double k1, double k2, uint8_t axis, ProjectedPoint (*intersect)(const ProjectedPoint&, const ProjectedPoint&, double), bool closed); - - static ProjectedGeometryContainer newSlice(ProjectedGeometryContainer &slices, ProjectedGeometryContainer &slice, double area, double dist); -}; - -} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ - -#endif // MAPBOX_UTIL_GEOJSONVT_CLIP diff --git a/src/mbgl/util/geojsonvt_convert.cpp b/src/mbgl/util/geojsonvt_convert.cpp deleted file mode 100644 index 9d4357bb749..00000000000 --- a/src/mbgl/util/geojsonvt_convert.cpp +++ /dev/null @@ -1,283 +0,0 @@ -#include "geojsonvt_convert.hpp" -#include "geojsonvt_simplify.hpp" - -#include -#include -#include - -namespace mapbox { namespace util { namespace geojsonvt { - -std::vector Convert::convert(const JSDocument &data, double tolerance) { - - std::vector features; - const JSValue &rawType = data["type"]; - - if (std::string(rawType.GetString()) == "FeatureCollection") { - if (data.HasMember("features")) { - const JSValue &rawFeatures = data["features"]; - if (rawFeatures.IsArray()) { - printf("there are %i total features to convert\n", rawFeatures.Size()); - for (rapidjson::SizeType i = 0; i < rawFeatures.Size(); ++i) { - convertFeature(features, rawFeatures[i], tolerance); - } - } - } - } else if (std::string(data["type"].GetString()) == "Feature") { - convertFeature(features, data, tolerance); - } else { - - /* In this case, we want to pass the entire JSON document as the - * value for key 'geometry' in a new JSON object, like so: - * - * convertFeature(features, ["geometry": data], tolerance); (pseudo-code) - * - * Currently this fails due to lack of a proper copy constructor. - * Maybe use move semantics? */ - -// JSValue feature; -// feature.SetObject(); -// feature.AddMember("geometry", data, data.GetAllocator()); -// convertFeature(features, feature, tolerance); - - } - - return std::move(features); -} - -void Convert::convertFeature(std::vector &features, const JSValue &feature, double tolerance) { - - const JSValue &geom = feature["geometry"]; - const JSValue &rawType = geom["type"]; - std::string type { rawType.GetString(), rawType.GetStringLength() }; - Tags tags; - - if (feature.HasMember("properties") && feature["properties"].IsObject()) { - const JSValue &properties = feature["properties"]; - rapidjson::Value::ConstMemberIterator itr = properties.MemberBegin(); - for (; itr != properties.MemberEnd(); ++itr) { - std::string key { itr->name.GetString(), itr->name.GetStringLength() }; - std::string val { itr->value.GetString(), itr->value.GetStringLength() }; - tags[key] = val; - } - } - - if (type == "Point") { - - std::array coordinates = {{ 0, 0 }}; - if (geom.HasMember("coordinates")) { - const JSValue &rawCoordinates = geom["coordinates"]; - if (rawCoordinates.IsArray()) { - coordinates[0] = rawCoordinates[(rapidjson::SizeType)0].GetDouble(); - coordinates[1] = rawCoordinates[(rapidjson::SizeType)1].GetDouble(); - } - } - ProjectedPoint point = projectPoint(LonLat(coordinates)); - - std::vector members = { point }; - ProjectedGeometryContainer geometry(members); - - features.push_back(create(tags, ProjectedFeatureType::Point, geometry)); - - } else if (type == "MultiPoint") { - - std::vector> coordinatePairs; - std::vector points; - if (geom.HasMember("coordinates")) { - const JSValue &rawCoordinatePairs = geom["coordinates"]; - if (rawCoordinatePairs.IsArray()) { - for (rapidjson::SizeType i = 0; i < rawCoordinatePairs.Size(); ++i) { - std::array coordinates = {{ 0, 0 }}; - const JSValue &rawCoordinates = rawCoordinatePairs[i]; - if (rawCoordinates.IsArray()) { - coordinates[0] = rawCoordinates[(rapidjson::SizeType)0].GetDouble(); - coordinates[1] = rawCoordinates[(rapidjson::SizeType)1].GetDouble(); - } - points.push_back(LonLat(coordinates)); - } - } - } - - ProjectedGeometryContainer geometry = project(points); - - features.push_back(create(tags, ProjectedFeatureType::Point, geometry)); - - } else if (type == "LineString") { - - std::vector> coordinatePairs; - std::vector points; - if (geom.HasMember("coordinates")) { - const JSValue &rawCoordinatePairs = geom["coordinates"]; - if (rawCoordinatePairs.IsArray()) { - for (rapidjson::SizeType i = 0; i < rawCoordinatePairs.Size(); ++i) { - std::array coordinates = {{ 0, 0 }}; - const JSValue &rawCoordinates = rawCoordinatePairs[i]; - if (rawCoordinates.IsArray()) { - coordinates[0] = rawCoordinates[(rapidjson::SizeType)0].GetDouble(); - coordinates[1] = rawCoordinates[(rapidjson::SizeType)1].GetDouble(); - } - points.push_back(LonLat(coordinates)); - } - } - } - - ProjectedGeometryContainer geometry({ project(points, tolerance) }); - - features.push_back(create(tags, ProjectedFeatureType::LineString, geometry)); - - } else if (type == "MultiLineString" || type == "Polygon") { - - ProjectedGeometryContainer rings; - if (geom.HasMember("coordinates")) { - const JSValue &rawLines = geom["coordinates"]; - if (rawLines.IsArray()) { - for (rapidjson::SizeType i = 0; i < rawLines.Size(); ++i) { - const JSValue &rawCoordinatePairs = rawLines[i]; - if (rawCoordinatePairs.IsArray()) { - std::vector points; - for (rapidjson::SizeType j = 0; j < rawCoordinatePairs.Size(); ++j) { - std::array coordinates = {{ 0, 0 }}; - const JSValue &rawCoordinates = rawCoordinatePairs[j]; - if (rawCoordinates.IsArray()) { - coordinates[0] = rawCoordinates[(rapidjson::SizeType)0].GetDouble(); - coordinates[1] = rawCoordinates[(rapidjson::SizeType)1].GetDouble(); - } - points.push_back(LonLat(coordinates)); - } - ProjectedGeometryContainer ring = project(points, tolerance); - rings.members.push_back(ring); - } - } - } - } - - ProjectedFeatureType projectedType = (type == "Polygon" ? - ProjectedFeatureType::Polygon : - ProjectedFeatureType::LineString); - - ProjectedGeometryContainer *geometry = &rings; - - features.push_back(create(tags, projectedType, *geometry)); - } - - else if (type == "MultiPolygon") { - - ProjectedGeometryContainer rings; - if (geom.HasMember("coordinates")) { - const JSValue &rawPolygons = geom["coordinates"]; - if (rawPolygons.IsArray()) { - for (rapidjson::SizeType i = 0; i < rawPolygons.Size(); ++i) { - std::vector points; - const JSValue &rawLines = rawPolygons[i]; - for (rapidjson::SizeType j = 0; j < rawLines.Size(); ++j) { - std::array coordinates = {{ 0, 0 }}; - const JSValue &rawCoordinatePairs = rawLines[i]; - if (rawCoordinatePairs.IsArray()) { - coordinates[0] = rawCoordinatePairs[(rapidjson::SizeType)0].GetDouble(); - coordinates[1] = rawCoordinatePairs[(rapidjson::SizeType)1].GetDouble(); - } - points.push_back(LonLat(coordinates)); - } - ProjectedGeometryContainer ring = project(points, tolerance); - rings.members.push_back(ring); - } - } - } - - ProjectedGeometryContainer *geometry = &rings; - - features.push_back(create(tags, ProjectedFeatureType::Polygon, *geometry)); - - } else if (type == "GeometryCollection") { - - if (geom.HasMember("geometries")) { - const JSValue &rawGeometries = geom["geometries"]; - if (rawGeometries.IsArray()) { - for (rapidjson::SizeType i = 0; i < rawGeometries.Size(); ++i) { - convertFeature(features, rawGeometries[i], tolerance); - } - } - } - - } else { - - printf("unsupported GeoJSON type: %s\n", geom["type"].GetString()); - - } -} - -ProjectedFeature Convert::create(Tags tags, ProjectedFeatureType type, ProjectedGeometry geometry) { - - ProjectedFeature feature(geometry, type, tags); - calcBBox(feature); - - return std::move(feature); -} - -ProjectedGeometryContainer Convert::project(const std::vector &lonlats, double tolerance) { - - ProjectedGeometryContainer projected; - for (size_t i = 0; i < lonlats.size(); ++i) { - projected.members.push_back(projectPoint(lonlats[i])); - } - if (tolerance) { - Simplify::simplify(projected, tolerance); - calcSize(projected); - } - - return std::move(projected); -} - -ProjectedPoint Convert::projectPoint(const LonLat &p_) { - - double sine = std::sin(p_.lat * M_PI / 180); - double x = p_.lon / 360 + 0.5; - double y = 0.5 - 0.25 * std::log((1 + sine) / (1 - sine)) / M_PI; - - return ProjectedPoint(x, y, 0); -} - -void Convert::calcSize(ProjectedGeometryContainer &geometryContainer) { - - double area = 0, dist = 0; - ProjectedPoint a, b; - - for (size_t i = 0; i < geometryContainer.members.size() - 1; ++i) { - a = (b ? b : geometryContainer.members[i].get()); - b = geometryContainer.members[i + 1].get(); - - area += a.x * b.y - b.x * a.y; - dist += std::abs(b.x - a.x) + std::abs(b.y - a.y); - } - - geometryContainer.area = std::abs(area / 2); - geometryContainer.dist = dist; -} - -void Convert::calcBBox(ProjectedFeature &feature) { - - ProjectedGeometryContainer *geometry = &(feature.geometry.get()); - ProjectedPoint *minPoint = &(feature.minPoint); - ProjectedPoint *maxPoint = &(feature.maxPoint); - - if (feature.type == ProjectedFeatureType::Point) { - calcRingBBox(*minPoint, *maxPoint, *geometry); - } else { - for (size_t i = 0; i < geometry->members.size(); ++i) { - ProjectedGeometryContainer *featureGeometry = &(geometry->members[i].get()); - calcRingBBox(*minPoint, *maxPoint, *featureGeometry); - } - } -} - -void Convert::calcRingBBox(ProjectedPoint &minPoint, ProjectedPoint &maxPoint, const ProjectedGeometryContainer &geometry) { - - for (size_t i = 0; i < geometry.members.size(); ++i) { - const ProjectedPoint *p = &(geometry.members[i].get()); - minPoint.x = std::min(p->x, minPoint.x); - maxPoint.x = std::max(p->x, maxPoint.x); - minPoint.y = std::min(p->y, minPoint.y); - maxPoint.y = std::max(p->y, maxPoint.y); - } -} - -} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ diff --git a/src/mbgl/util/geojsonvt_convert.hpp b/src/mbgl/util/geojsonvt_convert.hpp deleted file mode 100644 index 8861be13f05..00000000000 --- a/src/mbgl/util/geojsonvt_convert.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef MAPBOX_UTIL_GEOJSONVT_CONVERT -#define MAPBOX_UTIL_GEOJSONVT_CONVERT - -#include "geojsonvt_types.hpp" - -#include - -namespace mapbox { namespace util { namespace geojsonvt { - -class Convert { -public: - static std::vector convert(const JSDocument &data, double tolerance); - - static ProjectedFeature create(Tags tags, ProjectedFeatureType type, ProjectedGeometry geometry); - - static ProjectedGeometryContainer project(const std::vector &lonlats, double tolerance = 0); - -private: - static void convertFeature(std::vector &features, const JSValue &feature, double tolerance); - - static ProjectedPoint projectPoint(const LonLat &p); - - static void calcSize(ProjectedGeometryContainer &geometryContainer); - - static void calcBBox(ProjectedFeature &feature); - - static void calcRingBBox(ProjectedPoint &minPoint, ProjectedPoint &maxPoint, const ProjectedGeometryContainer &geometry); -}; - -} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ - -#endif // MAPBOX_UTIL_GEOJSONVT_CONVERT diff --git a/src/mbgl/util/geojsonvt_simplify.cpp b/src/mbgl/util/geojsonvt_simplify.cpp deleted file mode 100644 index e09297d56fb..00000000000 --- a/src/mbgl/util/geojsonvt_simplify.cpp +++ /dev/null @@ -1,86 +0,0 @@ -#include "geojsonvt_simplify.hpp" - -#include - -namespace mapbox { namespace util { namespace geojsonvt { - -void Simplify::simplify(ProjectedGeometryContainer &points, double tolerance) { - - const double sqTolerance = tolerance * tolerance; - const size_t len = points.members.size(); - size_t first = 0; - size_t last = len - 1; - std::stack stack; - double maxSqDist = 0; - double sqDist = 0; - size_t index = 0; - - points.members[first].get().z = 1; - points.members[last].get().z = 1; - - while (last) { - - maxSqDist = 0; - - for (size_t i = (first + 1); i < last; ++i) { - sqDist = getSqSegDist(points.members[i].get(), - points.members[first].get(), - points.members[last].get()); - - if (sqDist > maxSqDist) { - index = i; - maxSqDist = sqDist; - } - } - - if (maxSqDist > sqTolerance) { - points.members[index].get().z = maxSqDist; - stack.push(first); - stack.push(index); - stack.push(index); - stack.push(last); - } - - if (stack.size()) { - last = stack.top(); - stack.pop(); - } else { - last = 0; - } - - if (stack.size()) { - first = stack.top(); - stack.pop(); - } else { - first = 0; - } - } -} - -double Simplify::getSqSegDist(const ProjectedPoint &p, const ProjectedPoint &a, const ProjectedPoint &b) { - - double x = a.x; - double y = a.y; - double dx = b.x - a.x; - double dy = b.y - a.y; - - if (dx || dy) { - - 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) { - x += dx * t; - y += dy * t; - } - } - - dx = p.x - x; - dy = p.y - y; - - return dx * dx + dy * dy; -} - -} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ diff --git a/src/mbgl/util/geojsonvt_simplify.hpp b/src/mbgl/util/geojsonvt_simplify.hpp deleted file mode 100644 index 7b73337eb7f..00000000000 --- a/src/mbgl/util/geojsonvt_simplify.hpp +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef MAPBOX_UTIL_GEOJSONVT_SIMPLIFY -#define MAPBOX_UTIL_GEOJSONVT_SIMPLIFY - -#include "geojsonvt_types.hpp" - -namespace mapbox { namespace util { namespace geojsonvt { - -class Simplify { -public: - static void simplify(ProjectedGeometryContainer &points, double tolerance); - -private: - static double getSqSegDist(const ProjectedPoint &p, const ProjectedPoint &a, const ProjectedPoint &b); -}; - -} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ - -#endif // MAPBOX_UTIL_GEOJSONVT_SIMPLIFY diff --git a/src/mbgl/util/geojsonvt_tile.cpp b/src/mbgl/util/geojsonvt_tile.cpp deleted file mode 100644 index d77a2de88f7..00000000000 --- a/src/mbgl/util/geojsonvt_tile.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "geojsonvt_tile.hpp" - -namespace mapbox { namespace util { namespace geojsonvt { - -Tile Tile::createTile(std::vector &features, uint32_t z2, uint32_t tx, uint32_t ty, double tolerance, uint16_t extent, bool noSimplify) { - - Tile tile; - - for (size_t i = 0; i < features.size(); ++i) { - tile.numFeatures++; - addFeature(tile, features[i], z2, tx, ty, tolerance, extent, noSimplify); - } - - return std::move(tile); -} - -void Tile::addFeature(Tile &tile, ProjectedFeature &feature, uint32_t z2, uint32_t tx, uint32_t ty, double tolerance, uint16_t extent, bool noSimplify) { - - ProjectedGeometryContainer *geom = &(feature.geometry.get()); - ProjectedFeatureType type = feature.type; - std::vector transformed; - double sqTolerance = tolerance * tolerance; - ProjectedGeometryContainer ring; - - if (type == ProjectedFeatureType::Point) { - for (size_t i = 0; i < geom->members.size(); ++i) { - ProjectedPoint *p = &(geom->members[i].get()); - transformed.push_back(transformPoint(*p, z2, tx, ty, extent)); - tile.numPoints++; - tile.numSimplified++; - } - } else { - for (size_t i = 0; i < geom->members.size(); ++i) { - ring = geom->members[i].get(); - - if (!noSimplify && ((type == ProjectedFeatureType::LineString && ring.dist < tolerance) || - (type == ProjectedFeatureType::Polygon && ring.area < sqTolerance))) { - tile.numPoints += ring.members.size(); - continue; - } - - TileRing transformedRing; - - for (size_t j = 0; j < ring.members.size(); ++j) { - ProjectedPoint *p = &(ring.members[j].get()); - if (noSimplify || p->z > sqTolerance) { - TilePoint transformedPoint = transformPoint(*p, z2, tx, ty, extent); - transformedRing.points.push_back(transformedPoint); - tile.numSimplified++; - } - tile.numPoints++; - } - - transformed.push_back(transformedRing); - } - } - - if (transformed.size()) { - tile.features.push_back(TileFeature(transformed, type, Tags(feature.tags))); - } - -} - -TilePoint Tile::transformPoint(const ProjectedPoint &p, uint32_t z2, uint32_t tx, uint32_t ty, uint16_t extent) { - - int16_t x = extent * (p.x * z2 - tx); - int16_t y = extent * (p.y * z2 - ty); - - return TilePoint(x, y); -} - -} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ diff --git a/src/mbgl/util/geojsonvt_tile.hpp b/src/mbgl/util/geojsonvt_tile.hpp deleted file mode 100644 index d0df6ff2d98..00000000000 --- a/src/mbgl/util/geojsonvt_tile.hpp +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef MAPBOX_UTIL_GEOJSONVT_TILE -#define MAPBOX_UTIL_GEOJSONVT_TILE - -#include "geojsonvt_types.hpp" - -namespace mapbox { namespace util { namespace geojsonvt { - -class Tile { -public: - static Tile createTile(std::vector &features, uint32_t z2, uint32_t tx, uint32_t ty, double tolerance, uint16_t extent, bool noSimplify); - - static void addFeature(Tile &tile, ProjectedFeature &feature, uint32_t z2, uint32_t tx, uint32_t ty, double tolerance, uint16_t extent, bool noSimplify); - - inline operator bool() const { return this->numPoints > 0; } - -private: - static TilePoint transformPoint(const ProjectedPoint &p, uint32_t z2, uint32_t tx, uint32_t ty, uint16_t extent); - -public: - std::vector features; - uint32_t numPoints = 0; - uint32_t numSimplified = 0; - uint32_t numFeatures = 0; - std::vector source; -}; - -} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ - -#endif // MAPBOX_UTIL_GEOJSONVT_TILE diff --git a/src/mbgl/util/geojsonvt_types.hpp b/src/mbgl/util/geojsonvt_types.hpp deleted file mode 100644 index 650e7561daa..00000000000 --- a/src/mbgl/util/geojsonvt_types.hpp +++ /dev/null @@ -1,140 +0,0 @@ -#ifndef MAPBOX_UTIL_GEOJSONVT_TYPES -#define MAPBOX_UTIL_GEOJSONVT_TYPES - -#include - -#include - -#include -#include -#include -#include - -namespace mapbox { namespace util { namespace geojsonvt { - -struct LonLat { - LonLat(std::array coordinates) - : lon(coordinates[0]), lat(coordinates[1]) {} - - LonLat(double lon_, double lat_) - : lon(lon_), lat(lat_) {} - - double lon; - double lat; -}; - -#pragma mark - - -class ProjectedPoint; -class ProjectedGeometryContainer; - -using ProjectedGeometry = mapbox::util::variant; - -#pragma mark - - -class ProjectedPoint { -public: - ProjectedPoint(double x_ = -1, double y_ = -1, double z_ = -1) - : x(x_), y(y_), z(z_) {} - - inline operator bool() const { return (x >= 0 && y >= 0 && z >= 0); } - inline bool operator!= (const ProjectedPoint& rhs) const { return (x != rhs.x || y != rhs.y || z != rhs.z); } - -public: - double x = -1; - double y = -1; - double z = -1; -}; - -#pragma mark - - -using JSDocument = rapidjson::Document; -using JSValue = rapidjson::Value; - -#pragma mark - - -class ProjectedGeometryContainer { -public: - ProjectedGeometryContainer() {} - ProjectedGeometryContainer(std::vector members_) - : members(members_) {} - -public: - std::vector members; - double area = 0; - double dist = 0; -}; - -#pragma mark - - -using Tags = std::map; - -#pragma mark - - -enum class ProjectedFeatureType: uint8_t { - Point = 1, - LineString = 2, - Polygon = 3 -}; - -#pragma mark - - -class ProjectedFeature { -public: - ProjectedFeature(ProjectedGeometry geometry_, ProjectedFeatureType type_, Tags tags_) - : geometry(geometry_), type(type_), tags(tags_) {} - -public: - ProjectedGeometry geometry; - ProjectedFeatureType type; - Tags tags; - ProjectedPoint minPoint = ProjectedPoint(); - ProjectedPoint maxPoint = ProjectedPoint(); -}; - -#pragma mark - - -class TilePoint; -class TileRing; - -using TileGeometry = mapbox::util::variant; - -#pragma mark - - -class TilePoint { -public: - TilePoint(int16_t x_, int16_t y_) - : x(x_), y(y_) {} - -public: - const int16_t x = 0; - const int16_t y = 0; -}; - -#pragma mark - - -class TileRing { -public: - std::vector points; -}; - -#pragma mark - - -typedef ProjectedFeatureType TileFeatureType; - -#pragma mark - - -class TileFeature { -public: - TileFeature(std::vector geometry_, TileFeatureType type_, Tags tags_) - : geometry(geometry_), type(type_), tags(tags_) {} - -public: - std::vector geometry; - TileFeatureType type; - Tags tags; -}; - -} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ - -#endif // MAPBOX_UTIL_GEOJSONVT_TYPES diff --git a/src/mbgl/util/geojsonvt_util.hpp b/src/mbgl/util/geojsonvt_util.hpp deleted file mode 100644 index cfc2136b4e8..00000000000 --- a/src/mbgl/util/geojsonvt_util.hpp +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef MAPBOX_UTIL_GEOJSONVT_UTIL -#define MAPBOX_UTIL_GEOJSONVT_UTIL - -#include - -namespace mapbox { namespace util { namespace geojsonvt { - -class Time { -public: - inline static void time(std::string activity) { - Time::activities[activity] = clock(); - } - - inline static void timeEnd(std::string activity) { - printf("%s: %fms\n", activity.c_str(), double(clock() - Time::activities[activity]) / (CLOCKS_PER_SEC / 1000)); - } - -private: - static std::unordered_map activities; -}; - -} /* namespace geojsonvt */ } /* namespace util */ } /* namespace mapbox */ - -#endif // MAPBOX_UTIL_GEOJSONVT_UTIL From e99661f0f6fe3f11491952e3c066c54b90269db1 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 15 Jun 2015 16:47:04 -0700 Subject: [PATCH 82/85] patch up GeoJSONVT setup --- .gitmodules | 1 + Makefile | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.gitmodules b/.gitmodules index 9bd44721abb..d84cfecbc21 100644 --- a/.gitmodules +++ b/.gitmodules @@ -17,6 +17,7 @@ [submodule "platform/ios/vendor/SMCalloutView"] path = platform/ios/vendor/SMCalloutView url = https://github.com/nfarina/calloutview.git + [submodule "src/mbgl/util/geojsonvt"] path = src/mbgl/util/geojsonvt url = https://github.com/mapbox/geojson-vt-cpp diff --git a/Makefile b/Makefile index 748105102a6..2201d1b53f6 100644 --- a/Makefile +++ b/Makefile @@ -39,6 +39,9 @@ else SMCalloutView: endif +geojsonvt: + git submodule update --init src/mbgl/util/geojsonvt + KIF: git submodule update --init test/ios/KIF @@ -46,11 +49,11 @@ KIF: #### Build files ############################################################### .PRECIOUS: Makefile/project -Makefile/project: config/$(HOST).gypi styles/styles SMCalloutView +Makefile/project: config/$(HOST).gypi styles/styles SMCalloutView geojsonvt deps/run_gyp gyp/$(HOST).gyp $(CONFIG_$(HOST)) $(LIBS_$(HOST)) --generator-output=./build/$(HOST) -f make .PRECIOUS: Xcode/project -Xcode/project: config/$(HOST).gypi styles/styles SMCalloutView +Xcode/project: config/$(HOST).gypi styles/styles SMCalloutView geojsonvt deps/run_gyp gyp/$(HOST).gyp $(CONFIG_$(HOST)) $(LIBS_$(HOST)) --generator-output=./build/$(HOST) -f xcode @@ -115,7 +118,7 @@ xproj: xosx-proj #### iOS application builds #################################################### .PRECIOUS: Xcode/ios -Xcode/ios: gyp/ios.gyp config/ios.gypi styles/styles SMCalloutView +Xcode/ios: gyp/ios.gyp config/ios.gypi styles/styles SMCalloutView geojsonvt deps/run_gyp gyp/ios.gyp $(CONFIG_ios) $(LIBS_ios) --generator-output=./build/ios -f xcode .PHONY: ios-proj ios isim ipackage From 77c99f3eb94b064ba34bc222661a414de1890975 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 15 Jun 2015 17:05:09 -0700 Subject: [PATCH 83/85] add geojsonvt submodule for Android Linux --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 2201d1b53f6..1c15773b444 100644 --- a/Makefile +++ b/Makefile @@ -175,7 +175,7 @@ run-xlinux: xlinux .PRECIOUS: Makefile/android-% Makefile/android-%: CMD = deps/run_gyp android/mapboxgl-app.gyp $(CONFIG_android-$*) $(LIBS_android) --generator-output=./build/android-$* -f make-android -Makefile/android-%: config/android-%.gypi styles/styles +Makefile/android-%: config/android-%.gypi styles/styles geojsonvt @echo $(CMD) @$(ENV_android-$*) $(CMD) From e45bacf4f9ad8fe743a5cc8d28b7adc351c14236 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 15 Jun 2015 18:17:06 -0700 Subject: [PATCH 84/85] move to shared private header --- platform/ios/MGLMultiPoint_Private.h | 8 ++++++++ platform/ios/MGLPolygon.m | 7 +------ platform/ios/MGLPolyline.m | 7 +------ 3 files changed, 10 insertions(+), 12 deletions(-) create mode 100644 platform/ios/MGLMultiPoint_Private.h diff --git a/platform/ios/MGLMultiPoint_Private.h b/platform/ios/MGLMultiPoint_Private.h new file mode 100644 index 00000000000..a1a70a6778f --- /dev/null +++ b/platform/ios/MGLMultiPoint_Private.h @@ -0,0 +1,8 @@ +#import "MGLMultiPoint.h" + +@interface MGLMultiPoint (Private) + +- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count; +- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds; + +@end diff --git a/platform/ios/MGLPolygon.m b/platform/ios/MGLPolygon.m index 313a41876f1..d3b5f2c2a4c 100644 --- a/platform/ios/MGLPolygon.m +++ b/platform/ios/MGLPolygon.m @@ -1,11 +1,6 @@ #import "MGLPolygon.h" -@interface MGLMultiPoint (MGLPolygon) - -- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count; -- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds; - -@end +#import "MGLMultiPoint_Private.h" @implementation MGLPolygon diff --git a/platform/ios/MGLPolyline.m b/platform/ios/MGLPolyline.m index 8b9b312d044..86d113bd166 100644 --- a/platform/ios/MGLPolyline.m +++ b/platform/ios/MGLPolyline.m @@ -1,11 +1,6 @@ #import "MGLPolyline.h" -@interface MGLMultiPoint (MGLPolyline) - -- (instancetype)initWithCoordinates:(CLLocationCoordinate2D *)coords count:(NSUInteger)count; -- (BOOL)intersectsOverlayBounds:(MGLCoordinateBounds)overlayBounds; - -@end +#import "MGLMultiPoint_Private.h" @implementation MGLPolyline From effd91c3da7064a1f5087988efa18fbd192f34a2 Mon Sep 17 00:00:00 2001 From: "Justin R. Miller" Date: Mon, 15 Jun 2015 18:19:28 -0700 Subject: [PATCH 85/85] fix missing GYP include --- gyp/platform-ios.gypi | 1 + 1 file changed, 1 insertion(+) diff --git a/gyp/platform-ios.gypi b/gyp/platform-ios.gypi index 52424636417..484135a2ecd 100644 --- a/gyp/platform-ios.gypi +++ b/gyp/platform-ios.gypi @@ -37,6 +37,7 @@ '../include/mbgl/ios/MGLTypes.h', '../platform/ios/MGLTypes.m', '../include/mbgl/ios/MGLMultiPoint.h', + '../platform/ios/MGLMultiPoint_Private.h', '../platform/ios/MGLMultiPoint.mm', '../include/mbgl/ios/MGLOverlay.h', '../include/mbgl/ios/MGLPointAnnotation.h',