[Git][debian-gis-team/pycoast][master] 7 commits: New upstream version 1.4.0+dfsg
Antonio Valentino
gitlab at salsa.debian.org
Thu Jun 11 07:09:14 BST 2020
Antonio Valentino pushed to branch master at Debian GIS Project / pycoast
Commits:
2ec45a39 by Antonio Valentino at 2020-06-11T05:11:37+00:00
New upstream version 1.4.0+dfsg
- - - - -
37ab5b79 by Antonio Valentino at 2020-06-11T05:12:38+00:00
Update upstream source from tag 'upstream/1.4.0+dfsg'
Update to upstream version '1.4.0+dfsg'
with Debian dir 67a1ff76a7115c0591aa1431d22fa1a9220f78aa
- - - - -
4deefdc1 by Antonio Valentino at 2020-06-11T07:26:28+02:00
New upstream release
- - - - -
0c17c7b2 by Antonio Valentino at 2020-06-11T07:26:34+02:00
Refresh all patches
- - - - -
162803bb by Antonio Valentino at 2020-06-11T05:30:40+00:00
Update copyright dates
- - - - -
68ea99f1 by Antonio Valentino at 2020-06-11T05:49:15+00:00
Drop lintian-overrides
- - - - -
7632b06f by Antonio Valentino at 2020-06-11T05:49:29+00:00
Set distribution to unstable
- - - - -
23 changed files:
- .gitignore
- AUTHORS.md
- CHANGELOG.md
- debian/changelog
- debian/copyright
- debian/patches/0001-Skip-tests-that-use-shapes.patch
- − debian/source/lintian-overrides
- + docs/source/images/nh_points_agg.png
- docs/source/index.rst
- + docs/source/points.rst
- pycoast/__init__.py
- pycoast/cw_agg.py
- pycoast/cw_base.py
- pycoast/cw_pil.py
- pycoast/tests/coasts_and_grid_agg.ini
- + pycoast/tests/grid_nh_cfg_agg.png
- + pycoast/tests/nh_points_agg.ini
- + pycoast/tests/nh_points_agg.png
- + pycoast/tests/nh_points_cfg_pil.png
- + pycoast/tests/nh_points_pil.ini
- + pycoast/tests/nh_points_pil.png
- pycoast/tests/test_pycoast.py
- pycoast/version.py
Changes:
=====================================
.gitignore
=====================================
@@ -58,5 +58,3 @@ docs/_build/
# PyBuilder
target/
-
-
=====================================
AUTHORS.md
=====================================
@@ -15,3 +15,4 @@ The following people have made contributions to this project:
- [Martin Raspaud (mraspaud)](https://github.com/mraspaud)
- [Hrobjartur Thorsteinsson (thorsteinssonh)](https://github.com/thorsteinssonh)
- [Antonio Valentino (avalentino)](https://github.com/avalentino)
+- [Yufei Zhu (yufeizhu600)](https://github.com/yufeizhu600)
=====================================
CHANGELOG.md
=====================================
@@ -1,3 +1,39 @@
+## Version 1.4.0 (2020/06/08)
+
+### Issues Closed
+
+* [Issue 36](https://github.com/pytroll/pycoast/issues/36) - Add points to an image with list of lat/long pairs ([PR 42](https://github.com/pytroll/pycoast/pull/42))
+
+In this release 1 issue was closed.
+
+### Pull Requests Merged
+
+#### Bugs fixed
+
+* [PR 45](https://github.com/pytroll/pycoast/pull/45) - Fix cached overlays always being regenerated
+* [PR 44](https://github.com/pytroll/pycoast/pull/44) - Fix adding textbox in the add_points method
+
+In this release 2 pull requests were closed.
+
+
+## Version 1.3.2 (2019/12/06)
+
+### Issues Closed
+
+* [Issue 39](https://github.com/pytroll/pycoast/issues/39) - distorted/strange coastlines with pyproj-2.4.2 ([PR 40](https://github.com/pytroll/pycoast/pull/40))
+
+In this release 1 issue was closed.
+
+### Pull Requests Merged
+
+#### Bugs fixed
+
+* [PR 40](https://github.com/pytroll/pycoast/pull/40) - Fix compatibility with pyproj 2.4.2 and reduce generated warnings ([39](https://github.com/pytroll/pycoast/issues/39))
+* [PR 38](https://github.com/pytroll/pycoast/pull/38) - Remove unnecessary casting in adding overlay from dictionary
+
+In this release 2 pull requests were closed.
+
+
## Version 1.3.1 (2019/11/07)
### Pull Requests Merged
=====================================
debian/changelog
=====================================
@@ -1,10 +1,16 @@
-pycoast (1.3.2+dfsg-2) UNRELEASED; urgency=medium
+pycoast (1.4.0+dfsg-1) unstable; urgency=medium
+ * New upstream release.
* debian/control:
- add Testsuite: autopkgtest-pkg-python
+ * Drop lintian-overrides.
* Bump Standards-Version to 4.5.0, no changes.
+ * debian/patches:
+ - refresh all patches
+ * debian/copyright:
+ - update copyright dates
- -- Antonio Valentino <antonio.valentino at tiscali.it> Mon, 06 Jan 2020 16:52:00 +0000
+ -- Antonio Valentino <antonio.valentino at tiscali.it> Thu, 11 Jun 2020 05:49:18 +0000
pycoast (1.3.2+dfsg-1) unstable; urgency=medium
=====================================
debian/copyright
=====================================
@@ -12,7 +12,7 @@ Files-Excluded: docs/build/*
pycoast/tests/test_data/shapes/*
Files: *
-Copyright: 2011-2018, PyCoast Developers
+Copyright: 2011-2020, PyCoast Developers
2011-2016, Esben S. Nielsen <esn at dmi.dk>
2011-2015, Hróbjartur Þorsteinsson
2011-2015, Stefano Cerino
@@ -42,7 +42,7 @@ Copyright: 1991-2008, P. Wessel & W. H. F. Smith
License: GPL-2+
Files: debian/*
-Copyright: 2014, Antonio Valentino <antonio.valentino at tiscali.it>
+Copyright: 2014-2020, Antonio Valentino <antonio.valentino at tiscali.it>
License: GPL-3+
License: CC0-1.0
=====================================
debian/patches/0001-Skip-tests-that-use-shapes.patch
=====================================
@@ -7,20 +7,20 @@ 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 cf34154..b2811a2 100644
+index 82adcb0..b95edba 100644
--- a/pycoast/tests/test_pycoast.py
+++ b/pycoast/tests/test_pycoast.py
-@@ -357,6 +357,7 @@ class TestPIL(TestPycoast):
+@@ -397,6 +397,7 @@ class TestPIL(TestPycoast):
self.assertTrue(fft_metric(grid_data, res),
- 'Writing of nh polygons failed')
+ 'Writing of nh points failed')
+ @unittest.skip('dataset not available: test_data/shapes/Metareas.shp')
def test_add_shapefile_shapes(self):
from pycoast import ContourWriterPIL
grid_img = Image.open(os.path.join(os.path.dirname(__file__),
-@@ -644,6 +645,7 @@ class TestPILAGG(TestPycoast):
- self.assertTrue(fft_metric(grid_data, res),
- 'Writing of nh polygons failed')
+@@ -749,6 +750,7 @@ class TestPILAGG(TestPycoast):
+ res = np.array(img)
+ self.assertTrue(fft_metric(grid_data, res), 'Writing of nh points failed')
+ @unittest.skip('dataset not available: test_data/shapes/Metareas.shp')
def test_add_shapefile_shapes_agg(self):
=====================================
debian/source/lintian-overrides deleted
=====================================
@@ -1,3 +0,0 @@
-# Not worth the effort
-testsuite-autopkgtest-missing
-
=====================================
docs/source/images/nh_points_agg.png
=====================================
Binary files /dev/null and b/docs/source/images/nh_points_agg.png differ
=====================================
docs/source/index.rst
=====================================
@@ -20,6 +20,7 @@ images using data from the GSHHS and WDBII datasets
graticule
config
polygons_and_lines
+ points
shapefiles
test
PyCoast API <api/pycoast>
=====================================
docs/source/points.rst
=====================================
@@ -0,0 +1,88 @@
+Add custom points with descriptions
+-----------------------------------
+
+Pycoast can add a symbol to points of interest on an image. The following examples show how
+we might use the :meth:`~pycoast.cw_agg.ContourWriterAGG.add_points` method to annotate the
+points on an image.
+
+First of all, we setup a PIL image with an area definition, then we add coastlines and
+borders for reference.
+
+ >>> from PIL import Image
+ >>> from pycoast import ContourWriterAGG
+ >>> img = Image.new('RGB', (1024, 1024), (255, 255, 255))
+ >>> proj4_string = '+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m'
+ >>> area_extent = (-5326849.0625, -5326849.0625, 5326849.0625, 5326849.0625)
+ >>> area_def = AreaDefinition('nh', 'nh', 'nh', proj4_string, 1024, 1024, area_extent)
+ >>> cw = ContourWriterAGG('/home/esn/data/gshhs')
+ >>> cw.add_coastlines(img, area_def, outline='black', resolution='l', level=4)
+ >>> cw.add_borders(img, area_def, outline='black', width=3, level=1, resolution='c')
+
+Now we can add a circle, which is the default symbol, with default point size 6 at the
+location of Berlin, the name of the location will marked in a text box with black borders
+and the default text size is 12.
+
+ >>> points_list = [((13.4050, 52.5200), 'Berlin')]
+ >>> cw.add_points(pil_img, area, points_list=points_list, font_file=font_file)
+
+We can also annotate the image with text only by setting the ptsize to 0..
+The example below will add 'Rome' at the given location without a symbol..
+
+ >>> points_list = [((12.4964, 41.9028), 'Rome')]
+ >>> cw.add_points(pil_img, area, points_list=points_list,
+ ... font_file=font_file, font_size=16,
+ ... symbol='circle', ptsize=0,
+ ... box_outline='black', text_linewidth=1,
+ ... box_fill='yellow', box_opacity=200)
+
+Similarly, assign the description as an empty string will only draw the symbol on the image.
+The example below will draw a square symbol at the location of Paris.
+
+ >>> points_list = [((2.3522, 48.8566), '')]
+ >>> cw.add_points(pil_img, area, points_list=points_list,
+ ... font_file=font_file,
+ ... symbol='square', ptsize=10,
+ ... outline='red', width=1,
+ ... fill='blue', fill_opacity=128)
+
+Finally, we can fully customize the annotation as the example below, which will add
+a circle in black with line width set to 2 and filled in red color with opacity equals 255;
+the description will be 'London' in a textbox with blue borders and filled with green color
+with opacity set to 128.
+
+ >>> points_list = [((0.1278, 51.5074), 'London')]
+ >>> cw.add_points(img, area_def, points_list=points_list,
+ ... font_file=font_file, font_size=14,
+ ... symbol='circle', ptsize=14,
+ ... outline='black', width=2,
+ ... fill='red', fill_opacity=255,
+ ... box_outline='blue', box_linewidth=1.5,
+ ... box_fill='green', box_opacity=128)
+ >>> img.show()
+
+.. image:: images/nh_points_agg.png
+
+
+Please check out the docstrings of the :meth:`~pycoast.cw_agg.ContourWriterAGG.add_points`
+function for the full description of the parameters.
+
+Moreover, we can organize the overlay parameters in a dictionary and use :meth:`~pycoast.cw_agg.ContourWriterAGG.add_overlay_from_dict`
+to apply the overlays in one shot.
+
+ >>> points = {'points_list': [((2.3522, 48.8566), 'Paris'), ((0.1278, 51.5074), 'London')],
+ ... 'font': font_file,
+ ... 'symbol': 'circle', 'ptsize': 16,
+ ... 'outline': 'black', 'width': 3,
+ ... 'fill': 'red', 'fill_opacity': 128,
+ ... 'box_outline': 'blue', 'box_linewidth': 0.5,
+ ... 'box_fill': 'yellow', 'box_opacity': 200}
+
+ >>> overlays = {'coasts': {'outline': 'black', 'level': 4, 'resolution': 'l'},
+ ... 'borders': {'outline': 'black', 'width': 3, 'level': 1, 'resolution': 'c'},
+ ... 'points': points}
+
+ >>> img = cw.add_overlay_from_dict(overlays, area_def)
+
+ >>> img.save('./add_overlays_from_dict.png')
+
+.. _PIL: http://www.pythonware.com/products/pil/
=====================================
pycoast/__init__.py
=====================================
@@ -1,6 +1,5 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
-
from .cw_pil import ContourWriterPIL
from .cw_agg import ContourWriterAGG
from pycoast.cw_base import get_resolution_from_area
@@ -8,6 +7,7 @@ from .version import get_versions
__version__ = get_versions()['version']
del get_versions
+
class ContourWriter(ContourWriterPIL):
"""Writer wrapper for deprecation warning.
=====================================
pycoast/cw_agg.py
=====================================
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
# pycoast, Writing of coastlines, borders and rivers to images in Python
#
-# Copyright (C) 2011-2018 PyCoast Developers
+# Copyright (C) 2011-2020 PyCoast Developers
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -52,32 +52,39 @@ class ContourWriterAGG(ContourWriterBase):
pen = aggdraw.Pen(kwargs['outline'],
kwargs['width'],
kwargs['outline_opacity'])
- if kwargs['fill'] is None:
- fill_opacity = 0
- else:
- fill_opacity = kwargs['fill_opacity']
+
+ fill_opacity = kwargs.get('fill_opacity', 255) if kwargs['fill'] else 0
+
brush = aggdraw.Brush(kwargs['fill'], fill_opacity)
draw.polygon(coordinates, pen, brush)
def _draw_rectangle(self, draw, coordinates, **kwargs):
"""Draw rectangle."""
- pen = aggdraw.Pen(kwargs['outline'])
+ pen = aggdraw.Pen(kwargs['outline'],
+ kwargs['width'],
+ kwargs['outline_opacity'])
+
+ fill_opacity = kwargs.get('fill_opacity', 255) if kwargs['fill'] else 0
- fill_opacity = kwargs.get('fill_opacity', 255)
brush = aggdraw.Brush(kwargs['fill'], fill_opacity)
+
draw.rectangle(coordinates, pen, brush)
def _draw_ellipse(self, draw, coordinates, **kwargs):
"""Draw ellipse."""
- pen = aggdraw.Pen(kwargs['outline'])
+ pen = aggdraw.Pen(kwargs['outline'],
+ kwargs['width'],
+ kwargs['outline_opacity'])
+
+ fill_opacity = kwargs.get('fill_opacity', 255) if kwargs['fill'] else 0
- fill_opacity = kwargs.get('fill_opacity', 255)
brush = aggdraw.Brush(kwargs['fill'], fill_opacity)
+
draw.ellipse(coordinates, brush, pen)
def _draw_text_box(self, draw, text_position, text, font, outline,
- box_outline, box_opacity):
- """Add text box in xy."""
+ box_outline, box_opacity, **kwargs):
+ """Add a text box at position (x,y)."""
if box_outline is not None:
text_size = draw.textsize(text, font)
@@ -88,9 +95,12 @@ class ContourWriterAGG(ContourWriterBase):
yLR = yUL + text_size[1]
box_size = (xUL, yUL, xLR, yLR)
+ width = kwargs.get('box_linewidth', 1)
+ fill = kwargs.get('box_fill', None)
+
self._draw_rectangle(
- draw, box_size, fill=box_outline, fill_opacity=box_opacity,
- outline=box_outline)
+ draw, box_size, outline=box_outline, width=width,
+ outline_opacity=box_opacity, fill=fill, fill_opacity=box_opacity)
self._draw_text(draw, text_position, text, font, align="no")
@@ -101,6 +111,40 @@ class ContourWriterAGG(ContourWriterBase):
kwargs['outline_opacity'])
draw.line(coordinates, pen)
+ def _draw_asterisk(self, draw, pt_size, coordinate, **kwargs):
+ """Draw a asterisk sign '*' at the given coordinate. """
+ half_ptsize = int(round(pt_size / 2.))
+ x, y = coordinate
+
+ outline = kwargs.get('outline', 'white')
+ width = kwargs.get('width', 1.)
+ outline_opacity = kwargs.get('outline_opacity', 255)
+
+ # draw '|'
+ (x_bm, y_bm) = (x, y - half_ptsize) # bottom middle point
+ (x_tm, y_tm) = (x, y + half_ptsize) # top middle point
+ self._draw_line(draw, [(x_bm, y_bm), (x_tm, y_tm)],
+ outline=outline, width=width,
+ outline_opacity=outline_opacity)
+ # draw '-'
+ (x_lm, y_lm) = (x - half_ptsize, y) # left middle point
+ (x_rm, y_rm) = (x + half_ptsize, y) # right middle point
+ self._draw_line(draw, [(x_lm, y_lm), (x_rm, y_rm)],
+ outline=outline, width=width,
+ outline_opacity=outline_opacity)
+ # draw '/'
+ (x_bl, y_bl) = (x - half_ptsize, y - half_ptsize) # bottom left point
+ (x_tr, y_tr) = (x + half_ptsize, y + half_ptsize) # top right point
+ self._draw_line(draw, [(x_bl, y_bl), (x_tr, y_tr)],
+ outline=outline, width=width,
+ outline_opacity=outline_opacity)
+ # draw '\'
+ (x_tl, y_tl) = (x - half_ptsize, y + half_ptsize) # top left point
+ (x_br, y_br) = (x + half_ptsize, y - half_ptsize) # bottom right point
+ self._draw_line(draw, [(x_tl, y_tl), (x_br, y_br)],
+ outline=outline, width=width,
+ outline_opacity=outline_opacity)
+
def _finalize(self, draw):
"""Flush the AGG image object."""
draw.flush()
@@ -274,7 +318,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 a PIL image object
+ """Add a lon-lat grid to a PIL image object.
:Parameters:
image : object
@@ -520,7 +564,7 @@ class ContourWriterAGG(ContourWriterBase):
"""
image = Image.open(filename)
- image = image.convert("RGBA")
+ 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,
@@ -592,7 +636,7 @@ class ContourWriterAGG(ContourWriterBase):
"""
image = Image.open(filename)
- image = image.convert("RGBA")
+ 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
=====================================
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
# pycoast, Writing of coastlines, borders and rivers to images in Python
#
-# Copyright (C) 2011-2018 PyCoast Developers
+# Copyright (C) 2011-2020 PyCoast Developers
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -557,7 +557,10 @@ class ContourWriterBase(object):
prj = Proj(proj_def)
# Calculate min and max lons and lats of interest
- lon_min, lon_max, lat_min, lat_max = _get_lon_lat_bounding_box(area_extent, x_size, y_size, prj)
+ lon_min, lon_max, lat_min, lat_max = _get_lon_lat_bounding_box(area_extent,
+ x_size,
+ y_size,
+ prj)
# Iterate through shapes
for shape in shapes:
@@ -695,7 +698,7 @@ class ContourWriterBase(object):
logger.error("Error in %s", str(config_file))
raise
- SECTIONS = ['cache', 'coasts', 'rivers', 'borders', 'cities', 'grid']
+ SECTIONS = ['cache', 'coasts', 'rivers', 'borders', 'cities', 'points', 'grid']
overlays = {}
for section in config.sections():
if section in SECTIONS:
@@ -709,7 +712,8 @@ class ContourWriterBase(object):
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.
+ """Create and return a transparent image adding all the overlays contained in
+ the `overlays` dict.
:Parameters:
overlays : dict
@@ -717,24 +721,31 @@ class ContourWriterBase(object):
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.
+ The latest time allowed for cache the cache file. If the cache
+ file is older than this (mtime), the cache should be
+ regenerated. Defaults to 0 meaning always reuse the cached
+ file if it exists. Requires "cache" to be configured in the
+ provided dictionary (see below).
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*.
+ a new image is created, otherwise the provide background is
+ used and changed *in place*.
The keys in `overlays` that will be taken into account are:
- cache, coasts, rivers, borders, cities, grid
+ cache, coasts, rivers, borders, cities, points, 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.
+ 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, add_points). For cache, two parameters are
+ configurable:
+
+ - `file`: specify the directory and the prefix
+ of the file to save the caches decoration to (for example
+ /var/run/black_coasts_red_borders)
+ - `regenerate`: True or False (default) to force the overwriting
+ of an already cached file.
"""
@@ -745,7 +756,7 @@ class ContourWriterBase(object):
area_def.area_id + '.png')
try:
- config_time = cache_epoch
+ config_time = cache_epoch or 0
cache_time = os.path.getmtime(cache_file)
# Cache file will be used only if it's newer than config file
if ((config_time is not None and config_time < cache_time)
@@ -767,6 +778,9 @@ class ContourWriterBase(object):
else:
foreground = Image.new('RGBA', (x_size, y_size), (0, 0, 0, 0))
+ is_agg = self._draw_module == "AGG"
+
+ # Coasts, rivers, borders
default_resolution = get_resolution_from_area(area_def)
DEFAULT = {'level': 1,
@@ -779,14 +793,10 @@ class ContourWriterBase(object):
'y_offset': 0,
'resolution': default_resolution}
- is_agg = self._draw_module == "AGG"
-
- # Coasts, rivers, borders
for section, fun in zip(['coasts', 'rivers', 'borders'],
[self.add_coastlines,
self.add_rivers,
self.add_borders]):
-
if section in overlays:
params = DEFAULT.copy()
@@ -821,7 +831,30 @@ class ContourWriterBase(object):
self.add_cities(foreground, area_def, citylist, font_file,
font_size, pt_size, outline, box_outline,
box_opacity)
+ # Points management
+ if 'points' in overlays:
+ DEFAULT_FONTSIZE = 12
+ DEFAULT_SYMBOL = 'circle'
+ DEFAULT_PTSIZE = 6
+ DEFAULT_OUTLINE = 'white'
+ DEFAULT_FILL = None
+
+ params = overlays['points'].copy()
+ points_list = list(params.pop('points_list'))
+ font_file = params.pop('font')
+ font_size = int(params.pop('font_size', DEFAULT_FONTSIZE))
+
+ symbol = params.pop('symbol', DEFAULT_SYMBOL)
+ ptsize = int(params.pop('ptsize', DEFAULT_PTSIZE))
+
+ outline = params.pop('outline', DEFAULT_OUTLINE)
+ fill = params.pop('fill', DEFAULT_FILL)
+
+ self.add_points(foreground, area_def, points_list, font_file, font_size,
+ symbol, ptsize, outline, fill, **params)
+
+ # Grids overlay
if 'grid' in overlays:
lon_major = float(overlays['grid'].get('lon_major', 10.0))
lat_major = float(overlays['grid'].get('lat_major', 10.0))
@@ -867,7 +900,8 @@ class ContourWriterBase(object):
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.
+ """Create and return a transparent image adding all the overlays contained in
+ a configuration file.
:Parameters:
config_file : str
@@ -877,7 +911,8 @@ class ContourWriterBase(object):
"""
overlays = self._config_to_dict(config_file)
- return self.add_overlay_from_dict(overlays, area_def, os.path.getmtime(config_file), background)
+ 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):
@@ -888,6 +923,7 @@ class ContourWriterBase(object):
db_root_path = self.db_root_path
if db_root_path is None:
raise ValueError("'db_root_path' must be specified to use this method")
+
draw = self._get_canvas(image)
# read shape file with points
@@ -924,24 +960,138 @@ class ContourWriterBase(object):
except ValueError as exc:
logger.debug("Point not added (%s)", str(exc))
else:
-
# add_dot
if ptsize is not None:
dot_box = [x - ptsize, y - ptsize,
x + ptsize, y + ptsize]
self._draw_ellipse(
draw, dot_box, fill=outline, outline=outline)
- text_position = [x + 9, y - 5] # FIX ME
+ text_position = [x + 9, y - 5] # FIXME
else:
text_position = [x, y]
# add text_box
- self._draw_text_box(draw, text_position, city_name, font,
- outline, box_outline, box_opacity)
+ self._draw_text_box(draw, text_position, city_name, font, outline,
+ box_outline, box_opacity)
logger.info("%s added", str(city_name))
self._finalize(draw)
+ def add_points(self, image, area_def, points_list, font_file, font_size=12,
+ symbol='circle', ptsize=6, outline='black', fill='white', **kwargs):
+ """Add a symbol and/or text at the point(s) of interest to a PIL image object.
+
+ :Parameters:
+ image : object
+ PIL image object
+ area_def : object
+ Area Definition of the provided image
+ points_list : list [((lon, lat), desc)]
+ | a list of points defined with (lon, lat) in float and a desc in string
+ | [((lon1, lat1), desc1), ((lon2, lat2), desc2)]
+ | lon : float
+ | longitude of a point
+ | lat : float
+ | latitude of a point
+ | desc : str
+ | description of a point
+ font_file : str
+ Path to font file
+ font_size : int
+ Size of font
+ symbol : string
+ type of symbol, one of the elelment from the list
+ ['circle', 'square', 'asterisk']
+ ptsize : int
+ Size of the point.
+ outline : str or (R, G, B), optional
+ Line color of the symbol
+ fill : str or (R, G, B), optional
+ Filling color of the symbol
+
+ :Optional keyword arguments:
+ width : float
+ Line width of the symbol
+ outline_opacity : int, optional {0; 255}
+ Opacity of the line of the symbol.
+ fill_opacity : int, optional {0; 255}
+ Opacity of the filling of the symbol
+ box_outline : str or (R, G, B), optional
+ Line color of the textbox borders.
+ box_linewidth : float
+ Line width of the the borders of the textbox
+ box_fill : str or (R, G, B), optional
+ Filling color of the background of the textbox.
+ box_opacity : int, optional {0; 255}
+ Opacity of the background filling of the textbox.
+ """
+ try:
+ from pyresample.geometry import AreaDefinition
+ except ImportError:
+ raise ImportError("Missing required 'pyresample' module, please install it.")
+
+ if not isinstance(area_def, AreaDefinition):
+ raise ValueError("Expected 'area_def' is an instance of AreaDefinition object")
+
+ draw = self._get_canvas(image)
+
+ # Iterate through points list
+ for point in points_list:
+ (lon, lat), desc = point
+ try:
+ x, y = area_def.get_xy_from_lonlat(lon, lat)
+ except ValueError:
+ logger.info("Point %s is out of the area, it will not be added to the image.",
+ str((lon, lat)))
+ else:
+ if ptsize != 0:
+ half_ptsize = int(round(ptsize / 2.))
+
+ dot_box = [x - half_ptsize, y - half_ptsize,
+ x + half_ptsize, y + half_ptsize]
+
+ width = kwargs.get('width', 1.)
+ outline_opacity = kwargs.get('outline_opacity', 255)
+ fill_opacity = kwargs.get('fill_opacity', 0)
+
+ # draw the symbol at the (x, y) position
+ if symbol == 'circle': # a 'circle' or a 'dot' i.e circle with fill
+ self._draw_ellipse(draw, dot_box,
+ outline=outline, width=width,
+ outline_opacity=outline_opacity,
+ fill=fill, fill_opacity=fill_opacity)
+ elif symbol == 'square':
+ self._draw_rectangle(draw, dot_box,
+ outline=outline, width=width,
+ outline_opacity=outline_opacity,
+ fill=fill, fill_opacity=fill_opacity)
+ elif symbol == 'asterisk': # an '*' sign
+ self._draw_asterisk(draw, ptsize, (x, y),
+ outline=outline, width=width,
+ outline_opacity=outline_opacity)
+ else:
+ raise ValueError("Unsupported symbol type: " + str(symbol))
+
+ elif desc is None:
+ logger.error("'ptsize' is 0 and 'desc' is None, nothing will be added to the image.")
+
+ if desc is not None:
+ text_position = [x + ptsize, y] # draw the text box next to the point
+ font = self._get_font(outline, font_file, font_size)
+
+ new_kwargs = kwargs.copy()
+
+ box_outline = new_kwargs.pop('box_outline', 'white')
+ box_opacity = new_kwargs.pop('box_opacity', 0)
+
+ # add text_box
+ self._draw_text_box(draw, text_position, desc, font, outline,
+ box_outline, box_opacity, **new_kwargs)
+
+ logger.debug("Point %s has been added to the image", str((lon, lat)))
+
+ self._finalize(draw)
+
def _get_lon_lat_bounding_box(area_extent, x_size, y_size, prj):
"""Get extreme lon and lat values
=====================================
pycoast/cw_pil.py
=====================================
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
# pycoast, Writing of coastlines, borders, and rivers to images in Python
#
-# Copyright (C) 2011-2018 PyCoast Developers
+# Copyright (C) 2011-2020 PyCoast Developers
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -61,11 +61,11 @@ class ContourWriterPIL(ContourWriterBase):
draw.rectangle(coordinates, fill=kwargs['fill'], outline=kwargs['outline'])
def _draw_text_box(self, draw, text_position, text, font, outline,
- box_outline, box_opacity):
+ box_outline, box_opacity, **kwargs):
"""Add text box in xy."""
if box_outline is not None:
logger.warning(
- "Box background will not added; please install aggdraw lib")
+ "Box background will not be added; please use ContourWriterAGG module")
self._draw_text(
draw, text_position, text, font, align="no", fill=outline)
@@ -74,6 +74,33 @@ class ContourWriterPIL(ContourWriterBase):
"""Draw line."""
draw.line(coordinates, fill=kwargs['outline'])
+ def _draw_asterisk(self, draw, pt_size, coordinate, **kwargs):
+ """Draw a asterisk sign '*' center at the given coordinate """
+ half_ptsize = int(round(pt_size / 2.))
+ x, y = coordinate
+
+ outline = kwargs.get('outline', 'white')
+
+ # draw '|'
+ (x_bm, y_bm) = (x, y - half_ptsize) # bottom middle point
+ (x_tm, y_tm) = (x, y + half_ptsize) # top middle point
+ self._draw_line(draw, [(x_bm, y_bm), (x_tm, y_tm)], outline=outline)
+
+ # draw '-'
+ (x_lm, y_lm) = (x - half_ptsize, y) # left middle point
+ (x_rm, y_rm) = (x + half_ptsize, y) # right middle point
+ self._draw_line(draw, [(x_lm, y_lm), (x_rm, y_rm)], outline=outline)
+
+ # draw '/'
+ (x_bl, y_bl) = (x - half_ptsize, y - half_ptsize) # bottom left point
+ (x_tr, y_tr) = (x + half_ptsize, y + half_ptsize) # top right point
+ self._draw_line(draw, [(x_bl, y_bl), (x_tr, y_tr)], outline=outline)
+
+ # draw '\'
+ (x_tl, y_tl) = (x - half_ptsize, y + half_ptsize) # top left point
+ (x_br, y_br) = (x + half_ptsize, y - half_ptsize) # bottom right point
+ self._draw_line(draw, [(x_tl, y_tl), (x_br, y_br)], outline=outline)
+
def add_shapefile_shapes(self, image, area_def, filename, feature_type=None,
fill=None, outline='white',
x_offset=0, y_offset=0):
=====================================
pycoast/tests/coasts_and_grid_agg.ini
=====================================
@@ -12,7 +12,7 @@ lat_minor = 2.0
minor_outline = blue
outline = blue
lon_placement = tblr
-lat_placement =
-font = pycoast/tests/test_data/DejaVuSerif.ttf
+lat_placement = ''
+font = 'pycoast/tests/test_data/DejaVuSerif.ttf'
font_size = 10
=====================================
pycoast/tests/grid_nh_cfg_agg.png
=====================================
Binary files /dev/null and b/pycoast/tests/grid_nh_cfg_agg.png differ
=====================================
pycoast/tests/nh_points_agg.ini
=====================================
@@ -0,0 +1,25 @@
+[coasts]
+level = 4
+resolution = l
+outline = black
+
+[borders]
+level = 1
+outline = black
+width = 3
+resolution = c
+
+[points]
+points_list = ((2.3522, 48.8566), 'Paris'), ((0.1278, 51.5074), 'London')
+font = 'pycoast/tests/test_data/DejaVuSerif.ttf'
+symbol = circle
+ptsize = 16
+outline = black
+width = 3
+fill = red
+fill_opacity = 128
+box_outline = blue
+box_linewidth = 0.5
+box_fill = yellow
+box_opacity = 200
+
=====================================
pycoast/tests/nh_points_agg.png
=====================================
Binary files /dev/null and b/pycoast/tests/nh_points_agg.png differ
=====================================
pycoast/tests/nh_points_cfg_pil.png
=====================================
Binary files /dev/null and b/pycoast/tests/nh_points_cfg_pil.png differ
=====================================
pycoast/tests/nh_points_pil.ini
=====================================
@@ -0,0 +1,19 @@
+[coasts]
+level = 4
+resolution = l
+outline = black
+
+[borders]
+level = 1
+outline = black
+width = 3
+resolution = c
+
+[points]
+points_list = ((13.4050, 52.5200), 'Berlin'), ((12.4964, 41.9028), 'Rome')
+font = 'pycoast/tests/test_data/DejaVuSerif.ttf'
+symbol = square
+ptsize = 6
+outline = red
+fill = yellow
+box_outline = black
=====================================
pycoast/tests/nh_points_pil.png
=====================================
Binary files /dev/null and b/pycoast/tests/nh_points_pil.png differ
=====================================
pycoast/tests/test_pycoast.py
=====================================
@@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
# pycoast, Writing of coastlines, borders and rivers to images in Python
#
-# Copyright (C) 2011-2018 PyCoast Developers
+# Copyright (C) 2011-2020 PyCoast Developers
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
@@ -24,6 +24,7 @@ import numpy as np
from PIL import Image, ImageFont
import time
+
def tmp(f):
f.tmp = True
return f
@@ -357,6 +358,45 @@ class TestPIL(TestPycoast):
self.assertTrue(fft_metric(grid_data, res),
'Writing of nh polygons failed')
+ def test_add_points_pil(self):
+ from pycoast import ContourWriterPIL
+ from pyresample.geometry import AreaDefinition
+
+ font_file = os.path.join(os.path.dirname(__file__), 'test_data',
+ 'DejaVuSerif.ttf')
+ grid_img = Image.open(os.path.join(os.path.dirname(__file__),
+ 'nh_points_pil.png'))
+ grid_data = np.array(grid_img)
+
+ img = Image.new('RGB', (1024, 1024), (255, 255, 255))
+
+ proj4_string = '+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m'
+ area_extent = (-5326849.0625, -5326849.0625,
+ 5326849.0625, 5326849.0625)
+
+ area_def = AreaDefinition('nh', 'nh', 'nh', proj4_string,
+ 1024, 1024, area_extent)
+
+ cw = ContourWriterPIL(gshhs_root_dir)
+ cw.add_coastlines(img, area_def, outline='black', resolution='l',
+ level=4)
+ cw.add_borders(img, area_def, outline='black', level=1,
+ resolution='c')
+
+ points_list = [((13.4050, 52.5200), 'Berlin')]
+ cw.add_points(img, area_def, points_list=points_list, font_file=font_file,
+ symbol='asterisk', ptsize=6, outline='red',
+ box_outline='black')
+
+ points_list = [((12.4964, 41.9028), 'Rome')]
+ cw.add_points(img, area_def, points_list=points_list, font_file=font_file,
+ symbol='square', ptsize=6, outline='blue', fill='yellow',
+ box_outline='black')
+
+ res = np.array(img)
+ self.assertTrue(fft_metric(grid_data, res),
+ 'Writing of nh points failed')
+
def test_add_shapefile_shapes(self):
from pycoast import ContourWriterPIL
grid_img = Image.open(os.path.join(os.path.dirname(__file__),
@@ -412,6 +452,34 @@ class TestPIL(TestPycoast):
self.assertTrue(fft_metric(grid_data, res),
'Writing of nh grid failed')
+ def test_config_file_points_and_borders_pil(self):
+ from pycoast import ContourWriterPIL
+ from pyresample.geometry import AreaDefinition
+
+ config_file = os.path.join(os.path.dirname(__file__),
+ 'nh_points_pil.ini')
+
+ grid_img = Image.open(os.path.join(os.path.dirname(__file__),
+ 'nh_points_cfg_pil.png'))
+ grid_data = np.array(grid_img)
+
+ img = Image.new('RGB', (1024, 1024), (255, 255, 255))
+
+ proj4_string = '+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m'
+ area_extent = (-5326849.0625, -5326849.0625,
+ 5326849.0625, 5326849.0625)
+
+ area_def = AreaDefinition('nh', 'nh', 'nh', proj4_string,
+ 1024, 1024, area_extent)
+
+ cw = ContourWriterPIL(gshhs_root_dir)
+
+ cw.add_overlay_from_config(config_file, area_def, img)
+
+ res = np.array(img)
+ self.assertTrue(fft_metric(grid_data, res),
+ 'Writing of nh points failed')
+
class TestPILAGG(TestPycoast):
@@ -517,9 +585,9 @@ class TestPILAGG(TestPycoast):
cw = ContourWriterAGG(gshhs_root_dir)
cw.add_coastlines(img, area_def, resolution='l', level=4)
- font = aggdraw.Font('blue', os.path.join(os.path.dirname(__file__),
- 'test_data',
- 'DejaVuSerif.ttf'), size=16,
+ font = aggdraw.Font('blue',
+ os.path.join(os.path.dirname(__file__), 'test_data', 'DejaVuSerif.ttf'),
+ size=16,
opacity=200)
cw.add_grid(img, area_def, (10.0, 10.0), (2.0, 2.0), font=font,
outline='blue', outline_opacity=255, width=1.0,
@@ -644,6 +712,43 @@ class TestPILAGG(TestPycoast):
self.assertTrue(fft_metric(grid_data, res),
'Writing of nh polygons failed')
+ def test_add_points_agg(self):
+ from pycoast import ContourWriterAGG
+ from pyresample.geometry import AreaDefinition
+
+ font_file = os.path.join(os.path.dirname(__file__), 'test_data',
+ 'DejaVuSerif.ttf')
+
+ grid_img = Image.open(os.path.join(os.path.dirname(__file__),
+ 'nh_points_agg.png'))
+ grid_data = np.array(grid_img)
+
+ img = Image.new('RGB', (1024, 1024), (255, 255, 255))
+ proj4_string = '+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m'
+ area_extent = (-5326849.0625, -5326849.0625,
+ 5326849.0625, 5326849.0625)
+
+ area_def = AreaDefinition('nh', 'nh', 'nh', proj4_string,
+ 1024, 1024, area_extent)
+
+ cw = ContourWriterAGG(gshhs_root_dir)
+ cw.add_coastlines(img, area_def, outline='black', resolution='l',
+ level=4)
+ cw.add_borders(img, area_def, outline='black', width=3, level=1,
+ resolution='c')
+
+ points_list = [((2.3522, 48.8566), 'Paris'),
+ ((0.1278, 51.5074), 'London')]
+ cw.add_points(img, area_def, points_list=points_list, font_file=font_file,
+ symbol='circle', ptsize=16,
+ outline='black', width=3,
+ fill='red', fill_opacity=128,
+ box_outline='blue', box_linewidth=0.5,
+ box_fill='yellow', box_opacity=200)
+
+ res = np.array(img)
+ self.assertTrue(fft_metric(grid_data, res), 'Writing of nh points failed')
+
def test_add_shapefile_shapes_agg(self):
from pycoast import ContourWriterAGG
grid_img = Image.open(os.path.join(os.path.dirname(__file__),
@@ -677,31 +782,59 @@ 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")
+# @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_nh_cfg_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_def = AreaDefinition('nh', 'nh', 'nh', proj_dict, 850, 850,
area_extent)
cw = ContourWriterAGG(gshhs_root_dir)
overlay = cw.add_overlay_from_config(overlay_config, area_def)
- img = Image.new('RGB', (425, 425))
+ img = Image.new('RGB', (850, 850), (255, 255, 255))
img.paste(overlay, mask=overlay)
res = np.array(img)
self.assertTrue(fft_metric(grid_data, res),
'Writing of nh grid failed')
+ def test_config_file_points_and_borders_agg(self):
+ from pycoast import ContourWriterAGG
+ from pyresample.geometry import AreaDefinition
+
+ config_file = os.path.join(os.path.dirname(__file__),
+ 'nh_points_agg.ini')
+
+ grid_img = Image.open(os.path.join(os.path.dirname(__file__),
+ 'nh_points_agg.png'))
+ grid_data = np.array(grid_img)
+
+ img = Image.new('RGB', (1024, 1024), (255, 255, 255))
+
+ proj4_string = '+proj=laea +lat_0=90 +lon_0=0 +a=6371228.0 +units=m'
+ area_extent = (-5326849.0625, -5326849.0625,
+ 5326849.0625, 5326849.0625)
+
+ area_def = AreaDefinition('nh', 'nh', 'nh', proj4_string,
+ 1024, 1024, area_extent)
+
+ cw = ContourWriterAGG(gshhs_root_dir)
+
+ cw.add_overlay_from_config(config_file, area_def, img)
+
+ res = np.array(img)
+ self.assertTrue(fft_metric(grid_data, res),
+ 'Add points with agg module from a config file failed')
+
def test_coastlines_convert_to_rgba_agg(self):
from pycoast import ContourWriterAGG
proj4_string = \
@@ -784,30 +917,37 @@ class TestFromConfig(TestPycoast):
'borders': {'outline': (255, 0, 0), 'resolution': 'c'},
'rivers': {'outline': 'blue', 'resolution': 'c', 'level': 5}}
+ # Create the original cache file
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))
+ mtime = os.path.getmtime(cache_filename)
- current_time = time.time()
-
- img = cw.add_overlay_from_dict(overlays, area_def, current_time)
+ # Reuse the generated cache file
+ 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))
+ self.assertEqual(os.path.getmtime(cache_filename), mtime)
+ # Regenerate cache file
+ current_time = time.time()
+ 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)
-
+ 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)
+ cw.add_overlay_from_dict(overlays, area_def)
self.assertNotEqual(os.path.getmtime(cache_filename), mtime)
self.assertTrue(fft_metric(euro_data, res),
=====================================
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.3.2)"
- git_full = "eb35e257be4bc570dc6f08fb044aca259fe71890"
- git_date = "2019-12-06 14:39:30 -0600"
+ git_refnames = " (tag: v1.4.0)"
+ git_full = "c96f05892af2bdc7148dbfdea7930385ca39aa1e"
+ git_date = "2020-06-08 14:34:54 -0500"
keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
return keywords
View it on GitLab: https://salsa.debian.org/debian-gis-team/pycoast/-/compare/a16e55a047be4ceb174cfc14880894e5608aaa85...7632b06fcd8941d70bc0efb763c5c244ba2ce4ee
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/pycoast/-/compare/a16e55a047be4ceb174cfc14880894e5608aaa85...7632b06fcd8941d70bc0efb763c5c244ba2ce4ee
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/20200611/1dd14e18/attachment-0001.html>
More information about the Pkg-grass-devel
mailing list