Skip to content

Commit

Permalink
Merge pull request #42 from yufeizhu600/feature_add_poi
Browse files Browse the repository at this point in the history
Add "add_points" method for point/symbol data
  • Loading branch information
mraspaud committed May 20, 2020
2 parents eb35e25 + 88aca5f commit 59e981f
Show file tree
Hide file tree
Showing 18 changed files with 504 additions and 47 deletions.
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -58,5 +58,3 @@ docs/_build/

# PyBuilder
target/


1 change: 1 addition & 0 deletions AUTHORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Binary file added docs/source/images/nh_points_agg.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
68 changes: 68 additions & 0 deletions docs/source/points.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
Custom points with or without textbox
--------------------------------

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 lacation 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,i
... font_file=font_file,
... symbol='square', ptsize=10,
... outline='red', width=1,
... fill='blue', fill_opacity=128)

Finally, we can fully costomize the annotation as the example below, which will add
a circle in black with linewidth 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

The :meth:`~pycoast.cw_agg.ContourWriterAGG.add_points` method accepts a list of longitude, latitude pairs, with an optional
Description string.

.. _PIL: http://www.pythonware.com/products/pil/
2 changes: 1 addition & 1 deletion pycoast/__init__.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#!/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
from .version import get_versions
__version__ = get_versions()['version']
del get_versions


class ContourWriter(ContourWriterPIL):
"""Writer wrapper for deprecation warning.
Expand Down
76 changes: 60 additions & 16 deletions pycoast/cw_agg.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -52,32 +52,39 @@ def _draw_polygon(self, draw, coordinates, **kwargs):
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)
Expand All @@ -88,9 +95,12 @@ def _draw_text_box(self, draw, text_position, text, font, outline,
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")

Expand All @@ -101,6 +111,40 @@ def _draw_line(self, draw, coordinates, **kwargs):
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()
Expand Down Expand Up @@ -274,7 +318,7 @@ def add_grid(self, image, area_def, Dlonlat, dlonlat,
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
Expand Down Expand Up @@ -520,7 +564,7 @@ def add_borders_to_file(self, filename, area_def, resolution='c',
"""
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,
Expand Down Expand Up @@ -592,7 +636,7 @@ def add_rivers_to_file(self, filename, area_def, resolution='c', level=1,
"""

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,
Expand Down

0 comments on commit 59e981f

Please sign in to comment.