from mpl_toolkits.basemap import Basemap
import matplotlib.pyplot as plt
import numpy as np
m = Basemap(projection='cyl',resolution='c',area_thresh=10,llcrnrlon=-180,urcrnrlon=180,\
llcrnrlat=-90,urcrnrlat=90)
m.etopo()
Actually, I did not know how to provide the lat, lon, lat0, and lon0 parameters required to show scale-bar. How to provide them?
map.drawmapscale(????,barstyle='simple',units='km',fontsize=9,labelstyle='simple',fontcolor='k')
The tutorial at
http://matplotlib.org/basemap/api/basemap_api.html describe it as follows:
drawmapscale(lon, lat, lon0, lat0, length, barstyle='simple', units='km', fontsize=9, yoffset=None, labelstyle='simple', fontcolor='k', fillcolor1='w', fillcolor2='k', ax=None, format='%d', zorder=None)
Would appreciate if someone could help me.
It appears that drawmapscale doesn't support Basemap instances with projection='cyl' (and possibly others; I have only checked projection='cyl' and projection='moll'):
In [7]: m = Basemap(projection='cyl',resolution='c',area_thresh=10,llcrnrlon=-180,\
urcrnrlon=180, llcrnrlat=-90,urcrnrlat=90)
In [8]: m.etopo()
Out[8]: <matplotlib.image.AxesImage at 0x10a899e90>
In [10]: m.drawmapscale(50, -75, 0, 0, 400)
This results in the following error:
ValueError: cannot draw map scale for projection='cyl'
But drawmapscale does appear to work for other projections. Using Mollweide, for example:
In [11]: m = Basemap(projection='moll', lon_0=0)
In [12]: m.etopo()
Out[12]: <matplotlib.image.AxesImage at 0x10c299450>
In [13]: m.drawmapscale(50, -75, 0, 0, 400)
Out[13]:
[<matplotlib.lines.Line2D at 0x11d2e41d0>,
<matplotlib.lines.Line2D at 0x109cd4d90>,
<matplotlib.lines.Line2D at 0x11d2e4750>,
<matplotlib.text.Text at 0x11d2e4d90>,
<matplotlib.text.Text at 0x11d2e5610>]
Unfortunately the Basemap API doesn't seem to mention anything about it not working for all projections. But here seems to be a potential workaround.
Related
I have a problem figuring out how to have Seaborn show the right values in a logarithmic barplot. A value of mine should be, in the ideal case, be 1. My dataseries (5,2,1,0.5,0.2) has a set of values that deviate from unity and I want to visualize these in a logarithmic barplot. However, when plotting this in the standard log-barplot it shows the following:
But the values under one are shown to increase from -infinity to their value, whilst the real values ought to look like this:
Strangely enough, I was unable to find a Seaborn, Pandas or Matplotlib attribute to "snap" to a different horizontal axis or "align" or ymin/ymax. I have a feeling I am unable to find it because I can't find the terms to shove down my favorite search engine. Some semi-solutions I found just did not match what I was looking for or did not have either xaxis = 1 or a ylog. A try that uses some jank Matplotlib lines:
If someone knows the right terms or a solution, thank you in advance.
Here are the Jupyter cells I used:
{1}
import seaborn as sns
import pandas as pd
import matplotlib.pyplot as plt
data = {'X': ['A','B','C','D','E'], 'Y': [5,2,1,0.5,0.2]}
df = pd.DataFrame(data)
{2}
%matplotlib widget
g = sns.catplot(data=df, kind="bar", y = "Y", x = "X", log = True)
{3}
%matplotlib widget
plt.vlines(x=data['X'], ymin=1, ymax=data['Y'])
You could let the bars start at 1 instead of at 0. You'll need to use sns.barplot directly.
The example code subtracts 1 of all y-values and sets the bar bottom at 1.
import matplotlib.pyplot as plt
from matplotlib.ticker import NullFormatter
import seaborn as sns
import pandas as pd
import numpy as np
data = {'X': ['A', 'B', 'C', 'D', 'E'], 'Y': [5, 2, 1, 0.5, 0.2]}
df = pd.DataFrame(data)
ax = sns.barplot(y=df["Y"] - 1, x=df["X"], bottom=1, log=True, palette='flare_r')
ax.axhline(y=1, c='k')
# change the y-ticks, as the default shows too few in this case
ax.set_yticks(np.append(np.arange(.2, .8, .1), np.arange(1, 7, 1)), minor=False)
ax.set_yticks(np.arange(.3, 6, .1), minor=True)
ax.yaxis.set_major_formatter(lambda x, pos: f'{x:.0f}' if x >= 1 else f'{x:.1f}')
ax.yaxis.set_minor_formatter(NullFormatter())
ax.bar_label(ax.containers[0], labels=df["Y"])
sns.despine()
plt.show()
PS: With these specific values, the plot might go without logscale:
Edited, adding suggestion from an answer
I have a list of vertices in lat/lon that define corners of a polygon on a map. I would like to draw that polygon on a map using cartopy, where the edges are great circles. I've tried following the examples at https://scitools.org.uk/cartopy/docs/v0.5/matplotlib/introductory_examples/02.polygon.html, but I can't get it to work. Here's what I have tried so far:
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import matplotlib.patches as mpatches
map_proj = ccrs.Orthographic(central_latitude=0.0, central_longitude=80.0)
ax = plt.axes(projection=map_proj)
ax.set_global() # added following an answer to my question
ax.gridlines()
ax.coastlines(linewidth=0.5, color='k', resolution='50m')
lat_corners = np.array([-20., 0., 50., 30.])
lon_corners = np.array([ 20., 90., 90., 30.]) + 15.0 # offset from gridline for clarity
poly_corners = np.zeros((len(lat_corners), 2), np.float64)
poly_corners[:,0] = lon_corners
poly_corners[:,1] = lat_corners
poly = mpatches.Polygon(poly_corners, closed=True, ec='r', fill=False, lw=1, fc=None, transform=ccrs.Geodetic())
ax.add_patch(poly)
Notice that the lines are not great circles, and there seem to be more than four vertices. I feel like this is such a simple thing to do there must be a way, but I can't figure that out from the cartopy documentation.
I think this is probably because Cartopy's default transform resolution is too low for this projection. You can work around this by forcing a higher resolution:
map_proj = ccrs.Orthographic(central_latitude=0.0, central_longitude=80.0)
map_proj._threshold /= 100.
...
This gives nice curved great circle arcs.
Mind, that the example uses
ax.set_global()
Here is a runnable code and output.
Credits go to: the asker, ImportanceOfBeingErnest, and ajdawson.
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import matplotlib.patches as mpatches
map_proj = ccrs.Orthographic(central_latitude=0.0, central_longitude=80.0)
map_proj._threshold /= 100. # the default values is bad, users need to set them manually
ax = plt.axes(projection=map_proj)
ax.set_global() # added following an answer to my question
ax.gridlines()
ax.coastlines(linewidth=0.5, color='k', resolution='50m')
lat_corners = np.array([-20., 0., 50., 30.])
lon_corners = np.array([ 20., 90., 90., 30.]) + 15.0 # offset from gridline for clarity
poly_corners = np.zeros((len(lat_corners), 2), np.float64)
poly_corners[:,0] = lon_corners
poly_corners[:,1] = lat_corners
poly = mpatches.Polygon(poly_corners, closed=True, ec='r', fill=True, lw=1, fc="yellow", transform=ccrs.Geodetic())
ax.add_patch(poly)
plt.show()
Output:
So I want to plot a 3d map using matplotlib basemap. But an error message comes popping up.
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
from mpl_toolkits.basemap import Basemap
from matplotlib.collections import PolyCollection
import numpy as np
map = Basemap(llcrnrlon=-20,llcrnrlat=0,urcrnrlon=15,urcrnrlat=50,)
fig = plt.figure()
ax = Axes3D(fig)
#ax.set_axis_off()
ax.azim = 270
ax.dist = 7
polys = []
for polygon in map.landpolygons:
polys.append(polygon.get_coords())
lc=PolyCollection(polys,edgecolor='black',facecolor='#DDDDDD',closed=False)
ax.add_collection3d(lc)
ax.add_collection3d(map.drawcoastlines(linewidth=0.25))
ax.add_collection3d(map.drawcountries(linewidth=0.35))
lons = np.array([-13.7, -10.8, -13.2, -96.8, -7.99, 7.5, -17.3, -3.7])
lats = np.array([9.6, 6.3, 8.5, 32.7, 12.5, 8.9, 14.7, 40.39])
cases = np.array([1971, 7069, 6073, 4, 6, 20, 1, 1])
deaths = np.array([1192, 2964, 1250, 1, 5, 8, 0, 0])
places = np.array(['Guinea', 'Liberia', 'Sierra Leone','United States','Mali','Nigeria', 'Senegal', 'Spain'])
x, y = map(lons, lats)
ax.bar3d(x, y, np.zeros(len(x)), 2, 2, deaths, color= 'r', alpha=0.8)
plt.show()
I got an error message on line 21{i.e ax.add_collection3d(map.drawcoastlines(linewidth=0.25))} saying:-
'It is not currently possible to manually set the aspect '
NotImplementedError: It is not currently possible to manually set the aspect on 3D axes'
I found this question because I had the exact question.
I later chanced upon some documentation that revealed the workaround - if setting of aspect is not implemented, then let's not set it by setting fix_aspect to false:
map = Basemap(fix_aspect=False)
EDIT:
I suppose I should add a little more to my previous answer to make it a little easier to understand what to do.
The NotImplementedError is a deliberate addition by the matplotlib team, as can be seen here. What the error is saying is that we are trying to fix the aspect ratio of the plot, but this is not implemented in 3d plots.
This error occurs when using mpl_toolkits.basemap() with 3d plots as it sets fix_aspect=True by default.
Therefore, to do away with the NotImplementedError, one can consider adding fix_aspect=False when calling mpl_toolkits.basemap(). For example:
map = Basemap(llcrnrlon=-20,llcrnrlat=0,urcrnrlon=15,urcrnrlat=50,fix_aspect=False)
I have an array of y-values that form a line. Additionally, I have an array with the same number of elements as the y-array of values ranging from 0 to 1. We'll call this array 'z'. I want to plot the array of y-values so that the color of each point corresponds with the z-value.
In gnuplot, you can do this using the 'lc variable':
plot ’data’ using 1:2:3 with points lc variable
Using the advice from here: Matplotlib scatterplot; colour as a function of a third variable
, I was able to use a scatter plot, which did work:
import matplotlib as mpl
import matplotlib.pyplot as plt
plt.scatter(x, y, c=z, s=1, edgecolors='none', cmap=mpl.cm.jet)
plt.colorbar()
plt.show()
Is there a way to do this with the plot method in matplotlib, similar to this?
plt.plot(x, y, c=z)
When I tried the above code, all of the lines just appeared black.
I had the same problem: wanted to plot line(s) with non-uniform color, which I wanted to be dependent on a third variable (z).
But I definitelly wanted to use a line, not markers (as in #joaquin's answer).
I found a solution in a matplotlib gallery example, using the class matplotlib.collections.LineCollection (link here).
Here is my example, which plots trajectories in a Basemap, coloring them according to its height:
import matplotlib.pyplot as plt
from mpl_toolkits.basemap import Basemap
from matplotlib.collections import LineCollection
import numpy as np
m = Basemap(llcrnrlon=-42,llcrnrlat=0,urcrnrlon=5,urcrnrlat=50, resolution='h')
fig = plt.figure()
m.drawcoastlines()
m.drawcountries()
for i in trajectorias:
# for each i, the x (longitude), y (latitude) and z (height)
# are read from a file and stored as numpy arrays
points = np.array([x, y]).T.reshape(-1, 1, 2)
segments = np.concatenate([points[:-1], points[1:]], axis=1)
lc = LineCollection(segments, cmap=plt.get_cmap('Spectral'),
norm=plt.Normalize(250, 1500))
lc.set_array(z)
lc.set_linewidth(2)
plt.gca().add_collection(lc)
axcb = fig.colorbar(lc)
axcb.set_label('cota (m)')
plt.show()
you can use scatter:
plt.scatter(range(len(y)), y, c=z, cmap=cm.hot)
here you have the ipython -pylab session:
In [27]: z = [0.3,0.4,0.5,0.6,0.7,0.2,0.3,0.4,0.5,0.8,0.9]
In [28]: y = [3, 7, 5, 6, 4, 8, 3, 4, 5, 2, 9]
In [29]: plt.scatter(range(len(y)), y, s=60, c=z, cmap=cm.hot)
Out[29]: <matplotlib.collections.PathCollection at 0x9ec8400>
If you want to use plot you can get the equivalent figure as above with (pycrust session):
>>> from matplotlib import pyplot as plt
>>> from matplotlib import cm
>>> y = [3,7,5,6,4,8,3,4,5,2,9]
>>> z = [0.3,0.4,0.5,0.6,0.7,0.2,0.3,0.4,0.5,0.8,0.9]
>>> for x, (v, c) in enumerate(zip(y,z)):
... plt.plot(x,v,marker='o', color=cm.hot(c))
...
[<matplotlib.lines.Line2D object at 0x0000000008C42518>]
[<matplotlib.lines.Line2D object at 0x0000000008C426D8>]
[<matplotlib.lines.Line2D object at 0x0000000008C42B38>]
[<matplotlib.lines.Line2D object at 0x0000000008C452B0>]
[<matplotlib.lines.Line2D object at 0x0000000008C45438>]
[<matplotlib.lines.Line2D object at 0x0000000008C45898>]
[<matplotlib.lines.Line2D object at 0x0000000008C45CF8>]
[<matplotlib.lines.Line2D object at 0x0000000008C48198>]
[<matplotlib.lines.Line2D object at 0x0000000008C485F8>]
[<matplotlib.lines.Line2D object at 0x0000000008C48A58>]
[<matplotlib.lines.Line2D object at 0x0000000008C4B1D0>]
>>> plt.show()
>>>
I'm creating some GIS-style plots in matplotlib of road networks and the like, so I'm using LineCollection to store and represent all of the roads and color accordingly. This is working fine, I color the roads based on a criteria and the following map:
from matplotlib.colors import ListedColormap,BoundaryNorm
from matplotlib.collections import LineCollection
cmap = ListedColormap(['grey','blue','green','yellow','orange','red','black'])
norm = BoundaryNorm([0,0.5,0.75,0.9,0.95,1.0,1.5,100],cmap.N)
roads = LineCollection(road_segments, array=ratios, cmap=cmap, norm=norm)
axes.add_collection(roads)
This works fine, however I would really like to have linewidths defined in a similar manner to the color map - ranging from 0.5 to 5 for each color
Does anyone know of a clever way of doing this?
The linewidths keyword.
import matplotlib.pyplot as plt
from matplotlib.collections import LineCollection
axes = plt.axes()
roads = LineCollection([
[[0, 0], [1, 1]],
[[0, 1], [1, 0]]
],
colors=['black', 'red'],
linewidths=[3, 8],
)
axes.add_collection(roads)
plt.show()
HTH