Files
MapLibreParticleLab/src/mbgl/geometry/feature_index.cpp
2016-05-03 17:25:49 -07:00

193 lines
6.7 KiB
C++

#include <mbgl/geometry/feature_index.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/style/style.hpp>
#include <mbgl/style/style_layer.hpp>
#include <mbgl/layer/symbol_layer.hpp>
#include <mbgl/util/get_geometries.hpp>
#include <mbgl/text/collision_tile.hpp>
#include <mbgl/util/constants.hpp>
#include <cassert>
#include <string>
using 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 (auto& ring : geometries) {
float minX = std::numeric_limits<float>::infinity();
float minY = std::numeric_limits<float>::infinity();
float maxX = -std::numeric_limits<float>::infinity();
float maxY = -std::numeric_limits<float>::infinity();
for (auto& p : ring) {
const float x = p.x;
const float y = p.y;
minX = util::min(minX, x);
minY = util::min(minY, y);
maxX = util::max(maxX, x);
maxY = util::max(maxY, y);
}
grid.insert(
IndexedSubfeature { index, sourceLayerName, bucketName, sortIndex++ },
{ int32_t(minX), int32_t(minY), int32_t(maxX), int32_t(maxY) });
}
}
bool vectorContains(const std::vector<std::string>& vector, const std::string& s) {
return std::find(vector.begin(), vector.end(), s) != vector.end();
}
bool vectorsIntersect(const std::vector<std::string>& vectorA, const std::vector<std::string>& vectorB) {
for (auto& a : vectorA) {
if (vectorContains(vectorB, a)) return true;;
}
return false;
}
bool topDown(const IndexedSubfeature& a, const IndexedSubfeature& b) {
return a.sortIndex > b.sortIndex;
}
bool topDownSymbols(const IndexedSubfeature& a, const IndexedSubfeature& b) {
return a.sortIndex < b.sortIndex;
}
void FeatureIndex::query(
std::unordered_map<std::string, std::vector<Feature>>& result,
const GeometryCollection& queryGeometry,
const float bearing,
const double tileSize,
const double scale,
const optional<std::vector<std::string>>& filterLayerIDs,
const GeometryTile& geometryTile,
const Style& style) const {
const float pixelsToTileUnits = util::EXTENT / tileSize / scale;
float additionalRadius = style.getQueryRadius() * pixelsToTileUnits;
float minX = std::numeric_limits<float>::infinity();
float minY = std::numeric_limits<float>::infinity();
float maxX = -std::numeric_limits<float>::infinity();
float maxY = -std::numeric_limits<float>::infinity();
for (auto& ring : queryGeometry) {
for (auto& p : ring) {
minX = util::min<float>(minX, p.x);
minY = util::min<float>(minY, p.y);
maxX = util::max<float>(maxX, p.x);
maxY = util::max<float>(maxY, p.y);
}
}
std::vector<IndexedSubfeature> features = grid.query({
int(minX - additionalRadius),
int(minY - additionalRadius),
int(maxX + additionalRadius),
int(maxY + additionalRadius)
});
std::sort(features.begin(), features.end(), topDown);
size_t previousSortIndex = std::numeric_limits<size_t>::max();
for (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, filterLayerIDs, geometryTile, style, bearing, pixelsToTileUnits);
}
// query symbol features
assert(collisionTile);
std::vector<IndexedSubfeature> symbolFeatures = collisionTile->queryRenderedSymbols(minX, minY, maxX, maxY, scale);
std::sort(symbolFeatures.begin(), symbolFeatures.end(), topDownSymbols);
for (auto& symbolFeature : symbolFeatures) {
addFeature(result, symbolFeature, queryGeometry, filterLayerIDs, geometryTile, style, bearing, pixelsToTileUnits);
}
}
void FeatureIndex::addFeature(
std::unordered_map<std::string, std::vector<Feature>>& result,
const IndexedSubfeature& indexedFeature,
const GeometryCollection& queryGeometry,
const optional<std::vector<std::string>>& filterLayerIDs,
const GeometryTile& geometryTile,
const Style& style,
const float bearing,
const float pixelsToTileUnits) const {
auto& layerIDs = bucketLayerIDs.at(indexedFeature.bucketName);
if (filterLayerIDs && !vectorsIntersect(layerIDs, *filterLayerIDs)) return;
auto sourceLayer = geometryTile.getLayer(indexedFeature.sourceLayerName);
assert(sourceLayer);
auto geometryTileFeature = sourceLayer->getFeature(indexedFeature.index);
assert(geometryTileFeature);
for (auto& layerID : layerIDs) {
if (filterLayerIDs && !vectorContains(*filterLayerIDs, layerID)) continue;
auto styleLayer = style.getLayer(layerID);
if (!styleLayer) continue;
if (!styleLayer->is<SymbolLayer>()) {
auto geometries = getGeometries(*geometryTileFeature);
if (!styleLayer->queryIntersectsGeometry(queryGeometry, geometries, bearing, pixelsToTileUnits)) continue;
}
Feature feature { mapbox::geometry::point<double>() };
feature.properties = geometryTileFeature->getProperties();
optional<uint64_t> id = geometryTileFeature->getID();
if (id) {
feature.id = Value(*id);
}
result[layerID].push_back(std::move(feature));
}
}
optional<GeometryCollection> FeatureIndex::translateQueryGeometry(
const GeometryCollection& queryGeometry,
const std::array<float, 2>& translate,
const 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 == TranslateAnchorType::Viewport) {
translateVec = util::rotate(translateVec, -bearing);
}
GeometryCollection translated;
for (auto& ring : queryGeometry) {
translated.emplace_back();
auto& translatedRing = translated.back();
for (auto& p : ring) {
translatedRing.push_back(p - translateVec);
}
}
return translated;
}
void FeatureIndex::addBucketLayerName(const std::string& bucketName, const std::string& layerID) {
auto& layerIDs = bucketLayerIDs[bucketName];
layerIDs.push_back(layerID);
}
void FeatureIndex::setCollisionTile(std::unique_ptr<CollisionTile> collisionTile_) {
collisionTile = std::move(collisionTile_);
}