//******************************************************************************************************
// 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
}
}