diff --git a/requirements.txt b/requirements.txt
index 8b221e2af064329ddb037980d47e60d8e0814e99..f9bfb48a31e1d9eeea6f2b0971860b41153caa18 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,3 +1,3 @@
 web.py>=0.50,<0.60
+gdal<2.5.0
 pyyaml
-gdal<2.5.0
\ No newline at end of file
diff --git a/setup.py b/setup.py
index f47bc7b279197d8a577dd25098efd82326e5bbd8..45895dc737ae3bc0c80bef40277c00d0b2b87438 100644
--- a/setup.py
+++ b/setup.py
@@ -36,11 +36,10 @@ def parse_requirements(filename):
 
 
 dirname = os.path.dirname(__file__)
-reqs_filename = os.path.join(dirname, 'requirements.txt')
 
+reqs_filename = os.path.join(dirname, 'requirements.txt')
 reqs = [str(req) for req in parse_requirements(reqs_filename)]
 
-
 setup(
     name="MapServer Rest API",
     version=version,
diff --git a/src/metadata.py b/src/metadata.py
index ffc02600614602aae369a963c8f0ac02c230f493..5911742b1b37e099f173a3eb134cbcd2e95e472a 100644
--- a/src/metadata.py
+++ b/src/metadata.py
@@ -83,6 +83,7 @@ def get_metadata_keys(obj):
 
 
 def set_metadata(obj, key, value):
+    # TODO: Fix this with upgrade to py3
     try:
         obj.setMetaData(key, value)
     except UnicodeEncodeError:
diff --git a/src/mra.py b/src/mra.py
index e0d70761f3cfe9b8fec4277e6974a1610421ecf6..b60a2adf58e8345aa122cce1da9b451b0ec0a363 100644
--- a/src/mra.py
+++ b/src/mra.py
@@ -642,13 +642,21 @@ class CoverageModel(LayerModel):
         info = ws.get_coveragestore_info(cs_name)
         cparam = info["connectionParameters"]
 
-        # if cparam["dbtype"] in ["tif", "tiff"]:
         self.ms.connectiontype = mapscript.MS_RASTER
         url = urllib.parse.urlparse(cparam["url"])
-        self.ms.data = self.ws.mra.get_file_path(url.path)
-        # TODO: strip extention.
-        # else:
-        #    raise ValueError("Unhandled type \"%s\"." % cparam["dbtype"])
+        filename = self.ws.mra.get_file_path(url.path)
+        if cs.tindex is None:
+            #if cparam["dbtype"] in ["tif", "tiff"]:
+            self.ms.data = filename
+            self.ms.tileindex = None
+            self.ms.tileitem = None
+            # TODO: strip extention.
+            #else:
+            #    raise ValueError("Unhandled type \"%s\"." % cparam["dbtype"])
+        else:
+            self.ms.data = None
+            self.ms.tileindex = cs.get_tileindex()
+            self.ms.tileitem = cs.get_tileitem()
 
         # Update mra metadatas, and make sure the mandatory ones are left untouched.
         self.update_mra_metadatas(metadata)
@@ -673,6 +681,8 @@ class CoverageModel(LayerModel):
         layer.ms.data = self.ms.data
         layer.ms.connectiontype = self.ms.connectiontype
         layer.ms.connection = self.ms.connection
+        layer.ms.tileindex = self.ms.tileindex
+        layer.ms.tileitem = self.ms.tileitem
 
         layer_name = self.get_mra_metadata("name")
 
@@ -889,18 +899,18 @@ class Workspace(Mapfile):
     def create_layermodel(self, st_type, store, name, metadata={}):
         if self.has_layermodel(st_type, store, name):
             raise KeyExists((st_type, store, name))
-        ft = self.__ms2model(mapscript.layerObj(self.ms), st_type=st_type)
+        lm = self.__ms2model(mapscript.layerObj(self.ms), st_type=st_type)
 
-        ft.update(store, name, metadata)
-        return ft
+        lm.update(store, name, metadata)
+        return lm
 
     def update_layermodel(self, st_type, store, name, metadata={}):
-        ft = self.get_layermodel(st_type, store, name)
-        ft.update(store, name, metadata)
+        lm = self.get_layermodel(st_type, store, name)
+        lm.update(store, name, metadata)
 
     def delete_layermodel(self, st_type, ds_name, ft_name):
-        model = self.get_layermodel(st_type, ds_name, ft_name)
-        if model.get_mra_metadata("layers", []):
+        lm = self.get_layermodel(st_type, ds_name, ft_name)
+        if lm.get_mra_metadata("layers", []):
             raise ValueError("The %s \"%s\" can't be delete because it is used." % (st_type, ft_name))
         self.ms.removeLayer(model.ms.index)
 
diff --git a/src/stores.py b/src/stores.py
index 1a46b96e907c51cec705beed458bcab5d93d262b..87a16aeff4544bf0e7ffa970f1268556abeda558 100644
--- a/src/stores.py
+++ b/src/stores.py
@@ -287,7 +287,7 @@ class Featuretype(object):
     def iterfeatures(self, what=[], when={}):
         if what != [] or when != {}:
             raise NotImplementedError("Iterfeature doesn't support filters yet.")
-        for i in range(self.backend.GetFeatureCount()):
+        for i in range(self.nbfeatures()):
             yield Feature(self.backend.GetFeature(i), self)
 
     def get_aditional_info(self):
@@ -369,6 +369,8 @@ class Band(object):
 class Coveragestore(object):
     """A coveragestore implementation backed by gdal."""
 
+    tindex = None
+
     def __init__(self, path):
         """Path will be used to open the store, it can be a simple filesystem path
         or something more complex used by gdal/ogr to access databases for example.
@@ -377,6 +379,14 @@ class Coveragestore(object):
 
         """
         self.backend = path if isinstance(path, gdal.Dataset) else gdal.Open(path)
+        if self.backend is None:
+            ds = Datastore(path if isinstance(path, ogr.DataSource) else ogr.Open(path))
+            if ds:
+                self.tindex = Featuretype(ds.backend.GetLayerByIndex(0), ds)
+                path = getattr(
+                    self.tindex.backend.GetFeature(0), self.get_tileitem())
+                self.backend = gdal.Open(str(path))
+
         if self.backend is None:
             raise ValueError("Coveragestore backend could not be opened. \"%s\"." % path)
 
@@ -387,7 +397,7 @@ class Coveragestore(object):
         return self.iterbands()
 
     def __contains__(self, idx):
-        return 0 < idx and idx < self.backend.RasterCount
+        return idx > 0 and idx < self.backend.RasterCount
 
     def __getitem__(self, idx):
         band = self.backend.GetRasterBand(idx)
@@ -408,12 +418,15 @@ class Coveragestore(object):
         return corners
 
     def get_extent(self):
+        if self.tindex is not None:
+            return self.tindex.get_extent()
+        #else:
         corners = self.get_corners()
-        minX = min(x for x, y in corners)
-        minY = min(y for x, y in corners)
-        maxX = max(x for x, y in corners)
-        maxY = max(y for x, y in corners)
-        return Extent(minX, minY, maxX, maxY)
+        minx = min(x for x, y in corners)
+        miny = min(y for x, y in corners)
+        maxx = max(x for x, y in corners)
+        maxy = max(y for x, y in corners)
+        return Extent(minx, miny, maxx, maxy)
 
     def get_latlon_extent(self):
         rect = mapscript.rectObj(*self.get_extent())
@@ -436,3 +449,16 @@ class Coveragestore(object):
     def iterbands(self):
         for i in range(1, self.backend.RasterCount + 1):
             yield Band(self.backend.GetRasterBand(i))
+
+    def get_tileindex(self):
+        """Return the path to the index file."""
+        if self.tindex is None:
+            return None
+        return self.tindex.ds.backend.GetName()
+
+    def get_tileitem(self):
+        """Return the field in the shapefile which contains
+           the filenames referenced by the index."""
+        if self.tindex is None:
+            return None
+        return self.tindex.backend.GetLayerDefn().GetFieldDefn(0).name