// Copyright (c) Microsoft Corporation. All Rights Reserved. // Licensed under the MIT License. using System; using System.Windows; namespace InteractiveDataDisplay.WPF { /// /// Performs transformations between data values and plot coordinates. /// public abstract class DataTransform : DependencyObject { /// Gets range of valid data values. method returns NaN for /// values outside this range. /// public Range Domain { get; private set; } /// /// Initializes a new instance of class. /// /// A range of valid data. protected DataTransform(Range domain) { Domain = domain; } /// /// Converts value from data to plot coordinates. /// /// A value in data coordinates. /// Value converted to plot coordinates or NaN if /// falls outside of . public abstract double DataToPlot(double dataValue); /// /// Converts value from plot coordinates to data. /// /// A value in plot coordinates. /// Value converted to data coordinates or NaN if no value in data coordinates /// matches . public abstract double PlotToData(double plotValue); /// Identity transformation public static readonly DataTransform Identity = new IdentityDataTransform(); } /// /// Provides identity transformation between data and plot coordinates. /// public class IdentityDataTransform : DataTransform { /// /// Initializes a new instance of class. /// public IdentityDataTransform() : base(new Range(double.MinValue, double.MaxValue)) { } /// /// Returns a value in data coordinates without convertion. /// /// A value in data coordinates. /// public override double DataToPlot(double dataValue) { return dataValue; } /// /// Returns a value in plot coordinates without convertion. /// /// A value in plot coordinates. /// public override double PlotToData(double plotValue) { return plotValue; } } /// /// Represents a mercator transform, used in maps. /// Transforms y coordinates. /// public sealed class MercatorTransform : DataTransform { /// /// Initializes a new instance of the class. /// public MercatorTransform() : base(new Range(-85, 85)) { CalcScale(maxLatitude); } /// /// Initializes a new instance of the class. /// /// The maximal latitude. public MercatorTransform(double maxLatitude) : base(new Range(-maxLatitude, maxLatitude)) { this.maxLatitude = maxLatitude; CalcScale(maxLatitude); } private void CalcScale(double inputMaxLatitude) { double maxLatDeg = inputMaxLatitude; double maxLatRad = maxLatDeg * Math.PI / 180; scale = maxLatDeg / Math.Log(Math.Tan(maxLatRad / 2 + Math.PI / 4)); } private double scale; /// /// Gets the scale. /// /// The scale. public double Scale { get { return scale; } } private double maxLatitude = 85; /// /// Gets the maximal latitude. /// /// The max latitude. public double MaxLatitude { get { return maxLatitude; } } /// /// Converts value from mercator to plot coordinates. /// /// A value in mercator coordinates. /// Value converted to plot coordinates. public override double DataToPlot(double dataValue) { if (-maxLatitude <= dataValue && dataValue <= maxLatitude) { dataValue = scale * Math.Log(Math.Tan(Math.PI * (dataValue + 90) / 360)); } return dataValue; } /// /// Converts value from plot to mercator coordinates. /// /// A value in plot coordinates. /// Value converted to mercator coordinates. public override double PlotToData(double plotValue) { if (-maxLatitude <= plotValue && plotValue <= maxLatitude) { double e = Math.Exp(plotValue / scale); plotValue = 360 * Math.Atan(e) / Math.PI - 90; } return plotValue; } } /// /// Provides linear transform u = * d + from data value d to plot coordinate u. /// public sealed class LinearDataTransform : DataTransform { /// /// Gets or sets the scale factor. /// public double Scale { get { return (double)GetValue(ScaleProperty); } set { SetValue(ScaleProperty, value); } } /// /// Identifies the dependency property. /// public static readonly DependencyProperty ScaleProperty = DependencyProperty.Register("Scale", typeof(double), typeof(LinearDataTransform), new PropertyMetadata(1.0)); /// /// Gets or sets the distance to translate an value. /// public double Offset { get { return (double)GetValue(OffsetProperty); } set { SetValue(OffsetProperty, value); } } /// /// Identifies the dependency property. /// public static readonly DependencyProperty OffsetProperty = DependencyProperty.Register("Offset", typeof(double), typeof(LinearDataTransform), new PropertyMetadata(0.0)); /// /// Initializes a new instance of the class. /// public LinearDataTransform() : base(new Range(double.MinValue, double.MaxValue)) { } /// /// Transforms a value according to defined and . /// /// A value in data coordinates. /// Transformed value. public override double DataToPlot(double dataValue) { return dataValue * Scale + Offset; } /// /// Returns a value in data coordinates from its transformed value. /// /// Transformed value. /// Original value or NaN if is 0. public override double PlotToData(double plotValue) { if (Scale != 0) { return (plotValue - Offset) / Scale; } else return double.NaN; } } }