bilinearInterpolator.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import csv
  2. import math
  3. import numpy as np
  4. class bilinearInterpolator():
  5. """
  6. This class takes a collection of 3-dimensional points from a .csv file.
  7. It contains a bilinear interpolator to find unknown points within the grid.
  8. """
  9. @property
  10. def probedGrid(self):
  11. return self._probedGrid
  12. """
  13. Constructor takes a file with a .csv extension and creates an evenly-spaced 'ideal' grid from the data points.
  14. This is done to get around any floating point errors that may exist in the data
  15. """
  16. def __init__(
  17. self,
  18. pointsFile
  19. ):
  20. self.pointsFile = pointsFile
  21. self.points = np.loadtxt(self.pointsFile, delimiter=',')
  22. self.xMin, self.xMax, self.xSpacing, self.xCount = self._axisParams(0)
  23. self.yMin, self.yMax, self.ySpacing, self.yCount = self._axisParams(1)
  24. # generate ideal grid to match actually probed points -- this is due to floating-point error issues
  25. idealGrid = ([
  26. [(x,y) for x in np.linspace(self.xMin,self.xMax,self.xCount, True)]
  27. for y in np.linspace(self.yMin,self.yMax,self.yCount, True)
  28. ])
  29. self._probedGrid = [[0] * self.yCount for i in range(0, self.xCount)]
  30. # align ideal grid indices with probed data points
  31. for rowIndex, row in enumerate(idealGrid):
  32. for colIndex, idealPoint in enumerate(row):
  33. minSqDist = math.inf
  34. for probed in self.points:
  35. # find closest point in ideal grid that corresponds to actual tested point
  36. # put z value in correct index
  37. sqDist = pow(probed[0] - idealPoint[0], 2) + pow(probed[1] - idealPoint[1],2)
  38. if sqDist <= minSqDist:
  39. minSqDist = sqDist
  40. indexX = rowIndex
  41. indexY = colIndex
  42. closestProbed = probed
  43. self.probedGrid[indexY][indexX] = closestProbed
  44. """
  45. Bilinear interpolation method to determine unknown z-values within grid of known z-values.
  46. NOTE: If one axis is outside the grid, linear interpolation is used instead.
  47. If both axes are outside of the grid, the z-value of the closest corner of the grid is returned.
  48. """
  49. def Interpolate(self, point):
  50. lin = False
  51. if point[0] < self.xMin:
  52. ix1 = 0
  53. lin = True
  54. elif point[0] > self.xMax:
  55. ix1 = self.xCount-1
  56. lin = True
  57. else:
  58. ix1 = math.floor((point[0] - self.xMin)/self.xSpacing)
  59. ix2 = math.ceil((point[0] - self.xMin)/self.xSpacing)
  60. def interpolatePoint(p1, p2, p, axis):
  61. return (p2[2]*(p[axis] - p1[axis]) + p1[2]*(p2[axis] - p[axis]))/(p2[axis] - p1[axis])
  62. if point[1] < self.yMin:
  63. if lin:
  64. return self.probedGrid[ix1][0][2]
  65. return interpolatePoint(self.probedGrid[ix1][0], self.probedGrid[ix2][0], point, 0)
  66. elif point[1] > self.yMax:
  67. if lin:
  68. return self.probedGrid[ix1][self.yCount - 1][2]
  69. return interpolatePoint(self.probedGrid[ix1][self.yCount - 1], self.probedGrid[ix2][self.yCount - 1], point, 0)
  70. else:
  71. iy1 = math.floor((point[1] - self.yMin)/self.ySpacing)
  72. iy2 = math.ceil((point[1] - self.yMin)/self.ySpacing)
  73. #if x was at an extrema, but y was not, perform linear interpolation on x axis
  74. if lin:
  75. return interpolatePoint(self.probedGrid[ix1][iy1], self.probedGrid[ix1][iy2], point, 1)
  76. def specialDiv(a, b):
  77. if b == 0:
  78. return 0.5
  79. else:
  80. return a/b
  81. x1 = self.probedGrid[ix1][iy1][0]
  82. x2 = self.probedGrid[ix2][iy1][0]
  83. y1 = self.probedGrid[ix2][iy1][1]
  84. y2 = self.probedGrid[ix2][iy2][1]
  85. Q11 = self.probedGrid[ix1][iy1][2]
  86. Q12 = self.probedGrid[ix1][iy2][2]
  87. Q21 = self.probedGrid[ix2][iy1][2]
  88. Q22 = self.probedGrid[ix2][iy2][2]
  89. r1 = specialDiv(point[0]-x1, x2-x1)*Q21 + specialDiv(x2-point[0], x2-x1)*Q11
  90. r2 = specialDiv(point[0]-x1, x2-x1)*Q22 + specialDiv(x2-point[0], x2-x1)*Q12
  91. p = specialDiv(point[1]-y1, y2-y1)*r2 + specialDiv(y2-point[1], y2-y1)*r1
  92. return p
  93. # Returns the min, max, spacing and size of one axis of the 2D grid
  94. def _axisParams(self, sortAxis):
  95. # sort the set and eliminate the previous, unsorted set
  96. srtSet = sorted(self.points, key=lambda x: x[sortAxis])
  97. dists = []
  98. for item0, item1 in zip(srtSet[:(len(srtSet)-2)], srtSet[1:]):
  99. dists.append(float(item1[sortAxis]) - float(item0[sortAxis]))
  100. axisSpacing = max(dists)
  101. # add an extra one for axisCount to account for the starting point
  102. axisMin = float(min(srtSet, key=lambda x: x[sortAxis])[sortAxis])
  103. axisMax = float(max(srtSet, key=lambda x: x[sortAxis])[sortAxis])
  104. axisRange = axisMax - axisMin
  105. axisCount = round((axisRange/axisSpacing) + 1)
  106. return axisMin, axisMax, axisSpacing, axisCount