diff --git a/src/utils.jl b/src/utils.jl index 5edc0402..926e02fa 100644 --- a/src/utils.jl +++ b/src/utils.jl @@ -1137,12 +1137,40 @@ end """ InterpolateDataFields2D(V::CartData, X, Y) -Interpolates a data field `V` on a 2D CartData grid defined. Typically used for horizontal surfaces +Interpolates a data field `V` on a 2D CartData grid defined by `X`,`Y`. Typically used for horizontal surfaces """ function InterpolateDataFields2D(V::CartData, X, Y) X_vec = V.x.val[:,1,1]; Y_vec = V.y.val[1,:,1]; - return InterpolateDataFields2D_vecs(X_vec, Y_vec, V.depth, V.fields, X, Y) + return InterpolateDataFields2D_vecs(X_vec, Y_vec, V.z, V.fields, X, Y) +end + +""" + InterpolateDataFields2D(Original::CartData, New::CartData; Rotate=0.0, Translate=(0,0,0), Scale=(1.0,1.0,1.0)) + +Interpolates a data field `Original` on a 2D CartData grid `New`. +Typically used for horizontal surfaces. + +Note: `Original` should have orthogonal coordinates. If it has not, e.g., because it was rotated, you'll have to specify the angle `Rotate` that it was rotated by + +""" +function InterpolateDataFields2D(Original::CartData, New::CartData; Rotate=0.0, Translate=(0.0,0.0,0.0), Scale=(1.0,1.0,1.0)) + if (Rotate!=0.0) || Any(Translate .!= (0,0,0)) || Any(Scale .!= (1.0,1.0,1.0)) + Original_r = RotateTranslateScale(Original, Rotate = -1.0*Rotate, Translate = -1.0.*Translate, Scale=-1.0.*Scale); + New_r = RotateTranslateScale(New, Rotate = -1.0*Rotate, Translate = -1.0.*Translate, Scale=-1.0.*Scale); + else + Original_r = Original; + New_r = New; + end + + X_vec = Original_r.x.val[:,1,1]; + Y_vec = Original_r.y.val[1,:,1]; + + Xnew = New_r.x.val + Ynew = New_r.y.val + Znew, fields_new = InterpolateDataFields2D_vecs(X_vec, Y_vec, Original_r.z, Original_r.fields, Xnew, Ynew) + + return CartData(New.x.val,New.y.val,Znew, fields_new) end @@ -1156,9 +1184,10 @@ function InterpolateDataFields2D_vecs(EW_vec, NS_vec, depth, fields_new, EW, NS) # fields_new = V.fields; field_names = keys(fields_new); for i = 1:length(fields_new) - if typeof(fields_new[i]) <: Tuple + data_tuple = fields_new[i] + + if (typeof(data_tuple) <: Tuple) & (!contains(String(field_names[i]),"colors")) # vector or anything that contains more than 1 field - data_tuple = fields_new[i] # we have a tuple (likely a vector field), so we have to loop data_array = zeros(size(EW,1),size(EW,2),size(EW,3),length(data_tuple)); # create a 3D array that holds the 2D interpolated values unit_array = zeros(size(data_array)); @@ -1168,12 +1197,36 @@ function InterpolateDataFields2D_vecs(EW_vec, NS_vec, depth, fields_new, EW, NS) end data_new = tuple([data_array[:,:,1,c] for c in 1:size(data_array,4)]...) # transform 3D matrix to tuple + elseif contains(String(field_names[i]),"colors") + # This is a 3D matrix. We need to interpolate each color channel separately, while using nearest neighbour + # as you don't want to average colors + + # use nearest neighbour to interpolate data + X,Y,_ = XYZGrid(EW_vec, NS_vec, depth.val[1]); + + coord = [vec(X)'; vec(Y)']; + kdtree = KDTree(coord; leafsize = 10); + points = [vec(EW)';vec(NS)']; + idx,dist = nn(kdtree, points); + I = CartesianIndices(axes(X)) + I_idx = I[idx] + I_loc = CartesianIndices(axes(EW)) + + data_array = zeros(size(EW,1),size(EW,2),size(EW,3),length(data_tuple)); # create a 3D array that holds the 2D interpolated values + for (n,i) in enumerate(I_loc) + ix,iy = i[1],i[2] + for j=1:length(data_tuple) + data_array[ix,iy,1,j] = data_tuple[j][I_idx[n]] + end + end + data_new = tuple([data_array[:,:,1,c] for c in 1:size(data_array,4)]...) # transform 3D matrix to tuple + else # scalar field - if length(size(fields_new[i]))==3 - interpol = linear_interpolation((EW_vec, NS_vec), fields_new[i][:,:,1], extrapolation_bc = Flat()); # create interpolation object + if length(size(data_tuple))==3 + interpol = linear_interpolation((EW_vec, NS_vec), data_tuple[:,:,1], extrapolation_bc = Flat()); # create interpolation object else - interpol = linear_interpolation((EW_vec, NS_vec), fields_new[i], extrapolation_bc = Flat()); # create interpolation object + interpol = linear_interpolation((EW_vec, NS_vec), data_tuple, extrapolation_bc = Flat()); # create interpolation object end data_new = interpol.(EW, NS); # interpolate data field @@ -1702,7 +1755,7 @@ function VoteMap(DataSets::GeoData, criteria::String; dims=(50,50,50)) end """ - Data_R = RotateTranslateScale(Data::Union{ParaviewData, CartData}; Rotate=0, Translate=(0,0,0), Scale=(1.0,1.0,1.0)) + Data_R = RotateTranslateScale(Data::Union{ParaviewData, CartData}; Rotate=0, Translate=(0,0,0), Scale=(1.0,1.0,1.0), Xc=(0.0,0.0)) Does an in-place rotation, translation and scaling of the Cartesian dataset `Data`. @@ -1711,6 +1764,7 @@ Note that we apply the transformations in exactly this order: - `Scale`: scaling applied to the `x,y,z` coordinates of the data set - `Rotate`: rotation around the `x/y` axis (around the center of the box) - `Translate`: translation +- `Xc`: center of rotation # Example ```julia @@ -1732,7 +1786,7 @@ ParaviewData fields: (:Depth,) ``` """ -function RotateTranslateScale(Data::Union{ParaviewData, CartData}; Rotate=0, Translate=(0,0,0), Scale=(1.0,1.0,1.0)) +function RotateTranslateScale(Data::Union{ParaviewData, CartData}; Rotate::Number=0.0, Translate=(0,0,0), Scale=(1.0,1.0,1.0), Xc=(0.0,0.0)) X,Y,Z = copy(Data.x.val), copy(Data.y.val), copy(Data.z.val); # Extract coordinates Xr,Yr,Zr = X,Y,Z; # Rotated coordinates @@ -1747,13 +1801,13 @@ function RotateTranslateScale(Data::Union{ParaviewData, CartData}; Rotate=0, Tra # 2) 2D rotation around X/Y axis, around center of box - Xm,Ym = mean(X), mean(Y); - R = [cosd(Rotate[1]) -sind(Rotate[1]); sind(Rotate[1]) cosd(Rotate[1])]; # 2D rotation matrix + Xm,Ym = 0.0, 0.0; + R = [cosd(Rotate) -sind(Rotate); sind(Rotate) cosd(Rotate)]; # 2D rotation matrix for i in eachindex(X) - Rot_XY = R*[X[i]-Xm; Y[i]-Ym]; - Xr[i] = Rot_XY[1] + Xm; - Yr[i] = Rot_XY[2] + Ym; + Rot_XY = R*[X[i]-Xc[1]; Y[i]-Xc[2]]; + Xr[i] = Rot_XY[1] + Xc[1]; + Yr[i] = Rot_XY[2] + Xc[2]; end # 3) Add translation @@ -1762,15 +1816,11 @@ function RotateTranslateScale(Data::Union{ParaviewData, CartData}; Rotate=0, Tra Zr .+= Translate[3]; # Modify original structure - #Data.x.val = Xr; - #Data.y.val = Yr; - #Data.z.val = Zr; if isa(Data, ParaviewData) Data_r = ParaviewData(Xr,Yr,Zr, Data.fields) else Data_r = CartData(Xr,Yr,Zr, Data.fields) end - return Data_r end diff --git a/test/test_utils.jl b/test/test_utils.jl index 0b907087..573fe49d 100644 --- a/test/test_utils.jl +++ b/test/test_utils.jl @@ -200,15 +200,14 @@ Data_VoteMap = VoteMap([Data_set3D_reverse, Data_set3D], ["Depthdata<-560","LonD X,Y,Z = LonLatDepthGrid(10:20,30:40,-50:-10); Data_C = ParaviewData(X,Y,Z,(Depth=Z,)) Data_C1 = RotateTranslateScale(Data_C, Rotate=30); -@test Data_C1.x.val[10] ≈ 20.964101615137753 -@test Data_C1.y.val[10] ≈ 32.66987298107781 +@test Data_C1.x.val[10] ≈ 1.4544826719043336 +@test Data_C1.y.val[10] ≈ 35.48076211353316 @test Data_C1.z.val[20] == -50 Data_C1 = RotateTranslateScale(Data_C, Scale=10, Rotate=10, Translate=(1,2,3)); -#@test Data_C1.x.val[10] ≈ 213.78115820908607 -#@test Data_C1.y.val[10] ≈ 339.4092822315127 -#@test Data_C1.z.val[20] == -497.0 - +@test Data_C1.x.val[10] ≈ 136.01901977224043 +@test Data_C1.y.val[10] ≈ 330.43547966037914 +@test Data_C1.z.val[20] == -497.0 # create point data set (e.g. Earthquakes) Lon,Lat,Depth = LonLatDepthGrid(15:0.05:17,35:0.05:37,280km);