From d3bf84753d0e12ef2446795976cf9244d268396d Mon Sep 17 00:00:00 2001 From: Philip Jales Date: Fri, 25 Aug 2023 10:06:21 +0100 Subject: [PATCH 1/5] Allow 3km grid with tile name in metres (not km) --- src/equi7grid/equi7grid.py | 146 +++++++++++++++++++++++-------------- 1 file changed, 92 insertions(+), 54 deletions(-) diff --git a/src/equi7grid/equi7grid.py b/src/equi7grid/equi7grid.py index b05fce7..9812d2d 100644 --- a/src/equi7grid/equi7grid.py +++ b/src/equi7grid/equi7grid.py @@ -99,11 +99,11 @@ class Equi7Grid(TiledProjectionSystem): _static_tilecodes = ["T6", "T3", "T1"] # supported grid spacing ( = the pixel sampling) _static_sampling = [ - 1000, 800, 750, 600, 500, 400, 300, 250, 200, 150, 125, 100, 96, 80, + 6000, 3000, 1000, 800, 750, 600, 500, 400, 300, 250, 200, 150, 125, 100, 96, 80, 75, 64, 60, 50, 48, 40, 32, 30, 25, 24, 20, 16, 10, 8, 5, 4, 2, 1 ] - def __init__(self, sampling): + def __init__(self, sampling, tile_names_in_m=False): """ Initialises an Equi7Grid class for a specified sampling. @@ -111,8 +111,13 @@ def __init__(self, sampling): ---------- sampling : int the grid sampling = size of pixels; in metres. + tile_names_in_m : bool, optional + controls whether the tile names are in metres or in km when > 1000m """ + # Store parameters + self.tile_names_in_m = tile_names_in_m + # check if the equi7grid.data have been loaded successfully if Equi7Grid._static_data is None: raise ValueError("cannot load Equi7Grid ancillary data!") @@ -124,8 +129,9 @@ def __init__(self, sampling): super(Equi7Grid, self).__init__(sampling, tag='Equi7') self.core.projection = 'multiple' - @staticmethod - def encode_sampling(sampling): + + + def encode_sampling(self, sampling): """ provides a string representing the sampling (e.g. for the tilenames) @@ -139,14 +145,16 @@ def encode_sampling(sampling): sampling_str : str string representing the sampling """ - if sampling <= 999: + if self.tile_names_in_m: sampling_str = str(sampling).rjust(3, '0') - if sampling >= 1000: - sampling_str = "".join((str(sampling / 1000.0)[0], 'K', str(sampling / 1000.0)[2])) + else: + if sampling <= 999: + sampling_str = str(sampling).rjust(3, '0') + if sampling >= 1000: + sampling_str = "".join((str(sampling / 1000.0)[0], 'K', str(sampling / 1000.0)[2])) return sampling_str - @staticmethod - def decode_sampling(sampling_str): + def decode_sampling(self, sampling_str): """ converts the string representing the sampling (e.g. from the tilenames) to an integer value in metres @@ -161,12 +169,15 @@ def decode_sampling(sampling_str): sampling : int the grid sampling = size of pixels; in metres. """ - if len(sampling_str) != 3: - raise ValueError('Resolution is badly defined!') - if sampling_str[1] == 'K': - sampling = int(sampling_str[0]) * 1000 + int(sampling_str[2]) * 100 - else: + if self.tile_names_in_m: sampling = int(sampling_str) + else: + if len(sampling_str) != 3: + raise ValueError('Resolution is badly defined!') + if sampling_str[1] == 'K': + sampling = int(sampling_str[0]) * 1000 + int(sampling_str[2]) * 100 + else: + sampling = int(sampling_str) return sampling def define_subgrids(self): @@ -204,9 +215,9 @@ def get_tiletype(self, sampling=None): sampling = int(sampling) - # allowing sampling of [1000, 800, 750, 600, 500, 400, 300, 250, 200, + # allowing sampling of [6000, 3000, 1000, 800, 750, 600, 500, 400, 300, 250, 200, # 150, 125, 100, 96, 80, 75, 64] metres - if ((sampling in range(64, 1001)) and (600000 % sampling == 0)): + if ((sampling in range(64, 6001)) and (600000 % sampling == 0)): tilecode = "T6" # allowing sampling of [60, 50, 48, 40, 32, 30, 25, 24, 20] metres elif ((sampling in range(20, 61)) and (300000 % sampling == 0)): @@ -670,48 +681,75 @@ def decode_tilename(self, tilename): """ tf = self.core.tile_ysize_m // 100000 - # allow short-form of tilename (e.g. "E012N018T6") - if len(tilename) == 10: - tile_size_m = int(tilename[-1]) * 100000 - if tile_size_m != self.core.tile_xsize_m: - raise ValueError(self.msg1) - llx = int(tilename[1:4]) - if llx % tf: - raise ValueError(self.msg2) - lly = int(tilename[5:8]) - if lly % tf: - raise ValueError(self.msg2) - tilecode = tilename[-2:] - if tilecode != self.core.tiletype: + if self.tile_names_in_m: + # allow long-form of tilename in metres (e.g. "EU3000M_E012N018T6") + if len(tilename) == 17: + subgrid_id = tilename[0:2] + if subgrid_id != self.core.tag: + raise ValueError(self.msg1) + tilename_sampling = tilename[2:].split('M') + sampling = Equi7Grid.decode_sampling(tilename_sampling[0]) + tilename_remaining = tilename.split('_')[1] + if sampling != self.core.sampling: + raise ValueError(self.msg1) + tile_size_m = int(tilename_remaining[-1]) * 100000 + if tile_size_m != self.core.tile_xsize_m: + raise ValueError(self.msg1) + llx = int(tilename_remaining[1:4]) + if llx % tf: + raise ValueError(self.msg2) + lly = int(tilename_remaining[5:8]) + if lly % tf: + raise ValueError(self.msg2) + tilecode = tilename_remaining[-2:] + if tilecode != self.core.tiletype: + raise ValueError(self.msg1) + else: raise ValueError(self.msg1) - subgrid_id = self.core.tag - sampling = self.core.sampling + else: - # allow long-form of tilename (e.g. "EU500M_E012N018T6") - elif len(tilename) == 17: - subgrid_id = tilename[0:2] - if subgrid_id != self.core.tag: - raise ValueError(self.msg1) - sampling = Equi7Grid.decode_sampling(tilename[2:5]) - if sampling != self.core.sampling: - raise ValueError(self.msg1) - tile_size_m = int(tilename[-1]) * 100000 - if tile_size_m != self.core.tile_xsize_m: - raise ValueError(self.msg1) - llx = int(tilename[8:11]) - if llx % tf: - raise ValueError(self.msg2) - lly = int(tilename[12:15]) - if lly % tf: - raise ValueError(self.msg2) - tilecode = tilename[-2:] - if tilecode != self.core.tiletype: + # allow short-form of tilename (e.g. "E012N018T6") + if len(tilename) == 10: + tile_size_m = int(tilename[-1]) * 100000 + if tile_size_m != self.core.tile_xsize_m: + raise ValueError(self.msg1) + llx = int(tilename[1:4]) + if llx % tf: + raise ValueError(self.msg2) + lly = int(tilename[5:8]) + if lly % tf: + raise ValueError(self.msg2) + tilecode = tilename[-2:] + if tilecode != self.core.tiletype: + raise ValueError(self.msg1) + subgrid_id = self.core.tag + sampling = self.core.sampling + + # allow long-form of tilename (e.g. "EU500M_E012N018T6") + elif len(tilename) == 17: + subgrid_id = tilename[0:2] + if subgrid_id != self.core.tag: + raise ValueError(self.msg1) + sampling = Equi7Grid.decode_sampling(tilename[2:5]) + if sampling != self.core.sampling: + raise ValueError(self.msg1) + tile_size_m = int(tilename[-1]) * 100000 + if tile_size_m != self.core.tile_xsize_m: + raise ValueError(self.msg1) + llx = int(tilename[8:11]) + if llx % tf: + raise ValueError(self.msg2) + lly = int(tilename[12:15]) + if lly % tf: + raise ValueError(self.msg2) + tilecode = tilename[-2:] + if tilecode != self.core.tiletype: + raise ValueError(self.msg1) + + # wrong length + else: raise ValueError(self.msg1) - # wrong length - else: - raise ValueError(self.msg1) - return subgrid_id, sampling, tile_size_m, llx * 100000, lly * 100000, tilecode def get_congruent_tiles_from_tilename(self, From f200491fc82dc2d60f3c1664e7289aa92093a659 Mon Sep 17 00:00:00 2001 From: Philip Jales Date: Fri, 25 Aug 2023 10:32:17 +0100 Subject: [PATCH 2/5] Fixes to interface --- src/equi7grid/equi7grid.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/equi7grid/equi7grid.py b/src/equi7grid/equi7grid.py index 9812d2d..e10c663 100644 --- a/src/equi7grid/equi7grid.py +++ b/src/equi7grid/equi7grid.py @@ -130,7 +130,6 @@ def __init__(self, sampling, tile_names_in_m=False): self.core.projection = 'multiple' - def encode_sampling(self, sampling): """ provides a string representing the sampling (e.g. for the tilenames) @@ -358,7 +357,7 @@ class Equi7Subgrid(TiledProjection): """ - def __init__(self, core, continent): + def __init__(self, core, continent, tile_names_in_m=False): """ Initialises an Equi7Subgrid class for a specified continent. @@ -368,6 +367,8 @@ def __init__(self, core, continent): defines core parameters of the (sub-) grid continent : str acronym of the continent, e.g. 'EU' or 'SA'. + tile_names_in_m : bool, optional + controls whether the tile names are in metres or in km when > 1000m """ # load WKT string and extent shape @@ -381,7 +382,7 @@ def __init__(self, core, continent): self.core = _core # holds name of the subgrid - self.name = ''.join(('EQUI7_', continent, Equi7Grid.encode_sampling(core.sampling), 'M')) + self.name = ''.join(('EQUI7_', continent, Equi7Grid(core.sampling, tile_names_in_m).encode_sampling(core.sampling), 'M')) # holds the extent of the subgrid in the lonlat-space self.polygon_geog = create_geometry_from_wkt(data['zone_extent'], epsg=4326) @@ -557,7 +558,7 @@ def encode_tilename(self, llx, lly, sampling, tilecode, shortform=False): # gives long-form of tilename (e.g. "EU500M_E012N018T6") tilename = "{}{}M_E{:03d}N{:03d}{}".format( self.core.tag, - Equi7Grid.encode_sampling(sampling), + self.encode_sampling(sampling), int(llx) // 100000, int(lly) // 100000, tilecode) From 6b58a2262f83816f59d975a0ec14f836b01f0e2c Mon Sep 17 00:00:00 2001 From: Philip Jales Date: Fri, 25 Aug 2023 11:45:21 +0100 Subject: [PATCH 3/5] Fix configuration passing to Equi7Subgrid --- src/equi7grid/equi7grid.py | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/equi7grid/equi7grid.py b/src/equi7grid/equi7grid.py index e10c663..8083833 100644 --- a/src/equi7grid/equi7grid.py +++ b/src/equi7grid/equi7grid.py @@ -44,6 +44,8 @@ from pytileproj.geometry import create_geometry_from_wkt from geographiclib.geodesic import Geodesic +TILE_NAMES_IN_M = True + def _load_static_data(module_path): """ @@ -115,8 +117,8 @@ def __init__(self, sampling, tile_names_in_m=False): controls whether the tile names are in metres or in km when > 1000m """ - # Store parameters - self.tile_names_in_m = tile_names_in_m + + self._tile_names_in_m = tile_names_in_m # check if the equi7grid.data have been loaded successfully if Equi7Grid._static_data is None: @@ -128,9 +130,10 @@ def __init__(self, sampling, tile_names_in_m=False): # initializing super(Equi7Grid, self).__init__(sampling, tag='Equi7') self.core.projection = 'multiple' + self.core.tile_names_in_m = tile_names_in_m - - def encode_sampling(self, sampling): + @staticmethod + def encode_sampling(sampling, tile_names_in_m=False): """ provides a string representing the sampling (e.g. for the tilenames) @@ -143,8 +146,10 @@ def encode_sampling(self, sampling): ------- sampling_str : str string representing the sampling + tile_names_in_m : bool, optional + controls whether the tile names are in metres or in km when > 1000m """ - if self.tile_names_in_m: + if tile_names_in_m: sampling_str = str(sampling).rjust(3, '0') else: if sampling <= 999: @@ -153,7 +158,8 @@ def encode_sampling(self, sampling): sampling_str = "".join((str(sampling / 1000.0)[0], 'K', str(sampling / 1000.0)[2])) return sampling_str - def decode_sampling(self, sampling_str): + @staticmethod + def decode_sampling(sampling_str, tile_names_in_m=False): """ converts the string representing the sampling (e.g. from the tilenames) to an integer value in metres @@ -162,13 +168,15 @@ def decode_sampling(self, sampling_str): ---------- sampling_str : str string representing the sampling + tile_names_in_m : bool, optional + controls whether the tile names are in metres or in km when > 1000m Returns ------- sampling : int the grid sampling = size of pixels; in metres. """ - if self.tile_names_in_m: + if tile_names_in_m: sampling = int(sampling_str) else: if len(sampling_str) != 3: @@ -188,6 +196,11 @@ def define_subgrids(self): subgrids : dict of Equi7Subgrid dict of all subgrids of the grid """ + # Set the tile names in metres or in km + # Reinforce copying into self.core, as it's initialized in TiledProjectionSystem not in Equi7Grid so can't + # take this custom property at init time. This is a bit of a hack but its needed in Equi7Subgrid() + self.core.tile_names_in_m = self._tile_names_in_m + subgrids = dict() for sg in self._static_subgrid_ids: subgrids[sg] = Equi7Subgrid(self.core, sg) @@ -357,7 +370,7 @@ class Equi7Subgrid(TiledProjection): """ - def __init__(self, core, continent, tile_names_in_m=False): + def __init__(self, core, continent): """ Initialises an Equi7Subgrid class for a specified continent. @@ -367,8 +380,6 @@ def __init__(self, core, continent, tile_names_in_m=False): defines core parameters of the (sub-) grid continent : str acronym of the continent, e.g. 'EU' or 'SA'. - tile_names_in_m : bool, optional - controls whether the tile names are in metres or in km when > 1000m """ # load WKT string and extent shape @@ -382,7 +393,7 @@ def __init__(self, core, continent, tile_names_in_m=False): self.core = _core # holds name of the subgrid - self.name = ''.join(('EQUI7_', continent, Equi7Grid(core.sampling, tile_names_in_m).encode_sampling(core.sampling), 'M')) + self.name = ''.join(('EQUI7_', continent, Equi7Grid.encode_sampling(core.sampling, core.tile_names_in_m), 'M')) # holds the extent of the subgrid in the lonlat-space self.polygon_geog = create_geometry_from_wkt(data['zone_extent'], epsg=4326) @@ -558,7 +569,7 @@ def encode_tilename(self, llx, lly, sampling, tilecode, shortform=False): # gives long-form of tilename (e.g. "EU500M_E012N018T6") tilename = "{}{}M_E{:03d}N{:03d}{}".format( self.core.tag, - self.encode_sampling(sampling), + Equi7Grid.encode_sampling(sampling), int(llx) // 100000, int(lly) // 100000, tilecode) @@ -682,7 +693,7 @@ def decode_tilename(self, tilename): """ tf = self.core.tile_ysize_m // 100000 - if self.tile_names_in_m: + if self.core.tile_names_in_m: # allow long-form of tilename in metres (e.g. "EU3000M_E012N018T6") if len(tilename) == 17: subgrid_id = tilename[0:2] From 53eae69ee07840596c71cdb380eb4f23d860db3f Mon Sep 17 00:00:00 2001 From: Philip Jales Date: Fri, 25 Aug 2023 12:21:36 +0100 Subject: [PATCH 4/5] Add missing configs for tile_names_in_m. Add test. --- src/equi7grid/equi7grid.py | 105 ++++++++++++++----------------------- tests/test_equi7grid.py | 23 ++++++++ 2 files changed, 62 insertions(+), 66 deletions(-) diff --git a/src/equi7grid/equi7grid.py b/src/equi7grid/equi7grid.py index 8083833..0b719ee 100644 --- a/src/equi7grid/equi7grid.py +++ b/src/equi7grid/equi7grid.py @@ -569,7 +569,7 @@ def encode_tilename(self, llx, lly, sampling, tilecode, shortform=False): # gives long-form of tilename (e.g. "EU500M_E012N018T6") tilename = "{}{}M_E{:03d}N{:03d}{}".format( self.core.tag, - Equi7Grid.encode_sampling(sampling), + Equi7Grid.encode_sampling(sampling, self.core.tile_names_in_m), int(llx) // 100000, int(lly) // 100000, tilecode) @@ -693,74 +693,47 @@ def decode_tilename(self, tilename): """ tf = self.core.tile_ysize_m // 100000 - if self.core.tile_names_in_m: - # allow long-form of tilename in metres (e.g. "EU3000M_E012N018T6") - if len(tilename) == 17: - subgrid_id = tilename[0:2] - if subgrid_id != self.core.tag: - raise ValueError(self.msg1) - tilename_sampling = tilename[2:].split('M') - sampling = Equi7Grid.decode_sampling(tilename_sampling[0]) - tilename_remaining = tilename.split('_')[1] - if sampling != self.core.sampling: - raise ValueError(self.msg1) - tile_size_m = int(tilename_remaining[-1]) * 100000 - if tile_size_m != self.core.tile_xsize_m: - raise ValueError(self.msg1) - llx = int(tilename_remaining[1:4]) - if llx % tf: - raise ValueError(self.msg2) - lly = int(tilename_remaining[5:8]) - if lly % tf: - raise ValueError(self.msg2) - tilecode = tilename_remaining[-2:] - if tilecode != self.core.tiletype: - raise ValueError(self.msg1) - else: + # allow short-form of tilename (e.g. "E012N018T6") + if len(tilename) == 10: + tile_size_m = int(tilename[-1]) * 100000 + if tile_size_m != self.core.tile_xsize_m: raise ValueError(self.msg1) - else: + llx = int(tilename[1:4]) + if llx % tf: + raise ValueError(self.msg2) + lly = int(tilename[5:8]) + if lly % tf: + raise ValueError(self.msg2) + tilecode = tilename[-2:] + if tilecode != self.core.tiletype: + raise ValueError(self.msg1) + subgrid_id = self.core.tag + sampling = self.core.sampling - # allow short-form of tilename (e.g. "E012N018T6") - if len(tilename) == 10: - tile_size_m = int(tilename[-1]) * 100000 - if tile_size_m != self.core.tile_xsize_m: - raise ValueError(self.msg1) - llx = int(tilename[1:4]) - if llx % tf: - raise ValueError(self.msg2) - lly = int(tilename[5:8]) - if lly % tf: - raise ValueError(self.msg2) - tilecode = tilename[-2:] - if tilecode != self.core.tiletype: - raise ValueError(self.msg1) - subgrid_id = self.core.tag - sampling = self.core.sampling - - # allow long-form of tilename (e.g. "EU500M_E012N018T6") - elif len(tilename) == 17: - subgrid_id = tilename[0:2] - if subgrid_id != self.core.tag: - raise ValueError(self.msg1) - sampling = Equi7Grid.decode_sampling(tilename[2:5]) - if sampling != self.core.sampling: - raise ValueError(self.msg1) - tile_size_m = int(tilename[-1]) * 100000 - if tile_size_m != self.core.tile_xsize_m: - raise ValueError(self.msg1) - llx = int(tilename[8:11]) - if llx % tf: - raise ValueError(self.msg2) - lly = int(tilename[12:15]) - if lly % tf: - raise ValueError(self.msg2) - tilecode = tilename[-2:] - if tilecode != self.core.tiletype: - raise ValueError(self.msg1) - - # wrong length - else: + # allow long-form of tilename (e.g. "EU500M_E012N018T6") + # tile_names_in_m True or False + else: + subgrid_id = tilename[0:2] + if subgrid_id != self.core.tag: raise ValueError(self.msg1) + tilename_sampling = tilename[2:].split('M') + sampling = Equi7Grid.decode_sampling(tilename_sampling[0], self.core.tile_names_in_m) + tilename_remaining = tilename.split('_')[1] + if sampling != self.core.sampling: + raise ValueError(self.msg1) + tile_size_m = int(tilename_remaining[-1]) * 100000 + if tile_size_m != self.core.tile_xsize_m: + raise ValueError(self.msg1) + llx = int(tilename_remaining[1:4]) + if llx % tf: + raise ValueError(self.msg2) + lly = int(tilename_remaining[5:8]) + if lly % tf: + raise ValueError(self.msg2) + tilecode = tilename_remaining[-2:] + if tilecode != self.core.tiletype: + raise ValueError(self.msg1) + return subgrid_id, sampling, tile_size_m, llx * 100000, lly * 100000, tilecode diff --git a/tests/test_equi7grid.py b/tests/test_equi7grid.py index d82d4d9..9a95aec 100644 --- a/tests/test_equi7grid.py +++ b/tests/test_equi7grid.py @@ -222,6 +222,29 @@ def test_lonlat2ij_in_tile(self): nptest.assert_equal(j, row_should) nptest.assert_equal(tilename, tile_should) + def test_lonlat2ij_in_tile(self): + """ + Tests the tile name with option tile_names_in_m as True or False + + """ + e7 = Equi7Grid(3000, tile_names_in_m=True) + column_should = 199 + row_should = 0 + tile_should = 'EU3000M_E048N012T6' + tilename, i, j = e7.lonlat2ij_in_tile(18.507, 44.571, lowerleft=True) + nptest.assert_equal(i, column_should) + nptest.assert_equal(j, row_should) + nptest.assert_equal(tilename, tile_should) + + e7 = Equi7Grid(3000, tile_names_in_m=False) + column_should = 199 + row_should = 0 + tile_should = 'EU3K0M_E048N012T6' + tilename, i, j = e7.lonlat2ij_in_tile(18.507, 44.571, lowerleft=True) + nptest.assert_equal(i, column_should) + nptest.assert_equal(j, row_should) + nptest.assert_equal(tilename, tile_should) + def test_proj4_reprojection_accuracy(self): """ Tests the proj4 reproject accuracy by forward and backward reprojection. From 0641ac3073b0116fe17ab818b975e660dced7a74 Mon Sep 17 00:00:00 2001 From: Philip Jales Date: Fri, 25 Aug 2023 12:24:36 +0100 Subject: [PATCH 5/5] Tidy up --- src/equi7grid/equi7grid.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/equi7grid/equi7grid.py b/src/equi7grid/equi7grid.py index 0b719ee..8de783f 100644 --- a/src/equi7grid/equi7grid.py +++ b/src/equi7grid/equi7grid.py @@ -44,8 +44,6 @@ from pytileproj.geometry import create_geometry_from_wkt from geographiclib.geodesic import Geodesic -TILE_NAMES_IN_M = True - def _load_static_data(module_path): """