I'm working on a robot that has a downward facing camera. To show how my localization filter is doing, I'd like to plot the grass patches in a graph with the path of the robot. This means that I have to place rotated images in a graph. Is it possible to plot an image relative to the origin, or each other, with rotation?
I want to avoid using ndimage.rotate as it rotates the data of the image, not the image in the axes.
I tried to use:
import import matplotlib.pyplot as mplplt
import matplotlib.transforms as mpltf
import matplotlib.image as mplimg
grass = mplimg.imread("grass0.png")
grass = mplimg.imread("grass1.png")
fig = mplplt.figure()
ax = mplplt.subplot(111)
#Show previous grass image
ax.imshow(grass, extent = (-5, 5, -5, 5))
#Next image
tf = mpltf.Affine2D().rotate_deg(30).translate(1,1)
ax.imshow(grass1, extent = (-5, 5, -5, 5))#, transform = tf)
mplplt.show()
to no avail, the image was located on top of the previous one.
Here's an example with two copies of an image, different rotations and translations, one over the next. Plenty of limit-adjusting and Z-ordering and making a comprehensible function and all left to do. I Frankensteined your code and http://matplotlib.org/examples/api/demo_affine_image.html:
import matplotlib.pyplot as mplplt
import matplotlib.transforms as mpltf
import matplotlib.image as mplimg
def imshow_affine(ax, z, *kl, **kwargs):
im = ax.imshow(z, *kl, **kwargs)
x1, x2, y1, y2 = im.get_extent()
im._image_skew_coordinate = (x2, y1)
return im
grass = mplimg.imread("hammer_map.png")
fig = mplplt.figure()
ax = mplplt.subplot(111)
tf = mpltf.Affine2D().rotate_deg(30).translate(-1,-1) + ax.transData
im1 = imshow_affine(ax, grass, interpolation='none',
origin='lower',
extent=[1, 7, 0, 5], clip_on=False, zorder=2)
im1.set_transform(tf)
tf = mpltf.Affine2D().rotate_deg(190).translate(-2,1) + ax.transData
im2 = imshow_affine(ax, grass, interpolation='none',
origin='lower',
extent=[-3, 3, -2, 3], clip_on=False, zorder=1)
im2.set_transform(tf)
mplplt.show()
Related
I got some sort of a problem with a pendulum animation, I tried to display my animation (the pendulum's movement) next to a graph in two separate axes, but when I try my code, it barely works displaying two axes that overlap on one another... Here is what I tried:
PS: best would be that the circles I was intended to add at the end of my pendulum appear on the final animation, but I really have no idea how to put them only on a particular ax
from numpy import sin, cos, pi, array
import numpy as np
import scipy.integrate
import matplotlib.pyplot as plt
import matplotlib.animation as animation
g = 10
y0 = np.array([np.pi / 2.0, 0]) # angle, vitesse
j = 0.2
def f(y, t):
return np.array([y[1], -g * np.sin(y[0])-j*y[1]])
t = np.linspace(0, 100, 10000)
y = scipy.integrate.odeint(f, y0, t)
theta, thetadot = y[:, 0], y[:, 1]
fig, axs = plt.subplots(1,2)
axs[0] = fig.add_subplot(xlim=(-1.5, 1.5), ylim=(-1.5, 1.5))
axs[0].grid()
axs[0].set_box_aspect(1)
# anchor = plt.Circle((0, 0), 0.01, color='black')
# mass = plt.Circle((sin(y0[0]),-cos(y0[0])), 0.2, color='black')
pendulums = axs[0].plot((0, sin(y0[0])), (0, -cos(y0[0])), 'o-', color = 'black')
# plt.gca().add_patch(weight) # adding circles
# plt.gca().add_patch(attach)
phase = axs[1].plot(theta,thetadot)
def animate(i):
angle = theta[i]
x = (0, sin(angle))
y = (0, -cos(angle))
#mass.center = (x[1],y[1])
pendulums[0].set_data(x, y)
anim = animation.FuncAnimation(fig, animate, interval=10)
plt.show()
I have a set of X,Y data points(from Remote sensing image),and I drew a scatter plot through the tutorial, the link is as follows:Generate a heatmap in MatPlotLib using a scatter data set
But when I try to draw a heat map, an error occurs:Heatmap cannot be displayed.
Is there a way to display two-dimensional data in a heat map, the different colors in the heat map represent the density of the pixels?
This is my code and result:
import rasterio
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.cm as cm
from scipy.ndimage.filters import gaussian_filter
def myplot(x, y, s, bins=1000):
heatmap, xedges, yedges = np.histogram2d(x, y, bins=bins)
heatmap = gaussian_filter(heatmap, sigma=s)
extent = [xedges[0], xedges[-1], yedges[0], yedges[-1]]
return heatmap.T, extent
fig, axs = plt.subplots(1, 2)
dataset = rasterio.open('E:/Jupyter Notebook/LC81490312016259LGN00/LC8_subset_layerstacking.tif')
red_band = dataset.read(4)
NIR_band = dataset.read(5)
np.seterr(divide='ignore', invalid='ignore')
ndvi = (NIR_band.astype(float)-red_band.astype(float))/(NIR_band.astype(float)+red_band.astype(float))
ndvi_flat = np.ndarray.flatten(ndvi)
red_band_flat = np.ndarray.flatten(red_band)
x = ndvi_flat
y = red_band_flat
sigmas = [0, 16]
for ax, s in zip(axs.flatten(), sigmas):
if s == 0:
ax.plot(x, y, 'k.', markersize=0.1)
#ax.set_aspect('equal')
ax.set_title("Scatter plot")
ax.set_xlabel('NDVI')
ax.set_ylabel('Red Reflectance')
else:
img, extent = myplot(x, y, s)
ax.imshow(img, origin='lower',cmap=cm.jet)
ax.set_title("Smoothing with $\sigma$ = %d" % s)
ax.set_xlabel('NDVI')
ax.set_ylabel('Red Reflectance')
plt.show()
The left image is a black scatter plot (no pixel density information), and the right image is a heat map
The code and the data I need to process are stored in GitHub:https://github.com/Flyinfish-gzh/remote-sensing-data-visualization
I want to animate a point moving along a path from one location to another on the map.
For example, I drawn a path from New York to New Delhi, using Geodetic transform. Eg. taken from docs Adding data to the map
plt.plot([ny_lon, delhi_lon], [ny_lat, delhi_lat],
color='blue', linewidth=2, marker='o',
transform=ccrs.Geodetic(),
)
Now i want to move a point along this path.
My idea was to somehow get some (say 50) points, along the path and plot a marker on each point for each frame. But I am not able to find a way to get the points on the path.
I found a function transform_points under classCRS, but I am unable to use this, as this gives me the same number of points i have, not the points in between.
Thanks in advance!
There are a couple of approaches to this.
The matplotlib approach
I'll start with perhaps the most basic if you are familiar with matplotlib, but this approach suffers from indirectly using cartopy's functionality, and is therefore harder to configure/extend.
There is a private _get_transformed_path method on a Line2D object (the thing that is returned from plt.plot). The resulting TransformedPath object has a get_transformed_path_and_affine method, which basically will give us the projected line (in the coordinate system of the Axes being drawn).
In [1]: import cartopy.crs as ccrs
In [3]: import matplotlib.pyplot as plt
In [4]: ax = plt.axes(projection=ccrs.Robinson())
In [6]: ny_lon, ny_lat = -75, 43
In [7]: delhi_lon, delhi_lat = 77.23, 28.61
In [8]: [line] = plt.plot([ny_lon, delhi_lon], [ny_lat, delhi_lat],
...: color='blue', linewidth=2, marker='o',
...: transform=ccrs.Geodetic(),
...: )
In [9]: t_path = line._get_transformed_path()
In [10]: path_in_data_coords, _ = t_path.get_transformed_path_and_affine()
In [11]: path_in_data_coords.vertices
Out[11]:
array([[-6425061.82215208, 4594257.92617961],
[-5808923.84969279, 5250795.00604155],
[-5206753.88613758, 5777772.51828996],
[-4554622.94040482, 6244967.03723341],
[-3887558.58343227, 6627927.97123701],
[-3200922.19194864, 6932398.19937816],
[-2480001.76507805, 7165675.95095855],
[-1702269.5101901 , 7332885.72276795],
[ -859899.12295981, 7431215.78426759],
[ 23837.23431173, 7453455.61302756],
[ 889905.10635756, 7397128.77301289],
[ 1695586.66856764, 7268519.87627204],
[ 2434052.81300274, 7073912.54130764],
[ 3122221.22299409, 6812894.40443648],
[ 3782033.80448001, 6478364.28561403],
[ 4425266.18173684, 6062312.15662039],
[ 5049148.25986903, 5563097.6328901 ],
[ 5616318.74912886, 5008293.21452795],
[ 6213232.98764984, 4307186.23400115],
[ 6720608.93929235, 3584542.06839575],
[ 7034261.06659143, 3059873.62740856]])
We can pull this together with matplotlib's animation functionality to do as requested:
import cartopy.crs as ccrs
import matplotlib.animation as animation
import matplotlib.pyplot as plt
ax = plt.axes(projection=ccrs.Robinson())
ax.stock_img()
ny_lon, ny_lat = -75, 43
delhi_lon, delhi_lat = 77.23, 28.61
[line] = plt.plot([ny_lon, delhi_lon], [ny_lat, delhi_lat],
color='blue', linewidth=2, marker='o',
transform=ccrs.Geodetic(),
)
t_path = line._get_transformed_path()
path_in_data_coords, _ = t_path.get_transformed_path_and_affine()
# Draw the point that we want to animate.
[point] = plt.plot(ny_lon, ny_lat, marker='o', transform=ax.projection)
def animate_point(i):
verts = path_in_data_coords.vertices
i = i % verts.shape[0]
# Set the coordinates of the line to the coordinate of the path.
point.set_data(verts[i, 0], verts[i, 1])
ani = animation.FuncAnimation(
ax.figure, animate_point,
frames= path_in_data_coords.vertices.shape[0],
interval=125, repeat=True)
ani.save('point_ani.gif', writer='imagemagick')
plt.show()
The cartopy approach
Under the hood, cartopy's matplotlib implementation (as used above), is calling the project_geometry method. We may as well make use of this directly as it is often more convenient to be using Shapely geometries than it is matplotlib Paths.
With this approach, we simply define a shapely geometry, and then construct the source and target coordinate reference systems that we want to convert the geometry from/to:
target_cs.project_geometry(geometry, source_cs)
The only thing we have to watch out for is that the result can be a MultiLineString (or more generally, any Multi- geometry type). However, in our simple case, we don't need to deal with that (incidentally, the same was true of the simple Path returned in the first example).
The code to produce a similar plot to above:
import cartopy.crs as ccrs
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import numpy as np
import shapely.geometry as sgeom
ax = plt.axes(projection=ccrs.Robinson())
ax.stock_img()
ny_lon, ny_lat = -75, 43
delhi_lon, delhi_lat = 77.23, 28.61
line = sgeom.LineString([[ny_lon, ny_lat], [delhi_lon, delhi_lat]])
projected_line = ccrs.PlateCarree().project_geometry(line, ccrs.Geodetic())
# We only animate along one of the projected lines.
if isinstance(projected_line, sgeom.MultiLineString):
projected_line = projected_line.geoms[0]
ax.add_geometries(
[projected_line], ccrs.PlateCarree(),
edgecolor='blue', facecolor='none')
[point] = plt.plot(ny_lon, ny_lat, marker='o', transform=ccrs.PlateCarree())
def animate_point(i):
verts = np.array(projected_line.coords)
i = i % verts.shape[0]
# Set the coordinates of the line to the coordinate of the path.
point.set_data(verts[i, 0], verts[i, 1])
ani = animation.FuncAnimation(
ax.figure, animate_point,
frames=len(projected_line.coords),
interval=125, repeat=True)
ani.save('projected_line_ani.gif', writer='imagemagick')
plt.show()
Final remaaaaarrrrrrks....
The approach naturally generalises to animating any type of matplotlib Arrrrtist.... in this case, I took a bit more control over the great circle resolution, and I animated an image along the great circle:
import cartopy.crs as ccrs
import matplotlib.animation as animation
import matplotlib.pyplot as plt
import numpy as np
import shapely.geometry as sgeom
ax = plt.axes(projection=ccrs.Mercator())
ax.stock_img()
line = sgeom.LineString([[-5.9845, 37.3891], [-82.3666, 23.1136]])
# Higher resolution version of Mercator. Same workaround as found in
# https://github.com/SciTools/cartopy/issues/8#issuecomment-326987465.
class HighRes(ax.projection.__class__):
#property
def threshold(self):
return super(HighRes, self).threshold / 100
projected_line = HighRes().project_geometry(line, ccrs.Geodetic())
# We only animate along one of the projected lines.
if isinstance(projected_line, sgeom.MultiLineString):
projected_line = projected_line.geoms[0]
# Add the projected line to the map.
ax.add_geometries(
[projected_line], ax.projection,
edgecolor='blue', facecolor='none')
def ll_to_extent(x, y, ax_size=(4000000, 4000000)):
"""
Return an image extent in centered on the given
point with the given width and height.
"""
return [x - ax_size[0] / 2, x + ax_size[0] / 2,
y - ax_size[1] / 2, y + ax_size[1] / 2]
# Image from https://pixabay.com/en/sailing-ship-boat-sail-pirate-28930/.
pirate = plt.imread('pirates.png')
img = ax.imshow(pirate, extent=ll_to_extent(0, 0), transform=ax.projection, origin='upper')
ax.set_global()
def animate_ship(i):
verts = np.array(projected_line.coords)
i = i % verts.shape[0]
# Set the extent of the image to the coordinate of the path.
img.set_extent(ll_to_extent(verts[i, 0], verts[i, 1]))
ani = animation.FuncAnimation(
ax.figure, animate_ship,
frames=len(projected_line.coords),
interval=125, repeat=False)
ani.save('arrrr.gif', writer='imagemagick')
plt.show()
All code and images for this answer can be found at https://gist.github.com/pelson/618a5f4ca003e56f06d43815b21848f6.
Assume I have drawn some background image like this:
fig, ax = plt.subplots()
imdata = np.random.randn(10, 10)
im = ax.imshow(imdata, extent=(0, 1, 0, 1), aspect='auto',
cmap='coolwarm', interpolation='nearest')
Now I'm adding a number of rectangles like:
rect = matplotlib.patches.Rectangle((0.3,0.3),0.4,0.4)
ax.add_artist(rect)
Now I want to cut several other rectangles out of the previously added rectangle, so the underlying image is shown again. By cut, I really mean that specifying such a "deletion rectangle" will cut out parts from the previously drawn rectangles. So if they overlap, only the overlapping parts will be cut away. Where the "deletion rectangles" do not intersect space occupied by the rectangles above, nothing shall happen to the visible area.
How can I achieve that?
You can use a path to construct the rectangles. To position the rectangles the vertices of the path can be translated and transformed. Then, using the fact that inverted vertices will be cut out of a path, one create holes in the outer rectangle.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch, Rectangle
fig, ax = plt.subplots()
imdata = np.random.randn(10, 10)
# create rectangle, coordinates are ignored
rec = Rectangle((0,0),1,1).get_path()
#the big rectangle
r0 = rec.vertices+0.5
# r1 and r2 are the rectangles to cut out of r0
r1 = 0.6+rec.vertices[::-1]*0.35
r2 = 1+rec.vertices[::-1]*0.35
path = Path(vertices=np.concatenate([r0, r1, r2]),
codes=np.concatenate([rec.codes]*3))
im = ax.imshow(imdata, extent=(0, 2, 0, 2), aspect='equal',
cmap='coolwarm', interpolation='nearest')
patch = PathPatch(path, facecolor='w')
ax.add_patch(patch)
plt.tight_layout()
plt.show()
Or, a solution which makes it easier to specify the coordinates of the rectangles:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch, Rectangle
fig, ax = plt.subplots()
imdata = np.random.randn(10, 10)
def create_rec(x0, y0, width, height):
rec_patch = Rectangle((x0, y0),width, height)
rec_path = rec_patch.get_path()
rec_path = rec_patch.get_patch_transform().transform_path(rec_path)
return rec_path.vertices, rec_path.codes
#the big rectangle
r0,c = create_rec(0.3, 0.6, 1, 1.2)
# r1 and r2 are the rectangles to cut out of r0
r1,c = create_rec(0.4, 0.7, 0.3, 0.4)
r2,c = create_rec(0.8, 1, 0.4, 0.5)
path = Path(vertices=np.concatenate([r0, r1[::-1], r2[::-1]]),
codes=np.concatenate([c]*3))
im = ax.imshow(imdata, extent=(0, 2, 0, 2), aspect='equal',
cmap='coolwarm', interpolation='nearest')
patch = PathPatch(path, facecolor='w')
ax.add_patch(patch)
plt.tight_layout()
plt.show()
To account for the case where the rectangle is partially outside the original rectangle, the following (based on the second solution) might help:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.path import Path
from matplotlib.patches import PathPatch, Rectangle
fig, ax = plt.subplots()
imdata = np.random.randn(10, 10)
def create_rec(x0, y0, width, height):
rec_patch = Rectangle((x0, y0),width, height)
rec_path = rec_patch.get_path()
rec_path = rec_patch.get_patch_transform().transform_path(rec_path)
return rec_path.vertices, rec_path.codes
#the big rectangle
r0,c = create_rec(0.3, 0.6, 1, 1.2)
# r1 and r2 are the rectangles to cut out of r0
r1,c = create_rec(0.2, 0.5, 0.3, 0.4)
r2,c = create_rec(0.8, 1, 0.4, 0.5)
path = Path(vertices=np.concatenate([r0, r1[::-1], r2[::-1]]),
codes=np.concatenate([c]*3))
im = ax.imshow(imdata, extent=(0, 2, 0, 2), aspect='equal',
cmap='coolwarm', interpolation='nearest')
patho = Path(vertices=r0,codes=c)
patcho = PathPatch(patho, facecolor='none', edgecolor="none")
ax.add_patch(patcho)
patch = PathPatch(path, facecolor='w', clip_path=patcho, edgecolor="none")
ax.add_patch(patch)
plt.show()
I am trying to plot some histogrammed data on a polar axis but it wont seem to work properly. An example is below, I use the custom projection found How to make the angles in a matplotlib polar plot go clockwise with 0° at the top? it works for a scatter plot so I think my problem is with the histogram function. This has been driving me nuts all day, does anyone know what I am doing wrong...........
import random
import numpy as np
import matplotlib.pyplot as plt
baz = np.zeros((20))
freq = np.zeros((20))
pwr = np.zeros((20))
for x in range(20):
baz[x] = random.randint(20,25)*10
freq[x] = random.randint(1,10)*10
pwr[x] = random.randint(-10,-1)*10
baz = baz*np.pi/180.
abins = np.linspace(0,2*np.pi,360) # 0 to 360 in steps of 360/N.
sbins = np.linspace(1, 100)
H, xedges, yedges = np.histogram2d(baz, freq, bins=(abins,sbins), weights=pwr)
plt.figure(figsize=(14,14))
plt.subplot(1, 1, 1, projection='northpolar')
#plt.scatter(baz, freq)
plt.pcolormesh(H)
plt.show()
Your code works if you explicitly pass a mgrid (with similar characteristics than your a bins and sbins) to the pcolormesh command.
Below is an example inspired by your code:
import matplotlib.pyplot as plt
import numpy as np
#Generate the data
size = 200
baz = 10*np.random.randint(20, 25, size)*np.pi/180.
freq = 10*np.random.randint(1, 10, size)
pwr = 10*np.random.randint(-10, -1, size)
abins = np.linspace(0, 2*np.pi, 360) # 0 to 360 in steps of 360/N.
sbins = np.linspace(1, 100, 50)
H, xedges, yedges = np.histogram2d(baz, freq, bins=(abins,sbins), weights=pwr)
#Grid to plot your data on using pcolormesh
theta, r = np.mgrid[0:2*np.pi:360j, 1:100:50j]
fig, ax = plt.subplots(figsize=(14,14), subplot_kw=dict(projection='northpolar'))
ax.pcolormesh(theta, r, H)
ax.set_yticklabels([]) #remove yticklabels
plt.show()