Author: luc
Date: Wed Feb 12 11:12:45 2014
New Revision: 1567599
URL: http://svn.apache.org/r1567599
Log:
Rewrote completely the enclosing spherical cap computation on 2-sphere.
The previous version was based on Welzl algorithm, but it appeared this
algorithm really need some properties that hold in Euclidean spaces and
not on the sphere.
The current version is not perfect in the sense that some times the
enclosing spherical cap returned is not the smallest possible. It is
documented in the Javadoc.
Removed:
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalCapGenerator.java
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/geometry/spherical/twod/SphericalCapGeneratorTest.java
Modified:
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/PropertiesComputer.java
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSetTest.java
Modified:
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/PropertiesComputer.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/PropertiesComputer.java?rev=1567599&r1=1567598&r2=1567599&view=diff
==============================================================================
---
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/PropertiesComputer.java
(original)
+++
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/PropertiesComputer.java
Wed Feb 12 11:12:45 2014
@@ -16,6 +16,7 @@
*/
package org.apache.commons.math3.geometry.spherical.twod;
+import java.util.ArrayList;
import java.util.List;
import org.apache.commons.math3.exception.MathInternalError;
@@ -40,13 +41,17 @@ class PropertiesComputer implements BSPT
/** Summed barycenter. */
private Vector3D summedBarycenter;
+ /** List of points strictly inside convex cells. */
+ private final List<Vector3D> convexCellsInsidePoints;
+
/** Simple constructor.
* @param tolerance below which points are consider to be identical
*/
public PropertiesComputer(final double tolerance) {
- this.tolerance = tolerance;
- this.summedArea = 0;
- this.summedBarycenter = Vector3D.ZERO;
+ this.tolerance = tolerance;
+ this.summedArea = 0;
+ this.summedBarycenter = Vector3D.ZERO;
+ this.convexCellsInsidePoints = new ArrayList<Vector3D>();
}
/** {@inheritDoc} */
@@ -80,6 +85,7 @@ class PropertiesComputer implements BSPT
// compute the geometrical properties of the convex cell
final double area = convexCellArea(boundary.get(0));
final Vector3D barycenter = convexCellBarycenter(boundary.get(0));
+ convexCellsInsidePoints.add(barycenter);
// add the cell contribution to the global properties
summedArea += area;
@@ -158,4 +164,11 @@ class PropertiesComputer implements BSPT
}
}
+ /** Get the points strictly inside convex cells.
+ * @return points strictly inside convex cells
+ */
+ public List<Vector3D> getConvexCellsInsidePoints() {
+ return convexCellsInsidePoints;
+ }
+
}
Modified:
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java?rev=1567599&r1=1567598&r2=1567599&view=diff
==============================================================================
---
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java
(original)
+++
commons/proper/math/trunk/src/main/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSet.java
Wed Feb 12 11:12:45 2014
@@ -19,18 +19,20 @@ package org.apache.commons.math3.geometr
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
-import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import org.apache.commons.math3.exception.MathIllegalStateException;
-import org.apache.commons.math3.exception.MathInternalError;
import org.apache.commons.math3.geometry.enclosing.EnclosingBall;
import org.apache.commons.math3.geometry.enclosing.WelzlEncloser;
+import org.apache.commons.math3.geometry.euclidean.threed.Euclidean3D;
import org.apache.commons.math3.geometry.euclidean.threed.Rotation;
+import org.apache.commons.math3.geometry.euclidean.threed.SphereGenerator;
import org.apache.commons.math3.geometry.euclidean.threed.Vector3D;
import org.apache.commons.math3.geometry.partitioning.AbstractRegion;
import org.apache.commons.math3.geometry.partitioning.BSPTree;
+import org.apache.commons.math3.geometry.partitioning.BoundaryProjection;
+import org.apache.commons.math3.geometry.partitioning.RegionFactory;
import org.apache.commons.math3.geometry.partitioning.SubHyperplane;
import org.apache.commons.math3.geometry.spherical.oned.Sphere1D;
import org.apache.commons.math3.util.FastMath;
@@ -457,9 +459,7 @@ public class SphericalPolygonsSet extend
* method return always false or true.
* </p>
* <p>
- * This method is <em>not</em> guaranteed to return the smallest enclosing
cap. It
- * will do so for small polygons without holes, but may select a very
large spherical
- * cap in other cases.
+ * This method is <em>not</em> guaranteed to return the smallest enclosing
cap.
* </p>
* @return a spherical cap enclosing the polygon
*/
@@ -488,239 +488,77 @@ public class SphericalPolygonsSet extend
0.5 * FastMath.PI);
}
- // extract boundary
- final List<Vertex> boundary = getBoundaryLoops();
+ // gather some inside points, to be used by the encloser
+ final List<Vector3D> points = getInsidePoints();
- final List<S2Point> points = new ArrayList<S2Point>();
+ // extract points from the boundary loops, to be used by the encloser
as well
+ final List<Vertex> boundary = getBoundaryLoops();
for (final Vertex loopStart : boundary) {
- // extract points from the boundary loops, to be used by the
encloser
- for (Vertex v = loopStart; points.isEmpty() || v != loopStart; v =
v.getOutgoing().getEnd()) {
- points.add(v.getLocation());
+ int count = 0;
+ for (Vertex v = loopStart; count == 0 || v != loopStart; v =
v.getOutgoing().getEnd()) {
+ ++count;
+ points.add(v.getLocation().getVector());
}
}
- // find the smallest enclosing spherical cap
- final SphericalCapGenerator capGenerator = new
SphericalCapGenerator(getInsidePoint(boundary));
- final WelzlEncloser<Sphere2D, S2Point> encloser =
- new WelzlEncloser<Sphere2D, S2Point>(getTolerance(),
capGenerator);
- EnclosingBall<Sphere2D, S2Point> enclosing = encloser.enclose(points);
-
- // ensure that the spherical cap (which was defined using only a few
points)
- // does not cross any edges
- if (enclosing.getRadius() >= 0.5 * FastMath.PI) {
- final List<Edge> notEnclosed = notEnclosed(boundary, enclosing);
- if (!notEnclosed.isEmpty()) {
- // the vertex-based spherical cap is too small
- // it does not fully enclose some edges
-
- // add the edges converging to the support points if any
- for (final S2Point point : enclosing.getSupport()) {
- final Vertex vertex = associatedVertex(point, boundary);
- if (vertex != null) {
- addEdge(vertex.getIncoming(), notEnclosed);
- addEdge(vertex.getOutgoing(), notEnclosed);
- }
+ // find the smallest enclosing 3D sphere
+ final SphereGenerator generator = new SphereGenerator();
+ final WelzlEncloser<Euclidean3D, Vector3D> encloser =
+ new WelzlEncloser<Euclidean3D, Vector3D>(getTolerance(),
generator);
+ EnclosingBall<Euclidean3D, Vector3D> enclosing3D =
encloser.enclose(points);
+ final Vector3D[] support3D = enclosing3D.getSupport();
+
+ // convert to 3D sphere to spherical cap
+ final double r = enclosing3D.getRadius();
+ final double h = enclosing3D.getCenter().getNorm();
+ if (h < getTolerance()) {
+ // the 3D sphere is centered on the unit sphere and covers it
+ // fall back to a crude approximation, based only on outside
convex cells
+ EnclosingBall<Sphere2D, S2Point> enclosingS2 =
+ new EnclosingBall<Sphere2D, S2Point>(S2Point.PLUS_K,
Double.POSITIVE_INFINITY);
+ for (Vector3D outsidePoint : getOutsidePoints()) {
+ final S2Point outsideS2 = new S2Point(outsidePoint);
+ final BoundaryProjection<Sphere2D> projection =
projectToBoundary(outsideS2);
+ if (FastMath.PI - projection.getOffset() <
enclosingS2.getRadius()) {
+ enclosingS2 = new EnclosingBall<Sphere2D,
S2Point>(outsideS2.negate(),
+
FastMath.PI - projection.getOffset(),
+
(S2Point) projection.getProjected());
}
- if (notEnclosed.size() < 3) {
- // this should never happen
- throw new MathInternalError();
- }
-
- // enlarge the spherical cap so it encloses even the three
farthest edges
- Collections.sort(notEnclosed, new
DistanceComparator(enclosing.getCenter().getVector()));
- enclosing =
capGenerator.ballOnSupport(notEnclosed.get(notEnclosed.size() - 1).getCircle(),
-
notEnclosed.get(notEnclosed.size() - 2).getCircle(),
-
notEnclosed.get(notEnclosed.size() - 3).getCircle());
-
}
+ return enclosingS2;
}
-
- // return the spherical cap found
- return enclosing;
-
- }
-
- /** Get an inside point.
- * @param boundary boundary loops (known to be non-empty)
- * @return an inside point
- */
- private Vector3D getInsidePoint(final List<Vertex> boundary) {
-
- // search for a leaf inside cell bounded by the loop
- final BSPTree<Sphere2D> leaf =
getInsideLeaf(boundary.get(0).getOutgoing());
-
- // build a convex region from this inside leaf
- final SphericalPolygonsSet convex =
- new SphericalPolygonsSet(leaf.pruneAroundConvexCell(Boolean.TRUE,
Boolean.FALSE, null),
- getTolerance());
-
- // select the convex region barycenter as the inside point
- return ((S2Point) convex.getBarycenter()).getVector();
-
- }
-
- /** Get an inside leaf cell relative to a single boundary loop.
- * @param edge edge belonging to the loop
- * @return an inside leaf cell
- */
- private BSPTree<Sphere2D> getInsideLeaf(final Edge edge) {
-
- // search for the node whose cut sub-hyperplane contains the edge
- final S2Point middlePoint = new S2Point(edge.getPointAt(0.5 *
edge.getLength()));
- final BSPTree<Sphere2D> cuttingNode =
getTree(true).getCell(middlePoint, getTolerance());
- if (cuttingNode.getCut() == null) {
- // this should never happen
- throw new MathInternalError();
+ final S2Point[] support = new S2Point[support3D.length];
+ for (int i = 0; i < support3D.length; ++i) {
+ support[i] = new S2Point(support3D[i]);
}
- final Vector3D cutPole = ((Circle)
cuttingNode.getCut().getHyperplane()).getPole();
- final Vector3D edgePole = edge.getCircle().getPole();
+ final EnclosingBall<Sphere2D, S2Point> enclosingS2 =
+ new EnclosingBall<Sphere2D, S2Point>(new
S2Point(enclosing3D.getCenter()),
+ FastMath.acos((1 + h * h
- r * r) / (2 * h)),
+ support);
- if (Vector3D.dotProduct(cutPole, edgePole) > 0) {
- // the inside part is on the minus part of the cut
- return cuttingNode.getMinus().getCell(middlePoint, getTolerance());
- } else {
- // the inside part is on the plus part of the cut
- return cuttingNode.getPlus().getCell(middlePoint, getTolerance());
- }
+ return enclosingS2;
}
- /** Get the edges that fails to be in an enclosing spherical cap.
- * <p>
- * This method must be called <em>only</em> when the edges endpoints
- * are already known to be all inside of the spherical cap.
- * </p>
- * @param boundary boundary loops
- * @param cap spherical cap that already encloses all vertices
- * @return the edges that are not fully in the cap, despite their
endpoints are
+ /** Gather some inside points.
+ * @return list of points known to be strictly in all inside convex cells
*/
- private List<Edge> notEnclosed(final List<Vertex> boundary,
- final EnclosingBall<Sphere2D, S2Point>
cap) {
-
- final List<Edge> edges = new ArrayList<Edge>();
-
- for (final Vertex loopStart : boundary) {
- int count = 0;
- Edge edge = null;
- for (Vertex vertex = loopStart; count == 0 || vertex != loopStart;
vertex = edge.getEnd()) {
-
- ++count;
- edge = vertex.getOutgoing();
-
- // find the intersection of the circle containing the edge and
the cap boundary
- // let (u, α) and (v, β) be the poles and angular radii of
the two circles
- // the intersection is computed as p = a u + b v + c u^v, with
||p|| = 1, so:
- // a = (cos α - u.v cos β) / (1 - (u.v)²)
- // b = (cos β - u.v cos α) / (1 - (u.v)²)
- // c = ±â[(1 - (a u + b v)²) / (1 - (u.v)²)]
- // here, α = Ï/2, hence cos α = 0
- // a u + b v is the part of the p vector in the (u, v) plane
- // c u^v is the part of the p vector orthogonal to the (u, v)
plane
- final Vector3D u = edge.getCircle().getPole();
- final Vector3D v = cap.getCenter().getVector();
- final double cosBeta = FastMath.cos(cap.getRadius());
- final double s = Vector3D.dotProduct(u, v);
- final double f = 1.0 / ((1 - s) * (1 + s));
- final double b = cosBeta * f;
- final double a = -s * b;
- final Vector3D inPlane = new Vector3D(a, u, b, v);
- final double aubv2 = inPlane.getNormSq();
- if (aubv2 < 1.0) {
-
- // the two circles have two intersection points
- final double c = FastMath.sqrt((1 - aubv2) * f);
- final Vector3D crossPlane = Vector3D.crossProduct(u, v);
- final Vector3D pPlus = new Vector3D(1, inPlane, +c,
crossPlane);
- final Vector3D pMinus = new Vector3D(1, inPlane, -c,
crossPlane);
- if (Vector3D.angle(pPlus,
edge.getStart().getLocation().getVector()) <= getTolerance() ||
- Vector3D.angle(pPlus,
edge.getStart().getLocation().getVector()) <= getTolerance() ||
- Vector3D.angle(pPlus,
edge.getStart().getLocation().getVector()) <= getTolerance() ||
- Vector3D.angle(pPlus,
edge.getStart().getLocation().getVector()) <= getTolerance()) {
- // the edge endpoints are really close, we select the
edge
- edges.add(edge);
- } else {
- // the edge is limited to a part of the circle, check
its extension
- final double startPhase =
edge.getCircle().getPhase(edge.getStart().getLocation().getVector());
- final double pPlusRelativePhase =
MathUtils.normalizeAngle(edge.getCircle().getPhase(pPlus),
-
startPhase + FastMath.PI);
- final double pMinusRelativePhase =
MathUtils.normalizeAngle(edge.getCircle().getPhase(pMinus),
-
startPhase + FastMath.PI);
- if (FastMath.min(pPlusRelativePhase,
pMinusRelativePhase) < edge.getLength()) {
- // the edge really goes out of the cap and enter
it again,
- // it is not entirely contained in the cap
- edges.add(edge);
- }
- }
-
- }
-
- }
- }
-
- return edges;
-
+ private List<Vector3D> getInsidePoints() {
+ final PropertiesComputer pc = new PropertiesComputer(getTolerance());
+ getTree(true).visit(pc);
+ return pc.getConvexCellsInsidePoints();
}
- /** Find the vertex associated to a point.
- * @param point point to associate to a vertex
- * @param boundary boundary loops
- * @return vertex associated to point, or null if the point is not a vertex
+ /** Gather some outside points.
+ * @return list of points known to be strictly in all outside convex cells
*/
- private Vertex associatedVertex(final S2Point point, final List<Vertex>
boundary) {
- for (final Vertex loopStart : boundary) {
- int count = 0;
- for (Vertex vertex = loopStart; count == 0 || vertex != loopStart;
vertex = vertex.getOutgoing().getEnd()) {
- ++count;
- if (point == vertex.getLocation()) {
- return vertex;
- }
- }
- }
- return null;
- }
-
- /** Add an edge to a list if not already present.
- * @param edge edge to add
- * @param list to which edge must be added
- */
- private void addEdge(final Edge edge, final List<Edge> list) {
- for (final Edge present : list) {
- if (present == edge) {
- // the edge was already in the list
- return;
- }
- }
- list.add(edge);
- }
-
- /** Comparator for sorting edges according to distance wrt a spherical cap
pole. */
- private static class DistanceComparator implements Comparator<Edge> {
-
- /** Pole of the spherical cap. */
- private final Vector3D pole;
-
- /** Simple constructor.
- * @param pole pole of the spherical cap
- */
- public DistanceComparator(final Vector3D pole) {
- this.pole = pole;
- }
-
- /** {@inheritDoc} */
- public int compare(final Edge o1, final Edge o2) {
- return Double.compare(distance(o1), distance(o2));
- }
-
- /** Compute distance between edge and pole.
- * @param edge edge
- * @return distance between adged and pole
- */
- private double distance(final Edge edge) {
- final double alpha = edge.getCircle().getPole().distance(pole);
- return FastMath.max(alpha + 0.5 * FastMath.PI, 1.5 * FastMath.PI -
alpha);
- }
-
+ private List<Vector3D> getOutsidePoints() {
+ final SphericalPolygonsSet complement =
+ (SphericalPolygonsSet) new
RegionFactory<Sphere2D>().getComplement(this);
+ final PropertiesComputer pc = new PropertiesComputer(getTolerance());
+ complement.getTree(true).visit(pc);
+ return pc.getConvexCellsInsidePoints();
}
}
Modified:
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSetTest.java
URL:
http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSetTest.java?rev=1567599&r1=1567598&r2=1567599&view=diff
==============================================================================
---
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSetTest.java
(original)
+++
commons/proper/math/trunk/src/test/java/org/apache/commons/math3/geometry/spherical/twod/SphericalPolygonsSetTest.java
Wed Feb 12 11:12:45 2014
@@ -435,6 +435,79 @@ public class SphericalPolygonsSetTest {
}
+ @Test
+ public void testGeographicalMap() {
+
+ SphericalPolygonsSet continental = buildSimpleZone(new double[][] {
+ { 51.14850, 2.51357 }, { 50.94660, 1.63900 }, { 50.12717, 1.33876
}, { 49.34737, -0.98946 },
+ { 49.77634, -1.93349 }, { 48.64442, -1.61651 }, { 48.90169, -3.29581
}, { 48.68416, -4.59234 },
+ { 47.95495, -4.49155 }, { 47.57032, -2.96327 }, { 46.01491, -1.19379
}, { 44.02261, -1.38422 },
+ { 43.42280, -1.90135 }, { 43.03401, -1.50277 }, { 42.34338, 1.82679
}, { 42.47301, 2.98599 },
+ { 43.07520, 3.10041 }, { 43.39965, 4.55696 }, { 43.12889, 6.52924
}, { 43.69384, 7.43518 },
+ { 44.12790, 7.54959 }, { 45.02851, 6.74995 }, { 45.33309, 7.09665
}, { 46.42967, 6.50009 },
+ { 46.27298, 6.02260 }, { 46.72577, 6.03738 }, { 47.62058, 7.46675
}, { 49.01778, 8.09927 },
+ { 49.20195, 6.65822 }, { 49.44266, 5.89775 }, { 49.98537, 4.79922
}
+ });
+ SphericalPolygonsSet corsica = buildSimpleZone(new double[][] {
+ { 42.15249, 9.56001 }, { 43.00998, 9.39000 }, { 42.62812, 8.74600
}, { 42.25651, 8.54421 },
+ { 41.58361, 8.77572 }, { 41.38000, 9.22975 }
+ });
+ RegionFactory<Sphere2D> factory = new RegionFactory<Sphere2D>();
+ SphericalPolygonsSet zone = (SphericalPolygonsSet)
factory.union(continental, corsica);
+ EnclosingBall<Sphere2D, S2Point> enclosing = zone.getEnclosingCap();
+ Vector3D enclosingCenter = ((S2Point)
enclosing.getCenter()).getVector();
+
+ double step = FastMath.toRadians(0.1);
+ for (Vertex loopStart : zone.getBoundaryLoops()) {
+ int count = 0;
+ for (Vertex v = loopStart; count == 0 || v != loopStart; v =
v.getOutgoing().getEnd()) {
+ ++count;
+ for (int i = 0; i < FastMath.ceil(v.getOutgoing().getLength()
/ step); ++i) {
+ Vector3D p = v.getOutgoing().getPointAt(i * step);
+ Assert.assertTrue(Vector3D.angle(p, enclosingCenter) <=
enclosing.getRadius());
+ }
+ }
+ }
+
+ S2Point supportPointA = s2Point(48.68416, -4.59234);
+ S2Point supportPointB = s2Point(41.38000, 9.22975);
+ Assert.assertEquals(enclosing.getRadius(),
supportPointA.distance(enclosing.getCenter()), 1.0e-10);
+ Assert.assertEquals(enclosing.getRadius(),
supportPointB.distance(enclosing.getCenter()), 1.0e-10);
+ Assert.assertEquals(0.5 * supportPointA.distance(supportPointB),
enclosing.getRadius(), 1.0e-10);
+ Assert.assertEquals(2, enclosing.getSupportSize());
+
+ EnclosingBall<Sphere2D, S2Point> continentalInscribed =
+ ((SphericalPolygonsSet)
factory.getComplement(continental)).getEnclosingCap();
+ Vector3D continentalCenter = ((S2Point)
continentalInscribed.getCenter()).getVector();
+ Assert.assertEquals(2.2, FastMath.toDegrees(FastMath.PI -
continentalInscribed.getRadius()), 0.1);
+ for (Vertex loopStart : continental.getBoundaryLoops()) {
+ int count = 0;
+ for (Vertex v = loopStart; count == 0 || v != loopStart; v =
v.getOutgoing().getEnd()) {
+ ++count;
+ for (int i = 0; i < FastMath.ceil(v.getOutgoing().getLength()
/ step); ++i) {
+ Vector3D p = v.getOutgoing().getPointAt(i * step);
+ Assert.assertTrue(Vector3D.angle(p, continentalCenter) <=
continentalInscribed.getRadius());
+ }
+ }
+ }
+
+ EnclosingBall<Sphere2D, S2Point> corsicaInscribed =
+ ((SphericalPolygonsSet)
factory.getComplement(corsica)).getEnclosingCap();
+ Vector3D corsicaCenter = ((S2Point)
corsicaInscribed.getCenter()).getVector();
+ Assert.assertEquals(0.34, FastMath.toDegrees(FastMath.PI -
corsicaInscribed.getRadius()), 0.01);
+ for (Vertex loopStart : corsica.getBoundaryLoops()) {
+ int count = 0;
+ for (Vertex v = loopStart; count == 0 || v != loopStart; v =
v.getOutgoing().getEnd()) {
+ ++count;
+ for (int i = 0; i < FastMath.ceil(v.getOutgoing().getLength()
/ step); ++i) {
+ Vector3D p = v.getOutgoing().getPointAt(i * step);
+ Assert.assertTrue(Vector3D.angle(p, corsicaCenter) <=
corsicaInscribed.getRadius());
+ }
+ }
+ }
+
+ }
+
private SubCircle create(Vector3D pole, Vector3D x, Vector3D y,
double tolerance, double ... limits) {
RegionFactory<Sphere1D> factory = new RegionFactory<Sphere1D>();
@@ -448,4 +521,16 @@ public class SphericalPolygonsSetTest {
return new SubCircle(phased, set);
}
+ private SphericalPolygonsSet buildSimpleZone(double[][] points) {
+ final S2Point[] vertices = new S2Point[points.length];
+ for (int i = 0; i < points.length; ++i) {
+ vertices[i] = s2Point(points[i][0], points[i][1]);
+ }
+ return new SphericalPolygonsSet(1.0e-10, vertices);
+ }
+
+ private S2Point s2Point(double latitude, double longitude) {
+ return new S2Point(FastMath.toRadians(longitude),
FastMath.toRadians(90.0 - latitude));
+ }
+
}