Using xarray interp to reproject a dataarray? - raster
I have looked a lot at the xarray documentation of the interp function and I cannot really make sense of it. I see it is a reprojection but it doesn't really fit a real case example.
Is their someone that could make sense of it for example by reprojecting this dataset on a webmercator datum?
Something like the example:
import xarray as xr
from pyproj import Transformer
ds = xr.tutorial.open_dataset("air_temperature").isel(time=0)
fig, axes = plt.subplots(ncols=2, figsize=(10, 4))
lon, lat = np.meshgrid(ds.lon, ds.lat)
shp = lon.shape
# reproject the grid
gcs_to_3857 = Transformer.from_crs(4326, 3857, always_xy=True)
x, y = gcs_to_3857.transform(lon.ravel(), lat.ravel())
# future index for a regular raster
X= np.linspace(x.min(), x.max(), shp[1])
Y= np.linspace(y.min(), y.max(), shp[0])
data["x"] = xr.DataArray(np.reshape(x, shp), dims=("lat", "lon"))
data["y"] = xr.DataArray(np.reshape(y, shp), dims=("lat", "lon"))
And here, I am stuck
Should be something like ds.interp(x=X,y=Y) but the array is indexed on lat lon
It is all a bit confusing to me...
You could also use reproject from rioxarray as suggested.
Here is the code:
import xarray as xr
import numpy as np
from rasterio.enums import Resampling
import matplotlib.pyplot as plt
ds = xr.tutorial.open_dataset('air_temperature').isel(time=0)
ds = ds.rio.write_crs('EPSG:4326')
dst = ds.rio.reproject('EPSG:3857', shape=(250, 250), resampling=Resampling.bilinear, nodata=np.nan)
fig, ax = plt.subplots(1, 2, figsize=(14, 5))
ds.air.plot(ax=ax[0])
dst.air.plot(ax=ax[1])
I think the idea is something like this:
In [1]: import xarray as xr
...: import numpy as np
...: from pyproj import Transformer
...:
...: ds = xr.tutorial.open_dataset("air_temperature").isel(time=0)
design a target grid in transformed space:
In [2]: # find the new bounds in mercator space
...: gcs_to_3857 = Transformer.from_crs(4326, 3857, always_xy=True)
...: x, y = gcs_to_3857.transform(
...: [ds.lon.min(), ds.lon.max()],
...: [ds.lat.min(), ds.lat.max()],
...: )
In [3]: # design a target grid for the re-projected data - can be any dims you want
...: X = np.linspace(x[0], x[1], 500)
...: Y = np.linspace(y[0], y[1], 600)
...: XX, YY = np.meshgrid(X, Y)
Transform this grid back into lat/lon
In [4]: # build a reverse transformer from Mercator back to lat/lons
...: merc_to_latlng = Transformer.from_crs(3857, 4326, always_xy=True)
...: new_lons, new_lats = merc_to_latlng.transform(XX.ravel(), YY.ravel())
...: new_lons = new_lons.reshape(XX.shape)
...: new_lats = new_lats.reshape(YY.shape)
Create new DataArrays to index the lat/lon values corresponding to the grid points on the target grid (indexed by x and y in Mercator space):
In [5]: # create arrays indexed by (x, y); also convert lons back to (0, 360)
...: new_lons_da = xr.DataArray((new_lons % 360), dims=["y", "x"], coords=[Y, X])
...: new_lats_da = xr.DataArray(new_lats, dims=["y", "x"], coords=[Y, X])
Use xarray's advanced indexing to interpolate the data to the new points while re-indexing onto the new grid
In [6]: ds_mercator = ds.interp(lon=new_lons_da, lat=new_lats_da, method="linear")
Now the data is indexed by x and y, with points equally spaced in Mercator space:
In [7]: ds_mercator
Out[7]:
<xarray.Dataset>
Dimensions: (y: 600, x: 500)
Coordinates:
time datetime64[ns] 2013-01-01
lon (y, x) float64 200.0 200.3 200.5 200.8 ... 329.2 329.5 329.7 330.0
lat (y, x) float64 15.0 15.0 15.0 15.0 15.0 ... 75.0 75.0 75.0 75.0
* y (y) float64 1.689e+06 1.708e+06 1.727e+06 ... 1.291e+07 1.293e+07
* x (x) float64 -1.781e+07 -1.778e+07 ... -3.369e+06 -3.34e+06
Data variables:
air (y, x) float64 nan nan nan nan nan ... 237.3 237.6 238.0 238.3 nan
Attributes:
Conventions: COARDS
title: 4x daily NMC reanalysis (1948)
description: Data is from NMC initialized reanalysis\n(4x/day). These a...
platform: Model
references: http://www.esrl.noaa.gov/psd/data/gridded/data.ncep.reanaly...
The new projection can be seen in the axes (and distortion) of the transformed (right) as compared to the original (left) datasets:
In [8]: fig, axes = plt.subplots(1, 2, figsize=(14, 5))
...: ds.air.plot(ax=axes[0])
...: ds_mercator.air.plot(ax=axes[1])
Out[8]: <matplotlib.collections.QuadMesh at 0x2b3b94be0
Related
Change longitude range of Kepler.gl map in order to make polygons around long 180 and -180 be continuous
I'm analyzing a region that goes from longitude 160 to 180 and from -180 to 0, but it's a continous region. As the standard map in Kepler.gl goes from -180 to 180, it shows one part at the beginning of the map and another at the end, as the image below shows. Which kleper.gl's parameter do I have to change in order to show this two parts together and in a continous way? I'm plotting it using python and jupyter notebook. The following code shows a simple example: from shapely.geometry import Polygon from shapely.ops import unary_union from keplergl import KeplerGl poly1 = Polygon([(-180, 20), (-160, 20), (-160, 0), (-180, 0)]) poly2 = Polygon([(180, 20), (160, 20), (160, 0), (180, 0)]) merged_poly = unary_union([poly1,poly2]).simplify(0,preserve_topology=True) geodf = geopandas.GeoDataFrame(columns=['id', 'amount', 'geometry' ], geometry='geometry') geodf = geodf.append({'id': 1, 'amount': 1000,'geometry':simple_poly}, ignore_index=True) geodf = geodf.set_geometry('geometry') m = KeplerGl(height=500) m.add_data(data=geodf.to_json(), name='test') m
Julia BoundsError with less information - don't know why
I have the following code: # package for ploting functions using Plots # use GR gr() # nb points to plot nbPts = 22 # define polar coordinates of a 30 degree (pi/6) rotation sine = sin(pi/6) cosine = cos(pi/6) # scale factor scale_factor = 0.9 #--------------------------------------- # 1. PLOT POINTS USING ROTATION MATRIX #--------------------------------------- # define Rotation matrix ( angle = pi/6, center = (0, 0) ) R = zeros(Float64, 2, 2) R[1,1] = R[2,2]= cosine R[1,2] = -sine R[2,1] = sine # Scale matrix ### ... <-- EXERCISE 4(c): define a uniform scaling matrix (use scale_factor) # arrays of points coords X_mat = zeros(nbPts) Y_mat= zeros(nbPts) # first Point (1,0) X_mat[1] = 1.0 Y_mat[1] = 0.0 for i in 2:nbPts prevPoint = [X_mat[i-1], Y_mat[i-1]] #apply rotation to previous point to obtain new point newPoint = R * prevPoint ### ... <-- EXERCISE 4(c): apply scaling matrix X_mat[i] = newPoint[1] Y_mat[i] = newPoint[2] end # plot points in blue plt1 = scatter(X_mat, Y_mat, color=:blue, xlim = (-1.1, 1.1), ylim = (-1.1, 1.1), label=false, title="Rotation using matrices" ); #--------------------------------------- # 2. PLOT POINTS USING COMPLEX NUMBERS #--------------------------------------- function ComplexProduct(z, w) (((z[1]*w[1])+(z[2]*w[2])),((z[1]*w[2])+(z[2]*w[1]))) ### ... <-- EXERCISE 4(b): implement complex product z * w end # first point: z = 1 + 0 * i Z = ( 1.0, 0.0 ) # second point: w = cosine( pi/6) + sine( pi/6) * i W = ( cosine, sine ) ### ... <-- EXERCISE 4(c): apply scale_factor to W # arrays of points coords X_comp = zeros(nbPts) Y_comp = zeros(nbPts) # first Point (1,0) X_comp[1] = Z[1] Y_comp[1] = Z[2] for i in 2:nbPts prevPoint = (X_comp[i-1], Y_comp[i-1]) newPoint = ComplexProduct(prevPoint[1], prevPoint[2]) ### <-- EXERCISE 4(b): compute newPoint by applying rotation to prevPoint (use complex product) X_comp[i] = newPoint[1] Y_comp[i] = newPoint[2] end # plot points in red plt2 = scatter(X_comp, Y_comp, color=:red, xlim = (-1.1, 1.1), ylim = (-1.1, 1.1), label=false, title="Rotation using complex numbers" ); # arrange and display display( plot( plt1, plt2, layout = (1, 2), size=(600*2, 600) )) The Error: The Thing I want: I have to implement a product of complex numbers and this should be used to calculate the rotation with complex numbers. Should look like that: What do I have to change so that the BoundsError is fixed? Don't know what exactly i do wrong because of the poorly information i get from this error log. Greetings and thanks for the help.
prevPoint[1] is a scalar while your function ComplexProduct expects something that has 2 elements. Perhaps you wanted to pass prevPoint instead of prevPoint[1]? BTW you use incorrect naming pattern. CamelNaming is discouraged for Julia. Your variable should be named prev_point and your function should be named complex_product.
Fixed the bug by changing the following code: newPoint = ComplexProduct(prevPoint, W) in line 92
How to change length of a side of polygon ensure the resulting polygon is similar shape
Polygon I have a polygon that has only 90 and 45-degree angles, now I want to change the length of any side and adjust the polygon so that results should be a similar polygon. Like this: Shape after side change
import numpy as np import matplotlib.pyplot as plt # an arbitrary polygon, for example a pentagon: P = np.array([[0.2, 0.1], [1, -0.2], [3, 1], [2, 0.7], [0.5, 1], [0.2, 0.1]]) P[-1, ] = P[0, ] # pick a side between, say the one between vertices P[i,] and P[i+1,], # and assume you want to make it of length a = 3: i=2 a=3 # pick a point V0 in the plane, where a vertex P[j,] of the scaled polygon should be places: j=1 v0 = [1,1] # calculate the scale, which is a / (length of edge P[i,] P[i+1,]): scale = a / np.linalg.norm(P[i+1, ] - P[i,]) # scale and position the polygon where you want it to be: P_scaled = scale * P + (v0 - P[j,]) # plot polygon P: plt.figure() plt.plot(P[:,0], P[:,1]) for k in range(P.shape[0]): plt.plot(P[k,0], P[k,1], 'ro') # plot polygon P_scaled: plt.plot(P_scaled[:,0], P_scaled[:,1]) for k in range(P_scaled.shape[0]): plt.plot(P_scaled[k,0], P_scaled[k,1], 'ro') axx = plt.gca() axx.set_aspect('equal') plt.show()
3D with value interpolation in R (X, Y, Z, V)
Is there an R package that does X, Y, Z, V interpolation? I see that Akima does X, Y, V but I need one more dimension. Basically I have X,Y,Z coordinates plus the value (V) that I want to interpolate. This is all GIS data but my GIS does not do voxel interpolation So if I have a point cloud of XYZ coordinates with a value of V, how can I interpolate what V would be at XYZ coordinate (15,15,-12) ? Some test data would look like this: X <-rbind(10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,10,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,20,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,30,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,40,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50,50) Y <- rbind(10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50,10,10,10,10,10,20,20,20,20,20,30,30,30,30,30,40,40,40,40,40,50,50,50,50,50) Z <- rbind(-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-5,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-17,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29,-29) V <- rbind(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,25,35,75,25,50,0,0,0,0,0,10,12,17,22,27,32,37,25,13,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,125,130,105,110,115,165,180,120,100,80,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0)
I had the same question and was hoping for an answer in R. My question was: How do I perform 3D (trilinear) interpolation using regular gridded coordinate/value data (x,y,z,v)? For example, CT images, where each image has pixel centers (x, y) and greyscale value (v) and there are multiple image "slices" (z) along the thing being imaged (e.g., head, torso, leg, ...). There is a slight problem with the given example data. # original example data (reformatted) X <- rep( rep( seq(10, 50, by=10), each=25), 3) Y <- rep( rep( seq(10, 50, by=10), each=5), 15) Z <- rep(c(-5, -17, -29), each=125) V <- rbind(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,25,35,75,25,50,0,0,0,0,0,10,12,17,22,27,32,37,25,13,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,50,125,130,105,110,115,165,180,120,100,80,60,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0) # the dimensions of the 3D grid described do not match the number of values (length(unique(X))*length(unique(Y))*length(unique(Z))) == length(V) ## [1] FALSE ## which makes sense since 75 != 375 # visualize this: library(rgl) plot3d(x=X, y=Y, z=Z, col=terrain.colors(181)[V]) # examine the example data real quick... df <- data.frame(x=X,y=Y,z=Z,v=V); head(df); table(df$x, df$y, df$z); # there are 5 V values at each X,Y,Z coordinate... duplicates! # redefine Z so there are 15 unique values # making 375 unique coordinate points # and matching the length of the given value vector, V df$z <- seq(-5, -29, length.out=15) head(df) table(df$x, df$y, df$z); # there is now 1 V value at each X,Y,Z coordinate # that was for testing, now actually redefine the Z vector. Z <- rep(seq(-5,-29, length.out = 15), 25) # plot it. library(rgl) plot3d(x=X, y=Y, z=Z, col=terrain.colors(181)[V]) I couldn't find any 4D interpolation functions in the usual R packages, so I wrote a quick and dirty one. The following implements (without ANY error checking... caveat emptor!) the technique described at: https://en.wikipedia.org/wiki/Trilinear_interpolation # convenience function #1: # define a function that takes a vector of lookup values and a value to lookup # and returns the two lookup values that the value falls between between = function(vec, value) { # extract list of unique lookup values u = unique(vec) # difference vector dvec = u - value vals = c(u[dvec==max(dvec[dvec<0])], u[dvec==min(dvec[dvec>0])]) return(vals) } # convenience function #2: # return the value (v) from a grid data.frame for given point (x, y, z) get_value = function(df, xi, yi, zi) { # assumes df is data.frame with column names: x, y, z, v subset(df, x==xi & y==yi & z==zi)$v } # inputs df (x,y,z,v), points to look up (x, y, z) interp3 = function(dfin, xin, yin, zin) { # TODO: check if all(xin, yin, zin) equals a grid point, if so just return the point value # TODO: check if any(xin, yin, zin) equals a grid point, if so then do bilinear or linear interp cube_x <- between(dfin$x, xin) cube_y <- between(dfin$y, yin) cube_z <- between(dfin$z, zin) # find the two values in each dimension that the lookup value falls within # and extract the cube of 8 points tmp <- subset(dfin, x %in% cube_x & y %in% cube_y & z %in% cube_z) stopifnot(nrow(tmp)==8) # define points in a periodic and cubic lattice x0 = min(cube_x); x1 = max(cube_x); y0 = min(cube_y); y1 = max(cube_y); z0 = min(cube_z); z1 = max(cube_z); # define differences in each dimension xd = (xin-x0)/(x1-x0); # 0.5 yd = (yin-y0)/(y1-y0); # 0.5 zd = (zin-z0)/(z1-z0); # 0.9166666 # interpolate along x: v00 = get_value(tmp, x0, y0, z0)*(1-xd) + get_value(tmp,x1,y0,z0)*xd # 2.5 v01 = get_value(tmp, x0, y0, z1)*(1-xd) + get_value(tmp,x1,y0,z1)*xd # 0 v10 = get_value(tmp, x0, y1, z0)*(1-xd) + get_value(tmp,x1,y1,z0)*xd # 0 v11 = get_value(tmp, x0, y1, z1)*(1-xd) + get_value(tmp,x1,y1,z1)*xd # 65 # interpolate along y: v0 = v00*(1-yd) + v10*yd # 1.25 v1 = v01*(1-yd) + v11*yd # 32.5 # interpolate along z: return(v0*(1-zd) + v1*zd) # 29.89583 (~91.7% between v0 and v1) } > interp3(df, 15, 15, -12) [1] 29.89583 Testing that same source's assertion that trilinear is simply linear(bilinear(), bilinear()), we can use the base R linear interpolation function, approx(), and the akima package's bilinear interpolation function, interp(), as follows: library(akima) approx(x=c(-11.857143,-13.571429), y=c(interp(x=df[round(df$z,1)==-11.9,"x"], y=df[round(df$z,1)==-11.9,"y"], z=df[round(df$z,1)==-11.9,"v"], xo=15, yo=15)$z, interp(x=df[round(df$z,1)==-13.6,"x"], y=df[round(df$z,1)==-13.6,"y"], z=df[round(df$z,1)==-13.6,"v"], xo=15, yo=15)$z), xout=-12)$y # [1] 0.2083331 Checked another package to triangulate: library(oce) Vmat <- array(data = V, dim = c(length(unique(X)), length(unique(Y)), length(unique(Z)))) approx3d(x=unique(X), y=unique(Y), z=unique(Z), f=Vmat, xout=15, yout=15, zout=-12) [1] 1.666667 So 'oce', 'akima' and my function all give pretty different answers. This is either a mistake in my code somewhere, or due to differences in the underlying Fortran code in the akima interp(), and whatever is in the oce 'approx3d' function that we'll leave for another day. Not sure what the correct answer is because the MWE is not exactly "minimum" or simple. But I tested the functions with some really simple grids and it seems to give 'correct' answers. Here's one simple 2x2x2 example: # really, really simple example: # answer is always the z-coordinate value sdf <- expand.grid(x=seq(0,1),y=seq(0,1),z=seq(0,1)) sdf$v <- rep(seq(0,1), each=4) > interp3(sdf,0.25,0.25,.99) [1] 0.99 > interp3(sdf,0.25,0.25,.4) [1] 0.4 Trying akima on the simple example, we get the same answer (phew!): library(akima) approx(x=unique(sdf$z), y=c(interp(x=sdf[sdf$z==0,"x"], y=sdf[sdf$z==0,"y"], z=sdf[sdf$z==0,"v"], xo=.25, yo=.25)$z, interp(x=sdf[sdf$z==1,"x"], y=sdf[sdf$z==1,"y"], z=sdf[sdf$z==1,"v"], xo=.25, yo=.25)$z), xout=.4)$y # [1] 0.4 The new example data in the OP's own, accepted answer was not possible to interpolate with my simple interp3() function above because: (a) the grid coordinates are not regularly spaced, and (b) the coordinates to lookup (x1, y1, z1) lie outside of the grid. # for completeness, here's the attempt: options(scipen = 999) XCoor=c(78121.6235,78121.6235,78121.6235,78121.6235,78136.723,78136.723,78136.723,78136.8969,78136.8969,78136.8969,78137.4595,78137.4595,78137.4595,78125.061,78125.061,78125.061,78092.4696,78092.4696,78092.4696,78092.7683,78092.7683,78092.7683,78092.7683,78075.1171,78075.1171,78064.7462,78064.7462,78064.7462,78052.771,78052.771,78052.771,78032.1179,78032.1179,78032.1179) YCoor=c(5213642.173,523642.173,523642.173,523642.173,523594.495,523594.495,523594.495,523547.475,523547.475,523547.475,523503.462,523503.462,523503.462,523426.33,523426.33,523426.33,523656.953,523656.953,523656.953,523607.157,523607.157,523607.157,523607.157,523514.671,523514.671,523656.81,523656.81,523656.81,523585.232,523585.232,523585.232,523657.091,523657.091,523657.091) ZCoor=c(-3.0,-5.0,-10.0,-13.0,-3.5,-6.5,-10.5,-3.5,-6.5,-9.5,-3.5,-5.5,-10.5,-3.5,-5.5,-7.5,-3.5,-6.5,-11.5,-3.0,-5.0,-9.0,-12.0,-6.5,-10.5,-2.5,-3.5,-8.0,-3.5,-6.5,-9.5,-2.5,-6.5,-8.5) V=c(2.4000,30.0,620.0,590.0,61.0,480.0,0.3700,0.0,0.3800,0.1600,0.1600,0.9000,0.4100,0.0,0.0,0.0061,6.0,52.0,0.3400,33.0,235.0,350.0,9300.0,31.0,2100.0,0.0,0.0,10.5000,3.8000,0.9000,310.0,0.2800,8.3000,18.0) adf = data.frame(x=XCoor, y=YCoor, z=ZCoor, v=V) # the first y value looks like a typo? > head(adf) x y z v 1 78121.62 5213642.2 -3.0 2.4 2 78121.62 523642.2 -5.0 30.0 3 78121.62 523642.2 -10.0 620.0 4 78121.62 523642.2 -13.0 590.0 5 78136.72 523594.5 -3.5 61.0 6 78136.72 523594.5 -6.5 480.0 x1=198130.000 y1=1913590.000 z1=-8 > interp3(adf, x1,y1,z1) numeric(0) Warning message: In min(dvec[dvec > 0]) : no non-missing arguments to min; returning Inf
Whether the test data did or not make sense, I still needed an algorithm. Test data is just that, something to fiddle with and as a test data it was fine. I wound up programming it in python and the following code takes XYZ V and does a 3D Inverse Distance Weighted (IDW) interpolation where you can set the number of points used in the interpolation. This python recipe only interpolates to one point (x1, y1, z1) but it is easy enough to extend. import numpy as np import math #34 points XCoor=np.array([78121.6235,78121.6235,78121.6235,78121.6235,78136.723,78136.723,78136.723,78136.8969,78136.8969,78136.8969,78137.4595,78137.4595,78137.4595,78125.061,78125.061,78125.061,78092.4696,78092.4696,78092.4696,78092.7683,78092.7683,78092.7683,78092.7683,78075.1171,78075.1171,78064.7462,78064.7462,78064.7462,78052.771,78052.771,78052.771,78032.1179,78032.1179,78032.1179]) YCoor=np.array([5213642.173,523642.173,523642.173,523642.173,523594.495,523594.495,523594.495,523547.475,523547.475,523547.475,523503.462,523503.462,523503.462,523426.33,523426.33,523426.33,523656.953,523656.953,523656.953,523607.157,523607.157,523607.157,523607.157,523514.671,523514.671,523656.81,523656.81,523656.81,523585.232,523585.232,523585.232,523657.091,523657.091,523657.091]) ZCoor=np.array([-3.0,-5.0,-10.0,-13.0,-3.5,-6.5,-10.5,-3.5,-6.5,-9.5,-3.5,-5.5,-10.5,-3.5,-5.5,-7.5,-3.5,-6.5,-11.5,-3.0,-5.0,-9.0,-12.0,-6.5,-10.5,-2.5,-3.5,-8.0,-3.5,-6.5,-9.5,-2.5,-6.5,-8.5]) V=np.array([2.4000,30.0,620.0,590.0,61.0,480.0,0.3700,0.0,0.3800,0.1600,0.1600,0.9000,0.4100,0.0,0.0,0.0061,6.0,52.0,0.3400,33.0,235.0,350.0,9300.0,31.0,2100.0,0.0,0.0,10.5000,3.8000,0.9000,310.0,0.2800,8.3000,18.0]) def Distance(x1,y1,z1, Npoints): i=0 d=[] while i < 33: d.append(math.sqrt((x1-XCoor[i])*(x1-XCoor[i]) + (y1-YCoor[i])*(y1-YCoor[i]) + (z1-ZCoor[i])*(z1-ZCoor[i]) )) i = i + 1 distance=np.array(d) myIndex=distance.argsort()[:Npoints] weightedNum=0 weightedDen=0 for i in myIndex: weightedNum=weightedNum + (V[i]/(distance[i]*distance[i])) weightedDen=weightedDen + (1/(distance[i]*distance[i])) InterpValue=weightedNum/weightedDen return InterpValue x1=198130.000 y1=1913590.000 z1=-8 print(Distance(x1,y1,z1, 12))
about plot contour figure by using r code
I am a green-hand on R code. Now I meet some trouble in plotting contour figure by using R code. I have checked help(filled.contour) which tells that if you want to plot the contour, x,y should be both in ascending order. Actually, I receive the data randomly, like: latitude, longitude, value 37.651098 140.725082 9519 37.650765 140.725248 9519 37.692738 140.749118 23600 37.692737 140.749118 9911 37.692695 140.749107 16591 37.692462 140.74902 6350 37.692442 140.749052 5507 37.692413 140.749148 5476 37.692383 140.74929 7069 37.692357 140.749398 6152 37.692377 140.749445 6170 37.692355 140.749587 7163 37.692298 140.749672 6831 37.692292 140.749787 6194 37.692283 140.749903 6696 37.692342 140.750007 8204 37.692585 140.750037 2872 37.692648 140.749948 3907 37.692655 140.749827 4891 37.692667 140.749687 4899 How can I plot the contour figure!? Here is my code: args <- commandArgs(trailingOnly = TRUE) data1 <- args[1] outputDir <- args[2] outputFig = paste(outputDir, "Cs13x.jpeg",sep=""); jpeg(file = outputFig, width = 800,height=600, pointsize=20) pinkcol <- rgb(1,0.7,0.7) gpsdata <- read.table(file=data1,sep=" "); lat <- as.vector(gpsdata[,1]); lon <- as.vector(gpsdata[,2]); datas <- as.vector(gpsdata[,3]); datas <- abs(datas) #---Convert gpsdata into x,y coordinate---# # Convert degree into value lat_pi <- lat*pi/180; lon_pi <- lon*pi/180; # calculate the value into corresponding x,y coordinate x = cos(lat_pi) * cos(lon_pi); y = cos(lat_pi) * sin(lon_pi); #----------# dataMatrix = matrix(datas, nrow = length(datas), ncol=length(datas)); plot.new() filled.contour(sort(x),sort(y, decreasing = TRUE),dataMatrix, col = rainbow(100), main="Contour Figure of Cs13x"); (**WRONG HERE!!!**) dev.off() <-------------- FINISH LINE ----------->
The 'akima' package will do it. It is designed to handle irregularly spaced z values. The first two points were widely separated from the rest and that made the results from the whole dataset look rather sketchy, so I omitted them. require(akima) gps.interp <- with( gpsdata[-(1:2), ], interp(x=latitude, y=longitude, z=value)) contour(gps.interp)