0

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()```

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.