[core] Distance expression: Change template class into template function in geometry_util, add range assertion for distance contaniner IndexRange
Change distance units to small letters, update tests accordingly
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
@@ -24,20 +24,6 @@ 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>;
|
||||
|
||||
@@ -45,16 +31,20 @@ 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]);
|
||||
}
|
||||
@@ -62,11 +52,13 @@ BBox getBBox(const mapbox::geometry::line_string<double>& line, const IndexRange
|
||||
}
|
||||
|
||||
// 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];
|
||||
@@ -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,
|
||||
@@ -98,6 +90,8 @@ 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];
|
||||
@@ -105,7 +99,7 @@ double lineToLineDistance(const mapbox::geometry::line_string<double>& line1,
|
||||
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),
|
||||
@@ -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;
|
||||
@@ -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
|
||||
@@ -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*/);
|
||||
@@ -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*/);
|
||||
@@ -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*/);
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
},
|
||||
@@ -405,24 +402,24 @@ optional<Arguments> parseValue(const style::conversion::Convertible& value, styl
|
||||
ctx.error("Failed to parse unit argument from 'distance' expression");
|
||||
return nullopt;
|
||||
}
|
||||
if (*input == "Meters" || *input == "Metres") {
|
||||
if (*input == "meters" || *input == "metres") {
|
||||
unit = mapbox::cheap_ruler::CheapRuler::Unit::Meters;
|
||||
} else if (*input == "Kilometers") {
|
||||
} else if (*input == "kilometers") {
|
||||
unit = mapbox::cheap_ruler::CheapRuler::Unit::Kilometers;
|
||||
} else if (*input == "Miles") {
|
||||
} else if (*input == "miles") {
|
||||
unit = mapbox::cheap_ruler::CheapRuler::Unit::Miles;
|
||||
} else if (*input == "NauticalMiles") {
|
||||
} else if (*input == "nauticalmiles") {
|
||||
unit = mapbox::cheap_ruler::CheapRuler::Unit::NauticalMiles;
|
||||
} else if (*input == "Yards") {
|
||||
} else if (*input == "yards") {
|
||||
unit = mapbox::cheap_ruler::CheapRuler::Unit::Yards;
|
||||
} else if (*input == "Feet") {
|
||||
} else if (*input == "feet") {
|
||||
unit = mapbox::cheap_ruler::CheapRuler::Unit::Feet;
|
||||
} else if (*input == "Inches") {
|
||||
} else if (*input == "inches") {
|
||||
unit = mapbox::cheap_ruler::CheapRuler::Unit::Inches;
|
||||
} else {
|
||||
ctx.error(
|
||||
"'distance' expression only accepts following Units: 'Kilometers', 'Miles', 'NauticalMiles', "
|
||||
"'Meters', 'Metres', 'Yards', 'Feet', 'Inches'.");
|
||||
"'distance' expression only accepts following units: kilometers, miles, nauticalmiles, "
|
||||
"meters, metres, yards, feet, inches.");
|
||||
return nullopt;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -547,21 +545,21 @@ Value convertValue(const mapbox::geojson::rapidjson_value& v) {
|
||||
std::string getUnits(const mapbox::cheap_ruler::CheapRuler::Unit& unit) {
|
||||
switch (unit) {
|
||||
case mapbox::cheap_ruler::CheapRuler::Kilometers:
|
||||
return "Kilometers";
|
||||
return "kilometers";
|
||||
case mapbox::cheap_ruler::CheapRuler::Miles:
|
||||
return "Miles";
|
||||
return "miles";
|
||||
case mapbox::cheap_ruler::CheapRuler::NauticalMiles:
|
||||
return "NauticalMiles";
|
||||
return "nauticalmiles";
|
||||
case mapbox::cheap_ruler::CheapRuler::Meters:
|
||||
return "Meters";
|
||||
return "meters";
|
||||
case mapbox::cheap_ruler::CheapRuler::Yards:
|
||||
return "Yards";
|
||||
return "yards";
|
||||
case mapbox::cheap_ruler::CheapRuler::Feet:
|
||||
return "Feet";
|
||||
return "feet";
|
||||
case mapbox::cheap_ruler::CheapRuler::Inches:
|
||||
return "Inches";
|
||||
return "inches";
|
||||
default:
|
||||
return "Error";
|
||||
return "error";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
@@ -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);
|
||||
@@ -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:
|
||||
|
||||
@@ -5,13 +5,31 @@
|
||||
namespace mbgl {
|
||||
|
||||
template <typename T>
|
||||
bool GeometryUtil<T>::rayIntersect(const Point<T>& p, const Point<T>& p1, const Point<T>& p2) {
|
||||
void updateBBox(GeometryBBox<T>& bbox, const Point<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]);
|
||||
}
|
||||
|
||||
// check if bbox1 is within bbox2
|
||||
template <typename T>
|
||||
bool boxWithinBox(const GeometryBBox<T>& bbox1, const GeometryBBox<T>& 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;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool rayIntersect(const Point<T>& p, const Point<T>& p1, const Point<T>& p2) {
|
||||
return ((p1.y > p.y) != (p2.y > p.y)) && (p.x < (p2.x - p1.x) * (p.y - p1.y) / (p2.y - p1.y) + p1.x);
|
||||
}
|
||||
|
||||
// check if point p is on line segment with end points p1 and p2
|
||||
template <typename T>
|
||||
bool GeometryUtil<T>::pointOnBoundary(const Point<T>& p, const Point<T>& p1, const Point<T>& p2) {
|
||||
bool pointOnBoundary(const Point<T>& p, const Point<T>& p1, const Point<T>& p2) {
|
||||
// requirements of point p on line segment:
|
||||
// 1. colinear: cross product of vector p->p1(x1, y1) and vector p->p2(x2, y2) equals to 0
|
||||
// 2. p is between p1 and p2
|
||||
@@ -23,10 +41,7 @@ bool GeometryUtil<T>::pointOnBoundary(const Point<T>& p, const Point<T>& p1, con
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool GeometryUtil<T>::segmentIntersectSegment(const Point<T>& a,
|
||||
const Point<T>& b,
|
||||
const Point<T>& c,
|
||||
const Point<T>& d) {
|
||||
bool segmentIntersectSegment(const Point<T>& a, const Point<T>& b, const Point<T>& c, const Point<T>& d) {
|
||||
// a, b are end points for line segment1, c and d are end points for line segment2
|
||||
const auto perp = [](const Point<T>& v1, const Point<T>& v2) { return (v1.x * v2.y - v1.y * v2.x); };
|
||||
|
||||
@@ -58,7 +73,7 @@ bool GeometryUtil<T>::segmentIntersectSegment(const Point<T>& a,
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool GeometryUtil<T>::lineIntersectPolygon(const Point<T>& p1, const Point<T>& p2, const Polygon<T>& polygon) {
|
||||
bool lineIntersectPolygon(const Point<T>& p1, const Point<T>& p2, const Polygon<T>& polygon) {
|
||||
for (auto ring : polygon) {
|
||||
auto length = ring.size();
|
||||
// loop through every edge of the ring
|
||||
@@ -73,7 +88,7 @@ bool GeometryUtil<T>::lineIntersectPolygon(const Point<T>& p1, const Point<T>& p
|
||||
|
||||
// ray casting algorithm for detecting if point is in polygon
|
||||
template <typename T>
|
||||
bool GeometryUtil<T>::pointWithinPolygon(const Point<T>& point, const Polygon<T>& polygon, bool trueOnBoundary) {
|
||||
bool pointWithinPolygon(const Point<T>& point, const Polygon<T>& polygon, bool trueOnBoundary) {
|
||||
bool within = false;
|
||||
for (const auto& ring : polygon) {
|
||||
const auto length = ring.size();
|
||||
@@ -89,7 +104,7 @@ bool GeometryUtil<T>::pointWithinPolygon(const Point<T>& point, const Polygon<T>
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool GeometryUtil<T>::pointWithinPolygons(const Point<T>& point, const MultiPolygon<T>& polygons, bool trueOnBoundary) {
|
||||
bool pointWithinPolygons(const Point<T>& point, const MultiPolygon<T>& polygons, bool trueOnBoundary) {
|
||||
for (const auto& polygon : polygons) {
|
||||
if (pointWithinPolygon(point, polygon, trueOnBoundary)) return true;
|
||||
}
|
||||
@@ -97,7 +112,7 @@ bool GeometryUtil<T>::pointWithinPolygons(const Point<T>& point, const MultiPoly
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool GeometryUtil<T>::lineStringWithinPolygon(const LineString<T>& line, const Polygon<T>& polygon) {
|
||||
bool lineStringWithinPolygon(const LineString<T>& line, const Polygon<T>& polygon) {
|
||||
const auto length = line.size();
|
||||
// First, check if geometry points of line segments are all inside polygon
|
||||
for (std::size_t i = 0; i < length; ++i) {
|
||||
@@ -116,14 +131,35 @@ bool GeometryUtil<T>::lineStringWithinPolygon(const LineString<T>& line, const P
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool GeometryUtil<T>::lineStringWithinPolygons(const LineString<T>& line, const MultiPolygon<T>& polygons) {
|
||||
bool lineStringWithinPolygons(const LineString<T>& line, const MultiPolygon<T>& polygons) {
|
||||
for (const auto& polygon : polygons) {
|
||||
if (lineStringWithinPolygon(line, polygon)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template struct GeometryUtil<double>;
|
||||
template struct GeometryUtil<int64_t>;
|
||||
template void updateBBox(GeometryBBox<int64_t>& bbox, const Point<int64_t>& p);
|
||||
template bool boxWithinBox(const GeometryBBox<int64_t>& bbox1, const GeometryBBox<int64_t>& bbox2);
|
||||
template bool segmentIntersectSegment(const Point<int64_t>& a,
|
||||
const Point<int64_t>& b,
|
||||
const Point<int64_t>& c,
|
||||
const Point<int64_t>& d);
|
||||
template bool rayIntersect(const Point<int64_t>& p, const Point<int64_t>& p1, const Point<int64_t>& p2);
|
||||
template bool pointOnBoundary(const Point<int64_t>& p, const Point<int64_t>& p1, const Point<int64_t>& p2);
|
||||
template bool lineIntersectPolygon(const Point<int64_t>& p1, const Point<int64_t>& p2, const Polygon<int64_t>& polygon);
|
||||
template bool pointWithinPolygon(const Point<int64_t>& point,
|
||||
const Polygon<int64_t>& polygon,
|
||||
bool trueOnBoundary = false);
|
||||
template bool pointWithinPolygons(const Point<int64_t>& point,
|
||||
const MultiPolygon<int64_t>& polygons,
|
||||
bool trueOnBoundary = false);
|
||||
template bool lineStringWithinPolygon(const LineString<int64_t>& line, const Polygon<int64_t>& polygon);
|
||||
template bool lineStringWithinPolygons(const LineString<int64_t>& line, const MultiPolygon<int64_t>& polygons);
|
||||
|
||||
template void updateBBox(GeometryBBox<double>& bbox, const Point<double>& p);
|
||||
template bool segmentIntersectSegment(const Point<double>& a,
|
||||
const Point<double>& b,
|
||||
const Point<double>& c,
|
||||
const Point<double>& d);
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -1,25 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <mbgl/util/geometry.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
|
||||
// contains minX, minY, maxX, maxY
|
||||
template <typename T>
|
||||
struct GeometryUtil {
|
||||
using type = T;
|
||||
static bool segmentIntersectSegment(const Point<type>& a,
|
||||
const Point<type>& b,
|
||||
const Point<type>& c,
|
||||
const Point<type>& d);
|
||||
static bool rayIntersect(const Point<type>& p, const Point<type>& p1, const Point<type>& p2);
|
||||
static bool pointOnBoundary(const Point<type>& p, const Point<type>& p1, const Point<type>& p2);
|
||||
static bool lineIntersectPolygon(const Point<type>& p1, const Point<type>& p2, const Polygon<type>& polygon);
|
||||
static bool pointWithinPolygon(const Point<type>& point, const Polygon<type>& polygon, bool trueOnBoundary = false);
|
||||
static bool pointWithinPolygons(const Point<type>& point,
|
||||
const MultiPolygon<type>& polygons,
|
||||
bool trueOnBoundary = false);
|
||||
static bool lineStringWithinPolygon(const LineString<type>& line, const Polygon<type>& polygon);
|
||||
static bool lineStringWithinPolygons(const LineString<type>& line, const MultiPolygon<type>& polygons);
|
||||
};
|
||||
using GeometryBBox = std::array<T, 4>;
|
||||
const GeometryBBox<int64_t> DefaultWithinBBox = std::array<int64_t, 4>{std::numeric_limits<int64_t>::max(),
|
||||
std::numeric_limits<int64_t>::max(),
|
||||
std::numeric_limits<int64_t>::min(),
|
||||
std::numeric_limits<int64_t>::min()};
|
||||
|
||||
const GeometryBBox<double> DefaultDistanceBBox = std::array<double, 4>{std::numeric_limits<double>::infinity(),
|
||||
std::numeric_limits<double>::infinity(),
|
||||
-std::numeric_limits<double>::infinity(),
|
||||
-std::numeric_limits<double>::infinity()};
|
||||
|
||||
template <typename T>
|
||||
void updateBBox(GeometryBBox<T>& bbox, const Point<T>& p);
|
||||
|
||||
// check if bbox1 is within bbox2
|
||||
template <typename T>
|
||||
bool boxWithinBox(const GeometryBBox<T>& bbox1, const GeometryBBox<T>& bbox2);
|
||||
|
||||
template <typename T>
|
||||
bool segmentIntersectSegment(const Point<T>& a, const Point<T>& b, const Point<T>& c, const Point<T>& d);
|
||||
|
||||
template <typename T>
|
||||
bool rayIntersect(const Point<T>& p, const Point<T>& p1, const Point<T>& p2);
|
||||
|
||||
template <typename T>
|
||||
bool pointOnBoundary(const Point<T>& p, const Point<T>& p1, const Point<T>& p2);
|
||||
|
||||
template <typename T>
|
||||
bool lineIntersectPolygon(const Point<T>& p1, const Point<T>& p2, const Polygon<T>& polygon);
|
||||
|
||||
template <typename T>
|
||||
bool pointWithinPolygon(const Point<T>& point, const Polygon<T>& polygon, bool trueOnBoundary = false);
|
||||
|
||||
template <typename T>
|
||||
bool pointWithinPolygons(const Point<T>& point, const MultiPolygon<T>& polygons, bool trueOnBoundary = false);
|
||||
|
||||
template <typename T>
|
||||
bool lineStringWithinPolygon(const LineString<T>& line, const Polygon<T>& polygon);
|
||||
|
||||
template <typename T>
|
||||
bool lineStringWithinPolygons(const LineString<T>& line, const MultiPolygon<T>& polygons);
|
||||
|
||||
} // namespace mbgl
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
["distance", {
|
||||
"type": "LineString",
|
||||
"coordinates": [[0, 0], [0, 5], [5, 5], [5, 0]]
|
||||
}, "Meters"
|
||||
}, "meters"
|
||||
]
|
||||
@@ -1,5 +1,5 @@
|
||||
["distance", {
|
||||
"type": "LineString",
|
||||
"coordinates": [[0, 0], [0, 6], [5, 5], [5, 0]]
|
||||
}, "Meters"
|
||||
}, "meters"
|
||||
]
|
||||
@@ -540,7 +540,7 @@ TEST(PropertyExpression, DistanceExpression) {
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
// Default Unit: Meters
|
||||
// Default Unit: meters
|
||||
ss << std::string(R"(["distance", )") << pointGeoSource << std::string(R"( ])");
|
||||
auto expression = createExpression(ss.str().c_str());
|
||||
ASSERT_TRUE(expression);
|
||||
@@ -550,9 +550,9 @@ TEST(PropertyExpression, DistanceExpression) {
|
||||
propExpr.evaluate(EvaluationContext(&pointFeature).withCanonicalTileID(&canonicalTileID), invalidResult);
|
||||
EXPECT_NEAR(491.307, evaluatedResult, 0.01);
|
||||
|
||||
// Unit: Meters
|
||||
// Unit: meters
|
||||
ss.str(std::string());
|
||||
ss << std::string(R"(["distance", )") << pointGeoSource << std::string(R"(, "Meters" ])");
|
||||
ss << std::string(R"(["distance", )") << pointGeoSource << std::string(R"(, "meters" ])");
|
||||
expression = createExpression(ss.str().c_str());
|
||||
ASSERT_TRUE(expression);
|
||||
propExpr = std::move(expression);
|
||||
@@ -561,9 +561,9 @@ TEST(PropertyExpression, DistanceExpression) {
|
||||
propExpr.evaluate(EvaluationContext(&pointFeature).withCanonicalTileID(&canonicalTileID), invalidResult);
|
||||
EXPECT_NEAR(491.307, evaluatedResult, 0.01);
|
||||
|
||||
// Unit: Kilometers
|
||||
// Unit: kilometers
|
||||
ss.str(std::string());
|
||||
ss << std::string(R"(["distance", )") << pointGeoSource << std::string(R"(, "Kilometers" ])");
|
||||
ss << std::string(R"(["distance", )") << pointGeoSource << std::string(R"(, "kilometers" ])");
|
||||
expression = createExpression(ss.str().c_str());
|
||||
ASSERT_TRUE(expression);
|
||||
propExpr = std::move(expression);
|
||||
@@ -572,9 +572,9 @@ TEST(PropertyExpression, DistanceExpression) {
|
||||
propExpr.evaluate(EvaluationContext(&pointFeature).withCanonicalTileID(&canonicalTileID), invalidResult);
|
||||
EXPECT_NEAR(0.491307, evaluatedResult, 0.00001);
|
||||
|
||||
// Unit: Miles
|
||||
// Unit: miles
|
||||
ss.str(std::string());
|
||||
ss << std::string(R"(["distance", )") << pointGeoSource << std::string(R"(, "Miles" ])");
|
||||
ss << std::string(R"(["distance", )") << pointGeoSource << std::string(R"(, "miles" ])");
|
||||
expression = createExpression(ss.str().c_str());
|
||||
ASSERT_TRUE(expression);
|
||||
propExpr = std::move(expression);
|
||||
@@ -583,9 +583,9 @@ TEST(PropertyExpression, DistanceExpression) {
|
||||
propExpr.evaluate(EvaluationContext(&pointFeature).withCanonicalTileID(&canonicalTileID), invalidResult);
|
||||
EXPECT_NEAR(0.305284, evaluatedResult, 0.00001);
|
||||
|
||||
// Unit: NauticalMiles
|
||||
// Unit: nauticalmiles
|
||||
ss.str(std::string());
|
||||
ss << std::string(R"(["distance", )") << pointGeoSource << std::string(R"(, "NauticalMiles" ])");
|
||||
ss << std::string(R"(["distance", )") << pointGeoSource << std::string(R"(, "nauticalmiles" ])");
|
||||
expression = createExpression(ss.str().c_str());
|
||||
ASSERT_TRUE(expression);
|
||||
propExpr = std::move(expression);
|
||||
@@ -594,9 +594,9 @@ TEST(PropertyExpression, DistanceExpression) {
|
||||
propExpr.evaluate(EvaluationContext(&pointFeature).withCanonicalTileID(&canonicalTileID), invalidResult);
|
||||
EXPECT_NEAR(0.265284, evaluatedResult, 0.00001);
|
||||
|
||||
// Unit: Yards
|
||||
// Unit: yards
|
||||
ss.str(std::string());
|
||||
ss << std::string(R"(["distance", )") << pointGeoSource << std::string(R"(, "Yards" ])");
|
||||
ss << std::string(R"(["distance", )") << pointGeoSource << std::string(R"(, "yards" ])");
|
||||
expression = createExpression(ss.str().c_str());
|
||||
ASSERT_TRUE(expression);
|
||||
propExpr = std::move(expression);
|
||||
@@ -605,9 +605,9 @@ TEST(PropertyExpression, DistanceExpression) {
|
||||
propExpr.evaluate(EvaluationContext(&pointFeature).withCanonicalTileID(&canonicalTileID), invalidResult);
|
||||
EXPECT_NEAR(537.299, evaluatedResult, 0.01);
|
||||
|
||||
// Unit: Feet
|
||||
// Unit: feet
|
||||
ss.str(std::string());
|
||||
ss << std::string(R"(["distance", )") << pointGeoSource << std::string(R"(, "Feet" ])");
|
||||
ss << std::string(R"(["distance", )") << pointGeoSource << std::string(R"(, "feet" ])");
|
||||
expression = createExpression(ss.str().c_str());
|
||||
ASSERT_TRUE(expression);
|
||||
propExpr = std::move(expression);
|
||||
@@ -616,9 +616,9 @@ TEST(PropertyExpression, DistanceExpression) {
|
||||
propExpr.evaluate(EvaluationContext(&pointFeature).withCanonicalTileID(&canonicalTileID), invalidResult);
|
||||
EXPECT_NEAR(1611.898, evaluatedResult, 0.01);
|
||||
|
||||
// Unit: Inches
|
||||
// Unit: inches
|
||||
ss.str(std::string());
|
||||
ss << std::string(R"(["distance", )") << pointGeoSource << std::string(R"(, "Inches" ])");
|
||||
ss << std::string(R"(["distance", )") << pointGeoSource << std::string(R"(, "inches" ])");
|
||||
expression = createExpression(ss.str().c_str());
|
||||
ASSERT_TRUE(expression);
|
||||
propExpr = std::move(expression);
|
||||
|
||||
Reference in New Issue
Block a user