pycoast 1.1.0__py3-none-any.whl → 1.7.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (176) hide show
  1. pycoast/__init__.py +29 -9
  2. pycoast/conftest.py +14 -0
  3. pycoast/cw_agg.py +762 -468
  4. pycoast/cw_base.py +1575 -768
  5. pycoast/cw_pil.py +583 -363
  6. pycoast/tests/__init__.py +19 -0
  7. pycoast/tests/brazil_shapefiles.png +0 -0
  8. pycoast/tests/brazil_shapefiles_agg.png +0 -0
  9. pycoast/tests/coasts_and_grid.ini +13 -0
  10. pycoast/tests/coasts_and_grid_agg.ini +17 -0
  11. pycoast/tests/contours_europe.png +0 -0
  12. pycoast/tests/contours_europe_agg.png +0 -0
  13. pycoast/tests/contours_europe_alpha.png +0 -0
  14. pycoast/tests/contours_geos.png +0 -0
  15. pycoast/tests/contours_geos_agg.png +0 -0
  16. pycoast/tests/dateline_boundary_cross.png +0 -0
  17. pycoast/tests/dateline_cross.png +0 -0
  18. pycoast/tests/eastern_shapes_agg.png +0 -0
  19. pycoast/tests/eastern_shapes_pil.png +0 -0
  20. pycoast/tests/grid_europe.png +0 -0
  21. pycoast/tests/grid_europe_agg.png +0 -0
  22. pycoast/tests/grid_europe_agg_txt.png +0 -0
  23. pycoast/tests/grid_from_dict_agg.png +0 -0
  24. pycoast/tests/grid_from_dict_pil.png +0 -0
  25. pycoast/tests/grid_geos.png +0 -0
  26. pycoast/tests/grid_geos_agg.png +0 -0
  27. pycoast/tests/grid_germ.png +0 -0
  28. pycoast/tests/grid_nh.png +0 -0
  29. pycoast/tests/grid_nh_agg.png +0 -0
  30. pycoast/tests/grid_nh_cfg_agg.png +0 -0
  31. pycoast/tests/lonlat_boundary_cross.png +0 -0
  32. pycoast/tests/nh_cities_agg.ini +26 -0
  33. pycoast/tests/nh_cities_agg.png +0 -0
  34. pycoast/tests/nh_cities_from_dict_agg.png +0 -0
  35. pycoast/tests/nh_cities_from_dict_pil.png +0 -0
  36. pycoast/tests/nh_cities_pil.ini +20 -0
  37. pycoast/tests/nh_cities_pil.png +0 -0
  38. pycoast/tests/nh_one_shapefile.ini +11 -0
  39. pycoast/tests/nh_points_agg.ini +24 -0
  40. pycoast/tests/nh_points_agg.png +0 -0
  41. pycoast/tests/nh_points_cfg_pil.png +0 -0
  42. pycoast/tests/nh_points_pil.ini +19 -0
  43. pycoast/tests/nh_points_pil.png +0 -0
  44. pycoast/tests/nh_polygons.png +0 -0
  45. pycoast/tests/nh_polygons_agg.png +0 -0
  46. pycoast/tests/no_h_scratch_agg.png +0 -0
  47. pycoast/tests/no_h_scratch_pil.png +0 -0
  48. pycoast/tests/no_v_scratch_agg.png +0 -0
  49. pycoast/tests/no_v_scratch_pil.png +0 -0
  50. pycoast/tests/one_shapefile_from_cfg_agg.png +0 -0
  51. pycoast/tests/one_shapefile_from_cfg_pil.png +0 -0
  52. pycoast/tests/test_data/DejaVuSerif.ttf +0 -0
  53. pycoast/tests/test_data/gshhs/CITIES/cities.txt +20 -0
  54. pycoast/tests/test_data/gshhs/GSHHS_shp/l/GSHHS_l_L1.dbf +0 -0
  55. pycoast/tests/test_data/gshhs/GSHHS_shp/l/GSHHS_l_L1.prj +1 -0
  56. pycoast/tests/test_data/gshhs/GSHHS_shp/l/GSHHS_l_L1.shp +0 -0
  57. pycoast/tests/test_data/gshhs/GSHHS_shp/l/GSHHS_l_L1.shx +0 -0
  58. pycoast/tests/test_data/gshhs/GSHHS_shp/l/GSHHS_l_L2.dbf +0 -0
  59. pycoast/tests/test_data/gshhs/GSHHS_shp/l/GSHHS_l_L2.prj +1 -0
  60. pycoast/tests/test_data/gshhs/GSHHS_shp/l/GSHHS_l_L2.shp +0 -0
  61. pycoast/tests/test_data/gshhs/GSHHS_shp/l/GSHHS_l_L2.shx +0 -0
  62. pycoast/tests/test_data/gshhs/GSHHS_shp/l/GSHHS_l_L3.dbf +0 -0
  63. pycoast/tests/test_data/gshhs/GSHHS_shp/l/GSHHS_l_L3.prj +1 -0
  64. pycoast/tests/test_data/gshhs/GSHHS_shp/l/GSHHS_l_L3.shp +0 -0
  65. pycoast/tests/test_data/gshhs/GSHHS_shp/l/GSHHS_l_L3.shx +0 -0
  66. pycoast/tests/test_data/gshhs/GSHHS_shp/l/GSHHS_l_L4.dbf +0 -0
  67. pycoast/tests/test_data/gshhs/GSHHS_shp/l/GSHHS_l_L4.prj +1 -0
  68. pycoast/tests/test_data/gshhs/GSHHS_shp/l/GSHHS_l_L4.shp +0 -0
  69. pycoast/tests/test_data/gshhs/GSHHS_shp/l/GSHHS_l_L4.shx +0 -0
  70. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_border_c_L1.dbf +0 -0
  71. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_border_c_L1.prj +1 -0
  72. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_border_c_L1.shp +0 -0
  73. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_border_c_L1.shx +0 -0
  74. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_border_c_L2.dbf +0 -0
  75. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_border_c_L2.prj +1 -0
  76. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_border_c_L2.shp +0 -0
  77. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_border_c_L2.shx +0 -0
  78. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_border_c_L3.dbf +0 -0
  79. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_border_c_L3.prj +1 -0
  80. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_border_c_L3.shp +0 -0
  81. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_border_c_L3.shx +0 -0
  82. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L01.dbf +0 -0
  83. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L01.prj +1 -0
  84. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L01.shp +0 -0
  85. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L01.shx +0 -0
  86. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L02.dbf +0 -0
  87. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L02.prj +1 -0
  88. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L02.shp +0 -0
  89. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L02.shx +0 -0
  90. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L03.dbf +0 -0
  91. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L03.prj +1 -0
  92. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L03.shp +0 -0
  93. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L03.shx +0 -0
  94. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L04.dbf +0 -0
  95. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L04.prj +1 -0
  96. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L04.shp +0 -0
  97. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L04.shx +0 -0
  98. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L05.dbf +0 -0
  99. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L05.prj +1 -0
  100. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L05.shp +0 -0
  101. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L05.shx +0 -0
  102. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L06.dbf +0 -0
  103. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L06.prj +1 -0
  104. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L06.shp +0 -0
  105. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L06.shx +0 -0
  106. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L07.dbf +0 -0
  107. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L07.prj +1 -0
  108. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L07.shp +0 -0
  109. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L07.shx +0 -0
  110. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L08.dbf +0 -0
  111. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L08.prj +1 -0
  112. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L08.shp +0 -0
  113. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L08.shx +0 -0
  114. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L09.dbf +0 -0
  115. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L09.prj +1 -0
  116. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L09.shp +0 -0
  117. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L09.shx +0 -0
  118. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L1.dbf +0 -0
  119. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L1.shp +0 -0
  120. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L1.shx +0 -0
  121. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L10.dbf +0 -0
  122. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L10.prj +1 -0
  123. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L10.shp +0 -0
  124. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L10.shx +0 -0
  125. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L11.dbf +0 -0
  126. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L11.prj +1 -0
  127. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L11.shp +0 -0
  128. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L11.shx +0 -0
  129. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L2.dbf +0 -0
  130. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L2.shp +0 -0
  131. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L2.shx +0 -0
  132. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L3.dbf +0 -0
  133. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L3.shp +0 -0
  134. pycoast/tests/test_data/gshhs/WDBII_shp/c/WDBII_river_c_L3.shx +0 -0
  135. pycoast/tests/test_data/shapes/Metareas.dbf +0 -0
  136. pycoast/tests/test_data/shapes/Metareas.mxd +0 -0
  137. pycoast/tests/test_data/shapes/Metareas.prj +1 -0
  138. pycoast/tests/test_data/shapes/Metareas.sbn +0 -0
  139. pycoast/tests/test_data/shapes/Metareas.sbx +0 -0
  140. pycoast/tests/test_data/shapes/Metareas.shp +0 -0
  141. pycoast/tests/test_data/shapes/Metareas.shx +0 -0
  142. pycoast/tests/test_data/shapes/README +3 -0
  143. pycoast/tests/test_data/shapes/divisao_politica/BRASIL.dbf +0 -0
  144. pycoast/tests/test_data/shapes/divisao_politica/BRASIL.shp +0 -0
  145. pycoast/tests/test_data/shapes/divisao_politica/BRASIL.shx +0 -0
  146. pycoast/tests/test_data/shapes/divisao_politica/BR_Capitais.dbf +0 -0
  147. pycoast/tests/test_data/shapes/divisao_politica/BR_Capitais.shp +0 -0
  148. pycoast/tests/test_data/shapes/divisao_politica/BR_Capitais.shx +0 -0
  149. pycoast/tests/test_data/shapes/divisao_politica/BR_Contorno.dbf +0 -0
  150. pycoast/tests/test_data/shapes/divisao_politica/BR_Contorno.shp +0 -0
  151. pycoast/tests/test_data/shapes/divisao_politica/BR_Contorno.shx +0 -0
  152. pycoast/tests/test_data/shapes/divisao_politica/BR_Regioes.dbf +0 -0
  153. pycoast/tests/test_data/shapes/divisao_politica/BR_Regioes.shp +0 -0
  154. pycoast/tests/test_data/shapes/divisao_politica/BR_Regioes.shx +0 -0
  155. pycoast/tests/test_data/shapes/divisao_politica/divisao_politica.txt +40 -0
  156. pycoast/tests/test_data/shapes/divisao_politica/leia.txt +9 -0
  157. pycoast/tests/test_data/shapes/metarea5.gsf +0 -0
  158. pycoast/tests/test_data/shapes/metarea5.tbl +21 -0
  159. pycoast/tests/test_data/shapes/metarea5.tbl.info +25 -0
  160. pycoast/tests/test_data/test_config.ini +12 -0
  161. pycoast/tests/test_pycoast.py +1916 -419
  162. pycoast/tests/two_shapefiles_agg.png +0 -0
  163. pycoast/tests/two_shapefiles_pil.png +0 -0
  164. pycoast/tests/western_shapes_agg.png +0 -0
  165. pycoast/tests/western_shapes_pil.png +0 -0
  166. pycoast/version.py +19 -17
  167. pycoast-1.7.1.dist-info/AUTHORS.md +20 -0
  168. pycoast-1.7.1.dist-info/LICENSE.txt +674 -0
  169. pycoast-1.7.1.dist-info/METADATA +81 -0
  170. pycoast-1.7.1.dist-info/RECORD +172 -0
  171. {pycoast-1.1.0.dist-info → pycoast-1.7.1.dist-info}/WHEEL +1 -1
  172. pycoast-1.1.0.dist-info/DESCRIPTION.rst +0 -3
  173. pycoast-1.1.0.dist-info/METADATA +0 -24
  174. pycoast-1.1.0.dist-info/RECORD +0 -13
  175. pycoast-1.1.0.dist-info/metadata.json +0 -1
  176. {pycoast-1.1.0.dist-info → pycoast-1.7.1.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,8 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: utf-8 -*-
1
3
  # pycoast, Writing of coastlines, borders and rivers to images in Python
2
4
  #
3
- # Copyright (C) 2011, 2016 Esben S. Nielsen
5
+ # Copyright (C) 2011-2020 PyCoast Developers
4
6
  #
5
7
  # This program is free software: you can redistribute it and/or modify
6
8
  # it under the terms of the GNU General Public License as published by
@@ -14,30 +16,50 @@
14
16
  #
15
17
  # You should have received a copy of the GNU General Public License
16
18
  # along with this program. If not, see <http://www.gnu.org/licenses/>.
19
+ """Main unit tests for pycoast."""
17
20
 
18
21
  import os
19
- import unittest
22
+ import pathlib
23
+ import time
24
+ from glob import glob
20
25
 
26
+ import aggdraw
21
27
  import numpy as np
28
+ import pytest
29
+ import shapefile
22
30
  from PIL import Image, ImageFont
31
+ from pyproj import CRS
32
+ from pyresample.geometry import AreaDefinition
33
+ from pytest_lazy_fixtures import lf as lazy_fixture
23
34
 
24
- from pycoast import ContourWriter, ContourWriterAGG
35
+ LOCAL_DIR = os.path.dirname(__file__)
25
36
 
37
+ gshhs_root_dir = os.path.join(LOCAL_DIR, "test_data", "gshhs")
38
+ repos_root_dir = os.path.join(LOCAL_DIR, "..", "..")
39
+ test_filename = "test_image.png"
40
+ grid_filename = "test_grid.png"
41
+ p_coasts_filename = "test_coasts_p_mode.png"
26
42
 
27
- def tmp(f):
28
- f.tmp = True
29
- return f
43
+ font_path = os.path.join(LOCAL_DIR, "test_data", "DejaVuSerif.ttf")
44
+ agg_font_20_yellow = aggdraw.Font("yellow", font_path, opacity=255, size=20)
45
+ agg_font_20_orange = aggdraw.Font("orange", font_path, opacity=255, size=20)
46
+ pil_font_20 = ImageFont.truetype(font_path, 20)
47
+ pil_font_16 = ImageFont.truetype(font_path, 16)
48
+
49
+
50
+ @pytest.fixture
51
+ def cd_test_dir(monkeypatch):
52
+ """Change directory to the pycoast/tests directory."""
53
+ monkeypatch.chdir(LOCAL_DIR)
30
54
 
31
55
 
32
56
  def fft_proj_rms(a1, a2):
33
- """Compute the RMS of differences between FFT vectors of a1
34
- and projection of FFT vectors of a2.
57
+ """Compute the RMS of differences between FFT vectors of a1 and projection of FFT vectors of a2.
58
+
35
59
  This metric is sensitive to large scale changes and image noise but
36
60
  insensitive to small rendering differences.
37
61
  """
38
-
39
62
  ms = 0
40
-
41
63
  for i in range(3):
42
64
  fr1 = np.fft.fft2(a1[:, :, i])
43
65
  fr2 = np.fft.fft2(a2[:, :, i])
@@ -49,579 +71,2054 @@ def fft_proj_rms(a1, a2):
49
71
  p2 = np.arctan2(fr2.imag, fr2.real)
50
72
 
51
73
  theta = p2 - p1
52
- l = ps2 * np.cos(theta)
53
- ms += ((l - ps1) ** 2).sum() / float(ps1.size)
74
+ adjusted_ps2 = ps2 * np.cos(theta)
75
+ ms += ((adjusted_ps2 - ps1) ** 2).sum() / float(ps1.size)
54
76
 
55
77
  rms = np.sqrt(ms)
56
78
 
57
79
  return rms
58
80
 
59
81
 
60
- def fft_metric(data1, data2, max_value=0.1):
61
- """Execute FFT metric
62
- """
63
-
82
+ def fft_metric(data1, data2, max_value=0.1, plot_failure=False):
83
+ """Execute FFT metric."""
64
84
  rms = fft_proj_rms(data1, data2)
65
- return rms <= max_value
85
+ within_threshold = rms <= max_value
86
+ if not within_threshold and plot_failure:
87
+ import matplotlib.pyplot as plt
66
88
 
89
+ fig, (ax1, ax2, ax3) = plt.subplots(3, 1)
90
+ ax1.imshow(data1)
91
+ ax2.imshow(data2)
92
+ ax3.imshow(np.abs(data1.astype(np.float64) - data2.astype(np.float64)).astype(np.uint8))
93
+ plt.show()
94
+ return within_threshold
67
95
 
68
- gshhs_root_dir = os.path.join(os.path.dirname(__file__), 'test_data', 'gshhs')
69
- test_file = 'test_image.png'
70
- grid_file = 'test_grid.png'
71
96
 
97
+ def europe():
98
+ """Create a Europe area."""
99
+ proj4_string = "+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84"
100
+ area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
101
+ area_def = (proj4_string, area_extent)
102
+ return area_def
72
103
 
73
- class TestPycoast(unittest.TestCase):
74
104
 
75
- def setUp(self):
76
- img = Image.new('RGB', (640, 480))
77
- img.save(test_file)
78
- img.save(grid_file)
105
+ EUROPE = europe()
79
106
 
80
- def tearDown(self):
81
- os.remove(test_file)
82
- os.remove(grid_file)
83
107
 
108
+ def geos():
109
+ """Create a geos area."""
110
+ proj4_string = "+proj=geos +lon_0=0.0 +a=6378169.00 +b=6356583.80 +h=35785831.0"
111
+ area_extent = (
112
+ -5570248.4773392612,
113
+ -5567248.074173444,
114
+ 5567248.074173444,
115
+ 5570248.4773392612,
116
+ )
117
+ area_def = (proj4_string, area_extent)
118
+ return area_def
84
119
 
85
- class TestPIL(TestPycoast):
86
120
 
87
- def test_europe(self):
88
- euro_img = Image.open(os.path.join(os.path.dirname(__file__),
89
- 'contours_europe.png'))
90
- euro_data = np.array(euro_img)
121
+ GEOS = geos()
91
122
 
92
- img = Image.new('RGB', (640, 480))
93
- proj4_string = \
94
- '+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84'
95
- area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
96
- area_def = (proj4_string, area_extent)
97
- cw = ContourWriter(gshhs_root_dir)
98
- cw.add_coastlines(img, area_def, resolution='l', level=4)
99
- cw.add_rivers(img, area_def, level=5, outline='blue')
100
- cw.add_borders(img, area_def, outline=(255, 0, 0))
101
123
 
102
- res = np.array(img)
103
- self.assertTrue(fft_metric(euro_data, res),
104
- 'Writing of contours failed')
124
+ def germ():
125
+ """Create an area covering Germany."""
126
+ proj4_string = "+proj=stere +ellps=bessel +lat_0=90.0 +lon_0=5.0 +lat_ts=50.0 +a=6378144.0 +b=6356759.0"
127
+ area_extent = [-155100.436345, -4441495.37946, 868899.563655, -3417495.37946]
128
+ area_def = (proj4_string, area_extent)
129
+ return area_def
105
130
 
106
- def test_europe_file(self):
107
- euro_img = Image.open(os.path.join(os.path.dirname(__file__),
108
- 'contours_europe.png'))
109
- euro_data = np.array(euro_img)
110
131
 
111
- proj4_string = \
112
- '+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84'
113
- area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
114
- area_def = (proj4_string, area_extent)
115
- cw = ContourWriter(gshhs_root_dir)
116
- cw.add_coastlines_to_file(test_file, area_def, resolution='l', level=4)
117
- cw.add_rivers_to_file(test_file, area_def, level=5, outline='blue')
118
- cw.add_borders_to_file(test_file, area_def, outline=(255, 0, 0))
132
+ GERM = germ()
119
133
 
120
- img = Image.open(test_file)
121
- res = np.array(img)
122
- self.assertTrue(
123
- fft_metric(euro_data, res), 'Writing of contours failed')
124
134
 
125
- def test_geos(self):
126
- geos_img = Image.open(os.path.join(os.path.dirname(__file__),
127
- 'contours_geos.png'))
128
- geos_data = np.array(geos_img)
135
+ def dateline_1():
136
+ """Create an area covering the dateline."""
137
+ proj4_string = "+proj=stere +lon_0=-170.00 +lat_0=60.00 +lat_ts=50.00 +ellps=WGS84"
138
+ area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
139
+ area_def = (proj4_string, area_extent)
140
+ return area_def
129
141
 
130
- img = Image.new('RGB', (425, 425))
131
- proj4_string = \
132
- '+proj=geos +lon_0=0.0 +a=6378169.00 +b=6356583.80 +h=35785831.0'
133
- area_extent = (-5570248.4773392612, -5567248.074173444,
134
- 5567248.074173444, 5570248.4773392612)
135
- area_def = (proj4_string, area_extent)
136
- cw = ContourWriter(gshhs_root_dir)
137
- cw.add_coastlines(img, area_def, resolution='l')
138
142
 
139
- res = np.array(img)
140
- self.assertTrue(
141
- fft_metric(geos_data, res), 'Writing of geos contours failed')
143
+ DATELINE_1 = dateline_1()
142
144
 
143
- def test_grid(self):
144
- grid_img = Image.open(os.path.join(os.path.dirname(__file__),
145
- 'grid_europe.png'))
146
- grid_data = np.array(grid_img)
147
- img = Image.new('RGB', (640, 480))
148
- proj4_string = \
149
- '+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84'
150
- area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
151
- area_def = (proj4_string, area_extent)
152
145
 
153
- cw = ContourWriter(gshhs_root_dir)
146
+ def dateline_2():
147
+ """Create another area covering the dateline."""
148
+ proj4_string = "+proj=stere +lon_0=140.00 +lat_0=60.00 +lat_ts=50.00 +ellps=WGS84"
149
+ area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
150
+ area_def = (proj4_string, area_extent)
151
+ return area_def
154
152
 
155
- cw.add_coastlines(img, area_def, resolution='l', level=4)
156
- font = ImageFont.truetype(os.path.join(os.path.dirname(__file__),
157
- 'test_data', 'DejaVuSerif.ttf'),
158
- 16)
159
- cw.add_grid(img, area_def, (10.0, 10.0), (2.0, 2.0),
160
- font=font, fill='blue', write_text=False,
161
- outline='blue', minor_outline='blue')
162
153
 
163
- res = np.array(img)
164
- self.assertTrue(fft_metric(grid_data, res), 'Writing of grid failed')
154
+ DATELINE_2 = dateline_2()
165
155
 
166
- def test_grid_geos(self):
167
- geos_img = Image.open(
168
- os.path.join(os.path.dirname(__file__), 'grid_geos.png'))
169
- geos_data = np.array(geos_img)
170
- img = Image.new('RGB', (425, 425))
171
- proj4_string = \
172
- '+proj=geos +lon_0=0.0 +a=6378169.00 +b=6356583.80 +h=35785831.0'
173
- area_extent = (-5570248.4773392612, -5567248.074173444,
174
- 5567248.074173444, 5570248.4773392612)
175
- area_def = (proj4_string, area_extent)
176
- cw = ContourWriter(gshhs_root_dir)
177
- cw.add_coastlines(img, area_def, resolution='l')
178
- cw.add_grid(img, area_def, (10.0, 10.0), (2.0, 2.0), fill='blue',
179
- outline='blue', minor_outline='blue',
180
- write_text=False)
181
156
 
182
- res = np.array(img)
183
- self.assertTrue(
184
- fft_metric(geos_data, res), 'Writing of geos contours failed')
157
+ def lonlat_0(pm=0):
158
+ """Create longlat projection over Cuba."""
159
+ proj4_string = "+proj=longlat +lon_0=0.0 +ellps=WGS84"
160
+ if pm:
161
+ proj4_string += f" +pm={pm}"
162
+ area_extent = (pm + -90.0, 15.0, pm + -70.0, 25.0)
163
+ area_def = (proj4_string, area_extent)
164
+ return area_def
185
165
 
186
- def test_grid_file(self):
187
- grid_img = Image.open(os.path.join(os.path.dirname(__file__),
188
- 'grid_europe.png'))
189
- grid_data = np.array(grid_img)
190
- proj4_string = \
191
- '+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84'
192
- area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
193
- area_def = (proj4_string, area_extent)
194
166
 
195
- cw = ContourWriter(gshhs_root_dir)
167
+ LONLAT_0 = lonlat_0()
168
+ LONLAT_0_180 = lonlat_0(pm=180)
196
169
 
197
- cw.add_coastlines_to_file(grid_file, area_def, resolution='l', level=4)
198
- font = ImageFont.truetype(os.path.join(os.path.dirname(__file__),
199
- 'test_data', 'DejaVuSerif.ttf'),
200
- 16)
201
- cw.add_grid_to_file(grid_file, area_def, (10.0, 10.0), (2.0, 2.0),
202
- font=font, fill='blue', write_text=False,
203
- outline='blue', minor_outline='blue')
204
170
 
205
- img = Image.open(grid_file)
206
- res = np.array(img)
207
- self.assertTrue(fft_metric(grid_data, res), 'Writing of grid failed')
171
+ def nh():
172
+ """Create the nh area."""
173
+ proj4_string = "+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m"
174
+ area_extent = (-5326849.0625, -5326849.0625, 5326849.0625, 5326849.0625)
175
+ area_def = (proj4_string, area_extent)
176
+ return area_def
208
177
 
209
- def test_dateline_cross(self):
210
- dl_img = Image.open(os.path.join(os.path.dirname(__file__),
211
- 'dateline_cross.png'))
212
- dl_data = np.array(dl_img)
213
178
 
214
- img = Image.new('RGB', (640, 480))
215
- proj4_string = '+proj=stere +lon_0=-170.00 +lat_0=60.00 +lat_ts=50.00 +ellps=WGS84'
216
- area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
217
- area_def = (proj4_string, area_extent)
179
+ NH = nh()
218
180
 
219
- cw = ContourWriter(gshhs_root_dir)
220
181
 
221
- cw.add_coastlines(img, area_def, resolution='l', level=4)
222
- font = ImageFont.truetype(os.path.join(os.path.dirname(__file__),
223
- 'test_data',
224
- 'DejaVuSerif.ttf'), 16)
225
- cw.add_grid(img, area_def, (10.0, 10.0), (2.0, 2.0),
226
- font=font, fill='blue', write_text=False,
227
- outline='blue', minor_outline='blue',
228
- lon_placement='b', lat_placement='lr')
182
+ def nh_1024():
183
+ """Create the nh area in 1024x1024 pixels."""
184
+ proj4_string = "+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m"
185
+ area_extent = (-5326849.0625, -5326849.0625, 5326849.0625, 5326849.0625)
186
+ area_def = AreaDefinition("nh", "nh", "nh", proj4_string, 1024, 1024, area_extent)
187
+ return area_def
229
188
 
230
- res = np.array(img)
231
- self.assertTrue(fft_metric(dl_data, res),
232
- 'Writing of dateline crossing data failed')
233
189
 
234
- def test_dateline_boundary_cross(self):
235
- dl_img = Image.open(os.path.join(os.path.dirname(__file__),
236
- 'dateline_boundary_cross.png'))
237
- dl_data = np.array(dl_img)
190
+ NH_1024 = nh_1024()
238
191
 
239
- img = Image.new('RGB', (640, 480))
240
- proj4_string = \
241
- '+proj=stere +lon_0=140.00 +lat_0=60.00 +lat_ts=50.00 +ellps=WGS84'
242
- area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
243
- area_def = (proj4_string, area_extent)
244
192
 
245
- cw = ContourWriter(gshhs_root_dir)
193
+ def nh_def(shape):
194
+ """Create the nh area definition with custom shape."""
195
+ proj4_string = "+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m"
196
+ area_extent = (-5326849.0625, -5326849.0625, 5326849.0625, 0.0)
197
+ area_def = AreaDefinition("nh", "nh", "nh", proj4_string, shape[1], shape[0], area_extent)
198
+ return area_def
246
199
 
247
- cw.add_coastlines(img, area_def, resolution='l', level=4)
248
- font = ImageFont.truetype(os.path.join(os.path.dirname(__file__),
249
- 'test_data',
250
- 'DejaVuSerif.ttf'), 16)
251
- cw.add_grid(img, area_def, (10.0, 10.0), (2.0, 2.0),
252
- font=font, fill='blue',
253
- outline='blue', minor_outline='blue', write_text=False,
254
- lon_placement='b', lat_placement='lr')
255
200
 
256
- res = np.array(img)
257
- self.assertTrue(fft_metric(dl_data, res),
258
- 'Writing of dateline boundary crossing data failed')
201
+ def brazil():
202
+ """Create a Brazil area."""
203
+ proj4_string = "+proj=merc +lon_0=-60 +lat_ts=-30.0 +a=6371228.0 +units=m"
204
+ area_extent = (-2000000.0, -5000000.0, 5000000.0, 2000000.0)
205
+ area_def = (proj4_string, area_extent)
206
+ return area_def
259
207
 
260
- def test_grid_nh(self):
261
- grid_img = Image.open(os.path.join(os.path.dirname(__file__),
262
- 'grid_nh.png'))
263
- grid_data = np.array(grid_img)
264
- img = Image.new('RGB', (425, 425))
265
- proj4_string = '+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m'
266
- area_extent = (-5326849.0625, -5326849.0625,
267
- 5326849.0625, 5326849.0625)
268
- area_def = (proj4_string, area_extent)
269
208
 
270
- cw = ContourWriter(gshhs_root_dir)
209
+ BRAZIL = brazil()
271
210
 
272
- cw.add_coastlines(img, area_def, resolution='l', level=4)
273
- font = ImageFont.truetype(os.path.join(os.path.dirname(__file__),
274
- 'test_data', 'DejaVuSerif.ttf'),
275
- 10)
276
- cw.add_grid(img, area_def, (10.0, 10.0), (2.0, 2.0),
277
- font=font, fill='blue',
278
- outline='blue', minor_outline='blue', write_text=False,
279
- lon_placement='tblr', lat_placement='')
280
211
 
281
- res = np.array(img)
282
- self.assertTrue(
283
- fft_metric(grid_data, res), 'Writing of nh grid failed')
212
+ def nh_425():
213
+ """Create the nh area in 425 pixels size."""
214
+ proj_dict = {
215
+ "proj": "laea",
216
+ "lat_0": 90.0,
217
+ "lon_0": 0.0,
218
+ "a": 6371228.0,
219
+ "units": "m",
220
+ }
221
+ area_extent = (-5326849.0625, -5326849.0625, 5326849.0625, 5326849.0625)
222
+ area_def = AreaDefinition("nh", "nh", "nh", proj_dict, 425, 425, area_extent)
223
+ return area_def
284
224
 
285
- def test_add_polygon(self):
286
- grid_img = Image.open(os.path.join(os.path.dirname(__file__),
287
- 'nh_polygons.png'))
288
- grid_data = np.array(grid_img)
289
- img = Image.new('RGB', (425, 425))
290
- proj4_string = '+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m'
291
- area_extent = (-5326849.0625, -5326849.0625,
292
- 5326849.0625, 5326849.0625)
293
- area_def = (proj4_string, area_extent)
294
225
 
295
- cw = ContourWriter(gshhs_root_dir)
226
+ NH_425 = nh_425()
296
227
 
297
- polygons = {
298
- 'REYKJAVIK_ATC_A': ((-20.0, 73.0), (0.0, 73.0), (0.0, 61.0),
299
- (-30.0, 61.0), (-39.0, 63.5), (-20, 70)),
300
- 'REYKJAVIK_ATC_B': (
301
- (-39, 63.5), (-55 + 4 / 6.0, 63.5), (-57 + 45 / 60.0, 65),
302
- (-76, 76), (-75, 78), (-60, 82), (0, 90),
303
- (30, 82), (0, 82), (0, 73), (-20, 73), (-20, 70)),
304
- 'REYKJAVIK_ATC': (
305
- (0.0, 73.0), (0.0, 61.0), (-30.0, 61.0), (-39, 63.5),
306
- (-55 + 4 / 6.0, 63.5), (-57 + 45 / 60.0, 65),
307
- (-76, 76), (-75, 78), (-60, 82), (0, 90), (30, 82), (0, 82)),
308
- 'ICELAND_BOX': ((-25, 62.5), (-25, 67), (-13, 67), (-13, 62.5))
309
- }
310
228
 
311
- cw.add_polygon(img, area_def, polygons['REYKJAVIK_ATC'], outline='red')
312
- cw.add_polygon(img, area_def, polygons['ICELAND_BOX'], outline='green',
313
- fill='gray')
314
- cw.add_coastlines(img, area_def, resolution='l', level=4)
229
+ def eurasia():
230
+ """Create a Eurasia area."""
231
+ proj4_string = "+proj=tmerc +ellps=WGS84 +lat_0=20.0 +lon_0=-50.0"
232
+ area_extent = [-4865942.5, 1781111.9, 4865942.5, 7235767.2]
233
+ area_def = (proj4_string, area_extent)
234
+ return area_def
315
235
 
316
- res = np.array(img)
317
- self.assertTrue(fft_metric(grid_data, res),
318
- 'Writing of nh polygons failed')
319
236
 
320
- def test_add_shapefile_shapes(self):
321
- grid_img = Image.open(os.path.join(os.path.dirname(__file__),
322
- 'brazil_shapefiles.png'))
323
- grid_data = np.array(grid_img)
237
+ def north_atlantic():
238
+ """Create a North Atlantic area."""
239
+ proj4_string = "+proj=tmerc +ellps=WGS84 +lat_0=20.0 +lon_0=50.0"
240
+ area_extent = [-4865942.5, 1781111.9, 4865942.5, 7235767.2]
241
+ area_def = (proj4_string, area_extent)
242
+ return area_def
324
243
 
325
- img = Image.new('RGB', (425, 425))
326
- proj4_string = '+proj=merc +lon_0=-60 +lat_ts=-30.0 +a=6371228.0 +units=m'
327
- area_extent = (-2000000.0, -5000000.0, 5000000.0, 2000000.0)
328
- area_def = (proj4_string, area_extent)
329
244
 
330
- cw = ContourWriter(gshhs_root_dir)
331
-
332
- cw.add_coastlines(img, area_def, resolution='l', level=4)
333
- cw.add_shapefile_shapes(img, area_def,
334
- os.path.join(
335
- os.path.dirname(__file__),
336
- 'test_data/shapes/Metareas.shp'),
337
- outline='red')
338
- cw.add_shapefile_shape(img, area_def,
339
- os.path.join(os.path.dirname(__file__),
340
- 'test_data/shapes/divisao_politica/BR_Regioes.shp'), 3,
341
- outline='blue')
342
- cw.add_shapefile_shape(img, area_def,
343
- os.path.join(os.path.dirname(__file__),
344
- 'test_data/shapes/divisao_politica/BR_Regioes.shp'), 4,
345
- outline='blue', fill='green')
245
+ def uk_and_ireland():
246
+ """Create an area covering Ireland and the UK."""
247
+ proj4_string = "+proj=stere +ellps=WGS84 +lon_0=-4.532 +lat_0=54.228"
248
+ area_extent = (-600000.0, -600000.0, 600000.0, 600000.0)
249
+ area_def = AreaDefinition("nh", "nh", "nh", proj4_string, 800, 800, area_extent)
250
+ return area_def
346
251
 
347
- res = np.array(img)
348
- self.assertTrue(
349
- fft_metric(grid_data, res), 'Writing of Brazil shapefiles failed')
350
252
 
253
+ def south_america():
254
+ """Create an area covering south America."""
255
+ proj4_string = "+proj=merc +lon_0=-60 +lat_ts=-30.0 +a=6371228.0 +units=m"
256
+ area_extent = (-2000000.0, -5000000.0, 5000000.0, 2000000.0)
257
+ area_def = AreaDefinition("nh", "nh", "nh", proj4_string, 425, 425, area_extent)
258
+ return area_def
259
+
260
+
261
+ def europe_1024():
262
+ """Create a Europe area in 1024 pixels size."""
263
+ proj4_string = "+proj=stere +ellps=WGS84 +lat_0=51.5 +lon_0=-0.1"
264
+ area_extent = (-2000000.0, -2000000.0, 2000000.0, 2000000.0)
265
+ area_def = AreaDefinition("nh", "nh", "nh", proj4_string, 1024, 1024, area_extent)
266
+ return area_def
267
+
268
+
269
+ def bering_straight():
270
+ """Create an area covering the Bering straight."""
271
+ proj4_string = "+proj=merc +ellps=WGS84 +lon_0=170.0"
272
+ area_extent = [-3899875.0, 1795000.0, 5014125.0, 9600000.0]
273
+ area_def = (proj4_string, area_extent)
274
+ return area_def
275
+
276
+
277
+ def hawaii():
278
+ """Create an area covering Hawai."""
279
+ proj4_string = "+proj=tmerc +ellps=WGS84 +lon_0=-155.0"
280
+ area_extent = [-3503550.0, -556597.5, 3503550.0, 5009377.3]
281
+ area_def = (proj4_string, area_extent)
282
+ return area_def
283
+
284
+
285
+ @pytest.fixture(scope="session")
286
+ def cw_pil():
287
+ """Create a PIL ContourWriter."""
288
+ from pycoast import ContourWriterPIL
289
+
290
+ cw = ContourWriterPIL(gshhs_root_dir)
291
+ return cw
292
+
293
+
294
+ @pytest.fixture
295
+ def cw_agg():
296
+ """Create a PIL ContourWriter."""
297
+ from pycoast import ContourWriterAGG
298
+
299
+ cw = ContourWriterAGG(gshhs_root_dir)
300
+ return cw
301
+
302
+
303
+ @pytest.fixture
304
+ def new_test_image(request, tmp_path):
305
+ """Create a new test image, and save it to tmp_path if the test fails."""
306
+ img_container = []
307
+
308
+ def _new_test_image(mode, shape, filename, color=0):
309
+ img = Image.new(mode, shape, color=color)
310
+ img_container.append(filename)
311
+ img_container.append(img)
312
+ return img
313
+
314
+ yield _new_test_image
315
+ try:
316
+ filename, img = img_container
317
+ except ValueError: # the fixture wasn't used
318
+ return
319
+ if request.node.rep_call.failed:
320
+ failed_path = tmp_path / filename
321
+ print(f"Failed image saved to: {failed_path}")
322
+ img.save(failed_path)
351
323
 
352
- @unittest.skipIf(ContourWriterAGG is ContourWriter, 'aggdraw not available')
353
- class TestPILAGG(TestPycoast):
324
+
325
+ def images_match(ref_image, test_image):
326
+ """Check is images match."""
327
+ return fft_metric(np.array(ref_image), np.array(test_image))
328
+
329
+
330
+ class _ContourWriterTestBase:
331
+ """Base class for test classes that need example images."""
332
+
333
+ def setup_method(self):
334
+ img = Image.new("RGB", (640, 480))
335
+ img.save(test_filename)
336
+ img.save(grid_filename)
337
+ img_p = Image.new("P", (640, 480))
338
+ img_p.save(p_coasts_filename)
339
+
340
+ def teardown_method(self):
341
+ os.remove(test_filename)
342
+ os.remove(grid_filename)
343
+ os.remove(p_coasts_filename)
344
+
345
+
346
+ @pytest.fixture
347
+ def test_file_path(tmp_path):
348
+ """Create a test image file on disk."""
349
+ path = tmp_path / test_filename
350
+ img = Image.new("RGB", (640, 480))
351
+ img.save(path)
352
+ yield path
353
+
354
+
355
+ @pytest.fixture
356
+ def grid_file_path(tmp_path):
357
+ """Create a test grid image file on disk."""
358
+ path = tmp_path / grid_filename
359
+ img = Image.new("RGB", (640, 480))
360
+ img.save(path)
361
+ yield path
362
+
363
+
364
+ class TestContourWriterPIL:
365
+ """Test PIL-based contour writer."""
366
+
367
+ def test_europe_coastlines_rivers_and_borders(self, cw_pil, new_test_image):
368
+ """Test coastlines, rivers and borders over Europe."""
369
+ filename = "contours_europe.png"
370
+
371
+ euro_img = Image.open(os.path.join(LOCAL_DIR, filename))
372
+
373
+ img = new_test_image("RGB", (640, 480), filename)
374
+
375
+ area_def = EUROPE
376
+ cw_pil.add_coastlines(img, area_def, resolution="l", level=4)
377
+ cw_pil.add_rivers(img, area_def, level=5, outline="blue")
378
+ cw_pil.add_borders(img, area_def, outline=(255, 0, 0), level=1)
379
+
380
+ assert images_match(euro_img, img), "Writing of contours failed"
381
+
382
+ def test_europe_coastlines_rivers_and_borders_on_file(self, cw_pil, test_file_path):
383
+ """Test coastlines, rivers and borders over Europe on a file."""
384
+ filename = "contours_europe.png"
385
+ euro_img = Image.open(os.path.join(LOCAL_DIR, filename))
386
+
387
+ area_def = EUROPE
388
+ cw_pil.add_coastlines_to_file(test_file_path, area_def, resolution="l", level=4)
389
+ cw_pil.add_rivers_to_file(test_file_path, area_def, level=5, outline="blue")
390
+ cw_pil.add_borders_to_file(test_file_path, area_def, outline=(255, 0, 0))
391
+
392
+ img = Image.open(test_file_path)
393
+ assert images_match(euro_img, img), "Writing of contours to file failed"
394
+
395
+ def test_geos(self, cw_pil, new_test_image):
396
+ filename = "contours_geos.png"
397
+ geos_img = Image.open(os.path.join(LOCAL_DIR, filename))
398
+
399
+ img = new_test_image("RGB", (425, 425), filename)
400
+ area_def = GEOS
401
+ cw_pil.add_coastlines(img, area_def, resolution="l")
402
+
403
+ assert images_match(geos_img, img), "Writing of geos contours failed"
404
+
405
+ @pytest.mark.parametrize(
406
+ "filename, shape, area_def, level, grid_kwargs",
407
+ [
408
+ (
409
+ "grid_europe.png",
410
+ (640, 480),
411
+ EUROPE,
412
+ 4,
413
+ dict(fill="blue", write_text=False, outline="blue", minor_outline="blue"),
414
+ ),
415
+ (
416
+ "grid_geos.png",
417
+ (425, 425),
418
+ GEOS,
419
+ 1,
420
+ dict(fill="blue", write_text=False, outline="blue", minor_outline="blue"),
421
+ ),
422
+ (
423
+ "grid_geos.png",
424
+ (425, 425),
425
+ GEOS,
426
+ 1,
427
+ dict(fill="blue", write_text=True, outline="blue", minor_outline="blue"),
428
+ ),
429
+ (
430
+ "grid_germ.png",
431
+ (1024, 1024),
432
+ GERM,
433
+ 4,
434
+ dict(fill="yellow", write_text=True, outline="red", minor_outline="white"),
435
+ ),
436
+ (
437
+ "dateline_cross.png",
438
+ (640, 480),
439
+ DATELINE_1,
440
+ 4,
441
+ dict(
442
+ fill="blue",
443
+ write_text=False,
444
+ outline="blue",
445
+ minor_outline="blue",
446
+ lon_placement="b",
447
+ lat_placement="lr",
448
+ ),
449
+ ),
450
+ (
451
+ "dateline_boundary_cross.png",
452
+ (640, 480),
453
+ DATELINE_2,
454
+ 4,
455
+ dict(
456
+ fill="blue",
457
+ write_text=False,
458
+ outline="blue",
459
+ minor_outline="blue",
460
+ lon_placement="b",
461
+ lat_placement="lr",
462
+ ),
463
+ ),
464
+ (
465
+ "lonlat_boundary_cross.png",
466
+ (640, 480),
467
+ LONLAT_0,
468
+ 4,
469
+ dict(
470
+ fill="blue",
471
+ write_text=False,
472
+ outline="blue",
473
+ minor_outline="blue",
474
+ lon_placement="b",
475
+ lat_placement="lr",
476
+ ),
477
+ ),
478
+ (
479
+ "lonlat_boundary_cross.png",
480
+ (640, 480),
481
+ LONLAT_0_180,
482
+ 4,
483
+ dict(
484
+ fill="blue",
485
+ write_text=False,
486
+ outline="blue",
487
+ minor_outline="blue",
488
+ lon_placement="b",
489
+ lat_placement="lr",
490
+ ),
491
+ ),
492
+ ],
493
+ )
494
+ @pytest.mark.usefixtures("cd_test_dir")
495
+ def test_grid(self, cw_pil, new_test_image, filename, shape, area_def, level, grid_kwargs):
496
+ img = new_test_image("RGB", shape, filename)
497
+
498
+ cw_pil.add_coastlines(img, area_def, resolution="l", level=level)
499
+ font = pil_font_16
500
+ cw_pil.add_grid(img, area_def, (10.0, 10.0), (2.0, 2.0), font=font, **grid_kwargs)
501
+
502
+ grid_img = Image.open(os.path.join(LOCAL_DIR, filename))
503
+ assert images_match(grid_img, img), "Writing of grid failed"
504
+
505
+ def test_grid_nh(self, cw_pil, new_test_image):
506
+ filename = "grid_nh.png"
507
+ grid_img = Image.open(os.path.join(LOCAL_DIR, filename))
508
+
509
+ img = new_test_image("RGB", (425, 425), filename)
510
+ area_def = NH
511
+
512
+ cw_pil.add_coastlines(img, area_def, resolution="l", level=4)
513
+ font = ImageFont.truetype(font_path, 10)
514
+ cw_pil.add_grid(
515
+ img,
516
+ area_def,
517
+ (10.0, 10.0),
518
+ (2.0, 2.0),
519
+ font=font,
520
+ fill="blue",
521
+ outline="blue",
522
+ minor_outline="blue",
523
+ write_text=False,
524
+ lon_placement="tblr",
525
+ lat_placement="",
526
+ )
527
+
528
+ assert images_match(grid_img, img), "Writing of nh grid failed"
529
+
530
+ def test_grid_file(self, cw_pil, grid_file_path):
531
+ filename = "grid_europe.png"
532
+ grid_img = Image.open(os.path.join(LOCAL_DIR, filename))
533
+
534
+ area_def = EUROPE
535
+
536
+ cw_pil.add_coastlines_to_file(grid_file_path, area_def, resolution="l", level=4)
537
+ font = pil_font_16
538
+ cw_pil.add_grid_to_file(
539
+ grid_file_path,
540
+ area_def,
541
+ (10.0, 10.0),
542
+ (2.0, 2.0),
543
+ font=font,
544
+ fill="blue",
545
+ write_text=False,
546
+ outline="blue",
547
+ minor_outline="blue",
548
+ )
549
+
550
+ img = Image.open(grid_file_path)
551
+ assert images_match(grid_img, img), "Writing of grid failed"
552
+
553
+ def test_add_polygon(self, cw_pil, new_test_image):
554
+ filename = "nh_polygons.png"
555
+ ref_image = Image.open(os.path.join(LOCAL_DIR, filename))
556
+
557
+ img = new_test_image("RGB", (425, 425), filename)
558
+ area_def = NH
559
+
560
+ polygons = {
561
+ "REYKJAVIK_ATC_A": (
562
+ (-20.0, 73.0),
563
+ (0.0, 73.0),
564
+ (0.0, 61.0),
565
+ (-30.0, 61.0),
566
+ (-39.0, 63.5),
567
+ (-20, 70),
568
+ ),
569
+ "REYKJAVIK_ATC_B": (
570
+ (-39, 63.5),
571
+ (-55 + 4 / 6.0, 63.5),
572
+ (-57 + 45 / 60.0, 65),
573
+ (-76, 76),
574
+ (-75, 78),
575
+ (-60, 82),
576
+ (0, 90),
577
+ (30, 82),
578
+ (0, 82),
579
+ (0, 73),
580
+ (-20, 73),
581
+ (-20, 70),
582
+ ),
583
+ "REYKJAVIK_ATC": (
584
+ (0.0, 73.0),
585
+ (0.0, 61.0),
586
+ (-30.0, 61.0),
587
+ (-39, 63.5),
588
+ (-55 + 4 / 6.0, 63.5),
589
+ (-57 + 45 / 60.0, 65),
590
+ (-76, 76),
591
+ (-75, 78),
592
+ (-60, 82),
593
+ (0, 90),
594
+ (30, 82),
595
+ (0, 82),
596
+ ),
597
+ "ICELAND_BOX": ((-25, 62.5), (-25, 67), (-13, 67), (-13, 62.5)),
598
+ }
599
+
600
+ cw_pil.add_polygon(img, area_def, polygons["REYKJAVIK_ATC"], outline="red")
601
+ cw_pil.add_polygon(img, area_def, polygons["ICELAND_BOX"], outline="green", fill="gray")
602
+ cw_pil.add_coastlines(img, area_def, resolution="l", level=4)
603
+
604
+ assert images_match(ref_image, img), "Writing of nh polygons failed"
605
+
606
+ def test_add_points_pil(self, cw_pil, new_test_image):
607
+ filename = "nh_points_pil.png"
608
+ ref_image = Image.open(os.path.join(LOCAL_DIR, filename))
609
+
610
+ img = new_test_image("RGB", (1024, 1024), filename, color=(255, 255, 255))
611
+
612
+ area_def = NH_1024
613
+
614
+ cw_pil.add_coastlines(img, area_def, outline="black", resolution="l", level=4)
615
+ cw_pil.add_borders(img, area_def, outline="black", level=1, resolution="c")
616
+
617
+ points_list = [((13.4050, 52.5200), "Berlin")]
618
+ cw_pil.add_points(
619
+ img,
620
+ area_def,
621
+ points_list=points_list,
622
+ font_file=font_path,
623
+ symbol="asterisk",
624
+ ptsize=6,
625
+ outline="red",
626
+ box_outline="black",
627
+ )
628
+
629
+ points_list = [((12.4964, 41.9028), "Rome")]
630
+ cw_pil.add_points(
631
+ img,
632
+ area_def,
633
+ points_list=points_list,
634
+ font_file=font_path,
635
+ symbol="square",
636
+ ptsize=6,
637
+ outline="blue",
638
+ fill="yellow",
639
+ box_outline="black",
640
+ )
641
+
642
+ assert images_match(ref_image, img), "Writing of nh points failed"
643
+
644
+ def test_add_points_coordinate_conversion(self, cw_pil, new_test_image):
645
+ """Check that a point added with lonlat coordinates matches the same point in pixel coordinates."""
646
+ shape = (512, 1024)
647
+ area_def = nh_def(shape)
648
+ lonlat_coords = (13.4050, 52.5200)
649
+ pixel_colrow = area_def.get_array_indices_from_lonlat(*lonlat_coords)
650
+ negative_pixel_colrow = (pixel_colrow[0] - shape[1], pixel_colrow[1] - shape[0])
651
+
652
+ img1 = Image.new("RGB", shape[::-1], (255, 255, 255))
653
+
654
+ points_list = [(lonlat_coords, "Berlin")]
655
+ cw_pil.add_points(
656
+ img1,
657
+ area_def,
658
+ points_list=points_list,
659
+ font_file=font_path,
660
+ symbol="asterisk",
661
+ ptsize=6,
662
+ outline="red",
663
+ )
664
+ res1 = np.array(img1)
665
+ assert (res1[..., 0] == 255).all() # everything is either white or red
666
+ assert (res1[..., 1] != 255).any() # not a completely white/empty image
667
+ assert (res1[..., 2] != 255).any() # not a completely white/empty image
668
+
669
+ for img_coords in (pixel_colrow, negative_pixel_colrow):
670
+ img2 = Image.new("RGB", shape[::-1], (255, 255, 255))
671
+ points_list = [(img_coords, "Berlin")]
672
+ cw_pil.add_points(
673
+ img2,
674
+ area_def,
675
+ points_list=points_list,
676
+ font_file=font_path,
677
+ symbol="asterisk",
678
+ ptsize=6,
679
+ outline="red",
680
+ coord_ref="image",
681
+ )
682
+ res2 = np.array(img2)
683
+ assert (res2 != 255).any() # not a completely black/empty image
684
+ np.testing.assert_allclose(res1, res2)
685
+
686
+ def test_add_points_bad_image_coords(self, cw_pil):
687
+ shape = (512, 1024)
688
+ area_def = nh_def(shape)
689
+ for pixel_colrow in (shape[::-1], (-10000, -10000)):
690
+ img1 = Image.new("RGB", shape[::-1], (255, 255, 255))
691
+
692
+ points_list = [(pixel_colrow, "Berlin")]
693
+ cw_pil.add_points(
694
+ img1,
695
+ area_def,
696
+ points_list=points_list,
697
+ font_file=font_path,
698
+ symbol="asterisk",
699
+ ptsize=6,
700
+ outline="red",
701
+ coord_ref="image",
702
+ )
703
+ res1 = np.array(img1)
704
+ np.testing.assert_allclose(res1, 255) # no added points
705
+
706
+ def test_add_points_bad_coord_ref(self, cw_pil):
707
+ shape = (512, 1024)
708
+ area_def = nh_def(shape)
709
+ img1 = Image.new("RGB", shape[::-1], (255, 255, 255))
710
+ points_list = [((0, 0), "Berlin")]
711
+ with pytest.raises(ValueError):
712
+ cw_pil.add_points(
713
+ img1,
714
+ area_def,
715
+ points_list=points_list,
716
+ font_file=font_path,
717
+ symbol="asterisk",
718
+ ptsize=6,
719
+ outline="red",
720
+ coord_ref="fake",
721
+ )
722
+
723
+ def test_add_shapefile_shapes(self, cw_pil, new_test_image):
724
+ filename = "brazil_shapefiles.png"
725
+ ref_image = Image.open(os.path.join(LOCAL_DIR, filename))
726
+
727
+ img = new_test_image("RGB", (425, 425), filename)
728
+ area_def = BRAZIL
729
+
730
+ cw_pil.add_coastlines(img, area_def, resolution="l", level=4)
731
+ cw_pil.add_shapefile_shapes(
732
+ img,
733
+ area_def,
734
+ os.path.join(LOCAL_DIR, "test_data/shapes/Metareas.shp"),
735
+ outline="red",
736
+ )
737
+ cw_pil.add_shapefile_shape(
738
+ img,
739
+ area_def,
740
+ os.path.join(
741
+ LOCAL_DIR,
742
+ "test_data/shapes/divisao_politica/BR_Regioes.shp",
743
+ ),
744
+ 3,
745
+ outline="blue",
746
+ )
747
+ cw_pil.add_shapefile_shape(
748
+ img,
749
+ area_def,
750
+ os.path.join(
751
+ LOCAL_DIR,
752
+ "test_data/shapes/divisao_politica/BR_Regioes.shp",
753
+ ),
754
+ 4,
755
+ outline="blue",
756
+ fill="green",
757
+ )
758
+
759
+ assert images_match(ref_image, img), "Writing of Brazil shapefiles failed"
760
+
761
+ @pytest.mark.usefixtures("cd_test_dir")
762
+ def test_config_file_coasts_and_grid(self, cw_pil, new_test_image):
763
+ overlay_config = os.path.join(LOCAL_DIR, "coasts_and_grid.ini")
764
+ filename = "grid_nh.png"
765
+ grid_img = Image.open(os.path.join(LOCAL_DIR, filename))
766
+
767
+ area_def = NH_425
768
+
769
+ overlay = cw_pil.add_overlay_from_config(overlay_config, area_def)
770
+ img = new_test_image("RGB", (425, 425), filename)
771
+ img.paste(overlay, mask=overlay)
772
+ assert images_match(grid_img, img), "Writing of nh grid failed"
773
+
774
+ @pytest.mark.usefixtures("cd_test_dir")
775
+ def test_config_file_points_and_borders_pil(self, cw_pil, new_test_image):
776
+ config_file = os.path.join(LOCAL_DIR, "nh_points_pil.ini")
777
+ filename = "nh_points_cfg_pil.png"
778
+ grid_img = Image.open(os.path.join(LOCAL_DIR, filename))
779
+
780
+ img = new_test_image("RGB", (1024, 1024), filename, color=(255, 255, 255))
781
+
782
+ area_def = NH_1024
783
+
784
+ cw_pil.add_overlay_from_config(config_file, area_def, img)
785
+
786
+ assert images_match(grid_img, img), "Writing of nh points failed"
787
+
788
+ def test_add_cities_pil(self, cw_pil, new_test_image):
789
+ filename = "nh_cities_pil.png"
790
+ grid_img = Image.open(os.path.join(LOCAL_DIR, filename))
791
+
792
+ img = new_test_image("RGB", (1024, 1024), filename, color=(255, 255, 255))
793
+
794
+ area_def = NH_1024
795
+
796
+ cw_pil.add_coastlines(img, area_def, outline="black", resolution="l", level=4)
797
+ cw_pil.add_borders(img, area_def, outline="black", level=1, resolution="c")
798
+
799
+ cities_list = ["Zurich", "Oslo", "Reykjavik", "Fairbanks", "Toronto"]
800
+ cw_pil.add_cities(
801
+ img,
802
+ area_def,
803
+ cities_list=cities_list,
804
+ font_file=font_path,
805
+ font_size=20,
806
+ symbol="square",
807
+ ptsize=16,
808
+ outline="black",
809
+ width=3,
810
+ fill="blue",
811
+ fill_opacity=128,
812
+ box_outline="blue",
813
+ box_linewidth=0.5,
814
+ box_fill="yellow",
815
+ box_opacity=200,
816
+ )
817
+
818
+ assert images_match(grid_img, img), "Writing of nh cities_pil failed"
819
+
820
+ @pytest.mark.usefixtures("cd_test_dir")
821
+ def test_add_cities_cfg_pil(self, cw_pil, new_test_image):
822
+ config_file = os.path.join(LOCAL_DIR, "nh_cities_pil.ini")
823
+ filename = "nh_cities_pil.png"
824
+ grid_img = Image.open(os.path.join(LOCAL_DIR, filename))
825
+
826
+ img = new_test_image("RGB", (1024, 1024), filename, color=(255, 255, 255))
827
+
828
+ area_def = NH_1024
829
+
830
+ cw_pil.add_overlay_from_config(config_file, area_def, img)
831
+
832
+ assert images_match(grid_img, img), "Writing of nh cities_cfg_pil failed"
833
+
834
+ def test_add_cities_from_dict_pil(self, cw_pil, new_test_image):
835
+ filename = "nh_cities_from_dict_pil.png"
836
+ grid_img = Image.open(os.path.join(LOCAL_DIR, filename))
837
+
838
+ img = new_test_image("RGB", (1024, 1024), filename, color=(255, 255, 255))
839
+
840
+ area_def = europe_1024()
841
+
842
+ overlays = {}
843
+ overlays["coasts"] = {"level": 4, "resolution": "l", "outline": "black"}
844
+ overlays["borders"] = {"level": 1, "outline": "black", "resolution": "c"}
845
+ cities_list1 = [
846
+ "Berlin",
847
+ "Paris",
848
+ "London",
849
+ "Dublin",
850
+ "Madrid",
851
+ "Reykjavik",
852
+ "Oslo",
853
+ "Rome",
854
+ ]
855
+ cities_list2 = ["Freiburg", "Montelimar", "Huesca", "Marseille"]
856
+ cities_list3 = ["Belp", "Bad Schwalbach", "Edinburgh", "Hilversum"]
857
+ cities_type1 = {
858
+ "cities_list": cities_list1,
859
+ "font": font_path,
860
+ "font_size": 26,
861
+ "symbol": "circle",
862
+ "ptsize": 24,
863
+ "outline": "black",
864
+ "fill": "red",
865
+ "box_outline": "black",
866
+ }
867
+ cities_type2 = {
868
+ "cities_list": cities_list2,
869
+ "font": font_path,
870
+ "font_size": 24,
871
+ "symbol": "pentagon",
872
+ "ptsize": 24,
873
+ "outline": "red",
874
+ "fill": "blue",
875
+ "box_outline": "black",
876
+ }
877
+ cities_type3 = {
878
+ "cities_list": cities_list3,
879
+ "font": font_path,
880
+ "font_size": 22,
881
+ "symbol": "star5",
882
+ "ptsize": 35,
883
+ "outline": "green",
884
+ "fill": "yellow",
885
+ "box_outline": "black",
886
+ }
887
+
888
+ overlays["cities"] = [cities_type1, cities_type2, cities_type3]
889
+
890
+ img = cw_pil.add_overlay_from_dict(overlays, area_def, background=img)
891
+
892
+ assert images_match(grid_img, img), "Writing of nh_cities_from_dict_pil failed"
893
+
894
+ def test_add_shapefiles_from_dict_pil(self, cw_pil, new_test_image):
895
+ filename = "two_shapefiles_pil.png"
896
+ grid_img = Image.open(os.path.join(LOCAL_DIR, filename))
897
+
898
+ img = new_test_image("RGB", (425, 425), filename)
899
+ area_def = south_america()
900
+
901
+ overlays = {}
902
+ overlays["coasts"] = {"level": 4, "resolution": "l"}
903
+ overlays["shapefiles"] = [
904
+ {
905
+ "filename": os.path.join(LOCAL_DIR, "test_data/shapes/Metareas.shp"),
906
+ "outline": "magenta",
907
+ "width": 2.5,
908
+ },
909
+ {
910
+ "filename": os.path.join(
911
+ LOCAL_DIR,
912
+ "test_data/shapes/divisao_politica/BR_Regioes.shp",
913
+ ),
914
+ "outline": "red",
915
+ "fill": "yellow",
916
+ "fill_opacity": 50,
917
+ },
918
+ ]
919
+
920
+ img = cw_pil.add_overlay_from_dict(overlays, area_def, background=img)
921
+
922
+ assert images_match(grid_img, img), "Writing of two shapefiles from dict pil failed"
923
+
924
+ @pytest.mark.usefixtures("cd_test_dir")
925
+ def test_add_one_shapefile_from_cfg_pil(self, cw_pil, new_test_image):
926
+ config_file = os.path.join(LOCAL_DIR, "nh_one_shapefile.ini")
927
+ filename = "one_shapefile_from_cfg_pil.png"
928
+ grid_img = Image.open(os.path.join(LOCAL_DIR, filename))
929
+
930
+ img = new_test_image("RGB", (425, 425), filename)
931
+ area_def = south_america()
932
+
933
+ cw_pil.add_overlay_from_config(config_file, area_def, img)
934
+
935
+ assert images_match(grid_img, img), "Writing one shapefile from cfg pil failed"
936
+
937
+ @pytest.mark.usefixtures("cd_test_dir")
938
+ def test_add_grid_from_dict_pil(self, cw_pil, new_test_image):
939
+ filename = "grid_from_dict_pil.png"
940
+ grid_img = Image.open(os.path.join(LOCAL_DIR, filename))
941
+
942
+ img = new_test_image("RGB", (800, 800), filename)
943
+ area_def = uk_and_ireland()
944
+
945
+ font = ImageFont.truetype(font_path, 40)
946
+
947
+ overlays = {}
948
+ overlays["coasts"] = {"width": 3.0, "level": 4, "resolution": "l"}
949
+ overlays["grid"] = {
950
+ "major_lonlat": (5, 5),
951
+ "minor_lonlat": (1, 1),
952
+ "outline": (255, 0, 0),
953
+ "outline_opacity": 127,
954
+ "minor_outline": (0, 0, 255),
955
+ "minor_outline_opacity": 127,
956
+ "width": 10.5,
957
+ "minor_width": 5.5,
958
+ "minor_is_tick": False,
959
+ "write_text": True,
960
+ "lat_placement": "lr",
961
+ "lon_placement": "b",
962
+ "font": font,
963
+ "fill": "yellow",
964
+ }
965
+ # Fill is pil text color! Pil Font can be None, then a of default font is choosen
966
+
967
+ img = cw_pil.add_overlay_from_dict(overlays, area_def, background=img)
968
+
969
+ assert images_match(grid_img, img), "Writing grid from dict pil failed"
970
+
971
+
972
+ @pytest.mark.parametrize(
973
+ "cw, filename, area_def, specific_kwargs",
974
+ [
975
+ (
976
+ lazy_fixture("cw_pil"),
977
+ "western_shapes_pil.png",
978
+ north_atlantic(),
979
+ dict(font=pil_font_16, fill="yellow", outline="red", minor_outline="red"),
980
+ ),
981
+ (
982
+ lazy_fixture("cw_pil"),
983
+ "eastern_shapes_pil.png",
984
+ eurasia(),
985
+ dict(font=pil_font_20, fill="yellow", outline="red", minor_outline="red"),
986
+ ),
987
+ (
988
+ lazy_fixture("cw_agg"),
989
+ "western_shapes_agg.png",
990
+ north_atlantic(),
991
+ dict(
992
+ font=agg_font_20_orange,
993
+ outline="blue",
994
+ width=5.0,
995
+ outline_opacity=100,
996
+ minor_outline="blue",
997
+ minor_width=5.0,
998
+ minor_outline_opacity=200,
999
+ ),
1000
+ ),
1001
+ (
1002
+ lazy_fixture("cw_agg"),
1003
+ "eastern_shapes_agg.png",
1004
+ eurasia(),
1005
+ dict(
1006
+ font=agg_font_20_orange,
1007
+ outline="blue",
1008
+ width=5.0,
1009
+ outline_opacity=100,
1010
+ minor_outline="blue",
1011
+ minor_width=5.0,
1012
+ minor_outline_opacity=200,
1013
+ ),
1014
+ ),
1015
+ ],
1016
+ )
1017
+ def test_shapes(new_test_image, cw, filename, area_def, specific_kwargs):
1018
+ """Test western/eastern shapes."""
1019
+ result_file = os.path.join(LOCAL_DIR, filename)
1020
+ grid_img = Image.open(result_file)
1021
+
1022
+ img = new_test_image("RGB", (1000, 560), filename)
1023
+
1024
+ cw.add_coastlines(img, area_def, resolution="l", level=2)
1025
+
1026
+ cw.add_grid(
1027
+ img,
1028
+ area_def,
1029
+ (10.0, 10.0),
1030
+ (5.0, 5.0),
1031
+ write_text=True,
1032
+ lon_placement="lbr",
1033
+ lat_placement="",
1034
+ **specific_kwargs,
1035
+ )
1036
+
1037
+ assert images_match(grid_img, img), "Writing of shapes failed"
1038
+
1039
+
1040
+ @pytest.mark.parametrize(
1041
+ "cw, filename, shape, area_def, specific_kwargs",
1042
+ [
1043
+ ( # lon=175 +/-40, lat=16..65 | Avoid Eurasia scratch with asymmetric area_extent
1044
+ lazy_fixture("cw_pil"),
1045
+ "no_h_scratch_pil.png",
1046
+ (888, 781),
1047
+ bering_straight(),
1048
+ dict(font=pil_font_20, fill="yellow", outline="orange", minor_outline="orange"),
1049
+ ),
1050
+ ( # lon=155+/-30 lat=-5..45 | No Eurasia problem (Eurasia has always lat > 0.0)
1051
+ lazy_fixture("cw_pil"),
1052
+ "no_v_scratch_pil.png",
1053
+ (888, 705),
1054
+ hawaii(),
1055
+ dict(font=pil_font_20, fill="yellow", outline="orange", minor_outline="orange"),
1056
+ ),
1057
+ ( # lon=175 +/-40, lat=16..65 | Avoid Eurasia scratch with asymmetric area_extent
1058
+ lazy_fixture("cw_agg"),
1059
+ "no_h_scratch_agg.png",
1060
+ (888, 781),
1061
+ bering_straight(),
1062
+ dict(
1063
+ font=agg_font_20_yellow,
1064
+ outline="green",
1065
+ width=5.0,
1066
+ outline_opacity=100,
1067
+ minor_outline="green",
1068
+ minor_width=5.0,
1069
+ minor_outline_opacity=200,
1070
+ ),
1071
+ ),
1072
+ ( # lon=155+/-30 lat=-5..45 | No Eurasia problem (Eurasia has always lat > 0.0)
1073
+ lazy_fixture("cw_agg"),
1074
+ "no_v_scratch_agg.png",
1075
+ (888, 705),
1076
+ hawaii(),
1077
+ dict(
1078
+ font=agg_font_20_yellow,
1079
+ outline="green",
1080
+ width=5.0,
1081
+ outline_opacity=100,
1082
+ minor_outline="green",
1083
+ minor_width=5.0,
1084
+ minor_outline_opacity=200,
1085
+ ),
1086
+ ),
1087
+ ],
1088
+ )
1089
+ @pytest.mark.usefixtures("cd_test_dir")
1090
+ def test_no_scratch(new_test_image, cw, filename, shape, area_def, specific_kwargs):
1091
+ """Test no scratches are visible."""
1092
+ result_file = os.path.join(LOCAL_DIR, filename)
1093
+ grid_img = Image.open(result_file)
1094
+
1095
+ img = new_test_image("RGB", shape, filename)
1096
+
1097
+ cw.add_coastlines(img, area_def, resolution="l", level=2)
1098
+
1099
+ cw.add_grid(
1100
+ img,
1101
+ area_def,
1102
+ (10.0, 10.0),
1103
+ (5.0, 5.0),
1104
+ write_text=True,
1105
+ lon_placement="bt",
1106
+ lat_placement="lr",
1107
+ **specific_kwargs,
1108
+ )
1109
+ cw.add_rivers(img, area_def, level=5, outline="blue")
1110
+ cw.add_borders(img, area_def, outline="red")
1111
+
1112
+ assert images_match(grid_img, img), "Writing of no_scratch failed"
1113
+
1114
+
1115
+ class TestContourWriterAGG(_ContourWriterTestBase):
1116
+ """Test AGG contour writer."""
354
1117
 
355
1118
  def test_europe_agg(self):
356
1119
  from pycoast import ContourWriterAGG
357
- euro_img = Image.open(os.path.join(os.path.dirname(__file__),
358
- 'contours_europe_agg.png'))
1120
+
1121
+ euro_img = Image.open(os.path.join(LOCAL_DIR, "contours_europe_agg.png"))
359
1122
  euro_data = np.array(euro_img)
360
1123
 
361
- img = Image.new('RGB', (640, 480))
362
- proj4_string = \
363
- '+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84'
1124
+ img = Image.new("RGB", (640, 480))
1125
+ proj4_string = "+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84"
364
1126
  area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
365
1127
  area_def = (proj4_string, area_extent)
366
1128
  cw = ContourWriterAGG(gshhs_root_dir)
367
- cw.add_coastlines(img, area_def, resolution='l', level=4)
368
- cw.add_rivers(img, area_def, level=5, outline='blue', width=0.5,
369
- outline_opacity=127)
370
- cw.add_borders(img, area_def, outline=(255, 0, 0),
371
- width=3, outline_opacity=32)
1129
+ cw.add_coastlines(img, area_def, resolution="l", level=4)
1130
+ cw.add_rivers(img, area_def, level=5, outline="blue", width=0.5, outline_opacity=127)
1131
+ cw.add_borders(img, area_def, outline=(255, 0, 0), width=3, outline_opacity=32)
372
1132
  res = np.array(img)
373
- self.assertTrue(fft_metric(euro_data, res),
374
- 'Writing of contours failed for AGG')
1133
+ assert fft_metric(euro_data, res), "Writing of contours failed for AGG"
375
1134
 
376
1135
  def test_europe_agg_file(self):
377
1136
  from pycoast import ContourWriterAGG
378
- euro_img = Image.open(os.path.join(os.path.dirname(__file__),
379
- 'contours_europe_agg.png'))
1137
+
1138
+ euro_img = Image.open(os.path.join(LOCAL_DIR, "contours_europe_agg.png"))
380
1139
  euro_data = np.array(euro_img)
381
1140
 
382
- proj4_string = \
383
- '+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84'
1141
+ proj4_string = "+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84"
384
1142
  area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
385
1143
  area_def = (proj4_string, area_extent)
386
1144
  cw = ContourWriterAGG(gshhs_root_dir)
387
- cw.add_coastlines_to_file(test_file, area_def, resolution='l', level=4)
388
- cw.add_rivers_to_file(test_file, area_def, level=5, outline='blue',
389
- width=0.5, outline_opacity=127)
390
- cw.add_borders_to_file(test_file, area_def, outline=(255, 0, 0),
391
- width=3, outline_opacity=32)
1145
+ cw.add_coastlines_to_file(test_filename, area_def, resolution="l", level=4)
1146
+ cw.add_rivers_to_file(test_filename, area_def, level=5, outline="blue", width=0.5, outline_opacity=127)
1147
+ cw.add_borders_to_file(test_filename, area_def, outline=(255, 0, 0), width=3, outline_opacity=32)
392
1148
 
393
- img = Image.open(test_file)
1149
+ img = Image.open(test_filename)
394
1150
  res = np.array(img)
395
- self.assertTrue(fft_metric(euro_data, res),
396
- 'Writing of contours failed for AGG')
1151
+ assert fft_metric(euro_data, res), "Writing of contours failed for AGG"
397
1152
 
398
1153
  def test_geos_agg(self):
399
1154
  from pycoast import ContourWriterAGG
400
- geos_img = Image.open(os.path.join(os.path.dirname(__file__),
401
- 'contours_geos_agg.png'))
1155
+
1156
+ geos_img = Image.open(os.path.join(LOCAL_DIR, "contours_geos_agg.png"))
402
1157
  geos_data = np.array(geos_img)
403
1158
 
404
- img = Image.new('RGB', (425, 425))
405
- proj4_string = \
406
- '+proj=geos +lon_0=0.0 +a=6378169.00 +b=6356583.80 +h=35785831.0'
407
- area_extent = (-5570248.4773392612, -5567248.074173444,
408
- 5567248.074173444, 5570248.4773392612)
1159
+ img = Image.new("RGB", (425, 425))
1160
+ proj4_string = "+proj=geos +lon_0=0.0 +a=6378169.00 +b=6356583.80 +h=35785831.0"
1161
+ area_extent = (
1162
+ -5570248.4773392612,
1163
+ -5567248.074173444,
1164
+ 5567248.074173444,
1165
+ 5570248.4773392612,
1166
+ )
409
1167
  # area_def = (proj4_string, area_extent)
410
1168
  cw = ContourWriterAGG(gshhs_root_dir)
411
- cw.add_coastlines(img, (proj4_string, area_extent),
412
- resolution='l', width=0.5)
1169
+ cw.add_coastlines(img, (proj4_string, area_extent), resolution="l", width=0.5)
413
1170
  res = np.array(img)
414
- self.assertTrue(fft_metric(geos_data, res),
415
- 'Writing of geos contours failed for AGG')
1171
+ assert fft_metric(geos_data, res), "Writing of geos contours failed for AGG"
416
1172
 
417
1173
  def test_grid_agg(self):
418
1174
  from pycoast import ContourWriterAGG
419
- grid_img = Image.open(os.path.join(os.path.dirname(__file__),
420
- 'grid_europe_agg.png'))
1175
+
1176
+ grid_img = Image.open(os.path.join(LOCAL_DIR, "grid_europe_agg.png"))
421
1177
  grid_data = np.array(grid_img)
422
1178
 
423
- img = Image.new('RGB', (640, 480))
424
- proj4_string = \
425
- '+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84'
1179
+ img = Image.new("RGB", (640, 480))
1180
+ proj4_string = "+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84"
426
1181
  area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
427
1182
  area_def = (proj4_string, area_extent)
428
1183
 
429
1184
  cw = ContourWriterAGG(gshhs_root_dir)
430
1185
 
431
- cw.add_coastlines(img, area_def, resolution='l', level=4)
432
- cw.add_grid(img, area_def, (10.0, 10.0), (2.0, 2.0), write_text=False,
433
- outline='blue', outline_opacity=255, width=1.0,
434
- minor_outline='white', minor_outline_opacity=255,
435
- minor_width=0.5, minor_is_tick=False)
1186
+ cw.add_coastlines(img, area_def, resolution="l", level=4)
1187
+ cw.add_grid(
1188
+ img,
1189
+ area_def,
1190
+ (10.0, 10.0),
1191
+ (2.0, 2.0),
1192
+ write_text=False,
1193
+ outline="blue",
1194
+ outline_opacity=255,
1195
+ width=1.0,
1196
+ minor_outline="white",
1197
+ minor_outline_opacity=255,
1198
+ minor_width=0.5,
1199
+ minor_is_tick=False,
1200
+ )
436
1201
 
437
1202
  res = np.array(img)
438
- self.assertTrue(
439
- fft_metric(grid_data, res), 'Writing of grid failed for AGG')
1203
+ assert fft_metric(grid_data, res), "Writing of grid failed for AGG"
440
1204
 
441
1205
  def test_grid_agg_txt(self):
442
1206
  from pycoast import ContourWriterAGG
443
- import aggdraw
444
- grid_img = Image.open(os.path.join(os.path.dirname(__file__),
445
- 'grid_europe_agg_txt.png'))
446
- grid_data = np.array(grid_img)
447
1207
 
448
- img = Image.new('RGB', (640, 480))
449
- proj4_string = \
450
- '+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84'
1208
+ grid_img = Image.open(os.path.join(LOCAL_DIR, "grid_europe_agg_txt.png"))
1209
+
1210
+ img = Image.new("RGB", (640, 480))
1211
+ proj4_string = "+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84"
451
1212
  area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
452
1213
  area_def = (proj4_string, area_extent)
453
1214
 
454
1215
  cw = ContourWriterAGG(gshhs_root_dir)
455
1216
 
456
- cw.add_coastlines(img, area_def, resolution='l', level=4)
457
- font = aggdraw.Font('blue', os.path.join(os.path.dirname(__file__),
458
- 'test_data',
459
- 'DejaVuSerif.ttf'), size=16,
460
- opacity=200)
461
- cw.add_grid(img, area_def, (10.0, 10.0), (2.0, 2.0), font=font,
462
- outline='blue', outline_opacity=255, width=1.0,
463
- minor_outline='white', minor_outline_opacity=255,
464
- minor_width=0.5, minor_is_tick=False, write_text=False)
465
-
466
- res = np.array(img)
467
- self.assertTrue(
468
- fft_metric(grid_data, res), 'Writing of grid failed for AGG')
1217
+ cw.add_coastlines(img, area_def, resolution="l", level=4)
1218
+ font = aggdraw.Font(
1219
+ "blue",
1220
+ font_path,
1221
+ size=16,
1222
+ opacity=200,
1223
+ )
1224
+ cw.add_grid(
1225
+ img,
1226
+ area_def,
1227
+ (10.0, 10.0),
1228
+ (2.0, 2.0),
1229
+ font=font,
1230
+ outline="blue",
1231
+ outline_opacity=255,
1232
+ width=1.0,
1233
+ minor_outline="white",
1234
+ minor_outline_opacity=255,
1235
+ minor_width=0.5,
1236
+ minor_is_tick=False,
1237
+ )
1238
+
1239
+ assert images_match(grid_img, img), "Writing of grid failed for AGG"
469
1240
 
470
1241
  def test_grid_geos_agg(self):
471
1242
  from pycoast import ContourWriterAGG
472
- geos_img = Image.open(os.path.join(os.path.dirname(__file__),
473
- 'grid_geos_agg.png'))
1243
+
1244
+ geos_img = Image.open(os.path.join(LOCAL_DIR, "grid_geos_agg.png"))
474
1245
  geos_data = np.array(geos_img)
475
- img = Image.new('RGB', (425, 425))
476
- proj4_string = \
477
- '+proj=geos +lon_0=0.0 +a=6378169.00 +b=6356583.80 +h=35785831.0'
478
- area_extent = (-5570248.4773392612, -5567248.074173444,
479
- 5567248.074173444, 5570248.4773392612)
1246
+ img = Image.new("RGB", (425, 425))
1247
+ proj4_string = "+proj=geos +lon_0=0.0 +a=6378169.00 +b=6356583.80 +h=35785831.0"
1248
+ area_extent = (
1249
+ -5570248.4773392612,
1250
+ -5567248.074173444,
1251
+ 5567248.074173444,
1252
+ 5570248.4773392612,
1253
+ )
480
1254
  area_def = (proj4_string, area_extent)
481
1255
  cw = ContourWriterAGG(gshhs_root_dir)
482
- cw.add_coastlines(img, area_def, resolution='l')
483
- cw.add_grid(img, area_def, (10.0, 10.0), (2.0, 2.0),
484
- fill='blue', outline='blue', minor_outline='blue',
485
- write_text=False)
1256
+ cw.add_coastlines(img, area_def, resolution="l")
1257
+ cw.add_grid(
1258
+ img,
1259
+ area_def,
1260
+ (10.0, 10.0),
1261
+ (2.0, 2.0),
1262
+ fill="blue",
1263
+ outline="blue",
1264
+ minor_outline="blue",
1265
+ write_text=False,
1266
+ )
486
1267
 
487
1268
  res = np.array(img)
488
- self.assertTrue(
489
- fft_metric(geos_data, res), 'Writing of geos contours failed')
1269
+ assert fft_metric(geos_data, res), "Writing of geos contours failed"
490
1270
 
491
1271
  def test_grid_agg_file(self):
492
1272
  from pycoast import ContourWriterAGG
493
- grid_img = Image.open(os.path.join(os.path.dirname(__file__),
494
- 'grid_europe_agg.png'))
1273
+
1274
+ grid_img = Image.open(os.path.join(LOCAL_DIR, "grid_europe_agg.png"))
495
1275
  grid_data = np.array(grid_img)
496
1276
 
497
- proj4_string = \
498
- '+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84'
1277
+ proj4_string = "+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84"
499
1278
  area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
500
1279
  area_def = (proj4_string, area_extent)
501
1280
 
502
1281
  cw = ContourWriterAGG(gshhs_root_dir)
503
1282
 
504
- cw.add_coastlines_to_file(grid_file, area_def, resolution='l', level=4)
505
- cw.add_grid_to_file(grid_file, area_def, (10.0, 10.0), (2.0, 2.0),
506
- write_text=False, outline='blue',
507
- outline_opacity=255, width=1.0,
508
- minor_outline='white', minor_outline_opacity=255,
509
- minor_width=0.5, minor_is_tick=False)
510
- img = Image.open(grid_file)
1283
+ cw.add_coastlines_to_file(grid_filename, area_def, resolution="l", level=4)
1284
+ cw.add_grid_to_file(
1285
+ grid_filename,
1286
+ area_def,
1287
+ (10.0, 10.0),
1288
+ (2.0, 2.0),
1289
+ write_text=False,
1290
+ outline="blue",
1291
+ outline_opacity=255,
1292
+ width=1.0,
1293
+ minor_outline="white",
1294
+ minor_outline_opacity=255,
1295
+ minor_width=0.5,
1296
+ minor_is_tick=False,
1297
+ )
1298
+ img = Image.open(grid_filename)
511
1299
  res = np.array(img)
512
- self.assertTrue(
513
- fft_metric(grid_data, res), 'Writing of grid failed for AGG')
1300
+ assert fft_metric(grid_data, res), "Writing of grid failed for AGG"
514
1301
 
515
1302
  def test_grid_nh_agg(self):
516
1303
  from pycoast import ContourWriterAGG
517
- import aggdraw
518
- grid_img = Image.open(os.path.join(os.path.dirname(__file__),
519
- 'grid_nh_agg.png'))
520
- grid_data = np.array(grid_img)
521
- img = Image.new('RGB', (425, 425))
522
- proj4_string = '+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m'
523
- area_extent = (-5326849.0625, -5326849.0625,
524
- 5326849.0625, 5326849.0625)
1304
+
1305
+ grid_img = Image.open(os.path.join(LOCAL_DIR, "grid_nh_agg.png"))
1306
+ img = Image.new("RGB", (425, 425))
1307
+ proj4_string = "+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m"
1308
+ area_extent = (-5326849.0625, -5326849.0625, 5326849.0625, 5326849.0625)
525
1309
  area_def = (proj4_string, area_extent)
526
1310
 
527
1311
  cw = ContourWriterAGG(gshhs_root_dir)
528
1312
 
529
- cw.add_coastlines(img, area_def, resolution='l', level=4)
530
- font = aggdraw.Font('blue', os.path.join(os.path.dirname(__file__),
531
- 'test_data',
532
- 'DejaVuSerif.ttf'), size=10)
533
- cw.add_grid(img, area_def, (10.0, 10.0), (2.0, 2.0),
534
- font=font, fill='blue', write_text=False,
535
- outline='blue', minor_outline='blue',
536
- lon_placement='tblr', lat_placement='')
537
-
538
- res = np.array(img)
1313
+ cw.add_coastlines(img, area_def, resolution="l", level=4)
1314
+ font = aggdraw.Font(
1315
+ "blue",
1316
+ font_path,
1317
+ size=10,
1318
+ )
1319
+ cw.add_grid(
1320
+ img,
1321
+ area_def,
1322
+ (10.0, 10.0),
1323
+ (2.0, 2.0),
1324
+ font=font,
1325
+ fill="blue",
1326
+ outline="blue",
1327
+ minor_outline="blue",
1328
+ lon_placement="tblr",
1329
+ lat_placement="",
1330
+ )
539
1331
 
540
1332
  # NOTE: Experience inconsistency in ttf font writing between systems.
541
1333
  # Still trying to figure out why this test sometimes fails to write
542
1334
  # correct font markings.
543
- self.assertTrue(fft_metric(grid_data, res),
544
- 'Writing of nh grid failed for AGG')
1335
+ assert images_match(grid_img, img), "Writing of nh grid failed for AGG"
545
1336
 
546
1337
  def test_add_polygon_agg(self):
547
1338
  from pycoast import ContourWriterAGG
548
- grid_img = Image.open(os.path.join(os.path.dirname(__file__),
549
- 'nh_polygons_agg.png'))
1339
+
1340
+ grid_img = Image.open(os.path.join(LOCAL_DIR, "nh_polygons_agg.png"))
550
1341
  grid_data = np.array(grid_img)
551
1342
 
552
- img = Image.new('RGB', (425, 425))
553
- proj4_string = '+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m'
554
- area_extent = (-5326849.0625, -5326849.0625,
555
- 5326849.0625, 5326849.0625)
1343
+ img = Image.new("RGB", (425, 425))
1344
+ proj4_string = "+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m"
1345
+ area_extent = (-5326849.0625, -5326849.0625, 5326849.0625, 5326849.0625)
556
1346
  area_def = (proj4_string, area_extent)
557
1347
 
558
1348
  cw = ContourWriterAGG(gshhs_root_dir)
559
1349
 
560
1350
  polygons = {
561
- 'REYKJAVIK_ATC_A': ((-20.0, 73.0), (0.0, 73.0), (0.0, 61.0),
562
- (-30.0, 61.0), (-39.0, 63.5), (-20, 70)),
563
- 'REYKJAVIK_ATC_B': ((-39, 63.5), (-55 + 4 / 6.0, 63.5),
564
- (-57 + 45 / 60.0, 65), (-76, 76),
565
- (-75, 78), (-60, 82), (0, 90), (30, 82),
566
- (0, 82), (0, 73), (-20, 73), (-20, 70)),
567
- 'REYKJAVIK_ATC': ((0.0, 73.0), (0.0, 61.0), (-30.0, 61.0),
568
- (-39, 63.5), (-55 + 4 / 6.0, 63.5),
569
- (-57 + 45 / 60.0, 65), (-76, 76), (-75, 78),
570
- (-60, 82), (0, 90), (30, 82), (0, 82)),
571
- 'ICELAND_BOX': ((-25, 62.5), (-25, 67), (-13, 67), (-13, 62.5))
1351
+ "REYKJAVIK_ATC_A": (
1352
+ (-20.0, 73.0),
1353
+ (0.0, 73.0),
1354
+ (0.0, 61.0),
1355
+ (-30.0, 61.0),
1356
+ (-39.0, 63.5),
1357
+ (-20, 70),
1358
+ ),
1359
+ "REYKJAVIK_ATC_B": (
1360
+ (-39, 63.5),
1361
+ (-55 + 4 / 6.0, 63.5),
1362
+ (-57 + 45 / 60.0, 65),
1363
+ (-76, 76),
1364
+ (-75, 78),
1365
+ (-60, 82),
1366
+ (0, 90),
1367
+ (30, 82),
1368
+ (0, 82),
1369
+ (0, 73),
1370
+ (-20, 73),
1371
+ (-20, 70),
1372
+ ),
1373
+ "REYKJAVIK_ATC": (
1374
+ (0.0, 73.0),
1375
+ (0.0, 61.0),
1376
+ (-30.0, 61.0),
1377
+ (-39, 63.5),
1378
+ (-55 + 4 / 6.0, 63.5),
1379
+ (-57 + 45 / 60.0, 65),
1380
+ (-76, 76),
1381
+ (-75, 78),
1382
+ (-60, 82),
1383
+ (0, 90),
1384
+ (30, 82),
1385
+ (0, 82),
1386
+ ),
1387
+ "ICELAND_BOX": ((-25, 62.5), (-25, 67), (-13, 67), (-13, 62.5)),
572
1388
  }
573
1389
 
574
- cw.add_polygon(img, area_def, polygons['REYKJAVIK_ATC'],
575
- outline='red', width=2)
576
- cw.add_polygon(img, area_def, polygons['ICELAND_BOX'],
577
- outline='green', fill='gray', width=2)
578
- cw.add_coastlines(img, area_def, resolution='l', level=4)
1390
+ cw.add_polygon(img, area_def, polygons["REYKJAVIK_ATC"], outline="red", width=2)
1391
+ cw.add_polygon(
1392
+ img,
1393
+ area_def,
1394
+ polygons["ICELAND_BOX"],
1395
+ outline="green",
1396
+ fill="gray",
1397
+ width=2,
1398
+ )
1399
+ cw.add_coastlines(img, area_def, resolution="l", level=4)
579
1400
 
580
1401
  res = np.array(img)
581
- self.assertTrue(fft_metric(grid_data, res),
582
- 'Writing of nh polygons failed')
1402
+ assert fft_metric(grid_data, res), "Writing of nh polygons failed"
1403
+
1404
+ def test_add_points_agg(self):
1405
+ from pyresample.geometry import AreaDefinition
1406
+
1407
+ from pycoast import ContourWriterAGG
1408
+
1409
+ grid_img = Image.open(os.path.join(LOCAL_DIR, "nh_points_agg.png"))
1410
+ grid_data = np.array(grid_img)
1411
+
1412
+ img = Image.new("RGB", (1024, 1024), (255, 255, 255))
1413
+ proj4_string = "+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m"
1414
+ area_extent = (-5326849.0625, -5326849.0625, 5326849.0625, 5326849.0625)
1415
+
1416
+ area_def = AreaDefinition("nh", "nh", "nh", proj4_string, 1024, 1024, area_extent)
1417
+
1418
+ cw = ContourWriterAGG(gshhs_root_dir)
1419
+ cw.add_coastlines(img, area_def, outline="black", resolution="l", level=4)
1420
+ cw.add_borders(img, area_def, outline="black", width=3, level=1, resolution="c")
1421
+
1422
+ points_list = [((2.3522, 48.8566), "Paris"), ((0.1278, 51.5074), "London")]
1423
+ cw.add_points(
1424
+ img,
1425
+ area_def,
1426
+ points_list=points_list,
1427
+ font_file=font_path,
1428
+ symbol="circle",
1429
+ ptsize=16,
1430
+ outline="black",
1431
+ width=3,
1432
+ fill="red",
1433
+ fill_opacity=128,
1434
+ box_outline="blue",
1435
+ box_linewidth=0.5,
1436
+ box_fill="yellow",
1437
+ box_opacity=200,
1438
+ )
1439
+
1440
+ res = np.array(img)
1441
+ assert fft_metric(grid_data, res), "Writing of nh points failed"
583
1442
 
584
1443
  def test_add_shapefile_shapes_agg(self):
585
1444
  from pycoast import ContourWriterAGG
586
- grid_img = Image.open(os.path.join(os.path.dirname(__file__),
587
- 'brazil_shapefiles_agg.png'))
1445
+
1446
+ grid_img = Image.open(os.path.join(LOCAL_DIR, "brazil_shapefiles_agg.png"))
588
1447
  grid_data = np.array(grid_img)
589
1448
 
590
- img = Image.new('RGB', (425, 425))
591
- proj4_string = \
592
- '+proj=merc +lon_0=-60 +lat_ts=-30.0 +a=6371228.0 +units=m'
1449
+ img = Image.new("RGB", (425, 425))
1450
+ proj4_string = "+proj=merc +lon_0=-60 +lat_ts=-30.0 +a=6371228.0 +units=m"
593
1451
  area_extent = (-2000000.0, -5000000.0, 5000000.0, 2000000.0)
594
1452
  area_def = (proj4_string, area_extent)
595
1453
 
596
1454
  cw = ContourWriterAGG(gshhs_root_dir)
597
1455
 
598
- cw.add_coastlines(img, area_def, resolution='l', level=4)
599
- cw.add_shapefile_shapes(img, area_def,
600
- os.path.join(
601
- os.path.dirname(__file__),
602
- 'test_data/shapes/Metareas.shp'),
603
- outline='red', width=2)
604
- cw.add_shapefile_shape(img, area_def,
605
- os.path.join(os.path.dirname(__file__),
606
- 'test_data/shapes/divisao_politica/BR_Regioes.shp'), 3,
607
- outline='blue')
608
- cw.add_shapefile_shape(img, area_def,
609
- os.path.join(os.path.dirname(__file__),
610
- 'test_data/shapes/divisao_politica/BR_Regioes.shp'), 4,
611
- outline='blue', fill='green')
1456
+ cw.add_coastlines(img, area_def, resolution="l", level=4)
1457
+ cw.add_shapefile_shapes(
1458
+ img,
1459
+ area_def,
1460
+ os.path.join(LOCAL_DIR, "test_data/shapes/Metareas.shp"),
1461
+ outline="red",
1462
+ width=2,
1463
+ )
1464
+ cw.add_shapefile_shape(
1465
+ img,
1466
+ area_def,
1467
+ os.path.join(
1468
+ LOCAL_DIR,
1469
+ "test_data/shapes/divisao_politica/BR_Regioes.shp",
1470
+ ),
1471
+ 3,
1472
+ outline="blue",
1473
+ )
1474
+ cw.add_shapefile_shape(
1475
+ img,
1476
+ area_def,
1477
+ os.path.join(
1478
+ LOCAL_DIR,
1479
+ "test_data/shapes/divisao_politica/BR_Regioes.shp",
1480
+ ),
1481
+ 4,
1482
+ outline="blue",
1483
+ fill="green",
1484
+ )
1485
+
1486
+ res = np.array(img)
1487
+ assert fft_metric(grid_data, res), "Writing of Brazil shapefiles failed"
1488
+
1489
+ @pytest.mark.usefixtures("cd_test_dir")
1490
+ def test_config_file_coasts_and_grid(self):
1491
+ from pyresample.geometry import AreaDefinition
1492
+
1493
+ from pycoast import ContourWriterAGG
1494
+
1495
+ overlay_config = os.path.join(LOCAL_DIR, "coasts_and_grid_agg.ini")
1496
+ grid_img = Image.open(os.path.join(LOCAL_DIR, "grid_nh_cfg_agg.png"))
1497
+ proj_dict = {
1498
+ "proj": "laea",
1499
+ "lat_0": 90.0,
1500
+ "lon_0": 0.0,
1501
+ "a": 6371228.0,
1502
+ "units": "m",
1503
+ }
1504
+ area_extent = (-5326849.0625, -5326849.0625, 5326849.0625, 5326849.0625)
1505
+ area_def = AreaDefinition("nh", "nh", "nh", proj_dict, 850, 850, area_extent)
1506
+
1507
+ cw = ContourWriterAGG(gshhs_root_dir)
1508
+ overlay = cw.add_overlay_from_config(overlay_config, area_def)
1509
+ img = Image.new("RGB", (850, 850), (255, 255, 255))
1510
+ img.paste(overlay, mask=overlay)
1511
+
1512
+ assert images_match(grid_img, img), "Writing of nh cfg grid failed"
1513
+
1514
+ @pytest.mark.usefixtures("cd_test_dir")
1515
+ def test_config_file_points_and_borders_agg(self):
1516
+ from pyresample.geometry import AreaDefinition
1517
+
1518
+ from pycoast import ContourWriterAGG
1519
+
1520
+ config_file = os.path.join(LOCAL_DIR, "nh_points_agg.ini")
1521
+
1522
+ grid_img = Image.open(os.path.join(LOCAL_DIR, "nh_points_agg.png"))
1523
+ grid_data = np.array(grid_img)
1524
+
1525
+ img = Image.new("RGB", (1024, 1024), (255, 255, 255))
1526
+
1527
+ proj4_string = "+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m"
1528
+ area_extent = (-5326849.0625, -5326849.0625, 5326849.0625, 5326849.0625)
1529
+
1530
+ area_def = AreaDefinition("nh", "nh", "nh", proj4_string, 1024, 1024, area_extent)
1531
+
1532
+ cw = ContourWriterAGG(gshhs_root_dir)
1533
+
1534
+ cw.add_overlay_from_config(config_file, area_def, img)
1535
+
1536
+ res = np.array(img)
1537
+ assert fft_metric(grid_data, res), "Add points with agg module from a config file failed"
1538
+
1539
+ def test_coastlines_convert_to_rgba_agg(self):
1540
+ from pycoast import ContourWriterAGG
1541
+
1542
+ proj4_string = "+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84"
1543
+ area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
1544
+ area_def = (proj4_string, area_extent)
1545
+
1546
+ cw = ContourWriterAGG(gshhs_root_dir)
1547
+ cw.add_coastlines_to_file(p_coasts_filename, area_def, resolution="l", level=4)
1548
+
1549
+ img = Image.open(p_coasts_filename)
1550
+ image_mode = img.mode
1551
+ img.close()
1552
+
1553
+ assert image_mode == "RGBA", "Conversion to RGBA failed."
1554
+
1555
+ def test_add_cities_agg(self):
1556
+ from pyresample.geometry import AreaDefinition
1557
+
1558
+ from pycoast import ContourWriterAGG
1559
+
1560
+ grid_img = Image.open(os.path.join(LOCAL_DIR, "nh_cities_agg.png"))
1561
+ grid_data = np.array(grid_img)
1562
+
1563
+ img = Image.new("RGB", (1024, 1024), (255, 255, 255))
1564
+ proj4_string = "+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m"
1565
+ area_extent = (-5326849.0625, -5326849.0625, 5326849.0625, 5326849.0625)
1566
+
1567
+ area_def = AreaDefinition("nh", "nh", "nh", proj4_string, 1024, 1024, area_extent)
1568
+
1569
+ cw = ContourWriterAGG(gshhs_root_dir)
1570
+ cw.add_coastlines(img, area_def, outline="black", resolution="l", level=4)
1571
+ cw.add_borders(img, area_def, outline="black", width=3, level=1, resolution="c")
1572
+
1573
+ cities_list = ["Zurich", "Oslo", "Reykjavik", "Fairbanks", "Toronto"]
1574
+ cw.add_cities(
1575
+ img,
1576
+ area_def,
1577
+ cities_list=cities_list,
1578
+ font_file=font_path,
1579
+ font_size=20,
1580
+ symbol="square",
1581
+ ptsize=16,
1582
+ outline="black",
1583
+ width=1,
1584
+ fill="blue",
1585
+ fill_opacity=128,
1586
+ box_outline="blue",
1587
+ box_linewidth=0.5,
1588
+ box_fill="yellow",
1589
+ box_opacity=200,
1590
+ )
1591
+
1592
+ res = np.array(img)
1593
+ assert fft_metric(grid_data, res), "Writing of nh cities_agg failed"
1594
+
1595
+ @pytest.mark.usefixtures("cd_test_dir")
1596
+ def test_add_cities_cfg_agg(self):
1597
+ from pyresample.geometry import AreaDefinition
1598
+
1599
+ from pycoast import ContourWriterAGG
1600
+
1601
+ config_file = os.path.join(LOCAL_DIR, "nh_cities_agg.ini")
1602
+
1603
+ grid_img = Image.open(os.path.join(LOCAL_DIR, "nh_cities_agg.png"))
1604
+ grid_data = np.array(grid_img)
1605
+
1606
+ img = Image.new("RGB", (1024, 1024), (255, 255, 255))
1607
+
1608
+ proj4_string = "+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m"
1609
+ area_extent = (-5326849.0625, -5326849.0625, 5326849.0625, 5326849.0625)
1610
+
1611
+ area_def = AreaDefinition("nh", "nh", "nh", proj4_string, 1024, 1024, area_extent)
1612
+
1613
+ cw = ContourWriterAGG(gshhs_root_dir)
1614
+
1615
+ cw.add_overlay_from_config(config_file, area_def, img)
1616
+
1617
+ res = np.array(img)
1618
+ assert fft_metric(grid_data, res), "Writing of nh cities_cfg_agg failed"
1619
+
1620
+ def test_add_cities_from_dict_agg(self):
1621
+ from pyresample.geometry import AreaDefinition
1622
+
1623
+ from pycoast import ContourWriterAGG
1624
+
1625
+ grid_img = Image.open(os.path.join(LOCAL_DIR, "nh_cities_from_dict_agg.png"))
1626
+ grid_data = np.array(grid_img)
1627
+
1628
+ img = Image.new("RGB", (1024, 1024), (255, 255, 255))
1629
+
1630
+ proj4_string = "+proj=stere +ellps=WGS84 +lat_0=51.5 +lon_0=-0.1"
1631
+ area_extent = (-2000000.0, -2000000.0, 2000000.0, 2000000.0)
1632
+
1633
+ area_def = AreaDefinition("nh", "nh", "nh", proj4_string, 1024, 1024, area_extent)
1634
+
1635
+ overlays = {}
1636
+ overlays["coasts"] = {"level": 4, "resolution": "l", "outline": "black"}
1637
+ overlays["borders"] = {"level": 1, "outline": "black", "resolution": "c"}
1638
+ cities_list1 = [
1639
+ "Berlin",
1640
+ "Paris",
1641
+ "London",
1642
+ "Dublin",
1643
+ "Madrid",
1644
+ "Reykjavik",
1645
+ "Oslo",
1646
+ "Rome",
1647
+ ]
1648
+ cities_list2 = ["Freiburg", "Montelimar", "Huesca", "Marseille"]
1649
+ cities_list3 = ["Belp", "Bad Schwalbach", "Edinburgh", "Hilversum"]
1650
+ cities_type1 = {
1651
+ "cities_list": cities_list1,
1652
+ "font": font_path,
1653
+ "font_size": 26,
1654
+ "symbol": "circle",
1655
+ "ptsize": 24,
1656
+ "outline": "black",
1657
+ "fill": "red",
1658
+ "box_outline": "black",
1659
+ "box_fill": "blue",
1660
+ "box_opacity": 50,
1661
+ "box_linewidth": 2.0,
1662
+ }
1663
+ cities_type2 = {
1664
+ "cities_list": cities_list2,
1665
+ "font": font_path,
1666
+ "font_size": 24,
1667
+ "symbol": "pentagon",
1668
+ "ptsize": 24,
1669
+ "outline": "red",
1670
+ "fill": "blue",
1671
+ "box_outline": "black",
1672
+ "box_fill": "yellow",
1673
+ "box_opacity": 50,
1674
+ "box_linewidth": 2.0,
1675
+ }
1676
+ cities_type3 = {
1677
+ "cities_list": cities_list3,
1678
+ "font": font_path,
1679
+ "font_size": 22,
1680
+ "symbol": "star5",
1681
+ "ptsize": 35,
1682
+ "outline": "green",
1683
+ "fill": "yellow",
1684
+ "box_outline": "black",
1685
+ "box_fill": "red",
1686
+ "box_opacity": 50,
1687
+ "box_linewidth": 2.0,
1688
+ }
1689
+
1690
+ overlays["cities"] = [cities_type1, cities_type2, cities_type3]
1691
+
1692
+ cw = ContourWriterAGG(gshhs_root_dir)
1693
+
1694
+ img = cw.add_overlay_from_dict(overlays, area_def, background=img)
1695
+
1696
+ res = np.array(img)
1697
+ assert fft_metric(grid_data, res), "Writing of nh_cities_from_dict_agg failed"
1698
+
1699
+ def test_add_shapefiles_from_dict_agg(self):
1700
+ from pyresample.geometry import AreaDefinition
1701
+
1702
+ from pycoast import ContourWriterAGG
1703
+
1704
+ grid_img = Image.open(os.path.join(LOCAL_DIR, "two_shapefiles_agg.png"))
1705
+ grid_data = np.array(grid_img)
1706
+
1707
+ img = Image.new("RGB", (425, 425))
1708
+ proj4_string = "+proj=merc +lon_0=-60 +lat_ts=-30.0 +a=6371228.0 +units=m"
1709
+ area_extent = (-2000000.0, -5000000.0, 5000000.0, 2000000.0)
1710
+ area_def = AreaDefinition("nh", "nh", "nh", proj4_string, 425, 425, area_extent)
1711
+
1712
+ cw = ContourWriterAGG(gshhs_root_dir)
1713
+
1714
+ overlays = {}
1715
+ overlays["coasts"] = {"level": 4, "resolution": "l"}
1716
+ overlays["shapefiles"] = [
1717
+ {
1718
+ "filename": os.path.join(LOCAL_DIR, "test_data/shapes/Metareas.shp"),
1719
+ "outline": "magenta",
1720
+ "width": 2.5,
1721
+ },
1722
+ {
1723
+ "filename": os.path.join(
1724
+ LOCAL_DIR,
1725
+ "test_data/shapes/divisao_politica/BR_Regioes.shp",
1726
+ ),
1727
+ "outline": "red",
1728
+ "fill": "yellow",
1729
+ "fill_opacity": 50,
1730
+ },
1731
+ ]
1732
+
1733
+ img = cw.add_overlay_from_dict(overlays, area_def, background=img)
1734
+
1735
+ res = np.array(img)
1736
+ assert fft_metric(grid_data, res), "Writing two shapefiles from dict agg failed"
1737
+
1738
+ @pytest.mark.usefixtures("cd_test_dir")
1739
+ def test_add_one_shapefile_from_cfg_agg(self):
1740
+ from pyresample.geometry import AreaDefinition
1741
+
1742
+ from pycoast import ContourWriterAGG
1743
+
1744
+ config_file = os.path.join(LOCAL_DIR, "nh_one_shapefile.ini")
1745
+
1746
+ grid_img = Image.open(os.path.join(LOCAL_DIR, "one_shapefile_from_cfg_agg.png"))
1747
+ grid_data = np.array(grid_img)
1748
+
1749
+ img = Image.new("RGB", (425, 425))
1750
+ proj4_string = "+proj=merc +lon_0=-60 +lat_ts=-30.0 +a=6371228.0 +units=m"
1751
+ area_extent = (-2000000.0, -5000000.0, 5000000.0, 2000000.0)
1752
+ area_def = AreaDefinition("nh", "nh", "nh", proj4_string, 425, 425, area_extent)
1753
+
1754
+ cw = ContourWriterAGG(gshhs_root_dir)
1755
+
1756
+ cw.add_overlay_from_config(config_file, area_def, img)
612
1757
 
613
1758
  res = np.array(img)
614
- self.assertTrue(
615
- fft_metric(grid_data, res), 'Writing of Brazil shapefiles failed')
1759
+ assert fft_metric(grid_data, res), "Writing one shapefile from cfg agg failed"
1760
+
1761
+ def test_add_grid_from_dict_agg(self):
1762
+ from pyresample.geometry import AreaDefinition
1763
+
1764
+ from pycoast import ContourWriterAGG
1765
+
1766
+ grid_img = Image.open(os.path.join(LOCAL_DIR, "grid_from_dict_agg.png"))
1767
+
1768
+ img = Image.new("RGB", (800, 800))
1769
+ proj4_string = "+proj=stere +ellps=WGS84 +lon_0=-4.532 +lat_0=54.228"
1770
+ area_extent = (-600000.0, -600000.0, 600000.0, 600000.0)
1771
+
1772
+ area_def = AreaDefinition("nh", "nh", "nh", proj4_string, 800, 800, area_extent)
1773
+
1774
+ cw = ContourWriterAGG(gshhs_root_dir)
1775
+
1776
+ font = aggdraw.Font(
1777
+ "yellow",
1778
+ font_path,
1779
+ opacity=255,
1780
+ size=40,
1781
+ )
1782
+
1783
+ overlays = {}
1784
+ overlays["coasts"] = {"width": 3.0, "level": 4, "resolution": "l"}
1785
+ overlays["grid"] = {
1786
+ "major_lonlat": (5, 5),
1787
+ "minor_lonlat": (1, 1),
1788
+ "outline": (255, 0, 0),
1789
+ "outline_opacity": 127,
1790
+ "minor_outline": (0, 0, 255),
1791
+ "minor_outline_opacity": 127,
1792
+ "width": 10.5,
1793
+ "minor_width": 5.5,
1794
+ "minor_is_tick": False,
1795
+ "write_text": True,
1796
+ "lat_placement": "lr",
1797
+ "lon_placement": "b",
1798
+ "font": font,
1799
+ "fill": "red",
1800
+ }
1801
+ # Fill has no agg effect! Agg Font can be None if and only if write_text is set to False
1802
+
1803
+ img = cw.add_overlay_from_dict(overlays, area_def, background=img)
1804
+
1805
+ assert images_match(grid_img, img), "Writing grid from dict agg failed"
1806
+
1807
+ def test_lonlat_pm_change(self):
1808
+ """Test that a longlat projection with a non-0 prime meridian is handled correctly."""
1809
+ from pyresample.geometry import AreaDefinition
1810
+
1811
+ from pycoast import ContourWriterAGG
1812
+
1813
+ area_def1 = AreaDefinition("", "", "", "+proj=longlat", 640, 480, (-55.0, -35.0, -5.0, 3.0))
1814
+ area_def2 = AreaDefinition("", "", "", "+proj=longlat +pm=180", 640, 480, (-55.0, -35.0, -5.0, 3.0))
1815
+ area_def3 = AreaDefinition("", "", "", "+proj=longlat +pm=180", 640, 480, (125.0, -35.0, 175.0, 3.0))
1816
+ img1 = Image.new("RGBA", (640, 480))
1817
+ img2 = Image.new("RGBA", (640, 480))
1818
+ img3 = Image.new("RGBA", (640, 480))
1819
+
1820
+ cw = ContourWriterAGG(gshhs_root_dir)
1821
+ cw.add_coastlines(img1, area_def1, resolution="l", level=4)
1822
+ cw.add_coastlines(img2, area_def2, resolution="l", level=4)
1823
+ cw.add_coastlines(img3, area_def3, resolution="l", level=4)
1824
+
1825
+ # with a prime meridian shift and extents shift, the images should be the same
1826
+ np.testing.assert_allclose(np.array(img1), np.array(img3))
1827
+ # with only a prime meridian shift and same extents, the images should be completely different
1828
+ _assert_all_notclose(np.array(img1), np.array(img2))
1829
+ # with only an extents change, the images should be completely different
1830
+ _assert_all_notclose(np.array(img2), np.array(img3))
1831
+
1832
+
1833
+ def _assert_all_notclose(*args, **kwargs):
1834
+ with pytest.raises(AssertionError):
1835
+ np.testing.assert_allclose(*args, **kwargs)
1836
+
1837
+
1838
+ class FakeAreaDef:
1839
+ """A fake area definition object."""
1840
+
1841
+ def __init__(self, proj4_string, area_extent, x_size, y_size):
1842
+ self.proj_str = self.proj_dict = proj4_string
1843
+ self.crs = CRS.from_user_input(proj4_string)
1844
+ self.area_extent = area_extent
1845
+ self.width = x_size
1846
+ self.height = y_size
1847
+ self.area_id = "fakearea"
1848
+
616
1849
 
1850
+ class TestFromConfig:
1851
+ """Test burning overlays from a config file."""
617
1852
 
618
- def suite():
619
- loader = unittest.TestLoader()
620
- mysuite = unittest.TestSuite()
621
- mysuite.addTest(loader.loadTestsFromTestCase(TestPIL))
622
- # mysuite.addTest(loader.loadTestsFromTestCase(TestPILAGG))
1853
+ def test_foreground(self):
1854
+ """Test generating a transparent foreground."""
1855
+ from pycoast import ContourWriterPIL
623
1856
 
624
- return mysuite
1857
+ euro_img = Image.open(os.path.join(LOCAL_DIR, "contours_europe_alpha.png"))
1858
+ euro_data = np.array(euro_img)
1859
+
1860
+ # img = Image.new('RGB', (640, 480))
1861
+ proj4_string = "+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84"
1862
+ area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
1863
+ area_def = FakeAreaDef(proj4_string, area_extent, 640, 480)
1864
+ cw = ContourWriterPIL(gshhs_root_dir)
1865
+ config_file = os.path.join(LOCAL_DIR, "test_data", "test_config.ini")
1866
+ img = cw.add_overlay_from_config(config_file, area_def)
1867
+
1868
+ res = np.array(img)
1869
+ assert fft_metric(euro_data, res), "Writing of contours failed"
1870
+
1871
+ overlays = {
1872
+ "coasts": {"level": [1, 2, 3, 4], "resolution": "l"},
1873
+ "borders": {"outline": (255, 0, 0), "resolution": "c"},
1874
+ "rivers": {"outline": "blue", "resolution": "c", "level": 5},
1875
+ }
1876
+
1877
+ img = cw.add_overlay_from_dict(overlays, area_def)
1878
+ res = np.array(img)
1879
+ assert fft_metric(euro_data, res), "Writing of contours failed"
1880
+
1881
+ def test_cache_generation_reuse(self, tmpdir):
1882
+ """Test generating a transparent foreground and cache it."""
1883
+ from pycoast import ContourWriterPIL
1884
+
1885
+ euro_img = Image.open(os.path.join(LOCAL_DIR, "contours_europe_alpha.png"))
1886
+ euro_data = np.array(euro_img)
1887
+
1888
+ proj4_string = "+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84"
1889
+ area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
1890
+ area_def = FakeAreaDef(proj4_string, area_extent, 640, 480)
1891
+ cw = ContourWriterPIL(gshhs_root_dir)
1892
+
1893
+ overlays = {
1894
+ "cache": {"file": os.path.join(tmpdir, "pycoast_cache")},
1895
+ "coasts": {"level": 4, "resolution": "l"},
1896
+ "borders": {"outline": (255, 0, 0), "resolution": "c"},
1897
+ "rivers": {"outline": "blue", "resolution": "c", "level": 5},
1898
+ }
1899
+
1900
+ # Create the original cache file
1901
+ img = cw.add_overlay_from_dict(overlays, area_def)
1902
+ res = np.array(img)
1903
+ img.close()
1904
+ cache_glob = glob(os.path.join(tmpdir, "pycoast_cache_*.png"))
1905
+ assert len(cache_glob) == 1
1906
+ cache_filename = cache_glob[0]
1907
+ assert fft_metric(euro_data, res), "Writing of contours failed"
1908
+ assert os.path.isfile(cache_filename)
1909
+ mtime = os.path.getmtime(cache_filename)
1910
+
1911
+ # Reuse the generated cache file
1912
+ img = cw.add_overlay_from_dict(overlays, area_def)
1913
+ res = np.array(img)
1914
+ img.close()
1915
+ assert fft_metric(euro_data, res), "Writing of contours failed"
1916
+ assert os.path.isfile(cache_filename)
1917
+ assert os.path.getmtime(cache_filename) == mtime
1918
+
1919
+ # Regenerate cache file
1920
+ current_time = time.time()
1921
+ fg_img = cw.add_overlay_from_dict(overlays, area_def, current_time)
1922
+ fg_img.close()
1923
+ mtime = os.path.getmtime(cache_filename)
1924
+ assert mtime > current_time
1925
+ assert fft_metric(euro_data, res), "Writing of contours failed"
1926
+
1927
+ fg_img = cw.add_overlay_from_dict(overlays, area_def, current_time)
1928
+ fg_img.close()
1929
+ assert os.path.getmtime(cache_filename) == mtime
1930
+ assert fft_metric(euro_data, res), "Writing of contours failed"
1931
+ overlays["cache"]["regenerate"] = True
1932
+ fg_img = cw.add_overlay_from_dict(overlays, area_def)
1933
+ fg_img.close()
1934
+
1935
+ assert os.path.getmtime(cache_filename) != mtime
1936
+ assert fft_metric(euro_data, res), "Writing of contours failed"
1937
+
1938
+ overlays.pop("cache")
1939
+ overlays["grid"] = {
1940
+ "outline": (255, 255, 255),
1941
+ "outline_opacity": 175,
1942
+ "minor_outline": (200, 200, 200),
1943
+ "minor_outline_opacity": 127,
1944
+ "width": 1.0,
1945
+ "minor_width": 0.5,
1946
+ "minor_is_tick": True,
1947
+ "write_text": True,
1948
+ "lat_placement": "lr",
1949
+ "lon_placement": "b",
1950
+ }
1951
+ fg_img = cw.add_overlay_from_dict(overlays, area_def)
1952
+ fg_img.close()
1953
+ os.remove(cache_filename)
1954
+
1955
+ def test_caching_with_param_changes(self, tmpdir):
1956
+ """Testing caching when changing parameters."""
1957
+ from pycoast import ContourWriterPIL
1958
+
1959
+ proj4_string = "+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84"
1960
+ area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
1961
+ area_def = FakeAreaDef(proj4_string, area_extent, 640, 480)
1962
+ cw = ContourWriterPIL(gshhs_root_dir)
1963
+
1964
+ font = ImageFont.truetype(font_path)
1965
+ overlays = {
1966
+ "cache": {"file": os.path.join(tmpdir, "pycoast_cache")},
1967
+ "grid": {"font": font},
1968
+ }
1969
+
1970
+ # Create the original cache file
1971
+ fg_img = cw.add_overlay_from_dict(overlays, area_def)
1972
+ fg_img.close()
1973
+ cache_glob = glob(os.path.join(tmpdir, "pycoast_cache_*.png"))
1974
+ assert len(cache_glob) == 1
1975
+ cache_filename = cache_glob[0]
1976
+ assert os.path.isfile(cache_filename)
1977
+ mtime = os.path.getmtime(cache_filename)
1978
+
1979
+ # Reuse the generated cache file
1980
+ fg_img = cw.add_overlay_from_dict(overlays, area_def)
1981
+ fg_img.close()
1982
+ cache_glob = glob(os.path.join(tmpdir, "pycoast_cache_*.png"))
1983
+ assert len(cache_glob) == 1
1984
+ assert os.path.isfile(cache_filename)
1985
+ assert os.path.getmtime(cache_filename) == mtime
1986
+
1987
+ # Remove the font option, should produce the same result
1988
+ # font is not considered when caching
1989
+ del overlays["grid"]["font"]
1990
+ fg_img = cw.add_overlay_from_dict(overlays, area_def)
1991
+ fg_img.close()
1992
+ cache_glob = glob(os.path.join(tmpdir, "pycoast_cache_*.png"))
1993
+ assert len(cache_glob) == 1
1994
+ assert os.path.isfile(cache_filename)
1995
+ assert os.path.getmtime(cache_filename) == mtime
1996
+
1997
+ # Changing a parameter should create a new cache file
1998
+ overlays = {
1999
+ "cache": {"file": os.path.join(tmpdir, "pycoast_cache")},
2000
+ "grid": {"width": 2.0},
2001
+ }
2002
+ fg_img = cw.add_overlay_from_dict(overlays, area_def)
2003
+ fg_img.close()
2004
+ cache_glob = glob(os.path.join(tmpdir, "pycoast_cache_*.png"))
2005
+ assert len(cache_glob) == 2
2006
+ assert os.path.isfile(cache_filename)
2007
+ new_cache_filename = cache_glob[0] if cache_glob[0] != cache_filename else cache_glob[1]
2008
+ # original cache file should be unchanged
2009
+ assert os.path.getmtime(cache_filename) == mtime
2010
+ # new cache file should be...new
2011
+ assert os.path.getmtime(new_cache_filename) != mtime
2012
+
2013
+ @pytest.mark.parametrize("background_mode", ["RGB", "RGBA"])
2014
+ @pytest.mark.parametrize("include_background_pattern", [False, True])
2015
+ @pytest.mark.parametrize("upper_right_opacity", [32, 127, 255])
2016
+ def test_cache_nocache_consistency(
2017
+ self, tmp_path, include_background_pattern, background_mode, upper_right_opacity
2018
+ ):
2019
+ """Test that an image generated with an image looks the same when using a cached foreground."""
2020
+ from pycoast import ContourWriterAGG
2021
+
2022
+ proj4_string = "+proj=longlat +ellps=WGS84"
2023
+ area_extent = (-10.0, -10.0, 10.0, 10.0)
2024
+ area_def = FakeAreaDef(proj4_string, area_extent, 200, 200)
2025
+ cw = ContourWriterAGG(gshhs_root_dir)
2026
+
2027
+ # create test shapefiles
2028
+ test_shape_filename1 = tmp_path / "test_shapes1"
2029
+ _create_polygon_shapefile(test_shape_filename1, [[[-10.0, 10.0], [-5.0, 10.0], [-5.0, 5.0], [-10.0, 5.0]]])
2030
+ test_shape_filename2 = tmp_path / "test_shapes2"
2031
+ _create_polygon_shapefile(test_shape_filename2, [[[5.0, 10.0], [10.0, 10.0], [10.0, 5.0], [5.0, 5.0]]])
2032
+
2033
+ overlays = {
2034
+ "cache": {"file": os.path.join(tmp_path, "pycoast_cache")},
2035
+ "shapefiles": [
2036
+ {
2037
+ "filename": str(test_shape_filename1),
2038
+ "fill": (0, 255, 0),
2039
+ "outline": (255, 255, 255),
2040
+ "fill_opacity": 255,
2041
+ },
2042
+ {
2043
+ "filename": str(test_shape_filename2),
2044
+ "fill": (0, 255, 0),
2045
+ "outline": (255, 255, 0),
2046
+ "fill_opacity": upper_right_opacity,
2047
+ },
2048
+ ],
2049
+ }
625
2050
 
626
- if __name__ == "__main__":
627
- suite()
2051
+ # Create the original cache file
2052
+ background_img1 = _create_background_image(include_background_pattern, background_mode)
2053
+ cached_image1 = cw.add_overlay_from_dict(overlays, area_def, background=background_img1)
2054
+
2055
+ # Reuse the generated cache file
2056
+ background_img2 = _create_background_image(include_background_pattern, background_mode)
2057
+ cached_image2 = cw.add_overlay_from_dict(overlays, area_def, background=background_img2)
2058
+
2059
+ # Create without cache
2060
+ overlays.pop("cache")
2061
+ background_img3 = _create_background_image(include_background_pattern, background_mode)
2062
+ cw.add_overlay_from_dict(overlays, area_def, background=background_img3)
2063
+
2064
+ # Manually (no dict, no cache)
2065
+ background_img4 = _create_background_image(include_background_pattern, background_mode)
2066
+ for shape_params in overlays["shapefiles"]:
2067
+ cw.add_shapefile_shapes(background_img4, area_def, **shape_params)
2068
+
2069
+ # two transparent overlay images
2070
+ np.testing.assert_allclose(np.array(cached_image1), np.array(cached_image2), atol=0)
2071
+ # cached overlay applied to background image should always be equal
2072
+ np.testing.assert_allclose(np.array(background_img1), np.array(background_img2), atol=0)
2073
+ # cached overlay applied to background image and regenerated overlay on background image should be equal
2074
+ # but due to floating point differences they are off by a little bit
2075
+ np.testing.assert_allclose(
2076
+ np.array(background_img1, dtype=np.float32), np.array(background_img3, dtype=np.float32), atol=1
2077
+ )
2078
+ # no cache version and manual version should be the same
2079
+ np.testing.assert_allclose(np.array(background_img3), np.array(background_img4), atol=0)
2080
+
2081
+ @pytest.mark.parametrize(
2082
+ ("crs_input", "extents", "size", "exp_resolution"),
2083
+ [
2084
+ (
2085
+ "+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84",
2086
+ (-3363403.31, -2291879.85, 2630596.69, 2203620.1),
2087
+ (640, 480),
2088
+ "l",
2089
+ ),
2090
+ (
2091
+ "+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84",
2092
+ (-3363403.31, -2291879.85, 2630596.69, 2203620.1),
2093
+ (6400, 4800),
2094
+ "h",
2095
+ ),
2096
+ ("+proj=longlat +ellps=WGS84", (-45.0, -35.0, 5.0, 3.0), (640, 480), "l"),
2097
+ ("+proj=longlat +pm=180 +ellps=WGS84", (-45.0, -35.0, 5.0, 3.0), (640, 480), "l"),
2098
+ ],
2099
+ )
2100
+ def test_get_resolution(self, crs_input, extents, size, exp_resolution):
2101
+ """Get the automagical resolution computation."""
2102
+ from pycoast import get_resolution_from_area
2103
+
2104
+ crs = CRS.from_user_input(crs_input)
2105
+ area_def = FakeAreaDef(crs, extents, *size)
2106
+ assert get_resolution_from_area(area_def) == exp_resolution
2107
+
2108
+
2109
+ def _create_polygon_shapefile(fn: pathlib.Path, polygon_coords: list) -> None:
2110
+ with shapefile.Writer(fn) as test_shapefile:
2111
+ test_shapefile.field("name", "C")
2112
+ test_shapefile.poly(polygon_coords)
2113
+ test_shapefile.record("test")
2114
+
2115
+
2116
+ def _create_background_image(add_pattern: bool, background_mode: str) -> Image:
2117
+ num_bands = len(background_mode)
2118
+ img_data = np.zeros((200, 200, num_bands), dtype=np.uint8)
2119
+ if background_mode[-1] == "A":
2120
+ img_data[..., -1] = 255
2121
+ if add_pattern:
2122
+ img_data[6:30, 6:30, 0] = 127
2123
+ img_data[6:30, -30:-6, 0] = 127
2124
+ return Image.fromarray(img_data, mode=background_mode)