1

I have a grid with some given data. This data is given by its angle (from 0 to π). Within this grid I have another smaller grid.

This might look like this:

enter image description here

Now I want to interpolate the angles on that grid.

I tried this by using scipy.interpolate.griddata what gives a good result. But there is a problem when the angles change from almost 0 to almost π (because the middle is π/2 ...)

Here is the result and it is easy to see what's going wrong.

enter image description here

How can I deal with that problem? Thank you! :)

Here is the code to reproduce:

import numpy as np
from matplotlib import pyplot as plt
from scipy.interpolate import griddata

ax = plt.subplot()
ax.set_aspect(1)

# Simulate some given data.
x, y = np.meshgrid(np.linspace(-10, 10, 20), np.linspace(-10, 10, 20))
data = np.arctan(y / 10) % np.pi
u = np.cos(data)
v = np.sin(data)

ax.quiver(x, y, u, v, headlength=0.01, headaxislength=0, pivot='middle', units='xy')

# Create a smaller grid within.
x1, y1 = np.meshgrid(np.linspace(-1, 5, 15), np.linspace(-6, 2, 20))
# ax.plot(x1, y1, '.', color='red', markersize=2)

# Interpolate data on grid.
interpolation = griddata((x.flatten(), y.flatten()), data.flatten(), (x1.flatten(), y1.flatten()))
u1 = np.cos(interpolation)
v1 = np.sin(interpolation)
ax.quiver(x1, y1, u1, v1, headlength=0.01, headaxislength=0, pivot='middle', units='xy',
          color='red', scale=3, width=0.03)

plt.show()

Edit:

Thanks to @bubble, there is a way to adjust the given angles before interpolation such that the result will be as desired. Therefore:

  1. Define a rectifying function:

    def RectifyData(data):
        for j in range(len(data)):
            step = data[j] - data[j - 1]
            if abs(step) > np.pi / 2:
                data[j] += np.pi * (2 * (step < 0) - 1)
        return data
    
  2. Interpolate as follows:

    interpolation = griddata((x.flatten(), y.flatten()),
                             RectifyData(data.flatten()),
                             (x1.flatten(), y1.flatten()))
    u1 = np.cos(interpolation)
    v1 = np.sin(interpolation)
    
Max16hr
  • 438
  • 2
  • 5
  • 20

2 Answers2

1

I tried direct interpolation of cos(angle) and sin(angle) values, but this still yielded to discontinues, that cause wrong line directions. The main idea consist in reducing discontinues, e.g. [2.99,3.01, 0.05,0.06] should be transformed to something like this: [2.99, 3.01, pi+0.05, pi+0.06]. This is needed to apply 2D interpolation algorithm correctly. Almost the same problem raises in the following post.

def get_rectified_angles(u, v):
    angles = np.arcsin(v)
    inds = u < 0
    angles[inds] *= -1
# Direct approach of removing discontinues 
#     for j in range(len(angles[1:])):  
#         if abs(angles[j] - angles[j - 1]) > np.pi / 2:
#             sel = [abs(angles[j] + np.pi - angles[j - 1]), abs(angles[j] - np.pi - angles[j-1])]
#             if np.argmin(sel) == 0:
#                 angles[j] += np.pi
#             else:
#                 angles[j] -= np.pi
    return angles


ax.quiver(x, y, u, v, headlength=0.01, headaxislength=0, pivot='middle', units='xy')

# # Create a smaller grid within.
x1, y1 = np.meshgrid(np.linspace(-1, 5, 15), np.linspace(-6, 2, 20))

angles = get_rectified_angles(u.flatten(), v.flatten())

interpolation = griddata((x.flatten(), y.flatten()), angles, (x1.flatten(), y1.flatten()))
u1 = np.cos(interpolation)
v1 = np.sin(interpolation)
ax.quiver(x1, y1, u1, v1, headlength=0.01, headaxislength=0, pivot='middle', units='xy',
          color='red', scale=3, width=0.03)

Probably, numpy.unwrap function could be used to fix discontinues. In case of 1d data, numpy.interp has keyword period to handle periodic data.

enter image description here

bubble
  • 1,634
  • 12
  • 17
  • Hmm, unfortunatley this is still not working properly. To see that, change the simulated data to `data = np.arctan(y / 2) % np.pi`. The angle is still too vertical, especially if you normalize the data. – Max16hr Apr 29 '19 at 13:13
  • This causes new discontinuities along the vertical axes. For that, try new data: `data = (np.arctan(y / 2) + np.pi / 2) % np.pi` – Max16hr Apr 30 '19 at 12:55
  • Ah, but the direct approach seems to be working! :) I'll try to understand, what is going on there. Thank you for all our efforts! – Max16hr Apr 30 '19 at 12:57
0
Import libraries
# cmath is mathematical functions for complex numbers
import cmath 
import numpy as np
from scipy.interpolate import griddata 
Let's work with this sample dataset
# example dataset
x = [1,2, 1, 2]
y = [1, 1, 2, 2]
xi = [1,   1.5,  2,   1.5]
yi = [1.5, 1,    1.5, 2]
angles = [30, 350, 270, 130]

Angles on 4 blue points are going to be interpolated on 4 red points as shown in the figure: enter image description here

Interpolate
# initialize an empty list named cmplx to store values 
cmplx=[]

# represent angles with unit vectors using complex numbers 
# i.e. radius=1, angle=a (angle needs to be in radians).
for a in angles:
    cmplx.append(cmath.rect( 1, np.deg2rad(a) ))

# interpolate values to grid (xi, yi)
cmplx_nums = griddata((x, y), cmplx, (xi, yi))

# phase of the complex number is the angle in radians. 
# convert to degrees and use modulus operator to complement to 360 i.e % 360 
angles_2 =[]
for cmplx in cmplx_nums:
    angle_rad = cmath.phase(cmplx)
    angles_2.append(np.rad2deg(angle_rad) % 360)
Print result
for x in angles_2:
print('{:.0f}'.format(x))

330
10
60
200
ZZZ
  • 704
  • 9
  • 18