// Copyright 2016, University of Freiburg, // Chair of Algorithms and Data Structures. // Authors: Patrick Brosi #ifndef UTIL_GEO_GEON_H_ #define UTIL_GEO_GEON_H_ #define _USE_MATH_DEFINES #include #include "util/Misc.h" #include "util/geo/Box.h" #include "util/geo/Line.h" #include "util/geo/Point.h" // ------------------- // Geometry stuff // ------------------ namespace util { namespace geon { // convenience aliases typedef Point DPoint; typedef Point FPoint; typedef Point IPoint; typedef Line DLine; typedef Line FLine; typedef Line ILine; typedef Box DBox; typedef Box FBox; typedef Box IBox; // typedef Polygon DPolygon; // typedef Polygon FPolygon; // typedef Polygon IPolygon; // _____________________________________________________________________________ // template // inline Line rotate(const Line& geo, double deg, const Point& center) // { // Line ret; // bgeo::strategy::transform::translate_transformer translate( // -center.getX(), -center.getY()); // bgeo::strategy::transform::rotate_transformer rotate( // deg); // bgeo::strategy::transform::translate_transformer translateBack( // center.getX(), center.getY()); // bgeo::strategy::transform::ublas_transformer translateRotate( // prod(rotate.matrix(), translate.matrix())); // bgeo::strategy::transform::ublas_transformer all( // prod(translateBack.matrix(), translateRotate.matrix())); // bgeo::transform(geo, ret, all); // return ret; // } // _____________________________________________________________________________ // template // inline MultiLine rotate(const MultiLine& geo, double deg, // const Point& center) { // MultiLine ret; // bgeo::strategy::transform::translate_transformer translate( // -center.getX(), -center.getY()); // bgeo::strategy::transform::rotate_transformer rotate( // deg); // bgeo::strategy::transform::translate_transformer translateBack( // center.getX(), center.getY()); // bgeo::strategy::transform::ublas_transformer translateRotate( // prod(rotate.matrix(), translate.matrix())); // bgeo::strategy::transform::ublas_transformer all( // prod(translateBack.matrix(), translateRotate.matrix())); // bgeo::transform(geo, ret, all); // return ret; // } // _____________________________________________________________________________ template inline Box pad(const Box& box, double padding) { return Box(Point(box.getLowerLeft().getX() - padding, box.getLowerLeft().getY() - padding), Point(box.getUpperRight().getX() + padding, box.getUpperRight().getY() + padding)); } // _____________________________________________________________________________ template inline Point rotate(const Point& p, double deg) { return p; } // _____________________________________________________________________________ // template // inline Line rotate(const Line& geo, double deg) { // Point center; // bgeo::centroid(geo, center); // return rotate(geo, deg, center); // } // _____________________________________________________________________________ // template // inline MultiLine rotate(const MultiLine& geo, double deg) { // Point center; // bgeo::centroid(geo, center); // return rotate(geo, deg, center); // } // _____________________________________________________________________________ template inline Point move(const Point& geo, T x, T y) { return Point(geo.getX() + x, geo.getY() + y); } // TODO: outfactor // template // struct RotatedBox { // RotatedBox(const Box& b, double rot, const Point& center) // : b(b), rotateDeg(rot), center(center) {} // RotatedBox(const Box& b, double rot) : b(b), rotateDeg(rot) { // bgeo::centroid(b, center); // } // Box b; // double rotateDeg; // Point center; // Polygon getPolygon() { // Polygon hull; // bgeo::convex_hull(b, hull); // return rotate(hull, rotateDeg, center); // } // }; // _____________________________________________________________________________ template inline Box minbox() { return Box(); } // _____________________________________________________________________________ // template // inline RotatedBox shrink(const RotatedBox& b, double d) { // double xd = // b.b.getUpperRight().getX() - b.b.getLowerLeft().getX(); // double yd = // b.b.getUpperRight().getY() - b.b.getLowerLeft().getY(); // if (xd <= 2 * d) d = xd / 2 - 1; // if (yd <= 2 * d) d = yd / 2 - 1; // Box r(Point(b.b.getLowerLeft().getX() + d, // b.b.getLowerLeft().getY() + d), // Point(b.b.getUpperRight().getX() - d, // b.b.getUpperRight().getY() - d)); // return RotatedBox(r, b.rotateDeg, b.center); // } // _____________________________________________________________________________ inline bool doubleEq(double a, double b) { return fabs(a - b) < 0.000001; } // _____________________________________________________________________________ template inline bool contains(const Point& p, const Box& box) { return p.getX() >= box.getLowerLeft().getX() && p.getX() <= box.getUpperRight().getX() && p.getY() >= box.getLowerLeft().getY() && p.getY() <= box.getUpperRight().getY(); } // _____________________________________________________________________________ template inline bool contains(const Line& l, const Box& box) { for (const auto& p : l) if (!contains(p, box)) return false; return true; } // _____________________________________________________________________________ // template // inline bool contains(const Point& p1, const Point& q1, const Point& // p2, // const Point& q2) { // Line a, b; // a.push_back(p1); // a.push_back(q1); // b.push_back(p2); // b.push_back(q2); // return bgeo::covered_by(a, b); // } // _____________________________________________________________________________ // template // inline bool contains(T p1x, T p1y, T q1x, T q1y, T p2x, T p2y, T q2x, T q2y) // { // Point p1(p1x, p1y); // Point q1(q1x, q1y); // Point p2(p2x, p2y); // Point q2(q2x, q2y); // return contains(p1, q1, p2, q2); // } // _____________________________________________________________________________ // template // inline bool intersects(const Point& p1, const Point& q1, // const Point& p2, const Point& q2) { /* * checks whether two line segments intersect */ // Line a, b; // a.push_back(p1); // a.push_back(q1); // b.push_back(p2); // b.push_back(q2); // return !(contains(p1, q1, p2, q2) || contains(p2, q2, p1, q1)) && // bgeo::intersects(a, b); // } // _____________________________________________________________________________ // template // inline bool intersects(T p1x, T p1y, T q1x, T q1y, T p2x, T p2y, T q2x, T // q2y) { /* * checks whether two line segments intersect */ // Point p1(p1x, p1y); // Point q1(q1x, q1y); // Point p2(p2x, p2y); // Point q2(q2x, q2y); // return intersects(p1, q1, p2, q2); // } // _____________________________________________________________________________ template inline Point intersection(T p1x, T p1y, T q1x, T q1y, T p2x, T p2y, T q2x, T q2y) { /* * calculates the intersection between two line segments */ if (doubleEq(p1x, q1x) && doubleEq(p1y, q1y)) return Point(p1x, p1y); // TODO: <-- intersecting with a point?? if (doubleEq(p2x, q1x) && doubleEq(p2y, q1y)) return Point(p2x, p2y); if (doubleEq(p2x, q2x) && doubleEq(p2y, q2y)) return Point(p2x, p2y); // TODO: <-- intersecting with a point?? if (doubleEq(p1x, q2x) && doubleEq(p1y, q2y)) return Point(p1x, p1y); double a = ((q2y - p2y) * (q1x - p1x)) - ((q2x - p2x) * (q1y - p1y)); double u = (((q2x - p2x) * (p1y - p2y)) - ((q2y - p2y) * (p1x - p2x))) / a; return Point(p1x + (q1x - p1x) * u, p1y + (q1y - p1y) * u); } // _____________________________________________________________________________ template inline Point intersection(const Point& p1, const Point& q1, const Point& p2, const Point& q2) { /* * calculates the intersection between two line segments */ return intersection(p1.getX(), p1.getY(), q1.getX(), q1.getY(), p2.getX(), p2.getY(), q2.getX(), q2.getY()); } // _____________________________________________________________________________ template inline bool lineIntersects(T p1x, T p1y, T q1x, T q1y, T p2x, T p2y, T q2x, T q2y) { /* * checks whether two lines intersect */ double EPSILON = 0.0000001; double a = ((q2y - p2y) * (q1x - p1x)) - ((q2x - p2x) * (q1y - p1y)); return a > EPSILON || a < -EPSILON; } // _____________________________________________________________________________ template inline bool lineIntersects(const Point& p1, const Point& q1, const Point& p2, const Point& q2) { /* * checks whether two lines intersect */ return lineIntersects(p1.getX(), p1.getY(), q1.getX(), q1.getY(), p2.getX(), p2.getY(), q2.getX(), q2.getY()); } // _____________________________________________________________________________ inline double angBetween(double p1x, double p1y, double q1x, double q1y) { double dY = q1y - p1y; double dX = q1x - p1x; return atan2(dY, dX); } // _____________________________________________________________________________ template inline double angBetween(const Point& p1, const Point& q1) { return angBetween(p1.getX(), p1.getY(), q1.getX(), q1.getY()); } // _____________________________________________________________________________ inline double dist(double x1, double y1, double x2, double y2) { return sqrt((x2 - x1) * (x2 - x1) + (y2 - y1) * (y2 - y1)); } // _____________________________________________________________________________ inline double innerProd(double x1, double y1, double x2, double y2, double x3, double y3) { double dx21 = x2 - x1; double dx31 = x3 - x1; double dy21 = y2 - y1; double dy31 = y3 - y1; double m12 = sqrt(dx21 * dx21 + dy21 * dy21); double m13 = sqrt(dx31 * dx31 + dy31 * dy31); double theta = acos(std::min((dx21 * dx31 + dy21 * dy31) / (m12 * m13), 1.0)); return theta * (180 / M_PI); } // _____________________________________________________________________________ template inline double innerProd(const Point& a, const Point& b, const Point& c) { return innerProd(a.template getX(), a.template getY(), b.template getX(), b.template getY(), c.template getX(), c.template getY()); } // _____________________________________________________________________________ template inline double dist(const Point& p1, const Point& p2) { return sqrt((p1.getX() - p2.getX()) * (p1.getX() - p2.getX()) + (p1.getY() - p2.getY()) * (p1.getY() - p2.getY())); } // _____________________________________________________________________________ template inline std::string getWKT(const Point& p) { std::stringstream ss; ss << "POINT (" << p.getX() << " " << p.getY() << ")"; return ss.str(); } // _____________________________________________________________________________ template inline std::string getWKT(const Line& l) { std::stringstream ss; ss << "LINESTRING ("; for (size_t i = 0; i < l.size(); i++) { if (i) ss << ", "; ss << l[i].getX() << " " << l[i].getY(); } return ss.str(); } // _____________________________________________________________________________ template inline double len(const Point& g) { return 0; } // _____________________________________________________________________________ template inline double len(const Line& g) { double ret = 0; for (size_t i = 1; i < g.size(); i++) ret += dist(g[i - 1], g[i]); return ret; } // _____________________________________________________________________________ template inline Point simplify(const Point& g, double d) { return g; } // _____________________________________________________________________________ template inline Line simplify(const Line& g, double d) { return g; } // _____________________________________________________________________________ inline double distToSegment(double lax, double lay, double lbx, double lby, double px, double py) { double d = dist(lax, lay, lbx, lby) * dist(lax, lay, lbx, lby); if (d == 0) return dist(px, py, lax, lay); double t = ((px - lax) * (lbx - lax) + (py - lay) * (lby - lay)) / d; if (t < 0) { return dist(px, py, lax, lay); } else if (t > 1) { return dist(px, py, lbx, lby); } return dist(px, py, lax + t * (lbx - lax), lay + t * (lby - lay)); } // _____________________________________________________________________________ template inline double distToSegment(const Point& la, const Point& lb, const Point& p) { return distToSegment(la.getX(), la.getY(), lb.getX(), lb.getY(), p.getX(), p.getY()); } // _____________________________________________________________________________ template inline Point projectOn(const Point& a, const Point& b, const Point& c) { if (doubleEq(a.getX(), b.getX()) && doubleEq(a.getY(), b.getY())) return a; if (doubleEq(a.getX(), c.getX()) && doubleEq(a.getY(), c.getY())) return a; if (doubleEq(b.getX(), c.getX()) && doubleEq(b.getY(), c.getY())) return b; double x, y; if (c.getX() == a.getX()) { // infinite slope x = a.getX(); y = b.getY(); } else { double m = (double)(c.getY() - a.getY()) / (c.getX() - a.getX()); double bb = (double)a.getY() - (m * a.getX()); x = (m * b.getY() + b.getX() - m * bb) / (m * m + 1); y = (m * m * b.getY() + m * b.getX() + bb) / (m * m + 1); } Point ret = Point(x, y); bool isBetween = dist(a, c) > dist(a, ret) && dist(a, c) > dist(c, ret); bool nearer = dist(a, ret) < dist(c, ret); if (!isBetween) return nearer ? a : c; return ret; } // _____________________________________________________________________________ template inline double parallelity(const Box& box, const Line& line) { double ret = M_PI; double a = angBetween( box.getLowerLeft(), Point(box.getLowerLeft().getX(), box.getUpperRight().getY())); double b = angBetween( box.getLowerLeft(), Point(box.getUpperRight().getX(), box.getLowerLeft().getY())); double c = angBetween( box.getUpperRight(), Point(box.getLowerLeft().getX(), box.getUpperRight().getY())); double d = angBetween( box.getUpperRight(), Point(box.getUpperRight().getX(), box.getLowerLeft().getY())); double e = angBetween(line.front(), line.back()); double vals[] = {a, b, c, d}; for (double ang : vals) { double v = fabs(ang - e); if (v > M_PI) v = 2 * M_PI - v; if (v > M_PI / 2) v = M_PI - v; if (v < ret) ret = v; } return 1 - (ret / (M_PI / 4)); } // _____________________________________________________________________________ // template // inline double parallelity(const Box& box, const MultiLine& multiline) { // double ret = 0; // for (const Line& l : multiline) { // ret += parallelity(box, l); // } // return ret / multiline.size(); // } // _____________________________________________________________________________ // template // inline bool intersects(const GeomA& a, const GeomB& b) { // return bgeo::intersects(a, b); // } // _____________________________________________________________________________ // template typename Geometry> // inline RotatedBox getOrientedEnvelope(Geometry pol) { // // TODO: implement this nicer, works for now, but inefficient // // see // // https://geidav.wordpress.com/tag/gift-wrapping/#fn-1057-FreemanShapira1975 // // for a nicer algorithm // Point center; // bgeo::centroid(pol, center); // Box tmpBox = getBoundingBox(pol); // double rotateDeg = 0; // // rotate in 5 deg steps // for (int i = 1; i < 360; i += 1) { // pol = rotate(pol, 1, center); // Box e; // bgeo::envelope(pol, e); // if (bgeo::area(tmpBox) > bgeo::area(e)) { // tmpBox = e; // rotateDeg = i; // } // } // return RotatedBox(tmpBox, -rotateDeg, center); // } // _____________________________________________________________________________ template inline Box getBoundingBox(const Point& p) { return Box(p, p); } // _____________________________________________________________________________ template inline Box getBoundingBox(const Line& l) { Box ret; for (const auto& p : l) extendBox(p, ret); return ret; } // _____________________________________________________________________________ template inline Box extendBox(const Box& a, Box b) { b = extendBox(a.getLowerLeft(), b); b = extendBox(a.getUpperRight(), b); return b; } // _____________________________________________________________________________ template inline Box extendBox(const Point& p, Box b) { if (p.getX() < b.getLowerLeft().getX()) b.getLowerLeft().setX(p.getX()); if (p.getY() < b.getLowerLeft().getY()) b.getLowerLeft().setY(p.getY()); if (p.getX() > b.getUpperRight().getX()) b.getUpperRight().setX(p.getX()); if (p.getY() > b.getUpperRight().getY()) b.getUpperRight().setY(p.getY()); return b; } // _____________________________________________________________________________ template inline Box extendBox(const Line& l, Box b) { for (const auto& p : l) b = extendBox(p, b); return b; } // _____________________________________________________________________________ template inline double commonArea(const Box& ba, const Box& bb) { T l = std::max(ba.getLowerLeft().getX(), bb.getLowerLeft().getX()); T r = std::min(ba.getUpperRight().getX(), bb.getUpperRight().getX()); T b = std::max(ba.getLowerLeft().getY(), bb.getLowerLeft().getY()); T t = std::min(ba.getUpperRight().getY(), bb.getUpperRight().getY()); if (l > r || b > t) return 0; return (r - l) * (t - b); } // _____________________________________________________________________________ // template typename Geometry> // inline RotatedBox getFullEnvelope(Geometry pol) { // Point center; // bgeo::centroid(pol, center); // Box tmpBox; // bgeo::envelope(pol, tmpBox); // double rotateDeg = 0; // MultiPolygon ml; // // rotate in 5 deg steps // for (int i = 1; i < 360; i += 1) { // pol = rotate(pol, 1, center); // Polygon hull; // bgeo::convex_hull(pol, hull); // ml.push_back(hull); // Box e; // bgeo::envelope(pol, e); // if (bgeo::area(tmpBox) > bgeo::area(e)) { // tmpBox = e; // rotateDeg = i; // } // } // bgeo::envelope(ml, tmpBox); // return RotatedBox(tmpBox, rotateDeg, center); // } // _____________________________________________________________________________ // template // inline RotatedBox getOrientedEnvelopeAvg(MultiLine ml) { // MultiLine orig = ml; // // get oriented envelope for hull // RotatedBox rbox = getFullEnvelope(ml); // Point center; // bgeo::centroid(rbox.b, center); // ml = rotate(ml, -rbox.rotateDeg - 45, center); // double bestDeg = -45; // double score = parallelity(rbox.b, ml); // for (double i = -45; i <= 45; i += .5) { // ml = rotate(ml, -.5, center); // double p = parallelity(rbox.b, ml); // if (parallelity(rbox.b, ml) > score) { // bestDeg = i; // score = p; // } // } // rbox.rotateDeg += bestDeg; // // move the box along 45deg angles from its origin until it fits the ml // // = until the intersection of its hull and the box is largest // Polygon p = rbox.getPolygon(); // p = rotate(p, -rbox.rotateDeg, rbox.center); // Polygon hull; // bgeo::convex_hull(orig, hull); // hull = rotate(hull, -rbox.rotateDeg, rbox.center); // Box box; // bgeo::envelope(hull, box); // rbox = RotatedBox(box, rbox.rotateDeg, rbox.center); // return rbox; // } // _____________________________________________________________________________ template inline Line densify(const Line& l, double d) { if (!l.size()) return l; Line ret; ret.reserve(l.size()); ret.push_back(l.front()); for (size_t i = 1; i < l.size(); i++) { double segd = dist(l[i - 1], l[i]); double dx = (l[i].getX() - l[i - 1].getX()) / segd; double dy = (l[i].getY() - l[i - 1].getY()) / segd; double curd = d; while (curd < segd) { ret.push_back( Point(l[i - 1].getX() + dx * curd, l[i - 1].getY() + dy * curd)); curd += d; } ret.push_back(l[i]); } return ret; } // _____________________________________________________________________________ template inline double frechetDistC(size_t i, size_t j, const Line& p, const Line& q, std::vector>& ca) { // based on Eiter / Mannila // http://www.kr.tuwien.ac.at/staff/eiter/et-archive/cdtr9464.pdf if (ca[i][j] > -1) return ca[i][j]; else if (i == 0 && j == 0) ca[i][j] = dist(p[0], q[0]); else if (i > 0 && j == 0) ca[i][j] = std::max(frechetDistC(i - 1, 0, p, q, ca), dist(p[i], q[0])); else if (i == 0 && j > 0) ca[i][j] = std::max(frechetDistC(0, j - 1, p, q, ca), dist(p[0], q[j])); else if (i > 0 && j > 0) ca[i][j] = std::max(std::min(std::min(frechetDistC(i - 1, j, p, q, ca), frechetDistC(i - 1, j - 1, p, q, ca)), frechetDistC(i, j - 1, p, q, ca)), dist(p[i], q[j])); else ca[i][j] = std::numeric_limits::infinity(); return ca[i][j]; } // _____________________________________________________________________________ template inline double frechetDist(const Line& a, const Line& b, double d) { // based on Eiter / Mannila // http://www.kr.tuwien.ac.at/staff/eiter/et-archive/cdtr9464.pdf auto p = densify(a, d); auto q = densify(b, d); std::vector> ca(p.size(), std::vector(q.size(), -1.0)); double fd = frechetDistC(p.size() - 1, q.size() - 1, p, q, ca); return fd; } // _____________________________________________________________________________ template inline double accFrechetDistC(const Line& a, const Line& b, double d) { auto p = densify(a, d); auto q = densify(b, d); std::vector> ca(p.size(), std::vector(q.size(), 0)); for (size_t i = 0; i < p.size(); i++) ca[i][0] = std::numeric_limits::infinity(); for (size_t j = 0; j < q.size(); j++) ca[0][j] = std::numeric_limits::infinity(); ca[0][0] = 0; for (size_t i = 1; i < p.size(); i++) { for (size_t j = 1; j < q.size(); j++) { double d = util::geo::dist(p[i], q[j]) * util::geo::dist(p[i], p[i - 1]); ca[i][j] = d + std::min(ca[i - 1][j], std::min(ca[i][j - 1], ca[i - 1][j - 1])); } } return ca[p.size() - 1][q.size() - 1]; } // _____________________________________________________________________________ template inline Point latLngToWebMerc(double lat, double lng) { double x = 6378137.0 * lng * 0.017453292519943295; double a = lat * 0.017453292519943295; double y = 3189068.5 * log((1.0 + sin(a)) / (1.0 - sin(a))); return Point(x, y); } // _____________________________________________________________________________ template inline Point webMercToLatLng(double x, double y) { double lat = 114.591559026 * (atan(exp(y / 6378137.0)) - 0.78539825); double lon = x / 111319.4907932735677; return Point(lon, lat); } // _____________________________________________________________________________ template inline double webMercMeterDist(const G1& a, const G2& b) { // euclidean distance on web mercator is in meters on equator, // and proportional to cos(lat) in both y directions double latA = 2 * atan(exp(a.getY() / 6378137.0)) - 1.5707965; double latB = 2 * atan(exp(b.getY() / 6378137.0)) - 1.5707965; return util::geo::dist(a, b) * cos((latA + latB) / 2.0); } } } #endif // UTIL_GEO_GEON_H_