Reputation: 11
I am trying to visualize a map from an SVG file using Pygame, but I'm facing an issue where countries with complex paths (such as those with nested areas or holes) are not being filled correctly. The outer borders are being drawn fine, but the inner areas (holes) are not respected, and the filling is not being rendered as expected.
import pygame
from svgpathtools import parse_path
from svgpathtools import svg2paths2
import re
DEFAULT_FILL = (200, 200, 200)
DEFAULT_STROKE = (0, 0, 0)
class_styles = {
"st0": {"fill": "#CFE5E8", "stroke": None, "stroke_width": None},
"st1": {"fill": "#FFEB64", "stroke": "#808080", "stroke_width": 1},
"st2": {"fill": "#BACE6B", "stroke": "#808080", "stroke_width": 1},
"st3": {"fill": "#89B8E4", "stroke": "#808080", "stroke_width": 1},
"st4": {"fill": "#AC8FC7", "stroke": "#808080", "stroke_width": 1},
"st5": {"fill": "#8EC79A", "stroke": "#808080", "stroke_width": 1},
"st6": {"fill": "#E89BBD", "stroke": "#808080", "stroke_width": 1},
"st7": {"fill": "#F8A758", "stroke": "#808080", "stroke_width": 1},
"st8": {"fill": "#FBD165", "stroke": "#808080", "stroke_width": 1},
"st9": {"fill": "#F9F9F9", "stroke": "#808080", "stroke_width": 1}
}
def hex_to_rgb(hex_color):
hex_color = hex_color.lstrip('#')
return tuple(int(hex_color[i:i + 2], 16) for i in (0, 2, 4))
def path_to_points(d):
path = parse_path(d)
points = []
for segment in path:
points.append((int(segment.start.real), int(segment.start.imag)))
return points
def split_paths(d):
"""Разбивает путь d на отдельные контуры по команде 'M'."""
sub_paths = re.split(r'(?=M)', d)
return [path.strip() for path in sub_paths if path.strip()]
def parse_svg_with_svgpathtools(file_path):
paths, attributes, svg_attributes = svg2paths2(file_path)
countries = {}
for attr in attributes:
country_id = attr.get('id')
d = attr.get('d')
class_name = attr.get('class')
if country_id and d:
countries[country_id] = {
"d": d,
"class": class_name
}
return countries
def draw_country(country_data, surface):
d = country_data["d"]
class_name = country_data.get("class", "")
fill = class_styles.get(class_name, {}).get("fill", "#cccccc")
stroke = class_styles.get(class_name, {}).get("stroke", "#000000")
stroke_width = class_styles.get(class_name, {}).get("stroke_width", 1)
fill_color = hex_to_rgb(fill) if fill else DEFAULT_FILL
stroke_color = hex_to_rgb(stroke) if stroke else DEFAULT_STROKE
sub_paths = split_paths(d)
for sub_path in sub_paths:
points = path_to_points(sub_path)
if len(points) > 2:
pygame.draw.polygon(surface, fill_color, points)
pygame.draw.lines(surface, stroke_color, closed=True, points=points, width=stroke_width)
# Main program
svg_file = 'wrld-21.svg'
countries_data = parse_svg_with_svgpathtools(svg_file)
pygame.init()
info = pygame.display.Info()
WIDTH, HEIGHT = info.current_w // 2, info.current_h // 2
screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.RESIZABLE)
pygame.display.set_caption("Map")
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill((255, 255, 255))
for country, data in countries_data.items():
draw_country(data, screen)
pygame.display.flip()
pygame.quit()
incorrect and correct fill [enter image description here](https://i.sstatic.net/md3zkFED.jpg)
At first, I thought the borders were being incorrectly copied from the file, but it turned out that the borders are being copied correctly. The issue lies in the incorrect filling.
Upvotes: 0
Views: 24