Using an image as marker on a map in Geopandas - image

I am trying to make a map of a wind farm. I would like to use an image of a wind turbine that I found online as the marker to show the locations of the turbines on the map. I have two problems:
although i was able to import the image using the following code below, I cannot visualize it...
I do not know how to define it as the marker I would like to use (which by the way, will require that I resize it...)
Here is my attempt:
from IPython.display import Image
im = Image('path/turb.png')
display (im)
Out:
But it should be this:
As for the mapping, I tried the following... without success:
fig, ax = plt.subplots(figsize = (10,10))
turb.plot(ax=ax, marker = im)
plt.show()
I got this error:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-53-d26dd941b982> in <module>()
1 minx, miny, maxx, maxy = ri.geometry.total_bounds
2 fig, ax = plt.subplots(figsize = (10,10))
----> 3 turb.plot(ax=ax, marker = im)
4 ri.plot(ax=ax)
5 #ax.set_xlim(minx, maxx) # added/substracted value is to give some margin around total bounds
11 frames
/usr/local/lib/python3.7/dist-packages/numpy/core/_asarray.py in asarray(a, dtype, order)
81
82 """
---> 83 return array(a, dtype, copy=False, order=order)
84
85
TypeError: float() argument must be a string or a number, not 'Image'

Here is a demo code and its resulting plot for the question.
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
path = "./images/YCcBa.png"
image = plt.imread(path)[10:10+128, 10:10+108]
def plot_images(x, y, image, ax):
for xi, yi in zip(x,y):
im = OffsetImage(image, zoom=72/ax.figure.dpi)
im.image.axes = ax
# create bbox for the images
ab = AnnotationBbox(im, (xi,yi), frameon=False, pad=0.0)
ax.add_artist(ab)
x = np.arange(10)
y = np.random.rand(10)
fig, ax = plt.subplots(figsize = (8,8))
plot_images(x, y, image, ax)
ax.plot(x, y) # plot lines connecting (x,y)'s
plt.show()

Related

Plot LINESTRING Z from GeoDataFrame using pydeck's PathLayer (or TripLayer)

I have a geodataframe with LINESTRING Z geometries:
TimeUTC
Latitude
Longitude
AGL
geometry
0
2021-06-16 00:34:04+00:00
42.8354
-70.9196
82.2
LINESTRING Z (42.83541343273769 -70.91961015378617 82.2, 42.83541343273769 -70.91961015378617 82.2)
1
2021-06-14 13:32:18+00:00
42.8467
-70.8192
66.3
LINESTRING Z (42.84674080836037 -70.81919357049679 66.3, 42.84674080836037 -70.81919357049679 66.3)
2
2021-06-18 23:56:05+00:00
43.0788
-70.7541
0.9
LINESTRING Z (43.07882882269921 -70.75414567194126 0.9, 43.07884601143309 -70.75416286067514 0, 43.07885174101104 -70.75416286067514 0, 43.07884028185512 -70.75415713109717 0, 43.07884601143309 -70.75414567194126 0, 43.07884601143309 -70.75414567194126 0)
I can plot the component points using pydeck's ScatterplotLayer using the raw
(not geo) dataframe but I need to also plot the full, smooth, track.
I've tried this:
layers = [
pdk.Layer(
type = "PathLayer",
data=tracks,
get_path="geometry",
width_scale=20,
width_min_pixels=5,
get_width=5,
get_color=[180, 0, 200, 140],
pickable=True,
),
]
view_state = pdk.ViewState(
latitude=gdf_polygon.centroid.x,
longitude=gdf_polygon.centroid.y,
zoom=6,
min_zoom=5,
max_zoom=15,
pitch=40.5,
bearing=-27.36)
r = pdk.Deck(layers=[layers], initial_view_state=view_state)
return(r)
Which silently fails. Try as I might, I cannot find a way to convert the
LINESTRING Z's (and I can do without the Z component if need be) to an object
that pydeck will accept.
I found a way to extract the info needed from GeoPandas and make it work in pydeck. You just need to apply a function that extracts the coordinates from the shapely geometries as a list. Here is a fully reproducible example:
import shapely
import numpy as np
import pandas as pd
import pydeck as pdk
import geopandas as gpd
linestring_a = shapely.geometry.LineString([[0,1,2],
[3,4,5],
[6,7,8]])
linestring_b = shapely.geometry.LineString([[7,15,1],
[8,14,2],
[9,13,3]])
multilinestring = shapely.geometry.MultiLineString([[[10,11,2],
[13,14,5],
[16,17,8]],
[[19,10,11],
[12,15,4],
[10,13,0]]])
gdf = gpd.GeoDataFrame({'id':[1,2,3],
'geometry':[linestring_a,
linestring_b,
multilinestring],
'color_hex':['#ed1c24',
'#faa61a',
'#ffe800']})
# Function that transforms a hex string into an RGB tuple.
def hex_to_rgb(h):
h = h.lstrip("#")
return tuple(int(h[i : i + 2], 16) for i in (0, 2, 4))
# Applying the HEX-to-RGB function above
gdf['color_rgb'] = gdf['color_hex'].apply(hex_to_rgb)
# Function that extracts the 2d list of coordinates from an input geometry
def my_geom_coord_extractor(input_geom):
if (input_geom is None) or (input_geom is np.nan):
return []
else:
if input_geom.type[:len('multi')].lower() == 'multi':
full_coord_list = []
for geom_part in input_geom.geoms:
geom_part_2d_coords = [[coord[0],coord[1]] for coord in list(geom_part.coords)]
full_coord_list.append(geom_part_2d_coords)
else:
full_coord_list = [[coord[0],coord[1]] for coord in list(input_geom.coords)]
return full_coord_list
# Applying the coordinate list extractor to the dataframe
gdf['coord_list'] = gdf['geometry'].apply(my_geom_coord_extractor)
gdf_polygon = gdf.unary_union.convex_hull
# Establishing the default view for the pydeck output
view_state = pdk.ViewState(latitude=gdf_polygon.centroid.coords[0][1],
longitude=gdf_polygon.centroid.coords[0][0],
zoom=4)
# Creating the pydeck layer
layer = pdk.Layer(
type="PathLayer",
data=gdf,
pickable=True,
get_color='color_rgb',
width_scale=20,
width_min_pixels=2,
get_path="coord_list",
get_width=5,
)
# Finalizing the pydeck output
r = pdk.Deck(layers=[layer], initial_view_state=view_state, tooltip={"text": "{id}"})
r.to_html("path_layer.html")
Here's the output it yields:
Big caveat
It seems like pydeck isn't able to deal with MultiLineString geometries. Notice how, in the example above, my original dataframe had 3 geometries, but only 2 lines were drawn in the screenshot.

Isomap Cant Reshape reshape array of size 72 into shape (8,8)

I am working on tutorial which use Isomap for image reconginzation, the code is as following:
the major error is that reshape function in def Plot2D , ValueError: cannot reshape array of size 72 into shape (8,8).
function for 2d plot :
def Plot2D(T, title, x, y, num_to_plot=40):
# This method picks a bunch of random samples (images in your case)
# to plot onto the chart:
fig = plt.figure()
ax = fig.add_subplot(111)
ax.set_title(title)
ax.set_xlabel('Component: {0}'.format(x))
ax.set_ylabel('Component: {0}'.format(y))
x_size = (max(T[:,x]) - min(T[:,x])) * 0.08
y_size = (max(T[:,y]) - min(T[:,y])) * 0.08
for i in range(num_to_plot):
img_num = int(random.random() * num_images)
x0, y0 = T[img_num,x]-x_size/2., T[img_num,y]-y_size/2.
x1, y1 = T[img_num,x]+x_size/2., T[img_num,y]+y_size/2.
img = df.iloc[img_num,:].reshape(num_pixels, num_pixels)
ax.imshow(img, aspect='auto', cmap=plt.cm.gray, interpolation='nearest', zorder=100000, extent=(x0, x1, y0, y1))
function for image uploading and processing:
df = []
for image_path in glob.glob("path/*.png"):
image= misc.imread(image_path)
df.append((image[::2, ::2] / 255.0).reshape(-1))
df = pd.DataFrame(df).T
iso = Isomap(n_neighbors=3,n_components=3).fit(df)
T = iso.transform(df)
function for Plotting :
num_images, num_pixels = df.shape
num_pixels = int(math.sqrt(num_pixels))
Plot2D(T, "test", 0, 1, num_to_plot=40)
error message:
<ipython-input-30-e9aeee7b57c9> in Plot2D(T, title, x, y, num_to_plot)
16 x0, y0 = T[img_num,x]-x_size/2., T[img_num,y]-y_size/2.
17 x1, y1 = T[img_num,x]+x_size/2., T[img_num,y]+y_size/2.
-> 18 img = df.iloc[img_num,:].reshape(num_pixels, num_pixels)
19 ax.imshow(img, aspect='auto', cmap=plt.cm.gray,
interpolation='nearest', zorder=100000, extent=(x0, x1, y0, y1))
ValueError: cannot reshape array of size 72 into shape (8,8)
I know it might be too late for you, but hopefully whoever is using this course won't waste their time on something so simple (like I did this morning). The course doesn't get updated anymore and some pandas and numpy function changed since its first release I believe.
img = df.iloc[img_num,:].reshape(num_pixels, num_pixels)
Needs to be changed into:
img = df.iloc[img_num,:].values.reshape(num_pixels, num_pixels)
Same with the code where they transform the MATLAB file:
for i in range(num_images):
df.loc[i,:] = df.loc[i,:].reshape(num_pixels, num_pixels).T.reshape(-1)
change to:
for i in range(num_images):
df.loc[i,:] = df.loc[i,:].values.reshape(num_pixels, num_pixels).T.reshape(-1)

Image segmentation based on mouse click

Using python framework we are able to create image segments as shown in attachment. Now, based on the mouse click in the image segment we need to highlight the segment with specific color.
Based on the mouse click I am able to get x/y coordinates of the specific location. Please suggest me how can I check on which image segment the coordinates belongs to?
The following is the code snippet:
from skimage.segmentation import felzenszwalb, slic,quickshift
from skimage.segmentation import mark_boundaries
from skimage.segmentation import find_boundaries
from skimage.util import img_as_float
from skimage import io
import matplotlib.pyplot as plt
from skimage import measure
from skimage import restoration
from skimage import img_as_float
import numpy as np
coords = []
def find_nearest(array,value):
idx = (np.abs(array-value)).argmin()
return array[idx]
def onclick(event):
global ix, iy
ix, iy = event.xdata, event.ydata
print ('ix ',ix)
print ("iy ",iy)
color = np.float64([1,0,1]) # red color
image[segments == 14] = color
mark_boundaries(image, segments)
ax.imshow(mark_boundaries(image, segments))
coords.append((ix, iy))
return
image=img_as_float(io.imread("amazon.jpg"))
segments = quickshift(image, ratio=1.0, kernel_size=20, max_dist=10,
return_tree=False, sigma=0, convert2lab=True, random_seed=42)
fig = plt.figure("Superpixels -- %d segments" % (500))
ax = fig.add_subplot(1, 1, 1)
fig,ax = plt.subplots()
color = np.float64([1,0,0])
image[segments == 14] = color # desired segment to be colored
fig.canvas.mpl_connect('button_press_event', onclick)
ax.imshow(mark_boundaries(image, segments))
plt.axis("off")
plt.show()
According to the documentation, quickshift returns an integer mask indicating segment labels. If you know what pixel the user clicked on, you can check the value of that pixel in your variable segments to get the segment number.
In you click handler, you can use
clicked_segment = segments[event.xdata, event.ydata]

How do I plot two animations in a single plot with matplotlib?

In the following code I have two separate animations and I have plotted them in a two separate subplots. I want both of them to run in a single plot instead of this. I have tried the approach explained below but it is giving me issues as explained below. Please help
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import time as t
x = np.linspace(0,5,100)
fig = plt.figure()
p1 = fig.add_subplot(2,1,1)
p2 = fig.add_subplot(2,1,2)
def gen1():
i = 0.5
while(True):
yield i
i += 0.1
def gen2():
j = 0
while(True):
yield j
j += 1
def run1(c):
p1.clear()
p1.set_xlim([0,15])
p1.set_ylim([0,100])
y = c*x
p1.plot(x,y,'b')
def run2(c):
p2.clear()
p2.set_xlim([0,15])
p2.set_ylim([0,100])
y = c*x
p2.plot(x,y,'r')
ani1 = animation.FuncAnimation(fig,run1,gen1,interval=1)
ani2 = animation.FuncAnimation(fig,run2,gen2,interval=1)
fig.show()
I tried creating a single subplot instead of p1 and p2 and have both the plots graphed in that single subplot. That is just plotting one graph and not both of them. As far as I can say it is because one of them is getting cleared right after it is plotted.
How do I get around this problem?
As you do not show the code that is actually producing the problem, it's hard to tell where the problem lies.
But to answer the question of how to animate two lines in the same axes (subplot), we can just get rid of the clear() command and update the lines, instead of producing a new plot for every frame (which is more efficient anyways).
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
x = np.linspace(0,15,100)
fig = plt.figure()
p1 = fig.add_subplot(111)
p1.set_xlim([0,15])
p1.set_ylim([0,100])
# set up empty lines to be updates later on
l1, = p1.plot([],[],'b')
l2, = p1.plot([],[],'r')
def gen1():
i = 0.5
while(True):
yield i
i += 0.1
def gen2():
j = 0
while(True):
yield j
j += 1
def run1(c):
y = c*x
l1.set_data(x,y)
def run2(c):
y = c*x
l2.set_data(x,y)
ani1 = animation.FuncAnimation(fig,run1,gen1,interval=1)
ani2 = animation.FuncAnimation(fig,run2,gen2,interval=1)
plt.show()

matplotlib: jpg image special effect/transformation as if its on a page of an open book

I wish to appear a figure (and certain text) as if they are printed on a page of an open book. Is it possible to transform an jpg image programmatically or in matplotlib to have such an effect?
You can use a background axis along with an open source book image to do something like this,
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_axes([0.1, 0.1, 0.8, 0.8])
ax2 = fig.add_axes([0.2, 0.3, 0.25, 0.3])
#Plot page from a book
im = plt.imread("./book_page.jpg")
implot = ax1.imshow(im, origin='lower')
# Plot a graph and set background to transparent
x = np.linspace(0,4.*np.pi,40)
y = np.sin(x)
ax2.plot(x,y,'-ro',alpha=0.5)
ax2.set_ylim([-1.1,1.1])
ax2.patch.set_alpha(0.0)
from matplotlib import rc
rc('text', usetex=True)
margin = im.shape[0]*0.075
ytext = im.shape[1]/2.+10
ax1.text(margin, ytext, "The following text is an example")
ax1.text(margin, 90, "Figure 1. Showing a sine function")
plt.show()
Which looks like this,
where I used the following book image.
UPDATE: Added non-affine transformation based on scikit-image warp example, but with Maxwell distribution. The solution saves the matplotlib line as an image in order to apply a pointwise transform. Mapping for vector graphics may be possible but I think this will be more complicated...
import numpy as np
import matplotlib.pyplot as plt
def maxwellian_transform_image(image):
from skimage.transform import PiecewiseAffineTransform, warp
rows, cols = image.shape[0], image.shape[1]
src_cols = np.linspace(0, cols, 20)
src_rows = np.linspace(0, rows, 10)
src_rows, src_cols = np.meshgrid(src_rows, src_cols)
src = np.dstack([src_cols.flat, src_rows.flat])[0]
# add maxwellian to row coordinates
x = np.linspace(0, 3., src.shape[0])
dst_rows = src[:, 1] + (np.sqrt(2/np.pi)*x**2 * np.exp(-x**2/2)) * 50
dst_cols = src[:, 0]
dst_rows *= 1.5
dst_rows -= 1.0 * 50
dst = np.vstack([dst_cols, dst_rows]).T
tform = PiecewiseAffineTransform()
tform.estimate(src, dst)
out_rows = image.shape[0] - 1.5 * 50
out_cols = cols
out = warp(image, tform, output_shape=(out_rows, out_cols))
return out
#Create the new figure
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
#Plot page from a book
im = plt.imread("./book_page.jpg")
implot = ax.imshow(im, origin='lower')
# Plot and save graph as image, will need some manipulation of location
temp, at = plt.subplots()
margin = im.shape[0]*0.1
x = np.linspace(margin,im.shape[0]/2.,40)
y = im.shape[1]/3. + 0.1*im.shape[1]*np.sin(12.*np.pi*x/im.shape[0])
at.plot(x,y,'-ro',alpha=0.5)
temp.savefig("lineplot.png",transparent=True)
#Read in plot as an image and apply transform
plot = plt.imread("./lineplot.png")
out = maxwellian_transform_image(plot)
ax.imshow(out, extent=[0,im.shape[1],0,im.shape[0]])
plt.show()
The figure now looks like,

Resources