'Make a new Datasource.
Parameters:
template:
Required URL template with placeholders for tile zoom, x and y,
e.g. "http://example.com/layer/{z}/{x}/{y}.json".
sort_key:
Optional field name to use when sorting features for rendering.
E.g. "name" or "name ascending" to sort ascending by name,
"name descending" to sort descending by name.
clipped:
Optional boolean flag to determine correct behavior for
duplicate geometries. When tile data is not clipped, features()
will check geometry uniqueness and throw out duplicates.
Setting clipped to false for actually-clipped geometries has no
effect but wastes time. Setting clipped to false for unclipped
geometries will result in possibly wrong-looking output.
Default is "true".
zoom_data:
Optional keyword specifying single or double zoom data tiles.
Works especially well with relatively sparse label layers.
When set to "double", tiles will be requested at one zoom level
out from the map view, e.g. double-sized z13 tiles will be used
to render a normal z14 map.
Default is "single".'

def __init__(self, template, sort_key=None, clipped='true', zoom_data='single'):

    (scheme, host, path, p, query, f) = urlparse(template)
    self.host = host
    self.port = (443 if (scheme == 'https') else 80)
    if (':' in host):
        self.host = host.split(':', 1)[0]
        self.port = int(host.split(':', 1)[1])
    self.path = ((path + ('?' if query else '')) + query)
    self.path = self.path.replace('%', '%%')
    self.path = self.path.replace('{Z}', '{z}').replace('{z}', '%(z)d')
    self.path = self.path.replace('{X}', '{x}').replace('{x}', '%(x)d')
    self.path = self.path.replace('{Y}', '{y}').replace('{y}', '%(y)d')
    if (sort_key is None):
        (self.sort, self.reverse) = (None, None)
    elif sort_key.lower().endswith('    descending'):
        logging.debug(('Will    sort    by    %s    descending' % sort_key))
        (self.sort, self.reverse) = (sort_key.split()[0], True)
    else:
        logging.debug(('Will    sort    by    %s    ascending' % sort_key))
        (self.sort, self.reverse) = (sort_key.split()[0], False)
    self.clipped = (clipped.lower() not in ('false', 'no', '0'))
    self.zoom_adjust = {'double': 1}.get(zoom_data.lower(), 0)
    bbox = Box2d(((- diameter) / 2), ((- diameter) / 2), (diameter / 2), (diameter / 2))
    PythonDatasource.__init__(self, envelope=bbox)
