[tilestache] 01/05: Imported Upstream version 1.51.5

Bas Couwenberg sebastic at debian.org
Wed Jan 25 20:37:48 UTC 2017


This is an automated email from the git hooks/post-receive script.

sebastic pushed a commit to branch master
in repository tilestache.

commit 35c6dd9ae5497ecda62dc2a501afdc5ce364b794
Author: Bas Couwenberg <sebastic at xs4all.nl>
Date:   Wed Jan 25 21:23:16 2017 +0100

    Imported Upstream version 1.51.5
---
 .travis.yml                             |  3 +-
 CHANGELOG                               |  5 +++
 README.md                               |  2 +-
 TileStache/Caches.py                    | 14 +++----
 TileStache/Config.py                    | 70 +++++++++------------------------
 TileStache/Core.py                      | 67 +++++++++++++++++++++++++++----
 TileStache/Geography.py                 |  7 ++--
 TileStache/Goodies/VecTiles/client.py   | 20 ++++++++--
 TileStache/Goodies/VecTiles/geojson.py  | 25 ++++++------
 TileStache/Goodies/VecTiles/mvt.py      |  6 ++-
 TileStache/Goodies/VecTiles/server.py   | 19 +++++++--
 TileStache/Goodies/VecTiles/topojson.py |  6 +--
 TileStache/Goodies/VecTiles/wkb.py      |  6 ++-
 TileStache/Mapnik.py                    |  5 ++-
 TileStache/Memcache.py                  |  9 ++++-
 TileStache/Pixels.py                    |  6 ++-
 TileStache/Providers.py                 | 16 ++++++--
 TileStache/VERSION                      |  2 +-
 TileStache/Vector/Arc.py                |  2 +-
 TileStache/Vector/__init__.py           | 17 +++++---
 TileStache/__init__.py                  | 46 ++++++++++++++++------
 requirements.txt                        |  3 +-
 setup.py                                |  2 +-
 tests/cache_tests.py                    | 17 +++++---
 tests/provider_tests.py                 | 11 +++---
 tests/servers/dummy-response-server.py  |  5 ++-
 tests/utils.py                          | 15 ++++---
 tests/vectiles_tests.py                 | 47 +++++++++++-----------
 tests/vector_tests.py                   | 10 ++---
 29 files changed, 287 insertions(+), 176 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 4300763..75450f2 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,12 +8,13 @@ dist: trusty
 
 python:
   - "2.7"
+  - "3.4"
 
 addons:
   postgresql: "9.4"
   apt:
     packages:
-      - postgresql-9.4-postgis-2.2
+      - postgresql-9.4-postgis-2.3
       - python-dev
       - libgdal1-dev
       - python-werkzeug
diff --git a/CHANGELOG b/CHANGELOG
index 360a1e7..236e613 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,8 @@
+2017-01-25: 1.51.5
+- Fix to be compliant with mapnik 3 grid renderer
+- Fix of preview protocol
+- Moving towards python 3 compliance
+
 2016-12-21: 1.51.4
 - Support for GDAL 2.x types OFTInteger64 and OFTInteger64 on Vector Tiles
 
diff --git a/README.md b/README.md
index d23aa28..8c893e5 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
 
 _a stylish alternative for caching your map tiles_
 
-[![Build Status](https://travis-ci.org/TileStache/TileStache.png)](https://travis-ci.org/TileStache/TileStache)
+[![Build Status](https://api.travis-ci.org/TileStache/TileStache.svg?branch=master)](https://travis-ci.org/TileStache/TileStache)
 
 **TileStache** is a Python-based server application that can serve up map tiles
 based on rendered geographic data. You might be familiar with [TileCache](http://tilecache.org),
diff --git a/TileStache/Caches.py b/TileStache/Caches.py
index ffd113b..eae4e5b 100644
--- a/TileStache/Caches.py
+++ b/TileStache/Caches.py
@@ -183,7 +183,7 @@ class Disk:
         "http://example.com/tilestache.cfg", the path *must* be an unambiguous
         filesystem path, e.g. "file:///tmp/cache"
     """
-    def __init__(self, path, umask=0022, dirs='safe', gzip='txt text json xml'.split()):
+    def __init__(self, path, umask=0o022, dirs='safe', gzip='txt text json xml'.split()):
         self.cachepath = path
         self.umask = int(umask)
         self.dirs = dirs
@@ -271,9 +271,9 @@ class Disk:
                         # Oh - no they didn't.
                         pass
                 
-                os.makedirs(lockpath, 0777&~self.umask)
+                os.makedirs(lockpath, 0o777&~self.umask)
                 break
-            except OSError, e:
+            except OSError as e:
                 if e.errno != 17:
                     raise
                 time.sleep(.2)
@@ -300,7 +300,7 @@ class Disk:
         
         try:
             os.remove(fullpath)
-        except OSError, e:
+        except OSError as e:
             # errno=2 means that the file does not exist, which is fine
             if e.errno != 2:
                 raise
@@ -332,8 +332,8 @@ class Disk:
         
         try:
             umask_old = os.umask(self.umask)
-            os.makedirs(dirname(fullpath), 0777&~self.umask)
-        except OSError, e:
+            os.makedirs(dirname(fullpath), 0o777&~self.umask)
+        except OSError as e:
             if e.errno != 17:
                 raise
         finally:
@@ -359,7 +359,7 @@ class Disk:
             os.unlink(fullpath)
             os.rename(tmp_path, fullpath)
 
-        os.chmod(fullpath, 0666&~self.umask)
+        os.chmod(fullpath, 0o666&~self.umask)
 
 class Multi:
     """ Caches tiles to multiple, ordered caches.
diff --git a/TileStache/Config.py b/TileStache/Config.py
index 18c91ef..2dfa6f6 100644
--- a/TileStache/Config.py
+++ b/TileStache/Config.py
@@ -60,11 +60,19 @@ documentation for TileStache.Providers, TileStache.Core, and TileStache.Geograph
 
 import sys
 import logging
-from sys import stderr, modules
+from sys import modules
 from os.path import realpath, join as pathjoin
-from urlparse import urljoin, urlparse
+try:
+    from urllib.parse import urljoin, urlparse
+except ImportError:
+    # Python 2
+    from urlparse import urljoin, urlparse
 from mimetypes import guess_type
-from urllib import urlopen
+try:
+    from urllib.request import urlopen
+except ImportError:
+    # Python 2
+    from urllib import urlopen
 from json import dumps
 
 try:
@@ -75,11 +83,11 @@ except ImportError:
 from ModestMaps.Geo import Location
 from ModestMaps.Core import Coordinate
 
-import Core
-import Caches
-import Providers
-import Geography
-import PixelEffects
+from . import Core
+from . import Caches
+from . import Providers
+from . import Geography
+from . import PixelEffects
 
 class Configuration:
     """ A complete site configuration, with a collection of Layer objects.
@@ -315,7 +323,7 @@ def _parseConfigCache(cache_dict, dirpath):
             raise Exception('Unknown cache: %s' % cache_dict['name'])
 
     elif 'class' in cache_dict:
-        _class = loadClassPath(cache_dict['class'])
+        _class = Core.loadClassPath(cache_dict['class'])
         kwargs = cache_dict.get('kwargs', {})
         kwargs = dict( [(str(k), v) for (k, v) in kwargs.items()] )
 
@@ -449,7 +457,7 @@ def _parseConfigLayer(layer_dict, config, dirpath):
         provider_kwargs = _class.prepareKeywordArgs(provider_dict)
 
     elif 'class' in provider_dict:
-        _class = loadClassPath(provider_dict['class'])
+        _class = Core.loadClassPath(provider_dict['class'])
         provider_kwargs = provider_dict.get('kwargs', {})
         provider_kwargs = dict( [(str(k), v) for (k, v) in provider_kwargs.items()] )
 
@@ -467,45 +475,3 @@ def _parseConfigLayer(layer_dict, config, dirpath):
     layer.pixel_effect = pixel_effect
 
     return layer
-
-def loadClassPath(classpath):
-    """ Load external class based on a path.
-
-        Example classpath: "Module.Submodule:Classname".
-
-        Equivalent soon-to-be-deprecated classpath: "Module.Submodule.Classname".
-    """
-    if ':' in classpath:
-        #
-        # Just-added support for "foo:blah"-style classpaths.
-        #
-        modname, objname = classpath.split(':', 1)
-
-        try:
-            __import__(modname)
-            module = modules[modname]
-            _class = eval(objname, module.__dict__)
-
-            if _class is None:
-                raise Exception('eval(%(objname)s) in %(modname)s came up None' % locals())
-
-        except Exception, e:
-            raise Core.KnownUnknown('Tried to import %s, but: %s' % (classpath, e))
-
-    else:
-        #
-        # Support for "foo.blah"-style classpaths, TODO: deprecate this in v2.
-        #
-        classpath = classpath.split('.')
-
-        try:
-            module = __import__('.'.join(classpath[:-1]), fromlist=str(classpath[-1]))
-        except ImportError, e:
-            raise Core.KnownUnknown('Tried to import %s, but: %s' % ('.'.join(classpath), e))
-
-        try:
-            _class = getattr(module, classpath[-1])
-        except AttributeError, e:
-            raise Core.KnownUnknown('Tried to import %s, but: %s' % ('.'.join(classpath), e))
-
-    return _class
diff --git a/TileStache/Core.py b/TileStache/Core.py
index 2b60e6c..163da22 100644
--- a/TileStache/Core.py
+++ b/TileStache/Core.py
@@ -144,12 +144,21 @@ The preview can be accessed through a URL like /<layer name>/preview.html:
 """
 
 import logging
+from sys import modules
 from wsgiref.headers import Headers
-from StringIO import StringIO
-from urlparse import urljoin
+try:
+    from io import BytesIO
+except ImportError:
+    # Python 2
+    from StringIO import StringIO as BytesIO
+try:
+    from urllib.parse import urljoin
+except ImportError:
+    # Python 2
+    from urlparse import urljoin
 from time import time
 
-from Pixels import load_palette, apply_palette, apply_palette256
+from .Pixels import load_palette, apply_palette, apply_palette256
 
 try:
     from PIL import Image
@@ -382,7 +391,7 @@ class Layer:
             # Start by checking for a tile in the cache.
             try:
                 body = cache.read(self, coord, format)
-            except TheTileLeftANote, e:
+            except TheTileLeftANote as e:
                 headers = e.headers
                 status_code = e.status_code
                 body = e.content
@@ -417,12 +426,12 @@ class Layer:
 
                 if body is None:
                     # No one else wrote the tile, do it here.
-                    buff = StringIO()
+                    buff = BytesIO()
 
                     try:
                         tile = self.render(coord, format)
                         save = True
-                    except NoTileLeftBehind, e:
+                    except NoTileLeftBehind as e:
                         tile = e.tile
                         save = False
                         status_code = 404
@@ -445,7 +454,7 @@ class Layer:
 
                     tile_from = 'layer.render()'
 
-            except TheTileLeftANote, e:
+            except TheTileLeftANote as e:
                 headers = e.headers
                 status_code = e.status_code
                 body = e.content
@@ -733,7 +742,7 @@ def _preview(layer):
 <html>
 <head>
     <title>TileStache Preview: %(layername)s</title>
-    <script src="http://cdn.rawgit.com/stamen/modestmaps-js/v1.0.0-beta1/modestmaps.min.js" type="text/javascript"></script>
+    <script src="//cdn.rawgit.com/stamen/modestmaps-js/v1.0.0-beta1/modestmaps.min.js" type="text/javascript"></script>
     <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
     <style type="text/css">
         html, body, #map {
@@ -818,3 +827,45 @@ def _rummy():
             '##Mh at M  .    ...:;;,:@A#@@@@@@@@@@@#@@@@@@#MMHAB@@@@#G#@@#: i@@       r@@#MMM#######@@@@#@@@@@@#####M#####@@',
             '#H3#@3. ,.    ...  :@@&@@@@@@@@@@@@@#@@#@@@MMBHGA at H&;:@@i :B@@@B     .@@#MM####@@@##@@@#@@@@@#######M##M#@@@',
             'M&AM5i;.,.   ..,,rA@@MH@@@@@@@@@@@@@##@@@@@MMMBB#@h9hH#s;3######,   .A@#MMM#####@@@@@##@@@#@@#####M#####M39B']
+
+def loadClassPath(classpath):
+    """ Load external class based on a path.
+
+        Example classpath: "Module.Submodule:Classname".
+
+        Equivalent soon-to-be-deprecated classpath: "Module.Submodule.Classname".
+    """
+    if ':' in classpath:
+        #
+        # Just-added support for "foo:blah"-style classpaths.
+        #
+        modname, objname = classpath.split(':', 1)
+
+        try:
+            __import__(modname)
+            module = modules[modname]
+            _class = eval(objname, module.__dict__)
+
+            if _class is None:
+                raise Exception('eval(%(objname)s) in %(modname)s came up None' % locals())
+
+        except Exception as e:
+            raise KnownUnknown('Tried to import %s, but: %s' % (classpath, e))
+
+    else:
+        #
+        # Support for "foo.blah"-style classpaths, TODO: deprecate this in v2.
+        #
+        classpath = classpath.split('.')
+
+        try:
+            module = __import__('.'.join(classpath[:-1]), fromlist=str(classpath[-1]))
+        except ImportError as e:
+            raise KnownUnknown('Tried to import %s, but: %s' % ('.'.join(classpath), e))
+
+        try:
+            _class = getattr(module, classpath[-1])
+        except AttributeError as e:
+            raise KnownUnknown('Tried to import %s, but: %s' % ('.'.join(classpath), e))
+
+    return _class
diff --git a/TileStache/Geography.py b/TileStache/Geography.py
index 62160b3..366ecb3 100644
--- a/TileStache/Geography.py
+++ b/TileStache/Geography.py
@@ -36,8 +36,7 @@ from ModestMaps.Core import Point, Coordinate
 from ModestMaps.Geo import deriveTransformation, MercatorProjection, LinearProjection, Location
 from math import log as _log, pi as _pi
 
-import Core
-import Config
+from . import Core
 
 class SphericalMercator(MercatorProjection):
     """ Spherical mercator projection for most commonly-used web map tile scheme.
@@ -143,6 +142,6 @@ def getProjectionByName(name):
         
     else:
         try:
-            return Config.loadClassPath(name)
-        except Exception, e:
+            return Core.loadClassPath(name)
+        except Exception as e:
             raise Core.KnownUnknown('Failed projection in configuration: "%s" - %s' % (name, e))
diff --git a/TileStache/Goodies/VecTiles/client.py b/TileStache/Goodies/VecTiles/client.py
index 49e16ea..efde249 100644
--- a/TileStache/Goodies/VecTiles/client.py
+++ b/TileStache/Goodies/VecTiles/client.py
@@ -32,10 +32,22 @@ See also:
 '''
 from math import pi, log as _log
 from threading import Thread, Lock as _Lock
-from httplib import HTTPConnection
+try:
+    from http.client import HTTPConnection
+except ImportError:
+    # Python 2
+    from httplib import HTTPConnection
 from itertools import product
-from StringIO import StringIO
-from urlparse import urlparse
+try:
+    from io import StringIO
+except ImportError:
+    # Python 2
+    from StringIO import StringIO
+try:
+    from urllib.parse import urlparse
+except ImportError:
+    # Python 2
+    from urlparse import urlparse
 from gzip import GzipFile
 
 import logging
@@ -259,7 +271,7 @@ class Datasource (PythonDatasource):
             
         if self.sort:
             logging.debug('Sorting by %s %s' % (self.sort, 'descending' if self.reverse else 'ascending'))
-            key_func = lambda (wkb, props): props.get(self.sort, None)
+            key_func = lambda wkb_props: wkb_props[1].get(self.sort, None)
             features.sort(reverse=self.reverse, key=key_func)
         
         if len(features) == 0:
diff --git a/TileStache/Goodies/VecTiles/geojson.py b/TileStache/Goodies/VecTiles/geojson.py
index 6638388..6bbde69 100644
--- a/TileStache/Goodies/VecTiles/geojson.py
+++ b/TileStache/Goodies/VecTiles/geojson.py
@@ -34,7 +34,7 @@ def get_tiles(names, config, coord):
     if bad_mimes:
         raise KnownUnknown('%s.get_tiles encountered a non-JSON mime-type in %s sub-layer: "%s"' % ((__name__, ) + bad_mimes[0]))
     
-    geojsons = map(json.loads, bodies)
+    geojsons = [json.loads(body.decode('utf8')) for body in bodies]
     bad_types = [(name, topo['type']) for (topo, name) in zip(geojsons, names) if topo['type'] != 'FeatureCollection']
     
     if bad_types:
@@ -42,10 +42,11 @@ def get_tiles(names, config, coord):
     
     return geojsons
 
-def mercator((x, y)):
+def mercator(xy):
     ''' Project an (x, y) tuple to spherical mercator.
     '''
-    x, y = pi * x/180, pi * y/180
+    _x, _y = xy
+    x, y = pi * _x/180, pi * _y/180
     y = log(tan(0.25 * pi + 0.5 * y))
     return 6378137 * x, 6378137 * y
 
@@ -98,14 +99,13 @@ def encode(file, features, zoom, is_clipped):
     for token in encoded:
         if charfloat_pat.match(token):
             # in python 2.7, we see a character followed by a float literal
-            file.write(token[0] + flt_fmt % float(token[1:]))
-        
+            piece = token[0] + flt_fmt % float(token[1:])
         elif float_pat.match(token):
             # in python 2.6, we see a simple float literal
-            file.write(flt_fmt % float(token))
-        
+            piece = flt_fmt % float(token)
         else:
-            file.write(token)
+            piece = token
+        file.write(piece.encode('utf8'))
 
 def merge(file, names, config, coord):
     ''' Retrieve a list of GeoJSON tile responses and merge them into one.
@@ -122,11 +122,10 @@ def merge(file, names, config, coord):
     for token in encoded:
         if charfloat_pat.match(token):
             # in python 2.7, we see a character followed by a float literal
-            file.write(token[0] + flt_fmt % float(token[1:]))
-        
+            piece = token[0] + flt_fmt % float(token[1:])
         elif float_pat.match(token):
             # in python 2.6, we see a simple float literal
-            file.write(flt_fmt % float(token))
-        
+            piece = flt_fmt % float(token)
         else:
-            file.write(token)
+            piece = token
+        file.write(piece.encode('utf8'))
diff --git a/TileStache/Goodies/VecTiles/mvt.py b/TileStache/Goodies/VecTiles/mvt.py
index 73dc62c..88fc952 100644
--- a/TileStache/Goodies/VecTiles/mvt.py
+++ b/TileStache/Goodies/VecTiles/mvt.py
@@ -36,7 +36,11 @@ By default, encode() approximates the floating point precision of WKB geometry
 to 26 bits for a significant compression improvement and no visible impact on
 rendering at zoom 18 and lower.
 '''
-from StringIO import StringIO
+try:
+    from io import StringIO
+except ImportError:
+    # Python 2
+    from StringIO import StringIO
 from zlib import decompress as _decompress, compress as _compress
 from struct import unpack as _unpack, pack as _pack
 import json
diff --git a/TileStache/Goodies/VecTiles/server.py b/TileStache/Goodies/VecTiles/server.py
index 9326239..afc5288 100644
--- a/TileStache/Goodies/VecTiles/server.py
+++ b/TileStache/Goodies/VecTiles/server.py
@@ -8,8 +8,16 @@ For a more general implementation, try the Vector provider:
     http://tilestache.org/doc/#vector-provider
 '''
 from math import pi
-from urlparse import urljoin, urlparse
-from urllib import urlopen
+try:
+    from urllib.parse import urljoin, urlparse
+except ImportError:
+    # Python 2
+    from urlparse import urljoin, urlparse
+try:
+    from urllib.request import urlopen
+except ImportError:
+    # Python 2
+    from urllib import urlopen
 from os.path import exists
 
 try:
@@ -17,7 +25,7 @@ try:
     from psycopg2 import connect
     from psycopg2.extensions import TransactionRollbackError
 
-except ImportError, err:
+except ImportError as err:
     # Still possible to build the documentation without psycopg2
 
     def connect(*args, **kwargs):
@@ -182,7 +190,10 @@ class Provider:
         
         if query not in self.columns:
             self.columns[query] = query_columns(self.dbinfo, self.srid, query, bounds)
-        
+
+        if not self.columns[query]:
+            return EmptyResponse(bounds)
+
         tolerance = self.simplify * tolerances[coord.zoom] if coord.zoom < self.simplify_until else None
         
         return Response(self.dbinfo, self.srid, query, self.columns[query], bounds, tolerance, coord.zoom, self.clip, coord, self.layer.name(), self.padding)
diff --git a/TileStache/Goodies/VecTiles/topojson.py b/TileStache/Goodies/VecTiles/topojson.py
index 04c775b..9bcd13b 100644
--- a/TileStache/Goodies/VecTiles/topojson.py
+++ b/TileStache/Goodies/VecTiles/topojson.py
@@ -23,7 +23,7 @@ def get_tiles(names, config, coord):
     if bad_mimes:
         raise KnownUnknown('%s.get_tiles encountered a non-JSON mime-type in %s sub-layer: "%s"' % ((__name__, ) + bad_mimes[0]))
     
-    topojsons = map(json.loads, bodies)
+    topojsons = [json.loads(body.decode('utf8')) for body in bodies]
     bad_types = [(name, topo['type']) for (topo, name) in zip(topojsons, names) if topo['type'] != 'Topology']
     
     if bad_types:
@@ -188,7 +188,7 @@ def encode(file, features, bounds, is_clipped):
         'arcs': arcs
         }
     
-    json.dump(result, file, separators=(',', ':'))
+    file.write(json.dumps(result, separators=(',', ':')).encode('utf8'))
 
 def merge(file, names, config, coord):
     ''' Retrieve a list of TopoJSON tile responses and merge them into one.
@@ -214,4 +214,4 @@ def merge(file, names, config, coord):
             for geometry in object['geometries']:
                 update_arc_indexes(geometry, output['arcs'], input['arcs'])
     
-    json.dump(output, file, separators=(',', ':'))
+    file.write(json.dumps(output, separators=(',', ':')).encode('utf8'))
diff --git a/TileStache/Goodies/VecTiles/wkb.py b/TileStache/Goodies/VecTiles/wkb.py
index 51c5d97..0ec954d 100644
--- a/TileStache/Goodies/VecTiles/wkb.py
+++ b/TileStache/Goodies/VecTiles/wkb.py
@@ -14,7 +14,11 @@ See also:
 '''
 
 from struct import unpack
-from StringIO import StringIO
+try:
+    from io import StringIO
+except ImportError:
+    # Python 2
+    from StringIO import StringIO
 
 #
 # wkbByteOrder
diff --git a/TileStache/Mapnik.py b/TileStache/Mapnik.py
index 8c14299..ef762b2 100644
--- a/TileStache/Mapnik.py
+++ b/TileStache/Mapnik.py
@@ -285,8 +285,9 @@ class GridProvider:
                     for (index, fields) in self.layers:
                         datasource = self.mapnik.layers[index].datasource
                         fields = (type(fields) is list) and map(str, fields) or datasource.fields()
-
-                        grid = mapnik.render_grid(self.mapnik, index, resolution=self.scale, fields=fields)
+                        grid = mapnik.Grid(width, height)
+                        mapnik.render_layer(self.mapnik, grid, layer=index, fields=fields)
+                        grid = grid.encode('utf', resolution=self.scale, features=True)
 
                         for key in grid['data']:
                             grid['data'][key][self.layer_id_key] = self.mapnik.layers[index].name
diff --git a/TileStache/Memcache.py b/TileStache/Memcache.py
index 43f5494..e1455a9 100644
--- a/TileStache/Memcache.py
+++ b/TileStache/Memcache.py
@@ -33,6 +33,7 @@ Memcache cache parameters:
 """
 from __future__ import absolute_import
 from time import time as _time, sleep as _sleep
+from base64 import b64encode, b64decode
 
 # We enabled absolute_import because case insensitive filesystems
 # cause this file to be loaded twice (the name of this file
@@ -109,7 +110,10 @@ class Cache:
         value = mem.get(key)
         mem.disconnect_all()
         
-        return value
+        if value is None:
+            return None
+        
+        return b64decode(value.encode('ascii'))
         
     def save(self, body, layer, coord, format):
         """ Save a cached tile.
@@ -117,5 +121,8 @@ class Cache:
         mem = Client(self.servers)
         key = tile_key(layer, coord, format, self.revision, self.key_prefix)
         
+        if body is not None:
+            body = b64encode(body).decode('ascii')
+        
         mem.set(key, body, layer.cache_lifespan or 0)
         mem.disconnect_all()
diff --git a/TileStache/Pixels.py b/TileStache/Pixels.py
index 399d0d3..dc652e7 100644
--- a/TileStache/Pixels.py
+++ b/TileStache/Pixels.py
@@ -23,7 +23,11 @@ in the lookup table. If the final byte is 0xFFFF, there is no transparency.
 """
 from struct import unpack, pack
 from math import sqrt, ceil, log
-from urllib import urlopen
+try:
+    from urllib.request import urlopen
+except ImportError:
+    # Python 2
+    from urllib import urlopen
 from operator import add
 
 try:
diff --git a/TileStache/Providers.py b/TileStache/Providers.py
index 30139fa..ca669c4 100644
--- a/TileStache/Providers.py
+++ b/TileStache/Providers.py
@@ -73,9 +73,17 @@ For an example of a non-image provider, see TileStache.Vector.Provider.
 import os
 import logging
 
-from StringIO import StringIO
+try:
+    from io import BytesIO
+except ImportError:
+    # Python 2
+    from StringIO import StringIO as BytesIO
 from string import Template
-import urllib2
+try:
+    import urllib.request as urllib2
+except ImportError:
+    # Python 2
+    import urllib2
 import urllib
 
 try:
@@ -87,7 +95,7 @@ except ImportError:
 import ModestMaps
 from ModestMaps.Core import Point, Coordinate
 
-import Geography
+from . import Geography
 
 # This import should happen inside getProviderByName(), but when testing
 # on Mac OS X features are missing from output. Wierd-ass C libraries...
@@ -140,7 +148,7 @@ class Verbatim:
     ''' Wrapper for PIL.Image that saves raw input bytes if modes and formats match.
     '''
     def __init__(self, bytes):
-        self.buffer = StringIO(bytes)
+        self.buffer = BytesIO(bytes)
         self.format = None
         self._image = None
         
diff --git a/TileStache/VERSION b/TileStache/VERSION
index 858461d..9d343dd 100644
--- a/TileStache/VERSION
+++ b/TileStache/VERSION
@@ -1 +1 @@
-1.51.4
+1.51.5
diff --git a/TileStache/Vector/Arc.py b/TileStache/Vector/Arc.py
index 958a62e..ae7364c 100644
--- a/TileStache/Vector/Arc.py
+++ b/TileStache/Vector/Arc.py
@@ -2,7 +2,7 @@
 """
 from operator import add
 
-from TileStache.Core import KnownUnknown
+from ..Core import KnownUnknown
 
 geometry_types = {
     'Point': 'esriGeometryPoint',
diff --git a/TileStache/Vector/__init__.py b/TileStache/Vector/__init__.py
index d64681f..0a53348 100644
--- a/TileStache/Vector/__init__.py
+++ b/TileStache/Vector/__init__.py
@@ -154,7 +154,11 @@ you can save yourself a world of trouble by using this definition:
 """
 
 from re import compile
-from urlparse import urlparse, urljoin
+try:
+    from urllib.parse import urljoin, urlparse
+except ImportError:
+    # Python 2
+    from urlparse import urljoin, urlparse
 
 try:
     from json import JSONEncoder, loads as json_loads
@@ -163,9 +167,9 @@ except ImportError:
 
 from osgeo import ogr, osr
 
-from TileStache.Core import KnownUnknown
-from TileStache.Geography import getProjectionByName
-from Arc import reserialize_to_arc, pyamf_classes
+from ..Core import KnownUnknown
+from ..Geography import getProjectionByName
+from .Arc import reserialize_to_arc, pyamf_classes
 
 class VectorResponse:
     """ Wrapper class for Vector response that makes it behave like a PIL.Image object.
@@ -220,9 +224,10 @@ class VectorResponse:
     
             for atom in encoded:
                 if float_pat.match(atom):
-                    out.write(('%%.%if' % self.precision) % float(atom))
+                    piece = ('%%.%if' % self.precision) % float(atom)
                 else:
-                    out.write(atom)
+                    piece = atom
+                out.write(piece.encode('utf8'))
         
         elif format in ('GeoBSON', 'ArcBSON'):
             import bson
diff --git a/TileStache/__init__.py b/TileStache/__init__.py
index fb9374c..ef0a501 100644
--- a/TileStache/__init__.py
+++ b/TileStache/__init__.py
@@ -8,6 +8,7 @@ designers and cartographers.
 
 Documentation available at http://tilestache.org/doc/
 """
+from __future__ import print_function
 import os.path
 
 __version__ = open(os.path.join(os.path.dirname(__file__), 'VERSION')).read().strip()
@@ -19,16 +20,32 @@ try:
     from urlparse import parse_qs
 except ImportError:
     from cgi import parse_qs
-from StringIO import StringIO
+try:
+    from io import StringIO
+except ImportError:
+    # Python 2
+    from StringIO import StringIO
 from os.path import dirname, join as pathjoin, realpath
 from datetime import datetime, timedelta
-from urlparse import urljoin, urlparse
+try:
+    from urllib.parse import urljoin, urlparse
+except ImportError:
+    # Python 2
+    from urlparse import urljoin, urlparse
 from wsgiref.headers import Headers
-from urllib import urlopen
+try:
+    from urllib.request import urlopen
+except ImportError:
+    # Python 2
+    from urllib import urlopen
 from os import getcwd
 from time import time
 
-import httplib
+try:
+    import http.client as httplib
+except ImportError:
+    # Python 2
+    import httplib
 import logging
 
 try:
@@ -43,8 +60,8 @@ from ModestMaps.Core import Coordinate
 # dictionary of configuration objects for requestLayer().
 _previous_configs = {}
 
-import Core
-import Config
+from . import Core
+from . import Config
 
 # regular expression for PATH_INFO
 _pathinfo_pat = re.compile(r'^/?(?P<l>\w.+)/(?P<z>\d+)/(?P<x>-?\d+)/(?P<y>-?\d+)\.(?P<e>\w+)$')
@@ -102,13 +119,18 @@ def parseConfig(configHandle):
         config_dict = configHandle
         dirpath = '.'
     else:
-        config_dict = json_load(urlopen(configHandle))
         scheme, host, path, p, q, f = urlparse(configHandle)
-
+        
         if scheme == '':
             scheme = 'file'
             path = realpath(path)
 
+        if scheme == 'file':
+            with open(path) as file:
+                config_dict = json_load(file)
+        else:
+            config_dict = json_load(urlopen(configHandle))
+
         dirpath = '%s://%s%s' % (scheme, host, dirname(path).rstrip('/') + '/')
 
     return Config.buildConfiguration(config_dict, dirpath)
@@ -275,7 +297,7 @@ def requestHandler2(config_hint, path_info, query_string=None, script_name=''):
             headers.setdefault('Expires', expires.strftime('%a, %d %b %Y %H:%M:%S GMT'))
             headers.setdefault('Cache-Control', 'public, max-age=%d' % layer.max_cache_age)
 
-    except Core.KnownUnknown, e:
+    except Core.KnownUnknown as e:
         out = StringIO()
 
         print >> out, 'Known unknown!'
@@ -351,7 +373,7 @@ class WSGITileServer:
             try:
                 self.config = parseConfig(config)
             except:
-                print "Error loading Tilestache config:"
+                print("Error loading Tilestache config:")
                 raise
 
         else:
@@ -369,12 +391,12 @@ class WSGITileServer:
         if self.autoreload: # re-parse the config file on every request
             try:
                 self.config = parseConfig(self.config_path)
-            except Exception, e:
+            except Exception as e:
                 raise Core.KnownUnknown("Error loading Tilestache config file:\n%s" % str(e))
 
         try:
             layer, coord, ext = splitPathInfo(environ['PATH_INFO'])
-        except Core.KnownUnknown, e:
+        except Core.KnownUnknown as e:
             return self._response(start_response, 400, str(e))
 
         #
diff --git a/requirements.txt b/requirements.txt
index 370c8da..fe6f600 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,7 +1,8 @@
-ModestMaps
+ModestMaps==1.4.7
 simplejson
 shapely
 pillow
 psycopg2
 python-memcached
 mapbox-vector-tile==0.5.0
+Werkzeug==0.11.13
\ No newline at end of file
diff --git a/setup.py b/setup.py
index 385984f..8fe24aa 100644
--- a/setup.py
+++ b/setup.py
@@ -20,7 +20,7 @@ def is_installed(name):
         return False
 
 
-requires = ['ModestMaps >=1.3.0','simplejson', 'Werkzeug', 'Pillow']
+requires = ['ModestMaps >=1.3.0','simplejson', 'Werkzeug == 0.11.13', 'Pillow']
 
 
 setup(name='TileStache',
diff --git a/tests/cache_tests.py b/tests/cache_tests.py
index 66e2e68..fda08f9 100644
--- a/tests/cache_tests.py
+++ b/tests/cache_tests.py
@@ -1,5 +1,6 @@
 import os
 from unittest import TestCase, skipIf
+from base64 import b64decode
 import memcache
 from . import utils
 
@@ -36,8 +37,10 @@ class CacheTests(TestCase):
         tile_mimetype, tile_content = utils.request(config_file_content, "memcache_osm", "png", 0, 0, 0)
         self.assertEqual(tile_mimetype, "image/png")
 
-        self.assertEqual(self.mc.get('/4/memcache_osm/0/0/0.PNG'), tile_content,
-            'Contents of memcached and value returned from TileStache do not match')
+        memcache_content = b64decode(self.mc.get('/4/memcache_osm/0/0/0.PNG').encode('ascii'))
+        
+        self.assertEqual(memcache_content, tile_content,
+            'Contents of memcached and value returned from TileStache should match')
 
     def test_memcache_keyprefix(self):
         '''Fetch tile and check the existence of key with prefix in memcached'''
@@ -64,8 +67,10 @@ class CacheTests(TestCase):
         tile_mimetype, tile_content = utils.request(config_file_content, "memcache_osm", "png", 0, 0, 0)
         self.assertEqual(tile_mimetype, "image/png")
 
-        self.assertEqual(self.mc.get('cool_prefix/1/memcache_osm/0/0/0.PNG'), tile_content,
-            'Contents of memcached and value returned from TileStache do not match')
+        memcache_content = b64decode(self.mc.get('cool_prefix/1/memcache_osm/0/0/0.PNG').encode('ascii'))
+        
+        self.assertEqual(memcache_content, tile_content,
+            'Contents of memcached and value returned from TileStache should match')
 
-        self.assertEqual(self.mc.get('/1/memcache_osm/0/0/0.PNG'), None,
-            'Memcache returned a value even though it should have been empty')
+        self.assertIsNone(self.mc.get('/1/memcache_osm/0/0/0.PNG'),
+            'Memcache value should be empty')
diff --git a/tests/provider_tests.py b/tests/provider_tests.py
index 181f271..130b552 100644
--- a/tests/provider_tests.py
+++ b/tests/provider_tests.py
@@ -30,7 +30,7 @@ class ProviderTests(TestCase):
 
         tile_mimetype, tile_content = utils.request(config_file_content, "osm", "png", 0, 0, 0)
         self.assertEqual(tile_mimetype, "image/png")
-        self.assertTrue(tile_content[:4] in '\x89\x50\x4e\x47') #check it is a png based on png magic number
+        self.assertTrue(tile_content[:4] in b'\x89\x50\x4e\x47') #check it is a png based on png magic number
 
 
     def test_url_template_wgs84(self):
@@ -55,12 +55,12 @@ class ProviderTests(TestCase):
 
         tile_mimetype, tile_content = utils.request(config_file_content, "osgeo_wms", "png", 0, 0, 0)
         self.assertEqual(tile_mimetype, "image/png")
-        self.assertTrue(tile_content[:4] in '\x89\x50\x4e\x47') #check it is a png based on png magic number
+        self.assertTrue(tile_content[:4] in b'\x89\x50\x4e\x47') #check it is a png based on png magic number
 
         #in WGS84 we typically have two tiles at zoom level 0. Get the second tile
         tile_mimetype, tile_content = utils.request(config_file_content, "osgeo_wms", "png", 0, 1, 0)
         self.assertEqual(tile_mimetype, "image/png")
-        self.assertTrue(tile_content[:4] in '\x89\x50\x4e\x47') #check it is a png based on png magic number
+        self.assertTrue(tile_content[:4] in b'\x89\x50\x4e\x47') #check it is a png based on png magic number
 
 
 class ProviderWithDummyResponseServer(TestCase):
@@ -71,8 +71,9 @@ class ProviderWithDummyResponseServer(TestCase):
     '''
 
     def setUp(self):
-        #create custom binary file that pretends to be a png and a server that always returns the same response
-        self.response_content = '\x89\x50\x4e\x47Meh, I am a custom file that loves utf8 chars like éøæ®!!!'
+        # Create custom binary file that pretends to be a png and a server that always returns the same response
+        # Smallest PNG from http://garethrees.org/2007/11/14/pngcrush/
+        self.response_content = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x01\x00\x00\x00\x007n\xf9$\x00\x00\x00\nIDATx\x9cc`\x00\x00\x00\x02\x00\x01H\xaf\xa4q\x00\x00\x00\x00IEND\xaeB`\x82'
         self.response_mimetype = 'image/png'
 
         self.temp_file_name = utils.create_temp_file(self.response_content)
diff --git a/tests/servers/dummy-response-server.py b/tests/servers/dummy-response-server.py
index 74d2514..2bd6902 100755
--- a/tests/servers/dummy-response-server.py
+++ b/tests/servers/dummy-response-server.py
@@ -1,3 +1,4 @@
+from __future__ import print_function
 import argparse
 from werkzeug.wrappers import Request, Response
 
@@ -17,14 +18,14 @@ if __name__ == '__main__':
     args = parser.parse_args()
 
     #read file into buffer
-    print 'Response Content: ' + args.response_file
+    print('Response Content:', args.response_file)
     global response_content
     f = open(args.response_file, 'rb')
     response_content = f.read()
     f.close()
 
     #set mimetype
-    print 'Response Mimetype: ' + args.response_mimetype
+    print('Response Mimetype:', args.response_mimetype)
     global response_mimetype
     response_mimetype = args.response_mimetype
 
diff --git a/tests/utils.py b/tests/utils.py
index 46c17c8..346c8b5 100644
--- a/tests/utils.py
+++ b/tests/utils.py
@@ -1,3 +1,4 @@
+from __future__ import print_function
 from tempfile import mkstemp
 import os
 import inspect
@@ -22,8 +23,11 @@ def request(config_content, layer_name, format, row, column, zoom):
     Helper method to write config_file to disk and do
     request
     '''
+    if sys.version_info.major == 2:
+        is_string = isinstance(config_content, basestring)
+    else:
+        is_string = isinstance(config_content, (str, bytes))
 
-    is_string = isinstance(config_content, basestring)
     if is_string:
         absolute_file_name = create_temp_file(config_content)
         config = parseConfig(absolute_file_name)
@@ -46,7 +50,7 @@ def create_temp_file(buffer):
     for deleting file once done
     '''
     fd, absolute_file_name = mkstemp(text=True)
-    file = os.fdopen(fd, 'w+b')
+    file = os.fdopen(fd, 'wb' if (type(buffer) is bytes) else 'w')
     file.write(buffer)
     file.close()
     return absolute_file_name
@@ -58,8 +62,7 @@ def create_dummy_server(file_with_content, mimetype):
     mimetype specified
     '''
 
-    # see http://stackoverflow.com/questions/50499/in-python-how-do-i-get-the-path-and-name-of-the-file-that-is-currently-executin
-    current_script_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
+    current_script_dir = os.path.dirname(os.path.abspath(__file__))
 
     #start new process using our dummy-response-server.py script
     dummy_server_file = os.path.join(current_script_dir, 'servers', 'dummy-response-server.py')
@@ -81,7 +84,7 @@ def create_dummy_server(file_with_content, mimetype):
     t.daemon = True # thread dies with the program
     t.start()
 
-    server_output = ''
+    server_output = b''
 
     # read line and enter busy loop until the server says it is ok
     while True:
@@ -97,7 +100,7 @@ def create_dummy_server(file_with_content, mimetype):
             continue
         else: # got line
             server_output += line
-            if "Running on http://" in server_output:
+            if b"Running on http://" in server_output:
                 break; #server is running, get out of here
             else:
                 continue
diff --git a/tests/vectiles_tests.py b/tests/vectiles_tests.py
index 95adb1b..22918d8 100644
--- a/tests/vectiles_tests.py
+++ b/tests/vectiles_tests.py
@@ -1,3 +1,5 @@
+from __future__ import print_function
+
 import os
 from unittest import TestCase, skipIf
 from collections import namedtuple
@@ -19,7 +21,8 @@ from . import utils
 def get_topo_transform(topojson):
     '''
     '''
-    def xform((x, y)):
+    def xform(xy):
+        x, y = xy
         lon = topojson['transform']['scale'][0] * x + topojson['transform']['translate'][0]
         lat = topojson['transform']['scale'][1] * y + topojson['transform']['translate'][1]
         
@@ -94,12 +97,11 @@ def decoded_pbf_asshape(feature, extent, srid=4326):
         3: "Polygon"
     }
     if feature['type'] in (1, 2):
-        coords = [
-            trans_coord(3857, srid, *coord2merc(*g, extent=extent)) for g in feature['geometry']]
+        coords = [trans_coord(3857, srid, *coord2merc(x, y, extent=extent))
+            for (x, y) in feature['geometry']]
     elif feature['type'] == 3:
-        coords = [
-            [trans_coord(3857, srid, *coord2merc(*g, extent=extent)) for g in feature['geometry'][0]]
-        ]
+        coords = [[trans_coord(3857, srid, *coord2merc(x, y, extent=extent))
+            for (x, y) in feature['geometry'][0]]]
     geoint = {
         'type': TYPES_MAP.get(feature['type']),
         'coordinates': coords,
@@ -235,7 +237,7 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
         # northwest quadrant should return San Francisco and Lima
 
         tile_mimetype, tile_content = utils.request(self.config_file_content, "vectile_test", "json", 0, 0, 1)
-        geojson_result = json.loads(tile_content)
+        geojson_result = json.loads(tile_content.decode('utf8'))
 
         self.assertTrue(tile_mimetype.endswith('/json'))
         self.assertEqual(geojson_result['type'], 'FeatureCollection')
@@ -261,7 +263,7 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
         # northeast quadrant should return Berlin
 
         tile_mimetype, tile_content = utils.request(self.config_file_content, "vectile_test", "json", 0, 1, 1)
-        geojson_result = json.loads(tile_content)
+        geojson_result = json.loads(tile_content.decode('utf8'))
 
         self.assertTrue(tile_mimetype.endswith('/json'))
         self.assertEqual(geojson_result['type'], 'FeatureCollection')
@@ -282,7 +284,7 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
 
         tile_mimetype, tile_content = utils.request(self.config_file_content, "vectile_test", "json", 0, 0, 0)
         self.assertTrue(tile_mimetype.endswith('/json'))
-        geojson_result = json.loads(tile_content)
+        geojson_result = json.loads(tile_content.decode('utf8'))
         west_hemisphere_geometry = asShape(geojson_result['features'][0]['geometry'])
         expected_geometry = LineString([(-180, 32), (180, 32)])
         self.assertTrue(expected_geometry.almost_equals(west_hemisphere_geometry))
@@ -305,7 +307,7 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
         
         tile_mimetype, tile_content = utils.request(self.config_file_content, "vectile_test", "json", 0, 0, 0)
         self.assertTrue(tile_mimetype.endswith('/json'))
-        geojson_result = json.loads(tile_content)
+        geojson_result = json.loads(tile_content.decode('utf8'))
         
         result_geom = asShape(geojson_result['features'][0]['geometry'])
         expected_geom = Polygon( [(-180, -85.05), (180, -85.05), (180, 85.05), (-180, 85.05), (-180, -85.05)])
@@ -346,7 +348,7 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
 
         tile_mimetype, tile_content = utils.request(self.config_file_content, "vectile_multi", "json", 0, 0, 0)
         self.assertTrue(tile_mimetype.endswith('/json'))
-        geojson_result = json.loads(tile_content)
+        geojson_result = json.loads(tile_content.decode('utf8'))
         
         feature1, feature2 = geojson_result['vectile_test'], geojson_result['vectile_copy']
         self.assertEqual(feature1['type'], 'FeatureCollection')
@@ -382,10 +384,10 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
         # northwest quadrant should return San Francisco and Lima
 
         tile_mimetype, tile_content = utils.request(self.config_file_content, "vectile_test", "topojson", 0, 0, 1)
-        topojson_result = json.loads(tile_content)
+        topojson_result = json.loads(tile_content.decode('utf8'))
 
         self.assertTrue(tile_mimetype.endswith('/json'))
-        print topojson_result
+        print(topojson_result)
         self.assertEqual(topojson_result['type'], 'Topology')
         self.assertEqual(len(topojson_result['objects']['vectile']['geometries']), 2)
 
@@ -404,7 +406,7 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
 
             elif feature['properties']['name'] == 'Lima':
                 cities.append(feature['properties']['name'])
-                print feature['coordinates']
+                print(feature['coordinates'])
                 self.assertTrue(hypot(point_lima.x - lon, point_lima.y - lat) < 1)
 
         self.assertTrue('San Francisco' in cities)
@@ -414,7 +416,7 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
         # northeast quadrant should return Berlin
 
         tile_mimetype, tile_content = utils.request(self.config_file_content, "vectile_test", "topojson", 0, 1, 1)
-        topojson_result = json.loads(tile_content)
+        topojson_result = json.loads(tile_content.decode('utf8'))
 
         self.assertTrue(tile_mimetype.endswith('/json'))
         self.assertEqual(topojson_result['type'], 'Topology')
@@ -435,7 +437,7 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
 
         tile_mimetype, tile_content = utils.request(self.config_file_content, "vectile_test", "topojson", 0, 0, 0)
         self.assertTrue(tile_mimetype.endswith('/json'))
-        topojson_result = json.loads(tile_content)
+        topojson_result = json.loads(tile_content.decode('utf8'))
         topojson_xform = get_topo_transform(topojson_result)
         
         parts = [topojson_result['arcs'][arc] for arc in topojson_result['objects']['vectile']['geometries'][0]['arcs']]
@@ -467,7 +469,7 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
         
         tile_mimetype, tile_content = utils.request(self.config_file_content, "vectile_test", "topojson", 0, 0, 0)
         self.assertTrue(tile_mimetype.endswith('/json'))
-        topojson_result = json.loads(tile_content)
+        topojson_result = json.loads(tile_content.decode('utf8'))
         topojson_xform = get_topo_transform(topojson_result)
         
         parts = [topojson_result['arcs'][arc[0]] for arc in topojson_result['objects']['vectile']['geometries'][0]['arcs']]
@@ -513,7 +515,7 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
 
         tile_mimetype, tile_content = utils.request(self.config_file_content, "vectile_multi", "topojson", 0, 0, 0)
         self.assertTrue(tile_mimetype.endswith('/json'))
-        topojson_result = json.loads(tile_content)
+        topojson_result = json.loads(tile_content.decode('utf8'))
         
         self.assertEqual(topojson_result['type'], 'Topology')
         self.assertEqual(topojson_result['objects']['vectile_test']['type'], 'GeometryCollection')
@@ -593,7 +595,7 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
         '''Create a line that goes from west to east (clip on) (pbf)'''
         self.defineGeometry('LINESTRING')
 
-        geom = LineString([(-180, 32), (180, 32)])
+        geom = LineString([(-179, 32), (179, 32)])
 
         self.insertTestRow(geom.wkt)
 
@@ -607,11 +609,10 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
         extent = tile_bounds_mercator(0, 0, 0)
 
         west_hemisphere_geometry = decoded_pbf_asshape(layer_result['features'][0], extent)
-        # order of points returned are different
-        expected_geometry = LineString([(180, 32), (-180, 32)])
+        expected_geometry = LineString([(-179, 32), (179, 32)])
         for returned, expected in zip(west_hemisphere_geometry.coords, expected_geometry.coords):
-            self.assertTrue(round(returned[0]) == expected[0])
-            self.assertTrue(round(returned[1]) == expected[1])
+            self.assertEqual(round(returned[0]), expected[0])
+            self.assertEqual(round(returned[1]), expected[1])
 
     def test_polygon_pbf(self):
         '''
diff --git a/tests/vector_tests.py b/tests/vector_tests.py
index 38082a2..980d18c 100644
--- a/tests/vector_tests.py
+++ b/tests/vector_tests.py
@@ -103,7 +103,7 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
         # western hemisphere should return San Francisco and Lima
 
         tile_mimetype, tile_content = utils.request(self.config_file_content, "vector_test", "geojson", 0, 0, 0)
-        geojson_result = json.loads(tile_content)
+        geojson_result = json.loads(tile_content.decode('utf8'))
 
         self.assertTrue(tile_mimetype.endswith('/json'))
         self.assertEqual(geojson_result['type'], 'FeatureCollection')
@@ -129,7 +129,7 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
         # eastern hemisphere should return Berlin
 
         tile_mimetype, tile_content = utils.request(self.config_file_content, "vector_test", "geojson", 0, 1, 0)
-        geojson_result = json.loads(tile_content)
+        geojson_result = json.loads(tile_content.decode('utf8'))
 
         self.assertTrue(tile_mimetype.endswith('/json'))
         self.assertEqual(geojson_result['type'], 'FeatureCollection')
@@ -151,7 +151,7 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
         # for western hemisphere....
         tile_mimetype, tile_content = utils.request(self.config_file_content, "vector_test", "geojson", 0, 0, 0)
         self.assertTrue(tile_mimetype.endswith('/json'))
-        geojson_result = json.loads(tile_content)
+        geojson_result = json.loads(tile_content.decode('utf8'))
         west_hemisphere_geometry = asShape(geojson_result['features'][0]['geometry'])
         expected_geometry = LineString([(-180, 32), (0, 32)])
         self.assertTrue(expected_geometry.almost_equals(west_hemisphere_geometry))
@@ -159,7 +159,7 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
         # for eastern hemisphere....
         tile_mimetype, tile_content = utils.request(self.config_file_content, "vector_test", "geojson", 0, 1, 0)
         self.assertTrue(tile_mimetype.endswith('/json'))
-        geojson_result = json.loads(tile_content)
+        geojson_result = json.loads(tile_content.decode('utf8'))
         east_hemisphere_geometry = asShape(geojson_result['features'][0]['geometry'])
         expected_geometry = LineString([(0, 32), (180, 32)])
         self.assertTrue(expected_geometry.almost_equals(east_hemisphere_geometry))
@@ -182,7 +182,7 @@ class VectorProviderTest(PostGISVectorTestBase, TestCase):
         
         tile_mimetype, tile_content = utils.request(self.config_file_content, "vector_test", "geojson", 0, 0, 0)
         self.assertTrue(tile_mimetype.endswith('/json'))
-        geojson_result = json.loads(tile_content)
+        geojson_result = json.loads(tile_content.decode('utf8'))
         
         result_geom = asShape(geojson_result['features'][0]['geometry'])
         expected_geom = Polygon( [(-180, -90), (0, -90), (0, 90), (-180, 90), (-180, -90)])

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-grass/tilestache.git



More information about the Pkg-grass-devel mailing list