diff --git a/mapping/enable/canvas.py b/mapping/enable/canvas.py index 45a8dc5..74fb6d3 100644 --- a/mapping/enable/canvas.py +++ b/mapping/enable/canvas.py @@ -1,10 +1,10 @@ -from __future__ import print_function +from __future__ import division, print_function import math -from io import BytesIO from enable.api import Canvas, ColorTrait -from kiva.image import Image +from kiva.fonttools import str_to_font +from kiva.image import GraphicsContext from kiva.constants import FILL from traits.api import Int, Range, Instance, on_trait_change @@ -25,36 +25,31 @@ class MappingCanvas(Canvas): # FIXME This is a hack - remove when viewport is fixed _zoom_level = Int(0) - _blank_tile = Instance(Image) + _blank_tile = Instance(GraphicsContext) def __blank_tile_default(self): - import pkg_resources - from PIL import Image as PilImage - from PIL import ImageDraw, ImageFont - - im = PilImage.new('RGB', (256, 256), (234, 224, 216)) + gc = GraphicsContext((256, 256), pix_format='rgba32') + font = str_to_font('swiss 18') + gc.clear((0.9, 0.875, 0.85, 1.0)) text = 'Image not available' - try: - font_file = pkg_resources.resource_filename( - 'mapping.enable', 'fonts/Verdana.ttf' - ) - font = ImageFont.truetype(font_file, 18) - except IOError: - font = ImageFont.load_default() - size = font.getsize(text) - pos = (256-size[0])//2, (256-size[1])//2 - - draw = ImageDraw.Draw(im) - draw.text(pos, text, fill=(200, 200, 200), font=font) - del draw - - tile = BytesIO() - im.save(tile, format='png') - return Image(BytesIO(tile.getvalue())) + with gc: + gc.set_font(font) + gc.set_stroke_color((0.75, 0.75, 0.75, 1.0)) + + width, height, _, _ = gc.get_full_text_extent(text) + pos = (256 - width) // 2, (256 - height) // 2 + gc.translate_ctm(*pos) + gc.show_text(text) + return gc def _tile_cache_changed(self, new): - new.process_raw = lambda d: Image(BytesIO(d)) + fmt_map = {3: 'rgb24', 4: 'rgba32'} + + def process(data): + return GraphicsContext(data, pix_format=fmt_map[data.shape[2]], + interpolation='nearest', bottom_up=True) + new.process_raw = process @on_trait_change('tile_cache:tile_ready') def _tile_ready(self, zoom_row_col): diff --git a/mapping/enable/fonts/Verdana.ttf b/mapping/enable/fonts/Verdana.ttf deleted file mode 100644 index 754a9b7..0000000 Binary files a/mapping/enable/fonts/Verdana.ttf and /dev/null differ diff --git a/mapping/enable/http_tile_manager.py b/mapping/enable/http_tile_manager.py index a3bdfcc..f7e8d99 100644 --- a/mapping/enable/http_tile_manager.py +++ b/mapping/enable/http_tile_manager.py @@ -11,6 +11,7 @@ from .tile_manager import TileManager from .cacheing_decorators import lru_cache from .async_loader import AsyncLoader, AsyncRequest, get_global_async_loader +from .utils import img_data_to_img_array @provides(ITileManager) @@ -54,8 +55,9 @@ def _async_loader_default(self): def _tile_received(self, tile_args, data): zoom, row, col = tile_args['zoom'], tile_args['row'], tile_args['col'] try: - data = self.process_raw(data) - self.get_tile.replace(data, self, zoom, row, col) + img = img_data_to_img_array(data) + img = self.process_raw(img) + self.get_tile.replace(img, self, zoom, row, col) self.tile_ready = (zoom, row, col) except Exception: # Failed to process tile diff --git a/mapping/enable/img_tile_manager.py b/mapping/enable/img_tile_manager.py index d6aeffe..cff44b1 100644 --- a/mapping/enable/img_tile_manager.py +++ b/mapping/enable/img_tile_manager.py @@ -1,11 +1,9 @@ from __future__ import division -from io import BytesIO import os import os.path as op import numpy as np -from PIL import Image from traits.api import String, Tuple, provides @@ -33,12 +31,7 @@ def get_tile(self, zoom, row, col): tile_path = op.join(zoom_dir, '{}.{}.npy'.format(row, col)) if not op.exists(tile_path): return None - - tile = np.load(tile_path) - img = Image.fromarray(tile, mode='RGB') - data = BytesIO() - img.save(data, format='png') - return self.process_raw(data.getvalue()) + return self.process_raw(np.load(tile_path)) def get_tile_size(self): return 256 diff --git a/mapping/enable/mbtile_manager.py b/mapping/enable/mbtile_manager.py index e50dd5c..4bf5108 100644 --- a/mapping/enable/mbtile_manager.py +++ b/mapping/enable/mbtile_manager.py @@ -1,10 +1,10 @@ - from traits.api import Instance, Str, provides from .i_tile_manager import ITileManager from .tile_manager import TileManager from .cacheing_decorators import lru_cache from .mbtiles import MbtileSet +from .utils import img_data_to_img_array @provides(ITileManager) @@ -18,7 +18,8 @@ def get_tile(self, zoom, row, col): if not data: return None else: - return self.process_raw(data) + img = img_data_to_img_array(data) + return self.process_raw(img) def get_tile_size(self): return 256 diff --git a/mapping/enable/utils.py b/mapping/enable/utils.py new file mode 100644 index 0000000..2fbf570 --- /dev/null +++ b/mapping/enable/utils.py @@ -0,0 +1,20 @@ +from io import BytesIO + +import numpy as np +from PIL import Image + +from kiva.compat import piltostring + +DEPTH_MAP = {'RGB': 3, 'RGBA': 4} + + +def img_data_to_img_array(data): + """ Convert a buffer of encoded image data to a numpy array of pixels. + """ + pil_img = Image.open(BytesIO(data)) + if pil_img.mode not in ('RGB', 'RGBA'): + pil_img = pil_img.convert(mode='RGBA') + + depth = DEPTH_MAP[pil_img.mode] + img = np.fromstring(piltostring(pil_img), np.uint8) + return np.resize(img, (pil_img.size[1], pil_img.size[0], depth)) diff --git a/setup.py b/setup.py index b248808..641fffd 100644 --- a/setup.py +++ b/setup.py @@ -30,7 +30,6 @@ include_package_data=True, package_data={ 'mapping': ['data/*'], - 'mapping.enable': ['fonts/*'], }, install_requires=__requires__, license='BSD',