Python Plotly - How to overlay grid lines on a contour plot? - contour

I am using python plotly and am trying to overlay grid lines on a contour plot. The following code does not generate an error but it also does not show grid lines. How can I make the grid lines show?

I didn't find a good way. The grid layer is there (you can see it if you add opacity<1 in your contour) but you cannot put the grid layer above the traces (or at least I didn't find a way reading the doc and the code).
I ended up rebuilding the grid on top:
def add_box(lfig, x, y):
lfig.add_trace(go.Scatter(
x=x,
y=y,
opacity=0.5,
marker_color='white',
line_width=1,
))
def compute_horizontal_lines(x_min, x_max, y_data):
x = np.tile([x_min, x_max, None], len(y_data))
y = np.ndarray.flatten(np.array([[a, a, None] for a in y_data]))
return x, y
def compute_vertical_lines(y_min, y_max, x_data):
y = np.tile([y_min, y_max, None], len(x_data))
x = np.ndarray.flatten(np.array([[a, a, None] for a in x_data]))
return x, y
hx, hy = compute_horizontal_lines(200, 20000, range(-150, 180, 30))
vrange = [100*i for i in range(3, 9)] + [1000*i for i in range(1, 10)] + [10000+1000*i for i in range(1, 5)]
vx, vy = compute_vertical_lines(-180, 180, vrange)
add_box(fig, hx, hy)
add_box(fig, vx, vy)
fig.show()
which give me something like:
contour plot with a grid on top

Related

From numpy to geotif having a georeferenced box

I created a numpy array by calculating the density of dwellings within an area through the following code:
def myplot(x, y, z, s, bins=10000):
heatmap, xedges, yedges = np.histogram2d(x, y, bins=bins, weights=z)
heatmap = gaussian_filter(heatmap, sigma=s)
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]
return heatmap.T, extent
fig, axs = plt.subplots(2, 2)
# Generate some test data
x = buildings["x"]
y = buildings["y"]
weights = buildings["Area"]
sigmas = [0, 16, 32, 64]
for ax, s in zip(axs.flatten(), sigmas):
if s == 0:
ax.plot(x, y, weights, 'k.', markersize=5)
ax.set_title("Scatter plot")
else:
img, extent = myplot(x, y, weights, s)
ax.imshow(img, extent=extent, origin='lower', cmap=cm.jet)
ax.set_title("Smoothing with $\sigma$ = %d" % s)
plt.savefig('export_'+str(s)+'.png', dpi=150, bbox_inches='tight')
plt.show()
This is the result and works fine:
enter image description here
Now I need to save it as a geotif and I know the extreme coordinates of the box angles. I tried to do that using the following code:
# create a georeferenced box
transform = from_bounds(extent[0], extent[1],extent[2], extent[3], 10000, 10000)
# save the georeferenced tif
with rio.open('data.tif', 'w', driver='GTiff', height=10000, width=10000, count=1, dtype='float64', nodata=0, crs=32632, transform=transform) as dst:
dst.write(img, 1)
The problem is that the result is transpose and not on the right position. Could you help me to find the solution?
I tried to develop the code but did not work
You should simply use numpy.transpose on your array - it is a very fast operation that does not copy the array.
GDAL uses traditional C style raster coordinates. In numpy an array with shape (x, y) is x lines of y pixels, while in GDAL it is the other way around.
# save the georeferenced tif
with rio.open('data.tif', 'w', driver='GTiff', height=10000, width=10000, count=1, dtype='float64', nodata=0, crs=32632, transform=transform) as dst:
dst.write(img.tranpose(), 1)

Algorithm to implement a higher order fractal

I'm trying to make a code for a n-order Koch fractal, defined as:
For that, I'm implementing a code in Matlab. I've been able to develop a code that is able to plot a second order fractal (zero and first orders are pretty easy though). For that, I've based the code in this post. Up to now, I've got this, which works, but obviously just for order two (or one and zero, just editing a bit the code)
clear all;
clc;
close all;
hold on;
% Counter-clockwise rotation matrix
R = [cosd(60) -sind(60);sind(60) cosd(60)];
angles = [0 60 -60 0 0]; % main angles
n = 2; % order
% Length of each straight segment
seglength = 3^-(n-1);
% Initialization of variables
a=0;
b=0;
c=1/3;
d=0;
for i=1:4^(n-2)
for j=1:4
p1 = [a;b];
p5 = [c;d];
p2 = (2.*p1+p5)/3;
p4 = (2.*p5+p1)/3;
p3 = p2 + R*(p4-p2);
x = [p1(1) p2(1) p3(1) p4(1) p5(1)];
y = [p1(2) p2(2) p3(2) p4(2) p5(2)];
line(x,y);
a=a+seglength*cosd(angles(j));
c=c+seglength*cosd(angles(j+1));
b=b+seglength*sind(angles(j));
d=d+seglength*sind(angles(j+1));
end
end
axis equal;
I know it is not a clean and optimum code. I'm just interested in any kind of tip that could help me to develop a n-order Koch fractal, just changing the value of n.
Any help is appreciated!
You can certainly improve upon your algorithm, specifically taking advantage of vectorization to reduce the computation to a single for loop (and making it easy to extend it to any order). You don't even really need recursion (although that's another option). Here's a vectorized function koch:
function [x, y] = koch(N, x, y)
R = [cosd(60) -sind(60); sind(60) cosd(60)];
x = x(:).';
y = y(:).';
for iOrder = 1:N % Loop N times
x1 = x(1:(end-1));
x5 = x(2:end);
x2 = (2.*x1+x5)./3;
x4 = (x1+2.*x5)./3;
y1 = y(1:(end-1));
y5 = y(2:end);
y2 = (2.*y1+y5)./3;
y4 = (y1+2.*y5)./3;
temp = R*[x4-x2; y4-y2];
x = [x1; x2; x2+temp(1, :); x4];
y = [y1; y2; y2+temp(2, :); y4];
x = [x(:).' x5(end)];
y = [y(:).' y5(end)];
end
end
For a set of M points (both x and y) at each iteration, the starting points for each line are given by x(1:(M-1)) and the ending points are given by x(2:M). You can interleave your 3 new points in between these by building a 4-by-M-1 matrix where the top row is your starting points and each successive row is one of your 3 new points (computed as in your link). Reshaping the resulting matrix with (:).' (and adding the very last end point) will give you a 1-by-4*M-3 set of points for the line, which can be used by the next iteration.
And here's the code in action:
[x, y] = koch(0, [0 1], [0 0]);
plot(x, y);
axis equal;
hold on;
[x, y] = koch(1, x, y); % or [x, y] = koch(1, [0 1], [0 0]);
plot(x, y);
[x, y] = koch(1, x, y); % or [x, y] = koch(2, [0 1], [0 0]);
plot(x, y);
[x, y] = koch(1, x, y); % or [x, y] = koch(3, [0 1], [0 0]);
plot(x, y);
legend({'0' '1' '2' '3'});
Notice you can get an nth order fractal by either passing n and your starting points or passing 1 and the points for the n-1th fractal.

loop (for loop) though even pixels in a picture in jython

I need to use a single for loop to loop through every even pixel in a picture. I think I was getting close with this code, but jython does not like it and I do not know why (something with the second for loop).
for x in range(0, width):
for y in range(0, height):
px = getPixels(pic, x, y)
Any help would be much appreciated.
Here is my full code if it helps. The point of the project is to resize a picture by moving all the even pixels to a new blank picture that is half the size.
def main():
#Allows the user to pick a picture
pic = makePicture(pickAFile())
show(pic)
#Finds the width and height of the selected picture
width = getWidth(pic)
height = getHeight(pic)
#Finds and divides width accordingly
if width % 2 == 0:
newW = width/2
else:
newW = width/2+1
#Finds and divides height accordingly
if height % 2 == 0:
newH = height/2
else:
newH = height/2+1
for x in range(0, width, 2):
for y in range(0, height, 2):
px = getPixels(pic, x, y)
Is it an issue with not tabbing over the loop for the y values? Does structuring the text like this fix the issue? (By the way, adding a 3rd parameter to the range() function lets you define a step value instead of the default 1.)
for x in range(0, width, 2):
for y in range(0, height, 2):
px = getPixels(pic, x, y)

Results from my thin plate spline interpolation implementation are dependant of the independent variables

I implemented the thin plate spline algorithm (see also this description) in order to interpolate scattered data using Python.
My algorithm seems to work correctly when the bounding box of the initial scattered data has an aspect ratio close to 1. However, scaling one of the data points coordinates changes the interpolation result. I created a minimal working example that is representative of what I am trying to accomplish. Below are two plots showing the results of the interpolation of 50 random points.
First, the interpolation of z = x^2 on the domain x = [0, 3], y = [0, 120]:
As you can see, the interpolation fails. Now, executing the same process but after scaling the x values by a factor of 40, I get:
This time, the result looks better. Choosing a slightly different scaling factor would have resulted in a slightly different interpolation. This shows that something is wrong in my algorithm but I can't find what exactly. Here is the algorithm:
import numpy as np
import numba as nb
# pts1 = Mx2 matrix (original coordinates)
# z1 = Mx1 column vector (original values)
# pts2 = Nx2 matrix (interpolation coordinates)
def gen_K(n, pts1):
K = np.zeros((n,n))
for i in range(0,n):
for j in range(0,n):
if i != j:
r = ( (pts1[i,0] - pts1[j,0])**2.0 + (pts1[i,1] - pts1[j,1])**2.0 )**0.5
K[i,j] = r**2.0*np.log(r)
return K
def compute_z2(m, n, pts1, pts2, coeffs):
z2 = np.zeros((m,1))
x_min = np.min(pts1[:,0])
x_max = np.max(pts1[:,0])
y_min = np.min(pts1[:,1])
y_max = np.max(pts1[:,1])
for k in range(0,m):
pt = pts2[k,:]
# If point is located inside bounding box of pts1
if (pt[0] >= x_min and pt[0] <= x_max and pt[1] >= y_min and pt[1] <= y_max):
z2[k,0] = coeffs[-3,0] + coeffs[-2,0]*pts2[k,0] + coeffs[-1,0]*pts2[k,1]
for i in range(0,n):
r2 = ( (pts1[i,0] - pts2[k,0])**2.0 + (pts1[i,1] - pts2[k,1])**2.0 )**0.5
if r2 != 0:
z2[k,0] += coeffs[i,0]*( r2**2.0*np.log(r2) )
else:
z2[k,0] = np.nan
return z2
gen_K_nb = nb.jit(nb.float64[:,:](nb.int64, nb.float64[:,:]), nopython = True)(gen_K)
compute_z2_nb = nb.jit(nb.float64[:,:](nb.int64, nb.int64, nb.float64[:,:], nb.float64[:,:], nb.float64[:,:]), nopython = True)(compute_z2)
def TPS(pts1, z1, pts2, factor):
n, m = pts1.shape[0], pts2.shape[0]
P = np.hstack((np.ones((n,1)),pts1))
Y = np.vstack((z1, np.zeros((3,1))))
K = gen_K_nb(n, pts1)
K += factor*np.identity(n)
L = np.zeros((n+3,n+3))
L[0:n, 0:n] = K
L[0:n, n:n+3] = P
L[n:n+3, 0:n] = P.T
L_inv = np.linalg.inv(L)
coeffs = L_inv.dot(Y)
return compute_z2_nb(m, n, pts1, pts2, coeffs)
Finally, here is the code snippet I used to create the two plots:
import matplotlib.pyplot as plt
import numpy as np
N = 50 # Number of random points
pts = np.random.rand(N,2)
pts[:,0] *= 3.0 # initial x values
pts[:,1] *= 120.0 # initial y values
z1 = (pts[:,0])**2.0
for scale in [1.0, 40.0]:
pts1 = pts.copy()
pts1[:,0] *= scale
x2 = np.linspace(np.min(pts1[:,0]), np.max(pts1[:,0]), 40)
y2 = np.linspace(np.min(pts1[:,1]), np.max(pts1[:,1]), 40)
x2, y2 = np.meshgrid(x2, y2)
pts2 = np.vstack((x2.flatten(), y2.flatten())).T
z2 = TPS(pts1, z1.reshape(z1.shape[0], 1), pts2, 0.0)
# Display
fig = plt.figure(figsize=(4,3))
ax = fig.add_subplot(111)
C = ax.contourf(x2, y2, z2.reshape(x2.shape), np.linspace(0,9,10), extend='both')
ax.plot(pts1[:,0], pts1[:,1], 'ok')
ax.set_xlabel('x')
ax.set_ylabel('y')
plt.colorbar(C, extendfrac=0)
plt.tight_layout()
plt.show()
Thin Plate Spline is scalar invariant, which means if you scale x and y by the same factor, the result should be the same. However, if you scale x and y differently, then the result will be different. This is common characteristics among radial basis functions. Some radial basis functions are not even scalar invariant.
When you say it "fails", what do you mean? The big question is, does it still exactly interpolate at the construction points? Assuming your code is correct and you do not have ill-conditioning, it should in which case it does not fail.
What I think is happening is that the addition of the scale is making the behavior in the x direction more dominant so you do not see the wiggles that come naturally from the interpolation.
As an aside, you can greatly speed up your code without using Numba by vectorizing.
import scipy.spatial.distance
import scipy.special
def gen_K(n,pts1):
# No need for n but kept to maintain compatability
pts1 = np.atleast_2d(pts1)
r = scipy.spatial.distance.cdist(pts1,pts1)
return scipy.special.xlogy(r**2,r)
It means you will get horrible ridges running through the surface. Resulting in a sub-optimal model fit. Read the caption below the images. Your model is experiencing the same effect, although plotted in 2D.

Hilbert-Peano curve to scan image of arbitrary size

I have written an implementation of Hilbert-Peano space filling curve in Python (from a Matlab one) to flatten my 2D image:
def hilbert_peano(n):
if n<=0:
x=0
y=0
else:
[x0, y0] = hilbert_peano(n-1)
x = (1/2) * np.array([-0.5+y0, -0.5+x0, 0.5+x0, 0.5-y0])
y = (1/2) * np.array([-0.5+x0, 0.5+y0, 0.5+y0, -0.5-y0])
return x,y
However, the classical Hilbert-Peano curve only works for multi-dimensionnal array whose shape is a power of two (ex: 256*256 or 512*512 in case of a 2D array (image)).
Does anybody know how to extend this to an array of arbitrary size?
I had the same problem and have written an algorithm that generates a Hilbert-like curve for rectangles of arbitrary size in 2D and 3D. Example for 55x31: curve55x31
The idea is to recursively apply a Hilbert-like template but avoid odd sizes when halving the domain dimensions. If the dimensions happen to be powers of two, the classic Hilbert curve is generated.
def gilbert2d(x, y, ax, ay, bx, by):
"""
Generalized Hilbert ('gilbert') space-filling curve for arbitrary-sized
2D rectangular grids.
"""
w = abs(ax + ay)
h = abs(bx + by)
(dax, day) = (sgn(ax), sgn(ay)) # unit major direction
(dbx, dby) = (sgn(bx), sgn(by)) # unit orthogonal direction
if h == 1:
# trivial row fill
for i in range(0, w):
print x, y
(x, y) = (x + dax, y + day)
return
if w == 1:
# trivial column fill
for i in range(0, h):
print x, y
(x, y) = (x + dbx, y + dby)
return
(ax2, ay2) = (ax/2, ay/2)
(bx2, by2) = (bx/2, by/2)
w2 = abs(ax2 + ay2)
h2 = abs(bx2 + by2)
if 2*w > 3*h:
if (w2 % 2) and (w > 2):
# prefer even steps
(ax2, ay2) = (ax2 + dax, ay2 + day)
# long case: split in two parts only
gilbert2d(x, y, ax2, ay2, bx, by)
gilbert2d(x+ax2, y+ay2, ax-ax2, ay-ay2, bx, by)
else:
if (h2 % 2) and (h > 2):
# prefer even steps
(bx2, by2) = (bx2 + dbx, by2 + dby)
# standard case: one step up, one long horizontal, one step down
gilbert2d(x, y, bx2, by2, ax2, ay2)
gilbert2d(x+bx2, y+by2, ax, ay, bx-bx2, by-by2)
gilbert2d(x+(ax-dax)+(bx2-dbx), y+(ay-day)+(by2-dby),
-bx2, -by2, -(ax-ax2), -(ay-ay2))
def main():
width = int(sys.argv[1])
height = int(sys.argv[2])
if width >= height:
gilbert2d(0, 0, width, 0, 0, height)
else:
gilbert2d(0, 0, 0, height, width, 0)
A 3D version and more documentation is available at https://github.com/jakubcerveny/gilbert
I found this page by Lutz Tautenhahn:
"Draw A Space-Filling Curve of Arbitrary Size" (http://lutanho.net/pic2html/draw_sfc.html)
The algorithm doesn't have a name, he doesn't reference anyone else and the sketch suggests he came up with it himself.
I wonder if this is possible for a z order curve and how?
[1]Draw A Space-Filling Curve of Arbitrary Size
I finally choose, as suggested by Betterdev as adaptive curves are not that straigthforward [1], to compute a bigger curve and then get rid of coordinates which are outside my image shape:
# compute the needed order
order = np.max(np.ceil([np.log2(M), np.log2(N)]))
# Hilbert curve to scan a 2^order * 2^order image
x, y = hilbert_peano(order)
mat = np.zeros((2**order, 2**order))
# curve as a 2D array
mat[x, y] = np.arange(0, x.size, dtype=np.uint)
# clip the curve to the image shape
mat = mat[:M, :N]
# compute new indices (from 0 to M*N)
I = np.argsort(mat.flat)
x_new, y_new = np.meshgrid(np.arange(0, N, dtype=np.uint), np.arange(0, M, dtype=np.uint))
# apply the new order to the grid
x_new = x_new.flat[I]
y_new = y_new.flat[I]
[1] Zhang J., Kamata S. and Ueshige Y., "A Pseudo-Hilbert Scan Algorithm for Arbitrarily-Sized Rectangle Region"

Resources