/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You 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 org.apache.lucene.spatial.util; import java.util.Random; import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.util.NumericUtils; import org.apache.lucene.util.TestUtil; import static org.apache.lucene.geo.GeoEncodingUtils.decodeLatitude; import static org.apache.lucene.geo.GeoEncodingUtils.decodeLongitude; import static org.apache.lucene.spatial.util.MortonEncoder.decodeLatitude; import static org.apache.lucene.spatial.util.MortonEncoder.decodeLongitude; import static org.apache.lucene.spatial.util.MortonEncoder.encode; import static org.apache.lucene.spatial.util.MortonEncoder.encodeCeil; import static org.apache.lucene.util.BitUtil.deinterleave; import static org.apache.lucene.util.BitUtil.interleave; /** * Tests methods in {@link MortonEncoder} */ public class TestMortonEncoder extends LuceneTestCase { public void testMortonEncoding() throws Exception { final long TRANSLATE = 1L << 31; final double LATITUDE_DECODE = 180.0D/(0x1L<<32); final double LONGITUDE_DECODE = 360.0D/(0x1L<<32); Random random = random(); for(int i=0; i < 10000; ++i) { long encoded = random().nextLong(); long encodedLat = deinterleave(encoded >>> 1); long encodedLon = deinterleave(encoded); double expectedLat = decodeLatitude((int)(encodedLat - TRANSLATE)); double decodedLat = decodeLatitude(encoded); double expectedLon = decodeLongitude((int)(encodedLon - TRANSLATE)); double decodedLon = decodeLongitude(encoded); assertEquals(expectedLat, decodedLat, 0.0D); assertEquals(expectedLon, decodedLon, 0.0D); // should round-trip assertEquals(encoded, encode(decodedLat, decodedLon)); // test within the range if (encoded != 0xFFFFFFFFFFFFFFFFL) { // this is the next representable value // all double values between [min .. max) should encode to the current integer // all double values between (min .. max] should encodeCeil to the next integer. double maxLat = expectedLat + LATITUDE_DECODE; encodedLat += 1; assertEquals(maxLat, decodeLatitude((int)(encodedLat - TRANSLATE)), 0.0D); double maxLon = expectedLon + LONGITUDE_DECODE; encodedLon += 1; assertEquals(maxLon, decodeLongitude((int)(encodedLon - TRANSLATE)), 0.0D); long encodedNext = encode(maxLat, maxLon); assertEquals(interleave((int)encodedLon, (int)encodedLat), encodedNext); // first and last doubles in range that will be quantized double minEdgeLat = Math.nextUp(expectedLat); double minEdgeLon = Math.nextUp(expectedLon); long encodedMinEdge = encode(minEdgeLat, minEdgeLon); long encodedMinEdgeCeil = encodeCeil(minEdgeLat, minEdgeLon); double maxEdgeLat = Math.nextDown(maxLat); double maxEdgeLon = Math.nextDown(maxLon); long encodedMaxEdge = encode(maxEdgeLat, maxEdgeLon); long encodedMaxEdgeCeil = encodeCeil(maxEdgeLat, maxEdgeLon); assertEquals(encodedLat - 1, deinterleave(encodedMinEdge >>> 1)); assertEquals(encodedLat, deinterleave(encodedMinEdgeCeil >>> 1)); assertEquals(encodedLon - 1, deinterleave(encodedMinEdge)); assertEquals(encodedLon, deinterleave(encodedMinEdgeCeil)); assertEquals(encodedLat - 1, deinterleave(encodedMaxEdge >>> 1)); assertEquals(encodedLat, deinterleave(encodedMaxEdgeCeil >>> 1)); assertEquals(encodedLon - 1, deinterleave(encodedMaxEdge)); assertEquals(encodedLon, deinterleave(encodedMaxEdgeCeil)); // check random values within the double range long minBitsLat = NumericUtils.doubleToSortableLong(minEdgeLat); long maxBitsLat = NumericUtils.doubleToSortableLong(maxEdgeLat); long minBitsLon = NumericUtils.doubleToSortableLong(minEdgeLon); long maxBitsLon = NumericUtils.doubleToSortableLong(maxEdgeLon); for (int j = 0; j < 100; j++) { double valueLat = NumericUtils.sortableLongToDouble(TestUtil.nextLong(random, minBitsLat, maxBitsLat)); double valueLon = NumericUtils.sortableLongToDouble(TestUtil.nextLong(random, minBitsLon, maxBitsLon)); // round down assertEquals(encoded, encode(valueLat, valueLon)); // round up assertEquals(interleave((int)encodedLon, (int)encodedLat), encodeCeil(valueLat, valueLon)); } } } } }