使用 cartopy,可以旋转本地地图以使北指向任意方向吗?
With cartopy, can a local map be rotated so that north points in an arbitrary direction?
我有这个 python 代码块来绘制城市规模的卫星地图。
# condensed from https://www.theurbanist.com.au/2021/03/plotting-openstreetmap-images-with-cartopy/
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patheffects as pe
import cartopy.geodesic as cgeo
import cartopy.crs as ccrs
import cartopy.io.img_tiles as cimgt
import io
from urllib.request import urlopen, Request
from PIL import Image
def plot_basemap(lon, lat, style = 'satellite', extent = [-100, -100, 100, 100], scale = None):
'''Download and plot an Open Street Map or Satellite base map
lon: central longitude
lat: central latitude
style: either 'osm' or 'satellite'
extent: the map limits in meters away from (lat, lon) [left, bottom, right, top]
scale: if not None, used to select the map scale
'''
# set up satellite map
cimgt.QuadtreeTiles.get_image = image_spoof # reformat web request for street map spoofing
img = cimgt.QuadtreeTiles()
# auto-calculate scale for satellite imagery
if scale is None:
scale = int(120/np.log(np.max(extent)))
scale = (scale<20) and scale or 19
# set up plot figure
fig = plt.figure(figsize=(10,10)) # open matplotlib figure
ax = fig.add_axes([0,0,1,1],projection=img.crs) # project using coordinate reference system (CRS) of street map
# add image to plot
extent = calc_extent(lon,lat,extent)
ax.set_extent(extent) # set extents
ax.add_image(img, int(scale)) # add OSM with zoom specification
return fig, ax
def calc_extent(lon, lat, extent_meters):
return [
cgeo.Geodesic().direct(points=(lon,lat),azimuths=90,distances=extent_meters[0]).base[:,0:2][0][0],
cgeo.Geodesic().direct(points=(lon,lat),azimuths=90,distances=extent_meters[2]).base[:,0:2][0][0],
cgeo.Geodesic().direct(points=(lon,lat),azimuths=0,distances=extent_meters[1]).base[:,0:2][0][1],
cgeo.Geodesic().direct(points=(lon,lat),azimuths=0,distances=extent_meters[3]).base[:,0:2][0][1],
]
def image_spoof(self, tile):
'''this function reformats web requests from OSM for cartopy
Heavily based on code by Joshua Hrisko at:
https://makersportal.com/blog/2020/4/24/geographic-visualizations-in-python-with-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
lat = 43.61
lon = -116.209
# style can be 'map' or 'satellite'
style = 'satellite'
plot_basemap(lon, lat, extent = [-2200, -1200, 2000, 2000])
做出如下图:
我试图突出这条从东南到西北流淌的河流,但它有点隐藏在这张巨大的地图中。我想制作一张地图,其中左侧为 NW,右侧为 SE,左右尺寸较长,垂直尺寸较短。然后,指北针将指向垂直线左侧约 45 度。
这可能吗?
找到了一些有用的方法:将投影设置为“RotatedPole”,极点在垂直于河流的方位角上大约 90 度。更一般地说,选择一个极点,使地图的“向上”指向极点,地图的 left/right 沿着极点的赤道延伸。
river_azimuth = -60 # average river azimuth
[pole_lon, pole_lat, pole_dist]=np.array(cgeo.Geodesic().direct([lon, lat], river_azimuth + 90, 10e6))[0,:] # 10e6 m is ~90 deg
proj = ccrs.RotatedPole(pole_longitude=pole_lon, pole_latitude=pole_lat)
然后,定义投影后,将其用作 fig.add_axes:
的参数
ax = fig.add_axes([0,0,1,1],projection=proj) # project using coordinate reference system (CRS) of street map
最后,使用 ax.set_ylim(地理坐标)放大感兴趣的区域。
y_mean = np.mean(ax.get_ylim())
ax.set_ylim(y_mean - 0.5/111, y_mean + 0.5/111)
我有这个 python 代码块来绘制城市规模的卫星地图。
# condensed from https://www.theurbanist.com.au/2021/03/plotting-openstreetmap-images-with-cartopy/
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patheffects as pe
import cartopy.geodesic as cgeo
import cartopy.crs as ccrs
import cartopy.io.img_tiles as cimgt
import io
from urllib.request import urlopen, Request
from PIL import Image
def plot_basemap(lon, lat, style = 'satellite', extent = [-100, -100, 100, 100], scale = None):
'''Download and plot an Open Street Map or Satellite base map
lon: central longitude
lat: central latitude
style: either 'osm' or 'satellite'
extent: the map limits in meters away from (lat, lon) [left, bottom, right, top]
scale: if not None, used to select the map scale
'''
# set up satellite map
cimgt.QuadtreeTiles.get_image = image_spoof # reformat web request for street map spoofing
img = cimgt.QuadtreeTiles()
# auto-calculate scale for satellite imagery
if scale is None:
scale = int(120/np.log(np.max(extent)))
scale = (scale<20) and scale or 19
# set up plot figure
fig = plt.figure(figsize=(10,10)) # open matplotlib figure
ax = fig.add_axes([0,0,1,1],projection=img.crs) # project using coordinate reference system (CRS) of street map
# add image to plot
extent = calc_extent(lon,lat,extent)
ax.set_extent(extent) # set extents
ax.add_image(img, int(scale)) # add OSM with zoom specification
return fig, ax
def calc_extent(lon, lat, extent_meters):
return [
cgeo.Geodesic().direct(points=(lon,lat),azimuths=90,distances=extent_meters[0]).base[:,0:2][0][0],
cgeo.Geodesic().direct(points=(lon,lat),azimuths=90,distances=extent_meters[2]).base[:,0:2][0][0],
cgeo.Geodesic().direct(points=(lon,lat),azimuths=0,distances=extent_meters[1]).base[:,0:2][0][1],
cgeo.Geodesic().direct(points=(lon,lat),azimuths=0,distances=extent_meters[3]).base[:,0:2][0][1],
]
def image_spoof(self, tile):
'''this function reformats web requests from OSM for cartopy
Heavily based on code by Joshua Hrisko at:
https://makersportal.com/blog/2020/4/24/geographic-visualizations-in-python-with-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
lat = 43.61
lon = -116.209
# style can be 'map' or 'satellite'
style = 'satellite'
plot_basemap(lon, lat, extent = [-2200, -1200, 2000, 2000])
做出如下图:
我试图突出这条从东南到西北流淌的河流,但它有点隐藏在这张巨大的地图中。我想制作一张地图,其中左侧为 NW,右侧为 SE,左右尺寸较长,垂直尺寸较短。然后,指北针将指向垂直线左侧约 45 度。
这可能吗?
找到了一些有用的方法:将投影设置为“RotatedPole”,极点在垂直于河流的方位角上大约 90 度。更一般地说,选择一个极点,使地图的“向上”指向极点,地图的 left/right 沿着极点的赤道延伸。
river_azimuth = -60 # average river azimuth
[pole_lon, pole_lat, pole_dist]=np.array(cgeo.Geodesic().direct([lon, lat], river_azimuth + 90, 10e6))[0,:] # 10e6 m is ~90 deg
proj = ccrs.RotatedPole(pole_longitude=pole_lon, pole_latitude=pole_lat)
然后,定义投影后,将其用作 fig.add_axes:
的参数ax = fig.add_axes([0,0,1,1],projection=proj) # project using coordinate reference system (CRS) of street map
最后,使用 ax.set_ylim(地理坐标)放大感兴趣的区域。
y_mean = np.mean(ax.get_ylim())
ax.set_ylim(y_mean - 0.5/111, y_mean + 0.5/111)