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