forked from sd18spring/InteractiveProgramming
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathwold_map.py
108 lines (80 loc) · 3.51 KB
/
wold_map.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
"""Exports a dict `states` that maps state codes to lists of polygons.
Author: Oliver Steele <[email protected]>
License: MIT
Requirements:
pip install BeautifulSoup
pip install svg.path
"""
import itertools
from bs4 import BeautifulSoup
from collections import OrderedDict
from svg.path import parse_path
from lxml import etree as ET
__all__ = ['countries', 'countries_by_code', 'countries_by_name']
SEGMENT_CTL_PT_PROPS = ['start', 'control', 'control1', 'control2', 'end']
"""An ordered list of names of `svg.path` properties that hold control points."""
def get_segment_control_points(segment):
"""Given an `svg.path` segment, return its list of control points.
Each control point is a pair of floats `(x, y)`.
This does the minimum to support the paths in the map files.
In particular, it simply returns the endpoints of arc segments.
Examples:
>>> get_segment_control_points(parse_path('M 10 20 L 30 40')[0])
[(10.0, 20.0), (30.0, 40.0)]
>>> get_segment_control_points(parse_path('m 10 20 l 30 40')[0])
[(10.0, 20.0), (40.0, 60.0)]
"""
cpts = (getattr(segment, prop) for prop in SEGMENT_CTL_PT_PROPS if hasattr(segment, prop))
return [(pt.real, pt.imag) for pt in cpts]
def path_to_points(path):
"""Given an `svg.path` Path, return a list of its control points.
Each control point is a pair of floats `(x, y)`.
Examples:
>>> path_to_points(parse_path('M 10 20 30 40'))
[(10.0, 20.0), (30.0, 40.0)]
>>> path_to_points(parse_path('M 10 20 30 40 100 200'))
[(10.0, 20.0), (30.0, 40.0), (100.0, 200.0)]
"""
pts = (pt
for segment in path
for pt in get_segment_control_points(segment))
# remove duplicates
return [pti.__next__() for _, pti in itertools.groupby(pts)]
def svg_path_to_polygons(path_data):
"""Return a list of polygons that collectively approximate the SVG path whose string is `path_data`.
This handles just enough cases to parse the map files.
Examples:
>>> svg_path_to_polygons('m 10 20 30 40')
[[(10.0, 20.0), (40.0, 60.0)]]
>>> svg_path_to_polygons('m 10 20 30 40 z')
[[(10.0, 20.0), (40.0, 60.0), (10.0, 20.0)]]
>>> svg_path_to_polygons('m 10 20 30 40 z m 100 200 10 20')
[[(10.0, 20.0), (40.0, 60.0), (10.0, 20.0)], [(110.0, 220.0), (120.0, 240.0)]]
"""
# `svg.path` treats the Move command as though it were Line.
# Split the path data, in order to collect one Path per contour.
path_strings = [s for s in path_data.split('m') if s]
path_prefix = 'm'
polygons = []
for path_string in path_strings:
if path_string[0] not in 'M':
path_string = path_prefix + path_string
path = parse_path(path_string)
polygons.append(path_to_points(path))
end_pt = path[-1].end
path_prefix = 'M %f,%f m' % (end_pt.real, end_pt.imag)
return polygons
def _load_countries(svg_filename = 'theworld.svg'):
"""Initialize the `countries` module variable."""
countries = {}
with open(svg_filename, 'r') as svg:
soup = BeautifulSoup(svg.read(),'lxml')
for p in soup.findAll('path'):
country_name = p.get('id', None)
path_data = p.get('d', None)
if country_name and path_data:
countries[country_name] = svg_path_to_polygons(path_data)
return OrderedDict(sorted(countries.items()))
countries= _load_countries()
"""A `dict` of state abbreviations (e.g. `"MA"`) to lists of polygons. Each polygon is a list of points.
Each point is a tuple of floats `(x, y)`."""