Nike
Nike

Reputation: 25

Labelling a map with python

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

Answers (0)

Related Questions