text: /**********************************************************************
   *
   * GEOS - Geometry Engine Open Source
   * http://geos.osgeo.org
   *
   * Copyright (C) 2006 Refractions Research 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/Envelope.java rev 1.46 (JTS-1.10)
   *
   **********************************************************************/
  
  #ifndef GEOS_GEOM_ENVELOPE_H
  #define GEOS_GEOM_ENVELOPE_H
  
  
  #include <geos/export.h>
  #include <geos/inline.h>
  #include <geos/geom/Coordinate.h>
  
  #include <string>
  #include <vector>
  #include <ostream> // for operator<<
  #include <memory>
  
  namespace geos {
  namespace geom { // geos::geom
  
  class Envelope;
  
  /// Output operator
  GEOS_DLL std::ostream& operator<< (std::ostream& os, const Envelope& o);
  
  class Coordinate;
  
  /**
   * \class Envelope geom.h geos.h
   *
   * \brief
   * An Envelope defines a rectangulare region of the 2D coordinate plane.
   *
   * It is often used to represent the bounding box of a Geometry,
   * e.g. the minimum and maximum x and y values of the Coordinates.
   *
   * Note that Envelopes support infinite or half-infinite regions, by using
   * the values of `Double_POSITIVE_INFINITY` and `Double_NEGATIVE_INFINITY`.
   *
   * When Envelope objects are created or initialized, the supplies extent
   * values are automatically sorted into the correct order.
   *
   */
  class GEOS_DLL Envelope {
  
  public:
  
      friend std::ostream& operator<< (std::ostream& os, const Envelope& o);
  
      typedef std::unique_ptr<Envelope> Ptr;
  
      /** \brief
       * Creates a null Envelope.
       */
      Envelope();
  
      /** \brief
       * Creates an Envelope for a region defined by maximum and minimum values.
       *
       * @param  x1  the first x-value
       * @param  x2  the second x-value
       * @param  y1  the first y-value
       * @param  y2  the second y-value
       */
      Envelope(double x1, double x2, double y1, double y2);
  
      /** \brief
       * Creates an Envelope for a region defined by two Coordinates.
       *
       * @param  p1  the first Coordinate
       * @param  p2  the second Coordinate
       */
      Envelope(const Coordinate& p1, const Coordinate& p2);
  
      /** \brief
       * Creates an Envelope for a region defined by a single Coordinate.
       *
       * @param  p  the Coordinate
       */
      explicit Envelope(const Coordinate& p);
  
      /** \brief
       * Create an Envelope from an Envelope string representation produced
       * by Envelope::toString()
       */
      explicit Envelope(const std::string& str);
  
      /** \brief
       * Test the point `q` to see whether it intersects the Envelope
       * defined by `p1-p2`.
       *
       * @param p1 one extremal point of the envelope
       * @param p2 another extremal point of the envelope
       * @param q the point to test for intersection
       * @return `true` if q intersects the envelope p1-p2
       */
      static bool intersects(const Coordinate& p1, const Coordinate& p2,
                             const Coordinate& q);
  
      /** \brief
       * Test the envelope defined by `p1-p2` for intersection
       * with the envelope defined by `q1-q2`.
       *
       * @param p1 one extremal point of the envelope P
       * @param p2 another extremal point of the envelope P
       * @param q1 one extremal point of the envelope Q
       * @param q2 another extremal point of the envelope Q
       *
       * @return `true` if Q intersects P
       */
      static bool intersects(const Coordinate& p1, const Coordinate& p2,
                             const Coordinate& q1, const Coordinate& q2);
  
      /** \brief
       * Check if the extent defined by two extremal points intersects
       * the extent of this Envelope.
       *
       * @param a a point
       * @param b another point
       * @return `true` if the extents intersect
       */
      bool intersects(const Coordinate& a, const Coordinate& b) const;
  
      /** \brief
       *  Initialize to a null Envelope.
       */
      void init();
  
      /** \brief
       * Initialize an Envelope for a region defined by maximum and minimum values.
       *
       * @param  x1  the first x-value
       * @param  x2  the second x-value
       * @param  y1  the first y-value
       * @param  y2  the second y-value
       */
      void init(double x1, double x2, double y1, double y2);
  
      /** \brief
       * Initialize an Envelope to a region defined by two Coordinates.
       *
       * @param  p1  the first Coordinate
       * @param  p2  the second Coordinate
       */
      void init(const Coordinate& p1, const Coordinate& p2);
  
      /** \brief
       * Initialize an Envelope to a region defined by a single Coordinate.
       *
       * @param  p  the Coordinate
       */
      void init(const Coordinate& p);
  
      // use assignment operator instead
      //void init(Envelope env);
  
      /** \brief
       * Makes this `Envelope` a "null" envelope, that is, the envelope
       * of the empty geometry.
       */
      void setToNull();
  
      /** \brief
       * Returns `true` if this Envelope is a "null" envelope.
       *
       * @return `true` if this Envelope is uninitialized or is the
       *                envelope of the empty geometry.
       */
      bool isNull() const;
  
      /** \brief
       * Returns the difference between the maximum and minimum x values.
       *
       * @return  `max x - min x`, or 0 if this is a null Envelope
       */
      double getWidth() const;
  
      /** \brief
       * Returns the difference between the maximum and minimum y values.
       *
       * @return `max y - min y`, or 0 if this is a null Envelope
       */
      double getHeight() const;
  
      /** \brief
       * Gets the area of this envelope.
       *
       * @return the area of the envelope
       * @return 0.0 if the envelope is null
       */
      double
      getArea() const
      {
          return getWidth() * getHeight();
      }
  
      /** \brief
       * Returns the Envelope maximum y-value. `min y > max y` indicates
       * that this is a null Envelope.
       */
      double getMaxY() const;
  
      /** \brief
       * Returns the Envelope maximum x-value. `min x > max x` indicates
       * that this is a null Envelope.
       */
      double getMaxX() const;
  
      /** \brief
       * Returns the Envelope minimum y-value. `min y > max y` indicates
       * that this is a null Envelope.
       */
      double getMinY() const;
  
      /** \brief
       * Returns the Envelope minimum x-value. `min x > max x` indicates
       * that this is a null Envelope.
       */
      double getMinX() const;
  
      /** \brief
       * Computes the coordinate of the centre of this envelope
       * (as long as it is non-null).
       *
       * @param centre The coordinate to write results into
       * @return `false` if the center could not be found (null envelope).
       */
      bool centre(Coordinate& centre) const;
  
      /** \brief
       * Computes the intersection of two [Envelopes](@ref Envelope).
       *
       * @param env the envelope to intersect with
       * @param result the envelope representing the intersection of
       *               the envelopes (this will be the null envelope
       *               if either argument is null, or they do not intersect)
       * @return false if not intersection is found
       */
      bool intersection(const Envelope& env, Envelope& result) const;
  
      /** \brief
       * Translates this envelope by given amounts in the X and Y direction.
       *
       * @param transX the amount to translate along the X axis
       * @param transY the amount to translate along the Y axis
       */
      void translate(double transX, double transY);
  
      /** \brief
       * Expands this envelope by a given distance in all directions.
       * Both positive and negative distances are supported.
       *
       * @param deltaX the distance to expand the envelope along the X axis
       * @param deltaY the distance to expand the envelope along the Y axis
       */
      void expandBy(double deltaX, double deltaY);
  
      /** \brief
       * Expands this envelope by a given distance in all directions.
       *
       * Both positive and negative distances are supported.
       *
       * @param p_distance the distance to expand the envelope
       */
      void
      expandBy(double p_distance)
      {
          expandBy(p_distance, p_distance);
      }
  
  
      /** \brief
       * Enlarges the boundary of the Envelope so that it contains p. Does
       * nothing if p is already on or within the boundaries.
       *
       * @param  p the Coordinate to include
       */
      void expandToInclude(const Coordinate& p);
  
      /** \brief
       * Enlarges the boundary of the Envelope so that it contains (x,y).
       *
       * Does nothing if (x,y) is already on or within the boundaries.
       *
       * @param  x  the value to lower the minimum x
       *            to or to raise the maximum x to
       * @param  y  the value to lower the minimum y
       *            to or to raise the maximum y to
       */
      void expandToInclude(double x, double y);
  
      /** \brief
       * Enlarges the boundary of the Envelope so that it contains `other`.
       *
       * Does nothing if other is wholly on or within the boundaries.
       *
       * @param other the Envelope to merge with
       */
      void expandToInclude(const Envelope* other);
      void expandToInclude(const Envelope& other);
  
      /** \brief
       * Tests if the Envelope `other` lies wholly inside this Envelope
       * (inclusive of the boundary).
       *
       * Note that this is **not** the same definition as the SFS `contains`,
       * which would exclude the envelope boundary.
       *
       * @param other the Envelope to check
       * @return `true` if `other` is contained in this Envelope
       *
       * @see covers(Envelope)
       */
      bool
      contains(const Envelope& other) const
      {
          return covers(other);
      }
  
      bool
      contains(const Envelope* other) const
      {
          return contains(*other);
      }
  
      /** \brief
       * Returns `true` if the given point lies in or on the envelope.
       *
       * @param p the point which this Envelope is being checked for containing
       * @return `true` if the point lies in the interior or on the boundary
       *         of this Envelope.
       */
      bool
      contains(const Coordinate& p) const
      {
          return covers(p.x, p.y);
      }
  
      /** \brief
       * Returns `true` if the given point lies in or on the envelope.
       *
       * @param x the x-coordinate of the point which this Envelope is
       *          being checked for containing
       * @param y the y-coordinate of the point which this Envelope is being
       *          checked for containing
       * @return `true` if `(x, y)` lies in the interior or on the boundary
       *         of this Envelope.
       */
      bool
      contains(double x, double y) const
      {
          return covers(x, y);
      }
  
      /** \brief
       * Check if the point p intersects (lies inside) the region of this Envelope.
       *
       * @param  p the Coordinate to be tested
       * @return true if the point intersects this Envelope
       */
      bool intersects(const Coordinate& p) const;
  
      /** \brief
       *  Check if the point (x, y) intersects (lies inside) the region of this Envelope.
       *
       * @param x the x-ordinate of the point
       * @param y the y-ordinate of the point
       * @return `true` if the point intersects this Envelope
       */
      bool intersects(double x, double y) const;
  
      /** \brief
       * Check if the region defined by other Envelope intersects the region of this Envelope.
       *
       * @param other the Envelope which this Envelope is being checked for intersection
       * @return true if the Envelopes intersects
       */
      bool intersects(const Envelope* other) const;
  
      bool intersects(const Envelope& other) const;
  
      /**
      * Tests if the region defined by other
      * is disjoint from the region of this Envelope
      *
      * @param other  the Envelope being checked for disjointness
      * @return  true if the Envelopes are disjoint
      */
      bool disjoint(const Envelope* other) const;
  
      bool disjoint(const Envelope& other) const;
  
      /** \brief
       * Tests if the given point lies in or on the envelope.
       *
       * @param x the x-coordinate of the point which this Envelope is being checked for containing
       * @param y the y-coordinate of the point which this Envelope is being checked for containing
       * @return `true` if `(x, y)` lies in the interior or on the boundary of this Envelope.
       */
      bool covers(double x, double y) const;
  
      /** \brief
       * Tests if the given point lies in or on the envelope.
       *
       * @param p the point which this Envelope is being checked for containing
       * @return `true` if the point lies in the interior or on the boundary of this Envelope.
       */
      bool covers(const Coordinate* p) const;
  
      /** \brief
       * Tests if the Envelope `other` lies wholly inside this Envelope (inclusive of the boundary).
       *
       * @param other the Envelope to check
       * @return true if this Envelope covers the `other`
       */
      bool covers(const Envelope& other) const;
  
      bool
      covers(const Envelope* other) const
      {
          return covers(*other);
      }
  
  
      /** \brief
       * Returns `true` if the Envelope `other` spatially equals this Envelope.
       *
       * @param  other the Envelope which this Envelope is being checked for equality
       * @return `true` if this and `other` Envelope objects are spatially equal
       */
      bool equals(const Envelope* other) const;
  
      /** \brief
       * Returns a `string` of the form `Env[minx:maxx,miny:maxy]`.
       *
       * @return a `string` of the form `Env[minx:maxx,miny:maxy]`
       */
      std::string toString() const;
  
      /** \brief
       * Computes the distance between this and another Envelope.
       *
       * The distance between overlapping Envelopes is 0. Otherwise, the
       * distance is the Euclidean distance between the closest points.
       */
      double distance(const Envelope& env) const;
  
      /** \brief
       * Computes the square of the distance between this and another Envelope.
       *
       * The distance between overlapping Envelopes is 0. Otherwise, the
       * distance is the Euclidean distance between the closest points.
       */
      double distanceSquared(const Envelope& env) const;
  
      /** \brief
       * Computes the distance between one Coordinate and an Envelope
       * defined by two other Coordinates. The order of the Coordinates
       * used to define the envelope is not significant.
       *
       * @param c the coordinate to from which distance should be found
       * @param p1 first coordinate defining an envelope
       * @param p2 second coordinate defining an envelope.
       */
      static double distanceToCoordinate(const Coordinate & c, const Coordinate & p1, const Coordinate & p2);
  
      /** \brief
       * Computes the squared distance between one Coordinate and an Envelope
       * defined by two other Coordinates. The order of the Coordinates
       * used to define the envelope is not significant.
       *
       * @param c the coordinate to from which distance should be found
       * @param p1 first coordinate defining an envelope
       * @param p2 second coordinate defining an envelope.
       */
      static double distanceSquaredToCoordinate(const Coordinate & c, const Coordinate & p1, const Coordinate & p2);
  
      std::size_t hashCode() const;
  
  private:
  
      /** \brief
       * Splits a string into parts based on the supplied delimiters.
       *
       * This is a generic function that really belongs in a utility
       * file somewhere
       */
      static std::vector<std::string> split(const std::string& str,
                                     const std::string& delimiters = " ");
  
      static double distance(double x0, double y0, double x1, double y1);
  
      /// the minimum x-coordinate
      double minx;
  
      /// the maximum x-coordinate
      double maxx;
  
      /// the minimum y-coordinate
      double miny;
  
      /// the maximum y-coordinate
      double maxy;
  };
  
  /// Checks if two Envelopes are equal (2D only check)
  GEOS_DLL bool operator==(const Envelope& a, const Envelope& b);
  
  GEOS_DLL bool operator!=(const Envelope& a, const Envelope& b);
  
  /// Strict weak ordering operator for Envelope
  /// This is the C++ equivalent of JTS's compareTo
  GEOS_DLL bool operator< (const Envelope& a, const Envelope& b);
  
  
  } // namespace geos::geom
  } // namespace geos
  
  #ifdef GEOS_INLINE
  # include "geos/geom/Envelope.inl"
  #endif
  
  #endif // ndef GEOS_GEOM_ENVELOPE_H
  