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.Set; 021import java.util.Map; 022import java.util.HashMap; 023import java.util.Collections; 024import java.util.Objects; 025import javax.measure.Unit; 026import javax.measure.quantity.Angle; 027import javax.measure.quantity.Length; 028 029import org.opengis.parameter.*; 030import org.opengis.referencing.*; 031import org.opengis.referencing.cs.*; 032import org.opengis.referencing.crs.*; 033import org.opengis.referencing.datum.*; 034import org.opengis.referencing.operation.*; 035import org.opengis.metadata.citation.Citation; 036import org.opengis.util.FactoryException; 037import org.opengis.test.util.PseudoFactory; 038import org.opengis.test.ValidatorContainer; 039import org.opengis.test.Units; 040 041import static org.junit.jupiter.api.Assertions.*; 042import static org.junit.jupiter.api.Assumptions.assumeTrue; 043import static org.opengis.test.referencing.ObjectFactoryTest.*; 044 045 046/** 047 * Creates referencing objects for a limited set of hard-coded EPSG codes 048 * using {@link ObjectFactory} and {@link MathTransformFactory}. 049 * This pseudo-factory can be used with implementations that do not support 050 * (or don't want to test) a "real" {@link CRSAuthorityFactory} for the EPSG database. 051 * 052 * @author Martin Desruisseaux (Geomatys) 053 * @author Johann Sorel (Geomatys) 054 * @author Michael Arneson (INT) 055 * @version 3.1 056 * @since 3.1 057 */ 058@SuppressWarnings("strictfp") // Because we still target Java 11. 059public strictfp class PseudoEpsgFactory extends PseudoFactory implements DatumAuthorityFactory, 060 CSAuthorityFactory, CRSAuthorityFactory 061{ 062 /** 063 * The reciprocal of the conversion from US feets to metres. 064 */ 065 static final double R_US_FEET = 3.2808333333333333333; 066 067 /** 068 * Conversion from Clarke's 1865 feet to metres. 069 */ 070 static final double CLARKE_FEET = 0.3047972654; 071 072 /** 073 * Conversion from feet to metres. 074 */ 075 static final double FEET = 0.3048; 076 077 /** 078 * Conversion from links to metres 079 */ 080 static final double LINKS = 0.66 * FEET; 081 082 /** 083 * Provider of predefined {@link Unit} instances (degree, metre, second, <i>etc</i>). 084 */ 085 protected final Units units; 086 087 /** 088 * Factory to use for building {@link Datum} instances, or {@code null} if none. 089 */ 090 protected final DatumFactory datumFactory; 091 092 /** 093 * Factory to use for building {@link CoordinateSystem} instances, or {@code null} if none. 094 */ 095 protected final CSFactory csFactory; 096 097 /** 098 * Factory to use for building {@link CoordinateReferenceSystem} instances, or {@code null} if none. 099 */ 100 protected final CRSFactory crsFactory; 101 102 /** 103 * Factory to use for building {@link Conversion} instances, or {@code null} if none. 104 */ 105 protected final CoordinateOperationFactory copFactory; 106 107 /** 108 * Factory to use for building {@link MathTransform} instances, or {@code null} if none. 109 */ 110 protected final MathTransformFactory mtFactory; 111 112 /** 113 * The set of validators to use for verifying objects conformance (never {@code null}). 114 */ 115 protected final ValidatorContainer validators; 116 117 /** 118 * Creates a new pseudo-factory which will use the given factories. 119 * 120 * @param units provider of predefined {@link Unit} instances. 121 * @param datumFactory factory for creating {@link Datum} instances. 122 * @param csFactory factory for creating {@link CoordinateSystem} instances. 123 * @param crsFactory factory for creating {@link CoordinateReferenceSystem} instances. 124 * @param copFactory factory for creating {@link Conversion} instances. 125 * @param mtFactory factory for creating {@link MathTransform} instances. 126 * @param validators the set of validators to use for verifying objects conformance, 127 * Cannot be {@code null}; if there is no particular validators, 128 * use {@link org.opengis.test.Validators#DEFAULT}. 129 */ 130 public PseudoEpsgFactory( 131 final Units units, 132 final DatumFactory datumFactory, 133 final CSFactory csFactory, 134 final CRSFactory crsFactory, 135 final CoordinateOperationFactory copFactory, 136 final MathTransformFactory mtFactory, 137 final ValidatorContainer validators) 138 { 139 this.units = Objects.requireNonNull(units, "The units cannot be null. Do you mean Units.getDefault()?"); 140 this.datumFactory = datumFactory; 141 this.csFactory = csFactory; 142 this.crsFactory = crsFactory; 143 this.copFactory = copFactory; 144 this.mtFactory = mtFactory; 145 this.validators = Objects.requireNonNull(validators, "The validators cannot be null. Do you mean Validators.DEFAULT?"); 146 } 147 148 /** 149 * Returns the given EPSG code as an integer. 150 * 151 * @param code the EPSG code to parse. 152 * @return the EPSG code as an integer. 153 * @throws NoSuchAuthorityCodeException if the given code cannot be parsed as an integer. 154 */ 155 private static int parseCode(String code) throws NoSuchAuthorityCodeException { 156 final int s = code.lastIndexOf(':'); 157 if (s >= 0) { 158 final String authority = code.substring(0, s).trim(); 159 if (!authority.equalsIgnoreCase("EPSG")) { 160 throw new NoSuchAuthorityCodeException("Unsupported \"" + authority + "\" authority.", "EPSG", code); 161 } 162 code = code.substring(s+1).trim(); 163 } 164 try { 165 return Integer.parseInt(code); 166 } catch (NumberFormatException cause) { 167 NoSuchAuthorityCodeException e = new NoSuchAuthorityCodeException( 168 "Unparseable EPSG code: " + code, "EPSG", code); 169 e.initCause(cause); 170 throw e; 171 } 172 } 173 174 /** 175 * Creates the exception to be thrown when the given code has not been recognized. 176 * 177 * @param code the code as a numerical value. 178 * @param given the code as given by the user. 179 * @return the exception to throw. 180 */ 181 private static NoSuchAuthorityCodeException noSuchAuthorityCode(final int code, final String given) { 182 final String codeAsText = String.valueOf(code); 183 return new NoSuchAuthorityCodeException("No case implemented for EPSG:" + codeAsText, 184 "EPSG", codeAsText, given); 185 } 186 187 /** 188 * Returns the organization or party responsible for definition and maintenance of the database. 189 * The default implementation returns {@code null}. 190 * 191 * @return the organization responsible for definition of the database. 192 */ 193 @Override 194 public Citation getAuthority() { 195 return null; 196 } 197 198 /** 199 * Returns the set of authority codes for objects of the given type. 200 * The default implementation returns an empty set. 201 * 202 * @param type the spatial reference objects type. 203 * @return the set of authority codes for spatial reference objects of the given type. 204 * @throws FactoryException if this method cannot provide the requested information. 205 * 206 * @todo Needs to be implemented. 207 */ 208 @Override 209 public Set<String> getAuthorityCodes(final Class<? extends IdentifiedObject> type) throws FactoryException { 210 return Collections.emptySet(); 211 } 212 213 /** 214 * Builds a map of properties for a referencing object to be built. The map shall contain 215 * at least the {@link IdentifiedObject#NAME_KEY} identifier associated to the given value. 216 * Subclasses can override this method in order to provide more information if they wish. 217 * 218 * @param code The EPSG code of the object being built. 219 * @param name The name of the object being built. 220 * @return a map containing the properties for the object to create. 221 */ 222 protected Map<String,Object> createPropertiesMap(final int code, final String name) { 223 final Map<String,Object> properties = new HashMap<>(4); 224 assertNull(properties.put(IdentifiedObject.NAME_KEY, name)); 225 assertNull(properties.put(IdentifiedObject.IDENTIFIERS_KEY, new EPSGIdentifier(code))); 226 return properties; 227 } 228 229 /** 230 * Returns an arbitrary object from a code. 231 * 232 * <table class="ogc"> 233 * <caption>Supported codes</caption> 234 * <tr><th>Code</th> <th>Name</th></tr> 235 * <tr><td>4326</td> <td>WGS 84</td></tr> 236 * <tr><td>6326</td> <td>World Geodetic System 1984</td></tr> 237 * <tr><td>6422</td> <td>Ellipsoidal 2D CS. Axes: latitude, longitude. Orientations: north, east. UoM: degree</td></tr> 238 * </table> 239 * 240 * @param code value allocated by authority. 241 * @return the datum for the given code. 242 * @throws FactoryException if the object creation failed. 243 * 244 * @deprecated This method is ambiguous. 245 */ 246 @Override 247 @SuppressWarnings("removal") 248 @Deprecated(since = "3.1", forRemoval = true) 249 public IdentifiedObject createObject(final String code) throws FactoryException { 250 final int id = parseCode(code); 251 switch (id) { 252 case 6326: return createDatum(code); 253 case 6422: return createCoordinateSystem(code); 254 case 4326: return createCoordinateReferenceSystem(code); 255 default: throw noSuchAuthorityCode(id, code); 256 } 257 } 258 259 260 261 262 /////////////////////////////////////////////////////////////////////////////////////////////// 263 /////////////////////////////// /////////////////////////////// 264 /////////////////////////////// D A T U M F A C T O R Y /////////////////////////////// 265 /////////////////////////////// /////////////////////////////// 266 /////////////////////////////////////////////////////////////////////////////////////////////// 267 268 /** 269 * Returns an arbitrary {@linkplain Datum datum} from a code. 270 * 271 * <table class="ogc"> 272 * <caption>Supported codes</caption> 273 * <tr><th>Code</th> <th>Name</th></tr> 274 * <tr><td>6326</td> <td>World Geodetic System 1984</td></tr> 275 * </table> 276 * 277 * @param code value allocated by authority. 278 * @return the datum for the given code. 279 * @throws FactoryException if the object creation failed. 280 */ 281 @Override 282 public Datum createDatum(final String code) throws FactoryException { 283 final int id = parseCode(code); 284 switch (id) { 285 case 6326: return createGeodeticDatum(code); 286 default: throw noSuchAuthorityCode(id, code); 287 } 288 } 289 290 /** 291 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 292 * 293 * @throws FactoryException if this method cannot provide the requested information. 294 */ 295 @Override 296 public EngineeringDatum createEngineeringDatum(String code) throws FactoryException { 297 final int id = parseCode(code); 298 switch (id) { 299 default: throw noSuchAuthorityCode(id, code); 300 } 301 } 302 303 /** 304 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 305 * 306 * @throws FactoryException if this method cannot provide the requested information. 307 * 308 * @deprecated {@code ImageDatum} is replaced by {@link EngineeringDatum} as of ISO 19111:2019. 309 */ 310 @Override 311 @Deprecated(since="3.1") 312 public ImageDatum createImageDatum(String code) throws FactoryException { 313 final int id = parseCode(code); 314 switch (id) { 315 default: throw noSuchAuthorityCode(id, code); 316 } 317 } 318 319 /** 320 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 321 * 322 * @throws FactoryException if this method cannot provide the requested information. 323 */ 324 @Override 325 public VerticalDatum createVerticalDatum(String code) throws FactoryException { 326 final int id = parseCode(code); 327 switch (id) { 328 default: throw noSuchAuthorityCode(id, code); 329 } 330 } 331 332 /** 333 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 334 * 335 * @throws FactoryException if this method cannot provide the requested information. 336 */ 337 @Override 338 public TemporalDatum createTemporalDatum(String code) throws FactoryException { 339 final int id = parseCode(code); 340 switch (id) { 341 default: throw noSuchAuthorityCode(id, code); 342 } 343 } 344 345 /** 346 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 347 * 348 * @throws FactoryException if this method cannot provide the requested information. 349 */ 350 @Override 351 public ParametricDatum createParametricDatum(String code) throws FactoryException { 352 final int id = parseCode(code); 353 switch (id) { 354 default: throw noSuchAuthorityCode(id, code); 355 } 356 } 357 358 /** 359 * Returns a {@linkplain GeodeticDatum geodetic reference frame} from a code. 360 * 361 * <table class="ogc"> 362 * <caption>Supported codes</caption> 363 * <tr><th>Code</th> <th>Name</th></tr> 364 * <tr><td>6326</td> <td>World Geodetic System 1984</td></tr> 365 * <tr><td>6284</td> <td>Pulkovo 1942</td></tr> 366 * </table> 367 * 368 * @param code value allocated by authority. 369 * @return the datum for the given code. 370 * @throws FactoryException if the object creation failed. 371 */ 372 @Override 373 public GeodeticDatum createGeodeticDatum(final String code) throws FactoryException { 374 final String name; 375 final int ellipsoid; 376 final int primeMeridian; 377 final int id = parseCode(code); 378 switch (id) { 379 case 6326: name="World Geodetic System 1984"; ellipsoid=7030; primeMeridian=8901; break; 380 case 6284: name="Pulkovo 1942"; ellipsoid=7024; primeMeridian=8901; break; 381 default: throw noSuchAuthorityCode(id, code); 382 } 383 assumeTrue(datumFactory != null, NO_DATUM_FACTORY); 384 final GeodeticDatum object = datumFactory.createGeodeticDatum(createPropertiesMap(id, name), 385 createEllipsoid (String.valueOf(ellipsoid)), 386 createPrimeMeridian(String.valueOf(primeMeridian))); 387 validators.validate(object); 388 return object; 389 } 390 391 /** 392 * Returns an {@linkplain Ellipsoid ellipsoid} from a code. 393 * 394 * <table class="ogc"> 395 * <caption>Supported codes</caption> 396 * <tr><th>Code</th> <th>Name</th></tr> 397 * <tr><td>7001</td> <td>Airy 1830</td></tr> 398 * <tr><td>7004</td> <td>Bessel 1841</td></tr> 399 * <tr><td>7011</td> <td>Clarke 1880 (IGN)</td></tr> 400 * <tr><td>7019</td> <td>GRS 1980</td></tr> 401 * <tr><td>7022</td> <td>International 1924</td></tr> 402 * <tr><td>7024</td> <td>Krassowsky 1940</td></tr> 403 * <tr><td>7030</td> <td>WGS 84</td></tr> 404 * </table> 405 * 406 * @param code value allocated by authority. 407 * @return the ellipsoid for the given code. 408 * @throws FactoryException if the object creation failed. 409 */ 410 @Override 411 public Ellipsoid createEllipsoid(final String code) throws FactoryException { 412 final String name; 413 final double semiMajorAxis; 414 double semiMinorAxis = Double.NaN; 415 double inverseFlattening = Double.NaN; 416 int unitCode = 9001; // Default unit is metre. 417 final int id = parseCode(code); 418 switch (id) { 419 case 7030: name="WGS 84"; semiMajorAxis=6378137; inverseFlattening=298.257223563; break; 420 case 7019: name="GRS 1980"; semiMajorAxis=6378137; inverseFlattening=298.2572221; break; 421 case 7001: name="Airy 1830"; semiMajorAxis=6377563.396; inverseFlattening=299.3249646; break; 422 case 7004: name="Bessel 1841"; semiMajorAxis=6377397.155; inverseFlattening=299.1528128; break; 423 case 7024: name="Krassowsky 1940"; semiMajorAxis=6378245; inverseFlattening=298.3; break; 424 case 7022: name="International 1924"; semiMajorAxis=6378388; inverseFlattening=297; break; 425 case 7011: name="Clarke 1880 (IGN)"; semiMajorAxis=6378249.2; semiMinorAxis=6356515; break; 426 default: throw noSuchAuthorityCode(id, code); 427 } 428 assumeTrue(datumFactory != null, NO_DATUM_FACTORY); 429 final Map<String,?> properties = createPropertiesMap(id, name); 430 final Unit<Length> unit = createUnit(String.valueOf(unitCode)).asType(Length.class); 431 final Ellipsoid object; 432 if (Double.isNaN(inverseFlattening)) { 433 object = datumFactory.createEllipsoid(properties, semiMajorAxis, semiMinorAxis, unit); 434 } else { 435 object = datumFactory.createFlattenedSphere(properties, semiMajorAxis, inverseFlattening, unit); 436 } 437 validators.validate(object); 438 return object; 439 } 440 441 /** 442 * Returns a {@linkplain PrimeMeridian prime meridian} from a EPSG code. 443 * 444 * <table class="ogc"> 445 * <caption>Supported codes</caption> 446 * <tr><th>Code</th> <th>Name</th></tr> 447 * <tr><td>8901</td> <td>Greenwich</td></tr> 448 * <tr><td>8903</td> <td>Paris</td></tr> 449 * <tr><td>8908</td> <td>Jakarta</td></tr> 450 * </table> 451 * 452 * @param code value allocated by authority. 453 * @return the prime meridian for the given code. 454 * @throws FactoryException if the object creation failed. 455 */ 456 @Override 457 public PrimeMeridian createPrimeMeridian(final String code) throws FactoryException { 458 final String name; 459 final double longitude; 460 final int unit; 461 final int id = parseCode(code); 462 switch (id) { 463 case 8901: name="Greenwich"; longitude= 0.0; unit=9102; break; 464 case 8903: name="Paris"; longitude= 2.5969213; unit=9105; break; 465 case 8908: name="Jakarta"; longitude=106.80771944444444; unit=9102; break; 466 default: throw noSuchAuthorityCode(id, code); 467 } 468 assumeTrue(datumFactory != null, NO_DATUM_FACTORY); 469 final PrimeMeridian object = datumFactory.createPrimeMeridian(createPropertiesMap(id, name), 470 longitude, createUnit(String.valueOf(unit)).asType(Angle.class)); 471 validators.validate(object); 472 return object; 473 } 474 475 476 477 478 /////////////////////////////////////////////////////////////////////////////////////////////// 479 /////////////////// /////////////////// 480 /////////////////// C O O R D I N A T E S Y S T E M F A C T O R Y /////////////////// 481 /////////////////// /////////////////// 482 /////////////////////////////////////////////////////////////////////////////////////////////// 483 484 /** 485 * Returns an arbitrary {@linkplain CoordinateSystem coordinate system} from a code. 486 * 487 * <table class="ogc"> 488 * <caption>Supported codes</caption> 489 * <tr><th>Code</th> <th>Name</th></tr> 490 * <tr><td>6422</td> <td>Ellipsoidal 2D CS. Axes: latitude, longitude. Orientations: north, east. UoM: degree</td></tr> 491 * </table> 492 * 493 * @param code value allocated by authority. 494 * @return the coordinate system for the given code. 495 * @throws FactoryException if the object creation failed. 496 */ 497 @Override 498 public CoordinateSystem createCoordinateSystem(final String code) throws FactoryException { 499 final int id = parseCode(code); 500 switch (id) { 501 case 6422: return createEllipsoidalCS(code); 502 default: throw noSuchAuthorityCode(id, code); 503 } 504 } 505 506 /** 507 * Creates a Cartesian coordinate system from a code. 508 * 509 * <table class="ogc"> 510 * <caption>Supported codes</caption> 511 * <tr><th>Code</th> <th>Name</th></tr> 512 * <tr><td>4400</td> <td>Cartesian 2D CS. Axes: easting, northing (E,N). Orientations: east, north. UoM: m.</td></tr> 513 * <tr><td>4495</td> <td>Cartesian 2D CS. Axes: easting, northing (X,Y). Orientations: east, north. UoM: ft.</td></tr> 514 * <tr><td>4497</td> <td>Cartesian 2D CS. Axes: easting, northing (X,Y). Orientations: east, north. UoM: ftUS.</td></tr> 515 * <tr><td>4498</td> <td>Cartesian 2D CS. Axes: easting, northing (Y,X). Orientations: east, north. UoM: m.</td></tr> 516 * <tr><td>4499</td> <td>Cartesian 2D CS. Axes: easting, northing (X,Y). Orientations: east, north. UoM: m.</td></tr> 517 * <tr><td>4500</td> <td>Cartesian 2D CS. Axes: northing, easting (N,E). Orientations: north, east. UoM: m.</td></tr> 518 * <tr><td>4530</td> <td>Cartesian 2D CS. Axes: northing, easting (X,Y). Orientations: north, east. UoM: m.</td></tr> 519 * <tr><td>4532</td> <td>Cartesian 2D CS. Axes: northing, easting (Y,X). Orientations: north, east. UoM: m.</td></tr> 520 * <tr><td>4534</td> <td>Cartesian 2D CS. Axes: northing, easting (no abbrev). Orientations: north, east. UoM: m.</td></tr> 521 * <tr><td>6503</td> <td>Cartesian 2D CS. Axes: westing, southing (Y,X). Orientations: west, south. UoM: m.</td></tr> 522 * <tr><td>6500</td> <td>Earth centred, earth fixed, righthanded 3D coordinate system, 523 * consisting of 3 orthogonal axes with X and Y axes in the equatorial plane, 524 * positive Z-axis parallel to mean earth rotation axis and pointing towards North Pole. 525 * UoM: m</td></tr> 526 * </table> 527 * 528 * @param code value allocated by authority. 529 * @return the coordinate system for the given code. 530 * @throws FactoryException if the object creation failed. 531 */ 532 @Override 533 public CartesianCS createCartesianCS(final String code) throws FactoryException { 534 final String name; 535 final int[] axes; 536 final int id = parseCode(code); 537 switch (id) { 538 case 4400: name = "Cartesian 2D CS. Axes: easting, northing (E,N). Orientations: east, north. UoM: m."; axes = new int[] { 1, 2}; break; 539 case 4495: name = "Cartesian 2D CS. Axes: easting, northing (X,Y). Orientations: east, north. UoM: ft."; axes = new int[] { 33, 34}; break; 540 case 4497: name = "Cartesian 2D CS. Axes: easting, northing (X,Y). Orientations: east, north. UoM: ftUS."; axes = new int[] { 37, 38}; break; 541 case 4498: name = "Cartesian 2D CS. Axes: easting, northing (Y,X). Orientations: east, north. UoM: m."; axes = new int[] { 39, 40}; break; 542 case 4499: name = "Cartesian 2D CS. Axes: easting, northing (X,Y). Orientations: east, north. UoM: m."; axes = new int[] { 41, 42}; break; 543 case 4500: name = "Cartesian 2D CS. Axes: northing, easting (N,E). Orientations: north, east. UoM: m."; axes = new int[] { 44, 43}; break; 544 case 4530: name = "Cartesian 2D CS. Axes: northing, easting (X,Y). Orientations: north, east. UoM: m."; axes = new int[] { 48, 47}; break; 545 case 4532: name = "Cartesian 2D CS. Axes: northing, easting (Y,X). Orientations: north, east. UoM: m."; axes = new int[] { 52, 51}; break; 546 case 4534: name = "Cartesian 2D CS. Axes: northing, easting (no abbrev). Orientations: north, east. UoM: m."; axes = new int[] {183, 184}; break; 547 case 6503: name = "Cartesian 2D CS. Axes: westing, southing (Y,X). Orientations: west, south. UoM: m."; axes = new int[] {122, 123}; break; 548 case 6500: { 549 name = "Earth centred, earth fixed, righthanded 3D coordinate system, " 550 + "consisting of 3 orthogonal axes with X and Y axes in the equatorial plane, " 551 + "positive Z-axis parallel to mean earth rotation axis and pointing towards North Pole. " 552 + "UoM: m"; 553 axes = new int[] {115, 116, 117}; 554 break; 555 } 556 default: throw noSuchAuthorityCode(id, code); 557 } 558 assumeTrue(csFactory != null, NO_CS_FACTORY); 559 final Map<String,?> properties = createPropertiesMap(id, name); 560 final CartesianCS object; 561 if (axes.length >= 3) { 562 object = csFactory.createCartesianCS(properties, 563 createCoordinateSystemAxis(String.valueOf(axes[0])), 564 createCoordinateSystemAxis(String.valueOf(axes[1])), 565 createCoordinateSystemAxis(String.valueOf(axes[2]))); 566 } else { 567 object = csFactory.createCartesianCS(properties, 568 createCoordinateSystemAxis(String.valueOf(axes[0])), 569 createCoordinateSystemAxis(String.valueOf(axes[1]))); 570 } 571 validators.validate(object); 572 return object; 573 } 574 575 /** 576 * Creates a polar coordinate system from a code. 577 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 578 * 579 * @param code value allocated by authority. 580 * @return the coordinate system for the given code. 581 * @throws FactoryException if this method cannot provide the requested information. 582 */ 583 @Override 584 public PolarCS createPolarCS(final String code) throws FactoryException { 585 final int id = parseCode(code); 586 switch (id) { 587 default: throw noSuchAuthorityCode(id, code); 588 } 589 } 590 591 /** 592 * Creates a cylindrical coordinate system from a code. 593 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 594 * 595 * @param code value allocated by authority. 596 * @return the coordinate system for the given code. 597 * @throws FactoryException if this method cannot provide the requested information. 598 */ 599 @Override 600 public CylindricalCS createCylindricalCS(final String code) throws FactoryException { 601 final int id = parseCode(code); 602 switch (id) { 603 default: throw noSuchAuthorityCode(id, code); 604 } 605 } 606 607 /** 608 * Creates a spherocal coordinate system from a code. 609 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 610 * 611 * @param code value allocated by authority. 612 * @return the coordinate system for the given code. 613 * @throws FactoryException if this method cannot provide the requested information. 614 */ 615 @Override 616 public SphericalCS createSphericalCS(final String code) throws FactoryException { 617 final int id = parseCode(code); 618 switch (id) { 619 default: throw noSuchAuthorityCode(id, code); 620 } 621 } 622 623 /** 624 * Creates an ellipsoidal coordinate system from a code. 625 * 626 * <table class="ogc"> 627 * <caption>Supported codes</caption> 628 * <tr><th>Code</th> <th>Name</th></tr> 629 * <tr><td>6403</td> <td>Ellipsoidal 2D CS. Axes: latitude, longitude. Orientations: north, east. UoM: grads.</td></tr> 630 * <tr><td>6422</td> <td>Ellipsoidal 2D CS. Axes: latitude, longitude. Orientations: north, east. UoM: degree</td></tr> 631 * <tr><td>6423</td> <td>Ellipsoidal 3D CS. Axes: latitude, longitude, ellipsoidal height. Orientations: north, east, up. UoM: degree, degree, metre.</td></tr> 632 * <tr><td>6424</td> <td>Ellipsoidal 2D CS. Axes: longitude, latitude. Orientations: east, north. UoM: degree</td></tr> 633 * </table> 634 * 635 * @param code value allocated by authority. 636 * @return the coordinate system for the given code. 637 * @throws FactoryException if the object creation failed. 638 */ 639 @Override 640 public EllipsoidalCS createEllipsoidalCS(final String code) throws FactoryException { 641 final String name; 642 final int[] axes; 643 final int id = parseCode(code); 644 switch (id) { 645 case 6403: { 646 name = "Ellipsoidal 2D CS. Axes: latitude, longitude. " 647 + "Orientations: north, east. " 648 + "UoM: grads. "; 649 axes = new int[] {58, 59}; // Geodetic latitude, Geodetic longitude 650 break; 651 } 652 case 6422: { 653 name = "Ellipsoidal 2D CS. Axes: latitude, longitude. " 654 + "Orientations: north, east. " 655 + "UoM: degree"; 656 axes = new int[] {106, 107}; // Geodetic latitude, Geodetic longitude 657 break; 658 } 659 case 6423: { 660 name = "Ellipsoidal 3D CS. Axes: latitude, longitude, ellipsoidal height. " 661 + "Orientations: north, east, up. " 662 + "UoM: degree, degree, metre."; 663 axes = new int[] {108, 109, 110}; // Geodetic latitude, Geodetic longitude, Ellipsoidal height 664 break; 665 } 666 case 6424: { 667 name = "Ellipsoidal 2D CS. Axes: longitude, latitude. " 668 + "Orientations: east, north. " 669 + "UoM: degree"; 670 axes = new int[] {220, 221}; // Geodetic longitude, Geodetic latitude 671 break; 672 } 673 default: throw noSuchAuthorityCode(id, code); 674 } 675 assumeTrue(csFactory != null, NO_CS_FACTORY); 676 final Map<String,?> properties = createPropertiesMap(id, name); 677 final EllipsoidalCS object; 678 if (axes.length >= 3) { 679 object = csFactory.createEllipsoidalCS(properties, 680 createCoordinateSystemAxis(String.valueOf(axes[0])), 681 createCoordinateSystemAxis(String.valueOf(axes[1])), 682 createCoordinateSystemAxis(String.valueOf(axes[2]))); 683 } else { 684 object = csFactory.createEllipsoidalCS(properties, 685 createCoordinateSystemAxis(String.valueOf(axes[0])), 686 createCoordinateSystemAxis(String.valueOf(axes[1]))); 687 } 688 validators.validate(object); 689 return object; 690 } 691 692 /** 693 * Creates a vertical coordinate system from a code. 694 * 695 * <table class="ogc"> 696 * <caption>Supported codes</caption> 697 * <tr><th>Code</th> <th>Name</th></tr> 698 * <tr><td>1030</td> <td>Vertical CS. Axis: height (H). Orientation: up. UoM: ft.</td></tr> 699 * <tr><td>6495</td> <td>Vertical CS. Axis: depth (D). Orientation: down. UoM: ft.</td></tr> 700 * <tr><td>6497</td> <td>Vertical CS. Axis: height (H). Orientation: up. UoM: ftUS.</td></tr> 701 * <tr><td>6498</td> <td>Vertical CS. Axis: depth (D). Orientation: down. UoM: m.;</td></tr> 702 * <tr><td>6499</td> <td>Vertical CS. Axis: height (H). Orientation: up. UoM: m.</td></tr> 703 * </table> 704 * 705 * @param code value allocated by authority. 706 * @return the coordinate system for the given code. 707 * @throws FactoryException if this method cannot provide the requested information. 708 */ 709 @Override 710 public VerticalCS createVerticalCS(final String code) throws FactoryException { 711 final int id = parseCode(code); 712 final String name; 713 final int axis; 714 switch (id) { 715 case 1030: name = "Vertical CS. Axis: height (H). Orientation: up. UoM: ft."; axis = 1082; break; 716 case 6495: name = "Vertical CS. Axis: depth (D). Orientation: down. UoM: ft."; axis = 214; break; 717 case 6497: name = "Vertical CS. Axis: height (H). Orientation: up. UoM: ftUS."; axis = 112; break; 718 case 6498: name = "Vertical CS. Axis: depth (D). Orientation: down. UoM: m."; axis = 113; break; 719 case 6499: name = "Vertical CS. Axis: height (H). Orientation: up. UoM: m."; axis = 114; break; 720 default: throw noSuchAuthorityCode(id, code); 721 } 722 assumeTrue(csFactory != null, NO_CS_FACTORY); 723 final Map<String,?> properties = createPropertiesMap(id, name); 724 final VerticalCS object = csFactory.createVerticalCS(properties, 725 createCoordinateSystemAxis(String.valueOf(axis))); 726 validators.validate(object); 727 return object; 728 } 729 730 /** 731 * Creates a temporal coordinate system from a code. 732 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 733 * 734 * @param code value allocated by authority. 735 * @return the coordinate system for the given code. 736 * @throws FactoryException if this method cannot provide the requested information. 737 */ 738 @Override 739 public TimeCS createTimeCS(final String code) throws FactoryException { 740 final int id = parseCode(code); 741 switch (id) { 742 default: throw noSuchAuthorityCode(id, code); 743 } 744 } 745 746 /** 747 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 748 * 749 * @throws FactoryException if this method cannot provide the requested information. 750 */ 751 @Override 752 public ParametricCS createParametricCS(String code) throws FactoryException { 753 final int id = parseCode(code); 754 switch (id) { 755 default: throw noSuchAuthorityCode(id, code); 756 } 757 } 758 759 /** 760 * Returns a {@linkplain CoordinateSystemAxis coordinate system axis} from a code. 761 * 762 * <table class="ogc"> 763 * <caption>Supported codes</caption> 764 * <tr><th>Code</th> <th>Name</th> <th>Abbreviation</th> <th>Unit</th></tr> 765 * <tr><td>1, 43</td> <td>Easting</td> <td>E</td> <td>metre</td></tr> 766 * <tr><td>2, 44</td> <td>Northing</td> <td>N</td> <td>metre</td></tr> 767 * <tr><td>41, 51</td> <td>Easting</td> <td>X</td> <td>metre</td></tr> 768 * <tr><td>42, 52</td> <td>Northing</td> <td>Y</td> <td>metre</td></tr> 769 * <tr><td>39, 47</td> <td>Easting</td> <td>Y</td> <td>metre</td></tr> 770 * <tr><td>40, 48</td> <td>Northing</td> <td>X</td> <td>metre</td></tr> 771 * <tr><td>33</td> <td>Easting</td> <td>X</td> <td>foot</td></tr> 772 * <tr><td>34</td> <td>Northing</td> <td>Y</td> <td>foot</td></tr> 773 * <tr><td>37</td> <td>Easting</td> <td>X</td> <td>foot US</td></tr> 774 * <tr><td>38</td> <td>Northing</td> <td>Y</td> <td>foot US</td></tr> 775 * <tr><td>122</td> <td>Westing</td> <td>Y</td> <td>metre</td></tr> 776 * <tr><td>123</td> <td>Southing</td> <td>X</td> <td>metre</td></tr> 777 * <tr><td>183</td> <td>Northing</td> <td>none</td> <td>metre</td></tr> 778 * <tr><td>184</td> <td>Easting</td> <td>none</td> <td>metre</td></tr> 779 * <tr><td>58</td> <td>Geodetic latitude</td> <td>Lat</td> <td>grad</td></tr> 780 * <tr><td>59</td> <td>Geodetic longitude</td> <td>Long</td> <td>grad</td></tr> 781 * <tr><td>106, 108, 221</td> <td>Geodetic latitude</td> <td>Lat</td> <td>degree</td></tr> 782 * <tr><td>107, 109, 220</td> <td>Geodetic longitude</td> <td>Long</td> <td>degree</td></tr> 783 * <tr><td>110</td> <td>Ellipsoidal height</td> <td>h</td> <td>metre</td></tr> 784 * <tr><td>115</td> <td>Geocentric X</td> <td>X</td> <td>metre</td></tr> 785 * <tr><td>116</td> <td>Geocentric Y</td> <td>Y</td> <td>metre</td></tr> 786 * <tr><td>117</td> <td>Geocentric Z</td> <td>Z</td> <td>metre</td></tr> 787 * <tr><td>112</td> <td>Gravity-related height</td> <td>H</td> <td>foot US</td></tr> 788 * <tr><td>113</td> <td>Gravity-related depth</td> <td>D</td> <td>metre</td></tr> 789 * <tr><td>114</td> <td>Gravity-related height</td> <td>H</td> <td>metre</td></tr> 790 * <tr><td>214</td> <td>Gravity-related depth</td> <td>D</td> <td>foot</td></tr> 791 * <tr><td>1082</td> <td>Gravity-related height</td> <td>H</td> <td>foot</td></tr> 792 * </table> 793 * 794 * @param code value allocated by authority. 795 * @return the axis for the given code. 796 * @throws FactoryException if the object creation failed. 797 */ 798 @Override 799 public CoordinateSystemAxis createCoordinateSystemAxis(final String code) throws FactoryException { 800 final String name; 801 final String abbreviation; 802 final AxisDirection direction; 803 final int unit; 804 final int id = parseCode(code); 805 switch (id) { 806 case 1: case 43: name="Easting"; abbreviation="E"; direction=AxisDirection.EAST; unit=9001; break; 807 case 2: case 44: name="Northing"; abbreviation="N"; direction=AxisDirection.NORTH; unit=9001; break; 808 case 41: case 51: name="Easting"; abbreviation="X"; direction=AxisDirection.EAST; unit=9001; break; 809 case 42: case 52: name="Northing"; abbreviation="Y"; direction=AxisDirection.NORTH; unit=9001; break; 810 case 39: case 47: name="Easting"; abbreviation="Y"; direction=AxisDirection.EAST; unit=9001; break; 811 case 40: case 48: name="Northing"; abbreviation="X"; direction=AxisDirection.NORTH; unit=9001; break; 812 case 33: name="Easting"; abbreviation="X"; direction=AxisDirection.EAST; unit=9002; break; 813 case 34: name="Northing"; abbreviation="Y"; direction=AxisDirection.NORTH; unit=9002; break; 814 case 37: name="Easting"; abbreviation="X"; direction=AxisDirection.EAST; unit=9003; break; 815 case 38: name="Northing"; abbreviation="Y"; direction=AxisDirection.NORTH; unit=9003; break; 816 case 122: name="Westing"; abbreviation="Y"; direction=AxisDirection.WEST; unit=9001; break; 817 case 123: name="Southing"; abbreviation="X"; direction=AxisDirection.SOUTH; unit=9001; break; 818 case 183: name="Northing"; abbreviation="none"; direction=AxisDirection.NORTH; unit=9001; break; 819 case 184: name="Easting"; abbreviation="none"; direction=AxisDirection.EAST; unit=9001; break; 820 case 108: case 221: 821 case 106: name="Geodetic latitude"; abbreviation="Lat"; direction=AxisDirection.NORTH; unit=9122; break; 822 case 58: name="Geodetic latitude"; abbreviation="Lat"; direction=AxisDirection.NORTH; unit=9105; break; 823 case 109: case 220: 824 case 107: name="Geodetic longitude"; abbreviation="Long"; direction=AxisDirection.EAST; unit=9122; break; 825 case 59: name="Geodetic longitude"; abbreviation="Long"; direction=AxisDirection.EAST; unit=9105; break; 826 case 110: name="Ellipsoidal height"; abbreviation="h"; direction=AxisDirection.UP; unit=9001; break; 827 case 115: name="Geocentric X"; abbreviation="X"; direction=AxisDirection.GEOCENTRIC_X; unit=9001; break; 828 case 116: name="Geocentric Y"; abbreviation="Y"; direction=AxisDirection.GEOCENTRIC_Y; unit=9001; break; 829 case 117: name="Geocentric Z"; abbreviation="Z"; direction=AxisDirection.GEOCENTRIC_Z; unit=9001; break; 830 case 112: name="Gravity-related height"; abbreviation="H"; direction=AxisDirection.UP; unit=9003; break; 831 case 113: name="Gravity-related depth"; abbreviation="D"; direction=AxisDirection.DOWN; unit=9001; break; 832 case 114: name="Gravity-related height"; abbreviation="H"; direction=AxisDirection.UP; unit=9001; break; 833 case 214: name="Gravity-related depth"; abbreviation="D"; direction=AxisDirection.DOWN; unit=9002; break; 834 case 1082: name="Gravity-related height"; abbreviation="H"; direction=AxisDirection.UP; unit=9002; break; 835 default: throw noSuchAuthorityCode(id, code); 836 } 837 assumeTrue(csFactory != null, NO_CS_FACTORY); 838 final CoordinateSystemAxis object = csFactory.createCoordinateSystemAxis(createPropertiesMap(id, name), 839 abbreviation, direction, createUnit(String.valueOf(unit))); 840 validators.validate(object); 841 return object; 842 } 843 844 /** 845 * Returns an {@linkplain Unit unit} from a code. 846 * 847 * <table class="ogc"> 848 * <caption>Supported codes</caption> 849 * <tr><th>Code</th> <th>Name</th></tr> 850 * <tr><td>9001</td> <td>metre</td></tr> 851 * <tr><td>9002</td> <td>foot</td></tr> 852 * <tr><td>9003</td> <td>foot US survey</td></tr> 853 * <tr><td>9102</td> <td>degree</td></tr> 854 * <tr><td>9105</td> <td>grad</td></tr> 855 * <tr><td>9122</td> <td>degree (supplier to define representation)</td></tr> 856 * </table> 857 * 858 * @param code value allocated by authority. 859 * @return the unit for the given code. 860 * @throws FactoryException if the object creation failed. 861 */ 862 @Override 863 public Unit<?> createUnit(final String code) throws FactoryException { 864 final int id = parseCode(code); 865 switch (id) { 866 case 9001: return units.metre(); 867 case 9002: return units.foot(); 868 case 9003: return units.footSurveyUS(); 869 case 9122: // Fall through 870 case 9102: return units.degree(); 871 case 9105: return units.grad(); 872 default: throw noSuchAuthorityCode(id, code); 873 } 874 } 875 876 877 878 879 /////////////////////////////////////////////////////////////////////////////////////////////// 880 ///////// ///////// 881 ///////// C O O R D I N A T E R E F E R E N C E S Y S T E M F A C T O R Y ///////// 882 ///////// ///////// 883 /////////////////////////////////////////////////////////////////////////////////////////////// 884 885 /** 886 * Returns an arbitrary {@linkplain CoordinateReferenceSystem coordinate reference system} from a code. 887 * 888 * <table class="ogc"> 889 * <caption>Supported codes</caption> 890 * <tr><th>Code</th> <th>Name</th></tr> 891 * <tr><td>4326</td> <td>WGS 84</td></tr> 892 * </table> 893 * 894 * @param code value allocated by authority. 895 * @return the coordinate reference system for the given code. 896 * @throws FactoryException if the object creation failed. 897 */ 898 @Override 899 public CoordinateReferenceSystem createCoordinateReferenceSystem(final String code) throws FactoryException { 900 final int id = parseCode(code); 901 switch (id) { 902 case 4326: return createGeographicCRS(code); 903 default: throw noSuchAuthorityCode(id, code); 904 } 905 } 906 907 /** 908 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 909 * 910 * @throws FactoryException if this method cannot provide the requested information. 911 */ 912 @Override 913 public CompoundCRS createCompoundCRS(String code) throws FactoryException { 914 final int id = parseCode(code); 915 switch (id) { 916 default: throw noSuchAuthorityCode(id, code); 917 } 918 } 919 920 /** 921 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 922 * 923 * @throws FactoryException if this method cannot provide the requested information. 924 */ 925 @Override 926 public DerivedCRS createDerivedCRS(String code) throws FactoryException { 927 final int id = parseCode(code); 928 switch (id) { 929 default: throw noSuchAuthorityCode(id, code); 930 } 931 } 932 933 /** 934 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 935 * 936 * @throws FactoryException if this method cannot provide the requested information. 937 */ 938 @Override 939 public EngineeringCRS createEngineeringCRS(String code) throws FactoryException { 940 final int id = parseCode(code); 941 switch (id) { 942 default: throw noSuchAuthorityCode(id, code); 943 } 944 } 945 946 /** 947 * Returns a {@linkplain GeographicCRS geographic coordinate reference system} from a code. 948 * 949 * <table class="ogc"> 950 * <caption>Supported codes</caption> 951 * <tr><th>Code</th> <th>Name</th></tr> 952 * <tr><td>4326</td> <td>WGS 84</td></tr> 953 * <tr><td>4284</td> <td>Pulkovo 1942</td></tr> 954 * </table> 955 * 956 * @param code value allocated by authority. 957 * @return the coordinate reference system for the given code. 958 * @throws FactoryException if the object creation failed. 959 */ 960 @Override 961 public GeographicCRS createGeographicCRS(final String code) throws FactoryException { 962 final String name; 963 final int datum; 964 final int coordinateSystem; 965 final int id = parseCode(code); 966 switch (id) { 967 case 4326: name="WGS 84"; datum=6326; coordinateSystem=6422; break; 968 case 4284: name="Pulkovo 1942"; datum=6284; coordinateSystem=6422; break; 969 default: throw noSuchAuthorityCode(id, code); 970 } 971 assumeTrue(crsFactory != null, NO_CRS_FACTORY); 972 final GeographicCRS object = crsFactory.createGeographicCRS(createPropertiesMap(id, name), 973 createGeodeticDatum(String.valueOf(datum)), 974 createEllipsoidalCS(String.valueOf(coordinateSystem))); 975 validators.validate(object); 976 return object; 977 } 978 979 /** 980 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 981 * 982 * @throws FactoryException if this method cannot provide the requested information. 983 */ 984 @Override 985 @Deprecated(since = "3.1") 986 public GeocentricCRS createGeocentricCRS(String code) throws FactoryException { 987 final int id = parseCode(code); 988 switch (id) { 989 default: throw noSuchAuthorityCode(id, code); 990 } 991 } 992 993 /** 994 * The default implementation delegates to {@link #createGeographicCRS(String)}. 995 * 996 * @throws FactoryException if this method cannot provide the requested information. 997 */ 998 @Override 999 public GeodeticCRS createGeodeticCRS(final String code) throws FactoryException { 1000 return createGeographicCRS(code); 1001 } 1002 1003 /** 1004 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 1005 * 1006 * @throws FactoryException if this method cannot provide the requested information. 1007 * 1008 * @deprecated {@code ImageCRS} is replaced by {@link EngineeringCRS} as of ISO 19111:2019. 1009 */ 1010 @Override 1011 @Deprecated(since="3.1") 1012 public ImageCRS createImageCRS(String code) throws FactoryException { 1013 final int id = parseCode(code); 1014 switch (id) { 1015 default: throw noSuchAuthorityCode(id, code); 1016 } 1017 } 1018 1019 /** 1020 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 1021 * 1022 * @throws FactoryException if this method cannot provide the requested information. 1023 */ 1024 @Override 1025 public ProjectedCRS createProjectedCRS(String code) throws FactoryException { 1026 final int id = parseCode(code); 1027 switch (id) { 1028 default: throw noSuchAuthorityCode(id, code); 1029 } 1030 } 1031 1032 /** 1033 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 1034 * 1035 * @throws FactoryException if this method cannot provide the requested information. 1036 */ 1037 @Override 1038 public TemporalCRS createTemporalCRS(String code) throws FactoryException { 1039 final int id = parseCode(code); 1040 switch (id) { 1041 default: throw noSuchAuthorityCode(id, code); 1042 } 1043 } 1044 1045 /** 1046 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 1047 * 1048 * @throws FactoryException if this method cannot provide the requested information. 1049 */ 1050 @Override 1051 public VerticalCRS createVerticalCRS(String code) throws FactoryException { 1052 final int id = parseCode(code); 1053 switch (id) { 1054 default: throw noSuchAuthorityCode(id, code); 1055 } 1056 } 1057 1058 /** 1059 * The default implementation throws {@link NoSuchAuthorityCodeException} unconditionally. 1060 * 1061 * @throws FactoryException if this method cannot provide the requested information. 1062 */ 1063 @Override 1064 public ParametricCRS createParametricCRS(String code) throws FactoryException { 1065 final int id = parseCode(code); 1066 switch (id) { 1067 default: throw noSuchAuthorityCode(id, code); 1068 } 1069 } 1070 1071 1072 1073 /////////////////////////////////////////////////////////////////////////////////////////////// 1074 //////////////// //////////////// 1075 //////////////// C O O R D I N A T E O P E R A T I O N F A C T O R Y //////////////// 1076 //////////////// //////////////// 1077 /////////////////////////////////////////////////////////////////////////////////////////////// 1078 1079 /** 1080 * Returns the parameters to use for creating the {@linkplain CoordinateOperation coordinate 1081 * operation} identified by the given EPSG code. The coordinate operation is typically a map 1082 * projection used by exactly one {@linkplain ProjectedCRS projected CRS}, which is listed in 1083 * the second column for information purpose. 1084 * 1085 * <p>The supported codes are determined from the set of examples published in the EPSG guidance 1086 * document, augmented with other sources (IGNF). 1087 * The following table lists the supported codes. 1088 * <i>Codes in italics are not official EPSG codes.</i></p> 1089 * 1090 * <table class="ogc"> 1091 * <caption>Supported codes</caption> 1092 * <tr><th>Code</th> <th>Used by CRS</th><th>CRS or transformation name</th> <th>Operation method</th></tr> 1093 * <tr><td>19905</td> <td>3002</td> <td>Makassar / NEIEZ</td> <td>Mercator (variant A)</td></tr> 1094 * <tr><td>19884</td> <td>3388</td> <td>Pulkovo 1942 / Caspian Sea Mercator</td> <td>Mercator (variant B)</td></tr> 1095 * <tr><td>3856</td> <td>3857</td> <td>WGS 84 / Pseudo-Mercator</td> <td>Popular Visualisation Pseudo Mercator</td></tr> 1096 * <tr><td>4085</td> <td>4087</td> <td>WGS 84 / World Equidistant Cylindrical</td> <td>Equidistant Cylindrical</td></tr> 1097 * <tr><td><i>310642901</i></td> <td><i>310642901</i></td> <td>IGNF:MILLER</td> <td><i>Miller_Cylindrical</i></td></tr> 1098 * <tr><td>19958</td> <td>29873</td> <td>Timbalai 1948 / RSO Borneo (m)</td> <td>Hotine Oblique Mercator (variant B)</td></tr> 1099 * <tr><td>19916</td> <td>27700</td> <td>OSGB 1936 / British National Grid</td> <td>Transverse Mercator</td></tr> 1100 * <tr><td>17529</td> <td>2053</td> <td>South African Survey Grid zone 29</td> <td>Transverse Mercator</td></tr> 1101 * <tr><td>19975</td> <td>2314</td> <td>Trinidad 1903 / Trinidad Grid</td> <td>Cassini-Soldner</td></tr> 1102 * <tr><td>19878</td> <td>3139</td> <td>Vanua Levu 1915 / Vanua Levu Grid</td> <td>Hyperbolic Cassini-Soldner</td></tr> 1103 * <tr><td>19910</td> <td>24200</td> <td>JAD69 / Jamaica National Grid</td> <td>Lambert Conic Conformal (1SP)</td></tr> 1104 * <tr><td>14204</td> <td>32040</td> <td>NAD27 / Texas South Central</td> <td>Lambert Conic Conformal (2SP)</td></tr> 1105 * <tr> <td>6198</td> <td>6201</td> <td>Michigan CS27 Central zone</td> <td>Lambert Conic Conformal (2SP Michigan)</td></tr> 1106 * <tr><td>19902</td> <td>31300</td> <td>Belge 1972 / Belge Lambert 72</td> <td>Lambert Conic Conformal (2SP Belgium)</td></tr> 1107 * <tr><td>19986</td> <td>3035</td> <td>ETRS89 / LAEA Europe</td> <td>Lambert Azimuthal Equal Area</td></tr> 1108 * <tr><td>16061</td> <td>5041</td> <td>WGS 84 / UPS North (E,N)</td> <td>Polar Stereographic (variant A)</td></tr> 1109 * <tr><td>19993</td> <td>3032</td> <td>WGS 84 / Australian Antarctic Polar</td> <td>Polar Stereographic (variant B)</td></tr> 1110 * <tr><td>19983</td> <td>2985</td> <td>Petrels 1972 / Terre Adelie Polar Stereographic</td> <td>Polar Stereographic (variant C)</td></tr> 1111 * <tr><td>19914</td> <td>28992</td> <td>Amersfoort / RD New</td> <td>Oblique Stereographic</td></tr> 1112 * <tr><td><i>9818</i></td> <td><i>9818</i></td> <td><i>Polyconic</i></td> <td><i>Polyconic</i></td></tr> 1113 * <tr><td><i>9840</i></td> <td><i>9840</i></td> <td><i>Orthographic</i></td> <td><i>Orthographic</i></td></tr> 1114 * <tr><td>15399</td> <td>3295</td> <td>Guam 1963 / Yap Islands</td> <td>Modified Azimuthal Equidistant</td></tr> 1115 * <tr><td>19952</td> <td>2065</td> <td>CRS S-JTSK (Ferro) / Krovak</td> <td>Krovak</td></tr> 1116 * <tr><td><i>9605</i></td> <td>4230</td> <td>ED50 to WGS 84</td> <td>Abridged Molodensky</td></tr> 1117 * <tr><td>15595</td> <td>5820</td> <td>EPSG topocentric example B</td> <td>Geocentric/topocentric conversions</td></tr> 1118 * </table> 1119 * 1120 * @param code the EPSG code of the {@linkplain CoordinateOperation coordinate operation} to create. 1121 * @return the coordinate operation (typically a map projection) parameters. 1122 * @throws FactoryException if the given EPSG code is unknown to this factory. 1123 * 1124 * @see ParameterizedTransformTest 1125 * @see AuthorityFactoryTest 1126 */ 1127 protected ParameterValueGroup createParameters(final int code) throws FactoryException { 1128 final ParameterValueGroup parameters = createParameters(mtFactory, code); 1129 validators.validate(parameters); 1130 return parameters; 1131 } 1132 1133 /** 1134 * Implementation of the above {@link #createParameters(int)} method, 1135 * as a static method for direct access by {@link ParameterizedTransformTest}. 1136 * 1137 * @param factory the factory to use for creating the parameters. 1138 * @param code authority code of the parameter to create. 1139 * @return parameter for the given authority code. 1140 * @throws FactoryException if the given EPSG code is unknown to this factory. 1141 */ 1142 static ParameterValueGroup createParameters(final MathTransformFactory factory, final int code) 1143 throws FactoryException 1144 { 1145 final ParameterValueGroup parameters; 1146 switch (code) { 1147 case 19905: { // "Makassar / NEIEZ" using operation method 9804 1148 parameters = factory.getDefaultParameters("Mercator (variant A)"); // Alias "Mercator (1SP)" 1149 parameters.parameter("semi_major").setValue(6377397.155); // Bessel 1841 1150 parameters.parameter("semi_minor").setValue(6377397.155 * (1 - 1/299.1528128)); 1151 parameters.parameter("Latitude of natural origin") .setValue( 0.0); 1152 parameters.parameter("Longitude of natural origin") .setValue(110.0); 1153 parameters.parameter("Scale factor at natural origin").setValue(0.997); 1154 parameters.parameter("False easting").setValue(3900000.0); 1155 parameters.parameter("False northing").setValue(900000.0); 1156 break; 1157 } 1158 case 19884: { // "Pulkovo 1942 / Caspian Sea Mercator" using operation method 9805 1159 parameters = factory.getDefaultParameters("Mercator (variant B) "); // Alias "Mercator (2SP)" 1160 parameters.parameter("semi_major").setValue(6378245.0); // Krassowski 1940 1161 parameters.parameter("semi_minor").setValue(6378245.0 * (1 - 1/298.3)); 1162 parameters.parameter("Latitude of 1st standard parallel").setValue(42.0); 1163 parameters.parameter("Longitude of natural origin") .setValue(51.0); 1164 break; 1165 } 1166 case 3856: { // "WGS 84 / Pseudo-Mercator" using operation method 1024 1167 parameters = factory.getDefaultParameters("Popular Visualisation Pseudo Mercator"); 1168 parameters.parameter("semi_major").setValue(6378137.0); // WGS 84 1169 parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572236)); 1170 break; 1171 } 1172 case 4085: { // "WGS 84 / World Equidistant Cylindrical" using operation method 1028 1173 parameters = factory.getDefaultParameters("Equidistant Cylindrical"); 1174 parameters.parameter("semi_major").setValue(6378137.0); // WGS 84 1175 parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572236)); 1176 break; 1177 } 1178 case 310642901: { // "IGNF:MILLER" (not an official EPSG code) 1179 parameters = factory.getDefaultParameters("Miller_Cylindrical"); 1180 parameters.parameter("semi_major").setValue(6378137.0); 1181 parameters.parameter("semi_minor").setValue(6378137.0); 1182 break; 1183 } 1184 case 19958: { // "Rectified Skew Orthomorphic Borneo Grid (metres)" using operation method 9815 1185 parameters = factory.getDefaultParameters("Hotine Oblique Mercator (variant B)"); 1186 parameters.parameter("semi_major").setValue(6377298.556); // Everest 1830 1187 parameters.parameter("semi_minor").setValue(6377298.556 * (1 - 1/300.8017)); 1188 parameters.parameter("Latitude of projection centre") .setValue( 4.0); // 4°00'00"N 1189 parameters.parameter("Longitude of projection centre").setValue(115.0); // 115°00'00"E 1190 parameters.parameter("Azimuth of initial line").setValue(53 + (18 + 56.9537/60)/60); // 53°18'56.9537" 1191 parameters.parameter("Angle from Rectified to Skew Grid").setValue(53 + (7 + 48.3685/60)/60); // 53°07'48.3685" 1192 parameters.parameter("Scale factor on initial line") .setValue(0.99984); 1193 parameters.parameter("Easting at projection centre") .setValue(590476.87); 1194 parameters.parameter("Northing at projection centre").setValue(442857.65); 1195 break; 1196 } 1197 case 19916: { // "British National Grid" using operation method 9807 1198 parameters = factory.getDefaultParameters("Transverse Mercator"); 1199 parameters.parameter("semi_major").setValue(6377563.396); // Airy 1200 parameters.parameter("semi_minor").setValue(6377563.396 * (1 - 1/299.32496)); 1201 parameters.parameter("Latitude of natural origin") .setValue(49.0); 1202 parameters.parameter("Longitude of natural origin").setValue(-2.0); 1203 parameters.parameter("Scale factor at natural origin").setValue(0.9996012717); 1204 parameters.parameter("False easting") .setValue( 400000.00); 1205 parameters.parameter("False northing").setValue(-100000.00); 1206 break; 1207 } 1208 case 17529: { // "South African Survey Grid zone 29" using operation method 9808 1209 parameters = factory.getDefaultParameters("Transverse Mercator (South Orientated)"); 1210 parameters.parameter("semi_major").setValue(6378137.0); // WGS 84 1211 parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572236)); 1212 parameters.parameter("Latitude of natural origin").setValue(0.0); 1213 parameters.parameter("Longitude of natural origin").setValue(29.0); 1214 parameters.parameter("Scale factor at natural origin").setValue(1.0); 1215 parameters.parameter("False easting") .setValue(0.0); 1216 parameters.parameter("False northing").setValue(0.0); 1217 break; 1218 } 1219 case 19975: { // "Trinidad 1903 / Trinidad Grid" using operation method 9806 1220 /* 1221 * Values used below are those published in IOGP Publication 373-7-2 §3.2.2 — September 2019. 1222 * They differ from values in EPSG geodetic dataset 9.8.11 in following aspects: geodetic dataset 1223 * uses Clarke's foot units instead of foot and link units, with same values for ellipsoid axis 1224 * lengths (before conversion) but different values for false easting and northing parameters. 1225 * The differences in easting/northing parameters is up to 0.8 metre. We keep the values published 1226 * in IOGP 373-7-2 because the sample point tested in map projection is computed with those values. 1227 */ 1228 parameters = factory.getDefaultParameters("Cassini-Soldner"); 1229 parameters.parameter("semi_major").setValue(20926348.0 * FEET); // Clarke 1858 1230 parameters.parameter("semi_minor").setValue(20855233.0 * FEET); 1231 parameters.parameter("Latitude of natural origin") .setValue(10 + (26 + 30.0/60)/60); // 10°26'30"N 1232 parameters.parameter("Longitude of natural origin").setValue(-(61 + 20.0/60)); // 61°20'00"W 1233 parameters.parameter("False easting") .setValue(430000.00 * LINKS); 1234 parameters.parameter("False northing").setValue(325000.00 * LINKS); 1235 break; 1236 } 1237 case 19878: { // "Vanua Levu Grid" using operation method 9833 1238 parameters = factory.getDefaultParameters("Hyperbolic Cassini-Soldner"); 1239 parameters.parameter("semi_major").setValue(20926202.0 * FEET); // Clarke 1880 1240 parameters.parameter("semi_minor").setValue(20854895.0 * FEET); 1241 parameters.parameter("Latitude of natural origin") .setValue(-(16 + 15./60)); // 16°15'00"S 1242 parameters.parameter("Longitude of natural origin").setValue( 179 + 20./60); // 179°20'00"E 1243 parameters.parameter("False easting") .setValue(1251331.8 * LINKS); 1244 parameters.parameter("False northing").setValue(1662888.5 * LINKS); 1245 break; 1246 } 1247 case 19910: { // "JAD69 / Jamaica National Grid" using operation method 9801 1248 parameters = factory.getDefaultParameters("Lambert Conic Conformal (1SP)"); 1249 parameters.parameter("semi_major").setValue(6378206.4); // Clarke 1866 1250 parameters.parameter("semi_minor").setValue(6356583.8); 1251 parameters.parameter("Latitude of natural origin") .setValue( 18.0); 1252 parameters.parameter("Longitude of natural origin") .setValue(-77.0); 1253 parameters.parameter("Scale factor at natural origin").setValue( 1.0); 1254 parameters.parameter("False easting") .setValue(250000.00); 1255 parameters.parameter("False northing").setValue(150000.00); 1256 break; 1257 } 1258 case 14204: { // "NAD27 / Texas South Central" using operation method 9802 1259 parameters = factory.getDefaultParameters("Lambert Conic Conformal (2SP)"); 1260 parameters.parameter("semi_major").setValue(6378206.4); // Clarke 1866 1261 parameters.parameter("semi_minor").setValue(6356583.8); 1262 parameters.parameter("Latitude of 1st standard parallel").setValue(28 + 23.0/60); // 28°23'00"N 1263 parameters.parameter("Latitude of 2nd standard parallel").setValue(30 + 17.0/60); // 30°17'00"N 1264 parameters.parameter("Latitude of false origin") .setValue(27 + 50.0/60); // 27°50'00"N 1265 parameters.parameter("Longitude of false origin") .setValue(-99.0); // 99°00'00"W 1266 parameters.parameter("Easting at false origin") .setValue(2000000 / R_US_FEET); 1267 parameters.parameter("Northing at false origin").setValue( 0 / R_US_FEET); 1268 break; 1269 } 1270 case 6198: { // "Michigan CS27 Central zone" using operation method 1051 1271 parameters = factory.getDefaultParameters("Lambert Conic Conformal (2SP Michigan)"); 1272 parameters.parameter("semi_major").setValue(6378206.4); // Clarke 1866 1273 parameters.parameter("semi_minor").setValue(6356583.8); 1274 parameters.parameter("Latitude of 1st standard parallel").setValue( 44 + 11.0/60); // 44°11' N 1275 parameters.parameter("Latitude of 2nd standard parallel").setValue( 45 + 42.0/60); // 45°42' N 1276 parameters.parameter("Latitude of false origin") .setValue( 43 + 19.0/60); // 43°19' N 1277 parameters.parameter("Longitude of false origin") .setValue(-84 - 20.0/60); // 84°20' W 1278 parameters.parameter("Easting at false origin") .setValue(2000000 / R_US_FEET); 1279 parameters.parameter("Northing at false origin").setValue( 0 / R_US_FEET); 1280 parameters.parameter("Ellipsoid scaling factor").setValue(1.0000382); 1281 break; 1282 } 1283 case 19902: { // "Belge 1972 / Belge Lambert 72" using operation method 9803 1284 parameters = factory.getDefaultParameters("Lambert Conic Conformal (2SP Belgium)"); 1285 parameters.parameter("semi_major").setValue(6378388.0); // International 1924 1286 parameters.parameter("semi_minor").setValue(6378388.0 * (1 - 1/297.0)); 1287 parameters.parameter("Latitude of 1st standard parallel").setValue(49 + 50.0/60); // 49°50'00.000"N 1288 parameters.parameter("Latitude of 2nd standard parallel").setValue(51 + 10.0/60); // 51°10'00.000"N 1289 parameters.parameter("Latitude of false origin") .setValue(90.0); // 90°00'00.000"N 1290 parameters.parameter("Longitude of false origin").setValue(4 + (21 + 24.983/60)/60); // 4°21'24.983"E 1291 parameters.parameter("Easting at false origin") .setValue( 150000.01); 1292 parameters.parameter("Northing at false origin").setValue(5400088.44); 1293 break; 1294 } 1295 case 19986: { // "Europe Equal Area 2001" using operation method 9820 1296 parameters = factory.getDefaultParameters("Lambert Azimuthal Equal Area"); 1297 parameters.parameter("semi_major").setValue(6378137.0); 1298 parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572221)); 1299 parameters.parameter("Latitude of natural origin") .setValue(52.0); 1300 parameters.parameter("Longitude of natural origin").setValue(10.0); 1301 parameters.parameter("False easting") .setValue(4321000.00); 1302 parameters.parameter("False northing").setValue(3210000.00); 1303 break; 1304 } 1305 case 16061: { // "Universal Polar Stereographic North" using operation method 9810 1306 parameters = factory.getDefaultParameters("Polar Stereographic (variant A)"); 1307 parameters.parameter("semi_major").setValue(6378137.0); // WGS84 1308 parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572236)); 1309 parameters.parameter("Latitude of natural origin").setValue(90.0); 1310 parameters.parameter("Longitude of natural origin").setValue(0.0); 1311 parameters.parameter("Scale factor at natural origin").setValue(0.994); 1312 parameters.parameter("False easting") .setValue(2000000.00); 1313 parameters.parameter("False northing").setValue(2000000.00); 1314 break; 1315 } 1316 case 19993: { // "Australian Antarctic Polar Stereographic" using operation method 9829 1317 parameters = factory.getDefaultParameters("Polar Stereographic (variant B)"); 1318 parameters.parameter("semi_major").setValue(6378137.0); // WGS84 1319 parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572236)); 1320 parameters.parameter("Latitude of standard parallel").setValue(-71.0); 1321 parameters.parameter("Longitude of origin").setValue(70.0); 1322 parameters.parameter("False easting") .setValue(6000000.00); 1323 parameters.parameter("False northing").setValue(6000000.00); 1324 break; 1325 } 1326 case 19983: { // "Petrels 1972 / Terre Adelie Polar Stereographic" using operation method 9830 1327 parameters = factory.getDefaultParameters("Polar Stereographic (variant C)"); 1328 parameters.parameter("semi_major").setValue(6378388.0); // International 1924 1329 parameters.parameter("semi_minor").setValue(6378388.0 * (1 - 1/297.0)); 1330 parameters.parameter("Latitude of standard parallel").setValue(-67.0); 1331 parameters.parameter("Longitude of origin").setValue(140.0); 1332 parameters.parameter("Easting at false origin") .setValue(300000.00); 1333 parameters.parameter("Northing at false origin").setValue(200000.00); 1334 break; 1335 } 1336 case 19914: { // "RD New" using operation method 9809 1337 parameters = factory.getDefaultParameters("Oblique Stereographic"); 1338 parameters.parameter("semi_major").setValue(6377397.155); // Bessel 1841 1339 parameters.parameter("semi_minor").setValue(6377397.155 * (1 - 1/299.15281)); 1340 parameters.parameter("Latitude of natural origin").setValue(52 + ( 9 + 22.178/60)/60); // 52°09'22.178"N 1341 parameters.parameter("Longitude of natural origin").setValue(5 + (23 + 15.500/60)/60); // 5°23'15.500"E 1342 parameters.parameter("Scale factor at natural origin").setValue(0.9999079); 1343 parameters.parameter("False easting") .setValue(155000.00); 1344 parameters.parameter("False northing").setValue(463000.00); 1345 break; 1346 } 1347 case 9818: { // (not an official EPSG code) using operation method 9818 1348 parameters = factory.getDefaultParameters("Polyconic"); 1349 parameters.parameter("semi_major").setValue(6378206.4); 1350 parameters.parameter("semi_minor").setValue(6356583.8); 1351 parameters.parameter("Latitude of natural origin") .setValue(0.0); 1352 parameters.parameter("Longitude of natural origin").setValue(0.0); 1353 parameters.parameter("False easting") .setValue(0.0); 1354 parameters.parameter("False northing").setValue(0.0); 1355 break; 1356 } 1357 case 9840: { // (not an official EPSG code) using operation method 9840 1358 parameters = factory.getDefaultParameters("Orthographic"); 1359 parameters.parameter("semi_major").setValue(6378137.0); // WGS84 1360 parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572236)); 1361 parameters.parameter("Latitude of natural origin").setValue(55.0); 1362 parameters.parameter("Longitude of natural origin").setValue(5.0); 1363 parameters.parameter("False easting") .setValue(0.0); 1364 parameters.parameter("False northing").setValue(0.0); 1365 break; 1366 } 1367 case 15399: { // "Guam 1963 / Yap Islands" using operation method 9832 1368 parameters = factory.getDefaultParameters("Modified Azimuthal Equidistant"); 1369 parameters.parameter("semi_major").setValue(6378206.4); // Clarke 1866 1370 parameters.parameter("semi_minor").setValue(6356583.8); 1371 parameters.parameter("Latitude of natural origin").setValue(9 + (32 + 48.15/60)/60); 1372 parameters.parameter("Longitude of natural origin").setValue(138 + (10 + 7.48/60)/60); 1373 parameters.parameter("False easting") .setValue(40000.0); 1374 parameters.parameter("False northing").setValue(60000.0); 1375 break; 1376 } 1377 case 19952: { // "CRS S-JTSK (Ferro) / Krovak" using operation method 9819 1378 parameters = factory.getDefaultParameters("Krovak"); 1379 parameters.parameter("semi_major").setValue(6377397.155); // Bessel 1380 parameters.parameter("semi_minor").setValue(6377397.155 * (1 - 1/299.15281)); 1381 parameters.parameter("Latitude of projection centre").setValue(49.5); // 49°30'00"N 1382 parameters.parameter("Longitude of origin").setValue(24 + 50.0/60); // 24°30'00"E 1383 parameters.parameter("Co-latitude of cone axis").setValue(30 + (17 + 17.3031/60)/60); 1384 parameters.parameter("Latitude of pseudo standard parallel").setValue(78.5); 1385 parameters.parameter("Scale factor on pseudo standard parallel").setValue(0.99990); 1386 break; 1387 } 1388 case 9605: { // (not an official EPSG code) using operation method 9605 1389 parameters = factory.getDefaultParameters("Abridged Molodensky"); 1390 parameters.parameter("dim").setValue(3); // Parameter defined by OGC 01-009 1391 parameters.parameter("src_semi_major").setValue(6378137.0); // WGS84 1392 parameters.parameter("src_semi_minor").setValue(6378137.0 * (1 - 1/298.2572236)); 1393 parameters.parameter("X-axis translation").setValue( 84.87); 1394 parameters.parameter("Y-axis translation").setValue( 96.49); 1395 parameters.parameter("Z-axis translation").setValue(116.95); 1396 parameters.parameter("Semi-major axis length difference").setValue(251); 1397 parameters.parameter("Flattening difference").setValue(1.41927E-05); 1398 break; 1399 } 1400 case 15594: { // EPSG topocentric example A 1401 parameters = factory.getDefaultParameters("Geographic/topocentric conversions"); 1402 parameters.parameter("semi_major").setValue(6378137.0); // WGS84 1403 parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572236)); 1404 parameters.parameter("Latitude of topocentric origin").setValue(55); 1405 parameters.parameter("Longitude of topocentric origin").setValue(5); 1406 parameters.parameter("Ellipsoidal height of topocentric origin").setValue(200); 1407 break; 1408 } 1409 case 15595: { // EPSG topocentric example B 1410 parameters = factory.getDefaultParameters("Geocentric/topocentric conversions"); 1411 parameters.parameter("semi_major").setValue(6378137.0); // WGS84 1412 parameters.parameter("semi_minor").setValue(6378137.0 * (1 - 1/298.2572236)); 1413 parameters.parameter("Geocentric X of topocentric origin").setValue(3652755.3058); 1414 parameters.parameter("Geocentric Y of topocentric origin").setValue( 319574.6799); 1415 parameters.parameter("Geocentric Z of topocentric origin").setValue(5201547.3536); 1416 break; 1417 } 1418 default: { 1419 throw noSuchAuthorityCode(code, String.valueOf(code)); 1420 } 1421 } 1422 return parameters; 1423 } 1424}