Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
Change template class into template function in geometry_util, add ra…
Browse files Browse the repository at this point in the history
…nge assertion for distance contaniner IndexRange
  • Loading branch information
zmiao committed Apr 20, 2020
1 parent db12759 commit 7a64458
Show file tree
Hide file tree
Showing 5 changed files with 136 additions and 97 deletions.
6 changes: 3 additions & 3 deletions src/mbgl/geometry/feature_index.cpp
Expand Up @@ -8,7 +8,7 @@
#include <mbgl/text/collision_index.hpp>
#include <mbgl/tile/tile_id.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/geometry_within.hpp>
#include <mbgl/util/geometry_util.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/projection.hpp>

Expand Down Expand Up @@ -216,7 +216,7 @@ void DynamicFeatureIndex::query(std::unordered_map<std::string, std::vector<Feat
const mbgl::ScreenLineString& queryGeometry,
const TransformState& state) const {
if (features.empty()) return;
mbgl::WithinBBox queryBox = DefaultBBox;
mbgl::GeometryBBox<int64_t> queryBox = DefaultWithinBBox;
for (const auto& p : queryGeometry) {
const LatLng c = screenCoordinateToLatLng(p, state);
const Point<double> pm = project(c, state);
Expand All @@ -225,7 +225,7 @@ void DynamicFeatureIndex::query(std::unordered_map<std::string, std::vector<Feat
}
for (const auto& f : features) {
// hit testing
mbgl::WithinBBox featureBox = DefaultBBox;
mbgl::GeometryBBox<int64_t> featureBox = DefaultWithinBBox;
for (const auto& p : f.envelope->front()) mbgl::updateBBox(featureBox, p);

const bool hit = mbgl::boxWithinBox(featureBox, queryBox) || mbgl::boxWithinBox(queryBox, featureBox);
Expand Down
70 changes: 34 additions & 36 deletions src/mbgl/style/expression/distance.cpp
Expand Up @@ -24,49 +24,41 @@ namespace {
const std::size_t MinPointsSize = 100;
const std::size_t MinLinePointsSize = 50;

using BBox = std::array<double, 4>;
const BBox DefaultBBox = BBox{std::numeric_limits<double>::infinity(),
std::numeric_limits<double>::infinity(),
-std::numeric_limits<double>::infinity(),
-std::numeric_limits<double>::infinity()};

// bbox[minX, minY, maxX, maxY]
void updateBBox(BBox& bbox, const mapbox::geometry::point<double>& p) {
bbox[0] = std::min(p.x, bbox[0]);
bbox[1] = std::min(p.y, bbox[1]);
bbox[2] = std::max(p.x, bbox[2]);
bbox[3] = std::max(p.y, bbox[3]);
}

// Inclusive index range for multipoint or linestring container
using IndexRange = std::pair<std::size_t, std::size_t>;

std::size_t getRangeSize(const IndexRange& range) {
return range.second - range.first + 1;
}

BBox getBBox(const mapbox::geometry::multi_point<double>& points, const IndexRange& range) {
BBox bbox = DefaultBBox;
using DistanceBBox = GeometryBBox<double>;

DistanceBBox getBBox(const mapbox::geometry::multi_point<double>& points, const IndexRange& range) {
assert(range.second >= range.first && range.second < points.size());
DistanceBBox bbox = DefaultDistanceBBox;
for (std::size_t i = range.first; i <= range.second; ++i) {
updateBBox(bbox, points[i]);
}
return bbox;
}

BBox getBBox(const mapbox::geometry::line_string<double>& line, const IndexRange& range) {
BBox bbox = DefaultBBox;
DistanceBBox getBBox(const mapbox::geometry::line_string<double>& line, const IndexRange& range) {
assert(range.second >= range.first && range.second < line.size());
DistanceBBox bbox = DefaultDistanceBBox;
for (std::size_t i = range.first; i <= range.second; ++i) {
updateBBox(bbox, line[i]);
}
return bbox;
}

// Calculate the distance between two bounding boxes.
// Calculate the delta in x and y direction, and use two fake points {0, 0} and {dx, dy} to calculate the distance.
// Distance will be 0 if bounding box are overlapping.
double bboxToBBoxDistance(const BBox& bbox1, const BBox& bbox2, mapbox::cheap_ruler::CheapRuler& ruler) {
double dx = 0.;
double dy = 0.;
// Calculate the delta in x and y direction, and use two fake points {0.0, 0.0} and {dx, dy} to calculate the distance.
// Distance will be 0.0 if bounding box are overlapping.
double bboxToBBoxDistance(const DistanceBBox& bbox1,
const DistanceBBox& bbox2,
mapbox::cheap_ruler::CheapRuler& ruler) {
double dx = 0.0;
double dy = 0.0;
// bbox1 in left side
if (bbox1[2] < bbox2[0]) {
dx = bbox2[0] - bbox1[2];
Expand All @@ -83,7 +75,7 @@ double bboxToBBoxDistance(const BBox& bbox1, const BBox& bbox2, mapbox::cheap_ru
if (bbox1[3] < bbox2[1]) {
dy = bbox2[1] - bbox1[3];
}
return ruler.distance(mapbox::geometry::point<double>{0., 0.}, mapbox::geometry::point<double>{dx, dy});
return ruler.distance(mapbox::geometry::point<double>{0.0, 0.0}, mapbox::geometry::point<double>{dx, dy});
}

double pointToLineDistance(const mapbox::geometry::point<double>& point,
Expand All @@ -98,14 +90,16 @@ double lineToLineDistance(const mapbox::geometry::line_string<double>& line1,
const mapbox::geometry::line_string<double>& line2,
IndexRange& range2,
mapbox::cheap_ruler::CheapRuler& ruler) {
assert(range1.second >= range1.first && range1.second < line1.size());
assert(range2.second >= range2.first && range2.second < line2.size());
double dist = std::numeric_limits<double>::infinity();
for (std::size_t i = range1.first; i < range1.second; ++i) {
const auto& p1 = line1[i];
const auto& p2 = line1[i + 1];
for (std::size_t j = range2.first; j < range2.second; ++j) {
const auto& q1 = line2[j];
const auto& q2 = line2[j + 1];
if (GeometryUtil<double>::segmentIntersectSegment(p1, p2, q1, q2)) return 0.;
if (segmentIntersectSegment(p1, p2, q1, q2)) return 0.0;
auto dist1 = std::min(pointToLineDistance(p1, mapbox::geometry::line_string<double>{q1, q2}, ruler),
pointToLineDistance(p2, mapbox::geometry::line_string<double>{q1, q2}, ruler));
auto dist2 = std::min(pointToLineDistance(q1, mapbox::geometry::line_string<double>{p1, p2}, ruler),
Expand All @@ -121,11 +115,13 @@ double pointsToPointsDistance(const mapbox::geometry::multi_point<double>& point
const mapbox::geometry::multi_point<double>& points2,
IndexRange& range2,
mapbox::cheap_ruler::CheapRuler& ruler) {
assert(range1.second >= range1.first && range1.second < points1.size());
assert(range2.second >= range2.first && range2.second < points2.size());
double dist = std::numeric_limits<double>::infinity();
for (std::size_t i = range1.first; i <= range1.second; ++i) {
for (std::size_t j = range2.first; j <= range2.second; ++j) {
dist = std::min(dist, ruler.distance(points1[i], points2[j]));
if (dist == 0.) return dist;
if (dist == 0.0) return dist;
}
}
return dist;
Expand Down Expand Up @@ -154,8 +150,7 @@ std::pair<mbgl::optional<IndexRange>, mbgl::optional<IndexRange>> splitRange(con

// <distance, range1, range2>
using DistPair = std::tuple<double, IndexRange, IndexRange>;
class Comparator {
public:
struct Comparator {
bool operator()(DistPair& left, DistPair& right) { return std::get<0>(left) < std::get<0>(right); }
};
// The priority queue will ensure the top element would always be the pair that has the biggest distance
Expand All @@ -180,7 +175,7 @@ double lineToLineDistance(const mapbox::geometry::line_string<double>& line1,
// In case the set size are relatively small, we could use brute-force directly
if (getRangeSize(rangeA) <= MinLinePointsSize && getRangeSize(rangeB) <= MinLinePointsSize) {
miniDist = std::min(miniDist, lineToLineDistance(line1, rangeA, line2, rangeB, ruler));
if (miniDist == 0.) return 0.;
if (miniDist == 0.0) return 0.0;
} else {
auto newRangesA = splitRange(rangeA, true /*isLine*/);
auto newRangesB = splitRange(rangeB, true /*isLine*/);
Expand Down Expand Up @@ -223,7 +218,7 @@ double pointsToPointsDistance(const mapbox::geometry::multi_point<double>& point
// In case the set size are relatively small, we could use brute-force directly
if (getRangeSize(rangeA) <= MinPointsSize && getRangeSize(rangeB) <= MinPointsSize) {
miniDist = std::min(miniDist, pointsToPointsDistance(pointSet1, rangeA, pointSet2, rangeB, ruler));
if (miniDist == 0.) return 0.;
if (miniDist == 0.0) return 0.0;
} else {
auto newRangesA = splitRange(rangeA, false /*isLine*/);
auto newRangesB = splitRange(rangeB, false /*isLine*/);
Expand Down Expand Up @@ -263,11 +258,13 @@ double pointsToLineDistance(const mapbox::geometry::multi_point<double>& points,

// In case the set size are relatively small, we could use brute-force directly
if (getRangeSize(rangeA) <= MinPointsSize && getRangeSize(rangeB) <= MinLinePointsSize) {
assert(rangeA.second >= rangeA.first && rangeA.second < points.size());
assert(rangeB.second >= rangeB.first && rangeB.second < line.size());
auto subLine =
mapbox::geometry::multi_point<double>(line.begin() + rangeB.first, line.begin() + rangeB.second + 1);
for (std::size_t i = rangeA.first; i <= rangeA.second; ++i) {
miniDist = std::min(miniDist, pointToLineDistance(points[i], subLine, ruler));
if (miniDist == 0.) return 0.;
if (miniDist == 0.0) return 0.0;
}
} else {
auto newRangesA = splitRange(rangeA, false /*isLine*/);
Expand Down Expand Up @@ -296,7 +293,7 @@ double pointsToLinesDistance(const mapbox::geometry::multi_point<double>& points
double dist = std::numeric_limits<double>::infinity();
for (const auto& line : lines) {
dist = std::min(dist, pointsToLineDistance(points, line, ruler));
if (dist == 0.) return dist;
if (dist == 0.0) return 0.0;
}
return dist;
}
Expand All @@ -307,7 +304,7 @@ double lineToLinesDistance(const mapbox::geometry::line_string<double>& line,
double dist = std::numeric_limits<double>::infinity();
for (const auto& l : lines) {
dist = std::min(dist, lineToLineDistance(line, l, ruler));
if (dist == 0.) return dist;
if (dist == 0.0) return 0.0;
}
return dist;
}
Expand Down Expand Up @@ -372,7 +369,7 @@ double calculateDistance(const GeometryTileFeature& feature,
double dist = std::numeric_limits<double>::infinity();
for (const auto& line : lines) {
dist = std::min(dist, lineToGeometryDistance(line, geoSet, unit));
if (dist == 0.) return dist;
if (dist == 0.0) return dist;
}
return dist;
},
Expand Down Expand Up @@ -438,8 +435,8 @@ optional<Arguments> parseValue(const style::conversion::Convertible& value, styl
}
}
ctx.error(
"'distance' expression needs to be an array with format [\"Distance\", GeoJSONObj, \"unit(optional, Meters by "
"default)\"].");
"'distance' expression needs to be an array with format [\"Distance\", GeoJSONObj, \"Units\"(\"Units\" is an "
"optional argument, 'Meters' will be used by default)\"].");
return nullopt;
}

Expand Down Expand Up @@ -474,6 +471,7 @@ EvaluationResult Distance::evaluate(const EvaluationContext& params) const {
if (geometryType == FeatureType::Point || geometryType == FeatureType::LineString) {
auto distance = calculateDistance(*params.feature, *params.canonical, geometries, unit);
if (!std::isnan(distance)) {
assert(distance >= 0.0);
return distance;
}
}
Expand Down
36 changes: 7 additions & 29 deletions src/mbgl/style/expression/within.cpp
Expand Up @@ -15,29 +15,6 @@
namespace mbgl {
namespace {

// contains minX, minY, maxX, maxY
using WithinBBox = std::array<int64_t, 4>;
const WithinBBox DefaultBBox = WithinBBox{std::numeric_limits<int64_t>::max(),
std::numeric_limits<int64_t>::max(),
std::numeric_limits<int64_t>::min(),
std::numeric_limits<int64_t>::min()};

// check if bbox1 is within bbox2
void updateBBox(WithinBBox& bbox, const Point<int64_t>& p) {
bbox[0] = std::min(p.x, bbox[0]);
bbox[1] = std::min(p.y, bbox[1]);
bbox[2] = std::max(p.x, bbox[2]);
bbox[3] = std::max(p.y, bbox[3]);
}

bool boxWithinBox(const WithinBBox& bbox1, const WithinBBox& bbox2) {
if (bbox1[0] <= bbox2[0]) return false;
if (bbox1[2] >= bbox2[2]) return false;
if (bbox1[1] <= bbox2[1]) return false;
if (bbox1[3] >= bbox2[3]) return false;
return true;
}

Point<int64_t> latLonToTileCoodinates(const Point<double>& point, const mbgl::CanonicalTileID& canonical) {
const double size = util::EXTENT * std::pow(2, canonical.z);

Expand All @@ -53,6 +30,7 @@ Point<int64_t> latLonToTileCoodinates(const Point<double>& point, const mbgl::Ca
return p;
};

using WithinBBox = GeometryBBox<int64_t>;
Polygon<int64_t> getTilePolygon(const Polygon<double>& polygon,
const mbgl::CanonicalTileID& canonical,
WithinBBox& bbox) {
Expand Down Expand Up @@ -149,7 +127,7 @@ MultiLineString<int64_t> getTileLines(const GeometryCollection& lines,

const auto worldSize = util::EXTENT * std::pow(2, canonical.z);
if (bbox[2] - bbox[0] <= worldSize / 2) {
bbox = DefaultBBox;
bbox = DefaultWithinBBox;
for (auto& line : results) {
for (auto& p : line) {
updatePoint(p, bbox, polyBBox, worldSize);
Expand All @@ -162,28 +140,28 @@ MultiLineString<int64_t> getTileLines(const GeometryCollection& lines,
bool featureWithinPolygons(const GeometryTileFeature& feature,
const CanonicalTileID& canonical,
const Feature::geometry_type& polygonGeoSet) {
WithinBBox polyBBox = DefaultBBox;
WithinBBox polyBBox = DefaultWithinBBox;
const auto polygons = getTilePolygons(polygonGeoSet, canonical, polyBBox);
assert(!polygons.empty());
const GeometryCollection& geometries = feature.getGeometries();
switch (feature.getType()) {
case FeatureType::Point: {
assert(!geometries.empty());
WithinBBox pointBBox = DefaultBBox;
WithinBBox pointBBox = DefaultWithinBBox;
MultiPoint<int64_t> points = getTilePoints(geometries.at(0), canonical, pointBBox, polyBBox);
if (!boxWithinBox(pointBBox, polyBBox)) return false;

return std::all_of(points.begin(), points.end(), [&polygons](const auto& p) {
return GeometryUtil<int64_t>::pointWithinPolygons(p, polygons);
return pointWithinPolygons<int64_t>(p, polygons);
});
}
case FeatureType::LineString: {
WithinBBox lineBBox = DefaultBBox;
WithinBBox lineBBox = DefaultWithinBBox;
MultiLineString<int64_t> multiLineString = getTileLines(geometries, canonical, lineBBox, polyBBox);
if (!boxWithinBox(lineBBox, polyBBox)) return false;

return std::all_of(multiLineString.begin(), multiLineString.end(), [&polygons](const auto& line) {
return GeometryUtil<int64_t>::lineStringWithinPolygons(line, polygons);
return lineStringWithinPolygons<int64_t>(line, polygons);
});
}
default:
Expand Down

0 comments on commit 7a64458

Please sign in to comment.