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

1"""Tests for the SVG draw module.""" 

2 

3import re 

4 

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 

11 

12 

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) 

17 

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"/>' 

22 

23 # Test with custom style 

24 style = _style(with_geometry='all', point_size=20, fill='red', stroke='blue', stroke_width=2) 

25 

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"/>' 

29 

30 

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) 

36 

37 style = _style(with_geometry='all', stroke='green', stroke_width=3) 

38 

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 """) 

44 

45 

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) 

51 

52 values = gws.StyleValues(with_geometry='all', fill='yellow', stroke='black', stroke_width=1) 

53 style = gws.Style() 

54 style.values = values 

55 

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 """) 

65 

66 

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" 

113 

114 

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) 

120 

121 style = _style(with_geometry='all', marker='circle', marker_size=16, marker_fill='blue', stroke='black') 

122 

123 frg = draw.shape_to_fragment(shape, view, style=style) 

124 assert len(frg) == 2 # Marker and path 

125 

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 """) 

135 

136 

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) 

141 

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 ] 

147 

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 """) 

157 

158 

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) 

164 

165 frg = draw.shape_to_fragment(shape, view) 

166 assert len(frg) == 0 

167 

168 

169def test_label_visibility(): 

170 """Test label visibility based on scale.""" 

171 shape = gws.base.shape.from_wkt('SRID=3857;POINT(100 200)') 

172 

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) 

176 

177 # Style with label visible only at scales between 1:500 and 1:2000 

178 

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 ) 

189 

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 

193 

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 

198 

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 

203 

204 

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) 

210 

211 values = gws.StyleValues(with_geometry='all', point_size=10, fill='red') 

212 style = gws.Style() 

213 style.values = values 

214 

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 """) 

224 

225 

226def _style(**kwargs) -> gws.Style: 

227 return gws.lib.style.Object('', '', gws.StyleValues(**kwargs))