/** * @param double|S2Point $radians_or_x * @param S2Point $y * Return the angle between two points, which is also equal to the distance * between these points on the unit sphere. The points do not need to be * normalized. */ public function __construct($radians_or_x = null, $y = null) { if ($radians_or_x instanceof S2Point && $y instanceof S2Point) { $this->radians = $radians_or_x->angle($y); } else { $this->radians = $radians_or_x === null ? 0 : $radians_or_x; } }
public static function toPoint() { return S2Point::normalize(self::toPointRaw()); }
public static function xyzToFace(S2Point $p) { $face = $p->largestAbsComponent(); if ($p->get($face) < 0) { $face += 3; } return $face; }
public function toString() { return sprintf("Edge: (%s -> %s)\n or [%s -> %s]", $this->start->toDegreesString(), $this->end->toDegreesString(), $this->start, $this->end); }
public static function normalize(S2Point $p) { $norm = $p->norm(); if ($norm != 0) { $norm = 1.0 / $norm; } return S2Point::mul($p, $norm); }
/** * Return the average area for cells at the given level. *#/ * public static double averageArea(int level) { * return S2Projections.AVG_AREA.getValue(level); * } * * /** * Return the average area of cells at this level. This is accurate to within * a factor of 1.7 (for S2_QUADRATIC_PROJECTION) and is extremely cheap to * compute. *#/ * public double averageArea() { * return averageArea(level); * } * * /** * Return the approximate area of this cell. This method is accurate to within * 3% percent for all cell sizes and accurate to within 0.1% for cells at * level 5 or higher (i.e. 300km square or smaller). It is moderately cheap to * compute. *#/ * public double approxArea() { * * // All cells at the first two levels have the same area. * if (level < 2) { * return averageArea(level); * } * * // First, compute the approximate area of the cell when projected * // perpendicular to its normal. The cross product of its diagonals gives * // the normal, and the length of the normal is twice the projected area. * double flatArea = 0.5 * S2Point.crossProd( * S2Point.sub(getVertex(2), getVertex(0)), S2Point.sub(getVertex(3), getVertex(1))).norm(); * * // Now, compensate for the curvature of the cell surface by pretending * // that the cell is shaped like a spherical cap. The ratio of the * // area of a spherical cap to the area of its projected disc turns out * // to be 2 / (1 + sqrt(1 - r*r)) where "r" is the radius of the disc. * // For example, when r=0 the ratio is 1, and when r=1 the ratio is 2. * // Here we set Pi*r*r == flat_area to find the equivalent disc. * return flatArea * 2 / (1 + Math.sqrt(1 - Math.min(S2.M_1_PI * flatArea, 1.0))); * } * * /** * Return the area of this cell as accurately as possible. This method is more * expensive but it is accurate to 6 digits of precision even for leaf cells * (whose area is approximately 1e-18). *#/ * public double exactArea() { * S2Point v0 = getVertex(0); * S2Point v1 = getVertex(1); * S2Point v2 = getVertex(2); * S2Point v3 = getVertex(3); * return S2.area(v0, v1, v2) + S2.area(v0, v2, v3); * } * * // ////////////////////////////////////////////////////////////////////// * // S2Region interface (see {@code S2Region} for details): * * @Override * public S2Region clone() { * S2Cell clone = new S2Cell(); * clone.face = this.face; * clone.level = this.level; * clone.orientation = this.orientation; * clone.uv = this.uv.clone(); * * return clone; * } */ public function getCapBound() { // Use the cell center in (u,v)-space as the cap axis. This vector is // very close to GetCenter() and faster to compute. Neither one of these // vectors yields the bounding cap with minimal surface area, but they // are both pretty close. // // It's possible to show that the two vertices that are furthest from // the (u,v)-origin never determine the maximum cap size (this is a // possible future optimization). $u = 0.5 * ($this->uv[0][0] + $this->uv[0][1]); $v = 0.5 * ($this->uv[1][0] + $this->uv[1][1]); $cap = S2Cap::fromAxisHeight(S2Point::normalize(S2Projections::faceUvToXyz($this->face, $u, $v)), 0); for ($k = 0; $k < 4; ++$k) { $cap = $cap->addPoint($this->getVertex($k)); } return $cap; }
public static function longitude(S2Point $p) { // Note that atan2(0, 0) is defined to be zero. return S1Angle::sradians(atan2($p->get(1), $p->get(0))); }
/** Return true if the cap is full, i.e. it contains all points. *#/ * public boolean isFull() { * return height >= 2; * } * * /** * Return the complement of the interior of the cap. A cap and its complement * have the same boundary but do not share any interior points. The complement * operator is not a bijection, since the complement of a singleton cap * (containing a single point) is the same as the complement of an empty cap. *#/ * public S2Cap complement() { * // The complement of a full cap is an empty cap, not a singleton. * // Also make sure that the complement of an empty cap has height 2. * double cHeight = isFull() ? -1 : 2 - Math.max(height, 0.0); * return S2Cap.fromAxisHeight(S2Point.neg(axis), cHeight); * } * * /** * Return true if and only if this cap contains the given other cap (in a set * containment sense, e.g. every cap contains the empty cap). *#/ * public boolean contains(S2Cap other) { * if (isFull() || other.isEmpty()) { * return true; * } * return angle().radians() >= axis.angle(other.axis) * + other.angle().radians(); * } * * /** * Return true if and only if the interior of this cap intersects the given * other cap. (This relationship is not symmetric, since only the interior of * this cap is used.) *#/ * public boolean interiorIntersects(S2Cap other) { * // Interior(X) intersects Y if and only if Complement(Interior(X)) * // does not contain Y. * return !complement().contains(other); * } * * /** * Return true if and only if the given point is contained in the interior of * the region (i.e. the region excluding its boundary). 'p' should be a * unit-length vector. *#/ * public boolean interiorContains(S2Point p) { * // assert (S2.isUnitLength(p)); * return isFull() || S2Point.sub(axis, p).norm2() < 2 * height; * } * * /** * Increase the cap height if necessary to include the given point. If the cap * is empty the axis is set to the given point, but otherwise it is left * unchanged. 'p' should be a unit-length vector. */ public function addPoint(S2Point $p) { // Compute the squared chord length, then convert it into a height. // assert (S2.isUnitLength(p)); if ($this->isEmpty()) { return new S2Cap($p, 0); } else { // To make sure that the resulting cap actually includes this point, // we need to round up the distance calculation. That is, after // calling cap.AddPoint(p), cap.Contains(p) should be true. $dist2 = S2Point::sub($this->axis, $p)->norm2(); $newHeight = max($this->height, self::ROUND_UP * 0.5 * $dist2); return new S2Cap($this->axis, $newHeight); } }