[Git][debian-gis-team/pycoast][master] 13 commits: Update the list of excluded files in d/copyright
Antonio Valentino (@antonio.valentino)
gitlab at salsa.debian.org
Sun Aug 29 18:56:23 BST 2021
Antonio Valentino pushed to branch master at Debian GIS Project / pycoast
Commits:
35b39da7 by Antonio Valentino at 2021-08-29T15:50:55+00:00
Update the list of excluded files in d/copyright
- - - - -
0eddb7c4 by Antonio Valentino at 2021-08-29T15:52:04+00:00
New upstream version 1.5.0+dfsg
- - - - -
f817e667 by Antonio Valentino at 2021-08-29T15:52:39+00:00
Update upstream source from tag 'upstream/1.5.0+dfsg'
Update to upstream version '1.5.0+dfsg'
with Debian dir 22fb15378d3f8bbff1ed35df7c5b437f12533554
- - - - -
5a70caeb by Antonio Valentino at 2021-08-29T15:54:22+00:00
New upstream release
- - - - -
6eda0270 by Antonio Valentino at 2021-08-29T15:55:28+00:00
Refresh all patches
- - - - -
ec9fbcb8 by Antonio Valentino at 2021-08-29T15:59:07+00:00
Update copyright dates
- - - - -
190c80a0 by Antonio Valentino at 2021-08-29T16:06:00+00:00
Update dependencies in d/control
- - - - -
8bd50fa5 by Antonio Valentino at 2021-08-29T16:51:25+00:00
Fix pytest args in d/ruels
- - - - -
fc25215e by Antonio Valentino at 2021-08-29T16:51:53+00:00
Add fonts-dejavu-core to build dependencies (for testing)
- - - - -
e4d11a32 by Antonio Valentino at 2021-08-29T17:11:16+00:00
New 0002-Fix-font-path.patch
- - - - -
70005567 by Antonio Valentino at 2021-08-29T17:15:59+00:00
Typo
- - - - -
0e07e9df by Antonio Valentino at 2021-08-29T17:17:19+00:00
No need to forward 0001-Skip-tests-that-use-shapes.patch
- - - - -
0617d6c1 by Antonio Valentino at 2021-08-29T17:23:35+00:00
Set distribution to unstable
- - - - -
25 changed files:
- − .bumpversion.cfg
- .github/PULL_REQUEST_TEMPLATE.md
- + .github/workflows/ci.yaml
- + .github/workflows/deploy-sdist.yaml
- − .travis.yml
- CHANGELOG.md
- README.rst
- RELEASING.md
- + continuous_integration/environment.yaml
- debian/changelog
- debian/control
- debian/copyright
- debian/patches/0001-Skip-tests-that-use-shapes.patch
- + debian/patches/0002-Fix-font-path.patch
- debian/patches/series
- debian/rules
- pycoast/__init__.py
- pycoast/cw_agg.py
- pycoast/cw_base.py
- pycoast/cw_pil.py
- pycoast/tests/__init__.py
- pycoast/tests/test_pycoast.py
- pycoast/version.py
- setup.cfg
- setup.py
Changes:
=====================================
.bumpversion.cfg deleted
=====================================
@@ -1,7 +0,0 @@
-[bumpversion]
-current_version = 1.1.0
-commit = True
-tag = True
-
-[bumpversion:file:pycoast/version.py]
-
=====================================
.github/PULL_REQUEST_TEMPLATE.md
=====================================
@@ -1,9 +1,7 @@
-<!-- Please make the PR against the `master` branch. -->
-
<!-- Describe what your PR does, and why -->
- [ ] Closes #xxxx <!-- remove if there is no corresponding issue, which should only be the case for minor changes -->
- [ ] Tests added <!-- for all bug fixes or enhancements -->
- [ ] Tests passed <!-- for all non-documentation changes) -->
- - [ ] Passes ``git diff origin/master **/*py | flake8 --diff`` <!-- remove if you did not edit any Python files -->
+ - [ ] Passes ``git diff origin/main **/*py | flake8 --diff`` <!-- remove if you did not edit any Python files -->
- [ ] Fully documented <!-- remove if this change should not be visible to users, e.g., if it is an internal clean-up, or if this is part of a larger project that will be documented later -->
=====================================
.github/workflows/ci.yaml
=====================================
@@ -0,0 +1,135 @@
+name: CI
+
+on: [push, pull_request]
+
+jobs:
+ lint:
+ name: lint and style checks
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout source
+ uses: actions/checkout at v2
+ - name: Set up Python
+ uses: actions/setup-python at v2
+ with:
+ python-version: 3.9
+ - name: Install dependencies
+ run: |
+ python -m pip install --upgrade pip
+ pip install flake8 flake8-docstrings flake8-debugger flake8-bugbear pytest
+ - name: Install Pycoast
+ run: |
+ pip install -e .
+ - name: Run linting
+ run: |
+ flake8 pycoast/
+
+ website:
+ name: build website
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout source
+ uses: actions/checkout at v2
+ with:
+ fetch-depth: 0
+
+# - name: Set up Python 3.9
+# uses: actions/setup-python at v2
+# with:
+# python-version: 3.9
+ - name: Setup Conda Environment
+ uses: conda-incubator/setup-miniconda at v2
+ with:
+ miniforge-variant: Mambaforge
+ miniforge-version: latest
+ python-version: 3.9
+ use-mamba: true
+ environment-file: continuous_integration/environment.yaml
+ activate-environment: test-environment
+
+ - name: Install Pycoast
+ shell: bash -l {0}
+ run: |
+ python -m pip install --no-deps -e .
+
+ - name: Run Sphinx Build
+ shell: bash -l {0}
+ run: |
+ cd docs; \
+ make html SPHINXOPTS="-W"
+
+ test:
+ runs-on: ${{ matrix.os }}
+ continue-on-error: ${{ matrix.experimental }}
+ needs: [lint]
+ strategy:
+ fail-fast: true
+ matrix:
+ os: ["windows-latest", "ubuntu-latest", "macos-latest"]
+ python-version: ["3.7", "3.9"]
+ experimental: [false]
+ include:
+ - python-version: "3.9"
+ os: "ubuntu-latest"
+ experimental: true
+
+ env:
+ PYTHON_VERSION: ${{ matrix.python-version }}
+ OS: ${{ matrix.os }}
+ UNSTABLE: ${{ matrix.experimental }}
+ ACTIONS_ALLOW_UNSECURE_COMMANDS: true
+
+ steps:
+ - name: Checkout source
+ uses: actions/checkout at v2
+
+# - name: Set up Python ${{ matrix.python-version }}
+# uses: actions/setup-python at v2
+# with:
+# python-version: ${{ matrix.python-version }}
+
+ - name: Setup Conda Environment
+ uses: conda-incubator/setup-miniconda at v2
+ with:
+ miniforge-variant: Mambaforge
+ miniforge-version: latest
+ python-version: ${{ matrix.python-version }}
+ use-mamba: true
+ environment-file: continuous_integration/environment.yaml
+
+ - name: Install unstable dependencies
+ shell: bash -l {0}
+ if: matrix.experimental == true
+ run: |
+ python -m pip install \
+ --index-url https://pypi.anaconda.org/scipy-wheels-nightly/simple/ \
+ --trusted-host pypi.anaconda.org \
+ --no-deps --pre --upgrade \
+ numpy;
+
+ - name: Install pycoast
+ shell: bash -l {0}
+ run: |
+ python -m pip install --no-deps -e .
+
+ - name: Run unit tests
+ shell: bash -l {0}
+ run: |
+ pytest --cov=pycoast pycoast/tests
+
+ - name: Coveralls Parallel
+ uses: AndreMiras/coveralls-python-action at develop
+ with:
+ flag-name: run-${{ matrix.test_number }}
+ parallel: true
+ if: runner.os == 'Linux'
+
+ coveralls:
+ needs: [test]
+ runs-on: ubuntu-latest
+ steps:
+ - name: Coveralls Finished
+ uses: AndreMiras/coveralls-python-action at develop
+ with:
+ parallel-finished: true
+
=====================================
.github/workflows/deploy-sdist.yaml
=====================================
@@ -0,0 +1,25 @@
+name: Deploy sdist
+
+on:
+ release:
+ types:
+ - published
+
+jobs:
+ test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout source
+ uses: actions/checkout at v2
+
+ - name: Create sdist
+ shell: bash -l {0}
+ run: python setup.py sdist
+
+ - name: Publish package to PyPI
+ if: github.event.action == 'published'
+ uses: pypa/gh-action-pypi-publish at v1.4.1
+ with:
+ user: __token__
+ password: ${{ secrets.pypi_password }}
\ No newline at end of file
=====================================
.travis.yml deleted
=====================================
@@ -1,55 +0,0 @@
-language: generic
-env:
- global:
- # Set defaults to avoid repeating in most cases
- - PYTHON_VERSION=$TRAVIS_PYTHON_VERSION
- - MAIN_CMD='python setup.py'
- - CONDA_DEPENDENCIES='sphinx pillow pyproj coveralls coverage mock aggdraw six pyshp pyresample'
- - PIP_DEPENDENCIES=''
- - SETUP_XVFB=False
- - EVENT_TYPE='push pull_request'
- - SETUP_CMD='test'
- - CONDA_CHANNELS='conda-forge'
- - CONDA_CHANNEL_PRIORITY='True'
-matrix:
- include:
- - env:
- - PYTHON_VERSION=2.7
- - NUMPY_VERSION=1.16
- os: linux
- - env:
- - PYTHON_VERSION=2.7
- - NUMPY_VERSION=1.16
- os: osx
- - env: PYTHON_VERSION=3.6
- os: linux
- - env: PYTHON_VERSION=3.6
- os: osx
- - 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
-script: coverage run --source=pycoast setup.py test
-after_success:
-- if [[ $PYTHON_VERSION == 3.6 ]]; then coveralls; fi
-deploy:
-- provider: pypi
- user: dhoese
- password:
- secure: M3wCEnirvwNDncuoLj8DpPFwNypLXcDGlAinNHfwZLIEzDTNrC5XhGZ3xbfmAeBGOIgJ2I6xDWJ6hYevv3HxBdgln9nJfO7tsnqisK1lxyF4/dntCJm5CLkJqM8To1o9zeht0dyZJ66fUAHfxwjXGBDoaPyEnsygWXH6vpEZ8K0=
- distributions: sdist
- skip_existing: true
- on:
- tags: true
- repo: pytroll/pycoast
-notifications:
- slack: pytroll:96mNSYSI1dBjGyzVXkBT6qFt
=====================================
CHANGELOG.md
=====================================
@@ -1,3 +1,22 @@
+## Version 1.5.0 (2021/08/18)
+
+### Issues Closed
+
+* [Issue 48](https://github.com/pytroll/pycoast/issues/48) - Raster images without map projection
+* [Issue 46](https://github.com/pytroll/pycoast/issues/46) - add_coastlines call is incorrect in documentation
+
+In this release 2 issues were closed.
+
+### Pull Requests Merged
+
+#### Features added
+
+* [PR 54](https://github.com/pytroll/pycoast/pull/54) - Add area and parameter hashing to overlay caching
+* [PR 53](https://github.com/pytroll/pycoast/pull/53) - Switch to GitHub Actions for CI
+
+In this release 2 pull requests were closed.
+
+
## Version 1.4.0 (2020/06/08)
### Issues Closed
@@ -13,7 +32,11 @@ In this release 1 issue was closed.
* [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.
+#### Features added
+
+* [PR 42](https://github.com/pytroll/pycoast/pull/42) - Add "add_points" method for point/symbol data ([36](https://github.com/pytroll/pycoast/issues/36))
+
+In this release 3 pull requests were closed.
## Version 1.3.2 (2019/12/06)
=====================================
README.rst
=====================================
@@ -1,8 +1,11 @@
PyCoast
=======
-.. image:: https://travis-ci.org/pytroll/pycoast.svg?branch=master
- :target: https://travis-ci.org/pytroll/pycoast
+.. image:: https://github.com/pytroll/pycoast/workflows/CI/badge.svg?branch=main
+ :target: https://github.com/pytroll/pycoast/actions?query=workflow%3A%22CI%22
+
+.. image:: https://coveralls.io/repos/github/pytroll/pycoast/badge.svg?branch=main
+ :target: https://coveralls.io/github/pytroll/pycoast?branch=main
.. image:: https://img.shields.io/pypi/v/pycoast.svg
:target: https://pypi.python.org/pypi/pycoast
=====================================
RELEASING.md
=====================================
@@ -1,28 +1,47 @@
# Releasing PyCoast
-1. checkout master
+1. checkout main branch
2. pull from repo
3. run the unittests
4. run `loghub` and update the `CHANGELOG.md` file:
-```
-loghub pytroll/pycoast -u <username> -st v<last-tag> -plg bug "Bugs fixed" -plg enhancement "Features added" -plg documentation "Documentation changes"
-```
+ ```
+ loghub pytroll/pycoast --token $LOGHUB_GITHUB_TOKEN -st v<previous version> -plg bug "Bugs fixed" -plg enhancement "Features added" -plg documentation "Documentation changes" -plg backwards-incompatibility "Backward incompatible changes" -plg refactor "Refactoring"
+ ```
-Don't forget to commit!
+ This uses a `LOGHUB_GITHUB_TOKEN` environment variable. This must be created
+ on GitHub and it is recommended that you add it to your `.bashrc` or
+ `.bash_profile` or equivalent.
+
+ This command will create a CHANGELOG.temp file which need to be added
+ to the top of the CHANGLOG.md file. The same content is also printed
+ to terminal, so that can be copy-pasted, too. Remember to update also
+ the version number to the same given in step 5. Don't forget to commit
+ CHANGELOG.md!
5. Create a tag with the new version number, starting with a 'v', eg:
-```
-git tag -a v0.22.45 -m "Version 0.22.45"
-```
+ ```
+ git tag -a v<new version> -m "Version <new version>"
+ ```
-See [semver.org](http://semver.org/) on how to write a version number.
+ For example if the previous tag was `v0.9.0` and the new release is a
+ patch release, do:
+ ```
+ git tag -a v0.9.1 -m "Version 0.9.1"
+ ```
+ See [semver.org](http://semver.org/) on how to write a version number..
-6. push changes to github `git push --follow-tags`
-Make sure the new tag has been pushed.
+6. push changes to github `git push --follow-tags`
+7. Verify github action unittests passed.
+8. Create a "Release" on GitHub by going to
+ https://github.com/pytroll/pycoast/releases and clicking "Draft a new release".
+ On the next page enter the newly created tag in the "Tag version" field,
+ "Version X.Y.Z" in the "Release title" field, and paste the markdown from
+ the changelog (the portion under the version section header) in the
+ "Describe this release" box. Finally click "Publish release".
+9. Verify the GitHub actions for deployment succeed and the release is on PyPI.
-7. Verify travis tests passed and deployed sdist to PyPI
=====================================
continuous_integration/environment.yaml
=====================================
@@ -0,0 +1,14 @@
+name: test-environment
+channels:
+ - conda-forge
+dependencies:
+ - sphinx
+ - pillow
+ - pyproj
+ - coveralls
+ - coverage
+ - aggdraw
+ - pyshp
+ - pyresample
+ - pytest
+ - pytest-cov
=====================================
debian/changelog
=====================================
@@ -1,11 +1,27 @@
-pycoast (1.4.0+dfsg-2) UNRELEASED; urgency=medium
+pycoast (1.5.0+dfsg-1) unstable; urgency=medium
- * Team upload.
+ [ Bas Couwenberg ]
* Bump watch file version to 4.
* Bump Standards-Version to 4.5.1, no changes.
* Update watch file for GitHub URL changes.
- -- Bas Couwenberg <sebastic at debian.org> Fri, 06 Nov 2020 19:48:07 +0100
+ [ Antonio Valentino ]
+ * New upstream release.
+ * debian/copyright
+ - update the list excluded files: pre-built documentation
+ is no longer included in the upstream tarball
+ - update copyright dates.
+ * debian/patches
+ - refresh all patches
+ - new 0002-Fix-font-path.patch (only needed for testing on
+ debian systems)
+ * debian/control
+ - update dependencies: drop six, add pytest and
+ fonts-dejavu-core (build only).
+ * debian/rules
+ - fix pytest args.
+
+ -- Antonio Valentino <antonio.valentino at tiscali.it> Sun, 29 Aug 2021 17:23:17 +0000
pycoast (1.4.0+dfsg-1) unstable; urgency=medium
=====================================
debian/control
=====================================
@@ -7,6 +7,7 @@ Rules-Requires-Root: no
Priority: optional
Build-Depends: debhelper-compat (= 12),
dh-python,
+ fonts-dejavu-core,
python3-all,
python3-aggdraw,
python3-numpy,
@@ -14,8 +15,8 @@ Build-Depends: debhelper-compat (= 12),
python3-pyproj,
python3-pyresample,
python3-pyshp,
+ python3-pytest,
python3-setuptools,
- python3-six,
python3-sphinx
Standards-Version: 4.5.1
Vcs-Browser: https://salsa.debian.org/debian-gis-team/pycoast
@@ -29,10 +30,10 @@ Depends: python3-aggdraw,
python3-pil,
python3-pyproj,
python3-pyshp,
- python3-six,
${python3:Depends},
${misc:Depends}
-Suggests: python-pycoast-doc
+Suggests: fonts-dejavu-core,
+ python-pycoast-doc
Description: Draw coastlines, borders and rivers on images (for Python 3)
Pycoast is a Python package to add coastlines, borders and rivers to
raster images using data from the GSHHG (previously known as GSHHS)
=====================================
debian/copyright
=====================================
@@ -1,18 +1,17 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: pycoast
Source: https://github.com/pytroll/pycoast
- The upstream tarball is repacked to exclude pre-built documentation and
- test data without unclear license. From the test data README:
+ The upstream tarball is repacked to exclude test data without
+ unclear license. From the test data README:
"
This data is preliminary.
We await permission from the data provider
to include these shapes in pycoast testing.
"
-Files-Excluded: docs/build/*
- pycoast/tests/test_data/shapes/*
+Files-Excluded: pycoast/tests/test_data/shapes/*
Files: *
-Copyright: 2011-2020, PyCoast Developers
+Copyright: 2011-2021, PyCoast Developers
2011-2016, Esben S. Nielsen <esn at dmi.dk>
2011-2015, Hróbjartur Þorsteinsson
2011-2015, Stefano Cerino
@@ -42,7 +41,7 @@ Copyright: 1991-2008, P. Wessel & W. H. F. Smith
License: GPL-2+
Files: debian/*
-Copyright: 2014-2020, Antonio Valentino <antonio.valentino at tiscali.it>
+Copyright: 2014-2021, Antonio Valentino <antonio.valentino at tiscali.it>
License: GPL-3+
License: CC0-1.0
=====================================
debian/patches/0001-Skip-tests-that-use-shapes.patch
=====================================
@@ -2,15 +2,16 @@ From: Antonio Valentino <antonio.valentino at tiscali.it>
Date: Sun, 3 Jul 2016 17:17:27 +0000
Subject: Skip tests that use shapes
+Forwarded: not-needed
---
pycoast/tests/test_pycoast.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/pycoast/tests/test_pycoast.py b/pycoast/tests/test_pycoast.py
-index 82adcb0..b95edba 100644
+index b599ff2..0533cc4 100644
--- a/pycoast/tests/test_pycoast.py
+++ b/pycoast/tests/test_pycoast.py
-@@ -397,6 +397,7 @@ class TestPIL(TestPycoast):
+@@ -392,6 +392,7 @@ class TestPIL(TestPycoast):
self.assertTrue(fft_metric(grid_data, res),
'Writing of nh points failed')
@@ -18,7 +19,7 @@ index 82adcb0..b95edba 100644
def test_add_shapefile_shapes(self):
from pycoast import ContourWriterPIL
grid_img = Image.open(os.path.join(os.path.dirname(__file__),
-@@ -749,6 +750,7 @@ class TestPILAGG(TestPycoast):
+@@ -745,6 +746,7 @@ class TestPILAGG(TestPycoast):
res = np.array(img)
self.assertTrue(fft_metric(grid_data, res), 'Writing of nh points failed')
=====================================
debian/patches/0002-Fix-font-path.patch
=====================================
@@ -0,0 +1,54 @@
+From: Antonio Valentino <antonio.valentino at tiscali.it>
+Date: Sun, 29 Aug 2021 16:53:41 +0000
+Subject: Fix font path
+
+The patch is only needed for testing on debian systems.
+
+Forwarded: not-needed
+---
+ pycoast/tests/coasts_and_grid_agg.ini | 3 ++-
+ pycoast/tests/nh_points_agg.ini | 3 ++-
+ pycoast/tests/nh_points_pil.ini | 3 ++-
+ 3 files changed, 6 insertions(+), 3 deletions(-)
+
+diff --git a/pycoast/tests/coasts_and_grid_agg.ini b/pycoast/tests/coasts_and_grid_agg.ini
+index 674334f..26b0389 100644
+--- a/pycoast/tests/coasts_and_grid_agg.ini
++++ b/pycoast/tests/coasts_and_grid_agg.ini
+@@ -13,6 +13,7 @@ minor_outline = blue
+ outline = blue
+ lon_placement = tblr
+ lat_placement = ''
+-font = 'pycoast/tests/test_data/DejaVuSerif.ttf'
++# font = 'pycoast/tests/test_data/DejaVuSerif.ttf'
++font = '/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf'
+ font_size = 10
+
+diff --git a/pycoast/tests/nh_points_agg.ini b/pycoast/tests/nh_points_agg.ini
+index 5a2252f..37d22a7 100644
+--- a/pycoast/tests/nh_points_agg.ini
++++ b/pycoast/tests/nh_points_agg.ini
+@@ -11,7 +11,8 @@ resolution = c
+
+ [points]
+ points_list = ((2.3522, 48.8566), 'Paris'), ((0.1278, 51.5074), 'London')
+-font = 'pycoast/tests/test_data/DejaVuSerif.ttf'
++# font = 'pycoast/tests/test_data/DejaVuSerif.ttf'
++font = '/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf'
+ symbol = circle
+ ptsize = 16
+ outline = black
+diff --git a/pycoast/tests/nh_points_pil.ini b/pycoast/tests/nh_points_pil.ini
+index a33738d..7f26982 100644
+--- a/pycoast/tests/nh_points_pil.ini
++++ b/pycoast/tests/nh_points_pil.ini
+@@ -11,7 +11,8 @@ resolution = c
+
+ [points]
+ points_list = ((13.4050, 52.5200), 'Berlin'), ((12.4964, 41.9028), 'Rome')
+-font = 'pycoast/tests/test_data/DejaVuSerif.ttf'
++# font = 'pycoast/tests/test_data/DejaVuSerif.ttf'
++font = '/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf'
+ symbol = square
+ ptsize = 6
+ outline = red
=====================================
debian/patches/series
=====================================
@@ -1 +1,2 @@
0001-Skip-tests-that-use-shapes.patch
+0002-Fix-font-path.patch
=====================================
debian/rules
=====================================
@@ -1,12 +1,11 @@
#!/usr/bin/make -f
# -*- makefile -*-
-# Uncomment this to turn on verbose mode.
#export DH_VERBOSE=1
export PYBUILD_NAME=pycoast
-#export PYBUILD_TEST_ARGS=test
+export PYBUILD_TEST_ARGS=$(CURDIR)/pycoast/tests
%:
=====================================
pycoast/__init__.py
=====================================
@@ -1,8 +1,9 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
+"""Pycoast package for adding geographic-based decorations to images."""
from .cw_pil import ContourWriterPIL
-from .cw_agg import ContourWriterAGG
-from pycoast.cw_base import get_resolution_from_area
+from .cw_agg import ContourWriterAGG # noqa
+from pycoast.cw_base import get_resolution_from_area # noqa
from .version import get_versions
__version__ = get_versions()['version']
del get_versions
=====================================
pycoast/cw_agg.py
=====================================
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""ContourWriter based on the aggdraw library."""
from PIL import Image
import logging
@@ -35,13 +36,14 @@ class ContourWriterAGG(ContourWriterBase):
Path to root dir of GSHHS and WDBII shapefiles
"""
+
_draw_module = "AGG"
# This is a flag to make _add_grid aware of which text draw routine
# from PIL or from aggdraw should be used
# (unfortunately they are not fully compatible)
def _get_canvas(self, image):
- """Returns AGG image object."""
+ """Return AGG image object."""
return aggdraw.Draw(image)
def _engine_text_draw(self, draw, x_pos, y_pos, txt, font, **kwargs):
@@ -85,7 +87,6 @@ class ContourWriterAGG(ContourWriterBase):
def _draw_text_box(self, draw, text_position, text, font, outline,
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)
margin = 2
@@ -112,7 +113,7 @@ class ContourWriterAGG(ContourWriterBase):
draw.line(coordinates, pen)
def _draw_asterisk(self, draw, pt_size, coordinate, **kwargs):
- """Draw a asterisk sign '*' at the given coordinate. """
+ """Draw a asterisk sign '*' at the given coordinate."""
half_ptsize = int(round(pt_size / 2.))
x, y = coordinate
@@ -403,7 +404,6 @@ class ContourWriterAGG(ContourWriterBase):
Use tick minor line style (True) or full minor line style (False)
"""
-
image = Image.open(filename)
image = image.convert('RGBA')
self.add_grid(image, area_def, Dlonlat, dlonlat,
@@ -450,7 +450,6 @@ class ContourWriterAGG(ContourWriterBase):
Pixel offset in y direction
"""
-
self._add_feature(image, area_def, 'polygon', 'GSHHS',
resolution=resolution, level=level,
fill=fill, fill_opacity=fill_opacity,
@@ -491,7 +490,6 @@ class ContourWriterAGG(ContourWriterBase):
Pixel offset in y direction
"""
-
image = Image.open(filename)
image = image.convert('RGBA')
self.add_coastlines(image, area_def, resolution=resolution,
@@ -529,7 +527,6 @@ class ContourWriterAGG(ContourWriterBase):
Pixel offset in y direction
"""
-
self._add_feature(image, area_def, 'line', 'WDBII', tag='border',
resolution=resolution, level=level, outline=outline,
width=width, outline_opacity=outline_opacity,
@@ -599,7 +596,6 @@ class ContourWriterAGG(ContourWriterBase):
Pixel offset in y direction
"""
-
self._add_feature(image, area_def, 'line', 'WDBII', tag='river',
zero_pad=True, resolution=resolution, level=level,
outline=outline, width=width,
@@ -634,7 +630,6 @@ class ContourWriterAGG(ContourWriterBase):
Pixel offset in y direction
"""
-
image = Image.open(filename)
image = image.convert("RGBA")
self.add_rivers(image, area_def, resolution=resolution, level=level,
=====================================
pycoast/cw_base.py
=====================================
@@ -16,8 +16,11 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""Base class for contour writers."""
import os
+import hashlib
+import json
import shapefile
import numpy as np
from PIL import Image
@@ -25,10 +28,7 @@ import pyproj
import logging
import ast
-try:
- import configparser
-except ImportError:
- from six.moves import configparser
+import configparser
logger = logging.getLogger(__name__)
@@ -44,12 +44,8 @@ def get_resolution_from_area(area_def):
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)
+ 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:
@@ -64,6 +60,14 @@ def get_resolution_from_area(area_def):
return "f"
+def hash_dict(dict_to_hash: dict) -> str:
+ """Hash dict object by serializing with json."""
+ dhash = hashlib.sha256()
+ encoded = json.dumps(dict_to_hash, sort_keys=True).encode()
+ dhash.update(encoded)
+ return dhash.hexdigest()
+
+
class Proj(pyproj.Proj):
"""Wrapper around pyproj to add in 'is_latlong'."""
@@ -95,8 +99,7 @@ class ContourWriterBase(object):
self.db_root_path = db_root_path
def _draw_text(self, draw, position, txt, font, align='cc', **kwargs):
- """Draw text with agg module
- """
+ """Draw text with agg module."""
txt_width, txt_height = draw.textsize(txt, font)
x_pos, y_pos = position
ax, ay = align.lower()
@@ -116,8 +119,7 @@ class ContourWriterBase(object):
raise NotImplementedError('Text drawing undefined for render engine')
def _draw_grid_labels(self, draw, xys, linetype, txt, font, **kwargs):
- """Draw text with default PIL module
- """
+ """Draw text with default PIL module."""
if font is None:
# NOTE: Default font does not use font size in PIL writer
font = self._get_font(kwargs.get('outline', 'black'), font, 12)
@@ -130,8 +132,11 @@ class ContourWriterBase(object):
self._draw_text(draw, xy[0], txt, font, align=xy[1], **kwargs)
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 coordinates"""
+ """Find intercepts of poly-line xys with image boundaries offset by margins.
+
+ Returns an array of coordinates.
+
+ """
x_size, y_size = size
def is_in_box(x_y, extents):
@@ -196,9 +201,7 @@ class ContourWriterBase(object):
Dlon, Dlat,
dlon, dlat,
font=None, write_text=True, **kwargs):
- """Add a lat lon grid to image
- """
-
+ """Add a lat lon grid to image."""
try:
proj_def = area_def.crs if hasattr(area_def, 'crs') else area_def.proj_dict
area_extent = area_def.area_extent
@@ -489,16 +492,20 @@ class ContourWriterBase(object):
def _add_shapefile_shape(self, image, area_def, filename, shape_id,
feature_type=None, **kwargs):
- """ for drawing a single shape (polygon/poly-line) definiton with id,
- shape_id from a custom shape file onto a PIL image
+ """Draw a single shape (polygon/poly-line) definition.
+
+ Accesses single shape using shape_id from a custom shape file.
+
"""
sf = shapefile.Reader(filename)
shape = sf.shape(shape_id)
return self.add_shapes(image, area_def, [shape], feature_type=feature_type, **kwargs)
def _add_line(self, image, area_def, lonlats, **kwargs):
- """ For drawing a custom polyline. Lon and lat coordinates given by the
- list lonlat.
+ """Draw a custom polyline.
+
+ Lon and lat coordinates given by the list lonlat.
+
"""
# create dummpy shapelike object
shape = type("", (), {})()
@@ -508,8 +515,10 @@ class ContourWriterBase(object):
self.add_shapes(image, area_def, [shape], feature_type="line", **kwargs)
def _add_polygon(self, image, area_def, lonlats, **kwargs):
- """ For drawing a custom polygon. Lon and lat coordinates given by the
- list lonlat.
+ """Draw a custom polygon.
+
+ Lon and lat coordinates given by the list lonlat.
+
"""
# create dummpy shapelike object
shape = type("", (), {})()
@@ -584,10 +593,9 @@ class ContourWriterBase(object):
# Check if polygon is possibly relevant
s_lon_ll, s_lat_ll, s_lon_ur, s_lat_ur = shape.bbox
- if lon_min > lon_max:
- pass
- elif (lon_max < s_lon_ll or lon_min > s_lon_ur or
- lat_max < s_lat_ll or lat_min > s_lat_ur):
+ shape_is_outside_lon = lon_max < s_lon_ll or lon_min > s_lon_ur
+ shape_is_outside_lat = lat_max < s_lat_ll or lat_min > s_lat_ur
+ if lon_min <= lon_max and (shape_is_outside_lon or shape_is_outside_lat):
# Polygon is irrelevant
continue
@@ -625,8 +633,7 @@ class ContourWriterBase(object):
db_name, tag=None, zero_pad=False, resolution='c',
level=1, x_offset=0, y_offset=0, db_root_path=None,
**kwargs):
- """Add a contour feature to image
- """
+ """Add a contour feature to image."""
shape_generator = self._iterate_db(
db_name, tag, resolution, level, zero_pad,
db_root_path=db_root_path
@@ -636,8 +643,7 @@ class ContourWriterBase(object):
x_offset=x_offset, y_offset=y_offset, **kwargs)
def _iterate_db(self, db_name, tag, resolution, level, zero_pad, db_root_path=None):
- """Iterate through datasets
- """
+ """Iterate through datasets."""
if db_root_path is None:
db_root_path = self.db_root_path
if db_root_path is None:
@@ -680,7 +686,6 @@ class ContourWriterBase(object):
def _finalize(self, draw):
"""Do any need finalization of the drawing."""
-
pass
def _config_to_dict(self, config_file):
@@ -712,8 +717,16 @@ 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.
+
+ Optionally caches overlay results for faster rendering of images with
+ the same provided AreaDefinition and parameters. Cached results are
+ identified by hashing the AreaDefinition and the overlays dictionary.
+
+ .. warning::
+
+ Font objects are ignored in parameter hashing as they can't be easily hashed.
+ Therefore font changes will not trigger a new rendering for the cache.
:Parameters:
overlays : dict
@@ -748,28 +761,18 @@ class ContourWriterBase(object):
of an already cached file.
"""
-
# Cache management
cache_file = None
if 'cache' in overlays:
- cache_file = (overlays['cache']['file'] + '_' +
- area_def.area_id + '.png')
-
- try:
- 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)
- 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("Regenerating cache file.")
- except OSError:
- logger.info("No overlay image found, new overlay image will be saved in cache.")
+ cache_file = self._generate_cache_filename(
+ overlays['cache']['file'],
+ area_def,
+ overlays,
+ )
+ regenerate = overlays['cache'].get('regenerate', False)
+ foreground = self._apply_cached_image(cache_file, cache_epoch, background, regenerate=regenerate)
+ if foreground is not None:
+ return foreground
x_size = area_def.width
y_size = area_def.height
@@ -794,24 +797,22 @@ class ContourWriterBase(object):
'resolution': default_resolution}
for section, fun in zip(['coasts', 'rivers', 'borders'],
- [self.add_coastlines,
- self.add_rivers,
- self.add_borders]):
- if section in overlays:
-
- params = DEFAULT.copy()
- params.update(overlays[section])
+ [self.add_coastlines, self.add_rivers, self.add_borders]):
+ if section not in overlays:
+ continue
+ params = DEFAULT.copy()
+ params.update(overlays[section])
- if section != "coasts":
- params.pop('fill_opacity', None)
- params.pop('fill', None)
+ if section != "coasts":
+ params.pop('fill_opacity', None)
+ params.pop('fill', None)
- if not is_agg:
- for key in ['width', 'outline_opacity', 'fill_opacity']:
- params.pop(key, None)
+ if not is_agg:
+ for key in ['width', 'outline_opacity', 'fill_opacity']:
+ params.pop(key, None)
- fun(foreground, area_def, **params)
- logger.info("%s added", section.capitalize())
+ fun(foreground, area_def, **params)
+ logger.info("%s added", section.capitalize())
# Cities management
if 'cities' in overlays:
@@ -862,6 +863,10 @@ class ContourWriterBase(object):
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))
+ grid_kwargs = {}
+ if is_agg:
+ width = float(overlays['grid'].get('width', 1.0))
+ grid_kwargs["width"] = width
write_text = overlays['grid'].get('write_text', True)
if isinstance(write_text, str):
@@ -888,20 +893,62 @@ class ContourWriterBase(object):
outline=outline, minor_outline=minor_outline,
minor_is_tick=minor_is_tick,
lon_placement=lon_placement,
- lat_placement=lat_placement)
+ lat_placement=lat_placement,
+ **grid_kwargs)
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])
+ self._write_and_apply_new_cached_image(cache_file, foreground, background)
return foreground
+ @staticmethod
+ def _apply_cached_image(cache_file, cache_epoch, background, regenerate=False):
+ try:
+ 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 and not regenerate:
+ 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
+ logger.info("Regenerating cache file.")
+ except OSError:
+ logger.info("No overlay image found, new overlay image will be saved in cache.")
+ return None
+
+ @staticmethod
+ def _write_and_apply_new_cached_image(cache_file, foreground, background):
+ 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])
+
+ def _generate_cache_filename(self, cache_prefix, area_def, overlays_dict):
+ area_hash = hash(area_def)
+ base_dir, file_prefix = os.path.split(cache_prefix)
+ params_to_hash = self._prepare_hashable_dict(overlays_dict)
+ param_hash = hash_dict(params_to_hash)
+ return os.path.join(base_dir, f"{file_prefix}_{area_hash}_{param_hash}.png")
+
+ @staticmethod
+ def _prepare_hashable_dict(nonhashable_dict):
+ params_to_hash = {}
+ # avoid wasteful deep copy by only doing two levels of copying
+ for overlay_name, overlay_dict in nonhashable_dict.items():
+ if overlay_name == 'cache':
+ continue
+ params_to_hash[overlay_name] = overlay_dict.copy()
+ # font objects are not hashable
+ for font_cat in ('cities', 'points', 'grid'):
+ if font_cat in params_to_hash:
+ params_to_hash[font_cat].pop('font', None)
+ return params_to_hash
+
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
@@ -916,9 +963,7 @@ class ContourWriterBase(object):
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
-
- """
+ """Add cities (point and name) to a PIL image object."""
if db_root_path is None:
db_root_path = self.db_root_path
if db_root_path is None:
@@ -1094,9 +1139,7 @@ class ContourWriterBase(object):
def _get_lon_lat_bounding_box(area_extent, x_size, y_size, prj):
- """Get extreme lon and lat values
- """
-
+ """Get extreme lon and lat values."""
x_ll, y_ll, x_ur, y_ur = area_extent
x_range = np.linspace(x_ll, x_ur, num=x_size)
y_range = np.linspace(y_ll, y_ur, num=y_size)
=====================================
pycoast/cw_pil.py
=====================================
@@ -16,6 +16,7 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""PIL-based ContourWriter."""
from PIL import Image, ImageFont
from PIL import ImageDraw
@@ -41,8 +42,7 @@ class ContourWriterPIL(ContourWriterBase):
# they are not fully compatible)
def _get_canvas(self, image):
- """Returns PIL image object."""
-
+ """Return PIL image object."""
return ImageDraw.Draw(image)
def _engine_text_draw(self, draw, x_pos, y_pos, txt, font, **kwargs):
@@ -75,7 +75,7 @@ class ContourWriterPIL(ContourWriterBase):
draw.line(coordinates, fill=kwargs['outline'])
def _draw_asterisk(self, draw, pt_size, coordinate, **kwargs):
- """Draw a asterisk sign '*' center at the given coordinate """
+ """Draw a asterisk sign '*' center at the given coordinate."""
half_ptsize = int(round(pt_size / 2.))
x, y = coordinate
@@ -333,7 +333,6 @@ class ContourWriterPIL(ContourWriterBase):
Pixel offset in y direction
"""
-
self._add_feature(image, area_def, 'polygon', 'GSHHS',
resolution=resolution, level=level,
fill=fill, outline=outline, x_offset=x_offset,
@@ -365,7 +364,6 @@ class ContourWriterPIL(ContourWriterBase):
Pixel offset in y direction
"""
-
image = Image.open(filename)
self.add_coastlines(image, area_def,
resolution=resolution, level=level,
@@ -396,7 +394,6 @@ class ContourWriterPIL(ContourWriterBase):
Pixel offset in y direction
"""
-
self._add_feature(image, area_def, 'line', 'WDBII',
tag='border', resolution=resolution, level=level,
outline=outline, x_offset=x_offset,
@@ -454,7 +451,6 @@ class ContourWriterPIL(ContourWriterBase):
Pixel offset in y direction
"""
-
self._add_feature(image, area_def, 'line', 'WDBII',
tag='river', zero_pad=True, resolution=resolution,
level=level, outline=outline, x_offset=x_offset,
@@ -483,7 +479,6 @@ class ContourWriterPIL(ContourWriterBase):
Pixel offset in y direction
"""
-
image = Image.open(filename)
self.add_rivers(image, area_def, resolution=resolution, level=level,
outline=outline, x_offset=x_offset, y_offset=y_offset)
=====================================
pycoast/tests/__init__.py
=====================================
@@ -0,0 +1,19 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+# pycoast, Writing of coastlines, borders and rivers to images in Python
+#
+# Copyright (C) 2021 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
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""Pycoast unit tests."""
=====================================
pycoast/tests/test_pycoast.py
=====================================
@@ -16,29 +16,24 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
+"""Main unit tests for pycoast."""
import os
import unittest
+from glob import glob
import numpy as np
from PIL import Image, ImageFont
import time
-def tmp(f):
- f.tmp = True
- return f
-
-
def fft_proj_rms(a1, a2):
- """Compute the RMS of differences between FFT vectors of a1
- and projection of FFT vectors of a2.
+ """Compute the RMS of differences between FFT vectors of a1 and projection of FFT vectors of a2.
+
This metric is sensitive to large scale changes and image noise but
insensitive to small rendering differences.
"""
-
ms = 0
-
for i in range(3):
fr1 = np.fft.fft2(a1[:, :, i])
fr2 = np.fft.fft2(a2[:, :, i])
@@ -50,8 +45,8 @@ def fft_proj_rms(a1, a2):
p2 = np.arctan2(fr2.imag, fr2.real)
theta = p2 - p1
- l = ps2 * np.cos(theta)
- ms += ((l - ps1) ** 2).sum() / float(ps1.size)
+ adjusted_ps2 = ps2 * np.cos(theta)
+ ms += ((adjusted_ps2 - ps1) ** 2).sum() / float(ps1.size)
rms = np.sqrt(ms)
@@ -59,9 +54,7 @@ def fft_proj_rms(a1, a2):
def fft_metric(data1, data2, max_value=0.1):
- """Execute FFT metric
- """
-
+ """Execute FFT metric."""
rms = fft_proj_rms(data1, data2)
return rms <= max_value
@@ -73,6 +66,7 @@ p_file_coasts = 'test_coasts_p_mode.png'
class TestPycoast(unittest.TestCase):
+ """Base class for test classes that need example images."""
def setUp(self):
img = Image.new('RGB', (640, 480))
@@ -88,6 +82,7 @@ class TestPycoast(unittest.TestCase):
class TestPIL(TestPycoast):
+ """Test PIL-based contour writer."""
def test_europe(self):
from pycoast import ContourWriterPIL
@@ -342,11 +337,11 @@ class TestPIL(TestPycoast):
(-39, 63.5), (-55 + 4 / 6.0, 63.5), (-57 + 45 / 60.0, 65),
(-76, 76), (-75, 78), (-60, 82), (0, 90),
(30, 82), (0, 82), (0, 73), (-20, 73), (-20, 70)),
- 'REYKJAVIK_ATC': (
+ 'REYKJAVIK_ATC': (
(0.0, 73.0), (0.0, 61.0), (-30.0, 61.0), (-39, 63.5),
(-55 + 4 / 6.0, 63.5), (-57 + 45 / 60.0, 65),
(-76, 76), (-75, 78), (-60, 82), (0, 90), (30, 82), (0, 82)),
- 'ICELAND_BOX': ((-25, 62.5), (-25, 67), (-13, 67), (-13, 62.5))
+ 'ICELAND_BOX': ((-25, 62.5), (-25, 67), (-13, 67), (-13, 62.5))
}
cw.add_polygon(img, area_def, polygons['REYKJAVIK_ATC'], outline='red')
@@ -482,6 +477,7 @@ class TestPIL(TestPycoast):
class TestPILAGG(TestPycoast):
+ """Test AGG contour writer."""
def test_europe_agg(self):
from pycoast import ContourWriterAGG
@@ -852,7 +848,7 @@ class TestPILAGG(TestPycoast):
self.assertTrue(image_mode == 'RGBA', 'Conversion to RGBA failed.')
-class FakeAreaDef():
+class FakeAreaDef:
"""A fake area definition object."""
def __init__(self, proj4_string, area_extent, x_size, y_size):
@@ -863,7 +859,7 @@ class FakeAreaDef():
self.area_id = 'fakearea'
-class TestFromConfig(TestPycoast):
+class TestFromConfig:
"""Test burning overlays from a config file."""
def test_foreground(self):
@@ -883,8 +879,7 @@ class TestFromConfig(TestPycoast):
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')
+ assert fft_metric(euro_data, res), 'Writing of contours failed'
overlays = {'coasts': {'level': [1, 2, 3, 4], 'resolution': 'l'},
'borders': {'outline': (255, 0, 0), 'resolution': 'c'},
@@ -892,96 +887,127 @@ class TestFromConfig(TestPycoast):
img = cw.add_overlay_from_dict(overlays, area_def)
res = np.array(img)
- self.assertTrue(fft_metric(euro_data, res),
- 'Writing of contours failed')
+ assert fft_metric(euro_data, res), 'Writing of contours failed'
- def test_cache(self):
+ def test_cache(self, tmpdir):
"""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')},
+ overlays = {'cache': {'file': os.path.join(tmpdir, 'pycoast_cache')},
'coasts': {'level': 4, 'resolution': 'l'},
'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))
+ cache_glob = glob(os.path.join(tmpdir, 'pycoast_cache_*.png'))
+ assert len(cache_glob) == 1
+ cache_filename = cache_glob[0]
+ assert fft_metric(euro_data, res), 'Writing of contours failed'
+ assert os.path.isfile(cache_filename)
mtime = os.path.getmtime(cache_filename)
# 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)
+ assert fft_metric(euro_data, res), 'Writing of contours failed'
+ assert os.path.isfile(cache_filename)
+ assert 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')
+ assert mtime > current_time
+ assert fft_metric(euro_data, res), 'Writing of contours failed'
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')
+ assert os.path.getmtime(cache_filename) == mtime
+ assert fft_metric(euro_data, res), 'Writing of contours failed'
overlays['cache']['regenerate'] = True
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')
+ assert os.path.getmtime(cache_filename) != mtime
+ assert fft_metric(euro_data, res), 'Writing of contours failed'
overlays.pop('cache')
overlays['grid'] = {'outline': (255, 255, 255), 'outline_opacity': 175,
'minor_outline': (200, 200, 200), 'minor_outline_opacity': 127,
'width': 1.0, 'minor_width': 0.5, 'minor_is_tick': True,
'write_text': True, 'lat_placement': 'lr', 'lon_placement': 'b'}
- img = cw.add_overlay_from_dict(overlays, area_def)
- os.remove(os.path.join(tmp, 'pycoast_cache_fakearea.png'))
+ cw.add_overlay_from_dict(overlays, area_def)
+ os.remove(cache_filename)
- def test_get_resolution(self):
- """Get the automagical resolution computation."""
- from pycoast import get_resolution_from_area
+ def test_caching_with_param_changes(self, tmpdir):
+ """Testing caching when changing parameters."""
+ from pycoast import ContourWriterPIL
+
+ # 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)
- 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')
-
+ cw = ContourWriterPIL(gshhs_root_dir)
-def suite():
- loader = unittest.TestLoader()
- mysuite = unittest.TestSuite()
- mysuite.addTest(loader.loadTestsFromTestCase(TestPIL))
- mysuite.addTest(loader.loadTestsFromTestCase(TestPILAGG))
- mysuite.addTest(loader.loadTestsFromTestCase(TestFromConfig))
+ font = ImageFont.truetype(os.path.join(
+ os.path.dirname(__file__), 'test_data', 'DejaVuSerif.ttf'))
+ overlays = {'cache': {'file': os.path.join(tmpdir, 'pycoast_cache')},
+ 'grid': {'font': font}}
- return mysuite
+ # Create the original cache file
+ cw.add_overlay_from_dict(overlays, area_def)
+ cache_glob = glob(os.path.join(tmpdir, 'pycoast_cache_*.png'))
+ assert len(cache_glob) == 1
+ cache_filename = cache_glob[0]
+ assert os.path.isfile(cache_filename)
+ mtime = os.path.getmtime(cache_filename)
+ # Reuse the generated cache file
+ cw.add_overlay_from_dict(overlays, area_def)
+ cache_glob = glob(os.path.join(tmpdir, 'pycoast_cache_*.png'))
+ assert len(cache_glob) == 1
+ assert os.path.isfile(cache_filename)
+ assert os.path.getmtime(cache_filename) == mtime
+
+ # Remove the font option, should produce the same result
+ # font is not considered when caching
+ del overlays['grid']['font']
+ cw.add_overlay_from_dict(overlays, area_def)
+ cache_glob = glob(os.path.join(tmpdir, 'pycoast_cache_*.png'))
+ assert len(cache_glob) == 1
+ assert os.path.isfile(cache_filename)
+ assert os.path.getmtime(cache_filename) == mtime
+
+ # Changing a parameter should create a new cache file
+ overlays = {'cache': {'file': os.path.join(tmpdir, 'pycoast_cache')},
+ 'grid': {'width': 2.0}}
+ cw.add_overlay_from_dict(overlays, area_def)
+ cache_glob = glob(os.path.join(tmpdir, 'pycoast_cache_*.png'))
+ assert len(cache_glob) == 2
+ assert os.path.isfile(cache_filename)
+ new_cache_filename = cache_glob[0] if cache_glob[0] != cache_filename else cache_glob[1]
+ # original cache file should be unchanged
+ assert os.path.getmtime(cache_filename) == mtime
+ # new cache file should be...new
+ assert os.path.getmtime(new_cache_filename) != mtime
-if __name__ == "__main__":
- suite()
+ 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)
+ assert get_resolution_from_area(area_def) == 'l'
+ area_def = FakeAreaDef(proj4_string, area_extent, 6400, 4800)
+ assert get_resolution_from_area(area_def) == 'h'
=====================================
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 = " (tag: v1.4.0)"
- git_full = "c96f05892af2bdc7148dbfdea7930385ca39aa1e"
- git_date = "2020-06-08 14:34:54 -0500"
+ git_refnames = " (HEAD -> main, tag: v1.5.0)"
+ git_full = "42ba82e32251fe48d62af1ebf0ad81d0577d74e4"
+ git_date = "2021-08-18 09:01:22 -0500"
keywords = {"refnames": git_refnames, "full": git_full, "date": git_date}
return keywords
=====================================
setup.cfg
=====================================
@@ -5,6 +5,9 @@ doc_files = docs/Makefile docs/source/*.rst
[flake8]
max-line-length = 120
+ignore = D107,D102
+exclude =
+ pycoast/version.py
[versioneer]
VCS = git
=====================================
setup.py
=====================================
@@ -18,9 +18,12 @@
from setuptools import setup
import versioneer
-requires = ['aggdraw', 'pyshp', 'numpy', 'pyproj', 'pillow', 'six']
-tests_require = ['aggdraw', 'pyshp', 'numpy', 'pyproj', 'pillow', 'six',
- 'pyresample']
+requires = ['aggdraw', 'pyshp', 'numpy', 'pyproj', 'pillow']
+
+extras_require = {
+ "docs": ["sphinx", 'pyresample', 'pytest'],
+ "tests": ['pyresample', 'pytest', 'pytest-cov', 'coverage', 'coveralls'],
+}
with open('README', 'r') as readme_file:
long_description = readme_file.read()
@@ -34,8 +37,8 @@ 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',
+ extras_require=extras_require,
+ python_requires='>3.7',
zip_safe=False,
classifiers=[
'Development Status :: 5 - Production/Stable',
View it on GitLab: https://salsa.debian.org/debian-gis-team/pycoast/-/compare/27161b5282ad876a999337e9ba1e7f4314b3b93d...0617d6c1ac0b13a6ada1a19ff1e2a3da0c19ea6d
--
View it on GitLab: https://salsa.debian.org/debian-gis-team/pycoast/-/compare/27161b5282ad876a999337e9ba1e7f4314b3b93d...0617d6c1ac0b13a6ada1a19ff1e2a3da0c19ea6d
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/20210829/0f033d2d/attachment-0001.htm>
More information about the Pkg-grass-devel
mailing list