Skip to content

Commit

Permalink
Make pycairo an optional dependency. Closes #12.
Browse files Browse the repository at this point in the history
  • Loading branch information
flopp committed Mar 6, 2021
1 parent 7fcb5b8 commit ded77cf
Show file tree
Hide file tree
Showing 15 changed files with 65 additions and 28 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
.env
.mypy_cache
.pytest_cache
.vscode
*.egg-info
*.pyc
*.png
Expand Down
11 changes: 10 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,24 @@ A python module to create static map images (PNG, SVG) with markers, geodesic li

## Installation

### SVG only version

```shell
pip install py-staticmaps
```

### SVG + PNG version (via Cairo)

```shell
pip install py-staticmaps[cairo]
```

`py-staticmaps` uses `pycairo` for creating antialiased raster-graphics, so make sure `libcairo2` is installed on your system (on Ubuntu just install the `libcairo2-dev` package, i.e. `sudo apt install libcairo2-dev`).


## Examples

Note: PNG support (e.g. `context.render_cairo(...)`) is only available if the `pycairo` module is installed.

### Markers and Geodesic Lines

Expand Down Expand Up @@ -195,4 +204,4 @@ Please take a look at the command line program which uses the `staticmaps` packa

## License

[MIT](LICENSE) © 2020 Florian Pigorsch
[MIT](LICENSE) © 2020-2012 Florian Pigorsch
10 changes: 7 additions & 3 deletions examples/custom_objects.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
# py-staticmaps
# Copyright (c) 2021 Florian Pigorsch; see /LICENSE for licensing information

import cairo # type: ignore
try:
import cairo # type: ignore
except ImportError:
pass
import s2sphere # type: ignore
import staticmaps

Expand Down Expand Up @@ -125,8 +128,9 @@ def render_cairo(self, renderer: staticmaps.CairoRenderer) -> None:
context.set_tile_provider(staticmaps.tile_provider_CartoDarkNoLabels)

# render png
image = context.render_cairo(800, 500)
image.write_to_png("custom_objects.png")
if staticmaps.cairo_is_supported():
image = context.render_cairo(800, 500)
image.write_to_png("custom_objects.png")

# render svg
svg_image = context.render_svg(800, 500)
Expand Down
5 changes: 3 additions & 2 deletions examples/draw_gpx.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@
break

# render png
image = context.render_cairo(800, 500)
image.write_to_png("running.png")
if staticmaps.cairo_is_supported():
image = context.render_cairo(800, 500)
image.write_to_png("running.png")

# render svg
svg_image = context.render_svg(800, 500)
Expand Down
5 changes: 3 additions & 2 deletions examples/frankfurt_newyork.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
context.add_object(staticmaps.Marker(newyork, color=staticmaps.RED, size=12))

# render png
image = context.render_cairo(800, 500)
image.write_to_png("frankfurt_newyork.png")
if staticmaps.cairo_is_supported():
image = context.render_cairo(800, 500)
image.write_to_png("frankfurt_newyork.png")

# render svg
svg_image = context.render_svg(800, 500)
Expand Down
5 changes: 3 additions & 2 deletions examples/freiburg_area.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,8 +445,9 @@
)

# render png
image = context.render_cairo(800, 500)
image.write_to_png("freiburg_area.png")
if staticmaps.cairo_is_supported():
image = context.render_cairo(800, 500)
image.write_to_png("freiburg_area.png")

# render svg
svg_image = context.render_svg(800, 500)
Expand Down
6 changes: 4 additions & 2 deletions examples/geodesic_circles.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,11 @@
context.add_object(staticmaps.Circle(center2, 2000, fill_color=staticmaps.TRANSPARENT, color=staticmaps.GREEN, width=2))
context.add_object(staticmaps.Marker(center1, color=staticmaps.RED))
context.add_object(staticmaps.Marker(center2, color=staticmaps.GREEN))

# render png
image = context.render_cairo(800, 600)
image.write_to_png("geodesic_circles.png")
if staticmaps.cairo_is_supported():
image = context.render_cairo(800, 600)
image.write_to_png("geodesic_circles.png")

# render svg
svg_image = context.render_svg(800, 600)
Expand Down
5 changes: 3 additions & 2 deletions examples/tile_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
context.set_tile_provider(provider)

# render png
image = context.render_cairo(800, 500)
image.write_to_png(f"provider_{name}.png")
if staticmaps.cairo_is_supported():
image = context.render_cairo(800, 500)
image.write_to_png(f"provider_{name}.png")

# render svg
svg_image = context.render_svg(800, 500)
Expand Down
5 changes: 3 additions & 2 deletions examples/us_capitals.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@
context.add_object(staticmaps.Marker(capital, size=5))

# render png
image = context.render_cairo(800, 500)
image.write_to_png("us_capitals.png")
if staticmaps.cairo_is_supported():
image = context.render_cairo(800, 500)
image.write_to_png("us_capitals.png")

# render svg
svg_image = context.render_svg(800, 500)
Expand Down
1 change: 1 addition & 0 deletions requirements-cairo.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
pycairo
1 change: 0 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
appdirs
geographiclib
PILLOW
pycairo
python-slugify
requests
s2sphere
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ def _read_reqs(rel_path: str) -> typing.List[str]:
packages=[PACKAGE],
install_requires=_read_reqs("requirements.txt"),
extras_require={
"cairo": _read_reqs("requirements-cairo.txt"),
"dev": _read_reqs("requirements-dev.txt"),
"examples": _read_reqs("requirements-examples.txt"),
},
Expand Down
2 changes: 1 addition & 1 deletion staticmaps/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

# flake8: noqa
from .area import Area
from .cairo_renderer import CairoRenderer
from .cairo_renderer import CairoRenderer, cairo_is_supported
from .circle import Circle
from .color import (
parse_color,
Expand Down
27 changes: 22 additions & 5 deletions staticmaps/cairo_renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,14 @@

import io
import math
import sys
import typing

import cairo # type: ignore
try:
import cairo # type: ignore
except ImportError:
pass

from PIL import Image # type: ignore

from .color import Color, BLACK, WHITE
Expand All @@ -17,21 +22,33 @@
from .object import Object # pylint: disable=cyclic-import


def cairo_is_supported() -> bool:
return "cairo" in sys.modules


# Dummy types, so that type annotation works if cairo is missing.
cairo_Context = typing.Any
cairo_ImageSurface = typing.Any


class CairoRenderer(Renderer):
def __init__(self, transformer: Transformer) -> None:
Renderer.__init__(self, transformer)

if not cairo_is_supported():
raise RuntimeError("Cannot render to Cairo since the 'cairo' module could not be imported.")

self._surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, *self._trans.image_size())
self._context = cairo.Context(self._surface)

def image_surface(self) -> cairo.ImageSurface:
def image_surface(self) -> cairo_ImageSurface:
return self._surface

def context(self) -> cairo.Context:
def context(self) -> cairo_Context:
return self._context

@staticmethod
def create_image(image_data: bytes) -> cairo.ImageSurface:
def create_image(image_data: bytes) -> cairo_ImageSurface:
image = Image.open(io.BytesIO(image_data))
if image.format == "PNG":
return cairo.ImageSurface.create_from_png(io.BytesIO(image_data))
Expand Down Expand Up @@ -103,7 +120,7 @@ def render_attribution(self, attribution: typing.Optional[str]) -> None:

def fetch_tile(
self, download: typing.Callable[[int, int, int], typing.Optional[bytes]], x: int, y: int
) -> typing.Optional[cairo.ImageSurface]:
) -> typing.Optional[cairo_ImageSurface]:
image_data = download(self._trans.zoom(), x, y)
if image_data is None:
return None
Expand Down
8 changes: 3 additions & 5 deletions staticmaps/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@

import math
import os
import sys
import typing

import appdirs # type: ignore
import cairo # type: ignore
import s2sphere # type: ignore
import svgwrite # type: ignore

from .cairo_renderer import CairoRenderer
from .cairo_renderer import CairoRenderer, cairo_is_supported
from .color import Color
from .meta import LIB_NAME
from .object import Object, PixelBoundsT
Expand Down Expand Up @@ -54,8 +52,8 @@ def set_tile_provider(self, provider: TileProvider) -> None:
def add_object(self, obj: Object) -> None:
self._objects.append(obj)

def render_cairo(self, width: int, height: int) -> cairo.ImageSurface:
if "cairo" not in sys.modules:
def render_cairo(self, width: int, height: int) -> typing.Any:
if not cairo_is_supported():
raise RuntimeError('You need to install the "cairo" module to enable "render_cairo".')

center, zoom = self.determine_center_zoom(width, height)
Expand Down

0 comments on commit ded77cf

Please sign in to comment.