在放大世界地图时保持标记大小按比例缩放
Keeping the marker size to scale when zooming in on a world map
我刚开始使用 Python,这里可能有点难以理解,但我想结合 Thomas Kühn 的 this example, by Zack Fizell, with 。
下面的数据创建了一张带有“火球”的世界地图。问题是放大时,圆圈的大小保持不变(绝对值)并且相对于背景变得更小。我想将 Thomas Kühn 的解决方案应用到 Zack Fizell 的这个例子中(不是真的针对火球,而是针对我自己使用世界地图的数据)。
问题是,在摆弄代码时,我几乎没有收到有用的错误消息。这让我很难弄清楚哪里出错了。
我已经包含了所有代码,所以当 link 在某个时候死掉时它仍然是一个有趣的问题(我只是不确定如何处理 csv-file 需要 运行例子)。
这是一个很长的问题,但我希望它至少是一个有趣的问题。
请随时给我提示如何改进这个问题
示例数据如下:
# Importing libraries
import matplotlib.pyplot as plt
import pandas as pd
import geopandas as gpd
# Reading cvs file using pandas
df = pd.read_csv('cneos_fireball_data.csv',
usecols=["Peak Brightness Date/Time (UT)",
"Calculated Total Impact Energy (kt)",
"Latitude (deg.)", "Longitude (deg.)"])
df = df.rename(columns={"Peak Brightness Date/Time (UT)":
'Datetime',
"Calculated Total Impact Energy (kt)":
'Impact Energy [kt]',
"Latitude (deg.)": 'Latitude',
"Longitude (deg.)": 'Longitude'})
# Converting to a datetime datatype
df['Datetime'] = pd.to_datetime(df['Datetime'], errors='coerce')
# Applying +/- based on direction and converting to numeric datatype
for x in range(len(df['Longitude'])):
if str(df.loc[x, 'Longitude'])[-1] == 'E':
df.loc[x, 'Longitude'] = str(df.loc[x, 'Longitude'])[:-1]
if str(df.loc[x, 'Longitude'])[-1] == 'W':
df.loc[x, 'Longitude'] = \
'-' + str(df.loc[x, 'Longitude'])[:-1]
for x in range(len(df['Latitude'])):
if str(df.loc[x, 'Latitude'])[-1] == 'N':
df.loc[x, 'Latitude'] = str(df.loc[x, 'Latitude'])[:-1]
if str(df.loc[x, 'Latitude'])[-1] == 'S':
df.loc[x, 'Latitude'] = \
'-' + str(df.loc[x, 'Latitude'])[:-1]
df['Longitude'] = pd.to_numeric(df['Longitude'], errors='coerce')
df['Latitude'] = pd.to_numeric(df['Latitude'], errors='coerce')
# Converting to numeric datatype
threshold = 20
df = df[df['Impact Energy [kt]'] < threshold]
df['Impact Energy [kt]'] = pd.to_numeric(df['Impact Energy [kt]'],
errors='coerce')
# Dropping the errors from data conversions and resetting index
df.dropna()
df = df.reset_index(drop=True)
# From GeoPandas, our world map data
worldmap = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
# Creating axes and plotting world map
fig, ax = plt.subplots(figsize=(12, 6))
worldmap.plot(color="lightgrey", ax=ax)
# Plotting our Impact Energy data with a color map
x = df['Longitude']
y = df['Latitude']
z = df['Impact Energy [kt]']
plt.scatter(x, y, s=20*z, c=z, alpha=0.6, vmin=0, vmax=threshold,
cmap='autumn')
plt.colorbar(label='Impact Energy [kt]')
# Creating axis limits and title
plt.xlim([-180, 180])
plt.ylim([-90, 90])
first_year = df["Datetime"].min().strftime("%Y")
last_year = df["Datetime"].max().strftime("%Y")
plt.title("NASA: Fireballs Reported by Government Sensors\n" +
str(first_year) + " - " + str(last_year))
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.show()
结果:
关键是我在调整解决方案时遇到了问题。主要是因为我不太了解MarkerUpdater
class(见下文)。
Thomas Kühn 的解决方案
from matplotlib import pyplot as plt
import numpy as np
##plt.switch_backend('TkAgg')
class MarkerUpdater:
def __init__(self):
##for storing information about Figures and Axes
self.figs = {}
##for storing timers
self.timer_dict = {}
def add_ax(self, ax, features=[]):
ax_dict = self.figs.setdefault(ax.figure,dict())
ax_dict[ax] = {
'xlim' : ax.get_xlim(),
'ylim' : ax.get_ylim(),
'figw' : ax.figure.get_figwidth(),
'figh' : ax.figure.get_figheight(),
'scale_s' : 1.0,
'scale_a' : 1.0,
'features' : [features] if isinstance(features,str) else features,
}
ax.figure.canvas.mpl_connect('draw_event', self.update_axes)
def update_axes(self, event):
for fig,axes in self.figs.items():
if fig is event.canvas.figure:
for ax, args in axes.items():
##make sure the figure is re-drawn
update = True
fw = fig.get_figwidth()
fh = fig.get_figheight()
fac1 = min(fw/args['figw'], fh/args['figh'])
xl = ax.get_xlim()
yl = ax.get_ylim()
fac2 = min(
abs(args['xlim'][1]-args['xlim'][0])/abs(xl[1]-xl[0]),
abs(args['ylim'][1]-args['ylim'][0])/abs(yl[1]-yl[0])
)
##factor for marker size
facS = (fac1*fac2)/args['scale_s']
##factor for alpha -- limited to values smaller 1.0
facA = min(1.0,fac1*fac2)/args['scale_a']
##updating the artists
if facS != 1.0:
for line in ax.lines:
if 'size' in args['features']:
line.set_markersize(line.get_markersize()*facS)
if 'alpha' in args['features']:
alpha = line.get_alpha()
if alpha is not None:
line.set_alpha(alpha*facA)
for path in ax.collections:
if 'size' in args['features']:
path.set_sizes([s*facS**2 for s in path.get_sizes()])
if 'alpha' in args['features']:
alpha = path.get_alpha()
if alpha is not None:
path.set_alpha(alpha*facA)
args['scale_s'] *= facS
args['scale_a'] *= facA
self._redraw_later(fig)
def _redraw_later(self, fig):
timer = fig.canvas.new_timer(interval=10)
timer.single_shot = True
timer.add_callback(lambda : fig.canvas.draw_idle())
timer.start()
##stopping previous timer
if fig in self.timer_dict:
self.timer_dict[fig].stop()
##storing a reference to prevent garbage collection
self.timer_dict[fig] = timer
if __name__ == '__main__':
my_updater = MarkerUpdater()
##setting up the figure
fig, axes = plt.subplots(nrows = 2, ncols =2)#, figsize=(1,1))
ax1,ax2,ax3,ax4 = axes.flatten()
## a line plot
x1 = np.linspace(0,np.pi,30)
y1 = np.sin(x1)
ax1.plot(x1, y1, 'ro', markersize = 10, alpha = 0.8)
ax3.plot(x1, y1, 'ro', markersize = 10, alpha = 1)
## a scatter plot
x2 = np.random.normal(1,1,30)
y2 = np.random.normal(1,1,30)
ax2.scatter(x2,y2, c = 'b', s = 100, alpha = 0.6)
## scatter and line plot
ax4.scatter(x2,y2, c = 'b', s = 100, alpha = 0.6)
ax4.plot([0,0.5,1],[0,0.5,1],'ro', markersize = 10) ##note: no alpha value!
##setting up the updater
my_updater.add_ax(ax1, ['size']) ##line plot, only marker size
my_updater.add_ax(ax2, ['size']) ##scatter plot, only marker size
my_updater.add_ax(ax3, ['alpha']) ##line plot, only alpha
my_updater.add_ax(ax4, ['size', 'alpha']) ##scatter plot, marker size and alpha
plt.show()
结果:
我的尝试
请注意:我没有接触过 MarketUpdater,而且我只使用 ax2
Thomas 的解决方案:
from matplotlib import pyplot as plt
import numpy as np
##plt.switch_backend('TkAgg')
class MarkerUpdater:
def __init__(self):
##for storing information about Figures and Axes
self.figs = {}
##for storing timers
self.timer_dict = {}
def add_ax(self, ax, features=[]):
ax_dict = self.figs.setdefault(ax.figure,dict())
ax_dict[ax] = {
'xlim' : ax.get_xlim(),
'ylim' : ax.get_ylim(),
'figw' : ax.figure.get_figwidth(),
'figh' : ax.figure.get_figheight(),
'scale_s' : 1.0,
'scale_a' : 1.0,
'features' : [features] if isinstance(features,str) else features,
}
ax.figure.canvas.mpl_connect('draw_event', self.update_axes)
def update_axes(self, event):
for fig,axes in self.figs.items():
if fig is event.canvas.figure:
for ax, args in axes.items():
##make sure the figure is re-drawn
update = True
fw = fig.get_figwidth()
fh = fig.get_figheight()
fac1 = min(fw/args['figw'], fh/args['figh'])
xl = ax.get_xlim()
yl = ax.get_ylim()
fac2 = min(
abs(args['xlim'][1]-args['xlim'][0])/abs(xl[1]-xl[0]),
abs(args['ylim'][1]-args['ylim'][0])/abs(yl[1]-yl[0])
)
##factor for marker size
facS = (fac1*fac2)/args['scale_s']
##factor for alpha -- limited to values smaller 1.0
facA = min(1.0,fac1*fac2)/args['scale_a']
##updating the artists
if facS != 1.0:
for line in ax.lines:
if 'size' in args['features']:
line.set_markersize(line.get_markersize()*facS)
if 'alpha' in args['features']:
alpha = line.get_alpha()
if alpha is not None:
line.set_alpha(alpha*facA)
for path in ax.collections:
if 'size' in args['features']:
path.set_sizes([s*facS**2 for s in path.get_sizes()])
if 'alpha' in args['features']:
alpha = path.get_alpha()
if alpha is not None:
path.set_alpha(alpha*facA)
args['scale_s'] *= facS
args['scale_a'] *= facA
self._redraw_later(fig)
def _redraw_later(self, fig):
timer = fig.canvas.new_timer(interval=10)
timer.single_shot = True
timer.add_callback(lambda : fig.canvas.draw_idle())
timer.start()
##stopping previous timer
if fig in self.timer_dict:
self.timer_dict[fig].stop()
##storing a reference to prevent garbage collection
self.timer_dict[fig] = timer
if __name__ == '__main__':
my_updater = MarkerUpdater()
##setting up the figure
fig, axes = plt.subplots(nrows=2, ncols=3,figsize=(12, 6)) # swatchai's comment
ax2 = axes.flatten()
## a line plot
# x1 = np.linspace(0,np.pi,30)
# y1 = np.sin(x1)
# ax1.plot(x1, y1, 'ro', markersize = 10, alpha = 0.8)
# ax3.plot(x1, y1, 'ro', markersize = 10, alpha = 1)
## a scatter plot
worldmap.plot(color="lightgrey", ax=ax2)
# Plotting our Impact Energy data with a color map
x = df['Longitude']
y = df['Latitude']
z = df['Impact Energy [kt]']
# x = np.random.normal(1,1,30)
# y = np.random.normal(1,1,30)
ax2.scatter(x, y, s=20*z, c=z, alpha=0.6, vmin=0, vmax=threshold,
cmap='autumn')
## scatter and line plot
# ax4.scatter(x2,y2, c = 'b', s = 100, alpha = 0.6)
# ax4.plot([0,0.5,1],[0,0.5,1],'ro', markersize = 10) ##note: no alpha value!
##setting up the updater
# my_updater.add_ax(ax1, ['size']) ##line plot, only marker size
my_updater.add_ax(ax2, ['size']) ##scatter plot, only marker size
# my_updater.add_ax(ax3, ['alpha']) ##line plot, only alpha
# my_updater.add_ax(ax4, ['size', 'alpha']) ##scatter plot, marker size and alpha
plt.show()
回溯
错误在这一行:
worldmap.plot(color="lightgrey", ax=ax2)
回溯:
Traceback (most recent call last):
File "c:\Users\x\OneDrive - Wageningen University & Research\Arjen Daane\Data\tempCodeRunnerFile.python", line 185, in <module>
worldmap.plot(color="lightgrey", ax=ax2)
File "C:\Users\x\AppData\Local\Programs\Python\Python310\lib\site-packages\geopandas\geodataframe.py", line 617, in plot
return plot_dataframe(self, *args, **kwargs)
File "C:\Users\x\AppData\Local\Programs\Python\Python310\lib\site-packages\geopandas\plotting.py", line 504, in plot_dataframe
ax.set_aspect("equal")
AttributeError: 'numpy.ndarray' object has no attribute 'set_aspect'
有人有兴趣帮助我前进吗?
问题是 MarkerUpdater 对象还试图重新缩放世界地图本身。您可以通过向 class 添加一个 if 语句来避免这种情况,从而避免重新缩放。还要为此解决方案向您的导入列表添加一个额外的导入:
import matplotlib.pyplot as plt
## UPDATE: Add this import: #########################################
from matplotlib.collections import PatchCollection
import pandas as pd
##plt.switch_backend('TkAgg')
class MarkerUpdater:
def __init__(self):
##for storing information about Figures and Axes
self.figs = {}
##for storing timers
self.timer_dict = {}
def add_ax(self, ax, features=[]):
ax_dict = self.figs.setdefault(ax.figure,dict())
ax_dict[ax] = {
'xlim' : ax.get_xlim(),
'ylim' : ax.get_ylim(),
'figw' : ax.figure.get_figwidth(),
'figh' : ax.figure.get_figheight(),
'scale_s' : 1.0,
'scale_a' : 1.0,
'features' : [features] if isinstance(features,str) else features,
}
ax.figure.canvas.mpl_connect('draw_event', self.update_axes)
def update_axes(self, event):
for fig,axes in self.figs.items():
if fig is event.canvas.figure:
for ax, args in axes.items():
##make sure the figure is re-drawn
update = True
fw = fig.get_figwidth()
fh = fig.get_figheight()
fac1 = min(fw/args['figw'], fh/args['figh'])
xl = ax.get_xlim()
yl = ax.get_ylim()
fac2 = min(
abs(args['xlim'][1]-args['xlim'][0])/abs(xl[1]-xl[0]),
abs(args['ylim'][1]-args['ylim'][0])/abs(yl[1]-yl[0])
)
##factor for marker size
facS = (fac1*fac2)/args['scale_s']
##factor for alpha -- limited to values smaller 1.0
facA = min(1.0,fac1*fac2)/args['scale_a']
##updating the artists
if facS != 1.0:
for line in ax.lines:
if 'size' in args['features']:
line.set_markersize(line.get_markersize()*facS)
if 'alpha' in args['features']:
alpha = line.get_alpha()
if alpha is not None:
line.set_alpha(alpha*facA)
for path in ax.collections:
if 'size' in args['features']:
############################### UPDATE: Add this if statement: ########################
if not isinstance(path, PatchCollection):
path.set_sizes([s*facS**2 for s in path.get_sizes()])
if 'alpha' in args['features']:
alpha = path.get_alpha()
if alpha is not None:
path.set_alpha(alpha*facA)
args['scale_s'] *= facS
args['scale_a'] *= facA
self._redraw_later(fig)
def _redraw_later(self, fig):
timer = fig.canvas.new_timer(interval=10)
timer.single_shot = True
timer.add_callback(lambda : fig.canvas.draw_idle())
timer.start()
##stopping previous timer
if fig in self.timer_dict:
self.timer_dict[fig].stop()
##storing a reference to prevent garbage collection
self.timer_dict[fig] = timer
if 'alpha' in args['features']:
alpha = path.get_alpha()
if alpha is not None:
path.set_alpha(alpha*facA)
args['scale_s'] *= facS
args['scale_a'] *= facA
self._redraw_later(fig)
def _redraw_later(self, fig):
timer = fig.canvas.new_timer(interval=10)
timer.single_shot = True
timer.add_callback(lambda : fig.canvas.draw_idle())
timer.start()
##stopping previous timer
if fig in self.timer_dict:
self.timer_dict[fig].stop()
##storing a reference to prevent garbage collection
self.timer_dict[fig] = timer
那么您的代码应该如下所示:
...
## UPDATE: construct the object
my_updater = MarkerUpdater()
# Creating axes and plotting world map
fig, ax = plt.subplots(figsize=(12, 6))
worldmap.plot(color="lightgrey", ax=ax)
# Plotting our Impact Energy data with a color map
x = df['Longitude']
y = df['Latitude']
z = df['Impact Energy [kt]']
plt.scatter(x, y, s=20*z, c=z, alpha=0.6, vmin=0, vmax=threshold,
cmap='autumn')
plt.colorbar(label='Impact Energy [kt]')
## UPDATE: Link the scatter plot to the object
my_updater.add_ax(ax, ['size']) ##line plot, only marker size
...
祝你好运!
我刚开始使用 Python,这里可能有点难以理解,但我想结合 Thomas Kühn 的 this example, by Zack Fizell, with
下面的数据创建了一张带有“火球”的世界地图。问题是放大时,圆圈的大小保持不变(绝对值)并且相对于背景变得更小。我想将 Thomas Kühn 的解决方案应用到 Zack Fizell 的这个例子中(不是真的针对火球,而是针对我自己使用世界地图的数据)。
问题是,在摆弄代码时,我几乎没有收到有用的错误消息。这让我很难弄清楚哪里出错了。
我已经包含了所有代码,所以当 link 在某个时候死掉时它仍然是一个有趣的问题(我只是不确定如何处理 csv-file 需要 运行例子)。
这是一个很长的问题,但我希望它至少是一个有趣的问题。
请随时给我提示如何改进这个问题
示例数据如下:
# Importing libraries
import matplotlib.pyplot as plt
import pandas as pd
import geopandas as gpd
# Reading cvs file using pandas
df = pd.read_csv('cneos_fireball_data.csv',
usecols=["Peak Brightness Date/Time (UT)",
"Calculated Total Impact Energy (kt)",
"Latitude (deg.)", "Longitude (deg.)"])
df = df.rename(columns={"Peak Brightness Date/Time (UT)":
'Datetime',
"Calculated Total Impact Energy (kt)":
'Impact Energy [kt]',
"Latitude (deg.)": 'Latitude',
"Longitude (deg.)": 'Longitude'})
# Converting to a datetime datatype
df['Datetime'] = pd.to_datetime(df['Datetime'], errors='coerce')
# Applying +/- based on direction and converting to numeric datatype
for x in range(len(df['Longitude'])):
if str(df.loc[x, 'Longitude'])[-1] == 'E':
df.loc[x, 'Longitude'] = str(df.loc[x, 'Longitude'])[:-1]
if str(df.loc[x, 'Longitude'])[-1] == 'W':
df.loc[x, 'Longitude'] = \
'-' + str(df.loc[x, 'Longitude'])[:-1]
for x in range(len(df['Latitude'])):
if str(df.loc[x, 'Latitude'])[-1] == 'N':
df.loc[x, 'Latitude'] = str(df.loc[x, 'Latitude'])[:-1]
if str(df.loc[x, 'Latitude'])[-1] == 'S':
df.loc[x, 'Latitude'] = \
'-' + str(df.loc[x, 'Latitude'])[:-1]
df['Longitude'] = pd.to_numeric(df['Longitude'], errors='coerce')
df['Latitude'] = pd.to_numeric(df['Latitude'], errors='coerce')
# Converting to numeric datatype
threshold = 20
df = df[df['Impact Energy [kt]'] < threshold]
df['Impact Energy [kt]'] = pd.to_numeric(df['Impact Energy [kt]'],
errors='coerce')
# Dropping the errors from data conversions and resetting index
df.dropna()
df = df.reset_index(drop=True)
# From GeoPandas, our world map data
worldmap = gpd.read_file(gpd.datasets.get_path("naturalearth_lowres"))
# Creating axes and plotting world map
fig, ax = plt.subplots(figsize=(12, 6))
worldmap.plot(color="lightgrey", ax=ax)
# Plotting our Impact Energy data with a color map
x = df['Longitude']
y = df['Latitude']
z = df['Impact Energy [kt]']
plt.scatter(x, y, s=20*z, c=z, alpha=0.6, vmin=0, vmax=threshold,
cmap='autumn')
plt.colorbar(label='Impact Energy [kt]')
# Creating axis limits and title
plt.xlim([-180, 180])
plt.ylim([-90, 90])
first_year = df["Datetime"].min().strftime("%Y")
last_year = df["Datetime"].max().strftime("%Y")
plt.title("NASA: Fireballs Reported by Government Sensors\n" +
str(first_year) + " - " + str(last_year))
plt.xlabel("Longitude")
plt.ylabel("Latitude")
plt.show()
结果:
关键是我在调整解决方案时遇到了问题。主要是因为我不太了解MarkerUpdater
class(见下文)。
Thomas Kühn 的解决方案
from matplotlib import pyplot as plt
import numpy as np
##plt.switch_backend('TkAgg')
class MarkerUpdater:
def __init__(self):
##for storing information about Figures and Axes
self.figs = {}
##for storing timers
self.timer_dict = {}
def add_ax(self, ax, features=[]):
ax_dict = self.figs.setdefault(ax.figure,dict())
ax_dict[ax] = {
'xlim' : ax.get_xlim(),
'ylim' : ax.get_ylim(),
'figw' : ax.figure.get_figwidth(),
'figh' : ax.figure.get_figheight(),
'scale_s' : 1.0,
'scale_a' : 1.0,
'features' : [features] if isinstance(features,str) else features,
}
ax.figure.canvas.mpl_connect('draw_event', self.update_axes)
def update_axes(self, event):
for fig,axes in self.figs.items():
if fig is event.canvas.figure:
for ax, args in axes.items():
##make sure the figure is re-drawn
update = True
fw = fig.get_figwidth()
fh = fig.get_figheight()
fac1 = min(fw/args['figw'], fh/args['figh'])
xl = ax.get_xlim()
yl = ax.get_ylim()
fac2 = min(
abs(args['xlim'][1]-args['xlim'][0])/abs(xl[1]-xl[0]),
abs(args['ylim'][1]-args['ylim'][0])/abs(yl[1]-yl[0])
)
##factor for marker size
facS = (fac1*fac2)/args['scale_s']
##factor for alpha -- limited to values smaller 1.0
facA = min(1.0,fac1*fac2)/args['scale_a']
##updating the artists
if facS != 1.0:
for line in ax.lines:
if 'size' in args['features']:
line.set_markersize(line.get_markersize()*facS)
if 'alpha' in args['features']:
alpha = line.get_alpha()
if alpha is not None:
line.set_alpha(alpha*facA)
for path in ax.collections:
if 'size' in args['features']:
path.set_sizes([s*facS**2 for s in path.get_sizes()])
if 'alpha' in args['features']:
alpha = path.get_alpha()
if alpha is not None:
path.set_alpha(alpha*facA)
args['scale_s'] *= facS
args['scale_a'] *= facA
self._redraw_later(fig)
def _redraw_later(self, fig):
timer = fig.canvas.new_timer(interval=10)
timer.single_shot = True
timer.add_callback(lambda : fig.canvas.draw_idle())
timer.start()
##stopping previous timer
if fig in self.timer_dict:
self.timer_dict[fig].stop()
##storing a reference to prevent garbage collection
self.timer_dict[fig] = timer
if __name__ == '__main__':
my_updater = MarkerUpdater()
##setting up the figure
fig, axes = plt.subplots(nrows = 2, ncols =2)#, figsize=(1,1))
ax1,ax2,ax3,ax4 = axes.flatten()
## a line plot
x1 = np.linspace(0,np.pi,30)
y1 = np.sin(x1)
ax1.plot(x1, y1, 'ro', markersize = 10, alpha = 0.8)
ax3.plot(x1, y1, 'ro', markersize = 10, alpha = 1)
## a scatter plot
x2 = np.random.normal(1,1,30)
y2 = np.random.normal(1,1,30)
ax2.scatter(x2,y2, c = 'b', s = 100, alpha = 0.6)
## scatter and line plot
ax4.scatter(x2,y2, c = 'b', s = 100, alpha = 0.6)
ax4.plot([0,0.5,1],[0,0.5,1],'ro', markersize = 10) ##note: no alpha value!
##setting up the updater
my_updater.add_ax(ax1, ['size']) ##line plot, only marker size
my_updater.add_ax(ax2, ['size']) ##scatter plot, only marker size
my_updater.add_ax(ax3, ['alpha']) ##line plot, only alpha
my_updater.add_ax(ax4, ['size', 'alpha']) ##scatter plot, marker size and alpha
plt.show()
结果:
我的尝试
请注意:我没有接触过 MarketUpdater,而且我只使用 ax2
Thomas 的解决方案:
from matplotlib import pyplot as plt
import numpy as np
##plt.switch_backend('TkAgg')
class MarkerUpdater:
def __init__(self):
##for storing information about Figures and Axes
self.figs = {}
##for storing timers
self.timer_dict = {}
def add_ax(self, ax, features=[]):
ax_dict = self.figs.setdefault(ax.figure,dict())
ax_dict[ax] = {
'xlim' : ax.get_xlim(),
'ylim' : ax.get_ylim(),
'figw' : ax.figure.get_figwidth(),
'figh' : ax.figure.get_figheight(),
'scale_s' : 1.0,
'scale_a' : 1.0,
'features' : [features] if isinstance(features,str) else features,
}
ax.figure.canvas.mpl_connect('draw_event', self.update_axes)
def update_axes(self, event):
for fig,axes in self.figs.items():
if fig is event.canvas.figure:
for ax, args in axes.items():
##make sure the figure is re-drawn
update = True
fw = fig.get_figwidth()
fh = fig.get_figheight()
fac1 = min(fw/args['figw'], fh/args['figh'])
xl = ax.get_xlim()
yl = ax.get_ylim()
fac2 = min(
abs(args['xlim'][1]-args['xlim'][0])/abs(xl[1]-xl[0]),
abs(args['ylim'][1]-args['ylim'][0])/abs(yl[1]-yl[0])
)
##factor for marker size
facS = (fac1*fac2)/args['scale_s']
##factor for alpha -- limited to values smaller 1.0
facA = min(1.0,fac1*fac2)/args['scale_a']
##updating the artists
if facS != 1.0:
for line in ax.lines:
if 'size' in args['features']:
line.set_markersize(line.get_markersize()*facS)
if 'alpha' in args['features']:
alpha = line.get_alpha()
if alpha is not None:
line.set_alpha(alpha*facA)
for path in ax.collections:
if 'size' in args['features']:
path.set_sizes([s*facS**2 for s in path.get_sizes()])
if 'alpha' in args['features']:
alpha = path.get_alpha()
if alpha is not None:
path.set_alpha(alpha*facA)
args['scale_s'] *= facS
args['scale_a'] *= facA
self._redraw_later(fig)
def _redraw_later(self, fig):
timer = fig.canvas.new_timer(interval=10)
timer.single_shot = True
timer.add_callback(lambda : fig.canvas.draw_idle())
timer.start()
##stopping previous timer
if fig in self.timer_dict:
self.timer_dict[fig].stop()
##storing a reference to prevent garbage collection
self.timer_dict[fig] = timer
if __name__ == '__main__':
my_updater = MarkerUpdater()
##setting up the figure
fig, axes = plt.subplots(nrows=2, ncols=3,figsize=(12, 6)) # swatchai's comment
ax2 = axes.flatten()
## a line plot
# x1 = np.linspace(0,np.pi,30)
# y1 = np.sin(x1)
# ax1.plot(x1, y1, 'ro', markersize = 10, alpha = 0.8)
# ax3.plot(x1, y1, 'ro', markersize = 10, alpha = 1)
## a scatter plot
worldmap.plot(color="lightgrey", ax=ax2)
# Plotting our Impact Energy data with a color map
x = df['Longitude']
y = df['Latitude']
z = df['Impact Energy [kt]']
# x = np.random.normal(1,1,30)
# y = np.random.normal(1,1,30)
ax2.scatter(x, y, s=20*z, c=z, alpha=0.6, vmin=0, vmax=threshold,
cmap='autumn')
## scatter and line plot
# ax4.scatter(x2,y2, c = 'b', s = 100, alpha = 0.6)
# ax4.plot([0,0.5,1],[0,0.5,1],'ro', markersize = 10) ##note: no alpha value!
##setting up the updater
# my_updater.add_ax(ax1, ['size']) ##line plot, only marker size
my_updater.add_ax(ax2, ['size']) ##scatter plot, only marker size
# my_updater.add_ax(ax3, ['alpha']) ##line plot, only alpha
# my_updater.add_ax(ax4, ['size', 'alpha']) ##scatter plot, marker size and alpha
plt.show()
回溯
错误在这一行:
worldmap.plot(color="lightgrey", ax=ax2)
回溯:
Traceback (most recent call last):
File "c:\Users\x\OneDrive - Wageningen University & Research\Arjen Daane\Data\tempCodeRunnerFile.python", line 185, in <module>
worldmap.plot(color="lightgrey", ax=ax2)
File "C:\Users\x\AppData\Local\Programs\Python\Python310\lib\site-packages\geopandas\geodataframe.py", line 617, in plot
return plot_dataframe(self, *args, **kwargs)
File "C:\Users\x\AppData\Local\Programs\Python\Python310\lib\site-packages\geopandas\plotting.py", line 504, in plot_dataframe
ax.set_aspect("equal")
AttributeError: 'numpy.ndarray' object has no attribute 'set_aspect'
有人有兴趣帮助我前进吗?
问题是 MarkerUpdater 对象还试图重新缩放世界地图本身。您可以通过向 class 添加一个 if 语句来避免这种情况,从而避免重新缩放。还要为此解决方案向您的导入列表添加一个额外的导入:
import matplotlib.pyplot as plt
## UPDATE: Add this import: #########################################
from matplotlib.collections import PatchCollection
import pandas as pd
##plt.switch_backend('TkAgg')
class MarkerUpdater:
def __init__(self):
##for storing information about Figures and Axes
self.figs = {}
##for storing timers
self.timer_dict = {}
def add_ax(self, ax, features=[]):
ax_dict = self.figs.setdefault(ax.figure,dict())
ax_dict[ax] = {
'xlim' : ax.get_xlim(),
'ylim' : ax.get_ylim(),
'figw' : ax.figure.get_figwidth(),
'figh' : ax.figure.get_figheight(),
'scale_s' : 1.0,
'scale_a' : 1.0,
'features' : [features] if isinstance(features,str) else features,
}
ax.figure.canvas.mpl_connect('draw_event', self.update_axes)
def update_axes(self, event):
for fig,axes in self.figs.items():
if fig is event.canvas.figure:
for ax, args in axes.items():
##make sure the figure is re-drawn
update = True
fw = fig.get_figwidth()
fh = fig.get_figheight()
fac1 = min(fw/args['figw'], fh/args['figh'])
xl = ax.get_xlim()
yl = ax.get_ylim()
fac2 = min(
abs(args['xlim'][1]-args['xlim'][0])/abs(xl[1]-xl[0]),
abs(args['ylim'][1]-args['ylim'][0])/abs(yl[1]-yl[0])
)
##factor for marker size
facS = (fac1*fac2)/args['scale_s']
##factor for alpha -- limited to values smaller 1.0
facA = min(1.0,fac1*fac2)/args['scale_a']
##updating the artists
if facS != 1.0:
for line in ax.lines:
if 'size' in args['features']:
line.set_markersize(line.get_markersize()*facS)
if 'alpha' in args['features']:
alpha = line.get_alpha()
if alpha is not None:
line.set_alpha(alpha*facA)
for path in ax.collections:
if 'size' in args['features']:
############################### UPDATE: Add this if statement: ########################
if not isinstance(path, PatchCollection):
path.set_sizes([s*facS**2 for s in path.get_sizes()])
if 'alpha' in args['features']:
alpha = path.get_alpha()
if alpha is not None:
path.set_alpha(alpha*facA)
args['scale_s'] *= facS
args['scale_a'] *= facA
self._redraw_later(fig)
def _redraw_later(self, fig):
timer = fig.canvas.new_timer(interval=10)
timer.single_shot = True
timer.add_callback(lambda : fig.canvas.draw_idle())
timer.start()
##stopping previous timer
if fig in self.timer_dict:
self.timer_dict[fig].stop()
##storing a reference to prevent garbage collection
self.timer_dict[fig] = timer
if 'alpha' in args['features']:
alpha = path.get_alpha()
if alpha is not None:
path.set_alpha(alpha*facA)
args['scale_s'] *= facS
args['scale_a'] *= facA
self._redraw_later(fig)
def _redraw_later(self, fig):
timer = fig.canvas.new_timer(interval=10)
timer.single_shot = True
timer.add_callback(lambda : fig.canvas.draw_idle())
timer.start()
##stopping previous timer
if fig in self.timer_dict:
self.timer_dict[fig].stop()
##storing a reference to prevent garbage collection
self.timer_dict[fig] = timer
那么您的代码应该如下所示:
...
## UPDATE: construct the object
my_updater = MarkerUpdater()
# Creating axes and plotting world map
fig, ax = plt.subplots(figsize=(12, 6))
worldmap.plot(color="lightgrey", ax=ax)
# Plotting our Impact Energy data with a color map
x = df['Longitude']
y = df['Latitude']
z = df['Impact Energy [kt]']
plt.scatter(x, y, s=20*z, c=z, alpha=0.6, vmin=0, vmax=threshold,
cmap='autumn')
plt.colorbar(label='Impact Energy [kt]')
## UPDATE: Link the scatter plot to the object
my_updater.add_ax(ax, ['size']) ##line plot, only marker size
...
祝你好运!