#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { FeatureIndex::FeatureIndex() : grid(util::EXTENT, 16, 0) { } void FeatureIndex::insert(const GeometryCollection& geometries, std::size_t index, const std::string& sourceLayerName, const std::string& bucketName) { for (const auto& ring : geometries) { grid.insert(IndexedSubfeature { index, sourceLayerName, bucketName, sortIndex++ }, mapbox::geometry::envelope(ring)); } } static bool vectorContains(const std::vector& vector, const std::string& s) { return std::find(vector.begin(), vector.end(), s) != vector.end(); } static bool vectorsIntersect(const std::vector& vectorA, const std::vector& vectorB) { for (const auto& a : vectorA) { if (vectorContains(vectorB, a)) { return true; } } return false; } static bool topDown(const IndexedSubfeature& a, const IndexedSubfeature& b) { return a.sortIndex > b.sortIndex; } static bool topDownSymbols(const IndexedSubfeature& a, const IndexedSubfeature& b) { return a.sortIndex < b.sortIndex; } static int16_t getAdditionalQueryRadius(const RenderedQueryOptions& queryOptions, const RenderStyle& style, const GeometryTile& tile, const float pixelsToTileUnits) { // Determine the additional radius needed factoring in property functions float additionalRadius = 0; auto getQueryRadius = [&](const RenderLayer& layer) { auto bucket = tile.getBucket(*layer.baseImpl); if (bucket) { additionalRadius = std::max(additionalRadius, bucket->getQueryRadius(layer) * pixelsToTileUnits); } }; if (queryOptions.layerIDs) { for (const auto& layerID : *queryOptions.layerIDs) { const RenderLayer* layer = style.getRenderLayer(layerID); if (layer) { getQueryRadius(*layer); } } } else { for (const RenderLayer* layer : style.getRenderLayers()) { getQueryRadius(*layer); } } return std::min(util::EXTENT, additionalRadius); } void FeatureIndex::query( std::unordered_map>& result, const GeometryCoordinates& queryGeometry, const float bearing, const double tileSize, const double scale, const RenderedQueryOptions& queryOptions, const GeometryTileData& geometryTileData, const CanonicalTileID& tileID, const RenderStyle& style, const CollisionTile* collisionTile, const GeometryTile& tile) const { // Determine query radius const float pixelsToTileUnits = util::EXTENT / tileSize / scale; const int16_t additionalRadius = getAdditionalQueryRadius(queryOptions, style, tile, pixelsToTileUnits); // Query the grid index mapbox::geometry::box box = mapbox::geometry::envelope(queryGeometry); std::vector features = grid.query({ box.min - additionalRadius, box.max + additionalRadius }); std::sort(features.begin(), features.end(), topDown); size_t previousSortIndex = std::numeric_limits::max(); for (const auto& indexedFeature : features) { // If this feature is the same as the previous feature, skip it. if (indexedFeature.sortIndex == previousSortIndex) continue; previousSortIndex = indexedFeature.sortIndex; addFeature(result, indexedFeature, queryGeometry, queryOptions, geometryTileData, tileID, style, bearing, pixelsToTileUnits); } // Query symbol features, if they've been placed. if (!collisionTile) { return; } std::vector symbolFeatures = collisionTile->queryRenderedSymbols(queryGeometry, scale); std::sort(symbolFeatures.begin(), symbolFeatures.end(), topDownSymbols); for (const auto& symbolFeature : symbolFeatures) { addFeature(result, symbolFeature, queryGeometry, queryOptions, geometryTileData, tileID, style, bearing, pixelsToTileUnits); } } void FeatureIndex::addFeature( std::unordered_map>& result, const IndexedSubfeature& indexedFeature, const GeometryCoordinates& queryGeometry, const RenderedQueryOptions& options, const GeometryTileData& geometryTileData, const CanonicalTileID& tileID, const RenderStyle& style, const float bearing, const float pixelsToTileUnits) const { auto& layerIDs = bucketLayerIDs.at(indexedFeature.bucketName); if (options.layerIDs && !vectorsIntersect(layerIDs, *options.layerIDs)) { return; } auto sourceLayer = geometryTileData.getLayer(indexedFeature.sourceLayerName); assert(sourceLayer); auto geometryTileFeature = sourceLayer->getFeature(indexedFeature.index); assert(geometryTileFeature); for (const auto& layerID : layerIDs) { if (options.layerIDs && !vectorContains(*options.layerIDs, layerID)) { continue; } auto renderLayer = style.getRenderLayer(layerID); if (!renderLayer || (!renderLayer->is() && !renderLayer->queryIntersectsFeature(queryGeometry, *geometryTileFeature, tileID.z, bearing, pixelsToTileUnits))) { continue; } if (options.filter && !(*options.filter)(*geometryTileFeature)) { continue; } result[layerID].push_back(convertFeature(*geometryTileFeature, tileID)); } } optional FeatureIndex::translateQueryGeometry( const GeometryCoordinates& queryGeometry, const std::array& translate, const style::TranslateAnchorType anchorType, const float bearing, const float pixelsToTileUnits) { if (translate[0] == 0 && translate[1] == 0) { return {}; } GeometryCoordinate translateVec(translate[0] * pixelsToTileUnits, translate[1] * pixelsToTileUnits); if (anchorType == style::TranslateAnchorType::Viewport) { translateVec = util::rotate(translateVec, -bearing); } GeometryCoordinates translated; for (const auto& p : queryGeometry) { translated.push_back(p - translateVec); } return translated; } void FeatureIndex::setBucketLayerIDs(const std::string& bucketName, const std::vector& layerIDs) { bucketLayerIDs[bucketName] = layerIDs; } } // namespace mbgl