Basemap cannot set aspect on 3D axis - python

I am using Basemap with a 3D graph to display ray paths. I would like to implement one of basemap's topo, shaded relief or bluemarble layers but I am running into the same issue over and over again:
NotImplementedError: It is not currently possible to manually set the aspect on 3D axes
I have already implemented fixed_aspect=False into calling the basemap
and have also tried ax.set_aspect('equal') which gives me the same error
I am using matplotlib ==2.2.3
Here is my code:
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.basemap import Basemap
import numpy as np
from io import StringIO
import re
f = open("best-working4D.ray_paths", 'r') #125 waves - 125 "lines"
data = f.read()
lines = data.split('\n ')
fig = plt.figure()
ax = plt.axes(projection='3d')
extent = [300, 360, 50, 75]
bm = Basemap(llcrnrlon=extent[0], llcrnrlat=extent[2],
urcrnrlon=extent[1], urcrnrlat=extent[3], resolution='l', fix_aspect= False)
bm.bluemarble()
for i in range(1, 119):
wave = lines[i]
j = wave.split('\n')
k = []
for i in j:
k.append(i.split())
x=[]
y=[]
z=[]
n= 0
for m in k[1:]:
x.append(m[0])
y.append(m[1])
z.append(m[2])
x= np.array(x).astype('float32')
y= np.array(y).astype('float32')
z= np.array(z).astype('float32')
ax.plot3D(x,y,z, color='red')
##Plotting Tropopause
T_hi = 20
xx, yy = np.meshgrid(range(300,360), range(50,75))
zz = yy*0 + T_hi
ax.plot_surface(xx, yy, zz, alpha=0.15)
ax.set_xlabel("Latitude [deg]")
ax.set_ylabel("Longitude [deg]")
ax.set_zlabel("Altitude [km]")
ax.add_collection3d(bm.drawcoastlines(linewidth=0.25))
plt.show()
The basemap IS working for the bm.drawcoastlines just nothing else.
IMAGELINK
I would greatly appreciate any ideas!

Related

ValueError: Invalid RGBA argument: What is causing this error?

I am trying to create a 3D colored bar chart using ideas from: this stackoverflow post.
First I create a 3D bar chart with the following code:
import numpy as np
import matplotlib.colors as colors
import matplotlib.cm as cm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
samples = np.random.randint(91,size=(5000,2))
F = np.zeros([91,91])
for s in samples:
F[s[0],s[1]] += 1
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x_data, y_data = np.meshgrid( np.arange(F.shape[1]),
np.arange(F.shape[0]) )
x_data = x_data.flatten()
y_data = y_data.flatten()
z_data = F.flatten()
ax.bar3d(x_data,y_data,np.zeros(len(z_data)),1,1,z_data )
plt.show()
The following is the output:
Now I try to color the bars using code verbatim from: this stackoverflow post. Here is the code:
import numpy as np
import matplotlib.colors as colors
import matplotlib.cm as cm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
samples = np.random.randint(91,size=(5000,2))
F = np.zeros([91,91])
for s in samples:
F[s[0],s[1]] += 1
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x_data, y_data = np.meshgrid( np.arange(F.shape[1]),
np.arange(F.shape[0]) )
x_data = x_data.flatten()
y_data = y_data.flatten()
z_data = F.flatten()
dz = F
offset = dz + np.abs(dz.min())
fracs = offset.astype(float)/offset.max()
norm = colors.Normalize(fracs.min(), fracs.max())
colors = cm.jet(norm(fracs))
# colors = np.random.rand(91,91,4)
ax.bar3d(x_data,y_data,np.zeros(len(z_data)),1,1,z_data,color=colors )
plt.show()
However I get: ValueError: Invalid RGBA argument:
Now I am unable to debug the Invalid RGBA argument because I don't understand what is causing the error. I even tried to use random colors instead with colors = np.random.rand(91,91,4) and still the error persists.
I have checked stackoverflow posts regarding Invalid RGBA argument (for example this,this,this and this) and none of that seems to answer my problem.
I want to know what could be causing this error. I am using the standard Anaconda distribution for python on Ubuntu Mate 16.
Could it be that due to recent updates in python, the solution as in the original stackoverflow post becomes obsolete?
The error message is misleading. You're getting a ValueError because the shape of colors is wrong, not because an RGBA value is invalid.
When coloring each bar a single color, color should be an array of length N, where N is the number of bars. Since there are 8281 bars,
In [121]: x_data.shape
Out[121]: (8281,)
colors should have shape (8281, 4). But instead, the posted code generates an array of shape (91, 91, 4):
In [123]: colors.shape
Out[123]: (91, 91, 4)
So to fix the problem, use color=colors.reshape(-1,4).
import numpy as np
import matplotlib.colors as colors
import matplotlib.cm as cm
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
samples = np.random.randint(91,size=(5000,2))
F = np.zeros([91,91])
for s in samples:
F[s[0],s[1]] += 1
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
x_data, y_data = np.meshgrid( np.arange(F.shape[1]),
np.arange(F.shape[0]) )
x_data = x_data.flatten()
y_data = y_data.flatten()
z_data = F.flatten()
dz = F
offset = dz + np.abs(dz.min())
fracs = offset.astype(float)/offset.max()
norm = colors.Normalize(fracs.min(), fracs.max())
colors = cm.jet(norm(fracs))
ax.bar3d(x_data,y_data,np.zeros(len(z_data)),1,1,z_data,color=colors.reshape(-1,4) )
plt.show()
The color argument expects a 1D array, similar to all other arguments of bar3d.
Hence, you need to replace the line offset = dz + np.abs(dz.min())
by
offset = z_data + np.abs(z_data.min())
for your case. dz is not useful here (maybe it was in the linked example).
Note that color=np.random.rand(len(z_data),4) would equally work.
Then the result will be

3D plot using geographic coordinates

I have a dataset looking like this:
1 38.7114 -7.92482 16.4375 0.2
...
I'd like to make a 3D scatter plot. I've done it using cartesian coordinates. How I can do it using geographic coordinates? Any hint?
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
import sys
from mpl_toolkits.basemap import Basemap
ID=[]
Latitude=[]
Longitude=[]
Depth=[]
cluster1='data1'
with open(cluster1) as f:
lines = f.readlines()
for line in lines:
items = line.strip().split()
lat = float(items[1])
lon = float(items[2])
dep = float(items[3])
mag = float(items[4])
Latitude.append(lat)
Longitude.append(lon)
Depth.append(dep)
ID.append(mag)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
p = ax.scatter(Longitude, Latitude, Depth, c=ID, marker='o')
ax.set_xlabel('Longitude')
ax.set_ylabel('Latitude')
ax.set_zlabel('Depth (km)')
ax.invert_zaxis()
cb = fig.colorbar(p,label='Magnitude')
plt.savefig('plot1.png')

Matplotlib discretize colorbar between given values

I have the plot bellow and I would like to discretize the colormap between 0 and 20. Could anyone help with that?
Here is the code:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import BoundaryNorm
from matplotlib.ticker import MaxNLocator
epi='epi'
with open(epi, 'r') as f2:
lines = f2.readlines()
data = [line.split() for line in lines]
a = np.array(data)
print a.shape
lat = a[:,0]
lat1=list(lat)
lat2=np.asarray(lat1).astype(float)
lon = a[:,1]
lon1=list(lon)
lon2=np.asarray(lon).astype(float)
x_space = 60
y_space = x_space*1.7
gridx = np.linspace(-8.8, -7.0, x_space)
gridy = np.linspace(38, 39.5, y_space )
grid, _, _ = np.histogram2d(lat2, lon2, bins=[gridy, gridx])
cmap = plt.get_cmap('hot_r')
plt.figure()
plt.axis((-8.8,-7.0,38.2,39))
plt.pcolormesh(gridx, gridy, grid,cmap=cmap)
plt.colorbar()
plt.show()
If you want a coarsely discretized colormap, you can change your get_cmap call and include the number of different (discrete) colors you want:
import matplotlib.pylab as pl
import numpy as np
data = np.random.random([10,10]) * 40
hot2 = pl.cm.get_cmap('hot', 20)
pl.figure()
pl.subplot(121)
pl.pcolormesh(data, cmap=pl.cm.hot, vmin=0, vmax=20)
pl.colorbar()
pl.subplot(122)
pl.pcolormesh(data, cmap=hot2, vmin=0, vmax=20)
pl.colorbar()

Vertically fill 3d matplotlib plot

I have a 3d plot made using matplotlib. I now want to fill the vertical space between the drawn line and the x,y axis to highlight the height of the line on the z axis. On a 2d plot this would be done with fill_between but there does not seem to be anything similar for a 3d plot. Can anyone help?
here is my current code
from stravalib import Client
import matplotlib as mpl
import numpy as np
import matplotlib.pyplot as plt
... code to get the data ....
mpl.rcParams['legend.fontsize'] = 10
fig = plt.figure()
ax = fig.gca(projection='3d')
zi = alt
x = df['x'].tolist()
y = df['y'].tolist()
ax.plot(x, y, zi, label='line')
ax.legend()
plt.show()
and the current plot
just to be clear I want a vertical fill to the x,y axis intersection NOT this...
You're right. It seems that there is no equivalent in 3D plot for the 2D plot function fill_between. The solution I propose is to convert your data in 3D polygons. Here is the corresponding code:
import math as mt
import matplotlib.pyplot as pl
import numpy as np
import random as rd
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.mplot3d.art3d import Poly3DCollection
# Parameter (reference height)
h = 0.0
# Code to generate the data
n = 200
alpha = 0.75 * mt.pi
theta = [alpha + 2.0 * mt.pi * (float(k) / float(n)) for k in range(0, n + 1)]
xs = [1.0 * mt.cos(k) for k in theta]
ys = [1.0 * mt.sin(k) for k in theta]
zs = [abs(k - alpha - mt.pi) * rd.random() for k in theta]
# Code to convert data in 3D polygons
v = []
for k in range(0, len(xs) - 1):
x = [xs[k], xs[k+1], xs[k+1], xs[k]]
y = [ys[k], ys[k+1], ys[k+1], ys[k]]
z = [zs[k], zs[k+1], h, h]
#list is necessary in python 3/remove for python 2
v.append(list(zip(x, y, z)))
poly3dCollection = Poly3DCollection(v)
# Code to plot the 3D polygons
fig = pl.figure()
ax = Axes3D(fig)
ax.add_collection3d(poly3dCollection)
ax.set_xlim([min(xs), max(xs)])
ax.set_ylim([min(ys), max(ys)])
ax.set_zlim([min(zs), max(zs)])
ax.set_xlabel("x")
ax.set_ylabel("y")
ax.set_zlabel("z")
pl.show()
It produces the following figure:
I hope this will help you.

Strange behavior of matplotlib's griddata

I have a .txt file with values
x1 y1 z1
x2 y2 z2
etc.
With my previous little experience I was trying to draw a contourf, with this code
import numpy as np
import matplotlib
from matplotlib import rc
import matplotlib.mlab as ml
from pylab import *
rc('font', family='serif')
rc('font', serif='Times New Roman')
rc('font', size='9')
rc('text', usetex=True)
from matplotlib.mlab import griddata
import matplotlib.pyplot as plt
import numpy.ma as ma
from numpy.random import uniform
from matplotlib.colors import LogNorm
matplotlib.use('pgf')
fig = plt.figure()
data = np.genfromtxt('Velocidad.txt')
matplotlib.rcParams['xtick.direction'] = 'out'
matplotlib.rcParams['ytick.direction'] = 'out'
rc('text', usetex=True)
rc('font', family='serif')
x = data[:,0]
y = data[:,1]
z = data[:,2]
xi = np.linspace(0,3000.0, 400)
yi = np.linspace(0,4.0, 200)
zi = griddata(x,y,z,xi,yi,interp='nn')
CS = plt.contourf(xi,yi,zi,200,cmap=plt.cm.jet,rasterized=True)
plt.colorbar()
plt.xlim(0,3000)
plt.ylim(0,4.0)
plt.ylabel(r'$t$')
plt.xlabel(r'$x$')
plt.title(r' Contour de $v(x,t)$')
plt.savefig("CampoVel.png", dpi=100)
plt.show()
the problem is the output:
When I see this picture and I look at the data (which is here, in this link) and I don't understand those discontinuities in x=750 and x=1875. And those strange vertical lines all over the plot. Looking at the data I would expect something smooth, at least in those positions, but the output obviously isn't. Is this a problem of griddata()? How can I solve it?
I have been told that as my data is regularly spaced on X and Y, I shouldn't use griddata(), but I have looked examples and I can't get the code to work.
If you simply reshape your data after loading it and skip the griddata thing, doing this:
data = data.reshape(81, 201, 3)
x = data[...,0]
y = data[...,1]
z = data[...,2]
CS = plt.contourf(x,y,z,200,cmap=plt.cm.jet,rasterized=True)
plt.colorbar()
plt.show()
You get this:

Categories