I'm trying to clip the satellite background image by circle:
import io
from PIL import Image
import cartopy.crs as ccrs
import matplotlib.patches as patches
import matplotlib.pyplot as plt
import cartopy.io.img_tiles as cimgt
from urllib.request import urlopen, Request
def image_spoof(self, tile):
'''this function reformats web requests from OSM for cartopy'''
url = self._image_url(tile) # get the url of the street map API
req = Request(url) # start request
req.add_header('User-agent','Anaconda 3') # add user agent to request
fh = urlopen(req)
im_data = io.BytesIO(fh.read()) # get image
fh.close() # close url
img = Image.open(im_data) # open image with PIL
img = img.convert(self.desired_tile_form) # set image format
return img, self.tileextent(tile), 'lower' # reformat for cartopy
proj = ccrs.PlateCarree()
ax = plt.axes(projection=proj)
# set extent
lon_min = -98.853627
lon_max = -98.752037
lat_min = 19.274685
lat_max = 19.376275
ax.set_extent((lon_min, lon_max, lat_min, lat_max), crs=proj)
cimgt.QuadtreeTiles.get_image = image_spoof # reformat web request for street map spoofing
osm_img = cimgt.QuadtreeTiles() # spoofed, downloaded street map
osm_img = ax.add_image(osm_img, 16) # add OSM with zoom specification
patch = patches.Circle((-98.802832, 19.32548), radius=0.03, transform=ax.transData)
ax.add_patch(patch)
# osm_img.set_clip_path(patch)
However, I see set_clip_path
does not work for this case. Is it possible to clip osm_img
by the circle patch? Or any other method to plot a circled satellite background?
I figure out one method which gets the image array and clip the image:
import cartopy.crs as ccrs
import matplotlib
import contextily as cx
import matplotlib.patches as patches
import matplotlib.pyplot as plt
ax = plt.axes(projection=ccrs.PlateCarree())
proj = ccrs.PlateCarree()
# set extent
lon_min = -98.853627
lon_max = -98.752037
lat_min = 19.274685
lat_max = 19.376275
extent = (lon_min, lon_max, lat_min, lat_max)
ax.set_extent(extent, crs=proj)
cx.add_basemap(ax, crs=proj, source=cx.providers.Esri.WorldImagery)
# get the background as image array
for child in ax.get_children():
if isinstance(child, matplotlib.image.AxesImage):
print("* Found image object.")
child0 = child
ccmap = child0.get_cmap()
img_array = child0.get_array()
break
pass
# clear the axis
plt.cla()
# create patch
patch = patches.Circle((-98.802832, 19.32548), radius=0.03, transform=ax.transData)
# clip the image
osm_img = ax.imshow(img_array, extent=extent, origin='lower', cmap=ccmap)
osm_img.set_clip_path(patch)