/// <summary>
        /// Creates a strategy for converting coordinates between reference systems.
        /// </summary>
        /// <param name="source">The source.</param>
        /// <param name="target">The target.</param>
        /// <returns>The produced transformation strategy.</returns>
        /// <exception cref="System.NotSupportedException">Conversion does not exist between the specified reference systems.</exception>
        public ICoordinateOperationStrategy CreateStrategy(ReferenceSystem source, ReferenceSystem target)
        {
            ICoordinateOperationStrategy <Coordinate, GeoCoordinate>    conversionToGeographic   = null;
            ICoordinateOperationStrategy <GeoCoordinate, GeoCoordinate> transformation           = null;
            ICoordinateOperationStrategy <GeoCoordinate, Coordinate>    conversionFromGeographic = null;

            switch (source.Type)
            {
            case ReferenceSystemType.Projected:
                conversionToGeographic = new ReverseCoordinateProjectionStrategy(source as ProjectedCoordinateReferenceSystem);
                break;

            case ReferenceSystemType.Geographic2D:
            case ReferenceSystemType.Geographic3D:
                conversionToGeographic = this.CreateReverseConversion(source as CoordinateReferenceSystem);
                break;
            }

            switch (target.Type)
            {
            case ReferenceSystemType.Projected:
                conversionFromGeographic = new ForwardCoordinateProjectionStrategy(target as ProjectedCoordinateReferenceSystem);
                break;

            case ReferenceSystemType.Geographic2D:
            case ReferenceSystemType.Geographic3D:
                conversionFromGeographic = this.CreateForwardConversion(target as CoordinateReferenceSystem);
                break;
            }

            // if no transformation is needed
            if (conversionFromGeographic.TargetReferenceSystem.Equals(conversionToGeographic.SourceReferenceSystem))
            {
                return(new CoordinateConversionStrategy(conversionToGeographic, conversionFromGeographic));
            }

            GeographicCoordinateReferenceSystem conversionTargetReferenceSystem = conversionToGeographic.TargetReferenceSystem as GeographicCoordinateReferenceSystem;
            GeographicCoordinateReferenceSystem conversionSourceReferenceSystem = conversionFromGeographic.SourceReferenceSystem as GeographicCoordinateReferenceSystem;

            // load matching forward transformation
            IEnumerable <CoordinateTransformation <GeoCoordinate> > transformations = this.provider.GeoCoordinateTransformations.WithProperties(conversionTargetReferenceSystem, conversionSourceReferenceSystem);

            if (transformations.Any())
            {
                transformation = new ForwardGeographicCoordinateTransformationStrategy(conversionTargetReferenceSystem, conversionSourceReferenceSystem, transformations.First());
                return(new CompoundCoordinateConversionStrategy(conversionToGeographic, transformation, conversionFromGeographic));
            }

            // if none found, load matching reverse transformation
            transformations = this.provider.GeoCoordinateTransformations.WithProperties(conversionSourceReferenceSystem, conversionTargetReferenceSystem);
            if (transformations.Any())
            {
                transformation = new ReverseGeographicCoordinateTransformationStrategy(conversionTargetReferenceSystem, conversionSourceReferenceSystem, transformations.First());
                return(new CompoundCoordinateConversionStrategy(conversionToGeographic, transformation, conversionFromGeographic));
            }

            throw new NotSupportedException(ReferenceMessages.ConversionDoesNotExistBetweenReferenceSystems);
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="CoordinateConversionStrategy" /> class.
 /// </summary>
 /// <param name="conversionToGeographic">The conversion to geographic coordinate.</param>
 /// <param name="conversionFromGeographic">The conversion from geographic coordinate.</param>
 /// <exception cref="System.ArgumentNullException">The conversion is null.</exception>
 public CoordinateConversionStrategy(ICoordinateOperationStrategy <Coordinate, GeoCoordinate> conversionToGeographic, ICoordinateOperationStrategy <GeoCoordinate, Coordinate> conversionFromGeographic)
 {
     if (conversionToGeographic == null)
     {
         throw new ArgumentNullException(nameof(conversionToGeographic));
     }
     this.conversionFromGeographic = conversionFromGeographic;
     this.conversionToGeographic   = conversionToGeographic ?? throw new ArgumentNullException(nameof(conversionFromGeographic));
 }
 /// <summary>
 /// Initializes a new instance of the <see cref="CompoundCoordinateConversionStrategy" /> class.
 /// </summary>
 /// <param name="conversionToGeographic">The conversion to geographic coordinate.</param>
 /// <param name="geographicTransformation">The geographic transformation.</param>
 /// <param name="conversionFromGeographic">The conversion from geographic coordinate.</param>
 /// <exception cref="System.ArgumentNullException">
 /// The conversion is null.
 /// or
 /// The transformation is null.
 /// </exception>
 public CompoundCoordinateConversionStrategy(ICoordinateOperationStrategy <Coordinate, GeoCoordinate> conversionToGeographic, ICoordinateOperationStrategy <GeoCoordinate, GeoCoordinate> geographicTransformation, ICoordinateOperationStrategy <GeoCoordinate, Coordinate> conversionFromGeographic)
 {
     if (conversionToGeographic == null)
     {
         throw new ArgumentNullException(nameof(conversionToGeographic));
     }
     if (conversionToGeographic == null)
     {
         throw new ArgumentNullException(nameof(geographicTransformation));
     }
     this.conversionFromGeographic = conversionFromGeographic;
     this.geographicTransformation = geographicTransformation;
     this.conversionToGeographic   = conversionToGeographic ?? throw new ArgumentNullException(nameof(conversionFromGeographic));
 }