0001: /*
0002: * GeoTools - OpenSource mapping toolkit
0003: * http://geotools.org
0004: * (C) 2005-2006, GeoTools Project Managment Committee (PMC)
0005: *
0006: * This library is free software; you can redistribute it and/or
0007: * modify it under the terms of the GNU Lesser General Public
0008: * License as published by the Free Software Foundation;
0009: * version 2.1 of the License.
0010: *
0011: * This library is distributed in the hope that it will be useful,
0012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0014: * Lesser General Public License for more details.
0015: */
0016: package org.geotools.openoffice;
0017:
0018: // J2SE dependencies
0019: import java.text.Format;
0020: import java.text.ParseException;
0021: import java.text.DecimalFormatSymbols;
0022: import java.util.Collection;
0023: import java.util.logging.Level;
0024: import java.util.logging.Logger;
0025: import java.util.logging.LogRecord;
0026:
0027: // OpenOffice dependencies
0028: import com.sun.star.lang.Locale;
0029: import com.sun.star.lang.XSingleServiceFactory;
0030: import com.sun.star.lang.XMultiServiceFactory;
0031: import com.sun.star.lang.IllegalArgumentException;
0032: import com.sun.star.comp.loader.FactoryHelper;
0033: import com.sun.star.registry.XRegistryKey;
0034: import com.sun.star.beans.XPropertySet;
0035: import com.sun.star.uno.AnyConverter;
0036:
0037: // GeoAPI dependencies
0038: import org.opengis.util.InternationalString;
0039: import org.opengis.parameter.ParameterValueGroup;
0040: import org.opengis.parameter.ParameterNotFoundException;
0041: import org.opengis.referencing.ReferenceSystem;
0042: import org.opengis.referencing.IdentifiedObject;
0043: import org.opengis.referencing.FactoryException;
0044: import org.opengis.referencing.datum.Ellipsoid;
0045: import org.opengis.referencing.datum.Datum;
0046: import org.opengis.referencing.datum.GeodeticDatum;
0047: import org.opengis.referencing.cs.CoordinateSystem;
0048: import org.opengis.referencing.crs.CRSAuthorityFactory;
0049: import org.opengis.referencing.crs.CoordinateReferenceSystem;
0050: import org.opengis.referencing.crs.GeneralDerivedCRS;
0051: import org.opengis.referencing.operation.Operation;
0052: import org.opengis.referencing.operation.MathTransform;
0053: import org.opengis.referencing.operation.TransformException;
0054: import org.opengis.referencing.operation.CoordinateOperation;
0055: import org.opengis.referencing.operation.CoordinateOperationFactory;
0056: import org.opengis.geometry.DirectPosition;
0057: import org.opengis.metadata.extent.GeographicBoundingBox;
0058: import org.opengis.metadata.extent.Extent;
0059: import org.opengis.metadata.Identifier;
0060:
0061: // Geotools dependencies
0062: import org.geotools.factory.Hints;
0063: import org.geotools.measure.Angle;
0064: import org.geotools.measure.Latitude;
0065: import org.geotools.measure.Longitude;
0066: import org.geotools.measure.AngleFormat;
0067: import org.geotools.parameter.ParameterGroup;
0068: import org.geotools.referencing.ReferencingFactoryFinder;
0069: import org.geotools.referencing.wkt.Formattable;
0070: import org.geotools.referencing.GeodeticCalculator;
0071: import org.geotools.referencing.factory.AbstractAuthorityFactory;
0072: import org.geotools.referencing.operation.AbstractCoordinateOperation;
0073: import org.geotools.geometry.GeneralDirectPosition;
0074: import org.geotools.metadata.iso.citation.Citations;
0075: import org.geotools.metadata.iso.extent.ExtentImpl;
0076: import org.geotools.resources.i18n.LoggingKeys;
0077: import org.geotools.resources.i18n.Logging;
0078: import org.geotools.resources.i18n.ErrorKeys;
0079: import org.geotools.resources.i18n.Errors;
0080:
0081: /**
0082: * Exports methods from the {@link org.geotools.referencing} package as
0083: * <A HREF="http://www.openoffice.org">OpenOffice</A> add-ins.
0084: *
0085: * @since 2.2
0086: * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/extension/openoffice/src/main/java/org/geotools/openoffice/Referencing.java $
0087: * @version $Id: Referencing.java 25050 2007-04-06 00:41:49Z jgarnett $
0088: * @author Martin Desruisseaux
0089: * @author Richard Deplanque
0090: */
0091: public final class Referencing extends Formulas implements XReferencing {
0092: /**
0093: * The name for the registration of this component.<BR>
0094: * <strong>NOTE:</strong> OpenOffice expects a field with exactly that name; do not rename!
0095: */
0096: private static final String __serviceName = "org.geotools.openoffice.Referencing";
0097:
0098: /**
0099: * The name of the provided service.
0100: */
0101: private static final String ADDIN_SERVICE = "com.sun.star.sheet.AddIn";
0102:
0103: /**
0104: * The authority used in this implementation.
0105: */
0106: private static final String AUTHORITY = "EPSG";
0107:
0108: /**
0109: * The decimal separator. Will be computed when a no locale is set.
0110: */
0111: private char decimalSeparator = '.';
0112:
0113: /**
0114: * The pattern for the {@link #angleFormat}. Used in order to avoid creating
0115: * new formats when the pattern didn't changed.
0116: */
0117: private transient String anglePattern;
0118:
0119: /**
0120: * The format to use for parsing and formatting angles.
0121: * Will be created only when first needed.
0122: */
0123: private transient Format angleFormat;
0124:
0125: /**
0126: * The CRS authority factory. Will be created only when first needed.
0127: */
0128: private transient CRSAuthorityFactory crsFactory;
0129:
0130: /**
0131: * The coordinate operation factory. Will be created only when first needed.
0132: */
0133: private transient CoordinateOperationFactory opFactory;
0134:
0135: /**
0136: * The last coordinate operation used. Cached for performance reasons.
0137: */
0138: private transient CoordinateOperation lastOperation;
0139:
0140: /**
0141: * The last source and target CRS used for fetching {@link #lastOperation}.
0142: */
0143: private transient String lastSourceCRS, lastTargetCRS;
0144:
0145: /**
0146: * The last geodetic calculator used, or {@code null} if none. Cached for better
0147: * performance when many orthodromic distances are computed on the same ellipsoid.
0148: */
0149: private transient GeodeticCalculator calculator;
0150:
0151: /**
0152: * The CRS authority code used for {@link #calculator} setup,
0153: * or {@code null} if not yet defined.
0154: */
0155: private transient String calculatorCRS;
0156:
0157: /**
0158: * Constructs a default implementation of {@code XReferencing} interface.
0159: */
0160: public Referencing() {
0161: methods
0162: .put(
0163: "getValueAngle",
0164: new MethodInfo(
0165: "Text",
0166: "VALUE.ANGLE",
0167: "Converts text in degrees-minutes-seconds to an angle in decimal degrees.",
0168: new String[] {
0169: "xOptions",
0170: "Provided by OpenOffice.",
0171: "text",
0172: "The text to be converted to an angle.",
0173: "pattern",
0174: "The text that describes the format (example: \"D°MM.m'\")." }));
0175: methods
0176: .put(
0177: "getTextAngle",
0178: new MethodInfo(
0179: "Text",
0180: "TEXT.ANGLE",
0181: "Converts an angle to text according to a given format.",
0182: new String[] {
0183: "xOptions",
0184: "Provided by OpenOffice.",
0185: "value",
0186: "The angle value (in decimal degrees) to be converted.",
0187: "pattern",
0188: "The text that describes the format (example: \"D°MM.m'\")." }));
0189: methods
0190: .put(
0191: "getTextLongitude",
0192: new MethodInfo(
0193: "Text",
0194: "TEXT.LONGITUDE",
0195: "Converts a longitude to text according to a given format.",
0196: new String[] {
0197: "xOptions",
0198: "Provided by OpenOffice.",
0199: "value",
0200: "The longitude value (in decimal degrees) to be converted.",
0201: "pattern",
0202: "The text that describes the format (example: \"D°MM.m'\")." }));
0203: methods
0204: .put(
0205: "getTextLatitude",
0206: new MethodInfo(
0207: "Text",
0208: "TEXT.LATITUDE",
0209: "Converts a latitude to text according to a given format.",
0210: new String[] {
0211: "xOptions",
0212: "Provided by OpenOffice.",
0213: "value",
0214: "The latitude value (in decimal degrees) to be converted.",
0215: "pattern",
0216: "The text that describes the format (example: \"D°MM.m'\")." }));
0217: methods
0218: .put(
0219: "getDescription",
0220: new MethodInfo(
0221: "Referencing",
0222: "CRS.DESCRIPTION",
0223: "Returns a description for an object identified by the given authority code.",
0224: new String[] { "xOptions",
0225: "Provided by OpenOffice.",
0226: "code",
0227: "The code allocated by authority." }));
0228: methods.put("getScope", new MethodInfo("Referencing",
0229: "CRS.SCOPE",
0230: "Returns the scope for an identified object.",
0231: new String[] { "xOptions", "Provided by OpenOffice.",
0232: "code", "The code allocated by authority." }));
0233: methods
0234: .put(
0235: "getValidArea",
0236: new MethodInfo(
0237: "Referencing",
0238: "CRS.VALID.AREA",
0239: "Returns the valid area as a textual description for an identified object.",
0240: new String[] { "xOptions",
0241: "Provided by OpenOffice.",
0242: "code",
0243: "The code allocated by authority." }));
0244: methods
0245: .put(
0246: "getBoundingBox",
0247: new MethodInfo(
0248: "Referencing",
0249: "CRS.BOUNDING.BOX",
0250: "Returns the valid area as a geographic bounding box for an identified object.",
0251: new String[] { "xOptions",
0252: "Provided by OpenOffice.",
0253: "code",
0254: "The code allocated by authority." }));
0255: methods.put("getRemarks", new MethodInfo("Referencing",
0256: "CRS.REMARKS",
0257: "Returns the remarks for an identified object.",
0258: new String[] { "xOptions", "Provided by OpenOffice.",
0259: "code", "The code allocated by authority." }));
0260: methods
0261: .put(
0262: "getAxis",
0263: new MethodInfo(
0264: "Referencing",
0265: "CRS.AXIS",
0266: "Returns the axis name for the specified dimension in an identified object.",
0267: new String[] {
0268: "xOptions",
0269: "Provided by OpenOffice.",
0270: "code",
0271: "The code allocated by authority.",
0272: "dimension",
0273: "The dimension (1, 2, ...)." }));
0274: methods
0275: .put(
0276: "getParameter",
0277: new MethodInfo(
0278: "Referencing",
0279: "CRS.PARAMETER",
0280: "Returns the value for a coordinate reference system parameter.",
0281: new String[] {
0282: "xOptions",
0283: "Provided by OpenOffice.",
0284: "code",
0285: "The code allocated by authority.",
0286: "parameter",
0287: "The parameter name (e.g. \"False easting\")." }));
0288: methods
0289: .put(
0290: "getWKT",
0291: new MethodInfo(
0292: "Referencing",
0293: "CRS.WKT",
0294: "Returns the Well Know Text (WKT) for an identified object.",
0295: new String[] {
0296: "xOptions",
0297: "Provided by OpenOffice.",
0298: "code",
0299: "The code allocated by authority.",
0300: "authority",
0301: "The authority name for choice of parameter names." }));
0302: methods
0303: .put(
0304: "getTransformWKT",
0305: new MethodInfo(
0306: "Referencing",
0307: "TRANSFORM.WKT",
0308: "Returns the Well Know Text (WKT) of a transformation between two coordinate reference systems.",
0309: new String[] {
0310: "xOptions",
0311: "Provided by OpenOffice.",
0312: "code",
0313: "The code allocated by authority.",
0314: "authority",
0315: "The authority name for choice of parameter names." }));
0316: methods
0317: .put(
0318: "getAccuracy",
0319: new MethodInfo(
0320: "Referencing",
0321: "TRANSFORM.ACCURACY",
0322: "Returns the accuracy of a transformation between two coordinate reference systems.",
0323: new String[] {
0324: "xOptions",
0325: "Provided by OpenOffice.",
0326: "source CRS",
0327: "The source coordinate reference system.",
0328: "target CRS",
0329: "The target coordinate reference system." }));
0330: methods
0331: .put(
0332: "getTransformedCoordinates",
0333: new MethodInfo(
0334: "Referencing",
0335: "TRANSFORM.COORD",
0336: "Transform coordinates from the given source CRS to the given target CRS.",
0337: new String[] {
0338: "xOptions",
0339: "Provided by OpenOffice.",
0340: "coordinates",
0341: "The coordinate values to transform.",
0342: "source CRS",
0343: "The source coordinate reference system.",
0344: "target CRS",
0345: "The target coordinate reference system." }));
0346: methods
0347: .put(
0348: "getOrthodromicDistance",
0349: new MethodInfo(
0350: "Referencing",
0351: "ORTHODROMIC.DISTANCE",
0352: "Computes the orthodromic distance and azimuth between two coordinates.",
0353: new String[] { "xOptions",
0354: "Provided by OpenOffice.",
0355: "source",
0356: "The source positions.",
0357: "target",
0358: "The target positions.", "CRS",
0359: "Authority code of the coordinate reference system." }));
0360: methods
0361: .put(
0362: "getOrthodromicForward",
0363: new MethodInfo(
0364: "Referencing",
0365: "ORTHODROMIC.FORWARD",
0366: "Computes the coordinates after a displacement of the specified distance.",
0367: new String[] { "xOptions",
0368: "Provided by OpenOffice.",
0369: "source",
0370: "The source positions.",
0371: "displacement",
0372: "The distance and azimuth.",
0373: "CRS",
0374: "Authority code of the coordinate reference system." }));
0375: }
0376:
0377: /**
0378: * Returns a factory for creating the service.
0379: * This method is called by the {@code com.sun.star.comp.loader.JavaLoader}; do not rename!
0380: *
0381: * @param implementation The name of the implementation for which a service is desired.
0382: * @param factories The service manager to be used if needed.
0383: * @param registry The registry key
0384: * @return A factory for creating the component.
0385: */
0386: public static XSingleServiceFactory __getServiceFactory(
0387: final String implementation,
0388: final XMultiServiceFactory factories,
0389: final XRegistryKey registry) {
0390: if (implementation.equals(Referencing.class.getName())) {
0391: return FactoryHelper.getServiceFactory(Referencing.class,
0392: __serviceName, factories, registry);
0393: }
0394: return null;
0395: }
0396:
0397: /**
0398: * Writes the service information into the given registry key.
0399: * This method is called by the {@code com.sun.star.comp.loader.JavaLoader}; do not rename!
0400: *
0401: * @param registry The registry key.
0402: * @return {@code true} if the operation succeeded.
0403: */
0404: public static boolean __writeRegistryServiceInfo(
0405: final XRegistryKey registry) {
0406: final String classname = Referencing.class.getName();
0407: return FactoryHelper.writeRegistryServiceInfo(classname,
0408: __serviceName, registry)
0409: && FactoryHelper.writeRegistryServiceInfo(classname,
0410: ADDIN_SERVICE, registry);
0411: }
0412:
0413: /**
0414: * The service name that can be used to create such an object by a factory.
0415: */
0416: public String getServiceName() {
0417: return __serviceName;
0418: }
0419:
0420: /**
0421: * Provides the supported service names of the implementation, including also
0422: * indirect service names.
0423: *
0424: * @return Sequence of service names that are supported.
0425: */
0426: public String[] getSupportedServiceNames() {
0427: return new String[] { ADDIN_SERVICE, __serviceName };
0428: }
0429:
0430: /**
0431: * Tests whether the specified service is supported, i.e. implemented by the implementation.
0432: *
0433: * @param name Name of service to be tested.
0434: * @return {@code true} if the service is supported, {@code false} otherwise.
0435: */
0436: public boolean supportsService(final String name) {
0437: return name.equals(ADDIN_SERVICE) || name.equals(__serviceName);
0438: }
0439:
0440: /**
0441: * Sets the locale to be used by this object.
0442: */
0443: public void setLocale(final Locale locale) {
0444: anglePattern = null;
0445: angleFormat = null;
0446: super .setLocale(locale);
0447: }
0448:
0449: // --------------------------------------------------------------------------------
0450: // H E L P E R M E T H O D S
0451: // --------------------------------------------------------------------------------
0452:
0453: /**
0454: * Returns the CRS authority factory.
0455: */
0456: private CRSAuthorityFactory crsFactory() {
0457: if (crsFactory == null) {
0458: crsFactory = ReferencingFactoryFinder
0459: .getCRSAuthorityFactory(AUTHORITY, null);
0460: }
0461: return crsFactory;
0462: }
0463:
0464: /**
0465: * Returns the coordinate operation for the two specified CRS.
0466: *
0467: * @param method The method invoking this {@code #getCoordinateOperation} method.
0468: * For logging purpose only.
0469: * @param source The source CRS authority code.
0470: * @param target The target CRS authority code.
0471: * @return The coordinate operation.
0472: * @throws FactoryException if the coordinate operation can't be created.
0473: */
0474: private CoordinateOperation getCoordinateOperation(
0475: final String method, final String source,
0476: final String target) throws FactoryException {
0477: if (lastOperation != null && lastSourceCRS.equals(source)
0478: && lastTargetCRS.equals(target)) {
0479: return lastOperation;
0480: }
0481: final CoordinateReferenceSystem sourceCRS;
0482: final CoordinateReferenceSystem targetCRS;
0483: final CoordinateOperation operation;
0484: final CRSAuthorityFactory crsFactory = crsFactory();
0485: sourceCRS = crsFactory.createCoordinateReferenceSystem(source);
0486: targetCRS = crsFactory.createCoordinateReferenceSystem(target);
0487: if (opFactory == null) {
0488: opFactory = ReferencingFactoryFinder
0489: .getCoordinateOperationFactory(new Hints(
0490: Hints.LENIENT_DATUM_SHIFT, Boolean.TRUE));
0491: }
0492: operation = opFactory.createOperation(sourceCRS, targetCRS);
0493: final Logger logger = getLogger();
0494: if (logger.isLoggable(Level.FINER)) {
0495: final LogRecord record = Logging.format(Level.FINER,
0496: LoggingKeys.CREATED_COORDINATE_OPERATION_$3,
0497: getIdentifier(operation), getIdentifier(sourceCRS),
0498: getIdentifier(targetCRS));
0499: record.setSourceClassName(Referencing.class.getName());
0500: record.setSourceMethodName(method);
0501: logger.log(record);
0502: }
0503: lastSourceCRS = source;
0504: lastTargetCRS = target;
0505: lastOperation = operation;
0506: return operation;
0507: }
0508:
0509: /**
0510: * Returns the identifier for the specified object. Used for logging.
0511: */
0512: private static String getIdentifier(final IdentifiedObject object) {
0513: final Collection identifiers = object.getIdentifiers();
0514: if (identifiers != null && !identifiers.isEmpty()) {
0515: return ((Identifier) identifiers.iterator().next())
0516: .getCode();
0517: }
0518: return object.getName().getCode();
0519: }
0520:
0521: /**
0522: * Returns the Well Know Text (WKT) for the specified object using the parameter names
0523: * from the specified authority.
0524: *
0525: * @param object The object to format.
0526: * @param authority The authority name for choice of parameter names. Usually "OGC".
0527: * @return The Well Know Text (WKT) for the specified object.
0528: * @throws IllegalArgumentException if {@code authority} is not a string value or void.
0529: * @throws UnsupportedOperationException if the object can't be formatted.
0530: */
0531: private static String toWKT(final Object object,
0532: final Object authority) throws IllegalArgumentException,
0533: UnsupportedOperationException {
0534: final String authorityString;
0535: if (AnyConverter.isVoid(authority)) {
0536: authorityString = "OGC";
0537: } else {
0538: authorityString = AnyConverter.toString(authority);
0539: }
0540: if (object instanceof Formattable) {
0541: return ((Formattable) object).toWKT(Citations
0542: .fromName(authorityString), 2);
0543: }
0544: if (object instanceof MathTransform) {
0545: return ((MathTransform) object).toWKT();
0546: }
0547: return ((IdentifiedObject) object).toWKT();
0548: }
0549:
0550: /**
0551: * Returns the geodetic calculator for the specified CRS, datum or ellipsoid.
0552: * This method cache the last calculator used for better performance when many
0553: * orthodromic distances are computed on the same ellipsoid.
0554: *
0555: * @throws IllegalArgumentException if {@code authorityCode} is not a string value or void.
0556: * @throws FactoryException if the geodetic calculator can't be created.
0557: */
0558: private GeodeticCalculator getGeodeticCalculator(
0559: final Object authorityCode)
0560: throws IllegalArgumentException, FactoryException {
0561: final String authorityString;
0562: if (AnyConverter.isVoid(authorityCode)) {
0563: authorityString = "EPSG:4326";
0564: } else {
0565: authorityString = AnyConverter.toString(authorityCode);
0566: }
0567: if (calculatorCRS == null
0568: || !calculatorCRS.equals(authorityString)) {
0569: final IdentifiedObject object = crsFactory().createObject(
0570: authorityString);
0571: if (object instanceof Ellipsoid) {
0572: calculator = new GeodeticCalculator((Ellipsoid) object);
0573: } else if (object instanceof GeodeticDatum) {
0574: calculator = new GeodeticCalculator(
0575: ((GeodeticDatum) object).getEllipsoid());
0576: } else if (object instanceof CoordinateReferenceSystem) {
0577: calculator = new GeodeticCalculator(
0578: (CoordinateReferenceSystem) object);
0579: } else {
0580: throw new FactoryException(
0581: Errors
0582: .format(ErrorKeys.ILLEGAL_COORDINATE_REFERENCE_SYSTEM));
0583: }
0584: calculatorCRS = authorityString;
0585: }
0586: return calculator;
0587: }
0588:
0589: /**
0590: * Returns the angle format to use for the specified pattern.
0591: *
0592: * @param pattern he text that describes the format (example: "D°MM.m'").
0593: * @return The angle format, as a {@link Format} object (instead of {@link AngleFormat}
0594: * in order to avoid too early class loading.
0595: * @throws IllegalArgumentException if {@code pattern} is not a string value or void.
0596: */
0597: private Format getAngleFormat(final Object pattern)
0598: throws IllegalArgumentException {
0599: final String patternString;
0600: if (AnyConverter.isVoid(pattern)) {
0601: patternString = "D°MM'SS.s\"";
0602: } else {
0603: patternString = AnyConverter.toString(pattern);
0604: }
0605: if (angleFormat == null) {
0606: final java.util.Locale locale = getJavaLocale();
0607: angleFormat = new AngleFormat(patternString, locale);
0608: anglePattern = patternString;
0609: decimalSeparator = new DecimalFormatSymbols(locale)
0610: .getDecimalSeparator();
0611: } else if (!patternString.equals(anglePattern)) {
0612: ((AngleFormat) angleFormat).applyPattern(patternString);
0613: anglePattern = patternString;
0614: }
0615: return angleFormat;
0616: }
0617:
0618: // --------------------------------------------------------------------------------
0619: // F O R M U L A I M P L E M E N T A T I O N S
0620: // --------------------------------------------------------------------------------
0621:
0622: /**
0623: * {@inheritDoc}
0624: */
0625: public double getValueAngle(final XPropertySet xOptions,
0626: final String text, final Object pattern)
0627: throws IllegalArgumentException {
0628: final AngleFormat angleFormat = (AngleFormat) getAngleFormat(pattern);
0629: try {
0630: return angleFormat.parse(text).degrees();
0631: } catch (ParseException exception) {
0632: /*
0633: * Parse failed. Tries to replace the dot by the decimal separator in current locale.
0634: */
0635: final String localized = text
0636: .replace('.', decimalSeparator);
0637: if (localized != text)
0638: try {
0639: return angleFormat.parse(localized).degrees();
0640: } catch (ParseException ignore) {
0641: // Ignore; will throw the first exception.
0642: }
0643: throw new IllegalArgumentException(
0644: getLocalizedMessage(exception));
0645: }
0646: }
0647:
0648: /**
0649: * {@inheritDoc}
0650: */
0651: public String getTextAngle(final XPropertySet xOptions,
0652: final double value, final Object pattern)
0653: throws IllegalArgumentException {
0654: return getAngleFormat(pattern).format(new Angle(value));
0655: }
0656:
0657: /**
0658: * {@inheritDoc}
0659: */
0660: public String getTextLongitude(final XPropertySet xOptions,
0661: final double value, final Object pattern)
0662: throws IllegalArgumentException {
0663: return getAngleFormat(pattern).format(new Longitude(value));
0664: }
0665:
0666: /**
0667: * {@inheritDoc}
0668: */
0669: public String getTextLatitude(final XPropertySet xOptions,
0670: final double value, final Object pattern)
0671: throws IllegalArgumentException {
0672: return getAngleFormat(pattern).format(new Latitude(value));
0673: }
0674:
0675: /**
0676: * {@inheritDoc}
0677: */
0678: public String getDescription(final XPropertySet xOptions,
0679: final String authorityCode) {
0680: final InternationalString description;
0681: try {
0682: description = crsFactory()
0683: .getDescriptionText(authorityCode);
0684: } catch (Exception exception) {
0685: return getLocalizedMessage(exception);
0686: }
0687: return (description != null) ? description
0688: .toString(getJavaLocale()) : emptyString();
0689: }
0690:
0691: /**
0692: * {@inheritDoc}
0693: */
0694: public String getScope(final XPropertySet xOptions,
0695: final String authorityCode) {
0696: final IdentifiedObject object;
0697: try {
0698: object = crsFactory().createObject(authorityCode);
0699: } catch (Exception exception) {
0700: return getLocalizedMessage(exception);
0701: }
0702: final InternationalString description;
0703: if (object instanceof Datum) {
0704: description = ((Datum) object).getScope();
0705: } else if (object instanceof ReferenceSystem) {
0706: description = ((ReferenceSystem) object).getScope();
0707: } else if (object instanceof CoordinateOperation) {
0708: description = ((CoordinateOperation) object).getScope();
0709: } else {
0710: description = null;
0711: }
0712: return (description != null) ? description
0713: .toString(getJavaLocale()) : emptyString();
0714: }
0715:
0716: /**
0717: * {@inheritDoc}
0718: */
0719: public String getValidArea(final XPropertySet xOptions,
0720: final String authorityCode) {
0721: Extent validArea;
0722: try {
0723: validArea = crsFactory().createCoordinateReferenceSystem(
0724: authorityCode).getValidArea();
0725: } catch (Exception exception) {
0726: try {
0727: validArea = ReferencingFactoryFinder
0728: .getCoordinateOperationAuthorityFactory(
0729: AUTHORITY, null)
0730: .createCoordinateOperation(authorityCode)
0731: .getValidArea();
0732: } catch (Exception ignore) {
0733: return getLocalizedMessage(exception);
0734: }
0735: }
0736: if (validArea != null) {
0737: final InternationalString description = validArea
0738: .getDescription();
0739: if (description != null) {
0740: return description.toString(getJavaLocale());
0741: }
0742: final GeographicBoundingBox box = ExtentImpl
0743: .getGeographicBoundingBox(validArea);
0744: if (box != null) {
0745: return box.toString();
0746: }
0747: }
0748: return emptyString();
0749: }
0750:
0751: /**
0752: * {@inheritDoc}
0753: */
0754: public double[][] getBoundingBox(final XPropertySet xOptions,
0755: final String authorityCode) {
0756: Extent validArea;
0757: try {
0758: validArea = crsFactory().createCoordinateReferenceSystem(
0759: authorityCode).getValidArea();
0760: } catch (Exception exception) {
0761: try {
0762: validArea = ReferencingFactoryFinder
0763: .getCoordinateOperationAuthorityFactory(
0764: AUTHORITY, null)
0765: .createCoordinateOperation(authorityCode)
0766: .getValidArea();
0767: } catch (Exception ignore) {
0768: reportException("getBoundingBox", exception);
0769: return getFailure(4, 4);
0770: }
0771: }
0772: final GeographicBoundingBox box = ExtentImpl
0773: .getGeographicBoundingBox(validArea);
0774: if (box == null) {
0775: return getFailure(4, 4);
0776: }
0777: return new double[][] {
0778: new double[] { box.getNorthBoundLatitude(),
0779: box.getWestBoundLongitude() },
0780: new double[] { box.getSouthBoundLatitude(),
0781: box.getEastBoundLongitude() } };
0782: }
0783:
0784: /**
0785: * {@inheritDoc}
0786: */
0787: public String getRemarks(final XPropertySet xOptions,
0788: final String authorityCode) {
0789: final IdentifiedObject object;
0790: try {
0791: object = crsFactory().createObject(authorityCode);
0792: } catch (Exception exception) {
0793: return getLocalizedMessage(exception);
0794: }
0795: final InternationalString remarks = object.getRemarks();
0796: return (remarks != null) ? remarks.toString(getJavaLocale())
0797: : emptyString();
0798: }
0799:
0800: /**
0801: * {@inheritDoc}
0802: */
0803: public String getAxis(final XPropertySet xOptions,
0804: final String authorityCode, final int dimension) {
0805: CoordinateSystem cs;
0806: try {
0807: cs = crsFactory().createCoordinateReferenceSystem(
0808: authorityCode).getCoordinateSystem();
0809: } catch (Exception exception) {
0810: try {
0811: cs = ReferencingFactoryFinder.getCSAuthorityFactory(
0812: AUTHORITY, null).createCoordinateSystem(
0813: authorityCode);
0814: } catch (Exception ignore) {
0815: // Ignore - we will report the previous exception instead.
0816: }
0817: return getLocalizedMessage(exception);
0818: }
0819: if (dimension >= 1 && dimension <= cs.getDimension()) {
0820: return cs.getAxis(dimension - 1).getName().getCode();
0821: } else {
0822: return Errors.format(ErrorKeys.INDEX_OUT_OF_BOUNDS_$1,
0823: new Integer(dimension));
0824: }
0825: }
0826:
0827: /**
0828: * {@inheritDoc}
0829: */
0830: public Object getParameter(final XPropertySet xOptions,
0831: final String authorityCode, final String parameter) {
0832: final IdentifiedObject object;
0833: try {
0834: object = crsFactory().createObject(authorityCode);
0835: } catch (FactoryException exception) {
0836: return getLocalizedMessage(exception);
0837: }
0838: final ParameterValueGroup parameters;
0839: if (object instanceof Operation) {
0840: parameters = ((Operation) object).getParameterValues();
0841: } else if (object instanceof GeneralDerivedCRS) {
0842: parameters = ((GeneralDerivedCRS) object)
0843: .getConversionFromBase().getParameterValues();
0844: } else {
0845: parameters = ParameterGroup.EMPTY;
0846: }
0847: try {
0848: return parameters.parameter(parameter).getValue();
0849: } catch (ParameterNotFoundException exception) {
0850: return Errors.format(ErrorKeys.UNKNOW_PARAMETER_$1,
0851: parameter);
0852: } catch (RuntimeException exception) {
0853: return getLocalizedMessage(exception);
0854: }
0855: }
0856:
0857: /**
0858: * {@inheritDoc}
0859: */
0860: public String getWKT(final XPropertySet xOptions,
0861: final String authorityCode, final Object authority) {
0862: try {
0863: return toWKT(crsFactory().createObject(authorityCode),
0864: authority);
0865: } catch (Exception exception) {
0866: return getLocalizedMessage(exception);
0867: }
0868: }
0869:
0870: /**
0871: * {@inheritDoc}
0872: */
0873: public String getTransformWKT(final XPropertySet xOptions,
0874: final String sourceCRS, final String targetCRS,
0875: final Object authority) {
0876: try {
0877: return toWKT(getCoordinateOperation("getTransformWKT",
0878: sourceCRS, targetCRS).getMathTransform(), authority);
0879: } catch (Exception exception) {
0880: return getLocalizedMessage(exception);
0881: }
0882: }
0883:
0884: /**
0885: * {@inheritDoc}
0886: */
0887: public double getAccuracy(final XPropertySet xOptions,
0888: final String sourceCRS, final String targetCRS) {
0889: final CoordinateOperation operation;
0890: try {
0891: operation = getCoordinateOperation("getAccuracy",
0892: sourceCRS, targetCRS);
0893: } catch (FactoryException exception) {
0894: reportException("getAccuracy", exception);
0895: return Double.NaN;
0896: }
0897: return AbstractCoordinateOperation.getAccuracy(operation);
0898: }
0899:
0900: /**
0901: * {@inheritDoc}
0902: */
0903: public double[][] getTransformedCoordinates(
0904: final XPropertySet xOptions, final double[][] coordinates,
0905: final String sourceCRS, final String targetCRS) {
0906: final CoordinateOperation operation;
0907: try {
0908: operation = getCoordinateOperation(
0909: "getTransformedCoordinates", sourceCRS, targetCRS);
0910: } catch (FactoryException exception) {
0911: reportException("getTransformedCoordinates", exception);
0912: return getFailure(coordinates.length, 2);
0913: }
0914: /*
0915: * We now have every information needed for applying the coordinate operations.
0916: * Creates a result array and transform every point.
0917: */
0918: boolean failureReported = false;
0919: final MathTransform mt = operation.getMathTransform();
0920: final GeneralDirectPosition sourcePt = new GeneralDirectPosition(
0921: mt.getSourceDimensions());
0922: final GeneralDirectPosition targetPt = new GeneralDirectPosition(
0923: mt.getTargetDimensions());
0924: final double[][] result = new double[coordinates.length][];
0925: for (int j = 0; j < coordinates.length; j++) {
0926: double[] coords = coordinates[j];
0927: if (coords == null) {
0928: continue;
0929: }
0930: for (int i = sourcePt.ordinates.length; --i >= 0;) {
0931: sourcePt.ordinates[i] = (i < coords.length) ? coords[i]
0932: : 0;
0933: }
0934: final DirectPosition pt;
0935: try {
0936: pt = mt.transform(sourcePt, targetPt);
0937: } catch (TransformException exception) {
0938: /*
0939: * The coordinate operation failed for this particular point. But maybe it will
0940: * succeed for an other point. Set the values to NaN and continue the loop. Note:
0941: * we will report the failure for logging purpose, but only the first one since
0942: * all subsequent failures are likely to be the same one.
0943: */
0944: if (!failureReported) {
0945: reportException("getTransformedCoordinates",
0946: exception);
0947: failureReported = true;
0948: }
0949: continue;
0950: }
0951: coords = new double[pt.getDimension()];
0952: for (int i = coords.length; --i >= 0;) {
0953: coords[i] = pt.getOrdinate(i);
0954: }
0955: result[j] = coords;
0956: }
0957: return result;
0958: }
0959:
0960: /**
0961: * {@inheritDoc}
0962: */
0963: public double[][] getOrthodromicDistance(
0964: final XPropertySet xOptions, final double[][] source,
0965: final double[][] target, final Object CRS) {
0966: final GeodeticCalculator calculator;
0967: try {
0968: calculator = getGeodeticCalculator(CRS);
0969: } catch (Exception exception) {
0970: reportException("getOrthodromicDistance", exception);
0971: return getFailure(source.length, 2);
0972: }
0973: boolean failureReported = false;
0974: final int dim = calculator.getCoordinateReferenceSystem()
0975: .getCoordinateSystem().getDimension();
0976: final GeneralDirectPosition sourcePt = new GeneralDirectPosition(
0977: dim);
0978: final GeneralDirectPosition targetPt = new GeneralDirectPosition(
0979: dim);
0980: final double[][] result = new double[getLength(source, target)][];
0981: for (int j = 0; j < result.length; j++) {
0982: final double[] src = source[j % source.length];
0983: final double[] dst = target[j % target.length];
0984: if (src == null || dst == null) {
0985: continue;
0986: }
0987: for (int i = dim; --i >= 0;) {
0988: sourcePt.ordinates[i] = (i < src.length) ? src[i] : 0;
0989: targetPt.ordinates[i] = (i < dst.length) ? dst[i] : 0;
0990: }
0991: try {
0992: calculator.setStartingPosition(sourcePt);
0993: calculator.setDestinationPosition(targetPt);
0994: } catch (TransformException exception) {
0995: if (!failureReported) {
0996: reportException("getOrthodromicDistance", exception);
0997: failureReported = true;
0998: }
0999: continue;
1000: }
1001: result[j] = new double[] {
1002: calculator.getOrthodromicDistance(),
1003: calculator.getAzimuth() };
1004: }
1005: return result;
1006: }
1007:
1008: /**
1009: * {@inheritDoc}
1010: */
1011: public double[][] getOrthodromicForward(
1012: final XPropertySet xOptions, final double[][] source,
1013: final double[][] displacement, final Object CRS) {
1014: final GeodeticCalculator calculator;
1015: try {
1016: calculator = getGeodeticCalculator(CRS);
1017: } catch (Exception exception) {
1018: reportException("getOrthodromicForward", exception);
1019: return getFailure(source.length, 2);
1020: }
1021: boolean failureReported = false;
1022: final int dim = calculator.getCoordinateReferenceSystem()
1023: .getCoordinateSystem().getDimension();
1024: final GeneralDirectPosition sourcePt = new GeneralDirectPosition(
1025: dim);
1026: final double[][] result = new double[getLength(source,
1027: displacement)][];
1028: for (int j = 0; j < result.length; j++) {
1029: final double[] src = source[j % source.length];
1030: final double[] mov = displacement[j % displacement.length];
1031: if (src == null || mov == null) {
1032: continue;
1033: }
1034: for (int i = dim; --i >= 0;) {
1035: sourcePt.ordinates[i] = (i < src.length) ? src[i] : 0;
1036: }
1037: double distance = 0, azimuth = 0;
1038: switch (mov.length) {
1039: default: // Fall through
1040: case 2:
1041: azimuth = mov[1]; // Fall through
1042: case 1:
1043: distance = mov[0]; // Fall through
1044: case 0:
1045: break;
1046: }
1047: final DirectPosition targetPt;
1048: try {
1049: calculator.setStartingPosition(sourcePt);
1050: calculator.setDirection(azimuth, distance);
1051: targetPt = calculator.getDestinationPosition();
1052: } catch (TransformException exception) {
1053: if (!failureReported) {
1054: reportException("getOrthodromicForward", exception);
1055: failureReported = true;
1056: }
1057: continue;
1058: }
1059: result[j] = targetPt.getCoordinates();
1060: }
1061: return result;
1062: }
1063:
1064: /**
1065: * Release resources used by this implementation.
1066: */
1067: protected void finalize() throws Throwable {
1068: if (crsFactory instanceof AbstractAuthorityFactory)
1069: try {
1070: ((AbstractAuthorityFactory) crsFactory).dispose();
1071: crsFactory = null;
1072: } catch (Exception exception) {
1073: // Ignore, since we are probably shuting down.
1074: }
1075: super.finalize();
1076: }
1077: }
|