How to rotate matplotlib.patches.Polygon? - python-3.x

How to rotate matplotlib.patches.Polygon? The output from the code below shows nothing.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib as mpl
fig, ax = plt.subplots(figsize=(10,3))
x = [0.3,0.6,.5,.4]
y = [0.7,0.7,0.9,0.9]
trapezoid = patches.Polygon(xy=list(zip(x,y)), fill=False)
t_start = ax.transData
t = mpl.transforms.Affine2D().rotate_deg(-45)
t_end = t_start + t
trapezoid.set_transform(t_end)
print(repr(t_start))
print(repr(t_end))
ax.add_patch(trapezoid)
plt.show()

When composing the tranformations, you must use t_end = t + t_start instead of t_start + t. The + operator is overloaded: a + b means to first apply a and then apply b. For affine transformations this means the matrix product B#A where A and B are the transformation matrices of a and b respectively. The matrix product is not commutative.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patches as patches
import matplotlib as mpl
import copy
fig, ax = plt.subplots(figsize=(10,3))
x = [0.3,0.6,.5,.4]
y = [0.7,0.7,0.9,0.9]
trapezoid = patches.Polygon(xy=list(zip(x,y)), fill=False)
ax.add_patch(copy.copy(trapezoid))
t_start = ax.transData
t = mpl.transforms.Affine2D().rotate_deg(-45)
t_end = t + t_start
trapezoid.set_transform(t_end)
ax.add_patch(trapezoid)
plt.show()

Related

How to add color and legend by points' label one by one in python?

I want to divide and color points,val_lab(611,3) by their labels,pred_LAB(611,)
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
fig = plt.figure()
ax = plt.axes(projection = '3d')
ax.set_xlabel('L')
ax.set_ylabel('A')
ax.set_zlabel('B')
for i in range(0, len(val_lab)):
ax.scatter3D(
val_lab[i,0],
val_lab[i,1],
val_lab[i,2],
s = 8,
marker='o',
c = pred_LAB
#cmap = 'rainbow'
)
#ax.legend(*points.legend_elements(), title = 'clusters')
plt.show()
The problem is it shows error,
c' argument has 611 elements, which is not acceptable for use with 'x'
with size 1, 'y' with size 1.
However, if the dataset only have ten points,it can show the figure correctly, I don't know how to solve this problem, besides, how to add legend of this figure?
In your solution you would want to replace c = pred_LAB with c = pred_LAB[i]. But you do not have to use a for loop to plot the data. You can just use the following:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# generate random input data
val_lab = np.random.randint(0,10,(611,3))
pred_LAB = np.random.uniform(0,1, (611,))
# plot data
fig = plt.figure()
ax = plt.axes(projection = '3d')
ax.set_xlabel('L')
ax.set_ylabel('A')
ax.set_zlabel('B')
# create one 3D scatter plot - no for loop
ax.scatter3D(
val_lab[:,0],
val_lab[:,1],
val_lab[:,2],
s = 8,
marker='o',
c = pred_LAB,
cmap = 'rainbow',
label='my points'
)
# add legend
plt.legend()
plt.show()

How do I plot vertical strips in matplotlib

I want to show the value of a 0 or 1 array on a plot with other timeseries.
How can I achieve something like the grey lines below - except mine will oscillate a lot more.
series.
For example, how to add osc here:
import numpy as np
import matplotlib.pyplot as plt
import datetime
import pandas as pd
n = 100
x = range(n)
y = np.random.rand(100)
osc = np.random.randint(2, size=n)
plt.plot(x,y)
plt.show(block=True)
Well, you can loop through the values and call axvspan(x0,x1,color=...,alpha=...);
import numpy as np
import matplotlib.pyplot as plt
n = 100
x = range(n)
y = np.random.randn(100).cumsum()
osc = np.random.randint(2, size=n)
plt.plot(x, y, color='crimson')
for x0, x1, os in zip(x[:-1], x[1:], osc):
if os:
plt.axvspan(x0, x1, color='blue', alpha=0.2, lw=0)
plt.margins(x=0)
plt.show()
Note that only the first 99 values of osc are used, as there are only 99 intervals.
See code below:
import numpy as np
import matplotlib.pyplot as plt
n = 100
x = range(n)
y = np.random.rand(100)
osc = np.random.randint(2, size=n)
fig,ax = plt.subplots()
ax.plot(x,y)
ax.axvspan(0,5,facecolor='grey', alpha=0.4)
plt.show()
Documentation on axvspan can be found here: https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.axvspan.html.
Similarly you can use axvline for just vertical lines.

colour map grids based on value in pandas dataframe

I want to fill the gridded map with colors based on the value of interest. A sample data is here:
import pandas as pd
df = pd.DataFrame()
df['lon'] = [100,105,110,115,120,125,130]
df['lat'] = [38,40,42,44,46,48,50]
df['value'] = [1,2,3,4,5,6,7]
Specifically, is it possible to do this with Cartopy? I found a similar question here:https://stackoverflow.com/questions/53412785/plotting-pandas-csv-data-onto-cartopy-map. But that post was to plot scattered points, I need to fill the grids with colors.
I myself tried:
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
lon, lat = np.meshgrid(df['lon'], df['lat'])
fig = plt.figure(figsize=[15,15])
ax = plt.axes(projection=ccrs.PlateCarree())
ax.pcolormesh(lon,lat,df['variable'],latlon=True,cmap='jet')
plt.show()
The error is at ax.pcolormesh(...), it says "not enough values to unpack (expected 2, got 1)"
Many thanks for your help.
For discrete data you can create rectangular patches for each point. Here is a possible solution for your sample data set. Each row of data (lat, long, value) is used to create a rectangular patch. The value is normalized by dividing with max(value) to enable using colormap for coloring the patches.
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import matplotlib.patches as mpatches
def make_rect(clon, clat, dlon, dlat):
lon_min = clon - dlon/2.
lat_min = clat - dlat/2.
lon_max = clon + dlon/2.
lat_max = clat + dlat/2.
# clockwise from LL
#lons = [lon_min, lon_min, lon_max, lon_max, lon_min]
#lats = [lat_min, lat_max, lat_max, lat_min, lat_min]
ll = [lon_min,lat_min]
ul = [lon_min,lat_max]
ur = [lon_max,lat_max]
lr = [lon_max,lat_min]
return [ll, ul, ur, lr, ll]
df = pd.DataFrame()
df['lon'] = [100,105,110,115,120,125,130]
df['lat'] = [38,40,42,44,46,48,50]
df['value'] = [1,2,3,4,5,6,7] # not suffice for meshgrid plot
# The colormap to use.
cm = plt.cm.get_cmap('jet')
fig = plt.figure(figsize=[8,6])
ax = plt.axes(projection=ccrs.PlateCarree(), extent=[95, 134, 35, 52])
# plot the red dots using the available data
# comment out if not needed
ax.plot(df['lon'], df['lat'], 'ro')
# plot rectangular patches at the data points
dlon, dlat = 5, 2 #spacings between data points
for lon1, lat1, val1 in zip(df['lon'], df['lat'], df['value']):
pcorners = make_rect(lon1, lat1, dlon, dlat)
poly = mpatches.Polygon(pcorners, ec='gray', fill=True, lw=0.25, \
fc=cm(val1 / max(df['value'])), transform=ccrs.PlateCarree())
ax.add_patch(poly)
ax.gridlines(draw_labels=True)
plt.show()
The output plot:

How to plot 3d rgb histogram of a colored image in python

I want to plot a 3d histogram of a colored image but I can only plot R and G value. what am I doing wrong here? or is there an easier way to do so
import numpy as np
import matplotlib.image as mpimg
from matplotlib import pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
img = mpimg.imread('model/obj4__0.png')
pixels = img.shape[0]*img.shape[1]
channels = 3
data = np.reshape(img[:, :, :channels], (pixels, channels))
histo_rgb, _ = np.histogramdd(data, bins=256)
histo_rg = np.sum(histo_rgb, 2)
levels = np.arange(256)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for g in levels:
ax.bar(levels, histo_rg[:, g], zs=g, zdir='y', color='r')
ax.set_xlabel('Red')
ax.set_ylabel('Green')
ax.set_zlabel('Number of pixels')
plt.show()
If I understand your question well, I have the same issue, and found this: https://www.bogotobogo.com/python/OpenCV_Python/python_opencv3_image_histogram_calcHist.php
Here is the code for your question:
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('images/GoldenGateSunset.png', -1)
cv2.imshow('GoldenGate',img)
color = ('b','g','r')
for channel,col in enumerate(color):
histr = cv2.calcHist([img],[channel],None,[256],[0,256])
plt.plot(histr,color = col)
plt.xlim([0,256])
plt.title('Histogram for color scale picture')
plt.show()
while True:
k = cv2.waitKey(0) & 0xFF
if k == 27: break # ESC key to exit
cv2.destroyAllWindows()
Note that this use cv2 functions but you can convert it to works with Numpy.
I will try to figure out with numpy and give you an update.

Reprojecting GOES16 Satellite Data with Cartopy and Pyproj

I'm trying to reproject GOES16 Full Disk imagery using cartopy or pyproj. I'd like to get it into a different projection. For this example, I try to reproject it to Mercator. However, when I run the code I get a full globe image of the data not in Mercator projection and without any cartopy features. I feel like I'm missing a simple step but can't figure out just what it is. Below is a reproducible example - I'm using Python 3.5.
import matplotlib
import matplotlib.pyplot as plt
from siphon.catalog import TDSCatalog, get_latest_access_url
import numpy as np
from datetime import datetime, timedelta
import cartopy.crs as ccrs
import cartopy.feature as cfeature
# query data
nowdate = datetime.utcnow()
cat = TDSCatalog('http://thredds-jumbo.unidata.ucar.edu/thredds/catalog/satellite/goes16/GOES16/Products/SeaSurfaceTemperature/FullDisk/' + \
str(nowdate.year) + str("%02d"%nowdate.month) + str("%02d"%nowdate.day) + '/catalog.xml')
dataset_name = sorted(cat.datasets.keys())[-1]
dataset = cat.datasets[dataset_name]
# load netcdf and read variables
nc = dataset.remote_access()
sst = np.array(nc.variables['SST'][:,:])
sst[np.isnan(sst)] = -1
sst = np.ma.array(sst)
sst[sst < 0] = np.ma.masked
X = nc.variables['x'][:]
Y = nc.variables['y'][:]
# define projections
proj_var = nc.variables['goes_imager_projection']
globe = ccrs.Globe(ellipse='sphere', semimajor_axis=proj_var.semi_major_axis,
semiminor_axis=proj_var.semi_minor_axis)
# define reprojection target
proj = ccrs.Mercator(central_longitude=proj_var.longitude_of_projection_origin,
latitude_true_scale=proj_var.latitude_of_projection_origin,
globe=globe)
# Plot
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(1, 1, 1, projection=proj)
ax.coastlines(resolution='50m', color='black')
ax.add_feature(cfeature.STATES, linestyle=':', edgecolor='black')
ax.add_feature(cfeature.BORDERS, linewidth=2, edgecolor='black')
im = ax.imshow(sst, extent=(X.min(), X.max(), Y.min(), Y.max()), origin='upper')
# try again, this time with pyproj
from pyproj import Proj
p = Proj(proj='geos', h=proj_var.perspective_point_height, lon_0=proj_var.longitude_of_projection_origin, sweep=proj_var.sweep_angle_axis)
X = nc.variables['x'][:] * proj_var.perspective_point_height
Y = nc.variables['y'][:] * proj_var.perspective_point_height
XX, YY = np.meshgrid(X,Y)
lons, lats = p(XX, YY, inverse=True)
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(1, 1, 1, projection=proj)
ax.coastlines(resolution='50m', color='black')
ax.add_feature(cfeature.STATES, linestyle=':', edgecolor='black')
ax.add_feature(cfeature.BORDERS, linewidth=2, edgecolor='black')
im = ax.imshow(sst, extent=(lons.min(), lons.max(), lats.min(), lats.max()), origin='upper')
Your methodology was correct, but you have to use pcolormesh instead of imshow.
This should work:
from datetime import datetime
import cartopy.feature as cfeature
from siphon.catalog import TDSCatalog
import matplotlib.pyplot as plt
from matplotlib import patheffects
import metpy
from metpy.plots import colortables
import xarray as xr
from xarray.backends import NetCDF4DataStore
%matplotlib inline
nowdate = datetime.utcnow()
cat = TDSCatalog('http://thredds-jumbo.unidata.ucar.edu/thredds/catalog/satellite/goes16/GOES16/Products/SeaSurfaceTemperature/FullDisk/' + \
str(nowdate.year) + str("%02d"%nowdate.month) + str("%02d"%nowdate.day) + '/catalog.xml')
dataset_name = sorted(cat.datasets.keys())[-1]
dataset = cat.datasets[dataset_name]
ds = dataset.remote_access(service='OPENDAP')
ds = NetCDF4DataStore(ds)
ds = xr.open_dataset(ds)
dqf = ds.metpy.parse_cf('DQF')
dat = ds.metpy.parse_cf('SST')
proj = dat.metpy.cartopy_crs
dat = dat.where(dqf == 0)
dat = dat.where(dat.variable > 274)
dat = dat.where(dat.variable < 310)
dat = dat - 273.15
# Plot in Mercator
import cartopy.crs as ccrs
newproj = ccrs.Mercator()
fig = plt.figure(figsize=[12, 12], dpi=100)
ax = fig.add_subplot(1,1,1, projection=newproj)
im = ax.pcolormesh(dat['x'], dat['y'], dat, cmap='jet', transform=proj, vmin=-2, vmax=38)
ax.set_extent((dat['x'].min() + 4000000, dat['x'].max()- 3200000, dat['y'].min()+ 5500000, dat['y'].max()- 650000), crs=proj)

Resources