//******************************************************************************************************// 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{ /// <summary> /// Function definition for the inverse distance weighting algorithm. /// </summary> /// <param name="x">The x-coordinate of the point at which the value is to be calculated.</param> /// <param name="y">The y-coordinate of the point at which the value is to be calculated.</param> /// <returns>The calculated value at the given location.</returns> public delegate double IDWFunc(double x, double y); /// <summary> /// Function defintion for calculating the distance between two points. /// </summary> /// <param name="x1">The x-coordinate of the first point.</param> /// <param name="y1">The y-coordinate of the first point.</param> /// <param name="x2">The x-coordinate of the second point.</param> /// <param name="y2">The y-coordinate of the second point.</param> /// <returns>The distance between the two given points.</returns> public delegate double DistanceFunc(double x1, double y1, double x2, double y2); /// <summary> /// Represents a function for calculating values /// at given coordinates based on sparse data sets. /// </summary> /// <remarks> /// Usage is similar to the <see cref="PiecewiseLinearFunction"/> class. /// </remarks> 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 ] /// <summary> /// Creates a new instance of the <see cref="InverseDistanceWeightingFunction"/> class. /// </summary> public InverseDistanceWeightingFunction() { m_distanceFunction = DefaultDistanceFunction; m_power = 1; } #endregion #region [ Properties ] /// <summary> /// Gets the collection of x-coordinates of points at which the values are known. /// </summary> public double[] XCoordinates { get { return m_xCoordinates; } } /// <summary> /// Gets the collection of y-coordinates of points at which the values are known. /// </summary> public double[] YCoordinates { get { return m_yCoordinates; } } /// <summary> /// Gets the collection of values of points at which the values are known. /// </summary> public double[] Values { get { return m_values; } } /// <summary> /// Gets the power applied to the inverse distance to control the speed of value's decay. /// </summary> public double Power { get { return m_power; } } /// <summary> /// Gets the function to be used to calculate the distance between two points. /// </summary> public DistanceFunc DistanceFunction { get { return m_distanceFunction; } } #endregion #region [ Methods ] /// <summary> /// Sets the collection of x-coordinates of points at which the values are known. /// </summary> /// <param name="xCoordinates">The x-coordinates of points at which the values are known.</param> /// <returns>A reference to the inverse distance weighting function.</returns> public InverseDistanceWeightingFunction SetXCoordinates(params double[] xCoordinates) { m_converter = null; m_xCoordinates = xCoordinates; return this; } /// <summary> /// Sets the collection of y-coordinates of points at which the values are known. /// </summary> /// <param name="yCoordinates">The y-coordinates of points at which the values are known.</param> /// <returns>A reference to the inverse distance weighting function.</returns> public InverseDistanceWeightingFunction SetYCoordinates(params double[] yCoordinates) { m_converter = null; m_yCoordinates = yCoordinates; return this; } /// <summary> /// Sets the collection of values of points at which the values are known. /// </summary> /// <param name="values">The values of points at which the values are known.</param> /// <returns>A reference to the inverse distance weighting function.</returns> public InverseDistanceWeightingFunction SetValues(params double[] values) { m_converter = null; m_values = values; return this; } /// <summary> /// Sets the power applied to the inverse distance to control the speed of value's decay. /// </summary> /// <param name="power">The power applied to the inverse of the distance.</param> /// <returns>A reference to the inverse distance weighting function.</returns> /// <remarks>Larger values increase the speed of decay such that a known value affects a smaller area.</remarks> public InverseDistanceWeightingFunction SetPower(double power) { m_converter = null; m_power = power; return this; } /// <summary> /// Sets the function to be used to calculate the distance between two points. /// </summary> /// <param name="distanceFunction">The function used to calculate distance between two points.</param> /// <returns>A reference to the inverse distance weighting function.</returns> 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 ] /// <summary> /// Converts the <see cref="InverseDistanceWeightingFunction"/> /// object to an <see cref="IDWFunc"/> to start converting values. /// </summary> /// <param name="idwFunction">The inverse distance wieghting function to be converted.</param> /// <exception cref="InvalidOperationException"> /// <para>the number of x-coordinates does not equal the number of y-coordinates</para> /// <para>- or -</para> /// <para>the number of coordinates does not equal the number of values</para> /// </exception> public static implicit operator IDWFunc(InverseDistanceWeightingFunction idwFunction) { return idwFunction.GetConverter(); } #endregion #region [ Static ] // Static Methods /// <summary> /// Calculates the distance between two points. /// </summary> /// <param name="x1">The x-coordinate of the first point.</param> /// <param name="y1">The y-coordinate of the first point.</param> /// <param name="x2">The x-coordinate of the second point.</param> /// <param name="y2">The y-coordinate of the second point.</param> /// <returns></returns> 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 }}
//******************************************************************************************************
// 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
{
/// <summary>
/// Function definition for the inverse distance weighting algorithm.
/// </summary>
/// <param name="x">The x-coordinate of the point at which the value is to be calculated.</param>
/// <param name="y">The y-coordinate of the point at which the value is to be calculated.</param>
/// <returns>The calculated value at the given location.</returns>
public delegate double IDWFunc(double x, double y);
/// <summary>
/// Function defintion for calculating the distance between two points.
/// </summary>
/// <param name="x1">The x-coordinate of the first point.</param>
/// <param name="y1">The y-coordinate of the first point.</param>
/// <param name="x2">The x-coordinate of the second point.</param>
/// <param name="y2">The y-coordinate of the second point.</param>
/// <returns>The distance between the two given points.</returns>
public delegate double DistanceFunc(double x1, double y1, double x2, double y2);
/// <summary>
/// Represents a function for calculating values
/// at given coordinates based on sparse data sets.
/// </summary>
/// <remarks>
/// Usage is similar to the <see cref="PiecewiseLinearFunction"/> class.
/// </remarks>
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 ]
/// <summary>
/// Creates a new instance of the <see cref="InverseDistanceWeightingFunction"/> class.
/// </summary>
public InverseDistanceWeightingFunction()
{
m_distanceFunction = DefaultDistanceFunction;
m_power = 1;
}
#endregion
#region [ Properties ]
/// <summary>
/// Gets the collection of x-coordinates of points at which the values are known.
/// </summary>
public double[] XCoordinates
{
get
{
return m_xCoordinates;
}
}
/// <summary>
/// Gets the collection of y-coordinates of points at which the values are known.
/// </summary>
public double[] YCoordinates
{
get
{
return m_yCoordinates;
}
}
/// <summary>
/// Gets the collection of values of points at which the values are known.
/// </summary>
public double[] Values
{
get
{
return m_values;
}
}
/// <summary>
/// Gets the power applied to the inverse distance to control the speed of value's decay.
/// </summary>
public double Power
{
get
{
return m_power;
}
}
/// <summary>
/// Gets the function to be used to calculate the distance between two points.
/// </summary>
public DistanceFunc DistanceFunction
{
get
{
return m_distanceFunction;
}
}
#endregion
#region [ Methods ]
/// <summary>
/// Sets the collection of x-coordinates of points at which the values are known.
/// </summary>
/// <param name="xCoordinates">The x-coordinates of points at which the values are known.</param>
/// <returns>A reference to the inverse distance weighting function.</returns>
public InverseDistanceWeightingFunction SetXCoordinates(params double[] xCoordinates)
{
m_converter = null;
m_xCoordinates = xCoordinates;
return this;
}
/// <summary>
/// Sets the collection of y-coordinates of points at which the values are known.
/// </summary>
/// <param name="yCoordinates">The y-coordinates of points at which the values are known.</param>
/// <returns>A reference to the inverse distance weighting function.</returns>
public InverseDistanceWeightingFunction SetYCoordinates(params double[] yCoordinates)
{
m_converter = null;
m_yCoordinates = yCoordinates;
return this;
}
/// <summary>
/// Sets the collection of values of points at which the values are known.
/// </summary>
/// <param name="values">The values of points at which the values are known.</param>
/// <returns>A reference to the inverse distance weighting function.</returns>
public InverseDistanceWeightingFunction SetValues(params double[] values)
{
m_converter = null;
m_values = values;
return this;
}
/// <summary>
/// Sets the power applied to the inverse distance to control the speed of value's decay.
/// </summary>
/// <param name="power">The power applied to the inverse of the distance.</param>
/// <returns>A reference to the inverse distance weighting function.</returns>
/// <remarks>Larger values increase the speed of decay such that a known value affects a smaller area.</remarks>
public InverseDistanceWeightingFunction SetPower(double power)
{
m_converter = null;
m_power = power;
return this;
}
/// <summary>
/// Sets the function to be used to calculate the distance between two points.
/// </summary>
/// <param name="distanceFunction">The function used to calculate distance between two points.</param>
/// <returns>A reference to the inverse distance weighting function.</returns>
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 ]
/// <summary>
/// Converts the <see cref="InverseDistanceWeightingFunction"/>
/// object to an <see cref="IDWFunc"/> to start converting values.
/// </summary>
/// <param name="idwFunction">The inverse distance wieghting function to be converted.</param>
/// <exception cref="InvalidOperationException">
/// <para>the number of x-coordinates does not equal the number of y-coordinates</para>
/// <para>- or -</para>
/// <para>the number of coordinates does not equal the number of values</para>
/// </exception>
public static implicit operator IDWFunc(InverseDistanceWeightingFunction idwFunction)
{
return idwFunction.GetConverter();
}
#endregion
#region [ Static ]
// Static Methods
/// <summary>
/// Calculates the distance between two points.
/// </summary>
/// <param name="x1">The x-coordinate of the first point.</param>
/// <param name="y1">The y-coordinate of the first point.</param>
/// <param name="x2">The x-coordinate of the second point.</param>
/// <param name="y2">The y-coordinate of the second point.</param>
/// <returns></returns>
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
}
}