广州经典3日旅行攻略?Python动态图告诉你!
今日表情😋 :
先上数据:
Day1 石室圣心教堂(2小时) → 沙面(2小时) → 陈家祠(2小时) → 上下九步行街(2小时)
Day2 越秀公园(4小时) → 中山纪念堂(2小时) → 永庆坊(2小时) → 夜游珠江(3小时)
Day3 广州长隆野生动物世界(1天)
再上视频:
最后上代码:
import numpy as np
import pandas as pd
import geopandas as gpd
import shapely
from shapely import geometry as geo
from shapely import wkt
import geopandas as gpd
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import contextily as ctx
import imageio
import os
from PIL import Image
plt.rcParams['font.family'] = 'sans-serif'
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
plt.rcParams['animation.writer'] = 'html'
plt.rcParams['animation.embed_limit'] = 100
def rgba_to_rgb(img_rgba):
img_rgb = Image.new("RGB", img_rgba.size, (255, 255, 255))
img_rgb.paste(img_rgba, mask=img_rgba.split()[3])
return img_rgb
def html_to_gif(html_file, gif_file, duration=0.5):
path = html_file.replace(".html","_frames")
images = [os.path.join(path,x) for x in sorted(os.listdir(path))]
frames = [imageio.imread(x) for x in images]
if frames[0].shape[-1]==4:
frames = [np.array(rgba_to_rgb(Image.fromarray(x))) for x in frames]
imageio.mimsave(gif_file, frames, 'gif', duration=duration)
return gif_file
cmap = [
'#2E91E5',
'#1CA71C',
'#DA16FF',
'#B68100',
'#EB663B',
'#00A08B',
'#FC0080',
'#6C7C32',
'#862A16',
'#620042',
'#DA60CA',
'#0D2A63']*100
dfcity = gpd.read_file("./data/dfdistrict.geojson"
).query("parent=='440100'").set_crs("epsg:4326").to_crs("epsg:3857")
dfplace = pd.read_csv("./data/travel_guangzhou.csv")
dfplace["geometry"] = [shapely.wkt.loads(x) for x in dfplace["geometry"]]
dfplace = gpd.GeoDataFrame(dfplace,geometry="geometry")
dfplace = dfplace.set_crs("epsg:4326").to_crs("epsg:3857")
df = pd.DataFrame({"x":[p.x for p in dfplace["geometry"]],
"y":[p.y for p in dfplace["geometry"]],
"z":dfplace["interval"],
"day":dfplace["day"]})
df.index = dfplace["place"]
def trace_map_dance(df,title = "广州经典3日旅行路线",
filename = None,
figsize = (8,6),dpi = 144,
duration = 0.5,
anotate_points = ["沙面","越秀公园","广州长隆野生动物世界"]):
fig, ax =plt.subplots(figsize=figsize,dpi=dpi)
def plot_frame(i):
ax.clear()
ax.axis("off")
k = i//3+1
m = i%3
dfdata = df.iloc[:k,:].copy()
dftmp = df.iloc[:k-1,:].copy()
text = dfdata["day"].tolist()[-1]
#============================================================
#绘制背景
#============================================================
#设置绘图范围
bounds = dfcity.total_bounds # k==1
if 1<k<len(df)+1:
bounds = dfplace.query("day=='{}'".format(text)).total_bounds
if k>=len(df)+1:
bounds = dfplace.total_bounds
(xmin,ymin,xmax,ymax) = bounds
cx = (xmin+xmax)/2.0
cy = (ymin+ymax)/2.0
dx = max(xmax-xmin,100)
dy = max(ymax-ymin,100)
if dx/dy<8/6.0:
dx = dy*8/6.0
else:
dy = dx*6.0/8
bounds = np.array([min(cx-8000,cx-dx/2.0),min(cy-6000,cy-dy/2.0),
max(cx+8000,cx+dx/2.0),max(cy+6000,cy+dy/2.0)])
ax.set_xlim(bounds[0]-(bounds[2]-bounds[0])/3, bounds[2]+(bounds[2]-bounds[0])/3)
ax.set_ylim(bounds[1]-(bounds[3]-bounds[1])/3, bounds[3]+(bounds[3]-bounds[1])/3)
gaode = 'http://wprd01.is.autonavi.com/appmaptile?x={x}&y={y}&z={z}&lang=zh_cn&size=1&scl=1&style=7'
openstreet = "https://{s}.tile-cyclosm.openstreetmap.fr/cyclosm/{z}/{x}/{y}.png"
ctx.add_basemap(ax,source=gaode,alpha=0.5)
#============================================================
#绘制散点
#============================================================
# 绘制散点图像
if len(dftmp)>0:
ax.scatter(dftmp["x"],dftmp["y"],s = 100*dftmp["z"]/df["z"].mean(),
c = (cmap*100)[0:len(dftmp)],alpha = 0.5,zorder = 3)
# 添加注释文字
for j,p in enumerate(dftmp.index):
px,py,pz = dftmp.loc[p,["x","y","z"]].tolist()
if p in anotate_points:
ax.annotate(p,xy = (px,py), xycoords = "data",xytext = (-20,15),
fontsize = 10,fontweight = "bold",color = cmap[j], textcoords = "offset points")
#绘制轨迹线
lines = [dfdata[["x","y"]].values[i:i+2] for i in range(len(dfdata)-1)]
for ln in lines[:-1]:
x, y = np.transpose(ln)
line = plt.Line2D(x,y,color="gray",linestyle="-",linewidth= 2.5)
ax.add_artist(line)
# 添加标题和排名序号
ax.set_title(title,color = "black",fontsize = 12)
ax.text(0.13, 0.9, text, va="center", ha="center",
alpha=0.5, size = 50,transform = ax.transAxes)
# 结尾呈现全局轨迹无动画
if i>=3*len(df):
for ln in lines[-1:]:
x, y = np.transpose(ln)
line = plt.Line2D(x,y,color="gray",linestyle="-",linewidth= 2.5)
ax.add_artist(line)
return 0
# 添加注意力动画
if m==0:
px,py,pz = dfdata["x"][[-1]],dfdata["y"][[-1]],dfdata["z"][-1]
p = dfdata.index[-1]+":"+str(pz)+"Hours"
ax.scatter(px,py,s = 800*pz/df["z"].mean(),
c = cmap[len(dfdata)-1:len(dfdata)],alpha = 0.5,zorder = 4)
ax.annotate(p,xy = (px,py), xycoords = "data",
xytext = (-20,15),fontsize = 20,fontweight = "bold",
color = cmap[k-1], textcoords = "offset points",zorder = 5)
for ln in lines[-1:]:
x, y = np.transpose(ln)
line = plt.Line2D(x,y,color="gray",linestyle=":",linewidth= 4.5)
ax.add_artist(line)
if m==1:
px,py,pz = dfdata["x"][[-1]],dfdata["y"][[-1]],dfdata["z"][-1]
p = dfdata.index[-1]+":"+str(pz)+"Hours"
ax.scatter(px,py,s = 400*pz/df["z"].mean(),
c = cmap[len(dfdata)-1:len(dfdata)],alpha = 0.5,zorder = 4)
ax.annotate(p,xy = (px,py), xycoords = "data",
xytext = (-20,15),fontsize = 15,fontweight = "bold",
color = cmap[k-1], textcoords = "offset points",zorder = 5)
for ln in lines[-1:]:
x, y = np.transpose(ln)
line = plt.Line2D(x,y,color="gray",linestyle="--",linewidth= 3.5)
ax.add_artist(line)
if m==2:
px,py,pz = dfdata["x"][[-1]],dfdata["y"][[-1]],dfdata["z"][-1]
p = dfdata.index[-1]+":"+str(pz)+"Hours"
ax.scatter(px,py,s = 100*pz/df["z"].mean(),
c = cmap[len(dfdata)-1:len(dfdata)],alpha = 0.5,zorder = 4)
ax.annotate(p,xy = (px,py), xycoords = "data",
xytext = (-20,15),fontsize = 10,fontweight = "bold",
color = cmap[k-1], textcoords = "offset points",zorder = 5)
for ln in lines[-1:]:
x, y = np.transpose(ln)
line = plt.Line2D(x,y,color="gray",linestyle="-",linewidth= 2.5)
ax.add_artist(line)
return 0
my_animation = animation.FuncAnimation(fig,plot_frame,
frames = range(0,3*len(df)+5),interval = int(duration*1000))
if filename is None:
try:
from IPython.display import HTML
HTML(my_animation.to_jshtml())
return HTML(my_animation.to_jshtml())
except ImportError:
pass
else:
my_animation.save(filename)
return filename
html_file = "广州经典3日旅行路线.html"
trace_map_dance(df,filename=html_file)
gif_file = html_file.replace(".html",".gif")
html_to_gif(html_file,gif_file,duration=0.8)
收工。
万水千山总是情,点个在看行不行?😋
评论