I have plotted the contour of some data from file, but I need to generate a file with the coordinates of this contour. My code is the following:
import os
import sys
import pandas as pd
import numpy as np
from descartes import PolygonPatch
import matplotlib.pyplot as plt
sys.path.insert(0, os.path.dirname(os.getcwd()))
import alphashape
data1 = pd.read_csv('SDSS_19.txt', sep='\s+', header=None)
data1 = pd.DataFrame(data1)
x = data1[0]
y = data1[1]
points = np.vstack((x, y)).T
fig, ax = plt.subplots(figsize=(20,20))
ax.scatter(x, y, alpha=0.5, color='red')
alpha_shape = alphashape.alphashape(points, 0.6)
ax.add_patch(PolygonPatch(alpha_shape, alpha=0.25))
plt.savefig('contour.png')
plt.show()
The output is the following:
What I need is to calculate the coordinates of some points (the more the best) of the black line (the contour).
any help? Thanks.
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!
I'm making plots using matplotlib colormap "seismic" and would like to have the white color centered on 0. When I run my script with no changes, white falls from 0 to -10. I tried then setting vmin=-50, vmax=50 but I completely lose the white in that case. Any suggestions on how to accomplish that?
from netCDF4 import Dataset as NetCDFFile
import matplotlib.pyplot as plt
import numpy as np
from mpl_toolkits.basemap import Basemap
nc = NetCDFFile('myfile.nc')
lat = nc.variables['lat'][:]
lon = nc.variables['lon'][:]
time = nc.variables['time'][:]
hgt = nc.variables['hgt'][:]
map = Basemap(llcrnrlon=180.,llcrnrlat=0.,urcrnrlon=320.,urcrnrlat=80.)
lons,lats = np.meshgrid(lon,lat)
x,y = map(lons,lats)
cs = map.contourf(x,y,hgt[0],cmap='seismic')
cbar = plt.colorbar(cs, orientation='horizontal', shrink=0.5,
cmap='seismic')
cbar.set_label('500mb Geopotential Height Anomalies(m)')
map.drawcoastlines()
map.drawparallels(np.arange(20,80,20),labels=[1,1,0,0], linewidth=0.5)
map.drawmeridians(np.arange(200,320,20),labels=[0,0,0,1], linewidth=0.5)
plt.show()`
Plot with defaults
Plot with vmin, vmax set
You can set the levels you want to show manually. As long as you have the same spacing of intervals to the left and to the right of zero this works nicely.
levels = [-50,-40,-30,-20,-10,10,20,30,40,50]
ax.contourf(X,Y,Z, levels)
Example:
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-6.3,6.3)
y = np.linspace(-3.1,3.1)
X,Y = np.meshgrid(x,y)
Z = -np.cos(X)*np.cos(Y)*45
levels = [-50,-40,-30,-20,-10,10,20,30,40,50]
fig, ax = plt.subplots(figsize=(4,2))
cont = ax.contourf(X,Y,Z,levels, cmap="seismic")
fig.colorbar(cont, orientation="horizontal")
plt.show()
Or, if you want the colorbar to be proportional to the data,
fig.colorbar(cont, orientation="horizontal", spacing="proportional")
If levels are unequal, you need to specify vmin and vmax.
levels = [-50,-40,-30,-20,-10,10,30,50,80,100]
cont = ax.contourf(X,Y,Z,levels, cmap="seismic", vmin=-50, vmax=50)
The disadvantage is that you loose resolution, hence you may use a BoundaryNorm to select equally spaced colors for unequally spaced labels.
import matplotlib.pyplot as plt
import matplotlib.colors
import numpy as np
x = np.linspace(-6.3,6.3)
y = np.linspace(-3.1,3.1)
X,Y = np.meshgrid(x,y)
Z = -np.cos(X)*np.cos(Y)*45
levels = [-50,-40,-30,-20,-10,10,30,50,80,100]
norm = matplotlib.colors.BoundaryNorm(levels, len(levels)-1)
fig, ax = plt.subplots(figsize=(4,2))
cont = ax.contourf(X,Y,Z,levels,cmap=plt.get_cmap("seismic",len(levels)-1), norm=norm)
fig.colorbar(cont, orientation="horizontal")
plt.show()
To change the ticklabels on the colorbar so something other than the levels or in case they are too dence you may use the ticks argument.
import matplotlib.pyplot as plt
import numpy as np
x = np.linspace(-6.3,6.3)
y = np.linspace(-3.1,3.1)
X,Y = np.meshgrid(x,y)
Z = -np.cos(X)*np.cos(Y)*45
levels = np.arange(-45,50,5)
levels = levels[levels!=0]
ticks=np.arange(-40,50,10)
fig, ax = plt.subplots(figsize=(4,2))
cont = ax.contourf(X,Y,Z,levels,cmap="seismic", spacing="proportional")
fig.colorbar(cont, orientation="horizontal", ticks=ticks, spacing="proportional")
plt.show()
I found this script on the matplotlib website:
"""
Demonstrates using custom hillshading in a 3D surface plot.
"""
from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cbook
from matplotlib import cm
from matplotlib.colors import LightSource
import matplotlib.pyplot as plt
import numpy as np
filename = cbook.get_sample_data('jacksboro_fault_dem.npz', asfileobj=False)
with np.load(filename) as dem:
z = dem['elevation']
nrows, ncols = z.shape
x = np.linspace(dem['xmin'], dem['xmax'], ncols)
y = np.linspace(dem['ymin'], dem['ymax'], nrows)
x, y = np.meshgrid(x, y)
region = np.s_[5:50, 5:50]
x, y, z = x[region], y[region], z[region]
fig, ax = plt.subplots(subplot_kw=dict(projection='3d'))
ls = LightSource(270, 45)
# To use a custom hillshading mode, override the built-in shading and pass
# in the rgb colors of the shaded surface calculated from "shade".
rgb = ls.shade(z, cmap=cm.gist_earth, vert_exag=0.1, blend_mode='soft')
surf = ax.plot_surface(x, y, z, rstride=1, cstride=1, facecolors=rgb,
linewidth=0, antialiased=False, shade=False)
plt.show()
They use the file jacksboro_fault_dem.npz to plot the elevation data and they get something like that:
Thanks to Google Earth I was able to get the text file maido_elevation_data.txt with latitude, longitude and elevation data of the following area (Maïdo, Reunion Island):
I made a function to get 3 lists for each coordinate from the text file:
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
def get_LAT_LONG_ALT(text_file):
ch=""
LAT=[]
LONG=[]
ALT=[]
with open(text_file,"r") as fich:
for ligne in fich:
for e in ligne:
ch+=e
liste=ch.replace("\n","").split("\t")
LAT.append(float(liste[0]))
LONG.append(float(liste[1]))
ALT.append(float(liste[2]))
ch=""
return LAT,LONG,ALT
fig = plt.figure()
axes = fig.add_subplot(111, projection="3d")
X = get_LAT_LONG_ALT("maido_elevation_data.txt")[0]
Y = get_LAT_LONG_ALT("maido_elevation_data.txt")[1]
Z = get_LAT_LONG_ALT("maido_elevation_data.txt")[2]
axes.scatter(X,Y,Z, c="r", marker="o")
axes.set_xlabel("Latitude")
axes.set_ylabel("Longitude")
axes.set_zlabel("Altitude")
plt.show()
How should I modify the script to get a good surface plot with my own data like they do?
PS: I will give you the links of the files in the comments because I'm not allowed to put more than 2 links... yes, I'm new :)
You should reshape your data it is a three column data x,y and z
You should have a file with only z values in a 2D table columns are x and rows are y.
Meshgrid fucntion in python should help.
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()