    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_testing/src/org/deegree/crs/configuration/deegree/CRSParser.java $
    002    /*----------------------------------------------------------------------------
    003     This file is part of deegree, http://deegree.org/
    004     Copyright (C) 2001-2009 by:
    005     Department of Geography, University of Bonn
    006     and
    007     lat/lon GmbH
    008
    009     This library is free software; you can redistribute it and/or modify it under
    010     the terms of the GNU Lesser General Public License as published by the Free
    011     Software Foundation; either version 2.1 of the License, or (at your option)
    012     any later version.
    013     This library is distributed in the hope that it will be useful, but WITHOUT
    014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
    015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
    016     details.
    017     You should have received a copy of the GNU Lesser General Public License
    018     along with this library; if not, write to the Free Software Foundation, Inc.,
    019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
    020
    021     Contact information:
    022
    023     lat/lon GmbH
    024     Aennchenstr. 19, 53177 Bonn
    025     Germany
    026     http://lat-lon.de/
    027
    028     Department of Geography, University of Bonn
    029     Prof. Dr. Klaus Greve
    030     Postfach 1147, 53001 Bonn
    031     Germany
    032     http://www.geographie.uni-bonn.de/deegree/
    033
    034     e-mail: info@deegree.org
    035     ----------------------------------------------------------------------------*/
    036
    037    package org.deegree.crs.configuration.deegree;
    038
    039    import java.io.IOException;
    040    import java.lang.reflect.Constructor;
    041    import java.lang.reflect.InvocationTargetException;
    042    import java.util.LinkedList;
    043    import java.util.List;
    044    import java.util.Map;
    045    import java.util.Properties;
    046
    047    import javax.vecmath.Point2d;
    048
    049    import org.deegree.crs.Identifiable;
    050    import org.deegree.crs.components.Axis;
    051    import org.deegree.crs.components.Ellipsoid;
    052    import org.deegree.crs.components.GeodeticDatum;
    053    import org.deegree.crs.components.PrimeMeridian;
    054    import org.deegree.crs.components.Unit;
    055    import org.deegree.crs.configuration.resources.XMLFileResource;
    056    import org.deegree.crs.coordinatesystems.CompoundCRS;
    057    import org.deegree.crs.coordinatesystems.CoordinateSystem;
    058    import org.deegree.crs.coordinatesystems.GeocentricCRS;
    059    import org.deegree.crs.coordinatesystems.GeographicCRS;
    060    import org.deegree.crs.coordinatesystems.ProjectedCRS;
    061    import org.deegree.crs.exceptions.CRSConfigurationException;
    062    import org.deegree.crs.projections.Projection;
    063    import org.deegree.crs.projections.azimuthal.LambertAzimuthalEqualArea;
    064    import org.deegree.crs.projections.azimuthal.StereographicAlternative;
    065    import org.deegree.crs.projections.azimuthal.StereographicAzimuthal;
    066    import org.deegree.crs.projections.conic.LambertConformalConic;
    067    import org.deegree.crs.projections.cylindric.Mercator;
    068    import org.deegree.crs.projections.cylindric.TransverseMercator;
    069    import org.deegree.crs.transformations.Transformation;
    070    import org.deegree.crs.transformations.helmert.Helmert;
    071    import org.deegree.crs.transformations.polynomial.LeastSquareApproximation;
    072    import org.deegree.crs.transformations.polynomial.PolynomialTransformation;
    073    import org.deegree.framework.log.ILogger;
    074    import org.deegree.framework.log.LoggerFactory;
    075    import org.deegree.framework.xml.XMLParsingException;
    076    import org.deegree.framework.xml.XMLTools;
    077    import org.deegree.i18n.Messages;
    078    import org.deegree.ogcbase.CommonNamespaces;
    079    import org.w3c.dom.Element;
    080
    081    /**
    082     * The <code>CRSParser</code> class TODO add class documentation here.
    083     *
    084     * @author <a href="mailto:bezema@lat-lon.de">Rutger Bezema</a>
    085     *
    086     * @author last edited by: $Author: mschneider $
    087     *
    088     * @version $Revision: 20382 $, $Date: 2009-10-27 12:27:31 +0100 (Di, 27. Okt 2009) $
    089     *
    090     */
    091    public class CRSParser extends XMLFileResource {
    092
    093        /**
    094         *
    095         */
    096        private static final long serialVersionUID = -5621078575657838568L;
    097
    098        private static ILogger LOG = LoggerFactory.getLogger( CRSParser.class );
    099
    100        /**
    101         * The prefix to use.
    102         */
    103        private final static String PRE = CommonNamespaces.CRS_PREFIX + ":";
    104
    105        private final static String XPATH_PRE = "*[" + PRE + "id='";
    106
    107        /**
    108         * @param provider
    109         *            to be used for callback.
    110         * @param rootElement
    111         *            to be used for the configuration.
    112         */
    113        public CRSParser( DeegreeCRSProvider provider, Element rootElement ) {
    114            super( provider, rootElement );
    115        }
    116
    117        /**
    118         * @param provider
    119         * @param properties
    120         */
    121        public CRSParser( DeegreeCRSProvider provider, Properties properties ) {
    122            this( provider, properties, "definitions", CommonNamespaces.CRSNS.toASCIIString() );
    123            // TODO Auto-generated constructor stub
    124        }
    125
    126        /**
    127         * @param provider
    128         * @param properties
    129         * @param defaultRootElement
    130         * @param namespace
    131         */
    132        public CRSParser( DeegreeCRSProvider provider, Properties properties, String defaultRootElement, String namespace ) {
    133            super( provider, properties, defaultRootElement, namespace );
    134        }
    135
    136        /**
    137         * @param crsDefintion
    138         *            to be parsed
    139         * @return an instance of the given crs or <code>null</code> if the crsDefinition is <code>null</code> or could
    140         *         not be mapped to a valid type.
    141         * @throws CRSConfigurationException
    142         *             if something went wrong while constructing the crs.
    143         */
    144        public CoordinateSystem parseCoordinateSystem( Element crsDefintion )
    145                                throws CRSConfigurationException {
    146            if ( crsDefintion == null ) {
    147                return null;
    148            }
    149            String crsType = crsDefintion.getLocalName();
    150
    151            CoordinateSystem result = null;
    152            if ( "geographicCRS".equalsIgnoreCase( crsType ) ) {
    153                result = parseGeographicCRS( crsDefintion );
    154            } else if ( "projectedCRS".equalsIgnoreCase( crsType ) ) {
    155                result = parseProjectedCRS( crsDefintion );
    156            } else if ( "geocentricCRS".equalsIgnoreCase( crsType ) ) {
    157                result = parseGeocentricCRS( crsDefintion );
    158            } else if ( "compoundCRS".equalsIgnoreCase( crsType ) ) {
    159                result = parseCompoundCRS( crsDefintion );
    160            }
    161
    162            if ( result == null && LOG.isDebug() ) {
    163                LOG.logDebug( "The element with localname "
    164                              + crsDefintion.getLocalName()
    165                              + " could not be mapped to a valid deegreec-crs, currently projectedCRS, geographicCRS, geocentricCRS and compoundCRS are supported." );
    166            }
    167            return result;
    168        }
    169
    170        public Element getURIAsType( String uri )
    171                                throws IOException {
    172            if ( uri == null || "".equals( uri ) ) {
    173                return null;
    174            }
    175            String id = uri.trim().toUpperCase();
    176            Element crsElement = null;
    177            // String xPath ="//*[crs:id='EPSG:31466']";
    178            String xPath = XPATH_PRE + id + "']";
    179            try {
    180                crsElement = XMLTools.getElement( getRootElement(), xPath, nsContext );
    181            } catch ( XMLParsingException e ) {
    182                LOG.logError( Messages.getMessage( "CRS_CONFIG_NO_RESULT_FOR_ID", id, e.getMessage() ), e );
    183            }
    184            if ( LOG.isDebug() ) {
    185                LOG.logDebug( "Trying to find elements with xpath: " + xPath
    186                              + ( ( crsElement == null ) ? " [failed]" : " [success]" ) );
    187            }
    188            return crsElement;
    189        }
    190
    191        public Helmert getWGS84Transformation( GeographicCRS sourceCRS ) {
    192            if ( sourceCRS == null ) {
    193                return null;
    194            }
    195            return sourceCRS.getGeodeticDatum().getWGS84Conversion();
    196        }
    197
    198        /**
    199         * @return the version of the root element of the empty string if no version attribute was found in the root
    200         *         element.
    201         * @throws CRSConfigurationException
    202         *             if the root element is empty
    203         */
    204        public String getVersion()
    205                                throws CRSConfigurationException {
    206            if ( getRootElement() == null ) {
    207                throw new CRSConfigurationException( "The crs parser has no root element, this cannot be." );
    208            }
    209            return getRootElement().getAttribute( "version" );
    210        }
    211
    212        /**
    213         * Parses all elements of the identifiable object.
    214         *
    215         * @param element
    216         *            the xml-representation of the id-object
    217         * @return the identifiable object or <code>null</code> if no id was given.
    218         * @throws CRSConfigurationException
    219         */
    220        protected Identifiable parseIdentifiable( Element element )
    221                                throws CRSConfigurationException {
    222            try {
    223                String[] identifiers = XMLTools.getNodesAsStrings( element, PRE + "id", nsContext );
    224                if ( identifiers == null || identifiers.length == 0 ) {
    225                    String msg = Messages.getMessage( "CRS_CONFIG_NO_ID", ( ( element == null ) ? "null"
    226                                                                                               : element.getLocalName() ) );
    227                    throw new CRSConfigurationException( msg );
    228                }
    229                for ( int i = 0; i < identifiers.length; ++i ) {
    230                    if ( identifiers[i] != null ) {
    231                        identifiers[i] = identifiers[i].toUpperCase().trim();
    232                    }
    233                }
    234                String[] names = XMLTools.getNodesAsStrings( element, PRE + "name", nsContext );
    235                String[] versions = XMLTools.getNodesAsStrings( element, PRE + "version", nsContext );
    236                String[] descriptions = XMLTools.getNodesAsStrings( element, PRE + "description", nsContext );
    237                String[] areasOfUse = XMLTools.getNodesAsStrings( element, PRE + "areaOfUse", nsContext );
    238                return new Identifiable( identifiers, names, versions, descriptions, areasOfUse );
    239            } catch ( XMLParsingException e ) {
    240                throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", "Identifiable",
    241                                                                          ( ( element == null ) ? "null"
    242                                                                                               : element.getLocalName() ),
    243                                                                          e.getMessage() ), e );
    244            }
    245        }
    246
    247        /**
    248         * Creates an axis array for the given crs element.
    249         *
    250         * @param crsElement
    251         *            to be parsed
    252         * @return an Array of axis defining their order.
    253         * @throws CRSConfigurationException
    254         *             if a required element could not be found, or an xmlParsingException occurred, or the axisorder uses
    255         *             names which were not defined in the axis elements.
    256         */
    257        protected Axis[] parseAxisOrder( Element crsElement )
    258                                throws CRSConfigurationException {
    259            String axisOrder = null;
    260            try {
    261                axisOrder = XMLTools.getRequiredNodeAsString( crsElement, PRE + "axisOrder", nsContext );
    262            } catch ( XMLParsingException e ) {
    263                throw new CRSConfigurationException(
    264                                                     Messages.getMessage(
    265                                                                          "CRS_CONFIG_PARSE_ERROR",
    266                                                                          "AxisOrder",
    267                                                                          ( ( crsElement == null ) ? "null"
    268                                                                                                  : crsElement.getLocalName() ),
    269                                                                          e.getMessage() ), e );
    270            }
    271            if ( axisOrder == null || "".equals( axisOrder.trim() ) ) {
    272                throw new CRSConfigurationException(
    273                                                     Messages.getMessage(
    274                                                                          "CRS_CONFIG_PARSE_ERROR",
    275                                                                          "AxisOrder",
    276                                                                          ( ( crsElement == null ) ? "null"
    277                                                                                                  : crsElement.getLocalName() ),
    278                                                                          " axisOrder element may not be empty" ) );
    279            }
    280            axisOrder = axisOrder.trim();
    281            String[] order = axisOrder.trim().split( "," );
    282            Axis[] axis = new Axis[order.length];
    283            String XPATH = PRE + "Axis[" + PRE + "name = '";
    284            for ( int i = 0; i < order.length; ++i ) {
    285                String t = order[i];
    286                if ( t != null && !"".equals( t.trim() ) ) {
    287                    t = t.trim();
    288                    try {
    289                        Element axisElement = XMLTools.getRequiredElement( crsElement, XPATH + t + "']", nsContext );
    290                        String axisOrientation = XMLTools.getRequiredNodeAsString( axisElement, PRE + "axisOrientation",
    291                                                                                   nsContext );
    292                        Unit unit = parseUnit( axisElement );
    293                        axis[i] = new Axis( unit, t, axisOrientation );
    294                    } catch ( XMLParsingException e ) {
    295                        throw new CRSConfigurationException(
    296                                                             Messages.getMessage(
    297                                                                                  "CRS_CONFIG_PARSE_ERROR",
    298                                                                                  "Axis",
    299                                                                                  ( ( crsElement == null ) ? "null"
    300                                                                                                          : crsElement.getLocalName() ),
    301                                                                                  e.getMessage() ), e );
    302                    }
    303                }
    304            }
    305            return axis;
    306        }
    307
    308        /**
    309         * Retrieves a transformation from the resource.
    310         *
    311         * @param transformationDefinition
    312         * @return the parsed transformation or <code>null</code> if no transformation could be parsed.
    313         */
    314        public Transformation parseTransformation( Element transformationDefinition ) {
    315            throw new UnsupportedOperationException(
    316                                                     "The parsing of transformations is not supported by this version of the deegree crs parser." );
    317        }
    318
    319        /**
    320         * Parse all polynomial transformations for a given crs.
    321         *
    322         * @param crsElement
    323         *            to parse the transformations for.
    324         * @return the list of transformations or the empty list if no transformations were found. Never <code>null</code>.
    325         */
    326        protected List<PolynomialTransformation> parseAlternativeTransformations( Element crsElement ) {
    327            List<Element> usedTransformations = null;
    328            try {
    329                usedTransformations = XMLTools.getElements( crsElement, PRE + "polynomialTransformation", nsContext );
    330            } catch ( XMLParsingException e ) {
    331                LOG.logError( e.getMessage(), e );
    332            }
    333            List<PolynomialTransformation> result = new LinkedList<PolynomialTransformation>();
    334            if ( usedTransformations != null ) {
    335                for ( Element transformationElement : usedTransformations ) {
    336                    PolynomialTransformation transform = getTransformation( transformationElement );
    337                    if ( transform != null ) {
    338                        result.add( transform );
    339                    }
    340                }
    341            }
    342            return result;
    343        }
    344
    345        /**
    346         * Parses the transformation variables from the given crs:coordinatesystem/crs:polynomialTransformation element. If
    347         * the class attribute is given, this method will try to invoke an instance of the given class, if it fails
    348         * <code>null</code> will be returned.
    349         *
    350         * @param transformationElement
    351         *            to parse the values from.
    352         * @return the instantiated transformation or <code>null</code> if it could not be instantiated.
    353         */
    354        protected PolynomialTransformation getTransformation( Element transformationElement ) {
    355            if ( transformationElement == null ) {
    356                throw new CRSConfigurationException( Messages.getMessage( "CRS_INVALID_NULL_PARAMETER",
    357                                                                          "transformationElement" ) );
    358            }
    359
    360            // order is not evaluated yet, because I do not know if it is required.
    361            // int order = -1;
    362            String tCRS = null;
    363            List<Double> aValues = new LinkedList<Double>();
    364            List<Double> bValues = new LinkedList<Double>();
    365            Element usedTransformation = null;
    366            try {
    367                usedTransformation = XMLTools.getRequiredElement( transformationElement, "*[1]", nsContext );
    368            } catch ( XMLParsingException e ) {
    369                throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR",
    370                                                                          "the transformation to use",
    371                                                                          transformationElement.getLocalName(),
    372                                                                          e.getLocalizedMessage() ), e );
    373            }
    374
    375            try {
    376                // order = XMLTools.getNodeAsInt( usedTransformation, PRE + "polynomialOrder", nsContext, -1 );
    377                tCRS = XMLTools.getRequiredNodeAsString( usedTransformation, PRE + "targetCRS", nsContext );
    378                Element tmp = XMLTools.getRequiredElement( usedTransformation, PRE + "xParameters", nsContext );
    379                String tmpValues = XMLTools.getStringValue( tmp );
    380                if ( tmpValues != null && !"".equals( tmpValues.trim() ) ) {
    381                    String[] split = tmpValues.split( "\\s" );
    382                    for ( String t : split ) {
    383                        aValues.add( Double.parseDouble( t ) );
    384                    }
    385                }
    386                tmp = XMLTools.getRequiredElement( usedTransformation, PRE + "yParameters", nsContext );
    387                tmpValues = XMLTools.getStringValue( tmp );
    388                if ( tmpValues != null && !"".equals( tmpValues.trim() ) ) {
    389                    String[] split = tmpValues.split( "\\s" );
    390                    for ( String t : split ) {
    391                        bValues.add( Double.parseDouble( t ) );
    392                    }
    393                }
    394            } catch ( XMLParsingException e ) {
    395                LOG.logError( e.getMessage(), e );
    396            }
    397
    398            if ( tCRS == null ) {
    399                throw new CRSConfigurationException(
    400                                                     Messages.getMessage(
    401                                                                          "CRS_CONFIG_PARSE_ERROR",
    402                                                                          "targetCRS",
    403                                                                          ( ( usedTransformation == null ) ? "null"
    404                                                                                                          : usedTransformation.getLocalName() ),
    405                                                                          "it is required and must denote a valid crs )" ) );
    406            }
    407
    408            if ( aValues.size() == 0 || bValues.size() == 0 ) {
    409                throw new CRSConfigurationException(
    410                                                     "The polynomial variables (xParameters and yParameters element) defining the approximation to a given transformation function are required and may not be empty" );
    411            }
    412
    413            CoordinateSystem targetCRS = getProvider().getCRSByID( tCRS );
    414
    415            PolynomialTransformation result = null;
    416            String name = usedTransformation.getLocalName().trim();
    417            String className = transformationElement.getAttribute( "class" );
    418            LOG.logDebug( "Trying to create transformation with name: " + name );
    419            if ( null != className && !"".equals( className.trim() ) ) {
    420                LOG.logDebug( "Trying to load user defined transformation class: " + className );
    421                try {
    422                    Class<?> t = Class.forName( className );
    423                    t.asSubclass( PolynomialTransformation.class );
    424
    425                    List<Element> children = XMLTools.getElements( usedTransformation, "*", nsContext );
    426                    List<Element> otherValues = new LinkedList<Element>();
    427                    for ( Element child : children ) {
    428                        if ( child != null ) {
    429                            String localName = child.getLocalName().trim();
    430                            if ( !( "targetCRS".equals( localName ) || "xParameters".equals( localName )
    431                                    || "yParameters".equals( localName ) || "polynomialOrder".equals( localName ) ) ) {
    432                                otherValues.add( child );
    433                            }
    434                        }
    435                    }
    436                    /**
    437                     * Load the constructor with the standard projection values and the element list.
    438                     */
    439                    /**
    440                     * For now, just load the constructor with the two lists and the crs class.
    441                     */
    442                    Constructor<?> constructor = t.getConstructor( aValues.getClass(), bValues.getClass(),
    443                                                                   targetCRS.getClass() );
    444                    result = (PolynomialTransformation) constructor.newInstance( aValues, bValues, targetCRS );
    445                } catch ( ClassNotFoundException e ) {
    446                    LOG.logError( e.getMessage(), e );
    447                } catch ( SecurityException e ) {
    448                    LOG.logError( e.getMessage(), e );
    449                } catch ( NoSuchMethodException e ) {
    450                    LOG.logError( e.getMessage(), e );
    451                } catch ( IllegalArgumentException e ) {
    452                    LOG.logError( e.getMessage(), e );
    453                } catch ( InstantiationException e ) {
    454                    LOG.logError( e.getMessage(), e );
    455                } catch ( IllegalAccessException e ) {
    456                    LOG.logError( e.getMessage(), e );
    457                } catch ( InvocationTargetException e ) {
    458                    LOG.logError( e.getMessage(), e );
    459                } catch ( XMLParsingException e ) {
    460                    // this will probably never happen.
    461                    LOG.logError( e.getMessage(), e );
    462                }
    463                if ( result == null ) {
    464                    LOG.logDebug( "Loading of user defined transformation class: " + className + " was not successful" );
    465                }
    466            } else if ( "leastsquare".equalsIgnoreCase( name ) ) {
    467                float scaleX = 1;
    468                float scaleY = 1;
    469                try {
    470                    scaleX = (float) XMLTools.getNodeAsDouble( usedTransformation, PRE + "scaleX", nsContext, 1 );
    471                } catch ( XMLParsingException e ) {
    472                    LOG.logError( "Could not parse scaleX from crs:leastsquare, because: " + e.getMessage(), e );
    473                }
    474                try {
    475                    scaleY = (float) XMLTools.getNodeAsDouble( usedTransformation, PRE + "scaleY", nsContext, 1 );
    476                } catch ( XMLParsingException e ) {
    477                    LOG.logError( "Could not parse scaleY from crs:leastsquare, because: " + e.getMessage(), e );
    478                }
    479                result = new LeastSquareApproximation( aValues, bValues, null, targetCRS, scaleX, scaleY );
    480            }
    481            return result;
    482        }
    483
    484        /**
    485         * Parses a unit from the given xml-parent.
    486         *
    487         * @param parent
    488         *            xml-node to parse the unit from.
    489         * @return the unit object.
    490         * @throws CRSConfigurationException
    491         *             if the unit object could not be created.
    492         */
    493        protected Unit parseUnit( Element parent )
    494                                throws CRSConfigurationException {
    495            String unitId = null;
    496            try {
    497                unitId = XMLTools.getNodeAsString( parent, PRE + "units", nsContext, null );
    498            } catch ( XMLParsingException e ) {
    499                throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", "units",
    500                                                                          ( ( parent == null ) ? "null"
    501                                                                                              : parent.getLocalName() ),
    502                                                                          e.getMessage() ), e );
    503            }
    504            Unit result = getProvider().getCachedIdentifiable( Unit.class, unitId );
    505            if ( result == null ) {
    506                result = Unit.createUnitFromString( unitId );
    507                if ( result == null ) {
    508                    throw new CRSConfigurationException(
    509                                                         Messages.getMessage(
    510                                                                              "CRS_CONFIG_PARSE_ERROR",
    511                                                                              "units",
    512                                                                              ( ( parent == null ) ? "null"
    513                                                                                                  : parent.getLocalName() ),
    514                                                                              "unknown unit: " + unitId ) );
    515                }
    516            }
    517            return result;
    518        }
    519
    520        /**
    521         * @param crsElement
    522         *            from which the crs is to be created (using chached datums, conversioninfos and projections).
    523         * @return a projected coordinatesystem based on the given xml-element.
    524         * @throws CRSConfigurationException
    525         *             if a required element could not be found, or an xmlParsingException occurred.
    526         */
    527        protected CoordinateSystem parseProjectedCRS( Element crsElement )
    528                                throws CRSConfigurationException {
    529            if ( crsElement == null ) {
    530                return null;
    531            }
    532            // no need to get it from the cache, because the abstract provider checked it already.
    533            Identifiable id = parseIdentifiable( crsElement );
    534
    535            Axis[] axis = parseAxisOrder( crsElement );
    536            List<PolynomialTransformation> transformations = parseAlternativeTransformations( crsElement );
    537            // Unit units = parseUnit( crsElement );
    538
    539            Element usedProjection = null;
    540            String usedGeographicCRS = null;
    541            try {
    542                usedProjection = XMLTools.getRequiredElement( crsElement, PRE + "projection", nsContext );
    543                usedGeographicCRS = XMLTools.getRequiredNodeAsString( crsElement, PRE + "usedGeographicCRS", nsContext );
    544
    545            } catch ( XMLParsingException e ) {
    546                throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR",
    547                                                                          "projectiontType or usedGeographicCRS",
    548                                                                          crsElement.getLocalName(), e.getMessage() ), e );
    549
    550            }
    551            // first create the datum.
    552            if ( usedGeographicCRS == null || "".equals( usedGeographicCRS.trim() ) ) {
    553                throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_REFERENCE_ID_IS_EMPTY",
    554                                                                          "usedGeographicCRS", id.getIdentifier() ) );
    555            }
    556            GeographicCRS geoCRS = (GeographicCRS) getProvider().getCRSByID( usedGeographicCRS );
    557            if ( geoCRS == null || geoCRS.getType() != CoordinateSystem.GEOGRAPHIC_CRS ) {
    558                throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJECTEDCRS_FALSE_CRSREF",
    559                                                                          id.getIdentifier(), usedGeographicCRS ) );
    560            }
    561
    562            // // then the projection.
    563            // if ( usedProjection == null || "".equals( usedProjection.trim() ) ) {
    564            // throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_REFERENCE_ID_IS_EMPTY",
    565            // "projectionType", id.getIdentifier() ) );
    566            // }
    567            // Projection projection = getProjectionByID( usedProjection, (GeographicCRS) geoCRS, axis[0].getUnits() );
    568            Projection projection = parseProjection( usedProjection, geoCRS, axis[0].getUnits() );
    569            if ( projection == null ) {
    570                throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PROJECTEDCRS_FALSE_PROJREF",
    571                                                                          id.getIdentifier(), usedProjection ) );
    572            }
    573            // adding to cache will be done in AbstractCRSProvider.
    574            return new ProjectedCRS( transformations, projection, axis, id );
    575        }
    576
    577        /**
    578         * @param crsElement
    579         *            from which the crs is to be created (using cached datums, conversioninfos and projections).
    580         *
    581         * @return a geographic coordinatesystem based on the given xml-element.
    582         * @throws CRSConfigurationException
    583         *             if a required element could not be found, or an xmlParsingException occurred.
    584         */
    585        protected CoordinateSystem parseGeographicCRS( Element crsElement )
    586                                throws CRSConfigurationException {
    587            if ( crsElement == null ) {
    588                return null;
    589            }
    590            Identifiable id = parseIdentifiable( crsElement );
    591            // no need to get it from the cache, because the abstract provider checked it already.
    592            Axis[] axis = parseAxisOrder( crsElement );
    593            List<PolynomialTransformation> transformations = parseAlternativeTransformations( crsElement );
    594            // get the datum
    595            GeodeticDatum usedDatum = parseReferencedGeodeticDatum( crsElement, id.getIdentifier() );
    596
    597            GeographicCRS result = new GeographicCRS( transformations, usedDatum, axis, id );
    598            // adding to cache will be done in AbstractCRSProvider.
    599            return result;
    600        }
    601
    602        /**
    603         * @param crsElement
    604         *            from which the crs is to be created (using cached datums, conversioninfos and projections).
    605         * @return a geocentric coordinatesystem based on the given xml-element.
    606         * @throws CRSConfigurationException
    607         *             if a required element could not be found, or an xmlParsingException occurred.
    608         */
    609        protected CoordinateSystem parseGeocentricCRS( Element crsElement )
    610                                throws CRSConfigurationException {
    611            // no need to get it from the cache, because the abstract provider checked it already.
    612            Identifiable id = parseIdentifiable( crsElement );
    613            Axis[] axis = parseAxisOrder( crsElement );
    614            List<PolynomialTransformation> transformations = parseAlternativeTransformations( crsElement );
    615            GeodeticDatum usedDatum = parseReferencedGeodeticDatum( crsElement, id.getIdentifier() );
    616            GeocentricCRS result = new GeocentricCRS( transformations, usedDatum, axis, id );
    617            // adding to cache will be done in AbstractCRSProvider.
    618            return result;
    619        }
    620
    621        /**
    622         * @param crsElement
    623         *            from which the crs is to be created.
    624         *
    625         * @return a compound coordinatesystem based on the given xml-element.
    626         * @throws CRSConfigurationException
    627         *             if a required element could not be found, or an xmlParsingException occurred.
    628         */
    629        protected CoordinateSystem parseCompoundCRS( Element crsElement ) {
    630            // no need to get it from the cache, because the abstract provider checked it already.
    631            Identifiable id = parseIdentifiable( crsElement );
    632            String usedCRS = null;
    633            try {
    634                usedCRS = XMLTools.getRequiredNodeAsString( crsElement, PRE + "usedCRS", nsContext );
    635            } catch ( XMLParsingException e ) {
    636                throw new CRSConfigurationException(
    637                                                     Messages.getMessage(
    638                                                                          "CRS_CONFIG_PARSE_ERROR",
    639                                                                          "usedCRS",
    640                                                                          ( ( crsElement == null ) ? "null"
    641                                                                                                  : crsElement.getLocalName() ),
    642                                                                          e.getMessage() ), e );
    643
    644            }
    645            if ( usedCRS == null || "".equals( usedCRS.trim() ) ) {
    646                throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_REFERENCE_ID_IS_EMPTY", "usedCRS",
    647                                                                          id.getIdentifier() ) );
    648            }
    649            CoordinateSystem usedCoordinateSystem = getProvider().getCRSByID( usedCRS );
    650            if ( usedCoordinateSystem == null
    651                 || ( usedCoordinateSystem.getType() != CoordinateSystem.GEOGRAPHIC_CRS && usedCoordinateSystem.getType() != CoordinateSystem.PROJECTED_CRS ) ) {
    652                throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_COMPOUND_FALSE_CRSREF",
    653                                                                          id.getIdentifier(), usedCRS ) );
    654            }
    655
    656            // get the datum
    657            Axis heigtAxis = null;
    658            double defaultHeight = 0;
    659            try {
    660                Element axisElement = XMLTools.getRequiredElement( crsElement, PRE + "heightAxis", nsContext );
    661                String axisName = XMLTools.getRequiredNodeAsString( axisElement, PRE + "name", nsContext );
    662                String axisOrientation = XMLTools.getRequiredNodeAsString( axisElement, PRE + "axisOrientation", nsContext );
    663                Unit unit = parseUnit( axisElement );
    664                heigtAxis = new Axis( unit, axisName, axisOrientation );
    665                defaultHeight = XMLTools.getNodeAsDouble( crsElement, PRE + "defaultHeight", nsContext, 0 );
    666            } catch ( XMLParsingException e ) {
    667                LOG.logError( e.getMessage(), e );
    668                throw new CRSConfigurationException( e.getMessage() );
    669            }
    670            // adding to cache will be done in AbstractCRSProvider.
    671            return new CompoundCRS( heigtAxis, usedCoordinateSystem, defaultHeight, id );
    672        }
    673
    674        /**
    675         * Parses the required usedDatum element from the given parentElement (probably a crs element).
    676         *
    677         * @param parentElement
    678         *            to parse the required usedDatum element from.
    679         * @param parentID
    680         *            optional for an appropriate error message.
    681         * @return the Datum.
    682         * @throws CRSConfigurationException
    683         *             if a parsing error occurred, the node was not defined or an illegal id reference (not found) was
    684         *             given.
    685         */
    686        protected GeodeticDatum parseReferencedGeodeticDatum( Element parentElement, String parentID )
    687                                throws CRSConfigurationException {
    688            String datumID = null;
    689            try {
    690                datumID = XMLTools.getRequiredNodeAsString( parentElement, PRE + "usedDatum", nsContext );
    691            } catch ( XMLParsingException e ) {
    692                throw new CRSConfigurationException(
    693                                                     Messages.getMessage(
    694                                                                          "CRS_CONFIG_PARSE_ERROR",
    695                                                                          "datumID",
    696                                                                          ( ( parentElement == null ) ? "null"
    697                                                                                                     : parentElement.getLocalName() ),
    698                                                                          e.getMessage() ), e );
    699            }
    700            if ( datumID == null || "".equals( datumID.trim() ) ) {
    701                throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_REFERENCE_ID_IS_EMPTY", "usedDatum",
    702                                                                          parentID ) );
    703            }
    704            GeodeticDatum usedDatum = getGeodeticDatumFromID( datumID );
    705            if ( usedDatum == null ) {
    706                throw new CRSConfigurationException(
    707                                                     Messages.getMessage( "CRS_CONFIG_USEDDATUM_IS_NULL", datumID, parentID ) );
    708            }
    709            return usedDatum;
    710        }
    711
    712        /**
    713         * @param datumID
    714         * @return the
    715         * @throws CRSConfigurationException
    716         */
    717        protected GeodeticDatum getGeodeticDatumFromID( String datumID )
    718                                throws CRSConfigurationException {
    719            if ( datumID == null || "".equals( datumID.trim() ) ) {
    720                return null;
    721            }
    722            String tmpDatumID = datumID.trim();
    723            GeodeticDatum result = getProvider().getCachedIdentifiable( GeodeticDatum.class, tmpDatumID );
    724            if ( result == null ) {
    725                Element datumElement = null;
    726                try {
    727                    datumElement = getURIAsType( tmpDatumID );
    728                } catch ( IOException e ) {
    729                    throw new CRSConfigurationException( e );
    730                }
    731                if ( datumElement == null ) {
    732                    throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_NO_ELEMENT", "datum", tmpDatumID ) );
    733                }
    734                // get the identifiable.
    735                Identifiable id = parseIdentifiable( datumElement );
    736
    737                // get the ellipsoid.
    738                Ellipsoid ellipsoid = null;
    739                try {
    740                    String ellipsID = XMLTools.getRequiredNodeAsString( datumElement, PRE + "usedEllipsoid", nsContext );
    741                    if ( ellipsID != null && !"".equals( ellipsID.trim() ) ) {
    742                        ellipsoid = getEllipsoidFromID( ellipsID );
    743                    }
    744                } catch ( XMLParsingException e ) {
    745                    throw new CRSConfigurationException(
    746                                                         Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", "usedEllipsoid",
    747                                                                              datumElement.getLocalName(), e.getMessage() ),
    748                                                         e );
    749                }
    750                if ( ellipsoid == null ) {
    751                    throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_DATUM_HAS_NO_ELLIPSOID",
    752                                                                              tmpDatumID ) );
    753                }
    754
    755                // get the primemeridian if any.
    756                PrimeMeridian pMeridian = null;
    757                try {
    758                    String pMeridianID = XMLTools.getNodeAsString( datumElement, PRE + "usedPrimeMeridian", nsContext, null );
    759                    if ( pMeridianID != null && !"".equals( pMeridianID.trim() ) ) {
    760                        pMeridian = getPrimeMeridianFromID( pMeridianID );
    761                    }
    762                    if ( pMeridian == null ) {
    763                        pMeridian = PrimeMeridian.GREENWICH;
    764                    }
    765                } catch ( XMLParsingException e ) {
    766                    throw new CRSConfigurationException(
    767                                                         Messages.getMessage( "CRS_CONFIG_PARSE_ERROR",
    768                                                                              "usedPrimeMeridian",
    769                                                                              datumElement.getLocalName(), e.getMessage() ),
    770                                                         e );
    771                }
    772
    773                // get the WGS84 if any.
    774                Helmert cInfo = null;
    775                try {
    776                    String infoID = XMLTools.getNodeAsString( datumElement, PRE + "usedWGS84ConversionInfo", nsContext,
    777                                                              null );
    778                    if ( infoID != null && !"".equals( infoID.trim() ) ) {
    779                        cInfo = getConversionInfoFromID( infoID );
    780                    }
    781                    // if ( cInfo == null ) {
    782                    // cInfo = new Helmert( "Created by DeegreeCRSProvider" );
    783                    // }
    784                } catch ( XMLParsingException e ) {
    785                    throw new CRSConfigurationException(
    786                                                         Messages.getMessage( "CRS_CONFIG_PARSE_ERROR",
    787                                                                              "wgs84ConversionInfo",
    788                                                                              datumElement.getLocalName(), e.getMessage() ),
    789                                                         e );
    790                }
    791
    792                result = new GeodeticDatum( ellipsoid, pMeridian, cInfo, id.getIdentifiers(), id.getNames(),
    793                                            id.getVersions(), id.getDescriptions(), id.getAreasOfUse() );
    794            }
    795            return getProvider().addIdToCache( result, false );
    796
    797        }
    798
    799        /**
    800         * @param meridianID
    801         *            the id to search for.
    802         * @return the primeMeridian with given id or <code>null</code>
    803         * @throws CRSConfigurationException
    804         *             if the longitude was not set or the units could not be parsed.
    805         */
    806        protected PrimeMeridian getPrimeMeridianFromID( String meridianID )
    807                                throws CRSConfigurationException {
    808            if ( meridianID == null || "".equals( meridianID.trim() ) ) {
    809                return null;
    810            }
    811            PrimeMeridian result = getProvider().getCachedIdentifiable( PrimeMeridian.class, meridianID );
    812            if ( result == null ) {
    813                Element meridianElement = null;
    814                try {
    815                    meridianElement = getURIAsType( meridianID );
    816                } catch ( IOException e ) {
    817                    throw new CRSConfigurationException( e );
    818                }
    819                if ( meridianElement == null ) {
    820                    throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_NO_ELEMENT", "primeMeridian",
    821                                                                              meridianID ) );
    822                }
    823                Identifiable id = parseIdentifiable( meridianElement );
    824                Unit units = parseUnit( meridianElement );
    825                double longitude = 0;
    826                try {
    827                    longitude = XMLTools.getRequiredNodeAsDouble( meridianElement, PRE + "longitude", nsContext );
    828                    boolean inDegrees = XMLTools.getNodeAsBoolean( meridianElement, PRE + "longitude/@inDegrees",
    829                                                                   nsContext, true );
    830                    longitude = ( longitude != 0 && inDegrees ) ? Math.toRadians( longitude ) : longitude;
    831                } catch ( XMLParsingException e ) {
    832                    throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", "longitude",
    833                                                                              meridianElement.getLocalName(),
    834                                                                              e.getMessage() ), e );
    835                }
    836                result = new PrimeMeridian( units, longitude, id );
    837            }
    838            return getProvider().addIdToCache( result, false );
    839        }
    840
    841        /**
    842         * Tries to find a cached ellipsoid, if not found, the config will be checked.
    843         *
    844         * @param ellipsoidID
    845         * @return an ellipsoid or <code>null</code> if no ellipsoid with given id was found, or the id was
    846         *         <code>null</code> or empty.
    847         * @throws CRSConfigurationException
    848         *             if something went wrong.
    849         */
    850        protected Ellipsoid getEllipsoidFromID( String ellipsoidID )
    851                                throws CRSConfigurationException {
    852            if ( ellipsoidID == null || "".equals( ellipsoidID.trim() ) ) {
    853                return null;
    854            }
    855            Ellipsoid result = getProvider().getCachedIdentifiable( Ellipsoid.class, ellipsoidID );
    856            if ( result == null ) {
    857                Element ellipsoidElement = null;
    858                try {
    859                    ellipsoidElement = getURIAsType( ellipsoidID );
    860                } catch ( IOException e ) {
    861                    throw new CRSConfigurationException( e );
    862                }
    863                if ( ellipsoidElement == null ) {
    864                    throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_NO_ELEMENT", "ellipsoid",
    865                                                                              ellipsoidID ) );
    866                }
    867                Identifiable id = parseIdentifiable( ellipsoidElement );
    868                Unit units = parseUnit( ellipsoidElement );
    869
    870                double semiMajor = Double.NaN;
    871                double inverseFlattening = Double.NaN;
    872                double eccentricity = Double.NaN;
    873                double semiMinorAxis = Double.NaN;
    874
    875                try {
    876                    semiMajor = XMLTools.getRequiredNodeAsDouble( ellipsoidElement, PRE + "semiMajorAxis", nsContext );
    877                    inverseFlattening = XMLTools.getNodeAsDouble( ellipsoidElement, PRE + "inverseFlattening", nsContext,
    878                                                                  Double.NaN );
    879                    eccentricity = XMLTools.getNodeAsDouble( ellipsoidElement, PRE + "eccentricity", nsContext, Double.NaN );
    880                    semiMinorAxis = XMLTools.getNodeAsDouble( ellipsoidElement, PRE + "semiMinorAxis", nsContext,
    881                                                              Double.NaN );
    882                } catch ( XMLParsingException e ) {
    883                    throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", "ellipsoid",
    884                                                                              ellipsoidElement.getLocalName(),
    885                                                                              e.getMessage() ), e );
    886                }
    887                if ( Double.isNaN( inverseFlattening ) && Double.isNaN( eccentricity ) && Double.isNaN( semiMinorAxis ) ) {
    888                    throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_ELLIPSOID_MISSES_PARAM",
    889                                                                              ellipsoidID ) );
    890                }
    891                if ( !Double.isNaN( inverseFlattening ) ) {
    892                    result = new Ellipsoid( semiMajor, units, inverseFlattening, id.getIdentifiers(), id.getNames(),
    893                                            id.getVersions(), id.getDescriptions(), id.getAreasOfUse() );
    894                } else if ( !Double.isNaN( eccentricity ) ) {
    895                    result = new Ellipsoid( semiMajor, eccentricity, units, id.getIdentifiers(), id.getNames(),
    896                                            id.getVersions(), id.getDescriptions(), id.getAreasOfUse() );
    897                } else {
    898                    result = new Ellipsoid( units, semiMajor, semiMinorAxis, id.getIdentifiers(), id.getNames(),
    899                                            id.getVersions(), id.getDescriptions(), id.getAreasOfUse() );
    900                }
    901            }
    902
    903            return getProvider().addIdToCache( result, false );
    904        }
    905
    906        /**
    907         * @param infoID
    908         *            to get the conversioninfo from.
    909         * @return the configured wgs84 conversion info parameters.
    910         * @throws CRSConfigurationException
    911         */
    912        protected Helmert getConversionInfoFromID( String infoID )
    913                                throws CRSConfigurationException {
    914            if ( infoID == null || "".equals( infoID.trim() ) ) {
    915                return null;
    916            }
    917            LOG.logDebug( "Searching for the wgs84 with id: " + infoID );
    918            Helmert result = getProvider().getCachedIdentifiable( Helmert.class, infoID );
    919            if ( result == null ) {
    920
    921                Element cInfoElement = null;
    922                try {
    923                    cInfoElement = getURIAsType( infoID );
    924                } catch ( IOException e ) {
    925                    throw new CRSConfigurationException( e );
    926                }
    927                if ( cInfoElement == null ) {
    928                    throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_NO_ELEMENT",
    929                                                                              "wgs84ConversionInfo", infoID ) );
    930                }
    931                Identifiable identifiable = parseIdentifiable( cInfoElement );
    932                double xT = 0, yT = 0, zT = 0, xR = 0, yR = 0, zR = 0, scale = 0;
    933                try {
    934                    xT = XMLTools.getNodeAsDouble( cInfoElement, PRE + "xAxisTranslation", nsContext, 0 );
    935                    yT = XMLTools.getNodeAsDouble( cInfoElement, PRE + "yAxisTranslation", nsContext, 0 );
    936                    zT = XMLTools.getNodeAsDouble( cInfoElement, PRE + "zAxisTranslation", nsContext, 0 );
    937                    xR = XMLTools.getNodeAsDouble( cInfoElement, PRE + "xAxisRotation", nsContext, 0 );
    938                    yR = XMLTools.getNodeAsDouble( cInfoElement, PRE + "yAxisRotation", nsContext, 0 );
    939                    zR = XMLTools.getNodeAsDouble( cInfoElement, PRE + "zAxisRotation", nsContext, 0 );
    940                    scale = XMLTools.getNodeAsDouble( cInfoElement, PRE + "scaleDifference", nsContext, 0 );
    941                } catch ( XMLParsingException e ) {
    942                    throw new CRSConfigurationException( Messages.getMessage( "CRS_CONFIG_PARSE_ERROR", "conversionInfo",
    943                                                                              "definitions", e.getMessage() ), e );
    944                }
    945                result = new Helmert( xT, yT, zT, xR, yR, zR, scale, null, GeographicCRS.WGS84, identifiable );
    946            }
    947            return getProvider().addIdToCache( result, false );
    948        }
    949
    950        /**
    951         * Parses and instantiates the projection from the given element.
    952         *
    953         * @param projectionElement
    954         *            to create the projection from.
    955         * @param underlyingCRS
    956         *            of the projected crs
    957         * @param units
    958         *            of the projected crs
    959         * @return the configured projection or <code>null</code> if not defined or found.
    960         * @throws CRSConfigurationException
    961         */
    962        protected Projection parseProjection( Element projectionElement, GeographicCRS underlyingCRS, Unit units )
    963                                throws CRSConfigurationException {
    964            if ( projectionElement == null ) {
    965                throw new CRSConfigurationException(
    966                                                     Messages.getMessage( "CRS_INVALID_NULL_PARAMETER", "projectionElement" ) );
    967            }
    968            try {
    969                Element usedProjection = XMLTools.getRequiredElement( projectionElement, "*[1]", nsContext );
    970                // All projections will have following parameters
    971                double latitudeOfNaturalOrigin = XMLTools.getNodeAsDouble( usedProjection, PRE + "latitudeOfNaturalOrigin",
    972                                                                           nsContext, 0 );
    973                boolean inDegrees = XMLTools.getNodeAsBoolean( usedProjection, PRE + "latitudeOfNaturalOrigin/@inDegrees",
    974                                                               nsContext, true );
    975                latitudeOfNaturalOrigin = ( latitudeOfNaturalOrigin != 0 && inDegrees ) ? Math.toRadians( latitudeOfNaturalOrigin )
    976                                                                                       : latitudeOfNaturalOrigin;
    977
    978                double longitudeOfNaturalOrigin = XMLTools.getNodeAsDouble( usedProjection, PRE
    979                                                                                            + "longitudeOfNaturalOrigin",
    980                                                                            nsContext, 0 );
    981                inDegrees = XMLTools.getNodeAsBoolean( usedProjection, PRE + "longitudeOfNaturalOrigin/@inDegrees",
    982                                                       nsContext, true );
    983                longitudeOfNaturalOrigin = ( longitudeOfNaturalOrigin != 0 && inDegrees ) ? Math.toRadians( longitudeOfNaturalOrigin )
    984                                                                                         : longitudeOfNaturalOrigin;
    985
    986                double scaleFactor = XMLTools.getNodeAsDouble( usedProjection, PRE + "scaleFactor", nsContext, 0 );
    987                double falseEasting = XMLTools.getNodeAsDouble( usedProjection, PRE + "falseEasting", nsContext, 0 );
    988                double falseNorthing = XMLTools.getNodeAsDouble( usedProjection, PRE + "falseNorthing", nsContext, 0 );
    989
    990                String projectionName = usedProjection.getLocalName().trim();
    991                String className = projectionElement.getAttribute( "class" );
    992                Point2d naturalOrigin = new Point2d( longitudeOfNaturalOrigin, latitudeOfNaturalOrigin );
    993                Projection result = null;
    994                if ( className != null && !"".equals( className.trim() ) ) {
    995                    LOG.logDebug( "Trying to load user defined projection class: " + className );
    996                    try {
    997                        Class<?> t = Class.forName( className );
    998                        t.asSubclass( Projection.class );
    999                        /**
    1000                         * try to get a constructor with a native type as a parameter, by going over the 'names' of the
    1001                         * classes of the parameters, the native type will show up as the typename e.g. int or long.....
    1002                         * <code>
    1003                         * public Projection( GeographicCRS geographicCRS, double falseNorthing, double falseEasting,
    1004                         * Point2d naturalOrigin, Unit units, double scale, boolean conformal, boolean equalArea )
    1005                         * </code>
    1006                         */
    1007                        List<Element> children = XMLTools.getElements( usedProjection, "*", nsContext );
    1008                        List<Element> otherValues = new LinkedList<Element>();
    1009                        for ( Element child : children ) {
    1010                            if ( child != null ) {
    1011                                String localName = child.getLocalName().trim();
    1012                                if ( !( "latitudeOfNaturalOrigin".equals( localName )
    1013                                        || "longitudeOfNaturalOrigin".equals( localName )
    1014                                        || "scaleFactor".equals( localName ) || "falseEasting".equals( localName ) || "falseNorthing".equals( localName ) ) ) {
    1015                                    otherValues.add( child );
    1016                                }
    1017                            }
    1018                        }
    1019                        /**
    1020                         * Load the constructor with the standard projection values and the element list.
    1021                         */
    1022                        Constructor<?> constructor = t.getConstructor( GeographicCRS.class, double.class, double.class,
    1023                                                                       Point2d.class, Unit.class, double.class, List.class );
    1024                        result = (Projection) constructor.newInstance( underlyingCRS, falseNorthing, falseEasting,
    1025                                                                       naturalOrigin, units, scaleFactor, otherValues );
    1026                    } catch ( ClassNotFoundException e ) {
    1027                        LOG.logError( e.getMessage(), e );
    1028                    } catch ( SecurityException e ) {
    1029                        LOG.logError( e.getMessage(), e );
    1030                    } catch ( NoSuchMethodException e ) {
    1031                        LOG.logError( e.getMessage(), e );
    1032                    } catch ( IllegalArgumentException e ) {
    1033                        LOG.logError( e.getMessage(), e );
    1034                    } catch ( InstantiationException e ) {
    1035                        LOG.logError( e.getMessage(), e );
    1036                    } catch ( IllegalAccessException e ) {
    1037                        LOG.logError( e.getMessage(), e );
    1038                    } catch ( InvocationTargetException e ) {
    1039                        LOG.logError( e.getMessage(), e );
    1040                    }
    1041                    if ( result == null ) {
    1042                        LOG.logDebug( "Loading of user defined transformation class: " + className + " was not successful" );
    1043                    }
    1044
    1045                } else {
    1046                    // no selfdefined projection, try one of the following, for the projection specific parameters, if any.
    1047                    if ( "transverseMercator".equalsIgnoreCase( projectionName ) ) {
    1048                        // change schema to let projection be identifiable. fix method geodetic
    1049                        boolean northernHemi = XMLTools.getNodeAsBoolean( usedProjection, PRE + "northernHemisphere",
    1050                                                                          nsContext, true );
    1051                        result = new TransverseMercator( northernHemi, underlyingCRS, falseNorthing, falseEasting,
    1052                                                         naturalOrigin, units, scaleFactor );
    1053                    } else if ( "lambertAzimuthalEqualArea".equalsIgnoreCase( projectionName ) ) {
    1054                        result = new LambertAzimuthalEqualArea( underlyingCRS, falseNorthing, falseEasting, naturalOrigin,
    1055                                                                units, scaleFactor );
    1056                    } else if ( "lambertConformalConic".equalsIgnoreCase( projectionName ) ) {
    1057                        double firstP = XMLTools.getNodeAsDouble( usedProjection, PRE + "firstParallelLatitude", nsContext,
    1058                                                                  Double.NaN );
    1059                        inDegrees = XMLTools.getNodeAsBoolean( usedProjection, PRE + "firstParallelLatitude/@inDegrees",
    1060                                                               nsContext, true );
    1061                        firstP = ( !Double.isNaN( firstP ) && inDegrees ) ? Math.toRadians( firstP ) : firstP;
    1062
    1063                        double secondP = XMLTools.getNodeAsDouble( usedProjection, PRE + "secondParallelLatitude",
    1064                                                                   nsContext, Double.NaN );
    1065                        inDegrees = XMLTools.getNodeAsBoolean( usedProjection, PRE + "secondParallelLatitude/@inDegrees",
    1066                                                               nsContext, true );
    1067                        secondP = ( !Double.isNaN( secondP ) && inDegrees ) ? Math.toRadians( secondP ) : secondP;
    1068                        result = new LambertConformalConic( firstP, secondP, underlyingCRS, falseNorthing, falseEasting,
    1069                                                            naturalOrigin, units, scaleFactor );
    1070                    } else if ( "stereographicAzimuthal".equalsIgnoreCase( projectionName ) ) {
    1071                        double trueScaleL = XMLTools.getNodeAsDouble( usedProjection, PRE + "trueScaleLatitude", nsContext,
    1072                                                                      Double.NaN );
    1073                        inDegrees = XMLTools.getNodeAsBoolean( usedProjection, PRE + "trueScaleLatitude/@inDegrees",
    1074                                                               nsContext, true );
    1075                        trueScaleL = ( !Double.isNaN( trueScaleL ) && inDegrees ) ? Math.toRadians( trueScaleL )
    1076                                                                                 : trueScaleL;
    1077                        result = new StereographicAzimuthal( trueScaleL, underlyingCRS, falseNorthing, falseEasting,
    1078                                                             naturalOrigin, units, scaleFactor );
    1079                    } else if ( "StereographicAlternative".equalsIgnoreCase( projectionName ) ) {
    1080                        result = new StereographicAlternative( underlyingCRS, falseNorthing, falseEasting, naturalOrigin,
    1081                                                               units, scaleFactor );
    1082                    } else if ( "mercator".equalsIgnoreCase( projectionName ) ) {
    1083                        result = new Mercator( underlyingCRS, falseNorthing, falseEasting, naturalOrigin, units,
    1084                                               scaleFactor );
    1085                    } else {
    1086                        throw new CRSConfigurationException(
    1087                                                             Messages.getMessage(
    1088                                                                                  "CRS_CONFIG_PROJECTEDCRS_INVALID_PROJECTION",
    1089                                                                                  projectionName,
    1090                                                                                  "StereographicAlternative, stereographicAzimuthal, lambertConformalConic, lambertAzimuthalEqualArea, transverseMercator" ) );
    1091
    1092                    }
    1093                }
    1094                return result;
    1095            } catch ( XMLParsingException e ) {
    1096                throw new CRSConfigurationException(
    1097                                                     Messages.getMessage( "CRS_CONFIG_PARSE_ERROR",
    1098                                                                          "projection parameters",
    1099                                                                          projectionElement.getLocalName(), e.getMessage() ),
    1100                                                     e );
    1101
    1102            }
    1103        }
    1104
    1105        /**
    1106         * @param <T>
    1107         *            should be at least of Type Identifiable.
    1108         * @param uniqueList
    1109         *            to check against
    1110         * @param mapping
    1111         *            to added the id of T to if it is found duplicate.
    1112         * @param toBeChecked
    1113         *            to check.
    1114         * @return the cached T if found or the given identifiable.
    1115         */
    1116        protected <T extends Identifiable> T checkForUniqueness( List<T> uniqueList, Map<String, String> mapping,
    1117                                                                 T toBeChecked ) {
    1118            T result = toBeChecked;
    1119            if ( uniqueList.contains( toBeChecked ) ) {
    1120                int index = uniqueList.indexOf( toBeChecked );
    1121                LOG.logInfo( "The Identifiable with id: " + toBeChecked.getIdentifier() + " was found to be equal with: "
    1122                             + uniqueList.get( index ).getIdentifier() );
    1123                String key = uniqueList.get( index ).getIdentifier();
    1124                boolean updatedEPSG = false;
    1125                if ( key != null && !"".equals( key.trim() ) ) {
    1126                    String value = mapping.get( key );
    1127                    String tbcID = toBeChecked.getIdentifier().toUpperCase();
    1128                    // it would be nicest to get the epsg code if any.
    1129                    if ( !key.toUpperCase().startsWith( "EPSG:" ) && tbcID.startsWith( "EPSG:" ) ) {
    1130                        if ( value == null || "".equals( value ) ) {
    1131                            value = key;
    1132                        } else {
    1133                            value += ", " + key;
    1134                        }
    1135                        updatedEPSG = true;
    1136                        mapping.remove( key );
    1137                        key = toBeChecked.getIdentifier();
    1138                    } else {
    1139                        if ( value == null || "".equals( value ) ) {
    1140                            value = toBeChecked.getIdentifier();
    1141                        } else {
    1142                            value += ", " + toBeChecked.getIdentifier();
    1143                        }
    1144                    }
    1145                    mapping.put( key, value );
    1146                }
    1147                // if updated to epsg, cache the epsg instead and remove the old identifiable.
    1148                if ( updatedEPSG ) {
    1149                    uniqueList.remove( index );
    1150                    uniqueList.add( toBeChecked );
    1151                } else {
    1152                    result = uniqueList.get( index );
    1153                }
    1154            } else {
    1155                LOG.logDebug( "Adding: " + toBeChecked.getIdentifier() + " to cache." );
    1156                uniqueList.add( toBeChecked );
    1157            }
    1158            return result;
    1159        }
    1160
    1161        public Transformation getTransformation( CoordinateSystem sourceCRS, CoordinateSystem targetCRS ) {
    1162            LOG.logError( "The retrieval of transformations is not supported for this deegree crs configuration format." );
    1163            return null;
    1164        }
    1165
    1166        /**
    1167         * Gets the Element for the given id and heuristically check the localname of the resulting root Element. This
    1168         * version supports following local names (see schema): <code>
    1169         * <ul>
    1170         * <li>ellipsoid</li>
    1171         * <li>geodeticDatum</li>
    1172         * <li>projectedCRS</li>
    1173         * <li>geographicCRS</li>
    1174         * <li>compoundCRS</li>
    1175         * <li>geocentricCRS</li>
    1176         * <li>primeMeridian</li>
    1177         * <li>wgs84Transformation</li>
    1178         * </ul>
    1179         * </code>
    1180         *
    1181         * @param id
    1182         *            to look for.
    1183         * @return the instantiated {@link Identifiable} or <code>null</code> if it could not be parsed.
    1184         */
    1185        public Identifiable parseIdentifiableObject( String id ) {
    1186            if ( id == null || "".equals( id ) ) {
    1187                return null;
    1188            }
    1189            Element resolvedID = null;
    1190            try {
    1191                resolvedID = getURIAsType( id );
    1192            } catch ( IOException e ) {
    1193                throw new CRSConfigurationException( e );
    1194            }
    1195            Identifiable result = null;
    1196            if ( resolvedID != null ) {
    1197                String localName = resolvedID.getLocalName();
    1198                if ( localName != null && !"".equals( localName.trim() ) ) {
    1199                    if ( localName.equals( "ellipsoid" ) ) {
    1200                        result = getEllipsoidFromID( id );
    1201                    } else if ( localName.equals( "geodeticDatum" ) ) {
    1202                        result = getGeodeticDatumFromID( id );
    1203                    } else if ( localName.equals( "projectedCRS" ) || localName.equals( "geographicCRS" )
    1204                                || localName.equals( "compoundCRS" ) || localName.equals( "geocentricCRS" ) ) {
    1205                        result = getProvider().getCRSByID( id );
    1206                    } else if ( localName.equals( "primeMeridian" ) ) {
    1207                        result = getPrimeMeridianFromID( id );
    1208                    } else if ( localName.equals( "wgs84Transformation" ) ) {
    1209                        result = getConversionInfoFromID( id );
    1210                    }
    1211                }
    1212            }
    1213            return result;
    1214        }
    1215    }