/********************************************************************** * * GEOS - Geometry Engine Open Source * http://geos.osgeo.org * * Copyright (C) 2009 2011 Sandro Santilli * Copyright (C) 2005-2006 Refractions Research Inc. * Copyright (C) 2001-2002 Vivid Solutions Inc. * * This is free software; you can redistribute and/or modify it under * the terms of the GNU Lesser General Public Licence as published * by the Free Software Foundation. * See the COPYING file for more information. * ********************************************************************** * * Last port: geom/LineSegment.java r18 (JTS-1.11) * **********************************************************************/ #pragma once #include #include // for composition #include #include #include #include #include // for ostream #include // for std::hash #include // for unique_ptr #include #include // Forward declarations namespace geos { namespace geom { class CoordinateSequence; class GeometryFactory; class LineString; } } namespace geos { namespace geom { // geos::geom /** * Represents a line segment defined by two Coordinate. * Provides methods to compute various geometric properties * and relationships of line segments. * * This class is designed to be easily mutable (to the extent of * having its contained points public). * This supports a common pattern of reusing a single LineSegment * object as a way of computing segment properties on the * segments defined by arrays or lists of {@link Coordinate}s. * * TODO: have this class keep pointers rather then real Coordinates ? */ class GEOS_DLL LineSegment { public: Coordinate p0; /// Segment start Coordinate p1; /// Segment end LineSegment(const Coordinate& c0, const Coordinate& c1) : p0(c0) , p1(c1) {}; LineSegment(double x0, double y0, double x1, double y1) : p0(x0, y0) , p1(x1, y1) {}; LineSegment() {}; void setCoordinates(const Coordinate& c0, const Coordinate& c1) { p0 = c0; p1 = c1; }; void setCoordinates(const LineSegment& ls) { setCoordinates(ls.p0, ls.p1); }; // obsoleted, use operator[] instead //const Coordinate& getCoordinate(std::size_t i) const; const Coordinate& operator[](std::size_t i) const { if(i == 0) { return p0; } assert(i == 1); return p1; }; Coordinate& operator[](std::size_t i) { if(i == 0) { return p0; } assert(i == 1); return p1; }; /// gets the minimum X ordinate value double minX() const { return std::min(p0.x, p1.x); }; /// gets the maximum X ordinate value double maxX() const { return std::max(p0.x, p1.x); }; /// gets the minimum Y ordinate value double minY() const { return std::min(p0.y, p1.y); }; /// gets the maximum Y ordinate value double maxY() const { return std::max(p0.y, p1.y); }; /// Computes the length of the line segment. double getLength() const { return p0.distance(p1); }; /// Tests whether the segment is horizontal. /// /// @return true if the segment is horizontal /// bool isHorizontal() const { return p0.y == p1.y; }; /// Tests whether the segment is vertical. /// /// @return true if the segment is vertical /// bool isVertical() const { return p0.x == p1.x; }; /** * Determines the orientation of a LineSegment relative to this segment. * The concept of orientation is specified as follows: * Given two line segments A and L, *
    *
  • A is to the left of a segment L if A lies wholly in the * closed half-plane lying to the left of L *
  • A is to the right of a segment L if A lies wholly in the * closed half-plane lying to the right of L *
  • otherwise, A has indeterminate orientation relative to L. * This happens if A is collinear with L or if A crosses * the line determined by L. *
* * @param seg the LineSegment to compare * * @return 1 if seg is to the left of this segment * @return -1 if seg is to the right of this segment * @return 0 if seg has indeterminate orientation relative * to this segment */ int orientationIndex(const LineSegment& seg) const; // TODO deprecate this int orientationIndex(const LineSegment* seg) const { assert(seg); return orientationIndex(*seg); }; /** * Determines the orientation index of a Coordinate relative to this segment. * The orientation index is as defined in Orientation::index(Coordinate, Coordinate, Coordinate). * * @param p the coordinate to compare * * @return 1 (LEFT) if "p" is to the left of this segment * @return -1 (RIGHT) if "p" is to the right of this segment * @return 0 (COLLINEAR) if "p" is collinear with this segment * */ int orientationIndex(const CoordinateXY& p) const { return algorithm::Orientation::index(p0, p1, p); } /** \brief * Determines the orientation index of a Coordinate * relative to this segment. * * The orientation index is as defined in * Orientation::index. * * @param p the Coordinate to compare * * @return 1 if p is to the left of this segment * @return -1 if p is to the right of this segment * @return 0 if p is collinear with this segment * * @see Orientation::index(Coordinate, Coordinate, * Coordinate) */ int orientationIndex(const Coordinate& p) const { return algorithm::Orientation::index(p0, p1, p); }; /// Reverses the direction of the line segment. void reverse(); /// Puts the line segment into a normalized form. // /// This is useful for using line segments in maps and indexes when /// topological equality rather than exact equality is desired. /// void normalize() { if(p1.compareTo(p0) < 0) { reverse(); } }; /// @return the angle this segment makes with the x-axis (in radians) double angle() const { return std::atan2(p1.y - p0.y, p1.x - p0.x); }; /// Computes the midpoint of the segment // /// @param ret will be set to the midpoint of the segment /// void midPoint(Coordinate& ret) const { ret = Coordinate( (p0.x + p1.x) / 2, (p0.y + p1.y) / 2); }; /// Computes the distance between this line segment and another one. double distance(const LineSegment& ls) const { return algorithm::Distance::segmentToSegment(p0, p1, ls.p0, ls.p1); }; /// Computes the distance between this line segment and a point. double distance(const CoordinateXY& p) const { return algorithm::Distance::pointToSegment(p, p0, p1); }; /** * Computes the perpendicular distance between the (infinite) * line defined by this line segment and a point. * If the segment has zero length this returns the distance between * the segment and the point. * * @param p the point to compute the distance to * @return the perpendicular distance between the line and point */ double distancePerpendicular(const CoordinateXY& p) const { if (p0.equals2D(p1)) return p0.distance(p); return algorithm::Distance::pointToLinePerpendicular(p, p0, p1); }; /** * Computes the oriented perpendicular distance between the (infinite) line * defined by this line segment and a point. * The oriented distance is positive if the point on the left of the line, * and negative if it is on the right. * If the segment has zero length this returns the distance between * the segment and the point. * * @param p the point to compute the distance to * @return the oriented perpendicular distance between the line and point */ double distancePerpendicularOriented(const CoordinateXY& p) const; /** \brief * Computes the Coordinate that lies a given * fraction along the line defined by this segment. * * A fraction of 0.0 returns the start point of * the segment; a fraction of 1.0 returns the end * point of the segment. * If the fraction is < 0.0 or > 1.0 the point returned * will lie before the start or beyond the end of the segment. * * @param segmentLengthFraction the fraction of the segment length * along the line * @param ret will be set to the point at that distance */ void pointAlong(double segmentLengthFraction, Coordinate& ret) const { ret = Coordinate( p0.x + segmentLengthFraction * (p1.x - p0.x), p0.y + segmentLengthFraction * (p1.y - p0.y)); }; /** \brief * Computes the {@link Coordinate} that lies a given * fraction along the line defined by this segment and offset from * the segment by a given distance. * * A fraction of 0.0 offsets * from the start point of the segment; * a fraction of 1.0 offsets * from the end point of the segment. * * The computed point is offset to the left of the line * if the offset distance is positive, to the right if negative. * * @param segmentLengthFraction the fraction of the segment * length along the line * * @param offsetDistance the distance the point is offset * from the segment * (positive is to the left, negative is to the right) * * @param ret will be set to the point at that distance and offset * * @throws IllegalStateException if the segment has zero length */ void pointAlongOffset(double segmentLengthFraction, double offsetDistance, Coordinate& ret) const; /** * Computes the {@link LineSegment} that is offset from * the segment by a given distance. * The computed segment is offset to the left of the line if the offset distance is * positive, to the right if negative. * * @param offsetDistance the distance the point is offset from the segment * (positive is to the left, negative is to the right) * @return a line segment offset by the specified distance * * @throws IllegalStateException if the segment has zero length */ LineSegment offset(double offsetDistance); /** \brief * Compute the projection factor for the projection of the point p * onto this LineSegment. * * The projection factor is the constant r * by which the vector for this segment must be multiplied to * equal the vector for the projection of p on the line * defined by this segment. * * The projection factor returned will be in the range * (-inf, +inf) * * @param p the point to compute the factor for * * @return the projection factor for the point * */ double projectionFactor(const CoordinateXY& p) const; /** \brief * Computes the fraction of distance (in [0.0, 1.0]) * that the projection of a point occurs along this line segment. * * If the point is beyond either ends of the line segment, * the closest fractional value (0.0 or 1.0) * is returned. * * Essentially, this is the {@link #projectionFactor} clamped to * the range [0.0, 1.0]. * * @param inputPt the point * @return the fraction along the line segment the projection * of the point occurs */ double segmentFraction(const CoordinateXY& inputPt) const; /** \brief * Compute the projection of a point onto the line determined * by this line segment. * * Note that the projected point * may lie outside the line segment. If this is the case, * the projection factor will lie outside the range [0.0, 1.0]. */ void project(const Coordinate& p, Coordinate& ret) const; CoordinateXY project(const CoordinateXY& p) const; /** \brief * Project a line segment onto this line segment and return the resulting * line segment. * * The returned line segment will be a subset of * the target line line segment. This subset may be null, if * the segments are oriented in such a way that there is no projection. * * Note that the returned line may have zero length (i.e. the same endpoints). * This can happen for instance if the lines are perpendicular to one another. * * @param seg the line segment to project * @param ret the projected line segment * @return true if there is an overlap, false otherwise */ bool project(const LineSegment& seg, LineSegment& ret) const; /// Computes the closest point on this line segment to another point. // /// @param p the point to find the closest point to /// @param ret the Coordinate to which the closest point on the line segment /// to the point p will be written /// void closestPoint(const CoordinateXY& p, CoordinateXY& ret) const; /** \brief * Returns true if other is * topologically equal to this LineSegment (e.g. irrespective * of orientation). * * @param other a LineSegment with which to do the comparison. * @return true if other is a LineSegment * with the same values for the x and y ordinates. */ bool equalsTopo(const LineSegment& other) const; /** * Computes the closest points on two line segments. * @param line the line segment to find the closest points to * @return a pair of Coordinates which are the closest points on * the line segments. */ std::array closestPoints(const LineSegment& line); std::array closestPoints(const LineSegment* line) { assert(line); return closestPoints(*line); } /** * Computes an intersection point between two segments, * if there is one. * There may be 0, 1 or many intersection points between two segments. * If there are 0, null is returned. If there is 1 or more, a single * one is returned (chosen at the discretion of the algorithm). * If more information is required about the details of the * intersection, the LineIntersector class should be used. * * @param line * @return intersection if found, setNull() otherwise */ Coordinate intersection(const LineSegment& line) const; /** \brief * Computes the intersection point of the lines defined * by two segments, if there is one. * * There may be 0, 1 or an infinite number of intersection points * between two lines. * If there is a unique intersection point, it is returned. * Otherwise, null is returned. * If more information is required about the details of the * intersection, the algorithms::LineIntersector class should * be used. * * @param line a line segment defining a straight line * @return intersection if found, setNull() otherwise * */ Coordinate lineIntersection(const LineSegment& line) const; /** * Creates a LineString with the same coordinates as this segment * * @param gf the geometery factory to use * @return a LineString with the same geometry as this segment */ std::unique_ptr toGeometry(const GeometryFactory& gf) const; /** \brief * Compares this object with the specified object for order. * * Uses the standard lexicographic ordering for the points in the LineSegment. * * @param other the LineSegment with which this LineSegment * is being compared * @return a negative integer, zero, or a positive integer as this * LineSegment is less than, equal to, or greater than the * specified LineSegment */ inline int compareTo(const LineSegment& other) const { int comp0 = p0.compareTo(other.p0); if (comp0 != 0) { return comp0; } return p1.compareTo(other.p1); } std::ostream& operator<< (std::ostream& o); inline bool operator==(const LineSegment& rhs) const { return compareTo(rhs) == 0; }; inline bool operator<(const LineSegment& rhs) const { return compareTo(rhs) < 0; }; inline bool operator>(const LineSegment& rhs) const { return compareTo(rhs) > 0; }; struct HashCode { inline std::size_t operator()(const LineSegment & s) const { std::size_t h = std::hash{}(s.p0.x); h ^= (std::hash{}(s.p0.y) << 1); h ^= (std::hash{}(s.p1.x) << 1); return h ^ (std::hash{}(s.p1.y) << 1); } inline std::size_t operator()(const LineSegment * s) const { std::size_t h = std::hash{}(s->p0.x); h ^= (std::hash{}(s->p0.y) << 1); h ^= (std::hash{}(s->p1.x) << 1); return h ^ (std::hash{}(s->p1.y) << 1); } }; using UnorderedSet = std::unordered_set; private: void project(double factor, CoordinateXY& ret) const; }; // std::ostream& operator<< (std::ostream& o, const LineSegment& l); } // namespace geos::geom } // namespace geos