/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2004-2008, Open Source Geospatial Foundation (OSGeo) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; * version 2.1 of the License. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. */ package org.geotools.resources; import java.util.Locale; import java.text.FieldPosition; import org.opengis.metadata.extent.GeographicBoundingBox; import org.opengis.referencing.FactoryException; import org.opengis.referencing.crs.GeographicCRS; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.CoordinateOperation; import org.opengis.referencing.operation.CoordinateOperationFactory; import org.opengis.referencing.operation.TransformException; import org.opengis.geometry.Envelope; import org.geotools.factory.Hints; import org.geotools.measure.Latitude; import org.geotools.measure.Longitude; import org.geotools.measure.AngleFormat; import org.geotools.metadata.iso.extent.GeographicBoundingBoxImpl; import org.geotools.referencing.CRS; import org.geotools.referencing.ReferencingFactoryFinder; import org.geotools.referencing.crs.DefaultGeographicCRS; import org.geotools.referencing.operation.TransformPathNotFoundException; import org.geotools.resources.i18n.Errors; import org.geotools.resources.i18n.ErrorKeys; /** * Provides convenience methods for {@linkplain GeographicBoundingBox geographic bounding boxes}. * This is mostly a helper class for {@link GeographicBoundingBoxImpl}; users should not use this * class directly. * * @since 2.4 * * @source $URL$ * @version $Id$ * @author Martin Desruisseaux (IRD) * @author Touraïvane */ public final class BoundingBoxes { /** * A set of hints used in order to fetch lenient coordinate operation factory. We accept * lenient transforms because {@link GeographicBoundingBox} are usually for approximative * bounds (e.g. the area of validity of some CRS). If a user wants accurate bounds, he * should probably use an {@link Envelope} with the appropriate CRS. */ private static final Hints LENIENT = new Hints(Hints.LENIENT_DATUM_SHIFT, Boolean.TRUE); /** * Prevents the creation of instances of this class. */ private BoundingBoxes() { } /** * Initializes a geographic bounding box from the specified envelope. If the envelope contains * a CRS, then the bounding box will be projected to a geographic CRS. Otherwise, the envelope * is assumed already in appropriate CRS. * * @param envelope The source envelope. * @param box The target bounding box. */ public static void copy(Envelope envelope, final GeographicBoundingBoxImpl box) throws TransformException { final CoordinateReferenceSystem crs = envelope.getCoordinateReferenceSystem(); if (crs != null) { final GeographicCRS standardCRS = CRSUtilities.getStandardGeographicCRS2D(crs); if (!startsWith(crs, standardCRS) && !startsWith(crs, DefaultGeographicCRS.WGS84) && !startsWith(crs, DefaultGeographicCRS.WGS84_3D)) { final CoordinateOperation operation; final CoordinateOperationFactory factory; factory = ReferencingFactoryFinder.getCoordinateOperationFactory(LENIENT); try { operation = factory.createOperation(crs, standardCRS); } catch (FactoryException exception) { throw new TransformPathNotFoundException(Errors.format( ErrorKeys.CANT_TRANSFORM_ENVELOPE, exception)); } envelope = CRS.transform(operation, envelope); } } box.setWestBoundLongitude(envelope.getMinimum(0)); box.setEastBoundLongitude(envelope.getMaximum(0)); box.setSouthBoundLatitude(envelope.getMinimum(1)); box.setNorthBoundLatitude(envelope.getMaximum(1)); } /** * Returns {@code true} if the specified {@code crs} starts with the specified {@code head}. */ private static final boolean startsWith(final CoordinateReferenceSystem crs, final CoordinateReferenceSystem head) { final int dimension = head.getCoordinateSystem().getDimension(); return crs.getCoordinateSystem().getDimension() >= dimension && CRS.equalsIgnoreMetadata(CRSUtilities.getSubCRS(crs, 0, dimension), head); } /** * Returns a string representation of the specified extent using the specified angle * pattern and locale. See {@link AngleFormat} for a description of angle patterns. * * @param box The bounding box to format. * @param pattern The angle pattern (e.g. {@code DD°MM'SS.s"}. * @param locale The locale, or {@code null} for the default one. */ public static String toString(final GeographicBoundingBox box, final String pattern, final Locale locale) { final AngleFormat format; format = (locale!=null) ? new AngleFormat(pattern, locale) : new AngleFormat(pattern); final FieldPosition pos = new FieldPosition(0); final StringBuffer buffer = new StringBuffer(); format.format(new Latitude(box.getNorthBoundLatitude()), buffer, pos).append(", "); format.format(new Longitude(box.getWestBoundLongitude()), buffer, pos).append(" - "); format.format(new Latitude(box.getSouthBoundLatitude()), buffer, pos).append(", "); format.format(new Longitude(box.getEastBoundLongitude()), buffer, pos); return buffer.toString(); } }