//****************************************************************************************************** // InverseDistanceWeightingFunction.cs - Gbtc // // Copyright © 2016, Grid Protection Alliance. All Rights Reserved. // // Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See // the NOTICE file distributed with this work for additional information regarding copyright ownership. // The GPA licenses this file to you under the MIT License (MIT), the "License"; you may // not use this file except in compliance with the License. You may obtain a copy of the License at: // // http://opensource.org/licenses/MIT // // Unless agreed to in writing, the subject software distributed under the License is distributed on an // "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the // License for the specific language governing permissions and limitations. // // Code Modification History: // ---------------------------------------------------------------------------------------------------- // 08/20/2016 - Stephen C. Wills // Generated original version of source code. // //****************************************************************************************************** using System; namespace GSF.NumericalAnalysis.Interpolation { /// /// Function definition for the inverse distance weighting algorithm. /// /// The x-coordinate of the point at which the value is to be calculated. /// The y-coordinate of the point at which the value is to be calculated. /// The calculated value at the given location. public delegate double IDWFunc(double x, double y); /// /// Function defintion for calculating the distance between two points. /// /// The x-coordinate of the first point. /// The y-coordinate of the first point. /// The x-coordinate of the second point. /// The y-coordinate of the second point. /// The distance between the two given points. public delegate double DistanceFunc(double x1, double y1, double x2, double y2); /// /// Represents a function for calculating values /// at given coordinates based on sparse data sets. /// /// /// Usage is similar to the class. /// public class InverseDistanceWeightingFunction { #region [ Members ] // Fields private IDWFunc m_converter; private DistanceFunc m_distanceFunction; private double[] m_xCoordinates; private double[] m_yCoordinates; private double[] m_values; private double m_power; #endregion #region [ Constructors ] /// /// Creates a new instance of the class. /// public InverseDistanceWeightingFunction() { m_distanceFunction = DefaultDistanceFunction; m_power = 1; } #endregion #region [ Properties ] /// /// Gets the collection of x-coordinates of points at which the values are known. /// public double[] XCoordinates { get { return m_xCoordinates; } } /// /// Gets the collection of y-coordinates of points at which the values are known. /// public double[] YCoordinates { get { return m_yCoordinates; } } /// /// Gets the collection of values of points at which the values are known. /// public double[] Values { get { return m_values; } } /// /// Gets the power applied to the inverse distance to control the speed of value's decay. /// public double Power { get { return m_power; } } /// /// Gets the function to be used to calculate the distance between two points. /// public DistanceFunc DistanceFunction { get { return m_distanceFunction; } } #endregion #region [ Methods ] /// /// Sets the collection of x-coordinates of points at which the values are known. /// /// The x-coordinates of points at which the values are known. /// A reference to the inverse distance weighting function. public InverseDistanceWeightingFunction SetXCoordinates(params double[] xCoordinates) { m_converter = null; m_xCoordinates = xCoordinates; return this; } /// /// Sets the collection of y-coordinates of points at which the values are known. /// /// The y-coordinates of points at which the values are known. /// A reference to the inverse distance weighting function. public InverseDistanceWeightingFunction SetYCoordinates(params double[] yCoordinates) { m_converter = null; m_yCoordinates = yCoordinates; return this; } /// /// Sets the collection of values of points at which the values are known. /// /// The values of points at which the values are known. /// A reference to the inverse distance weighting function. public InverseDistanceWeightingFunction SetValues(params double[] values) { m_converter = null; m_values = values; return this; } /// /// Sets the power applied to the inverse distance to control the speed of value's decay. /// /// The power applied to the inverse of the distance. /// A reference to the inverse distance weighting function. /// Larger values increase the speed of decay such that a known value affects a smaller area. public InverseDistanceWeightingFunction SetPower(double power) { m_converter = null; m_power = power; return this; } /// /// Sets the function to be used to calculate the distance between two points. /// /// The function used to calculate distance between two points. /// A reference to the inverse distance weighting function. public InverseDistanceWeightingFunction SetDistanceFunction(DistanceFunc distanceFunction) { m_converter = null; m_distanceFunction = distanceFunction ?? DefaultDistanceFunction; return this; } private IDWFunc GetConverter() { DistanceFunc distanceFunction = m_distanceFunction; double[] xCoordinates = m_xCoordinates ?? new double[0]; double[] yCoordinates = m_yCoordinates ?? new double[0]; double[] values = m_values ?? new double[0]; double power = m_power; if (xCoordinates.Length != yCoordinates.Length) throw new InvalidOperationException($"The number of x-coordinates must match the number of y-coordinates. ({xCoordinates.Length} != {yCoordinates.Length})"); if (xCoordinates.Length != values.Length) throw new InvalidOperationException($"The number of coordinates must match the number of values. ({xCoordinates.Length} != {values.Length})"); if (xCoordinates.Length == 0) return (x, y) => 0.0D; return m_converter ?? (m_converter = (x, y) => { double numerator = 0.0D; double denominator = 0.0D; for (int i = 0; i < xCoordinates.Length; i++) { double distance = distanceFunction(x, y, xCoordinates[i], yCoordinates[i]); if (distance == 0.0D) return values[i]; double inverseDistance = Math.Pow(1.0D / distance, power); numerator += values[i] * inverseDistance; denominator += inverseDistance; } return numerator / denominator; }); } #endregion #region [ Operators ] /// /// Converts the /// object to an to start converting values. /// /// The inverse distance wieghting function to be converted. /// /// the number of x-coordinates does not equal the number of y-coordinates /// - or - /// the number of coordinates does not equal the number of values /// public static implicit operator IDWFunc(InverseDistanceWeightingFunction idwFunction) { return idwFunction.GetConverter(); } #endregion #region [ Static ] // Static Methods /// /// Calculates the distance between two points. /// /// The x-coordinate of the first point. /// The y-coordinate of the first point. /// The x-coordinate of the second point. /// The y-coordinate of the second point. /// public static double DefaultDistanceFunction(double x1, double y1, double x2, double y2) { double xDiff = x1 - x2; double yDiff = y1 - y2; return Math.Sqrt(xDiff * xDiff + yDiff * yDiff); } #endregion } }