/** * Return the smallest rectangle containing the intersection of this rectangle * and the given rectangle. Note that the region of intersection may consist * of two disjoint rectangles, in which case a single rectangle spanning both * of them is returned. *#/ * public S2LatLngRect intersection(S2LatLngRect other) { * R1Interval intersectLat = lat.intersection(other.lat); * S1Interval intersectLng = lng.intersection(other.lng); * if (intersectLat.isEmpty() || intersectLng.isEmpty()) { * // The lat/lng ranges must either be both empty or both non-empty. * return empty(); * } * return new S2LatLngRect(intersectLat, intersectLng); * } * * /** * Return a rectangle that contains the convolution of this rectangle with a * cap of the given angle. This expands the rectangle by a fixed distance (as * opposed to growing the rectangle in latitude-longitude space). The returned * rectangle includes all points whose minimum distance to the original * rectangle is at most the given angle. *#/ * public S2LatLngRect convolveWithCap(S1Angle angle) { * // The most straightforward approach is to build a cap centered on each * // vertex and take the union of all the bounding rectangles (including the * // original rectangle; this is necessary for very large rectangles). * * // Optimization: convert the angle to a height exactly once. * S2Cap cap = S2Cap.fromAxisAngle(new S2Point(1, 0, 0), angle); * * S2LatLngRect r = this; * for (int k = 0; k < 4; ++k) { * S2Cap vertexCap = S2Cap.fromAxisHeight(getVertex(k).toPoint(), cap * .height()); * r = r.union(vertexCap.getRectBound()); * } * return r; * } * * /** Return the surface area of this rectangle on the unit sphere. *#/ * public double area() { * if (isEmpty()) { * return 0; * } * * // This is the size difference of the two spherical caps, multiplied by * // the longitude ratio. * return lng().getLength() * Math.abs(Math.sin(latHi().radians()) - Math.sin(latLo().radians())); * } * * /** Return true if two rectangles contains the same set of points. *#/ * @Override * public boolean equals(Object that) { * if (!(that instanceof S2LatLngRect)) { * return false; * } * S2LatLngRect otherRect = (S2LatLngRect) that; * return lat().equals(otherRect.lat()) && lng().equals(otherRect.lng()); * } * * /** * Return true if the latitude and longitude intervals of the two rectangles * are the same up to the given tolerance (see r1interval.h and s1interval.h * for details). *#/ * public boolean approxEquals(S2LatLngRect other, double maxError) { * return (lat.approxEquals(other.lat, maxError) && lng.approxEquals( * other.lng, maxError)); * } * * public boolean approxEquals(S2LatLngRect other) { * return approxEquals(other, 1e-15); * } * * @Override * public int hashCode() { * int value = 17; * value = 37 * value + lat.hashCode(); * return (37 * value + lng.hashCode()); * } * * // ////////////////////////////////////////////////////////////////////// * // S2Region interface (see {@code S2Region} for details): * * @Override * public S2Region clone() { * return new S2LatLngRect(this.lo(), this.hi()); * } */ public function getCapBound() { // We consider two possible bounding caps, one whose axis passes // through the center of the lat-long rectangle and one whose axis // is the north or south pole. We return the smaller of the two caps. if ($this->isEmpty()) { echo __METHOD__ . " empty\n"; return S2Cap::sempty(); } $poleZ = null; $poleAngle = null; if ($this->lat->lo() + $this->lat->hi() < 0) { // South pole axis yields smaller cap. $poleZ = -1; $poleAngle = S2::M_PI_2 + $this->lat->hi(); } else { $poleZ = 1; $poleAngle = S2::M_PI_2 - $this->lat->lo(); } $poleCap = S2Cap::fromAxisAngle(new S2Point(0, 0, $poleZ), S1Angle::sradians($poleAngle)); // For bounding rectangles that span 180 degrees or less in longitude, the // maximum cap size is achieved at one of the rectangle vertices. For // rectangles that are larger than 180 degrees, we punt and always return a // bounding cap centered at one of the two poles. $lngSpan = $this->lng->hi() - $this->lng->lo(); if (S2::IEEEremainder($lngSpan, 2 * S2::M_PI) >= 0) { if ($lngSpan < 2 * S2::M_PI) { $midCap = S2Cap::fromAxisAngle($this->getCenter()->toPoint(), S1Angle::sradians(0)); for ($k = 0; $k < 4; ++$k) { $midCap = $midCap->addPoint($this->getVertex($k)->toPoint()); } if ($midCap->height() < $poleCap->height()) { return $midCap; } } } return $poleCap; }
/** * Return true if the interior of this interval contains any point of the * interval 'y' (including its boundary). Works for empty, full, and singleton * intervals. *#/ * public boolean interiorIntersects(final S1Interval y) { * if (isEmpty() || y.isEmpty() || lo() == hi()) { * return false; * } * if (isInverted()) { * return y.isInverted() || y.lo() < hi() || y.hi() > lo(); * } else { * if (y.isInverted()) { * return y.lo() < hi() || y.hi() > lo(); * } * return (y.lo() < hi() && y.hi() > lo()) || isFull(); * } * } * * /** * Expand the interval by the minimum amount necessary so that it contains the * given point "p" (an angle in the range [-Pi, Pi]). *#/ * public S1Interval addPoint(double p) { * // assert (Math.abs(p) <= S2.M_PI); * if (p == -S2.M_PI) { * p = S2.M_PI; * } * * if (fastContains(p)) { * return new S1Interval(this); * } * * if (isEmpty()) { * return S1Interval.fromPoint(p); * } else { * // Compute distance from p to each endpoint. * double dlo = positiveDistance(p, lo()); * double dhi = positiveDistance(hi(), p); * if (dlo < dhi) { * return new S1Interval(p, hi()); * } else { * return new S1Interval(lo(), p); * } * // Adding a point can never turn a non-full interval into a full one. * } * } * * /** * Return an interval that contains all points within a distance "radius" of * a point in this interval. Note that the expansion of an empty interval is * always empty. The radius must be non-negative. */ public function expanded($radius) { // assert (radius >= 0); if ($this->isEmpty()) { return $this; } // Check whether this interval will be full after expansion, allowing // for a 1-bit rounding error when computing each endpoint. if ($this->getLength() + 2 * $radius >= 2 * S2::M_PI - 1.0E-15) { return self::full(); } // NOTE(dbeaumont): Should this remainder be 2 * M_PI or just M_PI ?? // double lo = Math.IEEEremainder(lo() - radius, 2 * S2.M_PI); $lo = S2::IEEEremainder($this->lo() - $radius, 2 * S2::M_PI); $hi = S2::IEEEremainder($this->hi() + $radius, 2 * S2::M_PI); if ($lo == -S2::M_PI) { $lo = S2::M_PI; } return new S1Interval($lo, $hi); }
public function getRectBound() { if ($this->isEmpty()) { return S2LatLngRect::emptya(); } // Convert the axis to a (lat,lng) pair, and compute the cap angle. $axisLatLng = new S2LatLng($this->axis); $capAngle = $this->angle()->radians(); $allLongitudes = false; $lat = array(); $lng = array(); $lng[0] = -S2::M_PI; $lng[1] = S2::M_PI; // Check whether cap includes the south pole. $lat[0] = $axisLatLng->lat()->radians() - $capAngle; if ($lat[0] <= -S2::M_PI_2) { $lat[0] = -S2::M_PI_2; $allLongitudes = true; } // Check whether cap includes the north pole. $lat[1] = $axisLatLng->lat()->radians() + $capAngle; if ($lat[1] >= S2::M_PI_2) { $lat[1] = S2::M_PI_2; $allLongitudes = true; } if (!$allLongitudes) { // Compute the range of longitudes covered by the cap. We use the law // of sines for spherical triangles. Consider the triangle ABC where // A is the north pole, B is the center of the cap, and C is the point // of tangency between the cap boundary and a line of longitude. Then // C is a right angle, and letting a,b,c denote the sides opposite A,B,C, // we have sin(a)/sin(A) = sin(c)/sin(C), or sin(A) = sin(a)/sin(c). // Here "a" is the cap angle, and "c" is the colatitude (90 degrees // minus the latitude). This formula also works for negative latitudes. // // The formula for sin(a) follows from the relationship h = 1 - cos(a). $sinA = sqrt($this->height * (2 - $this->height)); $sinC = cos($axisLatLng->lat()->radians()); if ($sinA <= $sinC) { $angleA = asin($sinA / $sinC); $lng[0] = S2::IEEEremainder($axisLatLng->lng()->radians() - $angleA, 2 * S2::M_PI); $lng[1] = S2::IEEEremainder($axisLatLng->lng()->radians() + $angleA, 2 * S2::M_PI); } } return new S2LatLngRect(new R1Interval($lat[0], $lat[1]), new S1Interval($lng[0], $lng[1])); }