/* Copyright 2002-2017 CS Systèmes d'Information * Licensed to CS Systèmes d'Information (CS) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * CS 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.orekit.models.earth.tessellation; import java.io.IOException; import java.util.List; import org.hipparchus.geometry.euclidean.threed.Vector3D; import org.hipparchus.geometry.partitioning.Region.Location; import org.hipparchus.geometry.partitioning.RegionFactory; import org.hipparchus.geometry.spherical.twod.S2Point; import org.hipparchus.geometry.spherical.twod.Sphere2D; import org.hipparchus.geometry.spherical.twod.SphericalPolygonsSet; import org.hipparchus.util.FastMath; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.orekit.Utils; import org.orekit.bodies.GeodeticPoint; import org.orekit.bodies.OneAxisEllipsoid; import org.orekit.errors.OrekitException; import org.orekit.frames.Frame; import org.orekit.frames.FramesFactory; import org.orekit.orbits.CircularOrbit; import org.orekit.orbits.Orbit; import org.orekit.orbits.PositionAngle; import org.orekit.time.AbsoluteDate; import org.orekit.time.TimeScalesFactory; import org.orekit.utils.Constants; import org.orekit.utils.IERSConventions; public class EllipsoidTessellatorTest { @Test public void testTilesAlongDescendingTrackWithoutTruncation() throws OrekitException { final EllipsoidTessellator tessellator = new EllipsoidTessellator(ellipsoid, new AlongTrackAiming(ellipsoid, orbit, false), 16); final List<List<Tile>> tiles = tessellator.tessellate(buildFrance(), 50000.0, 150000.0, 5000.0, 5000.0, false, false); Assert.assertEquals(2, tiles.size()); Assert.assertEquals(109, FastMath.max(tiles.get(0).size(), tiles.get(1).size())); Assert.assertEquals(4, FastMath.min(tiles.get(0).size(), tiles.get(1).size())); } @Test public void testTilesAlongDescendingTrackWithTruncation() throws OrekitException { final EllipsoidTessellator tessellator = new EllipsoidTessellator(ellipsoid, new AlongTrackAiming(ellipsoid, orbit, false), 16); final List<List<Tile>> tiles = tessellator.tessellate(buildFrance(), 50000.0, 150000.0, 5000.0, 5000.0, true, true); Assert.assertEquals(2, tiles.size()); Assert.assertEquals(108, FastMath.max(tiles.get(0).size(), tiles.get(1).size())); Assert.assertEquals(4, FastMath.min(tiles.get(0).size(), tiles.get(1).size())); } @Test public void testSampleAlongDescendingTrack() throws OrekitException { final EllipsoidTessellator tessellator = new EllipsoidTessellator(ellipsoid, new AlongTrackAiming(ellipsoid, orbit, false), 4); final List<List<GeodeticPoint>> samples = tessellator.sample(buildFrance(), 25000.0, 50000.0); Assert.assertEquals(2, samples.size()); Assert.assertEquals(452, FastMath.max(samples.get(0).size(), samples.get(1).size())); Assert.assertEquals(9, FastMath.min(samples.get(0).size(), samples.get(1).size())); } @Test public void testTilesAlongAscendingTrack() throws OrekitException { final EllipsoidTessellator tessellator = new EllipsoidTessellator(ellipsoid, new AlongTrackAiming(ellipsoid, orbit, true), 4); final List<List<Tile>> tiles = tessellator.tessellate(buildFrance(), 50000.0, 150000.0, 5000.0, 5000.0, false, false); Assert.assertEquals(2, tiles.size()); Assert.assertEquals(112, FastMath.max(tiles.get(0).size(), tiles.get(1).size())); Assert.assertEquals(6, FastMath.min(tiles.get(0).size(), tiles.get(1).size())); } @Test public void testSampleAlongAscendingTrack() throws OrekitException { final EllipsoidTessellator tessellator = new EllipsoidTessellator(ellipsoid, new AlongTrackAiming(ellipsoid, orbit, true), 4); final List<List<GeodeticPoint>> samples = tessellator.sample(buildFrance(), 25000.0, 50000.0); Assert.assertEquals(2, samples.size()); Assert.assertEquals(452, FastMath.max(samples.get(0).size(), samples.get(1).size())); Assert.assertEquals(10, FastMath.min(samples.get(0).size(), samples.get(1).size())); } @Test public void testTilesConstantAzimuth() throws OrekitException { final EllipsoidTessellator tessellator = new EllipsoidTessellator(ellipsoid, new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(120)), 4); final List<List<Tile>> tiles = tessellator.tessellate(buildFrance(), 50000.0, 150000.0, -5000.0, -5000.0, false, false); Assert.assertEquals(2, tiles.size()); Assert.assertEquals(86, FastMath.max(tiles.get(0).size(), tiles.get(1).size())); Assert.assertEquals(4, FastMath.min(tiles.get(0).size(), tiles.get(1).size())); checkTilesDontOverlap(tiles); } @Test public void testSampleConstantAzimuth() throws OrekitException { final EllipsoidTessellator tessellator = new EllipsoidTessellator(ellipsoid, new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(120)), 4); final List<List<GeodeticPoint>> samples = tessellator.sample(buildFrance(), 25000.0, 50000.0); Assert.assertEquals(2, samples.size()); Assert.assertEquals(455, FastMath.max(samples.get(0).size(), samples.get(1).size())); Assert.assertEquals(9, FastMath.min(samples.get(0).size(), samples.get(1).size())); } @Test public void testTilesIslandJoining() throws OrekitException { final EllipsoidTessellator tessellator = new EllipsoidTessellator(ellipsoid, new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(120.0)), 4); final List<List<Tile>> tiles = tessellator.tessellate(buildFrance(), 150000.0, 250000.0, -5000.0, -5000.0, false, false); Assert.assertEquals(1, tiles.size()); Assert.assertEquals(28, tiles.get(0).size()); checkTilesDontOverlap(tiles); } @Test public void testTilesSmallZoneWithoutTruncation() throws OrekitException, IOException { TileAiming aiming = new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(193.7)); EllipsoidTessellator tessellator = new EllipsoidTessellator(ellipsoid, aiming, 16); SphericalPolygonsSet small = buildSimpleZone(1.0e-10, new double[][] { { 43.6543, 1.4268 }, { 43.6120, 1.4179 }, { 43.6016, 1.3994 }, { 43.5682, 1.4159 }, { 43.5707, 1.4358 }, { 43.5573, 1.4941 }, { 43.6041, 1.4866 } }); final List<List<Tile>> tiles = tessellator.tessellate(small, 50000.0, 150000.0, 0, 0, false, false); Assert.assertEquals(1, tiles.size()); Assert.assertEquals(1, tiles.get(0).size()); Tile t = tiles.get(0).get(0); // without truncation, the tile must match width and length specification // (the remaining error is due to Cartesian distance and non-developable ellipsoid) Assert.assertEquals(150000.0, Vector3D.distance(ellipsoid.transform(t.getVertices()[0]), ellipsoid.transform(t.getVertices()[1])), 140.0); Assert.assertEquals( 50000.0, Vector3D.distance(ellipsoid.transform(t.getVertices()[1]), ellipsoid.transform(t.getVertices()[2])), 0.4); Assert.assertEquals(150000.0, Vector3D.distance(ellipsoid.transform(t.getVertices()[2]), ellipsoid.transform(t.getVertices()[3])), 140.0); Assert.assertEquals( 50000.0, Vector3D.distance(ellipsoid.transform(t.getVertices()[3]), ellipsoid.transform(t.getVertices()[0])), 0.4); } @Test public void testTilesSmallZoneWithTruncation() throws OrekitException, IOException { TileAiming aiming = new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(193.7)); EllipsoidTessellator tessellator = new EllipsoidTessellator(ellipsoid, aiming, 16); SphericalPolygonsSet small = buildSimpleZone(1.0e-10, new double[][] { { 43.6543, 1.4268 }, { 43.6120, 1.4179 }, { 43.6016, 1.3994 }, { 43.5682, 1.4159 }, { 43.5707, 1.4358 }, { 43.5573, 1.4941 }, { 43.6041, 1.4866 } }); final List<List<Tile>> tiles = tessellator.tessellate(small, 50000.0, 150000.0, 0, 0, true, true); Assert.assertEquals(1, tiles.size()); Assert.assertEquals(1, tiles.get(0).size()); Tile t = tiles.get(0).get(0); // with truncation, the tile is a fraction of the width and length specification Assert.assertEquals(3.0 / 16.0 * 150000.0, Vector3D.distance(ellipsoid.transform(t.getVertices()[0]), ellipsoid.transform(t.getVertices()[1])), 10.0); Assert.assertEquals(4.0 / 16.0 * 50000.0, Vector3D.distance(ellipsoid.transform(t.getVertices()[1]), ellipsoid.transform(t.getVertices()[2])), 0.01); Assert.assertEquals(3.0 / 16.0 * 150000.0, Vector3D.distance(ellipsoid.transform(t.getVertices()[2]), ellipsoid.transform(t.getVertices()[3])), 10.0); Assert.assertEquals(4.0 / 16.0 * 50000.0, Vector3D.distance(ellipsoid.transform(t.getVertices()[3]), ellipsoid.transform(t.getVertices()[0])), 0.01); } @Test public void testStairedTruncatedTiles() throws OrekitException { TileAiming aiming = new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(170.0)); EllipsoidTessellator tessellator = new EllipsoidTessellator(ellipsoid, aiming, 16); SphericalPolygonsSet small = buildSimpleZone(1.0e-10, new double[][] { { 45.335, 0.457 }, { 45.342, 0.469 }, { 45.371, 0.424 } }); final double maxWidth = 800.0; final double maxLength = 10000.0; final List<List<Tile>> tiles = tessellator.tessellate(small, maxWidth, maxLength, 0, 0, false, true); Assert.assertEquals(1, tiles.size()); Assert.assertEquals(4, tiles.get(0).size()); for (final Tile tile : tiles.get(0)) { Vector3D v0 = ellipsoid.transform(tile.getVertices()[0]); Vector3D v1 = ellipsoid.transform(tile.getVertices()[1]); Vector3D v2 = ellipsoid.transform(tile.getVertices()[2]); Vector3D v3 = ellipsoid.transform(tile.getVertices()[3]); Assert.assertTrue(Vector3D.distance(v0, v1) < (6.0002 / 16.0) * maxLength); Assert.assertTrue(Vector3D.distance(v2, v3) < (6.0002 / 16.0) * maxLength); Assert.assertEquals(maxWidth, Vector3D.distance(v1, v2), 1.0e-3); Assert.assertEquals(maxWidth, Vector3D.distance(v3, v0), 1.0e-3); } } @Test public void testTooThinRemainingRegion() throws OrekitException { TileAiming aiming = new ConstantAzimuthAiming(ellipsoid, -0.2185); EllipsoidTessellator tessellator = new EllipsoidTessellator(ellipsoid, aiming, 16); SphericalPolygonsSet small = buildSimpleZone(1.0e-10, new double[][]{ { 32.7342, -16.9407 }, { 32.7415, -16.9422 }, { 32.7481, -16.9463 }, { 32.7531, -16.9528 }, { 32.7561, -16.9608 }, { 32.7567, -16.9696 }, { 32.7549, -16.9781 }, { 32.7508, -16.9855 }, { 32.7450, -16.9909 }, { 32.7379, -16.9937 }, { 32.7305, -16.9937 }, { 32.7235, -16.9909 }, { 32.7177, -16.9855 }, { 32.7136, -16.9781 }, { 32.7118, -16.9696 }, { 32.7124, -16.9608 }, { 32.7154, -16.9528 }, { 32.7204, -16.9463 }, { 32.7269, -16.9422 } }); final double maxWidth = 40000.0; final double maxLength = 40000.0; final List<List<Tile>> tiles = tessellator.tessellate(small, maxWidth, maxLength, 0, 0, false, true); Assert.assertEquals(1, tiles.size()); Assert.assertEquals(1, tiles.get(0).size()); for (final Tile tile : tiles.get(0)) { Vector3D v0 = ellipsoid.transform(tile.getVertices()[0]); Vector3D v1 = ellipsoid.transform(tile.getVertices()[1]); Vector3D v2 = ellipsoid.transform(tile.getVertices()[2]); Vector3D v3 = ellipsoid.transform(tile.getVertices()[3]); Assert.assertTrue(Vector3D.distance(v0, v1) < 0.3 * maxLength); Assert.assertEquals(maxWidth, Vector3D.distance(v1, v2), 0.1); Assert.assertTrue(Vector3D.distance(v2, v3) < 0.3 * maxLength); Assert.assertEquals(maxWidth, Vector3D.distance(v3, v0), 0.1); } } @Test public void testNormalZoneTolerance() throws OrekitException { doTestVariableTolerance(1.0e-10); } @Test public void testLargeZoneTolerance() throws OrekitException { // this used to trigger an exception in EllipsoidTessellator.recurseMeetInside (Orekit) doTestVariableTolerance(1.0e-6); } @Test public void testHugeZoneTolerance() throws OrekitException { // this used to trigger an exception in Characterization.characterize (Apache Commons Math) // this was due to issue MATH-1266, solved in Apache Commons Math 3.6 doTestVariableTolerance(1.0e-4); } private void doTestVariableTolerance(final double tolerance) throws OrekitException { final ConstantAzimuthAiming aiming = new ConstantAzimuthAiming(ellipsoid, FastMath.toRadians(-168.178485)); EllipsoidTessellator tessellator = new EllipsoidTessellator(ellipsoid, aiming, 16); SphericalPolygonsSet small = buildSimpleZone(tolerance, new double[][]{ { -0.01048739, 0.01598931 }, { -0.00789627, 0.01555693 }, { -0.00558595, 0.01430664 }, { -0.00380677, 0.01237394 }, { -0.00275154, 0.00996826 }, { -0.00253461, 0.00735029 }, { -0.00317949, 0.00480374 }, { -0.00461629, 0.00260455 }, { -0.00668931, 0.00099105 }, { -0.00917392, 0.00013808 }, { -0.01180086, 0.00013808 }, { -0.01428546, 0.00099105 }, { -0.01635849, 0.00260455 }, { -0.01779529, 0.00480374 }, { -0.01844016, 0.00735029 }, { -0.01822323, 0.00996826 }, { -0.01716800, 0.01237394 }, { -0.01538882, 0.01430664 }, { -0.01307850, 0.01555693 } }); final double maxWidth = 40000.0; final double maxLength = 40000.0; final List<List<Tile>> tiles = tessellator.tessellate(small, maxWidth, maxLength, 0, 0, false, true); Assert.assertEquals(1, tiles.size()); Assert.assertEquals(1, tiles.get(0).size()); } private void checkTilesDontOverlap(final List<List<Tile>> tiles) { for (final List<Tile> list : tiles) { for (final Tile tile : list) { final SphericalPolygonsSet quadrilateral = new SphericalPolygonsSet(1.0e-10, toS2Point(tile.getVertices()[0]), toS2Point(tile.getVertices()[1]), toS2Point(tile.getVertices()[2]), toS2Point(tile.getVertices()[3])); for (final List<Tile> otherList : tiles) { for (final Tile otherTile : otherList) { if (otherTile != tile) { for (final GeodeticPoint vertex : otherTile.getVertices()) { Assert.assertEquals("tiles overlap at: " + vertex, Location.OUTSIDE, quadrilateral.checkPoint(toS2Point(vertex))); } } } } } } } private S2Point toS2Point(final GeodeticPoint point) { return new S2Point(point.getLongitude(), 0.5 * FastMath.PI - point.getLatitude()); } @Before public void setUp() throws OrekitException { Utils.setDataRoot("regular-data"); // the following orbital parameters have been computed using // Orekit tutorial about phasing, using the following configuration: // // orbit.date = 2012-01-01T00:00:00.000 // phasing.orbits.number = 143 // phasing.days.number = 10 // sun.synchronous.reference.latitude = 0 // sun.synchronous.reference.ascending = false // sun.synchronous.mean.solar.time = 10:30:00 // gravity.field.degree = 12 // gravity.field.order = 12 AbsoluteDate date = new AbsoluteDate("2012-01-01T00:00:00.000", TimeScalesFactory.getUTC()); Frame eme2000 = FramesFactory.getEME2000(); orbit = new CircularOrbit(7173352.811913891, -4.029194321683225E-4, 0.0013530362644647786, FastMath.toRadians(98.63218182243709), FastMath.toRadians(77.55565567747836), FastMath.PI, PositionAngle.TRUE, eme2000, date, Constants.EIGEN5C_EARTH_MU); ellipsoid = new OneAxisEllipsoid(Constants.WGS84_EARTH_EQUATORIAL_RADIUS, Constants.WGS84_EARTH_FLATTENING, FramesFactory.getITRF(IERSConventions.IERS_2010, true)); } private SphericalPolygonsSet buildFrance() { final SphericalPolygonsSet continental = buildSimpleZone(1.0e-10, 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 } }); final SphericalPolygonsSet corsica = EllipsoidTessellator.buildSimpleZone(1.0e-10, new GeodeticPoint(FastMath.toRadians(42.15249), FastMath.toRadians(9.56001), 0.0), new GeodeticPoint(FastMath.toRadians(43.00998), FastMath.toRadians(9.39000), 0.0), new GeodeticPoint(FastMath.toRadians(42.62812), FastMath.toRadians(8.74600), 0.0), new GeodeticPoint(FastMath.toRadians(42.25651), FastMath.toRadians(8.54421), 0.0), new GeodeticPoint(FastMath.toRadians(41.58361), FastMath.toRadians(8.77572), 0.0), new GeodeticPoint(FastMath.toRadians(41.38000), FastMath.toRadians(9.22975), 0.0)); return (SphericalPolygonsSet) new RegionFactory<Sphere2D>().union(continental, corsica); } private SphericalPolygonsSet buildSimpleZone(double tolerance, double[][] points) { for (int i = 0; i < points.length; ++i) { points[i][0] = FastMath.toRadians(points[i][0]); points[i][1] = FastMath.toRadians(points[i][1]); } return EllipsoidTessellator.buildSimpleZone(tolerance, points); } private Orbit orbit; private OneAxisEllipsoid ellipsoid; }