Exemple #1
0
class TilesManager(object):
   
    def __init__(self, **kwargs):
        """
        Manipulates tiles in general. Gives ability to list required tiles on a 
        bounding box, download them, render them, extract them from other mbtiles...
        
        Keyword arguments:
        remote -- use remote tiles (default True)
        stylefile -- mapnik stylesheet file, only necessary if `remote` is `False`
        cache -- use a local cache to share tiles between runs (default True)

        tmp_dir -- temporary folder for gathering tiles (default DEFAULT_TMP_DIR)
        tiles_url -- remote URL to download tiles (default DEFAULT_TILES_URL)
        tile_size -- default tile size (default DEFAULT_TILE_SIZE)
        tiles_dir -- Local folder containing existing tiles, and 
                     where cached tiles will be stored (default DEFAULT_TILES_DIR)
        
        mbtiles_file -- A MBTiles providing tiles (overrides ``tiles_url``)
        
        wms_server -- A WMS server url
        wms_layers -- The list of layers to be requested
        wms_options -- WMS parameters to be requested (see ``landez.reader.WMSReader``)
        """
        self.remote = kwargs.get('remote', True)
        self.stylefile = kwargs.get('stylefile')

        self.tmp_dir = kwargs.get('tmp_dir', DEFAULT_TMP_DIR)
        
        self.cache = kwargs.get('cache', True)
        self.tiles_dir = kwargs.get('tiles_dir', DEFAULT_TILES_DIR)
        self.tiles_url = kwargs.get('tiles_url', DEFAULT_TILES_URL)
        self.tiles_subdomains = kwargs.get('tiles_subdomains', DEFAULT_TILES_SUBDOMAINS)
        self.tile_size = kwargs.get('tile_size', DEFAULT_TILE_SIZE)
        
        self.mbtiles_file = kwargs.get('mbtiles_file')
        self.wms_server = kwargs.get('wms_server')
        self.wms_layers = kwargs.get('wms_layers', [])
        self.wms_options = kwargs.get('wms_options', {})
        
        self.reader = None
        basename = ''
        if self.mbtiles_file:
            self.reader = MBTilesReader(self.mbtiles_file, self.tile_size)
            basename = os.path.basename(self.mbtiles_file)
            self.remote = False
        elif self.wms_server:
            assert self.wms_layers, _("Request at least one layer")
            self.reader = WMSReader(self.wms_server, self.wms_layers, 
                                    self.tile_size, **self.wms_options)
            basename = '-'.join(self.wms_layers)
            self.remote = False
        elif self.stylefile:
            assert has_mapnik, _("Cannot render tiles without mapnik !")
            assert self.stylefile, _("A mapnik stylesheet is required")
            self.remote = False
            basename = os.path.basename(self.stylefile)
        else:
            url = urlparse(self.tiles_url)
            basename = url.netloc
        basename = re.sub(r'[^a-z^A-Z^0-9]+', '', basename)
        self.tmp_dir = os.path.join(self.tmp_dir, basename)
        self.tiles_dir = os.path.join(self.tiles_dir, basename)
        
        self.proj = GoogleProjection(self.tile_size)
        self._mapnik = None
        self._prj = None
        self._reader = None
        self._layers = []
        
        # Number of tiles rendered/downloaded here
        self.rendered = 0

    def tileslist(self, bbox, zoomlevels):
        """
        Build the tiles list within the bottom-left/top-right bounding 
        box (minx, miny, maxx, maxy) at the specified zoom levels.
        Return a list of tuples (z,x,y)
        """
        if len(bbox) != 4 or len(zoomlevels) == 0:
            raise InvalidCoverageError(_("Wrong format of bounding box or zoom levels."))

        xmin, ymin, xmax, ymax = bbox
        if abs(xmin) > 180 or abs(xmax) > 180 or \
           abs(ymin) > 90 or abs(ymax) > 90:
            raise InvalidCoverageError(_("Some coordinates exceed [-180,+180], [-90, 90]."))
        
        if xmin >= xmax or ymin >= ymax:
            raise InvalidCoverageError(_("Bounding box format is (xmin, ymin, xmax, ymax)"))
        
        if max(zoomlevels) >= self.proj.maxlevel:
            self.proj = GoogleProjection(self.tile_size, zoomlevels)
        
        ll0 = (xmin, ymax)  # left top
        ll1 = (xmax, ymin)  # right bottom

        l = []
        for z in zoomlevels:
            px0 = self.proj.fromLLtoPixel(ll0,z)
            px1 = self.proj.fromLLtoPixel(ll1,z)
            
            for x in range(int(px0[0]/self.tile_size),
                           int(px1[0]/self.tile_size)+1):
                if (x < 0) or (x >= 2**z):
                    continue
                for y in range(int(px0[1]/self.tile_size),
                               int(px1[1]/self.tile_size)+1):
                    if (y < 0) or (y >= 2**z):
                        continue
                    l.append((z, x, y))
        return l

    def tile_file(self, (z, x, y)):
        """
        Return folder (``z/x``) and name (``y.png``) for the specified tuple.
        """
        tile_dir = os.path.join("%s" % z, "%s" % x)
        y_mercator = (2**z - 1) - y
        tile_name = "%s.png" % y_mercator
        return tile_dir, tile_name
Exemple #2
0
class TilesManager(object):
   
    def __init__(self, **kwargs):
        """
        Manipulates tiles in general. Gives ability to list required tiles on a 
        bounding box, download them, render them, extract them from other mbtiles...
        
        Keyword arguments:
        remote -- use remote tiles (default True)
        stylefile -- mapnik stylesheet file, only necessary if `remote` is `False`
        cache -- use a local cache to share tiles between runs (default True)

        tmp_dir -- temporary folder for gathering tiles (default DEFAULT_TMP_DIR)
        tiles_url -- remote URL to download tiles (default DEFAULT_TILES_URL)
        tile_size -- default tile size (default DEFAULT_TILE_SIZE)
        tiles_dir -- Local folder containing existing tiles, and 
                     where cached tiles will be stored (default DEFAULT_TILES_DIR)
        mbtiles_file -- A MBTiles providing tiles (overrides ``tiles_url``)
        """
        self.remote = kwargs.get('remote', True)
        self.stylefile = kwargs.get('stylefile')

        self.tmp_dir = kwargs.get('tmp_dir', DEFAULT_TMP_DIR)
        
        self.cache = kwargs.get('cache', True)
        self.tiles_dir = kwargs.get('tiles_dir', DEFAULT_TILES_DIR)
        self.tiles_url = kwargs.get('tiles_url', DEFAULT_TILES_URL)
        self.tile_size = kwargs.get('tile_size', DEFAULT_TILE_SIZE)
        
        self.mbtiles_file = kwargs.get('mbtiles_file')
        if self.mbtiles_file:
            self.remote = False
        
        if not self.remote and not self.mbtiles_file:
            assert has_mapnik, "Cannot render tiles without mapnik !"
            assert self.stylefile, "A mapnik stylesheet is required"
        
        self.proj = GoogleProjection(self.tile_size)
        self._mapnik = None
        self._prj = None

        # Number of tiles rendered/downloaded here
        self.rendered = 0

    def tileslist(self, bbox, zoomlevels):
        """
        Build the tiles list within the bottom-left/top-right bounding 
        box (minx, miny, maxx, maxy) at the specified zoom levels.
        Return a list of tuples (z,x,y)
        """
        if len(bbox) != 4 or len(zoomlevels) == 0:
            raise InvalidCoverageError()

        xmin, ymin, xmax, ymax = bbox
        if abs(xmin) > 180 or abs(xmax) > 180 or \
           abs(ymin) > 90 or abs(ymax) > 90:
            raise InvalidCoverageError()
        
        if xmin >= xmax or ymin >= ymax:
            raise InvalidCoverageError()
        
        if max(zoomlevels) >= self.proj.maxlevel:
            self.proj = GoogleProjection(self.tile_size, zoomlevels)
        
        ll0 = (xmin, ymax)  # left top
        ll1 = (xmax, ymin)  # right bottom

        l = []
        for z in zoomlevels:
            px0 = self.proj.fromLLtoPixel(ll0,z)
            px1 = self.proj.fromLLtoPixel(ll1,z)
            
            for x in range(int(px0[0]/self.tile_size),
                           int(px1[0]/self.tile_size)+1):
                if (x < 0) or (x >= 2**z):
                    continue
                for y in range(int(px0[1]/self.tile_size),
                               int(px1[1]/self.tile_size)+1):
                    if (y < 0) or (y >= 2**z):
                        continue
                    l.append((z, x, y))
        return l

    def tile_file(self, (z, x, y)):
        """
        Return folder (``z/x``) and name (``y.png``) for the specified tuple.
        """
        tile_dir = os.path.join("%s" % z, "%s" % x)
        y_mercator = (2**z - 1) - y
        tile_name = "%s.png" % y_mercator
        return tile_dir, tile_name