/**
  * 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;
 }
Пример #2
0
 /**
  * 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);
 }
Пример #3
0
 public static function initLookupCell($level, $i, $j, $origOrientation, $pos, $orientation)
 {
     if ($level == self::LOOKUP_BITS) {
         $ij = ($i << self::LOOKUP_BITS) + $j;
         self::$LOOKUP_POS[($ij << 2) + $origOrientation] = ($pos << 2) + $orientation;
         self::$LOOKUP_IJ[($pos << 2) + $origOrientation] = ($ij << 2) + $orientation;
     } else {
         $level++;
         $i <<= 1;
         $j <<= 1;
         $pos <<= 2;
         // Initialize each sub-cell recursively.
         for ($subPos = 0; $subPos < 4; $subPos++) {
             $ij = S2::posToIJ($orientation, $subPos);
             $orientationMask = S2::posToOrientation($subPos);
             self::initLookupCell($level, $i + ($ij >> 1 & PHP_INT_MAX >> 0), $j + ($ij & 1), $origOrientation, $pos + $subPos, $orientation ^ $orientationMask);
             /* >>> */
         }
     }
 }
 /**
  * Return the inward-facing normal of the great circle passing through the
  * edge from vertex k to vertex k+1 (mod 4). The normals returned by
  * GetEdgeRaw are not necessarily unit length.
  *
  *  If this is not a leaf cell, set children[0..3] to the four children of
  * this cell (in traversal order) and return true. Otherwise returns false.
  * This method is equivalent to the following:
  *
  *  for (pos=0, id=child_begin(); id != child_end(); id = id.next(), ++pos)
  * children[i] = S2Cell(id);
  *
  * except that it is more than two times faster.
  * @param S2Cell[] $children
  */
 public function subdivide(&$children)
 {
     // This function is equivalent to just iterating over the child cell ids
     // and calling the S2Cell constructor, but it is about 2.5 times faster.
     if ($this->cellId->isLeaf()) {
         return false;
     }
     // Compute the cell midpoint in uv-space.
     $uvMid = $this->getCenterUV();
     // Create four children with the appropriate bounds.
     /** @var S2CellId $id */
     $id = $this->cellId->childBegin();
     for ($pos = 0; $pos < 4; ++$pos, $id = $id->next()) {
         $child =& $children[$pos];
         $child->face = $this->face;
         $child->level = $this->level + 1;
         $new_o = S2::posToOrientation($pos);
         $child->orientation = $this->orientation ^ $new_o;
         //        echo "this-ori:" . $this->orientation . " new_o:" . $new_o . " res:" . $child->orientation . "\n";
         $child->cellId = $id;
         $ij = S2::posToIJ($this->orientation, $pos);
         for ($d = 0; $d < 2; ++$d) {
             // The dimension 0 index (i/u) is in bit 1 of ij.
             $m = 1 - ($ij >> 1 - $d & 1);
             $child->uv[$d][$m] = $uvMid->get($d);
             $child->uv[$d][1 - $m] = $this->uv[$d][1 - $m];
         }
     }
     return true;
 }
Пример #5
0
 /**
  * Return the maximum level such that the metric is at least the given
  * value, or zero if there is no such level. For example,
  * S2.kMinWidth.GetMaxLevel(0.1) returns the maximum level such that all
  * cells have a minimum width of 0.1 or larger. The return value is always a
  * valid level.
  */
 public function getMaxLevel($value)
 {
     if ($value <= 0) {
         return S2CellId::MAX_LEVEL;
     }
     // This code is equivalent to computing a floating-point "level"
     // value and rounding down.
     $exponent = S2::exp((1 << $this->dim) * $this->deriv / $value);
     $level = max(0, min(S2CellId::MAX_LEVEL, $exponent - 1 >> $this->dim - 1));
     // assert (level == 0 || getValue(level) >= value);
     // assert (level == S2CellId.MAX_LEVEL || getValue(level + 1) < value);
     return $level;
 }
 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]));
 }