How can I align the y-axis (latitudes) of a map plot and a plot in python - matplotlib-basemap

I'm trying plot two panels in a plot.
The first one (left) is a data with latitude values in its y-axis. The second panel is a map.
I wanna that the latitude values of both panels coinciding, but I don't know how get it.
I have a code like this:
fig_mapa= plt.figure()
'''Mapa'''
ax1=fig_mapa.add_subplot(122)
map = Basemap(llcrnrlon=-90,llcrnrlat=-58.1,urcrnrlon=-32,urcrnrlat=12.6,
resolution='f',projection='merc',lon_0=-58,lat_0=-25, ax=ax1)
map.drawparallels(np.arange(-90,90.,5), labels=[0,1,0,0], linewidth=0.5)
map.drawmeridians(np.arange(-180.,180.,5), labels=[0,0,0,1], linewidth=0.5)
map.readshapefile("./Fases_tectonicas/Shapefiles/Unidades_Fi", 'Unidades_Fi', linewidth=0.1)
#map.warpimage(image='./Geotiffs/NE1_HR_LC_SR_W_DR/NE1_HR_LC_SR_W_DR.tif', zorder=1)
map.drawcoastlines(linewidth=0.5, color='k')
Nombre_Unidad= []
for elemento in map.Unidades_Fi_info:
Nombre_Unidad.append(elemento['NAME'])
for i in range(len(Nombre_Unidad)):
draw=map.Unidades_Fi[i]
poly=Polygon(draw, facecolor=color[Nombre_Unidad[i]],edgecolor='k', alpha=0.5,linewidth=0.1, zorder=2)
plt.gca().add_patch(poly)
'''Gráfico Eventos Compresivos'''
ax2= fig_mapa.add_subplot(121)
ax2.set_ylim(-58.1,12.6)
ax2.set_xlim(120,0)
ax2.set_xlabel('Tiempo [Ma]')
ax2.set_ylabel('Latitud[°]')
ax2.grid()

The simplest way to align two axes is with the sharex or sharey keyword for plt.subplots. However, the coordinates that Basemap shows and the coordinates that it uses for the Axes instance are two different things, so you will have to convert between the two if you want to have understandable ytick labels and some meaningful graph in your second Axes instance. Below I show how you can align the two y-axes, set the yticks properly and transform your data to the data coordinates of your Basemap. I left the creation of the Basemap untouched.
from matplotlib import pyplot as plt
from mpl_toolkits.basemap import Basemap
import numpy as np
##figure with two subplots and shared y-axis
fig,(ax2,ax1) = plt.subplots(nrows=1, ncols=2, sharey='row')
m1 = Basemap(llcrnrlon=-90,llcrnrlat=-58.1,urcrnrlon=-32,urcrnrlat=12.6,
#resolution='f',
projection='merc',lon_0=-58,lat_0=-25, ax=ax1)
m1.drawparallels(np.arange(-90,90.,5), labels=[0,1,0,0], linewidth=0.5)
m1.drawmeridians(np.arange(-180.,180.,5), labels=[0,0,0,1], linewidth=0.5)
m1.drawcoastlines(linewidth=0.5, color='k')
##turning off yticks at basemap
ax1.yaxis.set_ticks_position('none')
##setting yticks:
yticks = np.arange(-55,12.6,5)
##transform yticks:
_,yticks_data = m1(0*yticks,yticks)
ax2.set_yticks(yticks_data)
ax2.set_yticklabels(['{: >3}$^\circ${}'.format(
abs(int(y)), 'N' if y>0 else 'S' if y<0 else ' '
) for y in yticks])
ax2.set_xlim(120,0)
ax2.set_xlabel('Tiempo [Ma]')
ax2.set_ylabel('Latitud[$^\circ$]')
ax2.grid()
#some fake data for testing plotting
yrange = np.linspace(-60,20,100)
temp = (np.sin(10*np.deg2rad(yrange))+1)*50
##transform yrange
_,yrange_data = m1(0*yrange, yrange)
ax2.plot(temp,yrange_data)
plt.show()
The result of the above code looks like this:
Hope this helps.

Related

fill oceans for basemap in 3D

I would like to fill oceans for my basemap in 3D but
ax.add_collection3d(m.drawmapboundary(fill_color='aqua'))
doesn't seem to work because the basemap drawmapboundary method doesn’t return an object supported by add_collection3d but a matplotlib.collections.PatchCollection object. Is there any workaround similar to the one done for land polygons here? Thank you!
Drawing a rectangle (polygon) below the map is one solution. Here is the working code that you may try.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.basemap import Basemap
from matplotlib.collections import PolyCollection
map = Basemap()
fig = plt.figure()
ax = Axes3D(fig)
ax.azim = 270
ax.elev = 50
ax.dist = 8
ax.add_collection3d(map.drawcoastlines(linewidth=0.20))
ax.add_collection3d(map.drawcountries(linewidth=0.15))
polys = []
for polygon in map.landpolygons:
polys.append(polygon.get_coords())
# This fills polygons with colors
lc = PolyCollection(polys, edgecolor='black', linewidth=0.3, \
facecolor='#BBAAAA', alpha=1.0, closed=False)
lcs = ax.add_collection3d(lc, zs=0) # set zero zs
# Create underlying blue color rectangle
# It's `zs` value is -0.003, so it is plotted below land polygons
bpgon = np.array([[-180., -90],
[-180, 90],
[180, 90],
[180, -90]])
polys2 = []
polys2.append(bpgon)
lc2 = PolyCollection(polys2, edgecolor='none', linewidth=0.1, \
facecolor='#445599', alpha=1.0, closed=False)
lcs2 = ax.add_collection3d(lc2, zs=-0.003) # set negative zs value
plt.show()
The resulting plot:

Transferring basemap - cartopy

I am using basemap on Python 2.7 but would like to go for Python 3, and therefor, moving to cartopy. It would be fantastic if you would give me some advises how to change my code from basemap to cartopy:
This is the basemap code:
from mpl_toolkits.basemap import Basemap
# plot map without continents and coastlines
m = Basemap(projection='kav7',lon_0=0)
# draw map boundary, transparent
m.drawmapboundary()
m.drawcoastlines()
# draw paralells and medians, no labels
if (TheLatInfo[1] == len(TheLatList)) & (TheLonInfo[1] == len(TheLonList)):
m.drawparallels(np.arange(-90,90.,30.))
m.drawmeridians(np.arange(-180,180.,60.))
grids = m.pcolor(LngArrLons,LngArrLats,MSKTheCandData,cmap=cmap,norm=norm,latlon='TRUE')
This is the cartopy example I found and have changed some bits:
import cartopy.crs as ccrs
import matplotlib.pyplot as plt
import cartopy.feature as cpf
ax = plt.axes(projection=ccrs.Robinson())
ax.coastlines()
ax.set_boundary
ax.gridlines(draw_labels=False)
plt.show()
I am not sure about how to set the gridlines in the exact positions and how to color them black instead of grey. Furthermore, I wonder how to insert/overlay my actual map with data then. Is "ax.pcolor" well enough supported by cartopy?
Thank you!
To color your gridlines black, you can use a color= keyword:
ax.gridlines(color='black')
To specify lat/lon gridline placement, you really only need a few extra lines, if you don't care about labels:
import matplotlib.ticker as mticker
gl = ax.gridlines(color='black')
gl.xlocator = mticker.FixedLocator([-180, -90, 0, 90, 180])
gl.ylocator = mticker.FixedLocator([-90,-45,0,45,90])
(As of writing this, Robinson projections don't support gridline labels.)
To overlay your data on the map,pcolor should work, but it's famously slow. I would recommend pcolormesh, though you can substitute one for another in this syntax:
ax.pcolormesh(lon_values, lat_values, data)
Note that if your data come on a different projection than the map projection you're plotting (typically true), you need to specify the data's projection in the plotting syntax using the transform= keyword. That tells cartopy to transform your data from their original projection to that of the map. Plate Carrée is the same as cylindrical equidistant (typical for climate model output, for example):
ax.pcolormesh(lon_values, lat_values, data, transform=ccrs.PlateCarree())

How to convert from the image coordinates to Cartesian coordinates

I have this 3D image generated from the simple code below.
% Input Image size
imageSizeY = 200;
imageSizeX = 120;
imageSizeZ = 100;
%# create coordinates
[rowsInImage, columnsInImage, pagesInImage] = meshgrid(1:imageSizeY, 1:imageSizeX, 1:imageSizeZ);
%# get coordinate array of vertices
vertexCoords = [rowsInImage(:), columnsInImage(:), pagesInImage(:)];
centerY = imageSizeY/2;
centerX = imageSizeX/2;
centerZ = imageSizeZ/2;
radius = 28;
%# calculate distance from center of the cube
sphereVoxels = (rowsInImage - centerY).^2 + (columnsInImage - centerX).^2 + (pagesInImage - centerZ).^2 <= radius.^2;
%# Now, display it using an isosurface and a patch
fv = isosurface(sphereVoxels,0);
patch(fv,'FaceColor',[0 0 .7],'EdgeColor',[0 0 1]); title('Binary volume of a sphere');
view(45,45);
axis equal;
grid on;
xlabel('x-axis [pixels]'); ylabel('y-axis [pixels]'); zlabel('z-axis [pixels]')
I have tried plotting the image with isosurface and some other volume visualization tools, but there remains quite a few surprises for me from the plots.
The code has been written to conform to the image coordinate system (eg. see: vertexCoords) which is a left-handed coordinate system I presume. Nonetheless, the image is displayed in the Cartesian (right-handed) coordinate system. I have tried to see this displayed as the figure below, but that’s simply not happening.
I am wondering if the visualization functions have been written to display the image the way they do.
Image coordinate system:
Going forward, there are other aspects of the code I am to write for example if I have an input image sphereVoxels as in above, in addition to visualizing it, I would want to find north, south east, west, top and bottom locations in the image, as well as number and count the coordinates of the vertices, plus more.
I foresee this would likely become confusing for me if I don’t stick to one coordinate system, and considering that the visualization tools predominantly use the right-hand coordinate system, I would want to stick with that from the onset. However, I really do not know how to go about this.
Right-hand coordinate system:
Any suggestions to get through this?
When you call meshgrid, the dimensions x and y axes are switched (contrary to ndgrid). For example, in your case, it means that rowsInImage is a [120x100x200] = [x,y,z] array and not a [100x120x200] = [y,x,z] array even if meshgrid was called with arguments in the y,x,z order. I would change those two lines to be in the classical x,y,z order :
[columnsInImage, rowsInImage, pagesInImage] = meshgrid(1:imageSizeX, 1:imageSizeY, 1:imageSizeZ);
vertexCoords = [columnsInImage(:), rowsInImage(:), pagesInImage(:)];

PyPlot - Tricky axis color and labeling issues

I am fairly new to Matplotlib. This idea behind this figure is to graph temperature highs and lows. I've run into trouble with the xaxis and right yaxis.
For the xaxis, the color of the font doesn't want to change even though I call tick_params(labelcolor='#b6b6b6'). Also, the dates should only span from Jan - Dec. For unknown reasons, Matplotlib is prepending an extra Dec and appending an extra Jan, causing the text to flow outside of the graph's spine bounds. I want to remove these extra months.
For the right yaxis, I'm not sure I understand the use of subplots properly. I want to convert the ˚C temperatures in the left yaxis to ˚F and use the converted temps for the secondary yaxis.
Here's some code to reproduce something similar to what I've got.
import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.dates as dates
import matplotlib.ticker as ticker
# generate some data to plot
highs = np.linspace(0, 40, 365) # these numbers will escalate instead of fluctuate, but the problem with the axes will still be the same.
lows = np.linspace(-40, 0, 365)
date_rng = pd.date_range('1/1/2015', '12/31/2015', freq='D')
data = {'highs': highs, 'lows': lows}
to_plot = pd.DataFrame(data, index=date_rng)
fig, ax = plt.subplots()
# plot the basic data
lines = ax.plot(date_rng, to_plot['lows'], '-',
date_rng, to_plot['highs'], '-')
# get the axes reference
ax1 = plt.gca()
# fill in between the lines
ax1.fill_between(date_rng,
to_plot['lows'], to_plot['highs'],
facecolor='#b6b6b6', # gradient fillbetween
alpha=.2)
# set the xaxis to only 12 months and space the names.
ax1.xaxis.set_major_locator(dates.MonthLocator())
ax1.xaxis.set_minor_locator(dates.MonthLocator(bymonthday=15, interval=1))
ax1.xaxis.set_major_formatter(ticker.NullFormatter())
ax1.xaxis.set_minor_formatter(dates.DateFormatter('%b'))
for tick in ax1.xaxis.get_minor_ticks():
tick.tick1line.set_markersize(0)
tick.tick2line.set_markersize(0)
tick.label1.set_horizontalalignment('center')
# add a right y axis and set all yaxis properties
ax1.set_ylim([-50, 50])
# change the color and sizes scheme
info_colors = '#b6b6b6'
bold_colors = '#777777'
# graph lines
ax1.lines[0].set_color('#e93c00') # top redish orange
ax1.lines[1].set_color('#009ae9') # btm blue
plt.setp(lines, lw=.8, alpha=1)
# spines
ax.spines['top'].set_visible(False)
for pos in ['bottom', 'right', 'left']:
ax.spines[pos].set_edgecolor(info_colors)
# set the title
plt.title('Record Temps, 2005-15: Ann Arbour, MI', fontsize=10, color=bold_colors)
# ticks
ax1.tick_params(axis='both', color=info_colors, labelcolor=info_colors, length=5, direction='out', pad=7, labelsize=8)
# add a legend and edit its properties
leg = plt.legend(['Highs','Lows'], frameon=False, loc=0, fontsize='small')
for text in leg.get_texts():
text.set_color(info_colors)
plt.ylabel('˚C', color=info_colors)
# set extra yaxis label
ax2 = ax.twinx()
ax2.set_ylabel('˚F', color=info_colors)
ax2.tick_params('y', colors=info_colors)
To change the color of the minor labels, put the following code after ax1.xaxis.set_minor_formatter(dates.DateFormatter('%b')):
ax1.xaxis.set_tick_params(which='minor', colors=info_colors)

Wrong 3D plot in Matlab

I'm trying to create 3D plots in Matlab, and I have practically no experience. I'd really like to draw the figure described by these equations:
x = cos(u) sinh(t) / (cosh(t) - cos(u));
y = cos(u) sin(u) / (cosh(t) - cos(u));
z = sin(u);
where both u and t vary from -pi to pi. This is what Paul Bourke calls Ghost Plane.
clc
clear
a = -pi:.01:pi;
b = -pi:.01:pi;
[U,T] = meshgrid(a,b);
X = (cos(U).*sinh(T))./(cosh(T)-cos(U));
Y = (cos(U).*sin(U))./(cosh(T)-cos(U));
Z = sin(U);
figure
surf(X,Y,Z)
With the code, I get something... indescribable. How do I plot the figure?
Your code is correct. You just need to zoom in.
Some tips to make this more viewable:
Use less fine grids, by changing a = ... and b = ... to: linspace(-pi,pi,40);
Add this ,'FaceColor','none','EdgeColor','interp'); to the surf command to only plot the lines and those in color.
Add this axis equal vis3d; after the surf command, so the axis will have correct scaling and behave well while rotating.
Add whitebg('black'); and grid off; to make the background black.
Change to surf(X,Y,Z,-U,...); and add colormap('HSV'); if you want the same colors as in the original.
set(gca,'xtick',[]); set(gca,'xticklabel',[]); set(gca,'yticklabel',[]); set(gca,'ytick',[]); to remove the axis ticks
You might want to use the cameratoolbar to: Change the projection to perspective projection, zoom all the way out, move the camera in very close, to get nice perspective distortions.
Voilà: