001/* 002 * GeoAPI - Java interfaces for OGC/ISO standards 003 * Copyright © 2011-2024 Open Geospatial Consortium, Inc. 004 * http://www.geoapi.org 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); 007 * you may not use this file except in compliance with the License. 008 * You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.opengis.test.referencing; 019 020import java.util.Arrays; 021import java.util.EnumSet; 022import java.util.Collections; 023import java.util.Random; 024import java.awt.geom.Rectangle2D; 025 026import org.opengis.util.FactoryException; 027import org.opengis.util.NoSuchIdentifierException; 028import org.opengis.geometry.DirectPosition; 029import org.opengis.parameter.ParameterValueGroup; 030import org.opengis.parameter.ParameterDescriptorGroup; 031import org.opengis.referencing.crs.ProjectedCRS; 032import org.opengis.referencing.operation.Matrix; 033import org.opengis.referencing.operation.MathTransform; 034import org.opengis.referencing.operation.MathTransform2D; 035import org.opengis.referencing.operation.TransformException; 036import org.opengis.referencing.operation.MathTransformFactory; 037import org.opengis.referencing.operation.Conversion; 038import org.opengis.referencing.operation.Transformation; 039import org.opengis.referencing.operation.SingleOperation; 040import org.opengis.test.ToleranceModifiers; 041import org.opengis.test.ToleranceModifier; 042import org.opengis.test.CalculationType; 043import org.opengis.test.Configuration; 044 045import org.junit.jupiter.api.Test; 046 047import static java.lang.StrictMath.*; 048import static org.junit.jupiter.api.Assertions.*; 049import static org.junit.jupiter.api.Assumptions.*; 050import static org.opengis.test.ToleranceModifiers.NAUTICAL_MILE; 051import static org.opengis.test.referencing.AffineTransformTest.NO_FACTORY; 052 053 054/** 055 * Tests {@linkplain MathTransformFactory#createParameterizedTransform(ParameterValueGroup) 056 * parameterized math transforms} from the {@code org.opengis.referencing.operation} package. 057 * Math transform instances are created using the factory given at construction time. 058 * 059 * <h2>Skipping tests for unsupported operations</h2> 060 * If the tested factory throws a {@link NoSuchIdentifierException} during the invocation 061 * of one of the following methods: 062 * 063 * <ul> 064 * <li>{@link MathTransformFactory#getDefaultParameters(String)}</li> 065 * <li>{@link MathTransformFactory#createParameterizedTransform(ParameterValueGroup)}</li> 066 * </ul> 067 * 068 * then the tests is skipped. If any other kind of exception is thrown, or if {@code NoSuchIdentifierException} 069 * is thrown under other circumstances than the invocation of above methods, then the test fails. 070 * 071 * <h2>Tests and accuracy</h2> 072 * By default, every tests expect an accuracy of 1 centimetre. This accuracy matches the precision 073 * of most example points given in the EPSG guidance notice. Implementers can modify the kind of 074 * tests being executed and the tolerance threshold in different ways: 075 * 076 * <ul> 077 * <li>Set some <code>is<<var>Operation</var>>Supported</code> fields to {@code false}.</li> 078 * <li>Override some of the {@code testFoo()} method and set the {@link #tolerance tolerance} field 079 * before to invoke {@code super.testFoo()}.</li> 080 * <li>Override {@link #normalize(DirectPosition, DirectPosition, CalculationType) 081 * normalize(DirectPosition, DirectPosition, CalculationType)}.</li> 082 * <li>Override {@link #assertMatrixEquals(Matrix, Matrix, Matrix, String)}.</li> 083 * </ul> 084 * 085 * <h2>Usage example</h2> 086 * in order to specify their factories and run the tests in a JUnit framework, implementers can define 087 * a subclass in their own test suite as in the example below. That example shows also how implementers 088 * can alter some tests (here the tolerance value for the <cite>Lambert Azimuthal Equal Area</cite> projection) 089 * and add more checks to be executed after every tests (here ensuring that the {@linkplain #transform transform} 090 * implements the {@link MathTransform2D} interface): 091 * 092 * {@snippet lang="java" : 093 * import org.junit.jupiter.api.Test; 094 * import org.opengis.test.referencing.ParameterizedTransformTest; 095 * import static org.junit.jupiter.api.Assertions.*; 096 * 097 * public class MyTest extends ParameterizedTransformTest { 098 * public MyTest() { 099 * super(new MyMathTransformFactory()); 100 * } 101 * 102 * @Test 103 * @Override 104 * public void testLambertAzimuthalEqualArea() throws FactoryException, TransformException { 105 * tolerance = 0.1; // Increase the tolerance value to 10 cm. 106 * super.testLambertAzimuthalEqualArea(); 107 * // If more tests specific to this projection are wanted, do them here. 108 * // In this example, we replace the ellipsoid by a sphere and test again. 109 * // Note that spherical formulas can have an error up to 30 km compared 110 * // to ellipsoidal formulas, so we have to relax again the tolerance threshold. 111 * parameters.parameter("semi_minor").setValue(parameters.parameter("semi_major").doubleValue()); 112 * tolerance = 30000; // Increase the tolerance value to 30 km. 113 * super.testLambertAzimuthalEqualArea(); 114 * } 115 * 116 * @After 117 * public void ensureAllTransformAreMath2D() { 118 * assertTrue(transform instanceof MathTransform2D); 119 * } 120 * }} 121 * 122 * @see AffineTransformTest 123 * @see AuthorityFactoryTest 124 * 125 * @author Martin Desruisseaux (Geomatys) 126 * @version 3.1 127 * @since 3.1 128 */ 129@SuppressWarnings("strictfp") // Because we still target Java 11. 130public strictfp class ParameterizedTransformTest extends TransformTestCase { 131 /** 132 * The default tolerance threshold for comparing the results of direct transforms. 133 * This is set to the precision of coordinate point givens in the EPSG and IGNF 134 * documentation. 135 */ 136 private static final double TRANSFORM_TOLERANCE = 0.01; 137 138 /** 139 * The tolerance threshold for comparing the derivative coefficients. In each column of the 140 * derivative matrix of a map projection, there is typically one value greater than 100000 141 * (100 km - same order of magnitude than the transformed coordinate values) and all other 142 * values are close to zero. However, we cannot use the {@link #TRANSFORM_TOLERANCE} value 143 * in every cases because the expected derivative coefficients are computed using a numerical 144 * approximation. Some empirical tests have show that the difference between <i>forward 145 * difference</i> and <i>backward difference</i> can be close to 0.25, so we must 146 * be prepared to increase this tolerance threshold. 147 */ 148 private static final double DERIVATIVE_TOLERANCE = 0.01; 149 150 /** 151 * The delta value to use for computing an approximation of the derivative by finite 152 * difference, in metres. The conversion from metres to degrees is performed using 153 * the standard length of a nautical mile. 154 * 155 * <p>The 100 metres value has been determined empirically as a good compromise for map 156 * projections. Experience suggests that smaller values often <em>decrease</em> the 157 * precision, because of floating point errors when subtracting big numbers that are 158 * close in magnitude.</p> 159 */ 160 private static final double DERIVATIVE_DELTA = 100; 161 162 /** 163 * The factory for creating {@link MathTransform} objects, or {@code null} if none. 164 */ 165 protected final MathTransformFactory mtFactory; 166 167 /** 168 * The parameters of the math transform being tested. This field is set, together with the 169 * {@link #transform transform} field, during the execution of every {@code testFoo()} method 170 * in this class. 171 * 172 * <p>If this field is non-null before a test is run, then those parameters will be used directly. 173 * This allow implementers to alter the parameters before to run the test one more time.</p> 174 */ 175 protected ParameterValueGroup parameters; 176 177 /** 178 * A description of the test being run. This field is provided only for information purpose 179 * (typically for producing logging or error messages); it is not actually used by the tests. 180 * The value can be: 181 * 182 * <ul> 183 * <li>The name of the target {@link ProjectedCRS} when the {@linkplain #transform transform} 184 * being tested is a map projection</li> 185 * <li>The transformation name when the {@linkplain #transform transform} being tested is a 186 * datum shift operation.</li> 187 * </ul> 188 */ 189 protected String description; 190 191 /** 192 * Creates a new test without factory and with the given {@code isFooSupported} flags. 193 * The given array must be the result of a call to {@link #getEnabledKeys(int)}. 194 * 195 * @param isEnabled the enabled status of all options. 196 */ 197 ParameterizedTransformTest(final boolean[] isEnabled) { 198 super(isEnabled); 199 mtFactory = null; 200 } 201 202 /** 203 * Creates a new test using the given factory. If the given factory is {@code null}, 204 * then the tests will be skipped. 205 * 206 * @param factory factory for creating {@link MathTransform} instances. 207 */ 208 public ParameterizedTransformTest(final MathTransformFactory factory) { 209 this.mtFactory = factory; 210 } 211 212 /** 213 * Returns information about the configuration of the test which has been run. 214 * This method returns a map containing: 215 * 216 * <ul> 217 * <li>All the entries defined in the {@linkplain TransformTestCase#configuration() parent class}.</li> 218 * <li>All the following values associated to the {@link org.opengis.test.Configuration.Key} of the same name: 219 * <ul> 220 * <li>{@link #mtFactory}</li> 221 * </ul> 222 * </li> 223 * </ul> 224 * 225 * @return {@inheritDoc} 226 */ 227 @Override 228 public Configuration configuration() { 229 final Configuration op = super.configuration(); 230 assertNull(op.put(Configuration.Key.mtFactory, mtFactory)); 231 return op; 232 } 233 234 /** 235 * Invoked for preparing the header of a test failure message. 236 * 237 * @param buffer the buffer in which to append the header. 238 * @param message user supplied message to append, or {@code null}. 239 */ 240 @Override 241 final void appendErrorHeader(final StringBuilder buffer, final String message) { 242 if (description != null) { 243 buffer.append("Error in “").append(description).append("”: "); 244 } 245 super.appendErrorHeader(buffer, message); 246 } 247 248 /** 249 * Returns the error message for an unsupported operation method. 250 * 251 * @param name the operation method for which to return an error message. 252 * @return error message for an unsupported operation method. 253 */ 254 private static String unsupportedMethod(final String name) { 255 return "The “" + name + "” operation method is not supported by the tested implementation."; 256 } 257 258 /** 259 * Initialized the {@link #parameters} field to the default values for the given operation method. 260 * If the tested implementation does not support the specified operation method, then the test will 261 * be skipped. 262 * 263 * @param method the operation method for which to set parameter values. 264 */ 265 private void createParameters(final String method) { 266 assumeTrue(mtFactory != null, NO_FACTORY); 267 try { 268 parameters = mtFactory.getDefaultParameters(method); 269 } catch (NoSuchIdentifierException e) { 270 abort(unsupportedMethod(method)); // Will mark the test as "ignored". 271 } 272 } 273 274 /** 275 * Creates a math transform for the coordinate operation identified by {@link SamplePoints#operation} 276 * and stores the result in the {@link #transform} field. 277 * The set of allowed codes is documented in second column of the 278 * {@link PseudoEpsgFactory#createParameters(int)} method. 279 * 280 * @param type either {@code Conversion.class} or {@code Transformation.class}. 281 * @param sample the points which will be transformed. 282 * @throws FactoryException if the math transform cannot be created. 283 */ 284 private void createMathTransform(final Class<? extends SingleOperation> type, final SamplePoints sample) 285 throws FactoryException 286 { 287 try { 288 if (parameters == null) { 289 assumeTrue(mtFactory != null, NO_FACTORY); 290 parameters = PseudoEpsgFactory.createParameters(mtFactory, sample.operation); 291 validators.validate(parameters); 292 } 293 if (transform == null) { 294 assumeTrue(mtFactory != null, NO_FACTORY); 295 transform = mtFactory.createParameterizedTransform(parameters); 296 assertNotNull(transform, description); 297 validators.validate(transform); 298 } 299 } catch (NoSuchIdentifierException e) { 300 /* 301 * If a code was not found, ensure that the factory does not declare that it was 302 * a supported code. If the code was unsupported, then the test will be ignored. 303 */ 304 final String message; 305 if (parameters != null) { 306 final ParameterDescriptorGroup descriptor = parameters.getDescriptor(); 307 if (!Collections.disjoint(Utilities.getNameAndAliases(descriptor), 308 Utilities.getNameAndAliases(mtFactory.getAvailableMethods(type)))) 309 { 310 throw e; // Will mark the test as "failed". 311 } 312 message = unsupportedMethod(Utilities.getName(descriptor)); 313 } else { 314 message = "The “EPSG:" + sample.operation + "” coordinate operation uses the “" + e.getIdentifierCode() 315 + "” method, which is not supported by the tested implementation."; 316 } 317 abort(message); // Will mark the test as "ignored". 318 } 319 } 320 321 /** 322 * Initializes the tolerance thresholds to their default values if the user did not specified custom thresholds. 323 * This method sets the {@linkplain TransformTestCase#tolerance tolerance} threshold in units of the target CRS 324 * (typically <var>metres</var> for map projections), and the {@linkplain #derivativeDeltas derivative deltas} 325 * in units of the source CRS (typically <var>degrees</var> for map projections). 326 * The current implementation sets the following values: 327 * 328 * <ul> 329 * <li>{@link #tolerance} is sets to {@link #TRANSFORM_TOLERANCE}, unless a greater 330 * tolerance threshold is already set in which case the existing value is left 331 * unchanged.</li> 332 * <li>{@link #derivativeDeltas} is set to a value in degrees corresponding to 333 * approximately 1 metre on Earth (calculated using the standard nautical mile length). 334 * A finer value can lead to more accurate derivative approximation by the 335 * {@link #verifyDerivative(double[]) verifyDerivative(double...)} method, 336 * at the expense of more sensitivity to the accuracy of the 337 * {@link MathTransform#transform MathTransform.transform(…)} method being tested.</li> 338 * </ul> 339 * 340 * This method should be invoked <strong>after</strong> {@link #createMathTransform(Class, SamplePoints)} 341 * because because it needs to know the number of dimensions. 342 * 343 * @param modifier the tolerance modifier to apply. 344 */ 345 final void setTolerance(final ToleranceModifier modifier) { 346 if (toleranceModifier == null) { 347 toleranceModifier = modifier; 348 } 349 if (!(tolerance >= TRANSFORM_TOLERANCE)) { // !(a >= b) instead of (a < b) in order to catch NaN. 350 tolerance = TRANSFORM_TOLERANCE; 351 } 352 if (derivativeDeltas == null) { 353 derivativeDeltas = new double[transform.getSourceDimensions()]; 354 Arrays.fill(derivativeDeltas, DERIVATIVE_DELTA / (60 * NAUTICAL_MILE)); 355 } 356 } 357 358 /** 359 * Applies a unit conversion on the given coordinate values. This method is invoked by 360 * {@link AuthorityFactoryTest} before to test a {@link ProjectedCRS} using different 361 * units than the standard one. In addition to scale the units, this method scales also 362 * the tolerance factor by the same factor. 363 * 364 * @param mode {@link CalculationType#DIRECT_TRANSFORM} for scaling the output units (from 365 * metres to another linear unit), or {@link CalculationType#INVERSE_TRANSFORM} for 366 * scaling the input units (from degrees to another angular unit). 367 * @param coordinates the source or expected target points to scale. 368 * @param scale the scale factor, from standard units to the CRS units. 369 */ 370 final void applyUnitConversion(final CalculationType mode, final double[] coordinates, final double scale) { 371 for (int i=coordinates.length; --i>=0;) { 372 coordinates[i] *= scale; 373 } 374 toleranceModifier = ToleranceModifiers.concatenate(toleranceModifier, 375 ToleranceModifiers.scale(EnumSet.of(mode), scale, scale)); 376 } 377 378 /** 379 * Tests the transform consistency using many random points inside the area of validity. 380 * 381 * @param areaOfValidity the domain in which to create test points. 382 * @throws TransformException if a point cannot be transformed. 383 */ 384 final void verifyInDomainOfValidity(final Rectangle2D areaOfValidity) throws TransformException { 385 verifyInDomain(new double[] { 386 areaOfValidity.getMinX(), 387 areaOfValidity.getMinY() 388 }, new double[] { 389 areaOfValidity.getMaxX(), 390 areaOfValidity.getMaxY() 391 }, new int[] { 392 20, 20 393 }, new Random()); 394 } 395 396 /** 397 * Tests the <q>Mercator (variant A)</q> (EPSG:9804) projection method. 398 * First, this method transforms the point given in the example section of the 399 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 400 * Next, this method transforms a random set of points in the projection area of validity 401 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 402 * {@linkplain MathTransform#derivative derivatives} are coherent. 403 * 404 * <p>The math transform parameters and the sample coordinates are:</p> 405 * 406 * <div class="horizontal-flow"> 407 * <table class="ogc"> 408 * <caption>CRS characteristics</caption> 409 * <tr><th>Parameter</th> <th>Value</th></tr> 410 * <tr><td>semi-major axis</td> <td>6377397.155 m</td></tr> 411 * <tr><td>semi-minor axis</td> <td>6356078.962818189 m</td></tr> 412 * <tr><td>Latitude of natural origin</td> <td>0.0°</td></tr> 413 * <tr><td>Longitude of natural origin</td> <td>110.0°</td></tr> 414 * <tr><td>Scale factor at natural origin</td> <td>0.997</td></tr> 415 * <tr><td>False easting</td> <td>3900000.0 m</td></tr> 416 * <tr><td>False northing</td> <td>900000.0 m</td></tr> 417 * </table> 418 * <table class="ogc"> 419 * <caption>Test points</caption> 420 * <tr> 421 * <th>Source coordinates</th> 422 * <th>Expected results</th> 423 * </tr><tr class="coordinates"> 424 * <td>120°E<br>3°S</td> 425 * <td>5009726.58 m<br>569150.82 m</td> 426 * </tr><tr class="coordinates"> 427 * <td>110°E<br>0°N</td> 428 * <td>3900000.00 m<br>900000.00 m</td> 429 * </tr> 430 * </table> 431 * </div> 432 * 433 * @throws FactoryException if the math transform cannot be created. 434 * @throws TransformException if the example point cannot be transformed. 435 * 436 * @see AuthorityFactoryTest#testEPSG_3002() 437 */ 438 @Test 439 public void testMercator1SP() throws FactoryException, TransformException { 440 description = "Makassar / NEIEZ"; 441 final SamplePoints sample = SamplePoints.forCRS(3002); 442 createMathTransform(Conversion.class, sample); 443 setTolerance(ToleranceModifier.PROJECTION); 444 verifyTransform(sample.sourcePoints, sample.targetPoints); 445 verifyInDomainOfValidity(sample.areaOfValidity); 446 } 447 448 /** 449 * Tests the <q>Mercator (variant B)</q> (EPSG:9805) projection method. 450 * First, this method transforms the point given in the example section of the 451 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 452 * Next, this method transforms a random set of points in the projection area of validity 453 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 454 * {@linkplain MathTransform#derivative derivatives} are coherent. 455 * 456 * <p>The math transform parameters and the sample coordinates are:</p> 457 * 458 * <div class="horizontal-flow"> 459 * <table class="ogc"> 460 * <caption>CRS characteristics</caption> 461 * <tr><th>Parameter</th> <th>Value</th></tr> 462 * <tr><td>semi-major axis</td> <td>6378245.0 m</td></tr> 463 * <tr><td>semi-minor axis</td> <td>6356863.018773047 m</td></tr> 464 * <tr><td>Latitude of 1st standard parallel</td> <td>42.0°</td></tr> 465 * <tr><td>Longitude of natural origin</td> <td>51.0°</td></tr> 466 * <tr><td>False easting</td> <td>0.0 m</td></tr> 467 * <tr><td>False northing</td> <td>0.0 m</td></tr> 468 * </table> 469 * <table class="ogc"> 470 * <caption>Test points</caption> 471 * <tr> 472 * <th>Source coordinates</th> 473 * <th>Expected results</th> 474 * </tr><tr class="coordinates"> 475 * <td>53°E<br>53°N</td> 476 * <td>165704.29 m<br>5171848.07 m</td> 477 * </tr><tr class="coordinates"> 478 * <td>51°E<br>0°N</td> 479 * <td>0.00 m<br>0.00 m</td> 480 * </tr> 481 * </table> 482 * </div> 483 * 484 * @throws FactoryException if the math transform cannot be created. 485 * @throws TransformException if the example point cannot be transformed. 486 * 487 * @see AuthorityFactoryTest#testEPSG_3388() 488 */ 489 @Test 490 public void testMercator2SP() throws FactoryException, TransformException { 491 description = "Pulkovo 1942 / Caspian Sea Mercator"; 492 final SamplePoints sample = SamplePoints.forCRS(3388); 493 createMathTransform(Conversion.class, sample); 494 setTolerance(ToleranceModifier.PROJECTION); 495 verifyTransform(sample.sourcePoints, sample.targetPoints); 496 verifyInDomainOfValidity(sample.areaOfValidity); 497 } 498 499 /** 500 * Tests the <q>Mercator (variant C)</q> (EPSG:1044) projection method. 501 * First, this method transforms the point given in the example section of the 502 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 503 * Next, this method transforms a random set of points in the projection area of validity 504 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 505 * {@linkplain MathTransform#derivative derivatives} are coherent. 506 * 507 * <p>The math transform parameters and the sample coordinates are below. 508 * Note that this is similar to {@link #testMercator2SP()}, except that the 509 * <q>latitude of false origin</q> parameter is set to 42°N.</p> 510 * 511 * <div class="horizontal-flow"> 512 * <table class="ogc"> 513 * <caption>CRS characteristics</caption> 514 * <tr><th>Parameter</th> <th>Value</th></tr> 515 * <tr><td>semi-major axis</td> <td>6378245.0 m</td></tr> 516 * <tr><td>semi-minor axis</td> <td>6356863.018773047 m</td></tr> 517 * <tr><td>Latitude of 1st standard parallel</td> <td>42.0°</td></tr> 518 * <tr><td>Longitude of natural origin</td> <td>51.0°</td></tr> 519 * <tr><td>Latitude of false origin</td> <td>42.0°</td></tr> 520 * <tr><td>Easting at false origin</td> <td>0.0 m</td></tr> 521 * <tr><td>Northing at false origin</td> <td>0.0 m</td></tr> 522 * </table> 523 * <table class="ogc"> 524 * <caption>Test points</caption> 525 * <tr> 526 * <th>Source coordinates</th> 527 * <th>Expected results</th> 528 * </tr><tr class="coordinates"> 529 * <td>53°E<br>53°N</td> 530 * <td>165704.29 m<br>1351950.22 m</td> 531 * </tr><tr class="coordinates"> 532 * <td>51°E<br>42°N</td> 533 * <td>0.00 m<br>0.00 m</td> 534 * </tr> 535 * </table> 536 * </div> 537 * 538 * @throws FactoryException if the math transform cannot be created. 539 * @throws TransformException if the example point cannot be transformed. 540 */ 541 @Test 542 public void testMercatorVariantC() throws FactoryException, TransformException { 543 description = "Pulkovo 1942 / Caspian Sea Mercator"; 544 final SamplePoints sample = SamplePoints.forCRS(3388); 545 sample.targetPoints[1] = 1351950.22; // New Northing value for 53°N. 546 sample.sourcePoints[3] = 42; // New latitude where we expect a northing of 0 m. 547 /* 548 * Following is basically a copy-and-paste of PseudoEpsgFactory.createParameters(mtFactory, 3388) 549 * with a different projection ("variant C" instead of "variant B") and one more parameter value 550 * (the "Latitude of false origin"). 551 */ 552 createParameters("Mercator (variant C)"); 553 parameters.parameter("semi_major").setValue(6378245.0); // Krassowski 1940 554 parameters.parameter("semi_minor").setValue(6378245.0 * (1 - 1/298.3)); 555 parameters.parameter("Latitude of 1st standard parallel").setValue(42.0); 556 parameters.parameter("Longitude of natural origin") .setValue(51.0); 557 parameters.parameter("Latitude of false origin") .setValue(42.0); 558 validators.validate(parameters); 559 /* 560 * Following is common to all tests in this class. 561 */ 562 createMathTransform(Conversion.class, sample); 563 setTolerance(ToleranceModifier.PROJECTION); 564 verifyTransform(sample.sourcePoints, sample.targetPoints); 565 verifyInDomainOfValidity(sample.areaOfValidity); 566 } 567 568 /** 569 * Tests the <q>Mercator (Spherical)</q> (EPSG:1026) projection method. 570 * First, this method transforms the point given in the example section of the 571 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 572 * Next, this method transforms a random set of points in the projection area of validity 573 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 574 * {@linkplain MathTransform#derivative derivatives} are coherent. 575 * 576 * <p>The math transform parameters and the sample coordinates are below. 577 * Note that the sample point is the same as for {@link #testPseudoMercator()}, 578 * but with a different result in projected coordinates.</p> 579 * 580 * <div class="horizontal-flow"> 581 * <table class="ogc"> 582 * <caption>CRS characteristics</caption> 583 * <tr><th>Parameter</th> <th>Value</th></tr> 584 * <tr><td>semi-major axis</td> <td>6371007.0 m</td></tr> 585 * <tr><td>semi-minor axis</td> <td>6371007.0 m</td></tr> 586 * <tr><td>Longitude of natural origin</td> <td>0.0°</td></tr> 587 * <tr><td>False easting</td> <td>0.0 m</td></tr> 588 * <tr><td>False northing</td> <td>0.0 m</td></tr> 589 * </table> 590 * <table class="ogc"> 591 * <caption>Test points</caption> 592 * <tr> 593 * <th>Source coordinates</th> 594 * <th>Expected results</th> 595 * </tr><tr class="coordinates"> 596 * <td>100°20'00.000"W<br>24°22'54.433"N</td> 597 * <td>-11156569.90 m<br>2796869.94 m</td> 598 * </tr><tr class="coordinates"> 599 * <td>0°E<br>0°N</td> 600 * <td>0.00 m<br>0.00 m</td> 601 * </tr> 602 * </table> 603 * </div> 604 * 605 * @throws FactoryException if the math transform cannot be created. 606 * @throws TransformException if the example point cannot be transformed. 607 */ 608 @Test 609 public void testMercatorSpherical() throws FactoryException, TransformException { 610 description = "World Spherical Mercator"; 611 final SamplePoints sample = SamplePoints.forCRS(3857); 612 sample.targetPoints[0] = -11156569.90; // New Easting value. 613 sample.targetPoints[1] = 2796869.94; // New Northing value. 614 createParameters("Mercator (Spherical)"); 615 parameters.parameter("semi_major").setValue(6371007.0); 616 parameters.parameter("semi_minor").setValue(6371007.0); 617 validators.validate(parameters); 618 createMathTransform(Conversion.class, sample); 619 setTolerance(ToleranceModifier.PROJECTION); 620 verifyTransform(sample.sourcePoints, sample.targetPoints); 621 verifyInDomainOfValidity(sample.areaOfValidity); 622 } 623 624 /** 625 * Tests the <q>Mercator Popular Visualisation Pseudo Mercator</q> (EPSG:1024) projection 626 * method. First, this method transforms the point given in the example section of the 627 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 628 * Next, this method transforms a random set of points in the projection area of validity 629 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 630 * {@linkplain MathTransform#derivative derivatives} are coherent. 631 * 632 * <p>The math transform parameters and the sample coordinates are:</p> 633 * 634 * <div class="horizontal-flow"> 635 * <table class="ogc"> 636 * <caption>CRS characteristics</caption> 637 * <tr><th>Parameter</th> <th>Value</th></tr> 638 * <tr><td>semi-major axis</td> <td>6378137.0 m</td></tr> 639 * <tr><td>semi-minor axis</td> <td>6356752.314247833 m</td></tr> 640 * <tr><td>Latitude of natural origin</td> <td>0.0°</td></tr> 641 * <tr><td>Longitude of natural origin</td> <td>0.0°</td></tr> 642 * <tr><td>False easting</td> <td>0.0 m</td></tr> 643 * <tr><td>False northing</td> <td>0.0 m</td></tr> 644 * </table> 645 * <table class="ogc"> 646 * <caption>Test points</caption> 647 * <tr> 648 * <th>Source coordinates</th> 649 * <th>Expected results</th> 650 * </tr><tr class="coordinates"> 651 * <td>100°20'00.000"W<br>24°22'54.433"N</td> 652 * <td>-11169055.58 m<br>2800000.00 m</td> 653 * </tr><tr class="coordinates"> 654 * <td>0°E<br>0°N</td> 655 * <td>0.00 m<br>0.00 m</td> 656 * </tr> 657 * </table> 658 * </div> 659 * 660 * @throws FactoryException if the math transform cannot be created. 661 * @throws TransformException if the example point cannot be transformed. 662 * 663 * @see AuthorityFactoryTest#testEPSG_3857() 664 */ 665 @Test 666 public void testPseudoMercator() throws FactoryException, TransformException { 667 description = "WGS 84 / Pseudo-Mercator"; 668 final SamplePoints sample = SamplePoints.forCRS(3857); 669 createMathTransform(Conversion.class, sample); 670 setTolerance(ToleranceModifier.PROJECTION); 671 verifyTransform(sample.sourcePoints, sample.targetPoints); 672 verifyInDomainOfValidity(sample.areaOfValidity); 673 } 674 675 /** 676 * Tests the <q>IGNF:MILLER</q> projection. 677 * First, this method transforms the point given below 678 * and compares the {@link MathTransform} result with the expected result. Next, this method 679 * transforms a random set of points in the projection area of validity and ensures that the 680 * {@linkplain MathTransform#inverse() inverse transform} and the 681 * {@linkplain MathTransform#derivative derivatives} are coherent. 682 * 683 * <p>The math transform parameters and the sample coordinates are:</p> 684 * 685 * <div class="horizontal-flow"> 686 * <table class="ogc"> 687 * <caption>CRS characteristics</caption> 688 * <tr><th>Parameter</th> <th>Value</th></tr> 689 * <tr><td>semi_major</td> <td>6378137.0 m</td></tr> 690 * <tr><td>semi_minor</td> <td>6378137.0 m</td></tr> 691 * <tr><td>central_meridian</td> <td>0.0°</td></tr> 692 * <tr><td>false_easting</td> <td>0.0 m</td></tr> 693 * <tr><td>false_northing</td> <td>0.0 m</td></tr> 694 * </table> 695 * <table class="ogc"> 696 * <caption>Test points</caption> 697 * <tr> 698 * <th>Source coordinates</th> 699 * <th>Expected results</th> 700 * </tr><tr class="coordinates"> 701 * <td>2.478917°E<br>48.805639°N</td> 702 * <td>275951.78 m<br>5910061.78 m</td> 703 * </tr><tr class="coordinates"> 704 * <td>0°E<br>0°N</td> 705 * <td>0.00 m<br>0.00 m</td> 706 * </tr> 707 * </table> 708 * </div> 709 * 710 * @throws FactoryException if the math transform cannot be created. 711 * @throws TransformException if the example point cannot be transformed. 712 */ 713 @Test 714 public void testMiller() throws FactoryException, TransformException { 715 description = "IGNF:MILLER"; 716 final SamplePoints sample = SamplePoints.forCRS(310642901); 717 createMathTransform(Conversion.class, sample); 718 setTolerance(ToleranceModifier.PROJECTION); 719 verifyTransform(sample.sourcePoints, sample.targetPoints); 720 verifyInDomainOfValidity(sample.areaOfValidity); 721 } 722 723 /** 724 * Tests the <q>Hotine Oblique Mercator (variant B)</q> (EPSG:9815) projection method. 725 * First, this method transforms the point given in the example section of the 726 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 727 * Next, this method transforms a random set of points in the projection area of validity 728 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 729 * {@linkplain MathTransform#derivative derivatives} are coherent. 730 * 731 * <p>The math transform parameters and the sample coordinates are:</p> 732 * 733 * <div class="horizontal-flow"> 734 * <table class="ogc"> 735 * <caption>CRS characteristics</caption> 736 * <tr><th>Parameter</th> <th>Value</th></tr> 737 * <tr><td>semi-major axis</td> <td>6377298.556 m</td></tr> 738 * <tr><td>semi-minor axis</td> <td>6356097.550300896 m</td></tr> 739 * <tr><td>Latitude of projection centre</td> <td>4.0°</td></tr> 740 * <tr><td>Longitude of projection centre</td> <td>109.6855202029758°</td></tr> 741 * <tr><td>Azimuth of initial line</td> <td>53.31582047222222°</td></tr> 742 * <tr><td>Angle from Rectified to Skew Grid</td> <td>53.13010236111111°</td></tr> 743 * <tr><td>Scale factor on initial line</td> <td>0.99984</td></tr> 744 * <tr><td>Easting at projection centre</td> <td>590476.87 m</td></tr> 745 * <tr><td>Northing at projection centre</td> <td>442857.65 m</td></tr> 746 * </table> 747 * <table class="ogc"> 748 * <caption>Test points</caption> 749 * <tr> 750 * <th>Source coordinates</th> 751 * <th>Expected results</th> 752 * </tr><tr class="coordinates"> 753 * <td>115°48'19.8196"E<br>5°23'14.1129"N</td> 754 * <td>679245.73 m<br>596562.78 m</td> 755 * </tr><tr class="coordinates"> 756 * <td>115°E<br>4°N</td> 757 * <td>590476.87 m<br>442857.65 m</td> 758 * </tr> 759 * </table> 760 * </div> 761 * 762 * @throws FactoryException if the math transform cannot be created. 763 * @throws TransformException if the example point cannot be transformed. 764 * 765 * @see AuthorityFactoryTest#testEPSG_29873() 766 */ 767 @Test 768 public void testHotineObliqueMercator() throws FactoryException, TransformException { 769 description = "Timbalai 1948 / RSO Borneo (m)"; 770 final SamplePoints sample = SamplePoints.forCRS(29873); 771 createMathTransform(Conversion.class, sample); 772 setTolerance(ToleranceModifier.PROJECTION); 773 verifyTransform(sample.sourcePoints, sample.targetPoints); 774 verifyInDomainOfValidity(sample.areaOfValidity); 775 } 776 777 /** 778 * Tests the <q>Transverse Mercator</q> (EPSG:9807) projection method. 779 * First, this method transforms the point given in the example section of the 780 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 781 * Next, this method transforms a random set of points in the projection area of validity 782 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 783 * {@linkplain MathTransform#derivative derivatives} are coherent. 784 * 785 * <p>The math transform parameters and the sample coordinates are:</p> 786 * 787 * <div class="horizontal-flow"> 788 * <table class="ogc"> 789 * <caption>CRS characteristics</caption> 790 * <tr><th>Parameter</th> <th>Value</th></tr> 791 * <tr><td>semi-major axis</td> <td>6377563.396 m</td></tr> 792 * <tr><td>semi-minor axis</td> <td>6356256.908909849 m</td></tr> 793 * <tr><td>Latitude of natural origin</td> <td>49.0°</td></tr> 794 * <tr><td>Longitude of natural origin</td> <td>-2.0°</td></tr> 795 * <tr><td>Scale factor at natural origin</td> <td>0.9996012717</td></tr> 796 * <tr><td>False easting</td> <td>400000.0 m</td></tr> 797 * <tr><td>False northing</td> <td>-100000.0 m</td></tr> 798 * </table> 799 * <table class="ogc"> 800 * <caption>Test points</caption> 801 * <tr> 802 * <th>Source coordinates</th> 803 * <th>Expected results</th> 804 * </tr><tr class="coordinates"> 805 * <td>00°30'E<br>50°30'N</td> 806 * <td>577274.98 m<br>69740.49 m</td> 807 * </tr><tr class="coordinates"> 808 * <td>2°W<br>49°N</td> 809 * <td>400000.00 m<br>-100000.00 m</td> 810 * </tr> 811 * </table> 812 * </div> 813 * 814 * @throws FactoryException if the math transform cannot be created. 815 * @throws TransformException if the example point cannot be transformed. 816 * 817 * @see AuthorityFactoryTest#testEPSG_27700() 818 */ 819 @Test 820 public void testTransverseMercator() throws FactoryException, TransformException { 821 description = "OSGB 1936 / British National Grid"; 822 final SamplePoints sample = SamplePoints.forCRS(27700); 823 createMathTransform(Conversion.class, sample); 824 setTolerance(ToleranceModifier.PROJECTION); 825 verifyTransform(sample.sourcePoints, sample.targetPoints); 826 verifyInDomainOfValidity(sample.areaOfValidity); 827 } 828 829 /** 830 * Tests the <cite>Transverse Mercator (South Orientated)</cite> (EPSG:9808) projection method. 831 * First, this method transforms the point given in the example section of the 832 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 833 * Next, this method transforms a random set of points in the projection area of validity 834 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 835 * {@linkplain MathTransform#derivative derivatives} are coherent. 836 * 837 * <p>The math transform parameters and the sample coordinates are:</p> 838 * 839 * <div class="horizontal-flow"> 840 * <table class="ogc"> 841 * <caption>CRS characteristics</caption> 842 * <tr><th>Parameter</th> <th>Value</th></tr> 843 * <tr><td>semi-major axis</td> <td>6378137.0 m</td></tr> 844 * <tr><td>semi-minor axis</td> <td>6356752.314247833 m</td></tr> 845 * <tr><td>Latitude of natural origin</td> <td>0°</td></tr> 846 * <tr><td>Longitude of natural origin</td> <td>29°</td></tr> 847 * <tr><td>Scale factor at natural origin</td> <td>1</td></tr> 848 * <tr><td>False easting</td> <td>0 m</td></tr> 849 * <tr><td>False northing</td> <td>0 m</td></tr> 850 * </table> 851 * <table class="ogc"> 852 * <caption>Test points</caption> 853 * <tr> 854 * <th>Source coordinates</th> 855 * <th>Expected results</th> 856 * </tr><tr class="coordinates"> 857 * <td>28°16'57.479"E<br>25°43'55.302"S</td> 858 * <td>71984.48 m<br>2847342.74 m</td> 859 * </tr><tr class="coordinates"> 860 * <td>20°E<br>0°S</td> 861 * <td>0 m<br>0 m</td> 862 * </tr> 863 * </table> 864 * </div> 865 * 866 * @throws FactoryException if the math transform cannot be created. 867 * @throws TransformException if the example point cannot be transformed. 868 */ 869 @Test 870 public void testTransverseMercatorSouthOrientated() throws FactoryException, TransformException { 871 description = "Hartebeesthoek94 / Lo29"; 872 final SamplePoints sample = SamplePoints.forCRS(2053); 873 createMathTransform(Conversion.class, sample); 874 setTolerance(ToleranceModifier.PROJECTION); 875 /* 876 * In this particular case we have a conflict between the change of axis direction performed by the 877 * "Transverse Mercator (South Orientated)" operation method and the (east, north) axis directions 878 * documented in the MathTransformFactory.createParameterizedTransform(…) method. We do not mandate 879 * any particular behavior at this time, so we have to determine what the implementer choose to do, 880 * by projecting a point in the south hemisphere and checking the sign of the result. 881 */ 882 double[] expected = sample.targetPoints; 883 final double[] check = new double[] {-0.5, -0.5}; 884 transform.transform(check, 0, check, 0, 1); 885 if (check[1] < 0) { 886 /* 887 * Point in the South hemisphere have negative y values. In other words, the implementer chooses to 888 * keep (east,north) directions instead of (west,south). Reverse the sign of all expected coordinates. 889 */ 890 expected = expected.clone(); 891 for (int i=0; i<expected.length; i++) { 892 expected[i] = -expected[i]; 893 } 894 } 895 verifyTransform(sample.sourcePoints, expected); 896 verifyInDomainOfValidity(sample.areaOfValidity); 897 } 898 899 /** 900 * Tests the <q>Cassini-Soldner</q> (EPSG:9806) projection method. 901 * First, this method transforms the point given in the example section of the 902 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 903 * Next, this method transforms a random set of points in the projection area of validity 904 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 905 * {@linkplain MathTransform#derivative derivatives} are coherent. 906 * 907 * <p>The math transform parameters and the sample coordinates are:</p> 908 * 909 * <div class="horizontal-flow"> 910 * <table class="ogc"> 911 * <caption>CRS characteristics</caption> 912 * <tr><th>Parameter</th> <th>Value</th></tr> 913 * <tr><td>semi-major axis</td> <td>6378350.8704 m</td></tr> 914 * <tr><td>semi-minor axis</td> <td>6356675.0184 m</td></tr> 915 * <tr><td>Latitude of natural origin</td> <td>10.441666666666666°</td></tr> 916 * <tr><td>Longitude of natural origin</td> <td>-61.33333333333333°</td></tr> 917 * <tr><td>False easting</td> <td>86501.46392052001 m</td></tr> 918 * <tr><td>False northing</td> <td>65379.0134283 m</td></tr> 919 * </table> 920 * <table class="ogc"> 921 * <caption>Test points</caption> 922 * <tr> 923 * <th>Source coordinates</th> 924 * <th>Expected results</th> 925 * </tr><tr class="coordinates"> 926 * <td>60°00'00"W<br>10°00'00"N</td> 927 * <td>66644.94 links<br>82536.22 links</td> 928 * </tr><tr class="coordinates"> 929 * <td>61°20'00"W<br>10°26'30"N</td> 930 * <td>430000.00 links<br>325000.00 links</td> 931 * </tr> 932 * </table> 933 * <p class="right-note">1 link = 0.66 feet<br>1 feet = 0.3048 metre</p> 934 * </div> 935 * 936 * @throws FactoryException if the math transform cannot be created. 937 * @throws TransformException if the example point cannot be transformed. 938 * 939 * @see AuthorityFactoryTest#testEPSG_2314() 940 */ 941 @Test 942 public void testCassiniSoldner() throws FactoryException, TransformException { 943 description = "Trinidad 1903 / Trinidad Grid"; 944 final SamplePoints sample = SamplePoints.forCRS(2314); 945 createMathTransform(Conversion.class, sample); 946 setTolerance(ToleranceModifier.PROJECTION); 947 verifyTransform(sample.sourcePoints, sample.targetPoints); 948 verifyInDomainOfValidity(sample.areaOfValidity); 949 } 950 951 /** 952 * Tests the <q>Hyperbolic Cassini-Soldner</q> (EPSG:9833) projection method. 953 * First, this method transforms the point given in the example section of the 954 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 955 * Next, this method transforms a random set of points in the projection area of validity 956 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 957 * {@linkplain MathTransform#derivative derivatives} are coherent. 958 * 959 * <p>The math transform parameters and the sample coordinates are:</p> 960 * 961 * <div class="horizontal-flow"> 962 * <table class="ogc"> 963 * <caption>CRS characteristics</caption> 964 * <tr><th>Parameter</th> <th>Value</th></tr> 965 * <tr><td>semi-major axis</td> <td>6378306.3696 m</td></tr> 966 * <tr><td>semi-minor axis</td> <td>6356571.9960 m</td></tr> 967 * <tr><td>Latitude of natural origin</td> <td>-16.25°</td></tr> 968 * <tr><td>Longitude of natural origin</td> <td>179.33333333333333°</td></tr> 969 * <tr><td>False easting</td> <td>251727.9155424 m</td></tr> 970 * <tr><td>False northing</td> <td>334519.9537680 m</td></tr> 971 * </table> 972 * <table class="ogc"> 973 * <caption>Test points</caption> 974 * <tr> 975 * <th>Source coordinates</th> 976 * <th>Expected results</th> 977 * </tr><tr class="coordinates"> 978 * <td>179°59′39.6115″E<br>16°50′29.2435″S</td> 979 * <td>1601528.90 links<br>1336966.01 links</td> 980 * </tr><tr class="coordinates"> 981 * <td>16°15'00"S<br>179°20'00"E</td> 982 * <td>41251331.8 links<br>1662888.5 links</td> 983 * </tr> 984 * </table> 985 * <p class="right-note">1 link = 0.66 feet<br>1 feet = 0.3048 metre</p> 986 * </div> 987 * 988 * @throws FactoryException if the math transform cannot be created. 989 * @throws TransformException if the example point cannot be transformed. 990 * 991 * @see AuthorityFactoryTest#testEPSG_3139() 992 */ 993 @Test 994 public void testHyperbolicCassiniSoldner() throws FactoryException, TransformException { 995 description = "Vanua Levu 1915 / Vanua Levu Grid"; 996 final SamplePoints sample = SamplePoints.forCRS(3139); 997 createMathTransform(Conversion.class, sample); 998 setTolerance(ToleranceModifier.PROJECTION); 999 verifyTransform(sample.sourcePoints, sample.targetPoints); 1000 verifyInDomainOfValidity(sample.areaOfValidity); 1001 } 1002 1003 /** 1004 * Tests the <q>Lambert Conic Conformal (1SP)</q> (EPSG:9801) projection method. 1005 * First, this method transforms the point given in the example section of the 1006 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 1007 * Next, this method transforms a random set of points in the projection area of validity 1008 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 1009 * {@linkplain MathTransform#derivative derivatives} are coherent. 1010 * 1011 * <p>The math transform parameters and the sample coordinates are:</p> 1012 * 1013 * <div class="horizontal-flow"> 1014 * <table class="ogc"> 1015 * <caption>CRS characteristics</caption> 1016 * <tr><th>Parameter</th> <th>Value</th></tr> 1017 * <tr><td>semi-major axis</td> <td>6378206.4 m</td></tr> 1018 * <tr><td>semi-minor axis</td> <td>6356583.8 m</td></tr> 1019 * <tr><td>Latitude of natural origin</td> <td>18.0°</td></tr> 1020 * <tr><td>Longitude of natural origin</td> <td>-77.0°</td></tr> 1021 * <tr><td>Scale factor at natural origin</td> <td>1.0</td></tr> 1022 * <tr><td>False easting</td> <td>250000.0 m</td></tr> 1023 * <tr><td>False northing</td> <td>150000.0 m</td></tr> 1024 * </table> 1025 * <table class="ogc"> 1026 * <caption>Test points</caption> 1027 * <tr> 1028 * <th>Source coordinates</th> 1029 * <th>Expected results</th> 1030 * </tr><tr class="coordinates"> 1031 * <td>76°56'37.26"W<br>17°55'55.80"N</td> 1032 * <td>255966.58 m<br>142493.51 m</td> 1033 * </tr><tr class="coordinates"> 1034 * <td>77°W<br>18°N</td> 1035 * <td>250000.00 m<br>150000.00 m</td> 1036 * </tr> 1037 * </table> 1038 * </div> 1039 * 1040 * @throws FactoryException if the math transform cannot be created. 1041 * @throws TransformException if the example point cannot be transformed. 1042 * 1043 * @see AuthorityFactoryTest#testEPSG_24200() 1044 */ 1045 @Test 1046 public void testLambertConicConformal1SP() throws FactoryException, TransformException { 1047 description = "JAD69 / Jamaica National Grid"; 1048 final SamplePoints sample = SamplePoints.forCRS(24200); 1049 createMathTransform(Conversion.class, sample); 1050 setTolerance(ToleranceModifier.PROJECTION); 1051 verifyTransform(sample.sourcePoints, sample.targetPoints); 1052 verifyInDomainOfValidity(sample.areaOfValidity); 1053 } 1054 1055 /** 1056 * Tests the <q>Lambert Conic Conformal (2SP)</q> (EPSG:9802) projection method. 1057 * First, this method transforms the point given in the example section of the 1058 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 1059 * Next, this method transforms a random set of points in the projection area of validity 1060 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 1061 * {@linkplain MathTransform#derivative derivatives} are coherent. 1062 * 1063 * <p>The math transform parameters and the sample coordinates are:</p> 1064 * 1065 * <div class="horizontal-flow"> 1066 * <table class="ogc"> 1067 * <caption>CRS characteristics</caption> 1068 * <tr><th>Parameter</th> <th>Value</th></tr> 1069 * <tr><td>semi-major axis</td> <td>6378206.4 m</td></tr> 1070 * <tr><td>semi-minor axis</td> <td>6356583.8 m</td></tr> 1071 * <tr><td>Latitude of false origin</td> <td>27.833333333333333°</td></tr> 1072 * <tr><td>Longitude of false origin</td> <td>-99.0°</td></tr> 1073 * <tr><td>Latitude of 1st standard parallel</td> <td>28.383333333333333°</td></tr> 1074 * <tr><td>Latitude of 2nd standard parallel</td> <td>30.283333333333333°</td></tr> 1075 * <tr><td>Easting at false origin</td> <td>609601.2192024385 m</td></tr> 1076 * <tr><td>Northing at false origin</td> <td>0.0 m</td></tr> 1077 * </table> 1078 * <table class="ogc"> 1079 * <caption>Test points</caption> 1080 * <tr> 1081 * <th>Source coordinates</th> 1082 * <th>Expected results</th> 1083 * </tr><tr class="coordinates"> 1084 * <td>96°00'W<br>28°30'N</td> 1085 * <td>2963503.91 US feet<br>254759.80 US feet</td> 1086 * </tr><tr class="coordinates"> 1087 * <td>99°00'W<br>27°30'N</td> 1088 * <td>2000000.00 US feet<br>0 US feet</td> 1089 * </tr> 1090 * </table> 1091 * <p class="right-note">1 metre = 3.2808333… US feet</p> 1092 * </div> 1093 * 1094 * @throws FactoryException if the math transform cannot be created. 1095 * @throws TransformException if the example point cannot be transformed. 1096 * 1097 * @see AuthorityFactoryTest#testEPSG_32040() 1098 */ 1099 @Test 1100 public void testLambertConicConformal2SP() throws FactoryException, TransformException { 1101 description = "NAD27 / Texas South Central"; 1102 final SamplePoints sample = SamplePoints.forCRS(32040); 1103 createMathTransform(Conversion.class, sample); 1104 setTolerance(ToleranceModifier.PROJECTION); 1105 verifyTransform(sample.sourcePoints, sample.targetPoints); 1106 verifyInDomainOfValidity(sample.areaOfValidity); 1107 } 1108 1109 /** 1110 * Tests the <q>Lambert Conic Conformal (2SP Belgium)</q> (EPSG:9803) projection method. 1111 * First, this method transforms the point given in the example section of the 1112 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 1113 * Next, this method transforms a random set of points in the projection area of validity 1114 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 1115 * {@linkplain MathTransform#derivative derivatives} are coherent. 1116 * 1117 * <p>The math transform parameters and the sample coordinates are:</p> 1118 * 1119 * <div class="horizontal-flow"> 1120 * <table class="ogc"> 1121 * <caption>CRS characteristics</caption> 1122 * <tr><th>Parameter</th> <th>Value</th></tr> 1123 * <tr><td>semi-major axis</td> <td>6378388.0 m</td></tr> 1124 * <tr><td>semi-minor axis</td> <td>6356911.9461279465 m</td></tr> 1125 * <tr><td>Latitude of false origin</td> <td>90.0°</td></tr> 1126 * <tr><td>Longitude of false origin</td> <td>4.356939722222222°</td></tr> 1127 * <tr><td>Latitude of 1st standard parallel</td> <td>49.83333333333333°</td></tr> 1128 * <tr><td>Latitude of 2nd standard parallel</td> <td>51.16666666666667°</td></tr> 1129 * <tr><td>Easting at false origin</td> <td>150000.01256 m</td></tr> 1130 * <tr><td>Northing at false origin</td> <td>5400088.4378 m</td></tr> 1131 * </table> 1132 * <table class="ogc"> 1133 * <caption>Test points</caption> 1134 * <tr> 1135 * <th>Source coordinates</th> 1136 * <th>Expected results</th> 1137 * </tr><tr class="coordinates"> 1138 * <td>5°48'26.533"E<br>50°40'46.461"N</td> 1139 * <td>251763.20 m<br>153034.13 m</td> 1140 * </tr><tr class="coordinates"> 1141 * <td>4°21'24.983"E<br>90°00'00.000"N</td> 1142 * <td>150000.01 m<br>5400088.44 m</td> 1143 * </tr> 1144 * </table> 1145 * </div> 1146 * 1147 * @throws FactoryException if the math transform cannot be created. 1148 * @throws TransformException if the example point cannot be transformed. 1149 * 1150 * @see AuthorityFactoryTest#testEPSG_31300() 1151 */ 1152 @Test 1153 public void testLambertConicConformalBelgium() throws FactoryException, TransformException { 1154 description = "Belge 1972 / Belge Lambert 72"; 1155 final SamplePoints sample = SamplePoints.forCRS(31300); 1156 createMathTransform(Conversion.class, sample); 1157 setTolerance(ToleranceModifier.PROJECTION); 1158 verifyTransform(sample.sourcePoints, sample.targetPoints); 1159 verifyInDomainOfValidity(sample.areaOfValidity); 1160 } 1161 1162 /** 1163 * Tests the <q>Lambert Conic Conformal (2SP Michigan)</q> (EPSG:1051) projection method. 1164 * First, this method transforms the point given in the example section of the 1165 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 1166 * Next, this method transforms a random set of points in the projection area of validity 1167 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 1168 * {@linkplain MathTransform#derivative derivatives} are coherent. 1169 * 1170 * <p>The math transform parameters and the sample coordinates are:</p> 1171 * 1172 * <div class="horizontal-flow"> 1173 * <table class="ogc"> 1174 * <caption>CRS characteristics</caption> 1175 * <tr><th>Parameter</th> <th>Value</th></tr> 1176 * <tr><td>semi-major axis</td> <td>6378206.4 m</td></tr> 1177 * <tr><td>semi-minor axis</td> <td>6356583.8 m</td></tr> 1178 * <tr><td>Latitude of false origin</td> <td>43.316666666666667°</td></tr> 1179 * <tr><td>Longitude of false origin</td> <td>-84.333333333333333°</td></tr> 1180 * <tr><td>Latitude of 1st standard parallel</td> <td>44.183333333333333°</td></tr> 1181 * <tr><td>Latitude of 2nd standard parallel</td> <td>45.7°</td></tr> 1182 * <tr><td>Easting at false origin</td> <td>609601.2192024385 m</td></tr> 1183 * <tr><td>Northing at false origin</td> <td>0.0 m</td></tr> 1184 * </table> 1185 * <table class="ogc"> 1186 * <caption>Test points</caption> 1187 * <tr> 1188 * <th>Source coordinates</th> 1189 * <th>Expected results</th> 1190 * </tr><tr class="coordinates"> 1191 * <td>83°10"W<br>43°45'N</td> 1192 * <td>2308335.75 US feet<br>160210.48 US feet</td> 1193 * </tr><tr class="coordinates"> 1194 * <td>84°20'W<br>43°19'N</td> 1195 * <td>2000000.00 US feet<br>0 US feet</td> 1196 * </tr> 1197 * </table> 1198 * <p class="right-note">1 metre = 3.2808333… US feet</p> 1199 * </div> 1200 * 1201 * @throws FactoryException if the math transform cannot be created. 1202 * @throws TransformException if the example point cannot be transformed. 1203 */ 1204 @Test 1205 public void testLambertConicConformalMichigan() throws FactoryException, TransformException { 1206 description = "NAD27 / Michigan Central"; 1207 final SamplePoints sample = SamplePoints.forCRS(6201); 1208 createMathTransform(Conversion.class, sample); 1209 setTolerance(ToleranceModifier.PROJECTION); 1210 verifyTransform(sample.sourcePoints, sample.targetPoints); 1211 verifyInDomainOfValidity(sample.areaOfValidity); 1212 } 1213 1214 /** 1215 * Tests the <q>Lambert Azimuthal Equal Area</q> (EPSG:9820) projection method. 1216 * First, this method transforms the point given in the example section of the 1217 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 1218 * Next, this method transforms a random set of points in the projection area of validity 1219 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 1220 * {@linkplain MathTransform#derivative derivatives} are coherent. 1221 * 1222 * <p>The math transform parameters and the sample coordinates are:</p> 1223 * 1224 * <div class="horizontal-flow"> 1225 * <table class="ogc"> 1226 * <caption>CRS characteristics</caption> 1227 * <tr><th>Parameter</th> <th>Value</th></tr> 1228 * <tr><td>semi-major axis</td> <td>6378137.0 m</td></tr> 1229 * <tr><td>semi-minor axis</td> <td>6356752.314140284 m</td></tr> 1230 * <tr><td>Latitude of natural origin</td> <td>52.0°</td></tr> 1231 * <tr><td>Longitude of natural origin</td> <td>10.0°</td></tr> 1232 * <tr><td>False easting</td> <td>4321000.0 m</td></tr> 1233 * <tr><td>False northing</td> <td>3210000.0 m</td></tr> 1234 * </table> 1235 * <table class="ogc"> 1236 * <caption>Test points</caption> 1237 * <tr> 1238 * <th>Source coordinates</th> 1239 * <th>Expected results</th> 1240 * </tr><tr class="coordinates"> 1241 * <td>5°E<br>50°N</td> 1242 * <td>3962799.45 m<br>2999718.85 m</td> 1243 * </tr><tr class="coordinates"> 1244 * <td>10°E<br>52°N</td> 1245 * <td>4321000.00 m<br>3210000.00 m</td> 1246 * </tr> 1247 * </table> 1248 * </div> 1249 * 1250 * @throws FactoryException if the math transform cannot be created. 1251 * @throws TransformException if the example point cannot be transformed. 1252 * 1253 * @see AuthorityFactoryTest#testEPSG_3035() 1254 */ 1255 @Test 1256 public void testLambertAzimuthalEqualArea() throws FactoryException, TransformException { 1257 description = "ETRS89 / LAEA Europe"; 1258 final SamplePoints sample = SamplePoints.forCRS(3035); 1259 createMathTransform(Conversion.class, sample); 1260 setTolerance(ToleranceModifier.PROJECTION); 1261 verifyTransform(sample.sourcePoints, sample.targetPoints); 1262 verifyInDomainOfValidity(sample.areaOfValidity); 1263 } 1264 1265 /** 1266 * Tests the <q>Polar Stereographic (variant A)</q> (EPSG:9810) projection method. 1267 * First, this method transforms the point given in the example section of the 1268 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 1269 * Next, this method transforms a random set of points in the projection area of validity 1270 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 1271 * {@linkplain MathTransform#derivative derivatives} are coherent. 1272 * 1273 * <p>The math transform parameters and the sample coordinates are:</p> 1274 * 1275 * <div class="horizontal-flow"> 1276 * <table class="ogc"> 1277 * <caption>CRS characteristics</caption> 1278 * <tr><th>Parameter</th> <th>Value</th></tr> 1279 * <tr><td>semi-major axis</td> <td>6378137.0 m</td></tr> 1280 * <tr><td>semi-minor axis</td> <td>6356752.314247833 m</td></tr> 1281 * <tr><td>Latitude of natural origin</td> <td>90.0°</td></tr> 1282 * <tr><td>Longitude of natural origin</td> <td>0.0°</td></tr> 1283 * <tr><td>Scale factor at natural origin</td> <td>0.994</td></tr> 1284 * <tr><td>False easting</td> <td>2000000.0 m</td></tr> 1285 * <tr><td>False northing</td> <td>2000000.0 m</td></tr> 1286 * </table> 1287 * <table class="ogc"> 1288 * <caption>Test points</caption> 1289 * <tr> 1290 * <th>Source coordinates</th> 1291 * <th>Expected results</th> 1292 * </tr><tr class="coordinates"> 1293 * <td>44°E<br>73°N</td> 1294 * <td>3320416.75 m<br>632668.43 m</td> 1295 * </tr><tr class="coordinates"> 1296 * <td>0°E<br>90°N</td> 1297 * <td>2000000.00 m<br>2000000.00 m</td> 1298 * </tr> 1299 * </table> 1300 * </div> 1301 * 1302 * @throws FactoryException if the math transform cannot be created. 1303 * @throws TransformException if the example point cannot be transformed. 1304 * 1305 * @see AuthorityFactoryTest#testEPSG_5041() 1306 * @see AuthorityFactoryTest#testEPSG_32661() 1307 */ 1308 @Test 1309 public void testPolarStereographicA() throws FactoryException, TransformException { 1310 description = "WGS 84 / UPS North (E,N)"; 1311 final SamplePoints sample = SamplePoints.forCRS(5041); 1312 createMathTransform(Conversion.class, sample); 1313 setTolerance(ToleranceModifier.PROJECTION); 1314 verifyTransform(sample.sourcePoints, sample.targetPoints); 1315 verifyInDomainOfValidity(sample.areaOfValidity); 1316 } 1317 1318 /** 1319 * Tests the <q>Polar Stereographic (variant B)</q> (EPSG:9829) projection method. 1320 * First, this method transforms the point given in the example section of the 1321 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 1322 * Next, this method transforms a random set of points in the projection area of validity 1323 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 1324 * {@linkplain MathTransform#derivative derivatives} are coherent. 1325 * 1326 * <p>The math transform parameters and the sample coordinates are:</p> 1327 * 1328 * <div class="horizontal-flow"> 1329 * <table class="ogc"> 1330 * <caption>CRS characteristics</caption> 1331 * <tr><th>Parameter</th> <th>Value</th></tr> 1332 * <tr><th>Source coordinates</th> <th>Expected results</th></tr> 1333 * <tr><td>semi-major axis</td> <td>6378137.0 m</td></tr> 1334 * <tr><td>semi-minor axis</td> <td>6356752.314247833 m</td></tr> 1335 * <tr><td>Latitude of standard parallel</td> <td>-71.0°</td></tr> 1336 * <tr><td>Longitude of origin</td> <td>70.0°</td></tr> 1337 * <tr><td>False easting</td> <td>6000000.0 m</td></tr> 1338 * <tr><td>False northing</td> <td>6000000.0 m</td></tr> 1339 * </table> 1340 * <table class="ogc"> 1341 * <caption>Test points</caption> 1342 * <tr> 1343 * <th>Source coordinates</th> 1344 * <th>Expected results</th> 1345 * </tr><tr class="coordinates"> 1346 * <td>120°E<br>75°S</td> 1347 * <td>7255380.79 m<br>7053389.56 m</td> 1348 * </tr><tr class="coordinates"> 1349 * <td>70°E<br>90°S</td> 1350 * <td>6000000.00 m<br>6000000.00 m</td> 1351 * </tr> 1352 * </table> 1353 * </div> 1354 * 1355 * @throws FactoryException if the math transform cannot be created. 1356 * @throws TransformException if the example point cannot be transformed. 1357 * 1358 * @see AuthorityFactoryTest#testEPSG_3032() 1359 */ 1360 @Test 1361 public void testPolarStereographicB() throws FactoryException, TransformException { 1362 description = "Australian Antarctic Polar Stereographic"; 1363 final SamplePoints sample = SamplePoints.forCRS(3032); 1364 createMathTransform(Conversion.class, sample); 1365 setTolerance(ToleranceModifier.PROJECTION); 1366 verifyTransform(sample.sourcePoints, sample.targetPoints); 1367 verifyInDomainOfValidity(sample.areaOfValidity); 1368 } 1369 1370 /** 1371 * Tests the <q>Polar Stereographic (variant C)</q> (EPSG:9830) projection method. 1372 * First, this method transforms the point given in the example section of the 1373 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 1374 * Next, this method transforms a random set of points in the projection area of validity 1375 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 1376 * {@linkplain MathTransform#derivative derivatives} are coherent. 1377 * 1378 * <p>The math transform parameters and the sample coordinates are:</p> 1379 * 1380 * <div class="horizontal-flow"> 1381 * <table class="ogc"> 1382 * <caption>CRS characteristics</caption> 1383 * <tr><th>Parameter</th> <th>Value</th></tr> 1384 * <tr><th>Source coordinates</th> <th>Expected results</th></tr> 1385 * <tr><td>semi-major axis</td> <td>6378388.0 m</td></tr> 1386 * <tr><td>semi-minor axis</td> <td>6356911.9461279465 m</td></tr> 1387 * <tr><td>Latitude of standard parallel</td> <td>-67°</td></tr> 1388 * <tr><td>Longitude of origin</td> <td>140°</td></tr> 1389 * <tr><td>False easting</td> <td>300000 m</td></tr> 1390 * <tr><td>False northing</td> <td>200000 m</td></tr> 1391 * </table> 1392 * <table class="ogc"> 1393 * <caption>Test points</caption> 1394 * <tr> 1395 * <th>Source coordinates</th> 1396 * <th>Expected results</th> 1397 * </tr><tr class="coordinates"> 1398 * <td>140°04'17.040"E<br>66°36'18.820"S</td> 1399 * <td>303169.52 m<br>244055.72 m</td> 1400 * </tr><tr class="coordinates"> 1401 * <td>67°E<br>90°S</td> 1402 * <td>300000.00 m<br>200000.00 m</td> 1403 * </tr> 1404 * </table> 1405 * </div> 1406 * 1407 * @throws FactoryException if the math transform cannot be created. 1408 * @throws TransformException if the example point cannot be transformed. 1409 * 1410 * @see AuthorityFactoryTest#testEPSG_3032() 1411 */ 1412 @Test 1413 public void testPolarStereographicC() throws FactoryException, TransformException { 1414 description = "Petrels 1972 / Terre Adelie Polar Stereographic"; 1415 final SamplePoints sample = SamplePoints.forCRS(2985); 1416 createMathTransform(Conversion.class, sample); 1417 setTolerance(ToleranceModifier.PROJECTION); 1418 verifyTransform(sample.sourcePoints, sample.targetPoints); 1419 verifyInDomainOfValidity(sample.areaOfValidity); 1420 } 1421 1422 /** 1423 * Tests the <q>Oblique Stereographic</q> (EPSG:9809) projection method. 1424 * First, this method transforms the point given in the example section of the 1425 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 1426 * Next, this method transforms a random set of points in the projection area of validity 1427 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 1428 * {@linkplain MathTransform#derivative derivatives} are coherent. 1429 * 1430 * <p>The math transform parameters and the sample coordinates are:</p> 1431 * 1432 * <div class="horizontal-flow"> 1433 * <table class="ogc"> 1434 * <caption>CRS characteristics</caption> 1435 * <tr><th>Parameter</th> <th>Value</th></tr> 1436 * <tr><td>semi-major axis</td> <td>6377397.155 m</td></tr> 1437 * <tr><td>semi-minor axis</td> <td>6356078.9626186555 m</td></tr> 1438 * <tr><td>Latitude of natural origin</td> <td>52.15616055555556°</td></tr> 1439 * <tr><td>Longitude of natural origin</td> <td>5.38763888888889°</td></tr> 1440 * <tr><td>Scale factor at natural origin</td> <td>0.9999079</td></tr> 1441 * <tr><td>False easting</td> <td>155000.0 m</td></tr> 1442 * <tr><td>False northing</td> <td>463000.0 m</td></tr> 1443 * </table> 1444 * <table class="ogc"> 1445 * <caption>Test points</caption> 1446 * <tr> 1447 * <th>Source coordinates</th> 1448 * <th>Expected results</th> 1449 * </tr><tr class="coordinates"> 1450 * <td>6°E<br>53°N</td> 1451 * <td>196105.283 m<br>557057.739 m</td> 1452 * </tr><tr class="coordinates"> 1453 * <td>5°23'15.500"E<br>52°09'22.178"N</td> 1454 * <td>155000.000 m<br>463000.000 m</td> 1455 * </tr> 1456 * </table> 1457 * </div> 1458 * 1459 * @throws FactoryException if the math transform cannot be created. 1460 * @throws TransformException if the example point cannot be transformed. 1461 * 1462 * @see AuthorityFactoryTest#testEPSG_28992() 1463 */ 1464 @Test 1465 public void testObliqueStereographic() throws FactoryException, TransformException { 1466 description = "Amersfoort / RD New"; 1467 final SamplePoints sample = SamplePoints.forCRS(28992); 1468 createMathTransform(Conversion.class, sample); 1469 setTolerance(ToleranceModifier.PROJECTION); 1470 verifyTransform(sample.sourcePoints, sample.targetPoints); 1471 verifyInDomainOfValidity(sample.areaOfValidity); 1472 } 1473 1474 /** 1475 * Tests the <q>American Polyconic</q> (EPSG:9818) projection. 1476 * First, this method transforms the some of the points given in Table 19, p 132 of 1477 * <a href="http://pubs.er.usgs.gov/usgspubs/pp/pp1395">Map Projections, a working manual</a> 1478 * by John P.Snyder. Next, this method transforms a random set of points in the projection 1479 * area of validity and ensures that the {@linkplain MathTransform#inverse() inverse transform} 1480 * and the {@linkplain MathTransform#derivative derivatives} are coherent. 1481 * 1482 * <p>The math transform parameters and the sample coordinates are:</p> 1483 * 1484 * <div class="horizontal-flow"> 1485 * <table class="ogc"> 1486 * <caption>CRS characteristics</caption> 1487 * <tr><th>Parameter</th> <th>Value</th></tr> 1488 * <tr><td>semi-major axis</td> <td>6378206.4 m</td></tr> 1489 * <tr><td>semi-minor axis</td> <td>6356583.8 m</td></tr> 1490 * <tr><td>Latitude of natural origin</td> <td>0.0°</td></tr> 1491 * <tr><td>Longitude of natural origin</td> <td>0.0°</td></tr> 1492 * <tr><td>False easting</td> <td>0.0 m</td></tr> 1493 * <tr><td>False northing</td> <td>0.0 m</td></tr> 1494 * </table> 1495 * <table class="ogc"> 1496 * <caption>Test points</caption> 1497 * <tr> 1498 * <th>Source coordinates</th> 1499 * <th>Expected results</th> 1500 * </tr><tr class="coordinates"> 1501 * <td>See source</td> 1502 * <td>See source</td> 1503 * </tr> 1504 * </table> 1505 * </div> 1506 * 1507 * @throws FactoryException if the math transform cannot be created. 1508 * @throws TransformException if the example point cannot be transformed. 1509 */ 1510 @Test 1511 public void testPolyconic() throws FactoryException, TransformException { 1512 tolerance = max(tolerance, 0.5); // The sample points are only accurate to 1 metre. 1513 description = "American Polyconic"; 1514 final SamplePoints sample = SamplePoints.forCRS(9818); 1515 createMathTransform(Conversion.class, sample); 1516 setTolerance(ToleranceModifier.PROJECTION); 1517 verifyTransform(sample.sourcePoints, sample.targetPoints); 1518 verifyInDomainOfValidity(sample.areaOfValidity); 1519 } 1520 1521 /** 1522 * Tests the <q>Krovak</q> (EPSG:9819) projection. 1523 * First, this method transforms the point given in the example section of the 1524 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 1525 * Next, this method transforms a random set of points in the projection area of validity 1526 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 1527 * {@linkplain MathTransform#derivative derivatives} are coherent. 1528 * 1529 * <p>The math transform parameters and the sample coordinates are:</p> 1530 * 1531 * <div class="horizontal-flow"> 1532 * <table class="ogc"> 1533 * <caption>CRS characteristics</caption> 1534 * <tr><th>Parameter</th> <th>Value</th></tr> 1535 * <tr><td>semi-major axis</td> <td>6377397.155 m</td></tr> 1536 * <tr><td>semi-minor axis</td> <td>6356078.9626186555 m</td></tr> 1537 * <tr><td>Latitude of projection centre</td> <td>49.5°</td></tr> 1538 * <tr><td>Longitude of origin</td> <td>24.5°</td></tr> 1539 * <tr><td>Co-latitude of cone axis</td> <td>30.288139722222222°</td></tr> 1540 * <tr><td>Latitude of pseudo standard parallel</td> <td>78.5°</td></tr> 1541 * <tr><td>Scale factor on pseudo standard parallel</td> <td>0.9999</td></tr> 1542 * <tr><td>False easting</td> <td>0.0 m</td></tr> 1543 * <tr><td>False northing</td> <td>0.0 m</td></tr> 1544 * </table> 1545 * <table class="ogc"> 1546 * <caption>Test points</caption> 1547 * <tr> 1548 * <th>Source coordinates</th> 1549 * <th>Expected results</th> 1550 * </tr><tr class="coordinates"> 1551 * <td>16°50'59.179"E<br>50°12'32.442"N</td> 1552 * <td>-568990.997 m<br>-1050538.643 m</td> 1553 * </tr> 1554 * </table> 1555 * </div> 1556 * 1557 * @throws FactoryException if the math transform cannot be created. 1558 * @throws TransformException if the example point cannot be transformed. 1559 * 1560 * @see AuthorityFactoryTest#testEPSG_2065() 1561 */ 1562 @Test 1563 public void testKrovak() throws FactoryException, TransformException { 1564 description = "CRS S-JTSK (Ferro) / Krovak"; 1565 final SamplePoints sample = SamplePoints.forCRS(2065); 1566 createMathTransform(Conversion.class, sample); 1567 setTolerance(ToleranceModifier.PROJECTION); 1568 verifyTransform(sample.sourcePoints, sample.targetPoints); 1569 verifyInDomainOfValidity(sample.areaOfValidity); 1570 } 1571 1572 /** 1573 * Tests the <q>Orthographic</q> (EPSG:9840) projection. 1574 * First, this method transforms the point given in the example section of the 1575 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 1576 * Next, this method transforms a random set of points in the projection area of validity 1577 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 1578 * {@linkplain MathTransform#derivative derivatives} are coherent. 1579 * 1580 * <p>The math transform parameters and the sample coordinates are:</p> 1581 * 1582 * <div class="horizontal-flow"> 1583 * <table class="ogc"> 1584 * <caption>CRS characteristics</caption> 1585 * <tr><th>Parameter</th> <th>Value</th></tr> 1586 * <tr><td>semi-major axis</td> <td>6378137.0 m</td></tr> 1587 * <tr><td>semi-minor axis</td> <td>6356752.314247833 m</td></tr> 1588 * <tr><td>Latitude of natural origin</td> <td>55.0°</td></tr> 1589 * <tr><td>Longitude of natural origin</td> <td>5.0°</td></tr> 1590 * <tr><td>False easting</td> <td>0.0 m</td></tr> 1591 * <tr><td>False northing</td> <td>0.0 m</td></tr> 1592 * </table> 1593 * <table class="ogc"> 1594 * <caption>Test points</caption> 1595 * <tr> 1596 * <th>Source coordinates</th> 1597 * <th>Expected results</th> 1598 * </tr><tr class="coordinates"> 1599 * <td>2°07'46.38"E<br>53°48'33.82"N</td> 1600 * <td>–189011.711 m<br>–128 640.567 m</td> 1601 * </tr> 1602 * </table> 1603 * </div> 1604 * 1605 * @throws FactoryException if the math transform cannot be created. 1606 * @throws TransformException if the example point cannot be transformed. 1607 */ 1608 @Test 1609 public void testOrthographic() throws FactoryException, TransformException { 1610 description = "WGS 84 / Orthographic"; 1611 final SamplePoints sample = SamplePoints.forCRS(9840); 1612 createMathTransform(Conversion.class, sample); 1613 setTolerance(ToleranceModifier.PROJECTION); 1614 verifyTransform(sample.sourcePoints, sample.targetPoints); 1615 verifyInDomainOfValidity(sample.areaOfValidity); 1616 } 1617 1618 /** 1619 * Tests the <q>Equidistant Cylindrical</q> (EPSG:1028) projection. 1620 * First, this method transforms the point given in the example section of the 1621 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 1622 * Next, this method transforms a random set of points in the projection area of validity 1623 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 1624 * {@linkplain MathTransform#derivative derivatives} are coherent. 1625 * 1626 * <p>The math transform parameters and the sample coordinates are:</p> 1627 * 1628 * <div class="horizontal-flow"> 1629 * <table class="ogc"> 1630 * <caption>CRS characteristics</caption> 1631 * <tr><th>Parameter</th> <th>Value</th></tr> 1632 * <tr><td>semi-major axis</td> <td>6378137.0 m</td></tr> 1633 * <tr><td>semi-minor axis</td> <td>6356752.314247833 m</td></tr> 1634 * <tr><td>Latitude of natural origin</td> <td>0°</td></tr> 1635 * <tr><td>Longitude of natural origin</td> <td>0°</td></tr> 1636 * <tr><td>False easting</td> <td>0 m</td></tr> 1637 * <tr><td>False northing</td> <td>0 m</td></tr> 1638 * </table> 1639 * <table class="ogc"> 1640 * <caption>Test points</caption> 1641 * <tr> 1642 * <th>Source coordinates</th> 1643 * <th>Expected results</th> 1644 * </tr><tr class="coordinates"> 1645 * <td>10°00'00.000"E<br>55°00'00.000"N</td> 1646 * <td>1113194.91 m<br>6097230.31 m</td> 1647 * </tr> 1648 * </table> 1649 * </div> 1650 * 1651 * @throws FactoryException if the math transform cannot be created. 1652 * @throws TransformException if the example point cannot be transformed. 1653 */ 1654 @Test 1655 public void testEquidistantCylindrical() throws FactoryException, TransformException { 1656 description = "WGS84 / World Equidistant Cylindrical"; 1657 final SamplePoints sample = SamplePoints.forCRS(4087); 1658 createMathTransform(Conversion.class, sample); 1659 setTolerance(ToleranceModifier.PROJECTION); 1660 verifyTransform(sample.sourcePoints, sample.targetPoints); 1661 verifyInDomainOfValidity(sample.areaOfValidity); 1662 } 1663 1664 /** 1665 * Tests the <q>Modified Azimuthal Equidistant</q> (EPSG:9832) projection. 1666 * First, this method transforms the point given in the example section of the 1667 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 1668 * Next, this method transforms a random set of points in the projection area of validity 1669 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 1670 * {@linkplain MathTransform#derivative derivatives} are coherent. 1671 * 1672 * <p>The math transform parameters and the sample coordinates are:</p> 1673 * 1674 * <div class="horizontal-flow"> 1675 * <table class="ogc"> 1676 * <caption>CRS characteristics</caption> 1677 * <tr><th>Parameter</th> <th>Value</th></tr> 1678 * <tr><td>semi-major axis</td> <td>6378206.4 m</td></tr> 1679 * <tr><td>semi-minor axis</td> <td>6356583.8 m</td></tr> 1680 * <tr><td>Latitude of natural origin</td> <td>9.546708333333333°</td></tr> 1681 * <tr><td>Longitude of natural origin</td> <td>138.168744444444444°</td></tr> 1682 * <tr><td>False easting</td> <td>40000.0 m</td></tr> 1683 * <tr><td>False northing</td> <td>60000.0 m</td></tr> 1684 * </table> 1685 * <table class="ogc"> 1686 * <caption>Test points</caption> 1687 * <tr> 1688 * <th>Source coordinates</th> 1689 * <th>Expected results</th> 1690 * </tr><tr class="coordinates"> 1691 * <td>138°11'34.908"E<br>9°35'47.493"N</td> 1692 * <td>42665.90 m<br>65509.82 m</td> 1693 * </tr> 1694 * </table> 1695 * </div> 1696 * 1697 * @throws FactoryException if the math transform cannot be created. 1698 * @throws TransformException if the example point cannot be transformed. 1699 */ 1700 @Test 1701 public void testModifiedAzimuthalEquidistant() throws FactoryException, TransformException { 1702 description = "Guam 1963 / Yap Islands"; 1703 final SamplePoints sample = SamplePoints.forCRS(3295); 1704 createMathTransform(Conversion.class, sample); 1705 setTolerance(ToleranceModifier.PROJECTION); 1706 verifyTransform(sample.sourcePoints, sample.targetPoints); 1707 verifyInDomainOfValidity(sample.areaOfValidity); 1708 } 1709 1710 /** 1711 * Tests the <q>Abridged Molodensky</q> (EPSG:9605) datum shift operation. 1712 * First, this method transforms the point given in the example section of the 1713 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 1714 * Next, this method transforms a random set of geographic coordinates 1715 * and ensures that the {@linkplain MathTransform#inverse() inverse transform} and the 1716 * {@linkplain MathTransform#derivative derivatives} are coherent. 1717 * 1718 * <p>The math transform parameters and the sample coordinates are:</p> 1719 * 1720 * <div class="horizontal-flow"> 1721 * <table class="ogc"> 1722 * <caption>Conversion characteristics</caption> 1723 * <tr><th>Parameter</th> <th>Value</th></tr> 1724 * <tr><td>dim</td> <td>3</td></tr> 1725 * <tr><td>src_semi_major</td> <td>6378137.0 m</td></tr> 1726 * <tr><td>src_semi_minor</td> <td>6356752.314247833 m</td></tr> 1727 * <tr><td>X-axis translation</td> <td>84.87 m</td></tr> 1728 * <tr><td>Y-axis translation</td> <td>96.49 m</td></tr> 1729 * <tr><td>Z-axis translation</td> <td>116.95 m</td></tr> 1730 * <tr><td>Semi-major axis length difference</td> <td>251 m</td></tr> 1731 * <tr><td>Flattening difference</td> <td>1.41927E-05</td></tr> 1732 * </table> 1733 * <table class="ogc"> 1734 * <caption>Test points</caption> 1735 * <tr> 1736 * <th>Source coordinates</th> 1737 * <th>Expected results</th> 1738 * </tr><tr class="coordinates"> 1739 * <td>2°7'46.380"E<br>53°48'33.820"N<br>73.000 m</td> 1740 * <td>2°7'51.477"E<br>53°48'36.563"N<br>28.091 m</td> 1741 * </tr> 1742 * </table> 1743 * </div> 1744 * 1745 * @throws FactoryException if the math transform cannot be created. 1746 * @throws TransformException if the example point cannot be transformed. 1747 */ 1748 @Test 1749 public void testAbridgedMolodensky() throws FactoryException, TransformException { 1750 tolerance = max(tolerance, 0.001 * (NAUTICAL_MILE/60)); // 0.001 angular second (about 3 cm at equator). 1751 description = "WGS84 to ED50"; 1752 final SamplePoints sample = SamplePoints.forCRS(4230); 1753 createMathTransform(Transformation.class, sample); 1754 setTolerance(ToleranceModifier.GEOGRAPHIC); 1755 verifyTransform(sample.sourcePoints, sample.targetPoints); 1756 verifyInDomain3D(sample.areaOfValidity, -1000, +1000); 1757 } 1758 1759 /** 1760 * Tests the <q>Geographic/topocentric conversions</q> (EPSG:9837). 1761 * This method transforms the point given in the example section of the 1762 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 1763 * 1764 * <p>A CRS using this method is "EPSG topocentric example A" (EPSG:5819)". 1765 * The math transform parameters and the sample coordinates are:</p> 1766 * 1767 * <div class="horizontal-flow"> 1768 * <table class="ogc"> 1769 * <caption>Conversion characteristics</caption> 1770 * <tr><th>Parameter</th> <th>Value</th></tr> 1771 * <tr><td>semi_major</td> <td>6378137.0 m</td></tr> 1772 * <tr><td>semi_minor</td> <td>6356752.314245179 m</td></tr> 1773 * <tr><td>Latitude of topocentric origin</td> <td>55°00'00.000"N</td></tr> 1774 * <tr><td>Longitude of topocentric origin</td> <td>5°00'00.000"E</td></tr> 1775 * <tr><td>Ellipsoidal height of topocentric origin</td> <td>200 m</td></tr> 1776 * </table> 1777 * <table class="ogc"> 1778 * <caption>Test points</caption> 1779 * <tr> 1780 * <th>Source coordinates</th> 1781 * <th>Expected results</th> 1782 * </tr><tr class="coordinates"> 1783 * <td>2°07'46.38"E<br>53°48'33.82"N<br>73.0 m</td> 1784 * <td>–189013.869 m<br>–128642.040 m<br>–4220.171 m</td> 1785 * </tr><tr class="coordinates"> 1786 * <td>5°00'00.000"E<br>55°00'00.000"N<br>200 m</td> 1787 * <td>0 m<br>0 m<br>0 m</td> 1788 * </tr> 1789 * </table> 1790 * </div> 1791 * 1792 * @throws FactoryException if the math transform cannot be created. 1793 * @throws TransformException if the example point cannot be transformed. 1794 */ 1795 @Test 1796 public void testGeographicTopocentric() throws FactoryException, TransformException { 1797 description = "EPSG topocentric example A"; 1798 final SamplePoints sample = SamplePoints.forCRS(5819); 1799 createMathTransform(Transformation.class, sample); 1800 setTolerance(null); 1801 verifyTransform(sample.sourcePoints, sample.targetPoints); 1802 verifyInDomain3D(sample.areaOfValidity, -100, +100); 1803 } 1804 1805 /** 1806 * Tests the <q>Geocentric/topocentric conversions</q> (EPSG:9836). 1807 * This method transforms the point given in the example section of the 1808 * EPSG guidance note and compares the {@link MathTransform} result with the expected result. 1809 * 1810 * <p>A CRS using this method is "EPSG topocentric example B" (EPSG:5820)". 1811 * The math transform parameters and the sample coordinates are:</p> 1812 * 1813 * <div class="horizontal-flow"> 1814 * <table class="ogc"> 1815 * <caption>Conversion characteristics</caption> 1816 * <tr><th>Parameter</th> <th>Value</th></tr> 1817 * <tr><td>semi_major</td> <td>6378137.0 m</td></tr> 1818 * <tr><td>semi_minor</td> <td>6356752.314245179 m</td></tr> 1819 * <tr><td>Geocentric X of topocentric origin</td> <td>3652755.3058 m</td></tr> 1820 * <tr><td>Geocentric Y of topocentric origin</td> <td> 319574.6799 m</td></tr> 1821 * <tr><td>Geocentric Z of topocentric origin</td> <td>5201547.3536 m</td></tr> 1822 * </table> 1823 * <table class="ogc"> 1824 * <caption>Test points</caption> 1825 * <tr> 1826 * <th>Source coordinates</th> 1827 * <th>Expected results</th> 1828 * </tr><tr class="coordinates"> 1829 * <td>3771793.968 m<br> 140253.342 m<br>5124304.349 m</td> 1830 * <td>–189013.869 m<br>–128642.040 m<br> –4220.171 m</td> 1831 * </tr><tr class="coordinates"> 1832 * <td>3652755.3058 m<br>319574.6799 m<br>5201547.3536 m</td> 1833 * <td>0 m<br>0 m<br>0 m</td> 1834 * </tr> 1835 * </table> 1836 * </div> 1837 * 1838 * @throws FactoryException if the math transform cannot be created. 1839 * @throws TransformException if the example point cannot be transformed. 1840 */ 1841 @Test 1842 public void testGeocentricTopocentric() throws FactoryException, TransformException { 1843 description = "EPSG topocentric example B"; 1844 final SamplePoints sample = SamplePoints.forCRS(5820); 1845 createMathTransform(Transformation.class, sample); 1846 setTolerance(null); 1847 verifyTransform(sample.sourcePoints, sample.targetPoints); 1848 verifyInDomain3D(sample.areaOfValidity, 5000000, 5200000); 1849 } 1850 1851 /** 1852 * Executes {@link #verifyInDomain(double[], double[], int[], Random)} using a three-dimensional domain. 1853 * 1854 * @param areaOfValidity the horizontal domain of test points. 1855 * @param zmin the vertical minimum value of test points. 1856 * @param zmax the vertical maximum value of test points. 1857 * @throws TransformException if a point cannot be transformed. 1858 */ 1859 private void verifyInDomain3D(final Rectangle2D areaOfValidity, final double zmin, final double zmax) 1860 throws TransformException 1861 { 1862 verifyInDomain(new double[] {areaOfValidity.getMinX(), areaOfValidity.getMinY(), zmin}, 1863 new double[] {areaOfValidity.getMaxX(), areaOfValidity.getMaxY(), zmax}, 1864 new int[] {10, 10, 10}, new Random()); 1865 } 1866 1867 /** 1868 * Asserts that a matrix of derivatives is equal to the expected ones within a positive delta. 1869 * 1870 * @hidden 1871 */ 1872 @Override 1873 protected void assertMatrixEquals(final Matrix expected, final Matrix actual, final Matrix tolmat, final String message) 1874 throws DerivativeFailure 1875 { 1876 if (tolmat != null) { 1877 final int numRow = tolmat.getNumRow(); 1878 final int numCol = tolmat.getNumCol(); 1879 for (int j=0; j<numRow; j++) { 1880 for (int i=0; i<numCol; i++) { 1881 tolmat.setElement(j, i, DERIVATIVE_TOLERANCE); 1882 } 1883 } 1884 } 1885 super.assertMatrixEquals(expected, actual, tolmat, message); 1886 } 1887}