Style.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. # -*- coding: utf-8 -*-
  2. __author__ = 'wanger'
  3. __date__ = '2024-08-20'
  4. __copyright__ = '(C) 2024 by siwei'
  5. __revision__ = '1.0'
  6. # inbuilt libraries
  7. from typing import Dict, Iterable, List, Union
  8. # third-party libraries
  9. import seaborn as sns
  10. from matplotlib.colors import rgb2hex
  11. def coverage_style_colormapentry(
  12. color_ramp: Union[List, Dict, Iterable],
  13. min_value: float,
  14. max_value: float,
  15. number_of_classes: int = None,
  16. ):
  17. style_append = ""
  18. n = len(color_ramp)
  19. if isinstance(color_ramp, list):
  20. if n != number_of_classes:
  21. number_of_classes = n
  22. interval = (max_value - min_value) / (number_of_classes - 1)
  23. for i, color in enumerate(color_ramp):
  24. value = min_value + interval * i
  25. value = round(value, 1)
  26. style_append += (
  27. '<sld:ColorMapEntry color="{}" label="{}" quantity="{}"/>'.format(
  28. color, value, value
  29. )
  30. )
  31. elif isinstance(color_ramp, dict):
  32. if n != number_of_classes:
  33. number_of_classes = n
  34. interval = (max_value - min_value) / (number_of_classes - 1)
  35. for name, color, i in zip(color_ramp.keys(), color_ramp.values(), range(n)):
  36. value = min_value + interval * i
  37. style_append += (
  38. '<sld:ColorMapEntry color="{}" label=" {}" quantity="{}"/>'.format(
  39. color, name, value
  40. )
  41. )
  42. else:
  43. for i, color in enumerate(color_ramp):
  44. interval = (max_value - min_value) / (number_of_classes - 1)
  45. value = min_value + interval * i
  46. style_append += (
  47. '<sld:ColorMapEntry color="{}" label="{}" quantity="{}"/>'.format(
  48. color, value, value
  49. )
  50. )
  51. return style_append
  52. def coverage_style_xml(
  53. color_ramp, style_name, cmap_type, min_value, max_value, number_of_classes, opacity
  54. ):
  55. min_max_difference = max_value - min_value
  56. style_append = ""
  57. interval = min_max_difference / (number_of_classes - 1) # noqa
  58. # The main style of the coverage style
  59. if isinstance(color_ramp, str):
  60. palette = sns.color_palette(color_ramp, int(number_of_classes))
  61. color_ramp = [rgb2hex(i) for i in palette]
  62. style_append += coverage_style_colormapentry(
  63. color_ramp, min_value, max_value, number_of_classes
  64. )
  65. style = """
  66. <StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:gml="http://www.opengis.net/gml" version="1.0.0" xmlns:ogc="http://www.opengis.net/ogc" xmlns:sld="http://www.opengis.net/sld">
  67. <UserLayer>
  68. <sld:LayerFeatureConstraints>
  69. <sld:FeatureTypeConstraint/>
  70. </sld:LayerFeatureConstraints>
  71. <sld:UserStyle>
  72. <sld:Name>{2}</sld:Name>
  73. <sld:FeatureTypeStyle>
  74. <sld:Rule>
  75. <sld:RasterSymbolizer>
  76. <sld:Opacity>{3}</sld:Opacity>
  77. <sld:ChannelSelection>
  78. <sld:GrayChannel>
  79. <sld:SourceChannelName>1</sld:SourceChannelName>
  80. </sld:GrayChannel>
  81. </sld:ChannelSelection>
  82. <sld:ColorMap type="{0}">
  83. {1}
  84. </sld:ColorMap>
  85. </sld:RasterSymbolizer>
  86. </sld:Rule>
  87. </sld:FeatureTypeStyle>
  88. </sld:UserStyle>
  89. </UserLayer>
  90. </StyledLayerDescriptor>
  91. """.format(
  92. cmap_type, style_append, style_name, opacity
  93. )
  94. with open("style.sld", "w") as f:
  95. f.write(style)
  96. def outline_only_xml(color, width, geom_type="polygon"):
  97. if geom_type == "point":
  98. symbolizer = """
  99. <PointSymbolizer>
  100. <Graphic>
  101. <Mark>
  102. <WellKnownName>circle</WellKnownName>
  103. <Fill>
  104. <CssParameter name="fill">{}</CssParameter>
  105. </Fill>
  106. </Mark>
  107. <Size>8</Size>
  108. </Graphic>
  109. </PointSymbolizer>
  110. """.format(
  111. color
  112. )
  113. elif geom_type == "line":
  114. symbolizer = """
  115. <LineSymbolizer>
  116. <Stroke>
  117. <CssParameter name="stroke">{}</CssParameter>
  118. <CssParameter name="stroke-width"{}</CssParameter>
  119. </Stroke>
  120. </LineSymbolizer>
  121. """.format(
  122. color, width
  123. )
  124. elif geom_type == "polygon":
  125. symbolizer = """
  126. <PolygonSymbolizer>
  127. <Stroke>
  128. <CssParameter name="stroke">{}</CssParameter>
  129. <CssParameter name="stroke-width">{}</CssParameter>
  130. </Stroke>
  131. </PolygonSymbolizer>
  132. """.format(
  133. color, width
  134. )
  135. else:
  136. print("Error: Invalid geometry type")
  137. return
  138. style = """
  139. <StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:se="http://www.opengis.net/se" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" version="1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  140. <NamedLayer>
  141. <se:Name>Layer name</se:Name>
  142. <UserStyle>
  143. <se:Name>Layer name</se:Name>
  144. <se:FeatureTypeStyle>
  145. <se:Rule>
  146. <se:Name>Single symbol</se:Name>
  147. {}
  148. </se:Rule>
  149. </se:FeatureTypeStyle>
  150. </UserStyle>
  151. </NamedLayer>
  152. </StyledLayerDescriptor>
  153. """.format(
  154. symbolizer
  155. )
  156. with open("style.sld", "w") as f:
  157. f.write(style)
  158. def catagorize_xml(
  159. column_name: str,
  160. values: List[float],
  161. color_ramp: str = None,
  162. geom_type: str = "polygon",
  163. ):
  164. n = len(values)
  165. palette = sns.color_palette(color_ramp, int(n))
  166. palette_hex = [rgb2hex(i) for i in palette]
  167. rule = ""
  168. for value, color in zip(values, palette_hex):
  169. if geom_type == "point":
  170. rule += """
  171. <Rule>
  172. <Name>{0}</Name>
  173. <Title>{1}</Title>
  174. <ogc:Filter>
  175. <ogc:PropertyIsEqualTo>
  176. <ogc:PropertyName>{0}</ogc:PropertyName>
  177. <ogc:Literal>{1}</ogc:Literal>
  178. </ogc:PropertyIsEqualTo>
  179. </ogc:Filter>
  180. <PointSymbolizer>
  181. <Graphic>
  182. <Mark>
  183. <WellKnownName>circle</WellKnownName>
  184. <Fill>
  185. <CssParameter name="fill">{2}</CssParameter>
  186. </Fill>
  187. </Mark>
  188. <Size>5</Size>
  189. </Graphic>
  190. </PointSymbolizer>
  191. </Rule>
  192. """.format(
  193. column_name, value, color
  194. )
  195. elif geom_type == "line":
  196. rule += """
  197. <Rule>
  198. <Name>{1}</Name>
  199. <ogc:Filter>
  200. <ogc:PropertyIsEqualTo>
  201. <ogc:PropertyName>{0}</ogc:PropertyName>
  202. <ogc:Literal>{1}</ogc:Literal>
  203. </ogc:PropertyIsEqualTo>
  204. </ogc:Filter>
  205. <LineSymbolizer>
  206. <Stroke>
  207. <CssParameter name="stroke">{2}</CssParameter>
  208. <CssParameter name="stroke-width">1</CssParameter>
  209. </Stroke>
  210. </LineSymbolizer>
  211. </Rule>
  212. """.format(
  213. column_name, value, color
  214. )
  215. elif geom_type == "polygon":
  216. rule += """
  217. <Rule>
  218. <Name>{0}</Name>
  219. <Title>{1}</Title>
  220. <ogc:Filter>
  221. <ogc:PropertyIsEqualTo>
  222. <ogc:PropertyName>{0}</ogc:PropertyName>
  223. <ogc:Literal>{1}</ogc:Literal>
  224. </ogc:PropertyIsEqualTo>
  225. </ogc:Filter>
  226. <PolygonSymbolizer>
  227. <Fill>
  228. <CssParameter name="fill">{2}</CssParameter>
  229. </Fill>
  230. <Stroke>
  231. <CssParameter name="stroke">{3}</CssParameter>
  232. <CssParameter name="stroke-width">0.5</CssParameter>
  233. </Stroke>
  234. </PolygonSymbolizer>
  235. </Rule>
  236. """.format(
  237. column_name, value, color, "#000000"
  238. )
  239. else:
  240. print("Error: Invalid geometry type")
  241. return
  242. style = """
  243. <StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:ogc="http://www.opengis.net/ogc" xmlns:se="http://www.opengis.net/se" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd" version="1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  244. <NamedLayer>
  245. <se:Name>Layer name</se:Name>
  246. <UserStyle>
  247. <se:Name>Layer name</se:Name>
  248. <FeatureTypeStyle>
  249. {}
  250. </FeatureTypeStyle>
  251. </UserStyle>
  252. </NamedLayer>
  253. </StyledLayerDescriptor>
  254. """.format(
  255. rule
  256. )
  257. with open("style.sld", "w") as f:
  258. f.write(style)
  259. def classified_xml(
  260. style_name: str,
  261. column_name: str,
  262. values: List[float],
  263. color_ramp: str = None,
  264. geom_type: str = "polygon",
  265. ):
  266. max_value = max(values)
  267. min_value = min(values)
  268. diff = max_value - min_value
  269. n = 5
  270. interval = diff / 5
  271. palette = sns.color_palette(color_ramp, int(n))
  272. palette_hex = [rgb2hex(i) for i in palette]
  273. # interval = N/4
  274. # color_values = [{value: color} for value, color in zip(values, palette_hex)]
  275. # print(color_values)
  276. rule = ""
  277. for i, color in enumerate(palette_hex):
  278. print(i)
  279. rule += """
  280. <se:Rule>
  281. <se:Name>{1}</se:Name>
  282. <se:Description>
  283. <se:Title>{4}</se:Title>
  284. </se:Description>
  285. <ogc:Filter xmlns:ogc="http://www.opengis.net/ogc">
  286. <ogc:And>
  287. <ogc:PropertyIsGreaterThan>
  288. <ogc:PropertyName>{0}</ogc:PropertyName>
  289. <ogc:Literal>{5}</ogc:Literal>
  290. </ogc:PropertyIsGreaterThan>
  291. <ogc:PropertyIsLessThanOrEqualTo>
  292. <ogc:PropertyName>{0}</ogc:PropertyName>
  293. <ogc:Literal>{4}</ogc:Literal>
  294. </ogc:PropertyIsLessThanOrEqualTo>
  295. </ogc:And>
  296. </ogc:Filter>
  297. <se:PolygonSymbolizer>
  298. <se:Fill>
  299. <se:SvgParameter name="fill">{2}</se:SvgParameter>
  300. </se:Fill>
  301. <se:Stroke>
  302. <se:SvgParameter name="stroke">{3}</se:SvgParameter>
  303. <se:SvgParameter name="stroke-width">1</se:SvgParameter>
  304. <se:SvgParameter name="stroke-linejoin">bevel</se:SvgParameter>
  305. </se:Stroke>
  306. </se:PolygonSymbolizer>
  307. </se:Rule>
  308. """.format(
  309. column_name,
  310. style_name,
  311. color,
  312. "#000000",
  313. min_value + interval * i,
  314. min_value + interval * (i + 1),
  315. )
  316. style = """
  317. <StyledLayerDescriptor xmlns="http://www.opengis.net/sld" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ogc="http://www.opengis.net/ogc" version="1.1.0" xmlns:se="http://www.opengis.net/se" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.opengis.net/sld http://schemas.opengis.net/sld/1.1.0/StyledLayerDescriptor.xsd">
  318. <NamedLayer>
  319. <se:Name>{0}</se:Name>
  320. <UserStyle>
  321. <se:Name>{0}</se:Name>
  322. <se:FeatureTypeStyle>
  323. {1}
  324. </se:FeatureTypeStyle>
  325. </UserStyle>
  326. </NamedLayer>
  327. </StyledLayerDescriptor>
  328. """.format(
  329. style_name, rule
  330. )
  331. with open("style.sld", "w") as f:
  332. f.write(style)