/*
* Geotoolkit.org - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2014, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.internal.coverage;
import org.apache.sis.geometry.Envelope2D;
import org.apache.sis.geometry.GeneralEnvelope;
import org.apache.sis.measure.NumberRange;
import org.apache.sis.referencing.CommonCRS;
import org.apache.sis.referencing.CRS;
import org.junit.Assert;
import org.junit.BeforeClass;
import org.junit.Test;
import org.opengis.geometry.Envelope;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import java.util.Arrays;
import java.util.Map;
/**
* Date: 01/10/14
* Time: 10:17
*
* @author Alexis Manin (Geomatys)
*/
public strictfp class CoverageUtilitiesTest extends org.geotoolkit.test.TestBase {
private static final double EPSI = 1E-8;
/**
* See WMTS annex, Table E.3 — Definition of Well-known scale set GoogleCRS84Quad
*/
private static final double[] DEGREE_SCALES = new double[]{
1.40625000000000,
0.703125000000000,
0.351562500000000,
0.175781250000000,
8.78906250000000E-2,
4.39453125000000E-2,
2.19726562500000E-2,
1.09863281250000E-2,
5.49316406250000E-3,
2.74658203125000E-3,
1.37329101562500E-3,
6.86645507812500E-4,
3.43322753906250E-4,
1.71661376953125E-4,
8.58306884765625E-5,
4.29153442382812E-5,
2.14576721191406E-5,
1.07288360595703E-5,
5.36441802978516E-6
};
/**
* See WMTS annex, Table E.4 — Definition of Well-known scale set GoogleMapsCompatible
*/
private static final double[] METER_SCALES = new double[]{
156543.0339280410,
78271.51696402048,
39135.75848201023,
19567.87924100512,
9783.939620502561,
4891.969810251280,
2445.984905125640,
1222.992452562820,
611.4962262814100,
305.7481131407048,
152.8740565703525,
76.43702828517624,
38.21851414258813,
19.10925707129406,
9.554628535647032,
4.777314267823516,
2.388657133911758,
1.194328566955879,
0.5971642834779395
};
@BeforeClass
public static void sortArrays() {
// Needed for binary search.
Arrays.sort(METER_SCALES);
Arrays.sort(DEGREE_SCALES);
}
/**
* Check computing of WMTS compliant scales from input envelope.
*
* First tested envelopes are validity domain of CRS:84 and mercator CRS. We attempt out method to return same envelopes.
* After that, a test is done with a little envelope, to ensure envelope adaptation is good.
*
* @throws Exception
*/
@Test
public void toWellKnownScaleTest() throws Exception {
final NumberRange<Double> degreeRange = new NumberRange<>(Double.class, 0.005, true, 2d, true);
final NumberRange<Double> meterRange = new NumberRange<>(Double.class, 200000d, true, 2d, true);
final CoordinateReferenceSystem crs84 = CommonCRS.defaultGeographic();
final Envelope domain84 = org.geotoolkit.referencing.CRS.getEnvelope(crs84);
Map.Entry<Envelope,double[]> scales = CoverageUtilities.toWellKnownScale(domain84, degreeRange);
Assert.assertEquals("Input and output envelope must be the same.", domain84, scales.getKey());
for (final double scale : scales.getValue()) {
ensureAlmostContains(scale, DEGREE_SCALES);
}
final CoordinateReferenceSystem crsGoogle = CRS.forCode("EPSG:3857");
final Envelope domainGoogle = org.geotoolkit.referencing.CRS.getEnvelope(crsGoogle);
scales = CoverageUtilities.toWellKnownScale(domainGoogle, meterRange);
Assert.assertEquals("Input and output envelope must be the same.", domainGoogle, scales.getKey());
for (final double scale : scales.getValue()) {
ensureAlmostContains(scale, METER_SCALES);
}
final CoordinateReferenceSystem crsMercat = CRS.forCode("EPSG:3395");
final Envelope domainMercat = org.geotoolkit.referencing.CRS.getEnvelope(crsMercat);
scales = CoverageUtilities.toWellKnownScale(domainMercat, meterRange);
Assert.assertEquals("Input and output envelope must be the same.", domainMercat, scales.getKey());
for (final double scale : scales.getValue()) {
ensureAlmostContains(scale, METER_SCALES);
}
final double minLimit = 2E-5;
final double maxLimit = 0.36;
final NumberRange<Double> ReducedRange = new NumberRange<>(Double.class, minLimit, true, maxLimit, true);
final Envelope simple84 = new Envelope2D(crs84, 10, 10, 5, 5);
GeneralEnvelope expectedResult = new GeneralEnvelope(new Envelope2D(crs84, 0, 0, 22.5, 22.5));
scales = CoverageUtilities.toWellKnownScale(simple84, ReducedRange);
Assert.assertTrue("Input envelope has not been adapted as expected.", expectedResult.equals(scales.getKey()));
//-- we want to reach at least minLimit resolution, also the last scale level
//-- may be smaller than it
final double minExpectedResolution = 1.07288360595703E-5;
for (final double scale : scales.getValue()) {
Assert.assertTrue("Computed scale is outside input limits. Expected between : ["+minExpectedResolution+"; "+maxLimit+"[, found : "+scale, scale >= minExpectedResolution && scale < maxLimit);
ensureAlmostContains(scale, DEGREE_SCALES);
}
}
/**
* Check that input double value is near (tolerance is defined with {@linkplain #EPSI}) a value of the given array.
* @param toCheck
* @param array
*/
private static void ensureAlmostContains(final double toCheck, double[] array) {
int insertPoint = Arrays.binarySearch(array, toCheck);
if (insertPoint < 0) {
insertPoint = ~insertPoint;
if (insertPoint <= array.length) {
if (array[insertPoint-1]+EPSI >= toCheck) {
return;
} else if (insertPoint < array.length) {
if (array[insertPoint]-EPSI <= toCheck) return;
}
}
Assert.fail("Computed scale is not part of Well-Known scales : " + toCheck);
}
}
}