Closed nebukadnezar closed 4 months ago
Hi, great!
I tried out your example and I seem to get rid of the lines and texts by adding the following lines before the ta.allocate call:
for artist in self.ax.get_children():
if isinstance(artist, plt.Text):
artist.set_visible(False)
if isinstance(artist, plt.Line2D):
artist.remove()
HOWEVER, there seems to be no way to call .remove() on plt.Text artists, so I think these will still be kept in memory, and might become a lot after many iterations. Therefore I think it would make more sense if the ta.allocate call returned all the plt.Text and plt.Line2D objects so that you could call .remove() on these ones instead of the artists, which seemed to work when I tried that.
Long story short, I made updates so that text and line objects are returned and can then be removed outside of textalloc, try this with the latest version of textalloc (1.1.2):
#!/usr/bin/env python3
import json
import time
import sys
import os
import numpy as np
import matplotlib.pyplot as plt
import cartopy.crs as ccrs
import cartopy.feature as cfeature
import cartopy.io.img_tiles as cimgt
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
import textalloc as ta
from math import acos, cos, sin, radians, sqrt, atan2, degrees
#######################################################################################################
# live plot
class LivePlot:
def __init__(self):
self.fig, self.ax = plt.subplots(
figsize=(12, 12), subplot_kw={"projection": ccrs.PlateCarree()}
)
self.sc = None
self.lines = None
self.texts = None
self.ax.add_feature(cfeature.LAND)
self.ax.add_feature(cfeature.OCEAN)
self.ax.add_feature(cfeature.COASTLINE)
self.ax.add_feature(cfeature.BORDERS, linestyle=":")
self.ax.gridlines(draw_labels=True)
def update_plot(self, data, map_extent):
lats = data[0]
lons = data[1]
labels = data[2]
self.ax.set_extent(map_extent)
if self.sc:
self.sc.remove()
if self.texts:
[t.remove() for t in self.texts]
if self.lines:
[l.remove() for l in self.lines]
self.sc = self.ax.scatter(
lons, lats, c="red", s=5, transform=ccrs.PlateCarree()
)
# Plot labels
_, _, self.texts, self.lines = ta.allocate(
self.ax,
lons,
lats,
labels,
x_scatter=lons,
y_scatter=lats,
textsize=6,
draw_lines=True,
linewidth=0.5,
draw_all=False,
transform=ccrs.PlateCarree(),
avoid_label_lines_overlap=True,
)
self.ax.set_title("Live Positions")
plt.draw()
def plot_positions(data, live_plot, map_extent):
live_plot.update_plot(data, map_extent)
if __name__ == "__main__":
live_plot = None
live_plot = LivePlot()
plt.ion() # Turn on interactive mode
lon = 151
lat = -34
radius = 100 # km
while True:
# calculate the box size
left = lon - 1 / cos(radians(lat)) * radius / 111
right = lon + 1 / cos(radians(lat)) * radius / 111
top = lat + radius / 111
bottom = lat - radius / 111
n = 100
lons = np.random.uniform(left, right, size=n)
lats = np.random.uniform(bottom, top, size=n)
labels = [f"Label {i}" for i in range(0, n)]
thisdata = [lats, lons, labels]
plot_positions(thisdata, live_plot, [left, right, bottom, top])
plt.pause(0.01) # Shorter pause for more frequent updates
# sleep
time.sleep(2)
First of all, many thanks for implementing the cartopy compatibility. This works beautifully on static plots now!
But now I have new problem: I'm plotting live plots that update asynchronously. Turns out the textalloc labels are not cleared between redraws.
I've tried numerous methods to remove all artists between redraws, without success.
Any suggestions on how to remove the lines/labels from textalloc between redraws?
Here's an example code illustrating the issue: