/* * GeoTools - The Open Source Java GIS Toolkit * http://geotools.org * * (C) 2005-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.referencing.operation.projection; import static java.lang.Math.abs; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import javax.measure.unit.NonSI; import org.geotools.metadata.iso.citation.Citations; import org.geotools.parameter.ParameterGroup; import org.geotools.referencing.NamedIdentifier; import org.geotools.referencing.operation.projection.MapProjection.AbstractProvider; import org.geotools.resources.i18n.Vocabulary; import org.geotools.resources.i18n.VocabularyKeys; import org.opengis.parameter.GeneralParameterDescriptor; import org.opengis.parameter.GeneralParameterValue; import org.opengis.parameter.ParameterDescriptor; import org.opengis.parameter.ParameterDescriptorGroup; import org.opengis.parameter.ParameterNotFoundException; import org.opengis.parameter.ParameterValueGroup; import org.opengis.referencing.operation.CylindricalProjection; import org.opengis.referencing.operation.MathTransform; /** * Supports the popular visualisation projection used by Google, Microsoft, Yahoo, OSM and others * @author Andrea Aime - OpenGeo * * * * @source $URL$ */ public class MercatorPseudoProvider extends AbstractProvider { /** * For cross-version compatibility. */ private static final long serialVersionUID = 118002069939741891L; /** * The Auxiliary Sphere Type parameter accepts: * <ul> * <li>0 (use semimajor axis or radius of the geographic coordinate system)<li> * <li>1 (use semiminor axis or radius) </li> * <li>2 (calculate and use authalic radius), or 3 (use authalic radius and convert geodetic latitudes to authalic latitudes).</li> * </ul> * * Geotools only supports 0 for the moment */ public static final ParameterDescriptor AUXILIARY_SPHERE_TYPE = createDescriptor( new NamedIdentifier[] { new NamedIdentifier(Citations.ESRI, "Auxiliary_sphere_type"), }, 0, 0, 0, null); public static final ParameterDescriptor FAKE_ESRI_STANDARD_PARALLELL = createDescriptor( new NamedIdentifier[] { new NamedIdentifier(Citations.ESRI, "Standard_parallel_1"), }, 0, 0, 0, NonSI.DEGREE_ANGLE); /** * The parameters group. */ static final ParameterDescriptorGroup PARAMETERS = createDescriptorGroup(new NamedIdentifier[] { new NamedIdentifier(Citations.EPSG, "Popular Visualisation Pseudo Mercator"), new NamedIdentifier (Citations.EPSG, "1024"), new NamedIdentifier(Citations.ESRI, "Mercator_Auxiliary_Sphere"), new NamedIdentifier(Citations.GEOTOOLS, Vocabulary.formatInternational( VocabularyKeys.CYLINDRICAL_MERCATOR_PROJECTION)) }, new ParameterDescriptor[] { SEMI_MAJOR, SEMI_MINOR, LATITUDE_OF_ORIGIN, CENTRAL_MERIDIAN, SCALE_FACTOR, FALSE_EASTING, FALSE_NORTHING, // these two added for ESRI compatibility FAKE_ESRI_STANDARD_PARALLELL, AUXILIARY_SPHERE_TYPE }); static final ParameterDescriptorGroup BASE_PARAMETERS = createDescriptorGroup(new NamedIdentifier[] { new NamedIdentifier(Citations.EPSG, "Popular Visualisation Pseudo Mercator"), new NamedIdentifier (Citations.EPSG, "1024"), new NamedIdentifier(Citations.GEOTOOLS, Vocabulary.formatInternational( VocabularyKeys.CYLINDRICAL_MERCATOR_PROJECTION)) }, new ParameterDescriptor[] { SEMI_MAJOR, SEMI_MINOR, LATITUDE_OF_ORIGIN, CENTRAL_MERIDIAN, SCALE_FACTOR, FALSE_EASTING, FALSE_NORTHING, }); /** * Constructs a new provider. */ public MercatorPseudoProvider() { super(PARAMETERS); } /** * Returns the operation type for this map projection. */ @Override public Class<CylindricalProjection> getOperationType() { return CylindricalProjection.class; } /** * Creates a transform from the specified group of parameter values. * * @param parameters The group of parameter values. * @return The created math transform. * @throws ParameterNotFoundException if a required parameter was not found. */ protected MathTransform createMathTransform(final ParameterValueGroup parameters) throws ParameterNotFoundException { // make sure we assume a spherical reference parameters.parameter("semi_minor").setValue(parameters.parameter("semi_major").getValue()); return new Spherical(parameters); } /** * Just like the {@link Mercator1SP.Spherical} but returning the proper parameter for the * pseudo mercartor case */ private static final class Spherical extends Mercator.Spherical { /** * For cross-version compatibility. */ private static final long serialVersionUID = -7583892502939355783L; /** * Constructs a new map projection from the suplied parameters. * * @param parameters The parameter values in standard units. * @throws ParameterNotFoundException if a mandatory parameter is missing. */ protected Spherical(final ParameterValueGroup parameters) throws ParameterNotFoundException { super(parameters); } /** * Override to discard ESRI extra parameters, they are adding un-welcomed shift in the * reprojection results */ @Override public ParameterValueGroup getParameterValues() { final ParameterDescriptorGroup descriptor = BASE_PARAMETERS; final Collection<GeneralParameterDescriptor> expected = descriptor.descriptors(); final ParameterValueGroup values = descriptor.createValue(); set(expected, AbstractProvider.SEMI_MAJOR, values, semiMajor ); set(expected, AbstractProvider.SEMI_MINOR, values, semiMinor ); set(expected, AbstractProvider.CENTRAL_MERIDIAN, values, centralMeridian ); set(expected, AbstractProvider.LATITUDE_OF_ORIGIN, values, latitudeOfOrigin); set(expected, AbstractProvider.SCALE_FACTOR, values, scaleFactor ); set(expected, AbstractProvider.FALSE_EASTING, values, falseEasting ); set(expected, AbstractProvider.FALSE_NORTHING, values, falseNorthing ); if (!Double.isNaN(standardParallel)) { set(expected, AbstractProvider.STANDARD_PARALLEL_1, values, standardParallel); } return values; } /** * {@inheritDoc} */ public ParameterDescriptorGroup getParameterDescriptors() { return MercatorPseudoProvider.PARAMETERS; } @Override protected double getToleranceForAssertions(double longitude, double latitude) { final double delta = abs(longitude - centralMeridian) / 2 + abs(latitude - latitudeOfOrigin); if (delta > 40) { // When far from the valid area, use a larger tolerance. return 1; } else { // this projection forte is not exactly precision return 0.1; } } } }