| /* |
| * Copyright 2005 Google Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package com.google.common.geometry; |
| |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.logging.Logger; |
| |
| /** |
| */ |
| public strictfp class S2CellIdTest extends GeometryTestCase { |
| |
| private static final Logger logger = Logger.getLogger(S2CellIdTest.class.getName()); |
| |
| private S2CellId getCellId(double latDegrees, double lngDegrees) { |
| S2CellId id = S2CellId.fromLatLng(S2LatLng.fromDegrees(latDegrees, lngDegrees)); |
| logger.info(Long.toString(id.id(), 16)); |
| return id; |
| } |
| |
| public void testBasic() { |
| logger.info("TestBasic"); |
| // Check default constructor. |
| S2CellId id = new S2CellId(); |
| assertEquals(id.id(), 0); |
| assertTrue(!id.isValid()); |
| |
| // Check basic accessor methods. |
| id = S2CellId.fromFacePosLevel(3, 0x12345678, S2CellId.MAX_LEVEL - 4); |
| assertTrue(id.isValid()); |
| assertEquals(id.face(), 3); |
| // assertEquals(id.pos(), 0x12345700); |
| assertEquals(id.level(), S2CellId.MAX_LEVEL - 4); |
| assertTrue(!id.isLeaf()); |
| |
| // Check face definitions |
| assertEquals(getCellId(0, 0).face(), 0); |
| assertEquals(getCellId(0, 90).face(), 1); |
| assertEquals(getCellId(90, 0).face(), 2); |
| assertEquals(getCellId(0, 180).face(), 3); |
| assertEquals(getCellId(0, -90).face(), 4); |
| assertEquals(getCellId(-90, 0).face(), 5); |
| |
| // Check parent/child relationships. |
| assertEquals(id.childBegin(id.level() + 2).pos(), 0x12345610); |
| assertEquals(id.childBegin().pos(), 0x12345640); |
| assertEquals(id.parent().pos(), 0x12345400); |
| assertEquals(id.parent(id.level() - 2).pos(), 0x12345000); |
| |
| // Check ordering of children relative to parents. |
| assertTrue(id.childBegin().lessThan(id)); |
| assertTrue(id.childEnd().greaterThan(id)); |
| assertEquals(id.childBegin().next().next().next().next(), id.childEnd()); |
| assertEquals(id.childBegin(S2CellId.MAX_LEVEL), id.rangeMin()); |
| assertEquals(id.childEnd(S2CellId.MAX_LEVEL), id.rangeMax().next()); |
| |
| // Check wrapping from beginning of Hilbert curve to end and vice versa. |
| assertEquals(S2CellId.begin(0).prevWrap(), S2CellId.end(0).prev()); |
| |
| assertEquals(S2CellId.begin(S2CellId.MAX_LEVEL).prevWrap(), |
| S2CellId.fromFacePosLevel(5, ~0L >>> S2CellId.FACE_BITS, S2CellId.MAX_LEVEL)); |
| |
| assertEquals(S2CellId.end(4).prev().nextWrap(), S2CellId.begin(4)); |
| assertEquals(S2CellId.end(S2CellId.MAX_LEVEL).prev().nextWrap(), |
| S2CellId.fromFacePosLevel(0, 0, S2CellId.MAX_LEVEL)); |
| |
| // Check that cells are represented by the position of their center |
| // along the Hilbert curve. |
| assertEquals(id.rangeMin().id() + id.rangeMax().id(), 2 * id.id()); |
| } |
| |
| public void testInverses() { |
| logger.info("TestInverses"); |
| // Check the conversion of random leaf cells to S2LatLngs and back. |
| for (int i = 0; i < 200000; ++i) { |
| S2CellId id = getRandomCellId(S2CellId.MAX_LEVEL); |
| assertTrue(id.isLeaf() && id.level() == S2CellId.MAX_LEVEL); |
| S2LatLng center = id.toLatLng(); |
| assertEquals(S2CellId.fromLatLng(center).id(), id.id()); |
| } |
| } |
| |
| |
| public void testToToken() { |
| assertEquals("000000000000010a", new S2CellId(266).toToken()); |
| assertEquals("80855c", new S2CellId(-9185834709882503168L).toToken()); |
| } |
| |
| public void testTokens() { |
| logger.info("TestTokens"); |
| |
| // Test random cell ids at all levels. |
| for (int i = 0; i < 10000; ++i) { |
| S2CellId id = getRandomCellId(); |
| if (!id.isValid()) { |
| continue; |
| } |
| String token = id.toToken(); |
| assertTrue(token.length() <= 16); |
| assertEquals(S2CellId.fromToken(token), id); |
| } |
| // Check that invalid cell ids can be encoded. |
| String token = S2CellId.none().toToken(); |
| assertEquals(S2CellId.fromToken(token), S2CellId.none()); |
| } |
| |
| private static final int kMaxExpandLevel = 3; |
| |
| private void expandCell( |
| S2CellId parent, ArrayList<S2CellId> cells, Map<S2CellId, S2CellId> parentMap) { |
| cells.add(parent); |
| if (parent.level() == kMaxExpandLevel) { |
| return; |
| } |
| MutableInteger i = new MutableInteger(0); |
| MutableInteger j = new MutableInteger(0); |
| MutableInteger orientation = new MutableInteger(0); |
| int face = parent.toFaceIJOrientation(i, j, orientation); |
| assertEquals(face, parent.face()); |
| |
| int pos = 0; |
| for (S2CellId child = parent.childBegin(); !child.equals(parent.childEnd()); |
| child = child.next()) { |
| // Do some basic checks on the children |
| assertEquals(child.level(), parent.level() + 1); |
| assertTrue(!child.isLeaf()); |
| MutableInteger childOrientation = new MutableInteger(0); |
| assertEquals(child.toFaceIJOrientation(i, j, childOrientation), face); |
| assertEquals( |
| childOrientation.intValue(), orientation.intValue() ^ S2.posToOrientation(pos)); |
| |
| parentMap.put(child, parent); |
| expandCell(child, cells, parentMap); |
| ++pos; |
| } |
| } |
| |
| public void testContainment() { |
| logger.info("TestContainment"); |
| Map<S2CellId, S2CellId> parentMap = new HashMap<S2CellId, S2CellId>(); |
| ArrayList<S2CellId> cells = new ArrayList<S2CellId>(); |
| for (int face = 0; face < 6; ++face) { |
| expandCell(S2CellId.fromFacePosLevel(face, 0, 0), cells, parentMap); |
| } |
| for (int i = 0; i < cells.size(); ++i) { |
| for (int j = 0; j < cells.size(); ++j) { |
| boolean contained = true; |
| for (S2CellId id = cells.get(j); id != cells.get(i); id = parentMap.get(id)) { |
| if (!parentMap.containsKey(id)) { |
| contained = false; |
| break; |
| } |
| } |
| assertEquals(cells.get(i).contains(cells.get(j)), contained); |
| assertEquals(cells.get(j).greaterOrEquals(cells.get(i).rangeMin()) |
| && cells.get(j).lessOrEquals(cells.get(i).rangeMax()), contained); |
| assertEquals(cells.get(i).intersects(cells.get(j)), |
| cells.get(i).contains(cells.get(j)) || cells.get(j).contains(cells.get(i))); |
| } |
| } |
| } |
| |
| private static final int MAX_WALK_LEVEL = 8; |
| |
| public void testContinuity() { |
| logger.info("TestContinuity"); |
| // Make sure that sequentially increasing cell ids form a continuous |
| // path over the surface of the sphere, i.e. there are no |
| // discontinuous jumps from one region to another. |
| |
| double maxDist = S2Projections.MAX_EDGE.getValue(MAX_WALK_LEVEL); |
| S2CellId end = S2CellId.end(MAX_WALK_LEVEL); |
| S2CellId id = S2CellId.begin(MAX_WALK_LEVEL); |
| for (; !id.equals(end); id = id.next()) { |
| assertTrue(id.toPointRaw().angle(id.nextWrap().toPointRaw()) <= maxDist); |
| |
| // Check that the ToPointRaw() returns the center of each cell |
| // in (s,t) coordinates. |
| S2Point p = id.toPointRaw(); |
| int face = S2Projections.xyzToFace(p); |
| R2Vector uv = S2Projections.validFaceXyzToUv(face, p); |
| assertDoubleNear(Math.IEEEremainder( |
| S2Projections.uvToST(uv.x()), 1.0 / (1 << MAX_WALK_LEVEL)), 0); |
| assertDoubleNear(Math.IEEEremainder( |
| S2Projections.uvToST(uv.y()), 1.0 / (1 << MAX_WALK_LEVEL)), 0); |
| } |
| } |
| |
| public void testCoverage() { |
| logger.info("TestCoverage"); |
| // Make sure that random points on the sphere can be represented to the |
| // expected level of accuracy, which in the worst case is sqrt(2/3) times |
| // the maximum arc length between the points on the sphere associated with |
| // adjacent values of "i" or "j". (It is sqrt(2/3) rather than 1/2 because |
| // the cells at the corners of each face are stretched -- they have 60 and |
| // 120 degree angles.) |
| |
| double maxDist = 0.5 * S2Projections.MAX_DIAG.getValue(S2CellId.MAX_LEVEL); |
| for (int i = 0; i < 1000000; ++i) { |
| // randomPoint(); |
| S2Point p = new S2Point(0.37861576725894824, 0.2772406863275093, 0.8830558887338725); |
| S2Point q = S2CellId.fromPoint(p).toPointRaw(); |
| |
| assertTrue(p.angle(q) <= maxDist); |
| } |
| } |
| |
| public void testAllNeighbors(S2CellId id, int level) { |
| assertTrue(level >= id.level() && level < S2CellId.MAX_LEVEL); |
| |
| // We compute GetAllNeighbors, and then add in all the children of "id" |
| // at the given level. We then compare this against the result of finding |
| // all the vertex neighbors of all the vertices of children of "id" at the |
| // given level. These should give the same result. |
| ArrayList<S2CellId> all = new ArrayList<S2CellId>(); |
| ArrayList<S2CellId> expected = new ArrayList<S2CellId>(); |
| id.getAllNeighbors(level, all); |
| S2CellId end = id.childEnd(level + 1); |
| for (S2CellId c = id.childBegin(level + 1); !c.equals(end); c = c.next()) { |
| all.add(c.parent()); |
| c.getVertexNeighbors(level, expected); |
| } |
| // Sort the results and eliminate duplicates. |
| Collections.sort(all); |
| Collections.sort(expected); |
| Set<S2CellId> allSet = new HashSet<S2CellId>(all); |
| Set<S2CellId> expectedSet = new HashSet<S2CellId>(expected); |
| assertTrue(allSet.equals(expectedSet)); |
| } |
| |
| public void testNeighbors() { |
| logger.info("TestNeighbors"); |
| |
| // Check the edge neighbors of face 1. |
| final int outFaces[] = {5, 3, 2, 0}; |
| S2CellId faceNbrs[] = new S2CellId[4]; |
| S2CellId.fromFacePosLevel(1, 0, 0).getEdgeNeighbors(faceNbrs); |
| for (int i = 0; i < 4; ++i) { |
| assertTrue(faceNbrs[i].isFace()); |
| assertEquals(faceNbrs[i].face(), outFaces[i]); |
| } |
| |
| // Check the vertex neighbors of the center of face 2 at level 5. |
| ArrayList<S2CellId> nbrs = new ArrayList<S2CellId>(); |
| S2CellId.fromPoint(new S2Point(0, 0, 1)).getVertexNeighbors(5, nbrs); |
| Collections.sort(nbrs); |
| for (int i = 0; i < 4; ++i) { |
| assertEquals(nbrs.get(i), S2CellId.fromFaceIJ( |
| 2, (1 << 29) - (i < 2 ? 1 : 0), (1 << 29) - ((i == 0 || i == 3) ? 1 : 0)).parent(5)); |
| } |
| nbrs.clear(); |
| |
| // Check the vertex neighbors of the corner of faces 0, 4, and 5. |
| S2CellId id = S2CellId.fromFacePosLevel(0, 0, S2CellId.MAX_LEVEL); |
| id.getVertexNeighbors(0, nbrs); |
| Collections.sort(nbrs); |
| assertEquals(nbrs.size(), 3); |
| assertEquals(nbrs.get(0), S2CellId.fromFacePosLevel(0, 0, 0)); |
| assertEquals(nbrs.get(1), S2CellId.fromFacePosLevel(4, 0, 0)); |
| assertEquals(nbrs.get(2), S2CellId.fromFacePosLevel(5, 0, 0)); |
| |
| // Check that GetAllNeighbors produces results that are consistent |
| // with GetVertexNeighbors for a bunch of random cells. |
| for (int i = 0; i < 1000; ++i) { |
| S2CellId id1 = getRandomCellId(); |
| if (id1.isLeaf()) { |
| id1 = id1.parent(); |
| } |
| |
| // TestAllNeighbors computes approximately 2**(2*(diff+1)) cell id1s, |
| // so it's not reasonable to use large values of "diff". |
| int maxDiff = Math.min(6, S2CellId.MAX_LEVEL - id1.level() - 1); |
| int level = id1.level() + random(maxDiff); |
| testAllNeighbors(id1, level); |
| } |
| } |
| } |