summaryrefslogtreecommitdiff
path: root/src/mongo/db/geo/shapes.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/geo/shapes.cpp')
-rw-r--r--src/mongo/db/geo/shapes.cpp1290
1 files changed, 638 insertions, 652 deletions
diff --git a/src/mongo/db/geo/shapes.cpp b/src/mongo/db/geo/shapes.cpp
index d87cb9334ce..fa6018877bb 100644
--- a/src/mongo/db/geo/shapes.cpp
+++ b/src/mongo/db/geo/shapes.cpp
@@ -39,767 +39,753 @@ namespace mongo {
////////////// Point
- Point::Point() : x(0), y(0) { }
+Point::Point() : x(0), y(0) {}
- Point::Point(double x, double y) : x(x), y(y) { }
+Point::Point(double x, double y) : x(x), y(y) {}
- Point::Point(const BSONElement& e) {
- BSONObjIterator i(e.Obj());
- x = i.next().number();
- y = i.next().number();
- }
+Point::Point(const BSONElement& e) {
+ BSONObjIterator i(e.Obj());
+ x = i.next().number();
+ y = i.next().number();
+}
- Point::Point(const BSONObj& o) {
- BSONObjIterator i(o);
- x = i.next().number();
- y = i.next().number();
- }
+Point::Point(const BSONObj& o) {
+ BSONObjIterator i(o);
+ x = i.next().number();
+ y = i.next().number();
+}
- string Point::toString() const {
- StringBuilder buf;
- buf << "(" << x << "," << y << ")";
- return buf.str();
- }
+string Point::toString() const {
+ StringBuilder buf;
+ buf << "(" << x << "," << y << ")";
+ return buf.str();
+}
////////////// Circle
- Circle::Circle() {}
- Circle::Circle(double radius, Point center) : radius(radius), center(center) {}
+Circle::Circle() {}
+Circle::Circle(double radius, Point center) : radius(radius), center(center) {}
////////////// Box
- Box::Box() {}
+Box::Box() {}
- Box::Box(double x, double y, double size) :
- _min(x, y), _max(x + size, y + size) {
- }
+Box::Box(double x, double y, double size) : _min(x, y), _max(x + size, y + size) {}
- Box::Box(const Point& ptA, const Point& ptB) {
- init(ptA, ptB);
- }
+Box::Box(const Point& ptA, const Point& ptB) {
+ init(ptA, ptB);
+}
- void Box::init(const Point& ptA, const Point& ptB) {
- _min.x = min(ptA.x, ptB.x);
- _min.y = min(ptA.y, ptB.y);
- _max.x = max(ptA.x, ptB.x);
- _max.y = max(ptA.y, ptB.y);
- }
+void Box::init(const Point& ptA, const Point& ptB) {
+ _min.x = min(ptA.x, ptB.x);
+ _min.y = min(ptA.y, ptB.y);
+ _max.x = max(ptA.x, ptB.x);
+ _max.y = max(ptA.y, ptB.y);
+}
- void Box::init(const Box& other) {
- init(other._min, other._max);
- }
+void Box::init(const Box& other) {
+ init(other._min, other._max);
+}
- BSONArray Box::toBSON() const {
- return BSON_ARRAY(BSON_ARRAY(_min.x << _min.y) << BSON_ARRAY(_max.x << _max.y));
- }
+BSONArray Box::toBSON() const {
+ return BSON_ARRAY(BSON_ARRAY(_min.x << _min.y) << BSON_ARRAY(_max.x << _max.y));
+}
- string Box::toString() const {
- StringBuilder buf;
- buf << _min.toString() << " -->> " << _max.toString();
- return buf.str();
- }
+string Box::toString() const {
+ StringBuilder buf;
+ buf << _min.toString() << " -->> " << _max.toString();
+ return buf.str();
+}
- bool Box::between(double min, double max, double val, double fudge) const {
- return val + fudge >= min && val <= max + fudge;
- }
+bool Box::between(double min, double max, double val, double fudge) const {
+ return val + fudge >= min && val <= max + fudge;
+}
- bool Box::onBoundary(double bound, double val, double fudge) const {
- return (val >= bound - fudge && val <= bound + fudge);
- }
+bool Box::onBoundary(double bound, double val, double fudge) const {
+ return (val >= bound - fudge && val <= bound + fudge);
+}
- bool Box::mid(double amin, double amax,
- double bmin, double bmax, bool min, double* res) const {
- verify(amin <= amax);
- verify(bmin <= bmax);
+bool Box::mid(double amin, double amax, double bmin, double bmax, bool min, double* res) const {
+ verify(amin <= amax);
+ verify(bmin <= bmax);
- if (amin < bmin) {
- if (amax < bmin)
- return false;
- *res = min ? bmin : amax;
- return true;
- }
- if (amin > bmax)
+ if (amin < bmin) {
+ if (amax < bmin)
return false;
- *res = min ? amin : bmax;
+ *res = min ? bmin : amax;
return true;
}
-
- bool Box::intersects(const Box& other) const {
-
- bool intersectX = between(_min.x, _max.x, other._min.x) // contain part of other range
- || between(_min.x, _max.x, other._max.x) // contain part of other range
- || between(other._min.x, other._max.x, _min.x); // other range contains us
-
- bool intersectY = between(_min.y, _max.y, other._min.y)
- || between(_min.y, _max.y, other._max.y)
- || between(other._min.y, other._max.y, _min.y);
-
- return intersectX && intersectY;
- }
-
- double Box::legacyIntersectFraction(const Box& other) const {
-
- Point boundMin(0,0);
- Point boundMax(0,0);
-
- if (!mid(_min.x, _max.x, other._min.x, other._max.x, true, &boundMin.x) ||
- !mid(_min.x, _max.x, other._min.x, other._max.x, false, &boundMax.x) ||
- !mid(_min.y, _max.y, other._min.y, other._max.y, true, &boundMin.y) ||
- !mid(_min.y, _max.y, other._min.y, other._max.y, false, &boundMax.y))
- return 0;
-
- Box intersection(boundMin, boundMax);
- return intersection.area() / area();
- }
-
- double Box::area() const {
- return (_max.x - _min.x) * (_max.y - _min.y);
- }
-
- double Box::maxDim() const {
- return max(_max.x - _min.x, _max.y - _min.y);
- }
-
- Point Box::center() const {
- return Point((_min.x + _max.x) / 2,
- (_min.y + _max.y) / 2);
- }
-
- void Box::truncate(double min, double max) {
- if (_min.x < min) _min.x = min;
- if (_min.y < min) _min.y = min;
- if (_max.x > max) _max.x = max;
- if (_max.y > max) _max.y = max;
- }
-
- void Box::fudge(double error) {
- _min.x -= error;
- _min.y -= error;
- _max.x += error;
- _max.y += error;
- }
-
- void Box::expandToInclude(const Point& pt) {
- _min.x = min(_min.x, pt.x);
- _min.y = min(_min.y, pt.y);
- _max.x = max(_max.x, pt.x);
- _max.y = max(_max.y, pt.y);
- }
-
- bool Box::onBoundary(Point p, double fudge) const {
- return onBoundary(_min.x, p.x, fudge) ||
- onBoundary(_max.x, p.x, fudge) ||
- onBoundary(_min.y, p.y, fudge) ||
- onBoundary(_max.y, p.y, fudge);
- }
-
- bool Box::inside(Point p, double fudge) const {
- bool res = inside(p.x, p.y, fudge);
- return res;
- }
-
- bool Box::inside(double x, double y, double fudge) const {
- return between(_min.x, _max.x , x, fudge) &&
- between(_min.y, _max.y , y, fudge);
- }
-
- bool Box::contains(const Box& other, double fudge) const {
- return inside(other._min, fudge) && inside(other._max, fudge);
- }
+ if (amin > bmax)
+ return false;
+ *res = min ? amin : bmax;
+ return true;
+}
+
+bool Box::intersects(const Box& other) const {
+ bool intersectX = between(_min.x, _max.x, other._min.x) // contain part of other range
+ || between(_min.x, _max.x, other._max.x) // contain part of other range
+ || between(other._min.x, other._max.x, _min.x); // other range contains us
+
+ bool intersectY = between(_min.y, _max.y, other._min.y) ||
+ between(_min.y, _max.y, other._max.y) || between(other._min.y, other._max.y, _min.y);
+
+ return intersectX && intersectY;
+}
+
+double Box::legacyIntersectFraction(const Box& other) const {
+ Point boundMin(0, 0);
+ Point boundMax(0, 0);
+
+ if (!mid(_min.x, _max.x, other._min.x, other._max.x, true, &boundMin.x) ||
+ !mid(_min.x, _max.x, other._min.x, other._max.x, false, &boundMax.x) ||
+ !mid(_min.y, _max.y, other._min.y, other._max.y, true, &boundMin.y) ||
+ !mid(_min.y, _max.y, other._min.y, other._max.y, false, &boundMax.y))
+ return 0;
+
+ Box intersection(boundMin, boundMax);
+ return intersection.area() / area();
+}
+
+double Box::area() const {
+ return (_max.x - _min.x) * (_max.y - _min.y);
+}
+
+double Box::maxDim() const {
+ return max(_max.x - _min.x, _max.y - _min.y);
+}
+
+Point Box::center() const {
+ return Point((_min.x + _max.x) / 2, (_min.y + _max.y) / 2);
+}
+
+void Box::truncate(double min, double max) {
+ if (_min.x < min)
+ _min.x = min;
+ if (_min.y < min)
+ _min.y = min;
+ if (_max.x > max)
+ _max.x = max;
+ if (_max.y > max)
+ _max.y = max;
+}
+
+void Box::fudge(double error) {
+ _min.x -= error;
+ _min.y -= error;
+ _max.x += error;
+ _max.y += error;
+}
+
+void Box::expandToInclude(const Point& pt) {
+ _min.x = min(_min.x, pt.x);
+ _min.y = min(_min.y, pt.y);
+ _max.x = max(_max.x, pt.x);
+ _max.y = max(_max.y, pt.y);
+}
+
+bool Box::onBoundary(Point p, double fudge) const {
+ return onBoundary(_min.x, p.x, fudge) || onBoundary(_max.x, p.x, fudge) ||
+ onBoundary(_min.y, p.y, fudge) || onBoundary(_max.y, p.y, fudge);
+}
+
+bool Box::inside(Point p, double fudge) const {
+ bool res = inside(p.x, p.y, fudge);
+ return res;
+}
+
+bool Box::inside(double x, double y, double fudge) const {
+ return between(_min.x, _max.x, x, fudge) && between(_min.y, _max.y, y, fudge);
+}
+
+bool Box::contains(const Box& other, double fudge) const {
+ return inside(other._min, fudge) && inside(other._max, fudge);
+}
////////////// Polygon
- Polygon::Polygon() {
- }
-
- Polygon::Polygon(const vector<Point>& points) {
- init(points);
- }
-
- void Polygon::init(const vector<Point>& points) {
-
- _points.clear();
- _bounds.reset();
- _centroid.reset();
-
- _points.insert(_points.begin(), points.begin(), points.end());
- }
-
- void Polygon::init(const Polygon& other) {
- init(other._points);
- }
-
- int Polygon::size(void) const { return _points.size(); }
+Polygon::Polygon() {}
+
+Polygon::Polygon(const vector<Point>& points) {
+ init(points);
+}
+
+void Polygon::init(const vector<Point>& points) {
+ _points.clear();
+ _bounds.reset();
+ _centroid.reset();
+
+ _points.insert(_points.begin(), points.begin(), points.end());
+}
+
+void Polygon::init(const Polygon& other) {
+ init(other._points);
+}
+
+int Polygon::size(void) const {
+ return _points.size();
+}
+
+bool Polygon::contains(const Point& p) const {
+ return contains(p, 0) > 0;
+}
+
+/*
+ * Return values:
+ * -1 if no intersection
+ * 0 if maybe an intersection (using fudge)
+ * 1 if there is an intersection
+ *
+ * A ray casting intersection method is used.
+ */
+int Polygon::contains(const Point& p, double fudge) const {
+ Box fudgeBox(Point(p.x - fudge, p.y - fudge), Point(p.x + fudge, p.y + fudge));
+
+ int counter = 0;
+ Point p1 = _points[0];
+ for (int i = 1; i <= size(); i++) {
+ // XXX: why is there a mod here?
+ Point p2 = _points[i % size()];
+
+ // We need to check whether or not this segment intersects our error box
+ if (fudge > 0 &&
+ // Points not too far below box
+ fudgeBox._min.y <= std::max(p1.y, p2.y) &&
+ // Points not too far above box
+ fudgeBox._max.y >= std::min(p1.y, p2.y) &&
+ // Points not too far to left of box
+ fudgeBox._min.x <= std::max(p1.x, p2.x) &&
+ // Points not too far to right of box
+ fudgeBox._max.x >= std::min(p1.x, p2.x)) {
+ // If our box contains one or more of these points, we need to do an exact
+ // check.
+ if (fudgeBox.inside(p1)) {
+ return 0;
+ }
+ if (fudgeBox.inside(p2)) {
+ return 0;
+ }
- bool Polygon::contains(const Point& p) const { return contains(p, 0) > 0; }
+ // Do intersection check for vertical sides
+ if (p1.y != p2.y) {
+ double invSlope = (p2.x - p1.x) / (p2.y - p1.y);
- /*
- * Return values:
- * -1 if no intersection
- * 0 if maybe an intersection (using fudge)
- * 1 if there is an intersection
- *
- * A ray casting intersection method is used.
- */
- int Polygon::contains(const Point &p, double fudge) const {
- Box fudgeBox(Point(p.x - fudge, p.y - fudge), Point(p.x + fudge, p.y + fudge));
-
- int counter = 0;
- Point p1 = _points[0];
- for (int i = 1; i <= size(); i++) {
- // XXX: why is there a mod here?
- Point p2 = _points[i % size()];
-
- // We need to check whether or not this segment intersects our error box
- if (fudge > 0 &&
- // Points not too far below box
- fudgeBox._min.y <= std::max(p1.y, p2.y) &&
- // Points not too far above box
- fudgeBox._max.y >= std::min(p1.y, p2.y) &&
- // Points not too far to left of box
- fudgeBox._min.x <= std::max(p1.x, p2.x) &&
- // Points not too far to right of box
- fudgeBox._max.x >= std::min(p1.x, p2.x)) {
-
-
- // If our box contains one or more of these points, we need to do an exact
- // check.
- if (fudgeBox.inside(p1)) {
+ double xintersT = (fudgeBox._max.y - p1.y) * invSlope + p1.x;
+ if (fudgeBox._min.x <= xintersT && fudgeBox._max.x >= xintersT) {
return 0;
}
- if (fudgeBox.inside(p2)) {
+
+ double xintersB = (fudgeBox._min.y - p1.y) * invSlope + p1.x;
+ if (fudgeBox._min.x <= xintersB && fudgeBox._max.x >= xintersB) {
return 0;
}
+ }
- // Do intersection check for vertical sides
- if (p1.y != p2.y) {
- double invSlope = (p2.x - p1.x) / (p2.y - p1.y);
+ // Do intersection check for horizontal sides
+ if (p1.x != p2.x) {
+ double slope = (p2.y - p1.y) / (p2.x - p1.x);
- double xintersT = (fudgeBox._max.y - p1.y) * invSlope + p1.x;
- if (fudgeBox._min.x <= xintersT && fudgeBox._max.x >= xintersT) {
- return 0;
- }
-
- double xintersB = (fudgeBox._min.y - p1.y) * invSlope + p1.x;
- if (fudgeBox._min.x <= xintersB && fudgeBox._max.x >= xintersB) {
- return 0;
- }
+ double yintersR = (p1.x - fudgeBox._max.x) * slope + p1.y;
+ if (fudgeBox._min.y <= yintersR && fudgeBox._max.y >= yintersR) {
+ return 0;
}
- // Do intersection check for horizontal sides
- if (p1.x != p2.x) {
- double slope = (p2.y - p1.y) / (p2.x - p1.x);
-
- double yintersR = (p1.x - fudgeBox._max.x) * slope + p1.y;
- if (fudgeBox._min.y <= yintersR && fudgeBox._max.y >= yintersR) {
- return 0;
- }
-
- double yintersL = (p1.x - fudgeBox._min.x) * slope + p1.y;
- if (fudgeBox._min.y <= yintersL && fudgeBox._max.y >= yintersL) {
- return 0;
- }
- }
- } else if (fudge == 0){
- // If this is an exact vertex, we won't intersect, so check this
- if (p.y == p1.y && p.x == p1.x) return 1;
- else if (p.y == p2.y && p.x == p2.x) return 1;
-
- // If this is a horizontal line we won't intersect, so check this
- if (p1.y == p2.y && p.y == p1.y){
- // Check that the x-coord lies in the line
- if (p.x >= std::min(p1.x, p2.x) && p.x <= std::max(p1.x, p2.x))
- return 1;
+ double yintersL = (p1.x - fudgeBox._min.x) * slope + p1.y;
+ if (fudgeBox._min.y <= yintersL && fudgeBox._max.y >= yintersL) {
+ return 0;
}
}
+ } else if (fudge == 0) {
+ // If this is an exact vertex, we won't intersect, so check this
+ if (p.y == p1.y && p.x == p1.x)
+ return 1;
+ else if (p.y == p2.y && p.x == p2.x)
+ return 1;
+
+ // If this is a horizontal line we won't intersect, so check this
+ if (p1.y == p2.y && p.y == p1.y) {
+ // Check that the x-coord lies in the line
+ if (p.x >= std::min(p1.x, p2.x) && p.x <= std::max(p1.x, p2.x))
+ return 1;
+ }
+ }
- // Normal intersection test.
- // TODO: Invert these for clearer logic?
- if (p.y > std::min(p1.y, p2.y)) {
- if (p.y <= std::max(p1.y, p2.y)) {
- if (p.x <= std::max(p1.x, p2.x)) {
- if (p1.y != p2.y) {
- double xinters = (p.y-p1.y)*(p2.x-p1.x)/(p2.y-p1.y)+p1.x;
- // Special case of point on vertical line
- if (p1.x == p2.x && p.x == p1.x){
-
- // Need special case for the vertical edges, for example:
- // 1) \e pe/----->
- // vs.
- // 2) \ep---e/----->
- //
- // if we count exact as intersection, then 1 is in but 2 is out
- // if we count exact as no-int then 1 is out but 2 is in.
-
- return 1;
- } else if (p1.x == p2.x || p.x <= xinters) {
- counter++;
- }
+ // Normal intersection test.
+ // TODO: Invert these for clearer logic?
+ if (p.y > std::min(p1.y, p2.y)) {
+ if (p.y <= std::max(p1.y, p2.y)) {
+ if (p.x <= std::max(p1.x, p2.x)) {
+ if (p1.y != p2.y) {
+ double xinters = (p.y - p1.y) * (p2.x - p1.x) / (p2.y - p1.y) + p1.x;
+ // Special case of point on vertical line
+ if (p1.x == p2.x && p.x == p1.x) {
+ // Need special case for the vertical edges, for example:
+ // 1) \e pe/----->
+ // vs.
+ // 2) \ep---e/----->
+ //
+ // if we count exact as intersection, then 1 is in but 2 is out
+ // if we count exact as no-int then 1 is out but 2 is in.
+
+ return 1;
+ } else if (p1.x == p2.x || p.x <= xinters) {
+ counter++;
}
}
}
}
-
- p1 = p2;
- }
-
- if (counter % 2 == 0) {
- return -1;
- } else {
- return 1;
- }
- }
-
- const Point& Polygon::centroid() const {
-
- if (_centroid) {
- return *_centroid;
- }
-
- _centroid.reset(new Point());
-
- double signedArea = 0.0;
- double area = 0.0; // Partial signed area
-
- /// For all vertices except last
- int i = 0;
- for (i = 0; i < size() - 1; ++i) {
- area = _points[i].x * _points[i+1].y - _points[i+1].x * _points[i].y ;
- signedArea += area;
- _centroid->x += (_points[i].x + _points[i+1].x) * area;
- _centroid->y += (_points[i].y + _points[i+1].y) * area;
}
- // Do last vertex
- area = _points[i].x * _points[0].y - _points[0].x * _points[i].y;
- _centroid->x += (_points[i].x + _points[0].x) * area;
- _centroid->y += (_points[i].y + _points[0].y) * area;
- signedArea += area;
- signedArea *= 0.5;
- _centroid->x /= (6 * signedArea);
- _centroid->y /= (6 * signedArea);
-
- return *_centroid;
+ p1 = p2;
}
- const Box& Polygon::bounds() const {
-
- if (_bounds) {
- return *_bounds;
- }
-
- _bounds.reset(new Box(_points[0], _points[0]));
-
- for (int i = 1; i < size(); i++) {
- _bounds->expandToInclude(_points[i]);
- }
-
- return *_bounds;
+ if (counter % 2 == 0) {
+ return -1;
+ } else {
+ return 1;
}
+}
- R2Annulus::R2Annulus() :
- _inner(0.0), _outer(0.0) {
+const Point& Polygon::centroid() const {
+ if (_centroid) {
+ return *_centroid;
}
- R2Annulus::R2Annulus(const Point& center, double inner, double outer) :
- _center(center), _inner(inner), _outer(outer) {
- }
+ _centroid.reset(new Point());
- const Point& R2Annulus::center() const {
- return _center;
- }
-
- double R2Annulus::getInner() const {
- return _inner;
- }
+ double signedArea = 0.0;
+ double area = 0.0; // Partial signed area
- double R2Annulus::getOuter() const {
- return _outer;
+ /// For all vertices except last
+ int i = 0;
+ for (i = 0; i < size() - 1; ++i) {
+ area = _points[i].x * _points[i + 1].y - _points[i + 1].x * _points[i].y;
+ signedArea += area;
+ _centroid->x += (_points[i].x + _points[i + 1].x) * area;
+ _centroid->y += (_points[i].y + _points[i + 1].y) * area;
}
- bool R2Annulus::contains(const Point& point) const {
+ // Do last vertex
+ area = _points[i].x * _points[0].y - _points[0].x * _points[i].y;
+ _centroid->x += (_points[i].x + _points[0].x) * area;
+ _centroid->y += (_points[i].y + _points[0].y) * area;
+ signedArea += area;
+ signedArea *= 0.5;
+ _centroid->x /= (6 * signedArea);
+ _centroid->y /= (6 * signedArea);
- // See if we're inside the inner radius
- if (distanceCompare(point, _center, _inner) < 0) {
- return false;
- }
+ return *_centroid;
+}
- // See if we're outside the outer radius
- if (distanceCompare(point, _center, _outer) > 0) {
- return false;
- }
-
- return true;
+const Box& Polygon::bounds() const {
+ if (_bounds) {
+ return *_bounds;
}
- Box R2Annulus::getR2Bounds() const {
- return Box(_center.x - _outer, _center.y - _outer, 2 * _outer); // Box(_min.x, _min.y, edgeLength)
- }
+ _bounds.reset(new Box(_points[0], _points[0]));
- bool R2Annulus::fastContains(const Box& other) const {
- return circleContainsBox(Circle(_outer, _center), other)
- && !circleInteriorIntersectsWithBox(Circle(_inner, _center), other);
+ for (int i = 1; i < size(); i++) {
+ _bounds->expandToInclude(_points[i]);
}
- bool R2Annulus::fastDisjoint(const Box& other) const {
- return !circleIntersectsWithBox(Circle(_outer, _center), other)
- || circleInteriorContainsBox(Circle(_inner, _center), other);
- }
+ return *_bounds;
+}
- string R2Annulus::toString() const {
- return str::stream() << "center: " << _center.toString() << " inner: " << _inner
- << " outer: " << _outer;
- }
+R2Annulus::R2Annulus() : _inner(0.0), _outer(0.0) {}
- /////// Other methods
+R2Annulus::R2Annulus(const Point& center, double inner, double outer)
+ : _center(center), _inner(inner), _outer(outer) {}
- double S2Distance::distanceRad(const S2Point& pointA, const S2Point& pointB) {
- S1Angle angle(pointA, pointB);
- return angle.radians();
- }
+const Point& R2Annulus::center() const {
+ return _center;
+}
- double S2Distance::minDistanceRad(const S2Point& point, const S2Polyline& line) {
- int tmp;
- S1Angle angle(point, line.Project(point, &tmp));
- return angle.radians();
- }
+double R2Annulus::getInner() const {
+ return _inner;
+}
- double S2Distance::minDistanceRad(const S2Point& point, const S2Polygon& polygon) {
- S1Angle angle(point, polygon.Project(point));
- return angle.radians();
- }
+double R2Annulus::getOuter() const {
+ return _outer;
+}
- double S2Distance::minDistanceRad(const S2Point& point, const S2Cap& cap) {
- S1Angle angleToCenter(point, cap.axis());
- return (angleToCenter - cap.angle()).radians();
+bool R2Annulus::contains(const Point& point) const {
+ // See if we're inside the inner radius
+ if (distanceCompare(point, _center, _inner) < 0) {
+ return false;
}
- /**
- * Distance method that compares x or y coords when other direction is zero,
- * avoids numerical error when distances are very close to radius but axis-aligned.
- *
- * An example of the problem is:
- * (52.0 - 51.9999) - 0.0001 = 3.31965e-15 and 52.0 - 51.9999 > 0.0001 in double arithmetic
- * but:
- * 51.9999 + 0.0001 <= 52.0
- *
- * This avoids some (but not all!) suprising results in $center queries where points are
- * (radius + center.x, center.y) or vice-versa.
- */
- bool distanceWithin(const Point &p1, const Point &p2, double radius) {
- return distanceCompare(p1, p2, radius) <= 0.0;
+ // See if we're outside the outer radius
+ if (distanceCompare(point, _center, _outer) > 0) {
+ return false;
}
- // Compare the distance between p1 and p2 with the radius.
- // Float-number comparison might be inaccurate.
- //
- // > 0: distance is greater than radius
- // = 0: distance equals radius
- // < 0: distance is less than radius
- double distanceCompare(const Point &p1, const Point &p2, double radius) {
- double a = p2.x - p1.x;
- double b = p2.y - p1.y;
-
- if (a == 0) {
- //
- // Note: For some, unknown reason, when a 32-bit g++ optimizes this call, the sum is
- // calculated imprecisely. We need to force the compiler to always evaluate it
- // correctly, hence the weirdness.
- //
- // On some 32-bit linux machines, removing the volatile keyword or calculating the sum
- // inline will make certain geo tests fail. Of course this check will force volatile
- // for all 32-bit systems, not just affected systems.
- if (sizeof(void*) <= 4){
- volatile double sum = p2.y > p1.y ? p1.y + radius : p2.y + radius;
- return p2.y > p1.y ? p2.y - sum : p1.y - sum;
- } else {
- // Original math, correct for most systems
- return p2.y > p1.y ? p2.y - (p1.y + radius) : p1.y - (p2.y + radius);
- }
- }
+ return true;
+}
- if (b == 0) {
- if (sizeof(void*) <= 4){
- volatile double sum = p2.x > p1.x ? p1.x + radius : p2.x + radius;
- return p2.x > p1.x ? p2.x - sum : p1.x - sum;
- } else {
- return p2.x > p1.x ? p2.x - (p1.x + radius) : p1.x - (p2.x + radius);
- }
- }
+Box R2Annulus::getR2Bounds() const {
+ return Box(
+ _center.x - _outer, _center.y - _outer, 2 * _outer); // Box(_min.x, _min.y, edgeLength)
+}
- return sqrt((a * a) + (b * b)) - radius;
- }
+bool R2Annulus::fastContains(const Box& other) const {
+ return circleContainsBox(Circle(_outer, _center), other) &&
+ !circleInteriorIntersectsWithBox(Circle(_inner, _center), other);
+}
- // note: multiply by earth radius for distance
- double spheredist_rad(const Point& p1, const Point& p2) {
- // this uses the n-vector formula: http://en.wikipedia.org/wiki/N-vector
- // If you try to match the code to the formula, note that I inline the cross-product.
-
- double sinx1(sin(p1.x)), cosx1(cos(p1.x));
- double siny1(sin(p1.y)), cosy1(cos(p1.y));
- double sinx2(sin(p2.x)), cosx2(cos(p2.x));
- double siny2(sin(p2.y)), cosy2(cos(p2.y));
-
- double cross_prod =
- (cosy1*cosx1 * cosy2*cosx2) +
- (cosy1*sinx1 * cosy2*sinx2) +
- (siny1 * siny2);
-
- if (cross_prod >= 1 || cross_prod <= -1) {
- // fun with floats
- verify(fabs(cross_prod)-1 < 1e-6);
- return cross_prod > 0 ? 0 : M_PI;
- }
+bool R2Annulus::fastDisjoint(const Box& other) const {
+ return !circleIntersectsWithBox(Circle(_outer, _center), other) ||
+ circleInteriorContainsBox(Circle(_inner, _center), other);
+}
- return acos(cross_prod);
- }
+string R2Annulus::toString() const {
+ return str::stream() << "center: " << _center.toString() << " inner: " << _inner
+ << " outer: " << _outer;
+}
- // @param p1 A point on the sphere where x and y are degrees.
- // @param p2 A point on the sphere where x and y are degrees.
- // @return The distance between the two points in RADIANS. Multiply by radius to get arc
- // length.
- double spheredist_deg(const Point& p1, const Point& p2) {
- return spheredist_rad(Point(deg2rad(p1.x), deg2rad(p1.y)),
- Point(deg2rad(p2.x), deg2rad(p2.y)));
- }
+/////// Other methods
- // Technically lat/long bounds, not really tied to earth radius.
- bool isValidLngLat(double lng, double lat) {
- return abs(lng) <= 180 && abs(lat) <= 90;
- }
+double S2Distance::distanceRad(const S2Point& pointA, const S2Point& pointB) {
+ S1Angle angle(pointA, pointB);
+ return angle.radians();
+}
- double distance(const Point& p1, const Point &p2) {
- double a = p1.x - p2.x;
- double b = p1.y - p2.y;
+double S2Distance::minDistanceRad(const S2Point& point, const S2Polyline& line) {
+ int tmp;
+ S1Angle angle(point, line.Project(point, &tmp));
+ return angle.radians();
+}
- // Avoid numerical error if possible...
- if (a == 0) return abs(b);
- if (b == 0) return abs(a);
+double S2Distance::minDistanceRad(const S2Point& point, const S2Polygon& polygon) {
+ S1Angle angle(point, polygon.Project(point));
+ return angle.radians();
+}
- return sqrt((a * a) + (b * b));
- }
+double S2Distance::minDistanceRad(const S2Point& point, const S2Cap& cap) {
+ S1Angle angleToCenter(point, cap.axis());
+ return (angleToCenter - cap.angle()).radians();
+}
- static inline Vector2_d toVector2(const Point& p) {
- return Vector2_d(p.x, p.y);
- }
-
- // Given a segment (A, B) and a segment (C, D), check whether they intersect.
- bool linesIntersect(const Point& pA, const Point& pB, const Point& pC, const Point& pD) {
- Vector2_d a = toVector2(pA);
- Vector2_d b = toVector2(pB);
- Vector2_d c = toVector2(pC);
- Vector2_d d = toVector2(pD);
-
- // The normal of line AB
- Vector2_d normalAB = (b - a).Ortho();
-
- // Dot products of AC and the normal of AB
- // = 0 : C is on the line AB
- // > 0 : C is on one side
- // < 0 : C is on the other side
- double dotProdNormalAB_AC = normalAB.DotProd(c - a);
- double dotProdNormalAB_AD = normalAB.DotProd(d - a);
-
- // C and D can not on the same side of line AB
- if (dotProdNormalAB_AC * dotProdNormalAB_AD > 0) return false;
-
- // AB and CD are on the same line
- if (dotProdNormalAB_AC == 0 && dotProdNormalAB_AD == 0) {
- // Test if C or D is on segment AB.
- return (c - a).DotProd(c - b) <= 0 || (d - a).DotProd(d - b) <= 0;
- }
-
- // Check if A and B are on different sides of line CD.
- Vector2_d normalCD = (d - c).Ortho();
- double dotProdNormalCD_CA = normalCD.DotProd(a - c);
- double dotProdNormalCD_CB = normalCD.DotProd(b - c);
- return dotProdNormalCD_CA * dotProdNormalCD_CB <= 0; // Perhaps A or B is on line CD
- }
-
- static bool circleContainsBoxInternal(const Circle& circle,
- const Box& box,
- bool includeCircleBoundary) {
-
- // NOTE: a circle of zero radius is a point, and there are NO points contained inside a
- // zero-radius circle, not even the point itself.
-
- const Point& a = box._min;
- const Point& b = box._max;
- double compareLL = distanceCompare( circle.center, a, circle.radius ); // Lower left
- double compareUR = distanceCompare( circle.center, b, circle.radius ); // Upper right
- // Upper Left
- double compareUL = distanceCompare( circle.center, Point( a.x, b.y ), circle.radius );
- // Lower right
- double compareLR = distanceCompare( circle.center, Point( b.x, a.y ), circle.radius );
- if ( includeCircleBoundary ) {
- return compareLL <= 0 && compareUR <= 0 && compareUL <= 0 && compareLR <= 0;
- }
- else {
- return compareLL < 0 && compareUR < 0 && compareUL < 0 && compareLR < 0;
+/**
+ * Distance method that compares x or y coords when other direction is zero,
+ * avoids numerical error when distances are very close to radius but axis-aligned.
+ *
+ * An example of the problem is:
+ * (52.0 - 51.9999) - 0.0001 = 3.31965e-15 and 52.0 - 51.9999 > 0.0001 in double arithmetic
+ * but:
+ * 51.9999 + 0.0001 <= 52.0
+ *
+ * This avoids some (but not all!) suprising results in $center queries where points are
+ * (radius + center.x, center.y) or vice-versa.
+ */
+bool distanceWithin(const Point& p1, const Point& p2, double radius) {
+ return distanceCompare(p1, p2, radius) <= 0.0;
+}
+
+// Compare the distance between p1 and p2 with the radius.
+// Float-number comparison might be inaccurate.
+//
+// > 0: distance is greater than radius
+// = 0: distance equals radius
+// < 0: distance is less than radius
+double distanceCompare(const Point& p1, const Point& p2, double radius) {
+ double a = p2.x - p1.x;
+ double b = p2.y - p1.y;
+
+ if (a == 0) {
+ //
+ // Note: For some, unknown reason, when a 32-bit g++ optimizes this call, the sum is
+ // calculated imprecisely. We need to force the compiler to always evaluate it
+ // correctly, hence the weirdness.
+ //
+ // On some 32-bit linux machines, removing the volatile keyword or calculating the sum
+ // inline will make certain geo tests fail. Of course this check will force volatile
+ // for all 32-bit systems, not just affected systems.
+ if (sizeof(void*) <= 4) {
+ volatile double sum = p2.y > p1.y ? p1.y + radius : p2.y + radius;
+ return p2.y > p1.y ? p2.y - sum : p1.y - sum;
+ } else {
+ // Original math, correct for most systems
+ return p2.y > p1.y ? p2.y - (p1.y + radius) : p1.y - (p2.y + radius);
}
}
- bool circleContainsBox(const Circle& circle, const Box& box) {
- return circleContainsBoxInternal(circle, box, true);
- }
-
- bool circleInteriorContainsBox(const Circle& circle, const Box& box) {
- return circleContainsBoxInternal(circle, box, false);
- }
-
- // Check the intersection by measuring the distance between circle center and box center.
- static bool circleIntersectsWithBoxInternal(const Circle& circle,
- const Box& box,
- bool includeCircleBoundary) {
-
- // NOTE: a circle of zero radius is a point, and there are NO points to intersect inside a
- // zero-radius circle, not even the point itself.
- if (circle.radius == 0.0 && !includeCircleBoundary)
- return false;
-
- /* Collapses the four quadrants down into one.
- * ________
- * r|___B___ \ <- a quarter round corner here. Let's name it "D".
- * | | |
- * h| | |
- * | A |C|
- * |_______|_|
- * w r
- */
-
- Point boxCenter = box.center();
- double dx = abs(circle.center.x - boxCenter.x);
- double dy = abs(circle.center.y - boxCenter.y);
- double w = (box._max.x - box._min.x) / 2;
- double h = (box._max.y - box._min.y) / 2;
- const double& r = circle.radius;
-
- // Check if circle.center is in A, B or C.
- // The circle center could be above the box (B) or right to the box (C), but close enough.
- if (includeCircleBoundary) {
- if ((dx <= w + r && dy <= h) || (dx <= w && dy <= h + r)) return true;
+ if (b == 0) {
+ if (sizeof(void*) <= 4) {
+ volatile double sum = p2.x > p1.x ? p1.x + radius : p2.x + radius;
+ return p2.x > p1.x ? p2.x - sum : p1.x - sum;
} else {
- if ((dx < w + r && dy < h) || (dx < w && dy < h + r)) return true;
+ return p2.x > p1.x ? p2.x - (p1.x + radius) : p1.x - (p2.x + radius);
}
-
- // Now check if circle.center is in the round corner "D".
- double compareResult = distanceCompare(Point(dx, dy), Point(w, h), r);
- return compareResult < 0 || (compareResult == 0 && includeCircleBoundary);
}
- bool circleIntersectsWithBox(const Circle& circle, const Box& box) {
- return circleIntersectsWithBoxInternal(circle, box, true);
- }
+ return sqrt((a * a) + (b * b)) - radius;
+}
+
+// note: multiply by earth radius for distance
+double spheredist_rad(const Point& p1, const Point& p2) {
+ // this uses the n-vector formula: http://en.wikipedia.org/wiki/N-vector
+ // If you try to match the code to the formula, note that I inline the cross-product.
+
+ double sinx1(sin(p1.x)), cosx1(cos(p1.x));
+ double siny1(sin(p1.y)), cosy1(cos(p1.y));
+ double sinx2(sin(p2.x)), cosx2(cos(p2.x));
+ double siny2(sin(p2.y)), cosy2(cos(p2.y));
+
+ double cross_prod =
+ (cosy1 * cosx1 * cosy2 * cosx2) + (cosy1 * sinx1 * cosy2 * sinx2) + (siny1 * siny2);
+
+ if (cross_prod >= 1 || cross_prod <= -1) {
+ // fun with floats
+ verify(fabs(cross_prod) - 1 < 1e-6);
+ return cross_prod > 0 ? 0 : M_PI;
+ }
+
+ return acos(cross_prod);
+}
+
+// @param p1 A point on the sphere where x and y are degrees.
+// @param p2 A point on the sphere where x and y are degrees.
+// @return The distance between the two points in RADIANS. Multiply by radius to get arc
+// length.
+double spheredist_deg(const Point& p1, const Point& p2) {
+ return spheredist_rad(Point(deg2rad(p1.x), deg2rad(p1.y)), Point(deg2rad(p2.x), deg2rad(p2.y)));
+}
+
+// Technically lat/long bounds, not really tied to earth radius.
+bool isValidLngLat(double lng, double lat) {
+ return abs(lng) <= 180 && abs(lat) <= 90;
+}
+
+double distance(const Point& p1, const Point& p2) {
+ double a = p1.x - p2.x;
+ double b = p1.y - p2.y;
+
+ // Avoid numerical error if possible...
+ if (a == 0)
+ return abs(b);
+ if (b == 0)
+ return abs(a);
+
+ return sqrt((a * a) + (b * b));
+}
+
+static inline Vector2_d toVector2(const Point& p) {
+ return Vector2_d(p.x, p.y);
+}
+
+// Given a segment (A, B) and a segment (C, D), check whether they intersect.
+bool linesIntersect(const Point& pA, const Point& pB, const Point& pC, const Point& pD) {
+ Vector2_d a = toVector2(pA);
+ Vector2_d b = toVector2(pB);
+ Vector2_d c = toVector2(pC);
+ Vector2_d d = toVector2(pD);
+
+ // The normal of line AB
+ Vector2_d normalAB = (b - a).Ortho();
+
+ // Dot products of AC and the normal of AB
+ // = 0 : C is on the line AB
+ // > 0 : C is on one side
+ // < 0 : C is on the other side
+ double dotProdNormalAB_AC = normalAB.DotProd(c - a);
+ double dotProdNormalAB_AD = normalAB.DotProd(d - a);
+
+ // C and D can not on the same side of line AB
+ if (dotProdNormalAB_AC * dotProdNormalAB_AD > 0)
+ return false;
+
+ // AB and CD are on the same line
+ if (dotProdNormalAB_AC == 0 && dotProdNormalAB_AD == 0) {
+ // Test if C or D is on segment AB.
+ return (c - a).DotProd(c - b) <= 0 || (d - a).DotProd(d - b) <= 0;
+ }
+
+ // Check if A and B are on different sides of line CD.
+ Vector2_d normalCD = (d - c).Ortho();
+ double dotProdNormalCD_CA = normalCD.DotProd(a - c);
+ double dotProdNormalCD_CB = normalCD.DotProd(b - c);
+ return dotProdNormalCD_CA * dotProdNormalCD_CB <= 0; // Perhaps A or B is on line CD
+}
+
+static bool circleContainsBoxInternal(const Circle& circle,
+ const Box& box,
+ bool includeCircleBoundary) {
+ // NOTE: a circle of zero radius is a point, and there are NO points contained inside a
+ // zero-radius circle, not even the point itself.
+
+ const Point& a = box._min;
+ const Point& b = box._max;
+ double compareLL = distanceCompare(circle.center, a, circle.radius); // Lower left
+ double compareUR = distanceCompare(circle.center, b, circle.radius); // Upper right
+ // Upper Left
+ double compareUL = distanceCompare(circle.center, Point(a.x, b.y), circle.radius);
+ // Lower right
+ double compareLR = distanceCompare(circle.center, Point(b.x, a.y), circle.radius);
+ if (includeCircleBoundary) {
+ return compareLL <= 0 && compareUR <= 0 && compareUL <= 0 && compareLR <= 0;
+ } else {
+ return compareLL < 0 && compareUR < 0 && compareUL < 0 && compareLR < 0;
+ }
+}
+
+bool circleContainsBox(const Circle& circle, const Box& box) {
+ return circleContainsBoxInternal(circle, box, true);
+}
+
+bool circleInteriorContainsBox(const Circle& circle, const Box& box) {
+ return circleContainsBoxInternal(circle, box, false);
+}
+
+// Check the intersection by measuring the distance between circle center and box center.
+static bool circleIntersectsWithBoxInternal(const Circle& circle,
+ const Box& box,
+ bool includeCircleBoundary) {
+ // NOTE: a circle of zero radius is a point, and there are NO points to intersect inside a
+ // zero-radius circle, not even the point itself.
+ if (circle.radius == 0.0 && !includeCircleBoundary)
+ return false;
+
+ /* Collapses the four quadrants down into one.
+ * ________
+ * r|___B___ \ <- a quarter round corner here. Let's name it "D".
+ * | | |
+ * h| | |
+ * | A |C|
+ * |_______|_|
+ * w r
+ */
- bool circleInteriorIntersectsWithBox(const Circle& circle, const Box& box) {
- return circleIntersectsWithBoxInternal(circle, box, false);
+ Point boxCenter = box.center();
+ double dx = abs(circle.center.x - boxCenter.x);
+ double dy = abs(circle.center.y - boxCenter.y);
+ double w = (box._max.x - box._min.x) / 2;
+ double h = (box._max.y - box._min.y) / 2;
+ const double& r = circle.radius;
+
+ // Check if circle.center is in A, B or C.
+ // The circle center could be above the box (B) or right to the box (C), but close enough.
+ if (includeCircleBoundary) {
+ if ((dx <= w + r && dy <= h) || (dx <= w && dy <= h + r))
+ return true;
+ } else {
+ if ((dx < w + r && dy < h) || (dx < w && dy < h + r))
+ return true;
}
- bool lineIntersectsWithBox(const Point& a, const Point& b, const Box& box) {
- Point upperLeft(box._min.x, box._max.y);
- Point lowerRight(box._max.x, box._min.y);
+ // Now check if circle.center is in the round corner "D".
+ double compareResult = distanceCompare(Point(dx, dy), Point(w, h), r);
+ return compareResult < 0 || (compareResult == 0 && includeCircleBoundary);
+}
- return linesIntersect(a, b, upperLeft, box._min)
- || linesIntersect(a, b, box._min, lowerRight)
- || linesIntersect(a, b, lowerRight, box._max)
- || linesIntersect(a, b, box._max, upperLeft);
- }
+bool circleIntersectsWithBox(const Circle& circle, const Box& box) {
+ return circleIntersectsWithBoxInternal(circle, box, true);
+}
- // Doc: The last point specified is always implicitly connected to the first.
- // [[ 0 , 0 ], [ 3 , 6 ], [ 6 , 0 ]]
- bool edgesIntersectsWithBox(const vector<Point>& vertices, const Box& box) {
- for (size_t i = 0; i < vertices.size() - 1; i++) {
- if (lineIntersectsWithBox(vertices[i], vertices[i+1], box)) return true;
- }
- // The last point and first point.
- return lineIntersectsWithBox(vertices[vertices.size() - 1], vertices[0], box);
- }
+bool circleInteriorIntersectsWithBox(const Circle& circle, const Box& box) {
+ return circleIntersectsWithBoxInternal(circle, box, false);
+}
- bool polygonContainsBox(const Polygon& polygon, const Box& box) {
- // All vertices of box have to be inside the polygon.
- if (!polygon.contains(box._min)
- || !polygon.contains(box._max)
- || !polygon.contains(Point(box._min.x, box._max.y))
- || !polygon.contains(Point(box._max.x, box._min.y)))
- return false;
+bool lineIntersectsWithBox(const Point& a, const Point& b, const Box& box) {
+ Point upperLeft(box._min.x, box._max.y);
+ Point lowerRight(box._max.x, box._min.y);
- // No intersection between the polygon edges and the box.
- return !edgesIntersectsWithBox(polygon.points(), box);
- }
+ return linesIntersect(a, b, upperLeft, box._min) ||
+ linesIntersect(a, b, box._min, lowerRight) || linesIntersect(a, b, lowerRight, box._max) ||
+ linesIntersect(a, b, box._max, upperLeft);
+}
- bool polygonIntersectsWithBox(const Polygon& polygon, const Box& box) {
- // 1. Polygon contains the box.
- // Check the relaxed condition that whether the polygon include any vertex of the box.
- if (polygon.contains(box._min)
- || polygon.contains(box._max)
- || polygon.contains(Point(box._min.x, box._max.y))
- || polygon.contains(Point(box._max.x, box._min.y)))
+// Doc: The last point specified is always implicitly connected to the first.
+// [[ 0 , 0 ], [ 3 , 6 ], [ 6 , 0 ]]
+bool edgesIntersectsWithBox(const vector<Point>& vertices, const Box& box) {
+ for (size_t i = 0; i < vertices.size() - 1; i++) {
+ if (lineIntersectsWithBox(vertices[i], vertices[i + 1], box))
return true;
-
- // 2. Box contains polygon.
- // Check the relaxed condition that whether the box include any vertex of the polygon.
- for (vector<Point>::const_iterator it = polygon.points().begin();
- it != polygon.points().end(); it++) {
- if (box.inside(*it)) return true;
- }
-
- // 3. Otherwise they intersect on a portion of both shapes.
- // Edges intersects
- return edgesIntersectsWithBox(polygon.points(), box);
}
+ // The last point and first point.
+ return lineIntersectsWithBox(vertices[vertices.size() - 1], vertices[0], box);
+}
+
+bool polygonContainsBox(const Polygon& polygon, const Box& box) {
+ // All vertices of box have to be inside the polygon.
+ if (!polygon.contains(box._min) || !polygon.contains(box._max) ||
+ !polygon.contains(Point(box._min.x, box._max.y)) ||
+ !polygon.contains(Point(box._max.x, box._min.y)))
+ return false;
+
+ // No intersection between the polygon edges and the box.
+ return !edgesIntersectsWithBox(polygon.points(), box);
+}
+
+bool polygonIntersectsWithBox(const Polygon& polygon, const Box& box) {
+ // 1. Polygon contains the box.
+ // Check the relaxed condition that whether the polygon include any vertex of the box.
+ if (polygon.contains(box._min) || polygon.contains(box._max) ||
+ polygon.contains(Point(box._min.x, box._max.y)) ||
+ polygon.contains(Point(box._max.x, box._min.y)))
+ return true;
- bool ShapeProjection::supportsProject(const PointWithCRS& point, const CRS crs) {
-
- // Can always trivially project or project from SPHERE->FLAT
- if (point.crs == crs || point.crs == SPHERE)
+ // 2. Box contains polygon.
+ // Check the relaxed condition that whether the box include any vertex of the polygon.
+ for (vector<Point>::const_iterator it = polygon.points().begin(); it != polygon.points().end();
+ it++) {
+ if (box.inside(*it))
return true;
-
- invariant(point.crs == FLAT);
- // If crs is FLAT, we might be able to upgrade the point to SPHERE if it's a valid SPHERE
- // point (lng/lat in bounds). In this case, we can use FLAT data with SPHERE predicates.
- return isValidLngLat(point.oldPoint.x, point.oldPoint.y);
}
- bool ShapeProjection::supportsProject(const PolygonWithCRS& polygon, const CRS crs) {
- return polygon.crs == crs
- || (polygon.crs == STRICT_SPHERE && crs == SPHERE);
- }
+ // 3. Otherwise they intersect on a portion of both shapes.
+ // Edges intersects
+ return edgesIntersectsWithBox(polygon.points(), box);
+}
- void ShapeProjection::projectInto(PointWithCRS* point, CRS crs) {
- dassert(supportsProject(*point, crs));
-
- if (point->crs == crs)
- return;
+bool ShapeProjection::supportsProject(const PointWithCRS& point, const CRS crs) {
+ // Can always trivially project or project from SPHERE->FLAT
+ if (point.crs == crs || point.crs == SPHERE)
+ return true;
- if (FLAT == point->crs) {
- // Prohibit projection to STRICT_SPHERE CRS
- invariant(SPHERE == crs);
+ invariant(point.crs == FLAT);
+ // If crs is FLAT, we might be able to upgrade the point to SPHERE if it's a valid SPHERE
+ // point (lng/lat in bounds). In this case, we can use FLAT data with SPHERE predicates.
+ return isValidLngLat(point.oldPoint.x, point.oldPoint.y);
+}
- // Note that it's (lat, lng) for S2 but (lng, lat) for MongoDB.
- S2LatLng latLng =
- S2LatLng::FromDegrees(point->oldPoint.y, point->oldPoint.x).Normalized();
- dassert(latLng.is_valid());
- point->point = latLng.ToPoint();
- point->cell = S2Cell(point->point);
- point->crs = SPHERE;
- return;
- }
+bool ShapeProjection::supportsProject(const PolygonWithCRS& polygon, const CRS crs) {
+ return polygon.crs == crs || (polygon.crs == STRICT_SPHERE && crs == SPHERE);
+}
- // Prohibit projection to STRICT_SPHERE CRS
- invariant(SPHERE == point->crs && FLAT == crs);
- // Just remove the additional spherical information
- point->point = S2Point();
- point->cell = S2Cell();
- point->crs = FLAT;
- }
+void ShapeProjection::projectInto(PointWithCRS* point, CRS crs) {
+ dassert(supportsProject(*point, crs));
- void ShapeProjection::projectInto(PolygonWithCRS* polygon, CRS crs) {
- if (polygon->crs == crs) return;
+ if (point->crs == crs)
+ return;
- // Only project from STRICT_SPHERE to SPHERE
- invariant(STRICT_SPHERE == polygon->crs && SPHERE == crs);
- polygon->crs = SPHERE;
- }
+ if (FLAT == point->crs) {
+ // Prohibit projection to STRICT_SPHERE CRS
+ invariant(SPHERE == crs);
+
+ // Note that it's (lat, lng) for S2 but (lng, lat) for MongoDB.
+ S2LatLng latLng = S2LatLng::FromDegrees(point->oldPoint.y, point->oldPoint.x).Normalized();
+ dassert(latLng.is_valid());
+ point->point = latLng.ToPoint();
+ point->cell = S2Cell(point->point);
+ point->crs = SPHERE;
+ return;
+ }
+
+ // Prohibit projection to STRICT_SPHERE CRS
+ invariant(SPHERE == point->crs && FLAT == crs);
+ // Just remove the additional spherical information
+ point->point = S2Point();
+ point->cell = S2Cell();
+ point->crs = FLAT;
+}
+
+void ShapeProjection::projectInto(PolygonWithCRS* polygon, CRS crs) {
+ if (polygon->crs == crs)
+ return;
+
+ // Only project from STRICT_SPHERE to SPHERE
+ invariant(STRICT_SPHERE == polygon->crs && SPHERE == crs);
+ polygon->crs = SPHERE;
+}
} // namespace mongo