Coverage for gws-app/gws/lib/svg/_test/draw_test.py: 100%
88 statements
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 22:59 +0200
« prev ^ index » next coverage.py v7.11.0, created at 2025-10-16 22:59 +0200
1"""Tests for the SVG draw module."""
3import re
5import gws
6import gws.base.shape
7import gws.lib.svg.draw as draw
8import gws.lib.crs as crs
9import gws.lib.style
10import gws.test.util as u
13def test_shape_to_fragment_point():
14 shape = gws.base.shape.from_wkt('SRID=3857;POINT(100 200)')
15 bounds = gws.Bounds(crs=crs.WGS84, extent=[0, 0, 1000, 1000])
16 view = gws.MapView(bounds=bounds, size=[1000, 1000], rotation=0, scale=1, dpi=96)
18 # Test with default style
19 frg = draw.shape_to_fragment(shape, view)
20 assert len(frg) == 1
21 assert frg[0].to_string() == '<circle cx="377952" cy="3023622"/>'
23 # Test with custom style
24 style = _style(with_geometry='all', point_size=20, fill='red', stroke='blue', stroke_width=2)
26 frg = draw.shape_to_fragment(shape, view, style=style)
27 assert len(frg) == 1
28 assert frg[0].to_string() == '<circle cx="377952" cy="3023622" fill="red" stroke="blue" stroke-width="2px" r="10"/>'
31def test_shape_to_fragment_linestring():
32 """Test converting a linestring shape to SVG frg."""
33 shape = gws.base.shape.from_wkt('SRID=3857;LINESTRING(100 100, 200 200, 300 100)')
34 bounds = gws.Bounds(crs=crs.WGS84, extent=[0, 0, 1000, 1000])
35 view = gws.MapView(bounds=bounds, size=[1000, 1000], rotation=0, scale=1, dpi=96)
37 style = _style(with_geometry='all', stroke='green', stroke_width=3)
39 frg = draw.shape_to_fragment(shape, view, style=style)
40 assert len(frg) == 1
41 assert u.fxml(frg[0].to_string()) == u.fxml("""
42 <path d="M 377952.0 3401574.0 L 755905.0 3023622.0 L 1133858.0 3401574.0" fill="none" stroke="green" stroke-width="3px"/>
43 """)
46def test_shape_to_fragment_polygon():
47 """Test converting a polygon shape to SVG frg."""
48 shape = gws.base.shape.from_wkt('SRID=3857; POLYGON((100 100, 200 100, 200 200, 100 200, 100 100))')
49 bounds = gws.Bounds(crs=crs.WGS84, extent=[0, 0, 1000, 1000])
50 view = gws.MapView(bounds=bounds, size=[1000, 1000], rotation=0, scale=1, dpi=96)
52 values = gws.StyleValues(with_geometry='all', fill='yellow', stroke='black', stroke_width=1)
53 style = gws.Style()
54 style.values = values
56 frg = draw.shape_to_fragment(shape, view, style=style)
57 assert len(frg) == 1
58 xml = frg[0].to_string()
59 assert u.fxml(xml) == u.fxml("""
60 <path
61 fill-rule="evenodd"
62 d="M 377952.0 3401574.0 L 755905.0 3401574.0 L 755905.0 3023622.0 L 377952.0 3023622.0 L 377952.0 3401574.0 z"
63 fill="yellow" stroke="black" stroke-width="1px"/>
64 """)
67# def test_shape_to_fragment_with_label():
68# """Test shape with label."""
69# shape = gws.base.shape.from_wkt('SRID=3857; POINT(100 200)')
70# bounds = gws.Bounds(crs=crs.WGS84, extent=[0, 0, 1000, 1000])
71# view = gws.MapView(
72# bounds=bounds,
73# size=[1000, 1000],
74# rotation=0,
75# scale=1,
76# dpi=96
77# )
78#
79# values = gws.StyleValues(
80# with_geometry='all',
81# with_label='all',
82#
83# point_size=10,
84# fill='red',
85# label_font_size=12,
86# label_fill='black'
87# )
88# style = gws.Style()
89# style.values = values
90#
91# frg = draw.shape_to_fragment(shape, view, label="Test Label", style=style)
92# assert len(frg) == 2 # Point and label
93#
94# # Find the label group
95# label_group = None
96# for f in frg:
97# print(f.__dict__)
98# for el in frg:
99# if el.name == 'g' and el.attr('z-index') == 100:
100# label_group = el
101# break
102#
103# assert label_group is not None
104#
105# # Check text element inside the group
106# text_elements = [c for c in label_group.children() if c.name == 'text']
107# assert len(text_elements) > 0
108#
109# # Check tspan with the label text
110# tspans = [c for c in text_elements[0].children() if c.name == 'tspan']
111# assert len(tspans) == 1
112# assert tspans[0].__dict__ == "Test Label"
115def test_shape_to_fragment_with_marker():
116 """Test shape with marker."""
117 shape = gws.base.shape.from_wkt('SRID=3857; LINESTRING(100 100, 200 200)')
118 bounds = gws.Bounds(crs=crs.WGS84, extent=[0, 0, 1000, 1000])
119 view = gws.MapView(bounds=bounds, size=[1000, 1000], rotation=0, scale=1, dpi=96)
121 style = _style(with_geometry='all', marker='circle', marker_size=16, marker_fill='blue', stroke='black')
123 frg = draw.shape_to_fragment(shape, view, style=style)
124 assert len(frg) == 2 # Marker and path
126 xml = frg[0].to_string() + frg[1].to_string()
127 xml = re.sub(r'_M\w+', '_MID', xml)
128 assert u.fxml(xml) == u.fxml("""
129 <marker id="_MID" viewBox="0 0 16 16" refX="8" refY="8" markerUnits="userSpaceOnUse" markerWidth="16" markerHeight="16">
130 <circle fill="blue" cx="8" cy="8" r="8"/>
131 </marker>
132 <path d="M 377952.0 3401574.0 L 755905.0 3023622.0" fill="none" stroke="black" stroke-width="1px"
133 marker-start="url(#_MID)" marker-mid="url(#_MID)" marker-end="url(#_MID)"/>
134 """)
137def test_soup_to_fragment():
138 """Test converting a soup to SVG frg."""
139 bounds = gws.Bounds(crs=crs.WGS84, extent=[0, 0, 1000, 1000])
140 view = gws.MapView(bounds=bounds, size=[1000, 1000], rotation=0, scale=1, dpi=96)
142 points = [(100, 100), (200, 200), (300, 100)]
143 tags = [
144 ('line', {'x1': ['x', 0], 'y1': ['y', 0], 'x2': ['x', 1], 'y2': ['y', 1], 'stroke': 'black'}),
145 ('text', {'x': ['x', 2], 'y': ['y', 2], 'transform': ['r', 0, 1, 2]}, 'Test Text'),
146 ]
148 frg = draw.soup_to_fragment(view, points, tags)
149 assert len(frg) == 2
150 xml = frg[0].to_string() + frg[1].to_string()
151 assert u.fxml(xml) == u.fxml("""
152 <line x1="377952" y1="3401574" x2="755905" y2="3023622" stroke="black"/>
153 <text x="1133858" y="3401574" transform="rotate(-45, 1133858, 3401574)">
154 Test Text
155 </text>
156 """)
159def test_empty_shape():
160 """Test handling of empty shapes."""
161 shape = gws.base.shape.from_wkt('SRID=3857;POLYGON EMPTY')
162 bounds = gws.Bounds(crs=crs.WGS84, extent=[0, 0, 1000, 1000])
163 view = gws.MapView(bounds=bounds, size=[1000, 1000], rotation=0, scale=1, dpi=96)
165 frg = draw.shape_to_fragment(shape, view)
166 assert len(frg) == 0
169def test_label_visibility():
170 """Test label visibility based on scale."""
171 shape = gws.base.shape.from_wkt('SRID=3857;POINT(100 200)')
173 # Create view with scale 1:1000
174 bounds = gws.Bounds(crs=crs.WGS84, extent=[0, 0, 1000, 1000])
175 view = gws.MapView(bounds=bounds, size=[1000, 1000], rotation=0, scale=1000, dpi=96)
177 # Style with label visible only at scales between 1:500 and 1:2000
179 style = _style(
180 with_geometry='all',
181 with_label='all',
182 label_min_scale=500,
183 label_max_scale=2000,
184 point_size=10,
185 fill='red',
186 label_font_size=12,
187 label_fill='black',
188 )
190 # Label should be visible at scale 1:1000
191 frg = draw.shape_to_fragment(shape, view, label='Test Label', style=style)
192 assert len(frg) == 2 # Point and label
194 # Create view with scale 1:3000 (outside max_scale)
195 view.scale = 3000
196 frg = draw.shape_to_fragment(shape, view, label='Test Label', style=style)
197 assert len(frg) == 1 # Only point, no label
199 # Create view with scale 1:100 (outside min_scale)
200 view.scale = 100
201 frg = draw.shape_to_fragment(shape, view, label='Test Label', style=style)
202 assert len(frg) == 1 # Only point, no label
205def test_multigeometry():
206 """Test handling of multi-geometries."""
207 shape = gws.base.shape.from_wkt('SRID=3857;MULTIPOINT((100 100), (200 200))')
208 bounds = gws.Bounds(crs=crs.WGS84, extent=[0, 0, 1000, 1000])
209 view = gws.MapView(bounds=bounds, size=[1000, 1000], rotation=0, scale=1, dpi=96)
211 values = gws.StyleValues(with_geometry='all', point_size=10, fill='red')
212 style = gws.Style()
213 style.values = values
215 frg = draw.shape_to_fragment(shape, view, style=style)
216 assert len(frg) == 1
217 xml = frg[0].to_string()
218 assert u.fxml(xml) == u.fxml("""
219 <g>
220 <circle cx="377952" cy="3401574" fill="red" r="5"/>
221 <circle cx="755905" cy="3023622" fill="red" r="5"/>
222 </g>
223 """)
226def _style(**kwargs) -> gws.Style:
227 return gws.lib.style.Object('', '', gws.StyleValues(**kwargs))