1 #include <mbgl/geometry/feature_index.hpp>
2 #include <mbgl/renderer/render_layer.hpp>
3 #include <mbgl/renderer/query.hpp>
4 #include <mbgl/renderer/layers/render_symbol_layer.hpp>
5 #include <mbgl/text/collision_index.hpp>
6 #include <mbgl/util/constants.hpp>
7 #include <mbgl/util/math.hpp>
8 #include <mbgl/math/minmax.hpp>
9 #include <mbgl/style/filter.hpp>
10 #include <mbgl/tile/tile_id.hpp>
11 
12 #include <mapbox/geometry/envelope.hpp>
13 
14 #include <cassert>
15 #include <string>
16 
17 namespace mbgl {
18 
FeatureIndex(std::unique_ptr<const GeometryTileData> tileData_)19 FeatureIndex::FeatureIndex(std::unique_ptr<const GeometryTileData> tileData_)
20     : grid(util::EXTENT, util::EXTENT, util::EXTENT / 16) // 16x16 grid -> 32px cell
21     , tileData(std::move(tileData_)) {
22 }
23 
insert(const GeometryCollection & geometries,std::size_t index,const std::string & sourceLayerName,const std::string & bucketLeaderID)24 void FeatureIndex::insert(const GeometryCollection& geometries,
25                           std::size_t index,
26                           const std::string& sourceLayerName,
27                           const std::string& bucketLeaderID) {
28     for (const auto& ring : geometries) {
29         auto envelope = mapbox::geometry::envelope(ring);
30         if (envelope.min.x < util::EXTENT &&
31             envelope.min.y < util::EXTENT &&
32             envelope.max.x >= 0 &&
33             envelope.max.y >= 0) {
34             grid.insert(IndexedSubfeature(index, sourceLayerName, bucketLeaderID, sortIndex++),
35                         {convertPoint<float>(envelope.min), convertPoint<float>(envelope.max)});
36         }
37     }
38 }
39 
query(std::unordered_map<std::string,std::vector<Feature>> & result,const GeometryCoordinates & queryGeometry,const TransformState & transformState,const mat4 & posMatrix,const double tileSize,const double scale,const RenderedQueryOptions & queryOptions,const UnwrappedTileID & tileID,const std::vector<const RenderLayer * > & layers,const float additionalQueryPadding) const40 void FeatureIndex::query(
41         std::unordered_map<std::string, std::vector<Feature>>& result,
42         const GeometryCoordinates& queryGeometry,
43         const TransformState& transformState,
44         const mat4& posMatrix,
45         const double tileSize,
46         const double scale,
47         const RenderedQueryOptions& queryOptions,
48         const UnwrappedTileID& tileID,
49         const std::vector<const RenderLayer*>& layers,
50         const float additionalQueryPadding) const {
51 
52     if (!tileData) {
53         return;
54     }
55 
56     // Determine query radius
57     const float pixelsToTileUnits = util::EXTENT / tileSize / scale;
58     const int16_t additionalPadding = std::min<int16_t>(util::EXTENT, additionalQueryPadding * pixelsToTileUnits);
59 
60     // Query the grid index
61     mapbox::geometry::box<int16_t> box = mapbox::geometry::envelope(queryGeometry);
62     std::vector<IndexedSubfeature> features = grid.query({ convertPoint<float>(box.min - additionalPadding),
63                                                            convertPoint<float>(box.max + additionalPadding) });
64 
65 
66     std::sort(features.begin(), features.end(), [](const IndexedSubfeature& a, const IndexedSubfeature& b) {
67         return a.sortIndex > b.sortIndex;
68     });
69     size_t previousSortIndex = std::numeric_limits<size_t>::max();
70     for (const auto& indexedFeature : features) {
71 
72         // If this feature is the same as the previous feature, skip it.
73         if (indexedFeature.sortIndex == previousSortIndex) continue;
74         previousSortIndex = indexedFeature.sortIndex;
75 
76         addFeature(result, indexedFeature, queryOptions, tileID.canonical, layers, queryGeometry, transformState, pixelsToTileUnits, posMatrix);
77     }
78 }
79 
80 std::unordered_map<std::string, std::vector<Feature>>
lookupSymbolFeatures(const std::vector<IndexedSubfeature> & symbolFeatures,const RenderedQueryOptions & queryOptions,const std::vector<const RenderLayer * > & layers,const OverscaledTileID & tileID,const std::shared_ptr<std::vector<size_t>> & featureSortOrder) const81 FeatureIndex::lookupSymbolFeatures(const std::vector<IndexedSubfeature>& symbolFeatures,
82                                    const RenderedQueryOptions& queryOptions,
83                                    const std::vector<const RenderLayer*>& layers,
84                                    const OverscaledTileID& tileID,
85                                    const std::shared_ptr<std::vector<size_t>>& featureSortOrder) const {
86     std::unordered_map<std::string, std::vector<Feature>> result;
87     if (!tileData) {
88         return result;
89     }
90     std::vector<IndexedSubfeature> sortedFeatures(symbolFeatures.begin(), symbolFeatures.end());
91 
92     std::sort(sortedFeatures.begin(), sortedFeatures.end(), [featureSortOrder](const IndexedSubfeature& a, const IndexedSubfeature& b) {
93         // Same idea as the non-symbol sort order, but symbol features may have changed their sort order
94         // since their corresponding IndexedSubfeature was added to the CollisionIndex
95         // The 'featureSortOrder' is relatively inefficient for querying but cheap to build on every bucket sort
96         if (featureSortOrder) {
97             // queryRenderedSymbols documentation says we'll return features in
98             // "top-to-bottom" rendering order (aka last-to-first).
99             // Actually there can be multiple symbol instances per feature, so
100             // we sort each feature based on the first matching symbol instance.
101             auto sortedA = std::find(featureSortOrder->begin(), featureSortOrder->end(), a.index);
102             auto sortedB = std::find(featureSortOrder->begin(), featureSortOrder->end(), b.index);
103             assert(sortedA != featureSortOrder->end());
104             assert(sortedB != featureSortOrder->end());
105             return sortedA > sortedB;
106         } else {
107             // Bucket hasn't been re-sorted based on angle, so use same "reverse of appearance in source data"
108             // logic as non-symboles
109             return a.sortIndex > b.sortIndex;
110         }
111     });
112 
113     for (const auto& symbolFeature : sortedFeatures) {
114         mat4 unusedMatrix;
115         addFeature(result, symbolFeature, queryOptions, tileID.canonical, layers, GeometryCoordinates(), {}, 0, unusedMatrix);
116     }
117     return result;
118 }
119 
addFeature(std::unordered_map<std::string,std::vector<Feature>> & result,const IndexedSubfeature & indexedFeature,const RenderedQueryOptions & options,const CanonicalTileID & tileID,const std::vector<const RenderLayer * > & layers,const GeometryCoordinates & queryGeometry,const TransformState & transformState,const float pixelsToTileUnits,const mat4 & posMatrix) const120 void FeatureIndex::addFeature(
121     std::unordered_map<std::string, std::vector<Feature>>& result,
122     const IndexedSubfeature& indexedFeature,
123     const RenderedQueryOptions& options,
124     const CanonicalTileID& tileID,
125     const std::vector<const RenderLayer*>& layers,
126     const GeometryCoordinates& queryGeometry,
127     const TransformState& transformState,
128     const float pixelsToTileUnits,
129     const mat4& posMatrix) const {
130 
131     auto getRenderLayer = [&] (const std::string& layerID) -> const RenderLayer* {
132         for (const auto& layer : layers) {
133             if (layer->getID() == layerID) {
134                 return layer;
135             }
136         }
137         return nullptr;
138     };
139 
140     // Lazily calculated.
141     std::unique_ptr<GeometryTileLayer> sourceLayer;
142     std::unique_ptr<GeometryTileFeature> geometryTileFeature;
143 
144     for (const std::string& layerID : bucketLayerIDs.at(indexedFeature.bucketLeaderID)) {
145         const RenderLayer* renderLayer = getRenderLayer(layerID);
146         if (!renderLayer) {
147             continue;
148         }
149 
150         if (!geometryTileFeature) {
151             sourceLayer = tileData->getLayer(indexedFeature.sourceLayerName);
152             assert(sourceLayer);
153 
154             geometryTileFeature = sourceLayer->getFeature(indexedFeature.index);
155             assert(geometryTileFeature);
156         }
157 
158         if (!renderLayer->is<RenderSymbolLayer>() &&
159              !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, transformState, pixelsToTileUnits, posMatrix)) {
160             continue;
161         }
162 
163         if (options.filter && !(*options.filter)(style::expression::EvaluationContext { static_cast<float>(tileID.z), geometryTileFeature.get() })) {
164             continue;
165         }
166 
167         result[layerID].push_back(convertFeature(*geometryTileFeature, tileID));
168     }
169 }
170 
translateQueryGeometry(const GeometryCoordinates & queryGeometry,const std::array<float,2> & translate,const style::TranslateAnchorType anchorType,const float bearing,const float pixelsToTileUnits)171 optional<GeometryCoordinates> FeatureIndex::translateQueryGeometry(
172         const GeometryCoordinates& queryGeometry,
173         const std::array<float, 2>& translate,
174         const style::TranslateAnchorType anchorType,
175         const float bearing,
176         const float pixelsToTileUnits) {
177     if (translate[0] == 0 && translate[1] == 0) {
178         return {};
179     }
180 
181     GeometryCoordinate translateVec(translate[0] * pixelsToTileUnits, translate[1] * pixelsToTileUnits);
182     if (anchorType == style::TranslateAnchorType::Viewport) {
183         translateVec = util::rotate(translateVec, -bearing);
184     }
185 
186     GeometryCoordinates translated;
187     for (const auto& p : queryGeometry) {
188         translated.push_back(p - translateVec);
189     }
190     return translated;
191 }
192 
setBucketLayerIDs(const std::string & bucketLeaderID,const std::vector<std::string> & layerIDs)193 void FeatureIndex::setBucketLayerIDs(const std::string& bucketLeaderID, const std::vector<std::string>& layerIDs) {
194     bucketLayerIDs[bucketLeaderID] = layerIDs;
195 }
196 
197 } // namespace mbgl
198