I have a background picture on my 3d plot. I want to draw Line using stem or Line3D on top of the image. But stem got covered by the image. How to solve this?
# Controls for print and animation
i_Shape3d = 1 # Set Shape of 3d Plot: 0=Bar3d, 1=Line3d
i_print = 0 # Set i_print to 0 or 1 for printing each step.
i_StillOrAnimate = 0 # 0 for still, 1 for animation
i_Language = 1 # 0 for English, 1 for Chinese
i_df_PGKopitian = df_PGKopitian.copy()
if i_StillOrAnimate:
# Extract not all states from data_AbdProj for Animation
df_PGKopitian = df_PGKopitian.copy()
else:
# Extract just 1 state from data_AbdProj for still plot
li_State = ["PPINANG"]
i_State = li_State[0]
df_PGKopitian = df_PGKopitian[
df_PGKopitian['state'].isin(li_State)]
# Set Up Options of color for Line3D Plot
i_norm = colors.Normalize(
vmin=min(df_PGKopitian['rating']),
vmax=max(df_PGKopitian['rating']))
i_cmap = cm.get_cmap('tab10')
d_Colors = dict([(i, i_cmap(i_norm(i)))
for i in set(df_PGKopitian['rating'])])
# Establish the x, y, z coordinates for the 3D Plot:
n_plot = df_PGKopitian['name']
c_plot = df_PGKopitian['city']
s_plot = df_PGKopitian['state']
x_plot = df_PGKopitian['longitude']
y_plot = df_PGKopitian['latitude']
z_plot = df_PGKopitian['rating']
p_plot = df_PGKopitian['photo']
# Setting the view point range
m_elev, m_azim, m_rota = 40, 270, 0
d_elev, d_azim, d_rota = 0, 0, 0
d_elev, d_azim, d_rota = 10, 10, 10
# Time Array
n_Time = len(df_PGKopitian)
r_theta = int(np.ceil(n_Time / 1)) # Establish number of cycle
i_theta = np.linspace(0, 2*np.pi,
int(np.ceil(n_Time/r_theta)),
endpoint=False) # Establish a single cycle
theta = np.empty(0)
for i in range(r_theta):
theta = np.append(theta, i_theta) # Setting up cycles
x_theta = np.cos(theta) # Convert to x-axis
y_theta = np.sin(theta) # Convert to y-axis
SuffixMap_theta = 4 / n_Time # for 4 Images
# Annotation Preparations
Ann_bbox = dict(boxstyle ="round", fc ="0.8")
Ann_arrowprops = {'arrowstyle': '-|>',
'connectionstyle': 'angle, angleA = 0, angleB = 90, rad = 10'}
# Create the 3D scatter plot using the Axes3D() function:
fig = plt.figure(figsize=plt.figaspect(1.))
ax = fig.add_subplot(111, projection='3d')
# Plot the color bar for z-axis
c_map = cm.ScalarMappable(cmap='tab10')
c_map.set_array([min(z_plot),max(z_plot)])
c_map.set_clim(vmin=min(z_plot),vmax=max(z_plot))
clb = fig.colorbar(c_map, cax = fig.add_axes([0.85, 0.37, 0.03, 0.5]))
clb_Label = ('Rating' if not(i_Language) else '星级\n(Rating)')
clb_Labely = (1.1 if not(i_Language) else 1.15)
clb.set_label(clb_Label,
labelpad=-15,
y=clb_Labely, rotation=0)
def animate_func(num):
#num=3
# Establish the current State
i_State = df_PGKopitian['state'].iloc[num]
j_PGKopitiam = df_PGKopitian[df_PGKopitian['state']
== i_State]
# Establish the suffix of Map
# i_SuffixMap = 0
i_SuffixMap = int(np.ceil((num+1) * SuffixMap_theta))
df_ImageSfx = df_Images[df_Images['SUFFIX'] == i_SuffixMap]
# print datetime of each animation frame
print(f"{datetime.datetime.now()}: " +
" ".join('' for i in range(len(str(n_Time))-len(str(num+1))+1)) +
f"#{num+1}/{n_Time}; {i_State}")
# Load the image using the mpimg.imread() function:
if i_print: print(f"{num}: Load image by mpimg.imread")
imgMap = mpimg.imread(f"{FDir_MapImages}map_" +
f"{i_State}" +
f"{i_SuffixMap:02}" +
f"_rotated.png",
format='png')
# Construct matrix of pixels based on the image
if i_print: print(f"{num}: Construct matrix of pixels")
scale_grids = 1.
imgMap_xgrids = np.outer(
np.linspace(df_ImageSfx.at[i_State,'IMAGE LONGITUDE LEFT, x1'],
df_ImageSfx.at[i_State,'IMAGE LONGITUDE RIGHT, x2'],
int(imgMap.shape[0]*scale_grids)),
np.ones(1) )
imgMap_ygrids = np.outer(
np.ones(1),
np.linspace(df_ImageSfx.at[i_State,'IMAGE LATITUDE BOTTOM, y1'],
df_ImageSfx.at[i_State,'IMAGE LATITUDE TOP, y2'],
int(imgMap.shape[1]*scale_grids))
)
imgMap_zgrids = np.outer(
np.linspace(0, 0, int(imgMap.shape[0]*scale_grids)),
np.linspace(0, 0, int(imgMap.shape[1]*scale_grids)))
# Clear up the Figure
ax.clear() # Clears the figure to update the line, points,
# title, and axes
# Adding Figure Labels
if i_print: print(f"{num}: Adding Figure Labels")
if not(i_Language):
Title_Plot = (i_State + "\nPenang Kopitiam & Cafes"
+ "\n(4-Star Rating & Above)")
Title_Fontsize = 14
Title_Pad = '-30'
else:
# Set default font
rcParams['font.sans-serif'] = ['SimHei']
rcParams['font.family'] ='sans-serif'
# solve the problem that minus sign '-' is displayed as square
rcParams['axes.unicode_minus'] = False
Title_Plot = (f"{df_ImageSfx.at[i_State, '中文']}茶餐室" +
f"\n(4星级以上)")
Title_Fontsize = 15
Title_Pad = '-20'
ax.set_title(Title_Plot,
loc='center', pad = Title_Pad,
fontsize=Title_Fontsize, fontweight = 15,
color = d_Colors.get(z_plot.iloc[num]),
rotation = m_rota)
ax.set_xlabel('Longitude')
ax.set_ylabel('Latitude')
# Turn off the Z-Axis Markers & Tickers
if i_print: print(f"{num}: Turn off Z-Axis Markers & Tickers")
ax.zaxis.clear()
ax.set_zticks([])
ax.xaxis.clear()
ax.set_xticks([])
ax.yaxis.clear()
ax.set_yticks([])
# Adjust the 3D scaling
if i_print: print(f"{num}: Adjust the 3D scaling")
m = df_ImageSfx.at[
i_State,'IMAGE ASPECT RATIO, (y2-y1)/(x2-x1)']
ax.set_box_aspect(((1/m if m > 1 else 1),
(m if m < 1 else 1),
1))
ax.set_proj_type('persp')
# Setting the view point
if i_print: print(f"{num}: Setting the view point")
ax.view_init(azim=m_azim+d_azim*x_theta[num-1],
elev=m_elev+d_elev*y_theta[num-1])
# Plot the Image on the base of the 3D plot
if i_print: print(f"{num}: Plot the Image on the base of the 3D plot")
ax.plot_surface(imgMap_xgrids, imgMap_ygrids, imgMap_zgrids,
facecolors=imgMap, rstride=5, cstride=5,
cmap='gist_ncar')
# Finding the row of starting & ending datapoints,
# which are of the same state as the current "num"
if i_print: print(f"{num}: Find the row of start & end " +
f"of datapoints which are of the same state")
for i_row in range(len(s_plot)):
if s_plot.iloc[i_row] == i_State:
if ((i_row and s_plot.iloc[i_row-1] != i_State)
or not(i_row)):
r_Stt_STATE = i_row
if ((i_row<len(s_plot)-1 and s_plot.iloc[i_row+1] != i_State)
or i_row==len(s_plot)-1):
r_End_STATE = i_row
#print(f"{i_State} has {len(s.iloc[r_Stt_STATE:r_End_STATE+1])} rows of data")
# Adjust the xlim3d, ylim3d & box_aspect based on the datapoints
if i_print: print(f"{num}: Adjust the xlim3d, ylim3d & box_aspect")
xlim3d_min = (min(x_plot.iloc[r_Stt_STATE:r_End_STATE+1])
+ df_ImageSfx.at[i_State,'IMAGE LONGITUDE LEFT, x1'])/2
xlim3d_max = (max(x_plot.iloc[r_Stt_STATE:r_End_STATE+1])
+ df_ImageSfx.at[i_State,'IMAGE LONGITUDE RIGHT, x2'])/2
ylim3d_min = (min(y_plot.iloc[r_Stt_STATE:r_End_STATE+1])
+ df_ImageSfx.at[i_State,'IMAGE LATITUDE BOTTOM, y1'])/2
ylim3d_max = (max(y_plot.iloc[r_Stt_STATE:r_End_STATE+1])
+ df_ImageSfx.at[i_State,'IMAGE LATITUDE TOP, y2'])/2
zlim3d_max = max(z_plot.iloc[r_Stt_STATE:r_End_STATE+1])
slope_lim3d = (ylim3d_max - ylim3d_min) / (xlim3d_max - xlim3d_min)
ax.set_xlim3d(xlim3d_min, xlim3d_max)
ax.set_ylim3d(ylim3d_min, ylim3d_max)
ax.set_zlim3d(0, zlim3d_max)
ax.set_box_aspect((
(1/slope_lim3d if slope_lim3d > 1 else 1),
( slope_lim3d if slope_lim3d < 1 else 1), 0.2))
# Plot the hotspots by bar3D
if i_Shape3d==0 and False:
if i_print: print(f"{num}: Plot the hotspots by bar3D")
xpos_bar3D = x_plot.iloc[r_Stt_STATE:num+1]
ypos_bar3D = y_plot.iloc[r_Stt_STATE:num+1]
zpos_bar3D = np.ones_like(xpos_bar3D.shape)
dx_bar3D = dy_bar3D = np.ones_like(zpos_bar3D) * (0.05 *
(df_ImageSfx.at[i_State,'IMAGE LONGITUDE RIGHT, x2'] -
df_ImageSfx.at[i_State,'IMAGE LONGITUDE LEFT, x1']) /
(xlim3d_max - xlim3d_min))
dz_bar3D = z_plot.iloc[r_Stt_STATE:num+1]
co_bar3D = dz_bar3D.map(d_Colors)
xpos_bar3D -= dx_bar3D/2
ypos_bar3D -= dx_bar3D/2
ax.bar3d(xpos_bar3D, # Datapoints base's x-positions
ypos_bar3D, # Datapoints base's y-positions
zpos_bar3D, # Datapoints base's z-positions
dx_bar3D, # Shape of bar in x-direction
dy_bar3D, # Shape of bar in y-direction
dz_bar3D, # Shape of bar in z-direction
color = co_bar3D, # calors of bar according to z data
shade = True,
zsort='average')
for ni, ci, xi, yi, zi, pi in zip(n_plot.iloc[r_Stt_STATE:num+1],
c_plot.iloc[r_Stt_STATE:num+1],
x_plot.iloc[r_Stt_STATE:num+1],
y_plot.iloc[r_Stt_STATE:num+1],
z_plot.iloc[r_Stt_STATE:num+1],
p_plot.iloc[r_Stt_STATE:num+1]):
# Plot the hotspots by bar3D
if i_Shape3d==0 and False:
if i_print: print(f"{num}: Plot the hotspots by bar3D")
dx_bar3D = dy_bar3D = 0.05
#dx_bar3D = dy_bar3D = (0.05 *
# (xlim3d_max - xlim3d_min) /
# (df_Images.at[i_State,'x2'] - df_Images.at[i_State,'x1']))
dz_bar3D = zi - 0
xpos_bar3D = xi - dx_bar3D/2
ypos_bar3D = yi - dy_bar3D/2
zpos_bar3D = 0
ax.bar3d(xpos_bar3D, # Datapoints base's x-positions
ypos_bar3D, # Datapoints base's y-positions
zpos_bar3D, # Datapoints base's z-positions
dx_bar3D, # Shape of bar in x-direction
dy_bar3D, # Shape of bar in y-direction
dz_bar3D, # Shape of bar in z-direction
color = d_Colors.get(zi), # calors of bar
# according to z data
shade = True,
zsort='average')
# Plot the hotspots by Line3D
elif i_Shape3d==1 and True:
if i_print: print(f"{num}: Plot the hotspots by Line3D")
if i_print: print(f" {ni}; {xi}; {yi}; {zi}; {d_Colors.get(zi)}")
line = art3d.Line3D(*zip((xi, yi, 0), (xi, yi, zi)),
c= d_Colors.get(zi),
marker='o', markevery=(1, 1),
ls = '-', lw = 1.)
ax.add_line(line)
ax.stem([xi], [yi], [zi],
markerfmt='o', bottom=0)
# Annotation
if i_Shape3d in [0, 1] and ni == n_plot.iloc[-1]:
xp, yp, _ = proj3d.proj_transform(xi, yi, zi,
ax.get_proj())
Ann_offset = (15, 15+165*(0.035-yp)/0.115)
if i_print: print(f" xp: {xp}; yp: {yp}")
if not(i_Language):
n_txt_Annotate = ni.split(" ")
txt_Annotate = f"{ni}\n{ci}\n{zi}star"
xp_rightLimit = 0.03
else:
txt_Annotate = f"{ni}\n{ci}\n{zi}星级"
xp_rightLimit = 0.035
Insert the name of the Kopitiam into the Annotation BBox
ab_text = AnnotationBbox(
TextArea(txt_Annotate),
xy=(xp, yp),
xybox=(Ann_offset[0] *
(1 if xp < xp_rightLimit else -1),
Ann_offset[1]),
xycoords='data',
boxcoords="offset points",
box_alignment = ((0 if xp < xp_rightLimit else 1),
0.5),
pad=0.5,
bboxprops=Ann_bbox,
arrowprops=Ann_arrowprops
)
ax.add_artist(ab_text)
Test_Annotation = 0
if Test_Annotation == 1:
# y_min = -0.08; y_max = 0.035
# offsets total: xx, 15+165/0.115
p_xy = ((-0.08, -0.08), (-0.04, -0.08), (0.03, 0.035))
p_ff = ((15, 15), (15, 180), (15, 15))
for i in range(len(p_xy)):
ax.annotate(f"{p_xy[i][0]:.2f},\n{p_xy[i][1]:.2f}",
xy=(p_xy[i][0], p_xy[i][1]),
xytext =(p_ff[i][0] * (1 if p_xy[i][0]<0.03 else -1),
p_ff[i][1]),
ha=("left" if p_xy[i][0]<0.03 else "right"),
textcoords = 'offset points',
bbox = Ann_bbox,
arrowprops = Ann_arrowprops
)
I've tried looking at the imshow, plotsurface. Searching through the website. But no one seems to have a similar problem like I did.