From cb973a10b03c7bcf2285f1013831fa2c6a4b925a Mon Sep 17 00:00:00 2001
From: m431m <4568458+m431m@users.noreply.github.com>
Date: Thu, 9 Jul 2020 16:00:14 +0200
Subject: [PATCH] Added tileindex support for coverage store (#8)

---
 requirements.txt |  2 +-
 setup.py         |  3 +--
 src/metadata.py  |  1 +
 src/mra.py       | 34 ++++++++++++++++++++++------------
 src/stores.py    | 40 +++++++++++++++++++++++++++++++++-------
 5 files changed, 58 insertions(+), 22 deletions(-)

diff --git a/requirements.txt b/requirements.txt
index 8b221e2..f9bfb48 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 f47bc7b..45895dc 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 ffc0260..5911742 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 e0d7076..b60a2ad 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 1a46b96..87a16ae 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
-- 
GitLab