[Git][debian-gis-team/pycoast][master] 9 commits: New upstream version 1.3.0+dfsg
Antonio Valentino
gitlab at salsa.debian.org
Tue Oct 29 06:48:51 GMT 2019
Antonio Valentino pushed to branch master at Debian GIS Project / pycoast
Commits:
f59edc8c by Antonio Valentino at 2019-10-29T06:22:50Z
New upstream version 1.3.0+dfsg
- - - - -
adab560a by Antonio Valentino at 2019-10-29T06:23:11Z
Update upstream source from tag 'upstream/1.3.0+dfsg'
Update to upstream version '1.3.0+dfsg'
with Debian dir 2bbde586e185239f32da7a69370f982afae4ffcc
- - - - -
6029886a by Antonio Valentino at 2019-10-29T06:25:36Z
New upstream release
- - - - -
afd991d5 by Antonio Valentino at 2019-10-29T06:27:27Z
Refresh all patches
- - - - -
6c788024 by Antonio Valentino at 2019-10-29T06:30:36Z
Bump debhelper from old 11 to 12.
Fixes lintian: package-uses-old-debhelper-compat-version
See https://lintian.debian.org/tags/package-uses-old-debhelper-compat-version.html for more details.
- - - - -
e2187246 by Antonio Valentino at 2019-10-29T06:30:39Z
Remove obsolete fields Name from debian/upstream/metadata.
- - - - -
029a76f8 by Antonio Valentino at 2019-10-29T06:44:11Z
Update test dependencies
- - - - -
5ff6b453 by Antonio Valentino at 2019-10-29T06:44:19Z
Explicitly set Rules-Requires-Root
- - - - -
fb07d21e by Antonio Valentino at 2019-10-29T06:44:32Z
Set distribution to unstable
- - - - -
28 changed files:
- .travis.yml
- CHANGELOG.md
- debian/changelog
- − debian/compat
- debian/control
- debian/patches/0001-Skip-tests-that-use-shapes.patch
- debian/upstream/metadata
- docs/source/config.rst
- pycoast/__init__.py
- pycoast/cw_agg.py
- pycoast/cw_base.py
- + pycoast/tests/coasts_and_grid.ini
- + pycoast/tests/coasts_and_grid_agg.ini
- + pycoast/tests/contours_europe_alpha.png
- pycoast/tests/dateline_boundary_cross.png
- pycoast/tests/dateline_cross.png
- pycoast/tests/grid_europe.png
- pycoast/tests/grid_europe_agg.png
- pycoast/tests/grid_europe_agg_txt.png
- pycoast/tests/grid_geos.png
- pycoast/tests/grid_geos_agg.png
- + pycoast/tests/grid_germ.png
- pycoast/tests/grid_nh.png
- pycoast/tests/grid_nh_agg.png
- + pycoast/tests/test_data/test_config.ini
- pycoast/tests/test_pycoast.py
- pycoast/version.py
- setup.py
Changes:
=====================================
.travis.yml
=====================================
@@ -3,9 +3,8 @@ env:
global:
# Set defaults to avoid repeating in most cases
- PYTHON_VERSION=$TRAVIS_PYTHON_VERSION
- - NUMPY_VERSION=stable
- MAIN_CMD='python setup.py'
- - CONDA_DEPENDENCIES='sphinx pillow pyproj coveralls coverage mock aggdraw six pyshp'
+ - CONDA_DEPENDENCIES='sphinx pillow pyproj coveralls coverage mock aggdraw six pyshp pyresample'
- PIP_DEPENDENCIES=''
- SETUP_XVFB=False
- EVENT_TYPE='push pull_request'
@@ -14,9 +13,13 @@ env:
- CONDA_CHANNEL_PRIORITY='True'
matrix:
include:
- - env: PYTHON_VERSION=2.7
+ - env:
+ - PYTHON_VERSION=2.7
+ - NUMPY_VERSION=1.16
os: linux
- - env: PYTHON_VERSION=2.7
+ - env:
+ - PYTHON_VERSION=2.7
+ - NUMPY_VERSION=1.16
os: osx
- env: PYTHON_VERSION=3.6
os: linux
@@ -25,9 +28,16 @@ matrix:
- env: PYTHON_VERSION=3.6
os: windows
language: bash
+ - env: PYTHON_VERSION=3.7
+ os: linux
+ - env: PYTHON_VERSION=3.7
+ os: osx
+ - env: PYTHON_VERSION=3.7
+ os: windows
+ language: bash
install:
-- git clone --depth 1 git://github.com/astropy/ci-helpers.git
-- source ci-helpers/travis/setup_conda.sh
+ - git clone --depth 1 git://github.com/astropy/ci-helpers.git
+ - source ci-helpers/travis/setup_conda.sh
script: coverage run --source=pycoast setup.py test
after_success:
- if [[ $PYTHON_VERSION == 3.6 ]]; then coveralls; fi
=====================================
CHANGELOG.md
=====================================
@@ -1,3 +1,27 @@
+## Version 1.3.0 (2019/10/25)
+
+### Issues Closed
+
+* [Issue 29](https://github.com/pytroll/pycoast/issues/29) - pycoast compatability issue with pyproj 2+
+* [Issue 26](https://github.com/pytroll/pycoast/issues/26) - inconsitency adding lat labelling on the right (on graticules)... ([PR 33](https://github.com/pytroll/pycoast/pull/33))
+
+In this release 2 issues were closed.
+
+### Pull Requests Merged
+
+#### Bugs fixed
+
+* [PR 33](https://github.com/pytroll/pycoast/pull/33) - Fix #26 ([26](https://github.com/pytroll/pycoast/issues/26))
+
+#### Features added
+
+* [PR 32](https://github.com/pytroll/pycoast/pull/32) - Add dict configuration
+* [PR 30](https://github.com/pytroll/pycoast/pull/30) - Convert to RGBA mode when opening the image for adding coastlines or rivers to file.
+* [PR 25](https://github.com/pytroll/pycoast/pull/25) - Add coordinate grid overlaying from configuration file
+
+In this release 4 pull requests were closed.
+
+
## Version 1.2.3 (2019/06/06)
### Issues Closed
@@ -442,5 +466,3 @@ In this release 3 pull requests were closed.
- First version. [Esben S. Nielsen]
- First version. [Esben S. Nielsen]
-
-
=====================================
debian/changelog
=====================================
@@ -1,8 +1,19 @@
-pycoast (1.2.3+dfsg-3) UNRELEASED; urgency=medium
+pycoast (1.3.0+dfsg-1) unstable; urgency=medium
+ [ Bas Couwenberg ]
* Bump Standards-Version to 4.4.1, no changes.
- -- Bas Couwenberg <sebastic at debian.org> Mon, 30 Sep 2019 19:44:38 +0200
+ [ Antonio Valentino ]
+ * New upstream release.
+ * debian/patches:
+ - refresh all patches
+ * debian/control:
+ - bump debhelper from old 11 to 12
+ - update test dependencies (add python3-pyresample)
+ - explicitly set Rules-Requires-Root: no
+ * Remove obsolete fields Name from debian/upstream/metadata.
+
+ -- Antonio Valentino <antonio.valentino at tiscali.it> Tue, 29 Oct 2019 07:44:24 +0100
pycoast (1.2.3+dfsg-2) unstable; urgency=medium
=====================================
debian/compat deleted
=====================================
@@ -1 +0,0 @@
-11
=====================================
debian/control
=====================================
@@ -2,14 +2,16 @@ Source: pycoast
Maintainer: Debian GIS Project <pkg-grass-devel at lists.alioth.debian.org>
Uploaders: Antonio Valentino <antonio.valentino at tiscali.it>
Section: python
+Rules-Requires-Root: no
Priority: optional
-Build-Depends: debhelper (>= 11),
+Build-Depends: debhelper-compat (= 12),
dh-python,
python3-all,
python3-aggdraw,
python3-numpy,
python3-pil,
python3-pyproj,
+ python3-pyresample,
python3-pyshp,
python3-setuptools,
python3-six,
=====================================
debian/patches/0001-Skip-tests-that-use-shapes.patch
=====================================
@@ -7,10 +7,10 @@ Subject: Skip tests that use shapes
1 file changed, 2 insertions(+)
diff --git a/pycoast/tests/test_pycoast.py b/pycoast/tests/test_pycoast.py
-index 976bb61..16e972a 100644
+index 98ea288..862b647 100644
--- a/pycoast/tests/test_pycoast.py
+++ b/pycoast/tests/test_pycoast.py
-@@ -327,6 +327,7 @@ class TestPIL(TestPycoast):
+@@ -357,6 +357,7 @@ class TestPIL(TestPycoast):
self.assertTrue(fft_metric(grid_data, res),
'Writing of nh polygons failed')
@@ -18,7 +18,7 @@ index 976bb61..16e972a 100644
def test_add_shapefile_shapes(self):
from pycoast import ContourWriterPIL
grid_img = Image.open(os.path.join(os.path.dirname(__file__),
-@@ -591,6 +592,7 @@ class TestPILAGG(TestPycoast):
+@@ -644,6 +645,7 @@ class TestPILAGG(TestPycoast):
self.assertTrue(fft_metric(grid_data, res),
'Writing of nh polygons failed')
=====================================
debian/upstream/metadata
=====================================
@@ -1,6 +1,4 @@
----
Bug-Database: https://github.com/pytroll/pycoast/issues
Bug-Submit: https://github.com/pytroll/pycoast/issues/new
-Name: Pycoast
Repository: https://github.com/pytroll/pycoast.git
Repository-Browse: https://github.com/pytroll/pycoast
=====================================
docs/source/config.rst
=====================================
@@ -1,7 +1,7 @@
Pycoast from a configuration file
---------------------------------
-If you want to run to avoid typing the same options over and over again, of if
+If you want to run to avoid typing the same options over and over again, or if
caching is an optimization you want, you can use a configuration file with the
pycoast options you need:
@@ -9,13 +9,14 @@ pycoast options you need:
[cache]
file=/var/run/satellit/white_overlay
-
+ regenerate=False
+
[coasts]
level=1
width=0.75
outline=white
fill=yellow
-
+
[borders]
outline=white
width=0.5
=====================================
pycoast/__init__.py
=====================================
@@ -1,12 +1,12 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-from .version import get_versions
-__version__ = get_versions()['version']
-del get_versions
from .cw_pil import ContourWriterPIL
from .cw_agg import ContourWriterAGG
-
+from pycoast.cw_base import get_resolution_from_area
+from .version import get_versions
+__version__ = get_versions()['version']
+del get_versions
class ContourWriter(ContourWriterPIL):
"""Writer wrapper for deprecation warning.
@@ -21,4 +21,4 @@ class ContourWriter(ContourWriterPIL):
import warnings
warnings.warn("'ContourWriter' has been deprecated please use "
"'ContourWriterPIL' or 'ContourWriterAGG' instead", DeprecationWarning)
- super(ContourWriter, self).__init__(*args, **kwargs)
\ No newline at end of file
+ super(ContourWriter, self).__init__(*args, **kwargs)
=====================================
pycoast/cw_agg.py
=====================================
@@ -326,7 +326,7 @@ class ContourWriterAGG(ContourWriterBase):
minor_outline='white', minor_width=0.5,
minor_outline_opacity=255, minor_is_tick=True,
lon_placement='tb', lat_placement='lr'):
- """Add a lon-lat grid to an image.
+ """Add a lon-lat grid to an image. The resulting image is in 'RGBA' mode.
:Parameters:
image : object
@@ -361,6 +361,7 @@ class ContourWriterAGG(ContourWriterBase):
"""
image = Image.open(filename)
+ image = image.convert('RGBA')
self.add_grid(image, area_def, Dlonlat, dlonlat,
font=font, write_text=write_text,
fill=fill, fill_opacity=fill_opacity,
@@ -417,7 +418,7 @@ class ContourWriterAGG(ContourWriterBase):
level=1, fill=None, fill_opacity=255,
outline='white', width=1, outline_opacity=255,
x_offset=0, y_offset=0):
- """Add coastlines to an image file.
+ """Add coastlines to an image file. The resulting image is in 'RGBA' mode.
:Parameters:
filename : str
@@ -448,6 +449,7 @@ class ContourWriterAGG(ContourWriterBase):
"""
image = Image.open(filename)
+ image = image.convert('RGBA')
self.add_coastlines(image, area_def, resolution=resolution,
level=level, fill=fill,
fill_opacity=fill_opacity, outline=outline,
@@ -492,7 +494,7 @@ class ContourWriterAGG(ContourWriterBase):
def add_borders_to_file(self, filename, area_def, resolution='c',
level=1, outline='white', width=1,
outline_opacity=255, x_offset=0, y_offset=0):
- """Add borders to an image file.
+ """Add borders to an image file. The resulting image is in 'RGBA' mode.
:Parameters:
image : object
@@ -518,6 +520,7 @@ class ContourWriterAGG(ContourWriterBase):
"""
image = Image.open(filename)
+ image = image.convert("RGBA")
self.add_borders(image, area_def, resolution=resolution, level=level,
outline=outline, width=width,
outline_opacity=outline_opacity, x_offset=x_offset,
@@ -562,7 +565,7 @@ class ContourWriterAGG(ContourWriterBase):
def add_rivers_to_file(self, filename, area_def, resolution='c', level=1,
outline='white', width=1, outline_opacity=255,
x_offset=0, y_offset=0):
- """Add rivers to an image file.
+ """Add rivers to an image file. The resulting image is in 'RGBA' mode.
:Parameters:
image : object
@@ -589,6 +592,7 @@ class ContourWriterAGG(ContourWriterBase):
"""
image = Image.open(filename)
+ image = image.convert("RGBA")
self.add_rivers(image, area_def, resolution=resolution, level=level,
outline=outline, width=width,
outline_opacity=outline_opacity, x_offset=x_offset,
=====================================
pycoast/cw_base.py
=====================================
@@ -20,9 +20,10 @@
import os
import shapefile
import numpy as np
-from PIL import Image, ImageFont
+from PIL import Image
import pyproj
import logging
+import ast
try:
import configparser
@@ -32,6 +33,37 @@ except ImportError:
logger = logging.getLogger(__name__)
+def get_resolution_from_area(area_def):
+ """Get the best resolution for an area definition."""
+ x_size = area_def.width
+ y_size = area_def.height
+ prj = Proj(area_def.proj_str)
+ if prj.is_latlong():
+ x_ll, y_ll = prj(area_def.area_extent[0], area_def.area_extent[1])
+ x_ur, y_ur = prj(area_def.area_extent[2], area_def.area_extent[3])
+ x_resolution = (x_ur - x_ll) / x_size
+ y_resolution = (y_ur - y_ll) / y_size
+ else:
+ x_resolution = ((area_def.area_extent[2] -
+ area_def.area_extent[0]) /
+ x_size)
+ y_resolution = ((area_def.area_extent[3] -
+ area_def.area_extent[1]) /
+ y_size)
+ res = min(x_resolution, y_resolution)
+
+ if res > 25000:
+ return "c"
+ elif res > 5000:
+ return "l"
+ elif res > 1000:
+ return "i"
+ elif res > 200:
+ return "h"
+ else:
+ return "f"
+
+
class Proj(pyproj.Proj):
"""Wrapper around pyproj to add in 'is_latlong'."""
@@ -99,7 +131,7 @@ class ContourWriterBase(object):
def _find_line_intercepts(self, xys, size, margins):
"""Finds intercepts of poly-line xys with image boundaries
- offset by margins and returns an array of coordintes"""
+ offset by margins and returns an array of coordinates"""
x_size, y_size = size
def is_in_box(x_y, extents):
@@ -120,13 +152,13 @@ class ContourWriterBase(object):
xlim1 = margins[0]
ylim1 = margins[1]
xlim2 = x_size - margins[0]
- ylim2 = y_size - margins[0]
+ ylim2 = y_size - margins[1]
# only consider crossing within a box a little bigger than grid
# boundary
search_box = (-10, x_size + 10, -10, y_size + 10)
- # loop trought line steps and detect crossings
+ # loop through line steps and detect crossings
intercepts = []
align_left = 'LC'
align_right = 'RC'
@@ -168,7 +200,7 @@ class ContourWriterBase(object):
"""
try:
- proj4_string = area_def.proj4_string
+ proj4_string = area_def.proj_str
area_extent = area_def.area_extent
except AttributeError:
proj4_string = area_def[0]
@@ -234,7 +266,7 @@ class ContourWriterBase(object):
lat_max - shorten_max_lat,
float(lat_max - lat_min) / y_size)
# lin_lats in rather high definition so that it can be used to
- # posituion text labels near edges of image...
+ # position text labels near edges of image...
# perhaps better to find the actual length of line in pixels...
@@ -253,7 +285,7 @@ class ContourWriterBase(object):
# lons along major lat lines (extended slightly to avoid missing the
# end)
- lin_lons = np.arange(lon_min, lon_max + Dlon / 5.0, Dlon / 10.0)
+ lin_lons = np.linspace(lon_min, lon_max + Dlon / 5.0, max(x_size, y_size) / 5)
# MINOR LINES ######
if not kwargs['minor_is_tick']:
@@ -298,9 +330,9 @@ class ContourWriterBase(object):
for lon in maj_lons:
# Draw 'minor' tick lines dlat separation along the lon
if kwargs['minor_is_tick']:
- tick_lons = np.arange(lon - Dlon / 20.0,
- lon + Dlon / 20.0,
- Dlon / 50.0)
+ tick_lons = np.linspace(lon - Dlon / 20.0,
+ lon + Dlon / 20.0,
+ 5)
for lat in min_lats:
lonlats = [(x, lat) for x in tick_lons]
@@ -354,9 +386,9 @@ class ContourWriterBase(object):
for lat in maj_lats:
# Draw 'minor' tick dlon separation along the lat
if kwargs['minor_is_tick']:
- tick_lats = np.arange(lat - Dlat / 20.0,
- lat + Dlat / 20.0,
- Dlat / 50.0)
+ tick_lats = np.linspace(lat - Dlat / 20.0,
+ lat + Dlat / 20.0,
+ 5)
for lon in min_lons:
lonlats = [(lon, x) for x in tick_lats]
index_arrays, is_reduced = \
@@ -512,7 +544,7 @@ class ContourWriterBase(object):
"""
try:
- proj4_string = area_def.proj4_string
+ proj4_string = area_def.proj_str
area_extent = area_def.area_extent
except AttributeError:
proj4_string = area_def[0]
@@ -619,7 +651,7 @@ class ContourWriterBase(object):
if type(level) not in (list,):
level = range(1,level+1)
-
+
for i in level:
# One shapefile per level
@@ -644,22 +676,12 @@ class ContourWriterBase(object):
yield shape
def _finalize(self, draw):
- """Do any need finalization of the drawing
- """
+ """Do any need finalization of the drawing."""
pass
- def add_overlay_from_config(self, config_file, area_def):
- """Create and return a transparent image adding all the overlays contained in a configuration file.
-
- :Parameters:
- config_file : str
- Configuration file name
- area_def : object
- Area Definition of the creating image
-
- """
-
+ def _config_to_dict(self, config_file):
+ """Convert a config file to a dict."""
config = configparser.ConfigParser()
try:
with open(config_file, 'r'):
@@ -673,58 +695,79 @@ class ContourWriterBase(object):
logger.error("Error in %s", str(config_file))
raise
+ SECTIONS = ['cache', 'coasts', 'rivers', 'borders', 'cities', 'grid']
+ overlays = {}
+ for section in config.sections():
+ if section in SECTIONS:
+ overlays[section] = {}
+ for option in config.options(section):
+ val = config.get(section, option)
+ try:
+ overlays[section][option] = ast.literal_eval(val)
+ except ValueError:
+ overlays[section][option] = val
+ return overlays
+
+ def add_overlay_from_dict(self, overlays, area_def, cache_epoch=None, background=None):
+ """Create and return a transparent image adding all the overlays contained in the `overlays` dict.
+
+ :Parameters:
+ overlays : dict
+ overlays configuration
+ area_def : object
+ Area Definition of the creating image
+ cache_epoch: seconds since epoch
+ The latest time allowed for cache the cache file. If the cache file is older than this (mtime),
+ the cache should be regenerated.
+ background: pillow image instance
+ The image on which to write the overlays on. If it's None (default),
+ a new image is created, otherwise the provide background is use
+ an change *in place*.
+
+
+ The keys in `overlays` that will be taken into account are:
+ cache, coasts, rivers, borders, cities, grid
+
+ For all of them except `cache`, the items are the same as the corresponding
+ functions in pycoast, so refer to the docstrings of these functions
+ (add_coastlines, add_rivers, add_borders, add_grid, add_cities).
+ For cache, two parameters are configurable: `file` which specifies the directory
+ and the prefix of the file to save the caches decoration to
+ (for example /var/run/black_coasts_red_borders), and `regenerate` that can be
+ True or False (default) to force the overwriting of an already cached file.
+
+ """
+
# Cache management
cache_file = None
- if config.has_section('cache'):
- config_file_name, config_file_extention = \
- os.path.splitext(config_file)
- cache_file = (config.get('cache', 'file') + '_' +
+ if 'cache' in overlays:
+ cache_file = (overlays['cache']['file'] + '_' +
area_def.area_id + '.png')
try:
- configTime = os.path.getmtime(config_file)
- cacheTime = os.path.getmtime(cache_file)
+ config_time = cache_epoch
+ cache_time = os.path.getmtime(cache_file)
# Cache file will be used only if it's newer than config file
- if configTime < cacheTime:
+ if ((config_time is not None and config_time < cache_time)
+ and not overlays['cache'].get('regenerate', False)):
foreground = Image.open(cache_file)
logger.info('Using image in cache %s', cache_file)
+ if background is not None:
+ background.paste(foreground, mask=foreground.split()[-1])
return foreground
else:
- logger.info("Cache file is not used "
- "because config file has changed")
+ logger.info("Regenerating cache file.")
except OSError:
- logger.info("New overlay image will be saved in cache")
-
- x_size = area_def.x_size
- y_size = area_def.y_size
- foreground = Image.new('RGBA', (x_size, y_size), (0, 0, 0, 0))
-
- # Lines (coasts, rivers, borders) management
- prj = Proj(area_def.proj4_string)
- if prj.is_latlong():
- x_ll, y_ll = prj(area_def.area_extent[0], area_def.area_extent[1])
- x_ur, y_ur = prj(area_def.area_extent[2], area_def.area_extent[3])
- x_resolution = (x_ur - x_ll) / x_size
- y_resolution = (y_ur - y_ll) / y_size
- else:
- x_resolution = ((area_def.area_extent[2] -
- area_def.area_extent[0]) /
- x_size)
- y_resolution = ((area_def.area_extent[3] -
- area_def.area_extent[1]) /
- y_size)
- res = min(x_resolution, y_resolution)
-
- if res > 25000:
- default_resolution = "c"
- elif res > 5000:
- default_resolution = "l"
- elif res > 1000:
- default_resolution = "i"
- elif res > 200:
- default_resolution = "h"
+ logger.info("No overlay image found, new overlay image will be saved in cache.")
+
+ x_size = area_def.width
+ y_size = area_def.height
+ if cache_file is None and background is not None:
+ foreground = background
else:
- default_resolution = "f"
+ foreground = Image.new('RGBA', (x_size, y_size), (0, 0, 0, 0))
+
+ default_resolution = get_resolution_from_area(area_def)
DEFAULT = {'level': 1,
'outline': 'white',
@@ -736,18 +779,9 @@ class ContourWriterBase(object):
'y_offset': 0,
'resolution': default_resolution}
- SECTIONS = ['coasts', 'rivers', 'borders', 'cities']
- overlays = {}
-
- for section in config.sections():
- if section in SECTIONS:
- overlays[section] = {}
- for option in config.options(section):
- overlays[section][option] = config.get(section, option)
-
is_agg = self._draw_module == "AGG"
- # Coasts
+ # Coasts, rivers, borders
for section, fun in zip(['coasts', 'rivers', 'borders'],
[self.add_coastlines,
self.add_rivers,
@@ -795,14 +829,63 @@ class ContourWriterBase(object):
font_size, pt_size, outline, box_outline,
box_opacity)
+ if 'grid' in overlays:
+ lon_major = float(overlays['grid'].get('lon_major', 10.0))
+ lat_major = float(overlays['grid'].get('lat_major', 10.0))
+ lon_minor = float(overlays['grid'].get('lon_minor', 2.0))
+ lat_minor = float(overlays['grid'].get('lat_minor', 2.0))
+ font = overlays['grid'].get('font', None)
+ font_size = int(overlays['grid'].get('font_size', 10))
+
+ write_text = overlays['grid'].get('write_text', True)
+ if isinstance(write_text, str):
+ write_text = write_text.lower() in ['true', 'yes', '1', 'on']
+ outline = overlays['grid'].get('outline', 'white')
+ if isinstance(font, str):
+ if is_agg:
+ from aggdraw import Font
+ font = Font(outline, font, size=font_size)
+ else:
+ from PIL.ImageFont import truetype
+ font = truetype(font, font_size)
+ fill = overlays['grid'].get('fill', None)
+ minor_outline = overlays['grid'].get('minor_outline', 'white')
+ minor_is_tick = overlays['grid'].get('minor_is_tick',
+ 'true').lower() in \
+ ['true', 'yes', '1']
+ lon_placement = overlays['grid'].get('lon_placement', 'tb')
+ lat_placement = overlays['grid'].get('lat_placement', 'lr')
+
+ self.add_grid(foreground, area_def, (lon_major, lat_major),
+ (lon_minor, lat_minor),
+ font=font, write_text=write_text, fill=fill,
+ outline=outline, minor_outline=minor_outline,
+ minor_is_tick=minor_is_tick,
+ lon_placement=lon_placement,
+ lat_placement=lat_placement)
+
if cache_file is not None:
try:
foreground.save(cache_file)
except IOError as e:
logger.error("Can't save cache: %s", str(e))
-
+ if background is not None:
+ background.paste(foreground, mask=foreground.split()[-1])
return foreground
+ def add_overlay_from_config(self, config_file, area_def, background=None):
+ """Create and return a transparent image adding all the overlays contained in a configuration file.
+
+ :Parameters:
+ config_file : str
+ Configuration file name
+ area_def : object
+ Area Definition of the creating image
+
+ """
+ overlays = self._config_to_dict(config_file)
+ return self.add_overlay_from_dict(overlays, area_def, os.path.getmtime(config_file), background)
+
def add_cities(self, image, area_def, citylist, font_file, font_size,
ptsize, outline, box_outline, box_opacity, db_root_path=None):
"""Add cities (point and name) to a PIL image object
@@ -814,7 +897,7 @@ class ContourWriterBase(object):
raise ValueError("'db_root_path' must be specified to use this method")
try:
- proj4_string = area_def.proj4_string
+ proj4_string = area_def.proj_str
area_extent = area_def.area_extent
except AttributeError:
proj4_string = area_def[0]
=====================================
pycoast/tests/coasts_and_grid.ini
=====================================
@@ -0,0 +1,13 @@
+[coasts]
+level = 4
+resolution = l
+outline = white
+
+[grid]
+write_text = False
+lon_major = 10.0
+lat_major = 10.0
+lon_minor = 2.0
+lat_minor = 2.0
+minor_outline = blue
+outline = blue
=====================================
pycoast/tests/coasts_and_grid_agg.ini
=====================================
@@ -0,0 +1,18 @@
+[coasts]
+level = 4
+resolution = l
+outline = white
+
+[grid]
+write_text = True
+lon_major = 10.0
+lat_major = 10.0
+lon_minor = 2.0
+lat_minor = 2.0
+minor_outline = blue
+outline = blue
+lon_placement = tblr
+lat_placement =
+font = pycoast/tests/test_data/DejaVuSerif.ttf
+font_size = 10
+
=====================================
pycoast/tests/contours_europe_alpha.png
=====================================
Binary files /dev/null and b/pycoast/tests/contours_europe_alpha.png differ
=====================================
pycoast/tests/dateline_boundary_cross.png
=====================================
Binary files a/pycoast/tests/dateline_boundary_cross.png and b/pycoast/tests/dateline_boundary_cross.png differ
=====================================
pycoast/tests/dateline_cross.png
=====================================
Binary files a/pycoast/tests/dateline_cross.png and b/pycoast/tests/dateline_cross.png differ
=====================================
pycoast/tests/grid_europe.png
=====================================
Binary files a/pycoast/tests/grid_europe.png and b/pycoast/tests/grid_europe.png differ
=====================================
pycoast/tests/grid_europe_agg.png
=====================================
Binary files a/pycoast/tests/grid_europe_agg.png and b/pycoast/tests/grid_europe_agg.png differ
=====================================
pycoast/tests/grid_europe_agg_txt.png
=====================================
Binary files a/pycoast/tests/grid_europe_agg_txt.png and b/pycoast/tests/grid_europe_agg_txt.png differ
=====================================
pycoast/tests/grid_geos.png
=====================================
Binary files a/pycoast/tests/grid_geos.png and b/pycoast/tests/grid_geos.png differ
=====================================
pycoast/tests/grid_geos_agg.png
=====================================
Binary files a/pycoast/tests/grid_geos_agg.png and b/pycoast/tests/grid_geos_agg.png differ
=====================================
pycoast/tests/grid_germ.png
=====================================
Binary files /dev/null and b/pycoast/tests/grid_germ.png differ
=====================================
pycoast/tests/grid_nh.png
=====================================
Binary files a/pycoast/tests/grid_nh.png and b/pycoast/tests/grid_nh.png differ
=====================================
pycoast/tests/grid_nh_agg.png
=====================================
Binary files a/pycoast/tests/grid_nh_agg.png and b/pycoast/tests/grid_nh_agg.png differ
=====================================
pycoast/tests/test_data/test_config.ini
=====================================
@@ -0,0 +1,12 @@
+[coasts]
+level=4
+resolution=l
+
+[borders]
+outline=(255, 0, 0)
+resolution=c
+
+[rivers]
+level=5
+outline=blue
+resolution=c
=====================================
pycoast/tests/test_pycoast.py
=====================================
@@ -22,7 +22,7 @@ import unittest
import numpy as np
from PIL import Image, ImageFont
-
+import time
def tmp(f):
f.tmp = True
@@ -68,6 +68,7 @@ def fft_metric(data1, data2, max_value=0.1):
gshhs_root_dir = os.path.join(os.path.dirname(__file__), 'test_data', 'gshhs')
test_file = 'test_image.png'
grid_file = 'test_grid.png'
+p_file_coasts = 'test_coasts_p_mode.png'
class TestPycoast(unittest.TestCase):
@@ -76,10 +77,13 @@ class TestPycoast(unittest.TestCase):
img = Image.new('RGB', (640, 480))
img.save(test_file)
img.save(grid_file)
+ img_p = Image.new('P', (640, 480))
+ img_p.save(p_file_coasts)
def tearDown(self):
os.remove(test_file)
os.remove(grid_file)
+ os.remove(p_file_coasts)
class TestPIL(TestPycoast):
@@ -167,6 +171,32 @@ class TestPIL(TestPycoast):
res = np.array(img)
self.assertTrue(fft_metric(grid_data, res), 'Writing of grid failed')
+ def test_grid_germ(self):
+ """Check that issue #26 is fixed."""
+ from pycoast import ContourWriterPIL
+ result_file = os.path.join(os.path.dirname(__file__), 'grid_germ.png')
+ grid_img = Image.open(result_file)
+ grid_data = np.array(grid_img)
+ img = Image.new('RGB', (1024, 1024))
+ proj4_string = \
+ '+proj=stere +ellps=bessel +lat_0=90.0 +lon_0=5.0 +lat_ts=50.0 +a=6378144.0 +b=6356759.0'
+ area_extent = [-155100.436345, -4441495.37946, 868899.563655, -3417495.37946]
+
+ area_def = (proj4_string, area_extent)
+
+ cw = ContourWriterPIL(gshhs_root_dir)
+
+ cw.add_coastlines(img, area_def, resolution='l', level=4)
+ font = ImageFont.truetype(os.path.join(os.path.dirname(__file__),
+ 'test_data', 'DejaVuSerif.ttf'),
+ 16)
+ cw.add_grid(img, area_def, (10.0, 10.0), (2.0, 2.0),
+ font=font, fill='yellow', write_text=True,
+ outline='red', minor_outline='white')
+
+ res = np.array(img)
+ self.assertTrue(fft_metric(grid_data, res), 'Writing of grid to germ failed')
+
def test_grid_geos(self):
from pycoast import ContourWriterPIL
geos_img = Image.open(
@@ -359,6 +389,29 @@ class TestPIL(TestPycoast):
self.assertTrue(
fft_metric(grid_data, res), 'Writing of Brazil shapefiles failed')
+ def test_config_file_coasts_and_grid(self):
+ from pycoast import ContourWriterPIL
+ from pyresample.geometry import AreaDefinition
+ overlay_config = os.path.join(os.path.dirname(__file__),
+ "coasts_and_grid.ini")
+ grid_img = Image.open(os.path.join(os.path.dirname(__file__),
+ 'grid_nh.png'))
+ grid_data = np.array(grid_img)
+ proj_dict = {'proj': 'laea', 'lat_0': 90.0, 'lon_0': 0.0,
+ 'a': 6371228.0, 'units': 'm'}
+ area_extent = (-5326849.0625, -5326849.0625,
+ 5326849.0625, 5326849.0625)
+ area_def = AreaDefinition('nh', 'nh', 'nh', proj_dict, 425, 425,
+ area_extent)
+
+ cw = ContourWriterPIL(gshhs_root_dir)
+ overlay = cw.add_overlay_from_config(overlay_config, area_def)
+ img = Image.new('RGB', (425, 425))
+ img.paste(overlay, mask=overlay)
+ res = np.array(img)
+ self.assertTrue(fft_metric(grid_data, res),
+ 'Writing of nh grid failed')
+
class TestPILAGG(TestPycoast):
@@ -624,12 +677,161 @@ class TestPILAGG(TestPycoast):
self.assertTrue(
fft_metric(grid_data, res), 'Writing of Brazil shapefiles failed')
+ @unittest.skip("All kwargs are not supported, so can't create equal results")
+ def test_config_file_coasts_and_grid(self):
+ from pycoast import ContourWriterAGG
+ from pyresample.geometry import AreaDefinition
+ overlay_config = os.path.join(os.path.dirname(__file__),
+ "coasts_and_grid_agg.ini")
+ grid_img = Image.open(os.path.join(os.path.dirname(__file__),
+ 'grid_nh_agg.png'))
+ grid_data = np.array(grid_img)
+ proj_dict = {'proj': 'laea', 'lat_0': 90.0, 'lon_0': 0.0,
+ 'a': 6371228.0, 'units': 'm'}
+ area_extent = (-5326849.0625, -5326849.0625,
+ 5326849.0625, 5326849.0625)
+ area_def = AreaDefinition('nh', 'nh', 'nh', proj_dict, 425, 425,
+ area_extent)
+
+ cw = ContourWriterAGG(gshhs_root_dir)
+ overlay = cw.add_overlay_from_config(overlay_config, area_def)
+ img = Image.new('RGB', (425, 425))
+ img.paste(overlay, mask=overlay)
+
+ res = np.array(img)
+ self.assertTrue(fft_metric(grid_data, res),
+ 'Writing of nh grid failed')
+
+ def test_coastlines_convert_to_rgba_agg(self):
+ from pycoast import ContourWriterAGG
+ proj4_string = \
+ '+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84'
+ area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
+ area_def = (proj4_string, area_extent)
+
+ cw = ContourWriterAGG(gshhs_root_dir)
+ cw.add_coastlines_to_file(p_file_coasts, area_def, resolution='l', level=4)
+
+ img = Image.open(p_file_coasts)
+ image_mode = img.mode
+ img.close()
+
+ self.assertTrue(image_mode == 'RGBA', 'Conversion to RGBA failed.')
+
+
+class FakeAreaDef():
+ """A fake area definition object."""
+
+ def __init__(self, proj4_string, area_extent, x_size, y_size):
+ self.proj_str = proj4_string
+ self.area_extent = area_extent
+ self.width = x_size
+ self.height = y_size
+ self.area_id = 'fakearea'
+
+
+class TestFromConfig(TestPycoast):
+ """Test burning overlays from a config file."""
+
+ def test_foreground(self):
+ """Test generating a transparent foreground."""
+ from pycoast import ContourWriterPIL
+ euro_img = Image.open(os.path.join(os.path.dirname(__file__),
+ 'contours_europe_alpha.png'))
+ euro_data = np.array(euro_img)
+
+ # img = Image.new('RGB', (640, 480))
+ proj4_string = \
+ '+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84'
+ area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
+ area_def = FakeAreaDef(proj4_string, area_extent, 640, 480)
+ cw = ContourWriterPIL(gshhs_root_dir)
+ config_file = os.path.join(os.path.dirname(__file__), 'test_data', 'test_config.ini')
+ img = cw.add_overlay_from_config(config_file, area_def)
+
+ res = np.array(img)
+ self.assertTrue(fft_metric(euro_data, res),
+ 'Writing of contours failed')
+
+ overlays = {'coasts': {'level': 4, 'resolution': 'l'},
+ 'borders': {'outline': (255, 0, 0), 'resolution': 'c'},
+ 'rivers': {'outline': 'blue', 'resolution': 'c', 'level': 5}}
+
+ img = cw.add_overlay_from_dict(overlays, area_def)
+ res = np.array(img)
+ self.assertTrue(fft_metric(euro_data, res),
+ 'Writing of contours failed')
+
+ def test_cache(self):
+ """Test generating a transparent foreground and cache it."""
+ from pycoast import ContourWriterPIL
+ from tempfile import gettempdir
+ euro_img = Image.open(os.path.join(os.path.dirname(__file__),
+ 'contours_europe_alpha.png'))
+ euro_data = np.array(euro_img)
+
+ # img = Image.new('RGB', (640, 480))
+ proj4_string = \
+ '+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84'
+ area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
+ area_def = FakeAreaDef(proj4_string, area_extent, 640, 480)
+ cw = ContourWriterPIL(gshhs_root_dir)
+
+ tmp = gettempdir()
+
+ overlays = {'cache': {'file': os.path.join(tmp, 'pycoast_cache')},
+ 'coasts': {'level': 4, 'resolution': 'l'},
+ 'borders': {'outline': (255, 0, 0), 'resolution': 'c'},
+ 'rivers': {'outline': 'blue', 'resolution': 'c', 'level': 5}}
+
+ cache_filename = os.path.join(tmp, 'pycoast_cache_fakearea.png')
+ img = cw.add_overlay_from_dict(overlays, area_def)
+ res = np.array(img)
+ self.assertTrue(fft_metric(euro_data, res),
+ 'Writing of contours failed')
+ self.assertTrue(os.path.isfile(cache_filename))
+
+ current_time = time.time()
+
+ img = cw.add_overlay_from_dict(overlays, area_def, current_time)
+
+ mtime = os.path.getmtime(cache_filename)
+
+ self.assertGreater(mtime, current_time)
+ self.assertTrue(fft_metric(euro_data, res),
+ 'Writing of contours failed')
+
+ img = cw.add_overlay_from_dict(overlays, area_def, current_time)
+
+ self.assertEqual(os.path.getmtime(cache_filename), mtime)
+ self.assertTrue(fft_metric(euro_data, res),
+ 'Writing of contours failed')
+ overlays['cache']['regenerate'] = True
+ img = cw.add_overlay_from_dict(overlays, area_def)
+
+ self.assertNotEqual(os.path.getmtime(cache_filename), mtime)
+ self.assertTrue(fft_metric(euro_data, res),
+ 'Writing of contours failed')
+ os.remove(os.path.join(tmp, 'pycoast_cache_fakearea.png'))
+
+ def test_get_resolution(self):
+ """Get the automagical resolution computation."""
+ from pycoast import get_resolution_from_area
+ proj4_string = \
+ '+proj=stere +lon_0=8.00 +lat_0=50.00 +lat_ts=50.00 +ellps=WGS84'
+ area_extent = (-3363403.31, -2291879.85, 2630596.69, 2203620.1)
+ area_def = FakeAreaDef(proj4_string, area_extent, 640, 480)
+ self.assertEqual(get_resolution_from_area(area_def), 'l')
+ area_def = FakeAreaDef(proj4_string, area_extent, 6400, 4800)
+ self.assertEqual(get_resolution_from_area(area_def), 'h')
+
def suite():
loader = unittest.TestLoader()
mysuite = unittest.TestSuite()
mysuite.addTest(loader.loadTestsFromTestCase(TestPIL))
mysuite.addTest(loader.loadTestsFromTestCase(TestPILAGG))
+ mysuite.addTest(loader.loadTestsFromTestCase(TestFromConfig))
return mysuite
=====================================
pycoast/version.py
=====================================
@@ -23,9 +23,9 @@ def get_keywords():
# setup.py/versioneer.py will grep for the variable names, so they must
# each be defined on a line of their own. _version.py will just call
# get_keywords().
- git_refnames = " (HEAD -> master, tag: v1.2.3)"
- git_full = "eaddef8f6c84867137c6e32d5a89e629fa012fc2"
- git_date = "2019-06-06 10:16:30 -0500"
+ git_refnames = " (HEAD -> master, tag: v1.3.0)"
+ git_full = "60a725edca4b3896bcbba8ff67704041fd0e382b"
+ git_date = "2019-10-25 08:39:57 +0200"
keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
return keywords
=====================================
setup.py
=====================================
@@ -19,6 +19,8 @@ from setuptools import setup
import versioneer
requires = ['aggdraw', 'pyshp', 'numpy', 'pyproj', 'pillow', 'six']
+tests_require = ['aggdraw', 'pyshp', 'numpy', 'pyproj', 'pillow', 'six',
+ 'pyresample']
with open('README', 'r') as readme_file:
long_description = readme_file.read()
@@ -32,6 +34,7 @@ setup(name='pycoast',
author_email='esn at dmi.dk',
packages=['pycoast', 'pycoast.tests'],
install_requires=requires,
+ tests_require=tests_require,
test_suite='pycoast.tests.test_pycoast.suite',
zip_safe=False,
classifiers=[
View it on GitLab: https://salsa.debian.org/debian-gis-team/pycoast/compare/9b37e0e8d5f24ec68f99406777776cfbe6d64e2f...fb07d21eec6a2b4cc249607abc5811558d1c705a
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/pycoast/compare/9b37e0e8d5f24ec68f99406777776cfbe6d64e2f...fb07d21eec6a2b4cc249607abc5811558d1c705a
You're receiving this email because of your account on salsa.debian.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20191029/847f48b4/attachment-0001.html>
More information about the Pkg-grass-devel
mailing list