| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126 |
- # import csv
- import math
- import numpy as np
- class bilinearInterpolator:
- """
- This class takes a collection of 3-dimensional points from a .csv file.
- It contains a bilinear interpolator to find unknown points within the grid.
- """
- @property
- def probedGrid(self):
- return self._probedGrid
- """
- Constructor takes a file with a .csv extension and creates an evenly-spaced 'ideal' grid from the data points.
- This is done to get around any floating point errors that may exist in the data
- """
- def __init__(self, pointsFile):
-
- self.pointsFile = pointsFile
- self.points = np.loadtxt(self.pointsFile, delimiter=',')
- self.xMin, self.xMax, self.xSpacing, self.xCount = self._axisParams(0)
- self.yMin, self.yMax, self.ySpacing, self.yCount = self._axisParams(1)
- # generate ideal grid to match actually probed points -- this is due to floating-point error issues
- idealGrid = ([
- [(x, y) for x in np.linspace(self.xMin, self.xMax, self.xCount, True)]
- for y in np.linspace(self.yMin, self.yMax, self.yCount, True)
- ])
- self._probedGrid = [[0] * self.yCount for i in range(0, self.xCount)]
- # align ideal grid indices with probed data points
- for rowIndex, row in enumerate(idealGrid):
- for colIndex, idealPoint in enumerate(row):
- minSqDist = math.inf
- for probed in self.points:
- # find closest point in ideal grid that corresponds to actual tested point
- # put z value in correct index
- sqDist = pow(probed[0] - idealPoint[0], 2) + pow(probed[1] - idealPoint[1], 2)
- if sqDist <= minSqDist:
- minSqDist = sqDist
- indexX = rowIndex
- indexY = colIndex
- closestProbed = probed
- self.probedGrid[indexY][indexX] = closestProbed
- def Interpolate(self, point):
- """
- Bilinear interpolation method to determine unknown z-values within grid of known z-values.
- NOTE: If one axis is outside the grid, linear interpolation is used instead.
- If both axes are outside of the grid, the z-value of the closest corner of the grid is returned.
- """
- lin = False
- if point[0] < self.xMin:
- ix1 = 0
- lin = True
- elif point[0] > self.xMax:
- ix1 = self.xCount-1
- lin = True
- else:
- ix1 = math.floor((point[0] - self.xMin)/self.xSpacing)
- ix2 = math.ceil((point[0] - self.xMin)/self.xSpacing)
- def interpolatePoint(p1, p2, pt, axis):
- return (p2[2]*(pt[axis] - p1[axis]) + p1[2]*(p2[axis] - pt[axis]))/(p2[axis] - p1[axis])
- if point[1] < self.yMin:
- if lin:
- return self.probedGrid[ix1][0][2]
- return interpolatePoint(self.probedGrid[ix1][0], self.probedGrid[ix2][0], point, 0)
- elif point[1] > self.yMax:
- if lin:
- return self.probedGrid[ix1][self.yCount - 1][2]
- return interpolatePoint(
- self.probedGrid[ix1][self.yCount - 1], self.probedGrid[ix2][self.yCount - 1], point, 0)
- else:
- iy1 = math.floor((point[1] - self.yMin)/self.ySpacing)
- iy2 = math.ceil((point[1] - self.yMin)/self.ySpacing)
- # if x was at an extrema, but y was not, perform linear interpolation on x axis
- if lin:
- return interpolatePoint(self.probedGrid[ix1][iy1], self.probedGrid[ix1][iy2], point, 1)
- def specialDiv(a, b):
- if b == 0:
- return 0.5
- else:
- return a/b
- x1 = self.probedGrid[ix1][iy1][0]
- x2 = self.probedGrid[ix2][iy1][0]
- y1 = self.probedGrid[ix2][iy1][1]
- y2 = self.probedGrid[ix2][iy2][1]
- Q11 = self.probedGrid[ix1][iy1][2]
- Q12 = self.probedGrid[ix1][iy2][2]
- Q21 = self.probedGrid[ix2][iy1][2]
- Q22 = self.probedGrid[ix2][iy2][2]
- r1 = specialDiv(point[0]-x1, x2-x1)*Q21 + specialDiv(x2-point[0], x2-x1)*Q11
- r2 = specialDiv(point[0]-x1, x2-x1)*Q22 + specialDiv(x2-point[0], x2-x1)*Q12
- p = specialDiv(point[1]-y1, y2-y1)*r2 + specialDiv(y2-point[1], y2-y1)*r1
-
- return p
- # Returns the min, max, spacing and size of one axis of the 2D grid
- def _axisParams(self, sortAxis):
- # sort the set and eliminate the previous, unsorted set
- srtSet = sorted(self.points, key=lambda x: x[sortAxis])
- dists = []
- for item0, item1 in zip(srtSet[:(len(srtSet)-2)], srtSet[1:]):
- dists.append(float(item1[sortAxis]) - float(item0[sortAxis]))
- axisSpacing = max(dists)
- # add an extra one for axisCount to account for the starting point
- axisMin = float(min(srtSet, key=lambda x: x[sortAxis])[sortAxis])
- axisMax = float(max(srtSet, key=lambda x: x[sortAxis])[sortAxis])
- axisRange = axisMax - axisMin
- axisCount = round((axisRange/axisSpacing) + 1)
- return axisMin, axisMax, axisSpacing, axisCount
|