Reputation: 25
I want to create a map with all the countries labelled, but the text within each country's boundaries. For readability, I want the text to follow the curve of the country boundary. I tried an example I found "Geographic Information Systems", but the text are still mostly straight and goes over the country boundaries.
This is my current code:
world = gpd.read_file(/ne_110m_admin_0_countries.shp)
#Plot the map
world.plot(ax=ax)
# Function to check if the label fits within the country boundary
def label_fits(country, x, y, text, ax, fontsize):
label = ax.text(x, y, text, fontsize=fontsize, ha='center', va='center', color='black')
label_bbox = label.get_window_extent(renderer=ax.figure.canvas.get_renderer())
# Check if the label fits within the bounds of the country polygon
country_polygon = country.geometry
point = Point(x, y)
# Check if the point is inside the country (handle both Polygon and MultiPolygon)
if country_polygon.contains(point) or country_polygon.intersects(point):
# Ensure the label does not overlap the boundary
return label_bbox.width < country_polygon.bounds[2] - country_polygon.bounds[0] and \
label_bbox.height < country_polygon.bounds[3] - country_polygon.bounds[1]
return False
# Function to place curved text within the country boundary
def place_curved_text(x, y, text, ax, fontsize=10, radius=0.1, N=100):
# Generate curve coordinates
t = np.linspace(0, 1, len(text)) # Create a linear space for each character
curve_x = x + radius * np.cos(t * np.pi) # X coordinates for curved text
curve_y = y + radius * np.sin(t * np.pi) # Y coordinates for curved text
# Check if the label fits within the country bounds, adjusting position as necessary
for i, char in enumerate(text):
if not label_fits(country, curve_x[i], curve_y[i], char, ax, fontsize):
# If the label does not fit, adjust the position
curve_x[i] = x + radius * np.cos(t[i] * np.pi) * 1.2
curve_y[i] = y + radius * np.sin(t[i] * np.pi) * 1.2
ax.text(curve_x[i], curve_y[i], char, fontsize=fontsize, ha='center', va='center', color='black')
# Adjust label placement to ensure it stays within boundaries and curves
def adjust_label_placement(country, ax, max_fontsize=12):
label = country['SOVEREIGNT']
x, y = country.geometry.centroid.x, country.geometry.centroid.y
fontsize = 6 # Start with a small font size
# Start checking the largest font that fits inside the boundary
while fontsize <= max_fontsize:
if label_fits(country, x, y, label, ax, fontsize):
fontsize += 1
else:
break
# Place curved text with the appropriate font size
place_curved_text(x, y, label, ax, fontsize=fontsize - 1)
# Iterate over each country and place its label
for idx, country in world.iterrows():
adjust_label_placement(country, ax)
# Show the plot
plt.show()```
Upvotes: 0
Views: 36