class OnEarthRequestMgr(BaseRequestMgr): def __init__(self, params, bbox, region, proj_srs, tile_service): """!Initializes data needed for iteration through tiles. """ try: # checks all elements needed by this class, # invalid elements are removed self.cap_tree = OnEarthCapabilitiesTree(tile_service) except ParseError as error: grass.fatal( _("Unable to parse tile service file.\n%s\n") % str(error)) root = self.cap_tree.getroot() # parse tile service file and get needed data for making tile requests url, self.tile_span, t_patt_bbox, self.tile_size = self._parseTileService( root, bbox, region, params) self.url = url self.url[0] = params['url'] + '?' + url[0] # initialize data needed for iteration through tiles self._computeRequestData(bbox, t_patt_bbox, self.tile_span, self.tile_size) def GetMapRegion(self): """!Get size in pixels and bounding box of raster where all tiles will be merged. """ return self.map_region def _parseTileService(self, root, bbox, region, params): """!Get data from tile service file """ tiled_patterns = root.find('TiledPatterns') tile_groups = self._getAllTiledGroup(tiled_patterns) if not tile_groups: grass.fatal( _("Unable to parse tile service file. \n No tag '%s' was found." ) % 'TiledGroup') req_group = None for group in tile_groups: name = group.find('Name') if name.text == params['layers']: req_group = group break if req_group is None: grass.fatal( _("Tiled group '%s' was not found in tile service file") % params['layers']) group_t_patts = req_group.findall('TilePattern') best_patt = self._parseTilePattern(group_t_patts, bbox, region) urls = best_patt.text.split('\n') if params['urlparams']: url = self._insTimeToTilePatternUrl(params['urlparams'], urls) else: url = urls[0] for u in urls: if not 'time=${' in u: url = u url, t_bbox, width, height = self.cap_tree.gettilepatternurldata(url) tile_span = {} tile_span['x'] = abs(t_bbox[0] - t_bbox[2]) tile_span['y'] = abs(t_bbox[1] - t_bbox[3]) tile_pattern_bbox = req_group.find('LatLonBoundingBox') t_patt_bbox = {} for s in ['minx', 'miny', 'maxx', 'maxy']: t_patt_bbox[s] = float(tile_pattern_bbox.get(s)) tile_size = {} tile_size['x'] = width tile_size['y'] = height return url, tile_span, t_patt_bbox, tile_size def _getAllTiledGroup(self, parent, tiled_groups=None): """!Get all 'TileGroup' elements """ if not tiled_groups: tiled_groups = [] tiled_groups += parent.findall('TiledGroup') new_groups = parent.findall('TiledGroups') for group in new_groups: self._getAllTiledGroup(group, tiled_groups) return tiled_groups def _parseTilePattern(self, group_t_patts, bbox, region): """!Find best tile pattern for requested resolution. """ res = {} res['y'] = (bbox['maxy'] - bbox['miny']) / region['rows'] res['x'] = (bbox['maxx'] - bbox['minx']) / region['cols'] if res['x'] < res['y']: comp_res = 'x' else: comp_res = 'y' t_res = {} best_patt = None for pattern in group_t_patts: url, t_bbox, width, height = self.cap_tree.gettilepatternurldata( pattern.text.split('\n')[0]) t_res['x'] = abs(t_bbox[0] - t_bbox[2]) / width t_res['y'] = abs(t_bbox[1] - t_bbox[3]) / height if best_patt is None: best_res = t_res[comp_res] best_patt = pattern first = False continue best_diff = best_res - res[comp_res] tile_diff = t_res[comp_res] - res[comp_res] if (best_diff < tile_diff and tile_diff < 0) or \ (best_diff > tile_diff and best_diff > 0): best_res = t_res[comp_res] best_patt = pattern return best_patt def _insTimeToTilePatternUrl(self, url_params, urls): """!Time can be variable in some urls in OnEarth TMS. Insert requested time from 'urlparams' into the variable if any url of urls contains the variable. """ url = None not_sup_params = [] url_params_list = url_params.split('&') for param in url_params_list: try: k, v = param.split('=') except ValueError: grass.warning( _("Wrong form of parameter '%s' in '%s'. \n \ The parameter was ignored.") % (param, 'urlparams')) if k != 'time': not_sup_params.append(k) continue has_time_var = False for url in urls: url_p_idxs = self.geturlparamidxs(url, k) if not url_p_idxs: continue url_p_value = url[url_p_idxs[0] + len(k + '='):url_p_idxs[1]] if url_p_value[:2] == '${' and \ url_p_value[len(url_p_value) - 1] == '}': url = url[:url_p_idxs[0]] + param + url[url_p_idxs[1]:] has_time_var = True break if not has_time_var: grass.warning( _("Parameter '%s' in '%s' is not variable in tile pattern url." ) % (k, 'urlparams')) if not_sup_params: grass.warning(_("%s driver supports only '%s' parameter in '%s'. Other parameters are ignored.") % \ ('OnEarth GRASS', 'time', 'urlparams')) return url def _computeRequestData(self, bbox, t_patt_bbox, tile_span, tile_size): """!Initialize data needed for iteration through tiles. """ epsilon = 1e-15 mat_num_bbox = {} mat_num_bbox['min_row'] = mat_num_bbox['min_col'] = 0 mat_num_bbox['max_row'] = floor( (t_patt_bbox['maxy'] - t_patt_bbox['miny']) / tile_span['y'] + epsilon) mat_num_bbox['max_col'] = floor( (t_patt_bbox['maxx'] - t_patt_bbox['minx']) / tile_span['x'] + epsilon) BaseRequestMgr._computeRequestData(self, bbox, t_patt_bbox, self.tile_span, self.tile_size, mat_num_bbox) def GetNextTile(self): """!Get url for tile request from server and information for merging the tile with other tiles """ if self.i_col > self.t_num_bbox['max_col']: return None x_offset = self.tile_span['x'] * self.i_col y_offset = self.tile_span['y'] * self.i_row query_url = self.url[0] + "&" + "bbox=%s,%s,%s,%s" % ( float(self.query_bbox['minx'] + x_offset), float(self.query_bbox['miny'] - y_offset), float(self.query_bbox['maxx'] + x_offset), float(self.query_bbox['maxy'] - y_offset)) + self.url[1] self.tile_ref['t_cols_offset'] = int( self.tile_size['y'] * (self.i_col - self.t_num_bbox['min_col'])) self.tile_ref['t_rows_offset'] = int( self.tile_size['x'] * (self.i_row - self.t_num_bbox['min_row'])) if self.i_row >= self.t_num_bbox['max_row']: self.i_row = self.t_num_bbox['min_row'] self.i_col += 1 else: self.i_row += 1 return query_url, self.tile_ref
class OnEarthRequestMgr(BaseRequestMgr): def __init__(self, params, bbox, region, proj_srs, tile_service): """!Initializes data needed for iteration through tiles. """ try: # checks all elements needed by this class, # invalid elements are removed self.cap_tree = OnEarthCapabilitiesTree(tile_service) except ParseError as error: grass.fatal(_("Unable to parse tile service file.\n%s\n") % str(error)) root = self.cap_tree.getroot() # parse tile service file and get needed data for making tile requests url, self.tile_span, t_patt_bbox, self.tile_size = self._parseTileService( root, bbox, region, params) self.url = url self.url[0] = params['url'] + url[0] # initialize data needed for iteration through tiles self._computeRequestData(bbox, t_patt_bbox, self.tile_span, self.tile_size) def GetMapRegion(self): """!Get size in pixels and bounding box of raster where all tiles will be merged. """ return self.map_region def _parseTileService(self, root, bbox, region, params): """!Get data from tile service file """ tiled_patterns = root.find('TiledPatterns') tile_groups = self._getAllTiledGroup(tiled_patterns) if not tile_groups: grass.fatal( _("Unable to parse tile service file. \n No tag '%s' was found.") % 'TiledGroup') req_group = None for group in tile_groups: name = group.find('Name') if name.text == params['layers']: req_group = group break if req_group is None: grass.fatal(_("Tiled group '%s' was not found in tile service file") % params['layers']) group_t_patts = req_group.findall('TilePattern') best_patt = self._parseTilePattern(group_t_patts, bbox, region) urls = best_patt.text.split('\n') if params['urlparams']: url = self._insTimeToTilePatternUrl(params['urlparams'], urls) else: url = urls[0] for u in urls: if not 'time=${' in u: url = u url, t_bbox, width, height = self.cap_tree.gettilepatternurldata(url) tile_span = {} tile_span['x'] = abs(t_bbox[0] - t_bbox[2]) tile_span['y'] = abs(t_bbox[1] - t_bbox[3]) tile_pattern_bbox = req_group.find('LatLonBoundingBox') t_patt_bbox = {} for s in ['minx', 'miny', 'maxx', 'maxy']: t_patt_bbox[s] = float(tile_pattern_bbox.get(s)) tile_size = {} tile_size['x'] = width tile_size['y'] = height return url, tile_span, t_patt_bbox, tile_size def _getAllTiledGroup(self, parent, tiled_groups=None): """!Get all 'TileGroup' elements """ if not tiled_groups: tiled_groups = [] tiled_groups += parent.findall('TiledGroup') new_groups = parent.findall('TiledGroups') for group in new_groups: self._getAllTiledGroup(group, tiled_groups) return tiled_groups def _parseTilePattern(self, group_t_patts, bbox, region): """!Find best tile pattern for requested resolution. """ res = {} res['y'] = (bbox['maxy'] - bbox['miny']) / region['rows'] res['x'] = (bbox['maxx'] - bbox['minx']) / region['cols'] if res['x'] < res['y']: comp_res = 'x' else: comp_res = 'y' t_res = {} best_patt = None for pattern in group_t_patts: url, t_bbox, width, height = self.cap_tree.gettilepatternurldata(pattern.text.split( '\n')[0]) t_res['x'] = abs(t_bbox[0] - t_bbox[2]) / width t_res['y'] = abs(t_bbox[1] - t_bbox[3]) / height if best_patt is None: best_res = t_res[comp_res] best_patt = pattern first = False continue best_diff = best_res - res[comp_res] tile_diff = t_res[comp_res] - res[comp_res] if (best_diff < tile_diff and tile_diff < 0) or \ (best_diff > tile_diff and best_diff > 0): best_res = t_res[comp_res] best_patt = pattern return best_patt def _insTimeToTilePatternUrl(self, url_params, urls): """!Time can be variable in some urls in OnEarth TMS. Insert requested time from 'urlparams' into the variable if any url of urls contains the variable. """ url = None not_sup_params = [] url_params_list = url_params.split('&') for param in url_params_list: try: k, v = param.split('=') except ValueError: grass.warning(_("Wrong form of parameter '%s' in '%s'. \n \ The parameter was ignored.") % (param, 'urlparams')) if k != 'time': not_sup_params.append(k) continue has_time_var = False for url in urls: url_p_idxs = self.geturlparamidxs(url, k) if not url_p_idxs: continue url_p_value = url[url_p_idxs[0] + len(k + '='): url_p_idxs[1]] if url_p_value[:2] == '${' and \ url_p_value[len(url_p_value) - 1] == '}': url = url[:url_p_idxs[0]] + param + url[url_p_idxs[1]:] has_time_var = True break if not has_time_var: grass.warning( _("Parameter '%s' in '%s' is not variable in tile pattern url.") % (k, 'urlparams')) if not_sup_params: grass.warning( _("%s driver supports only '%s' parameter in '%s'. Other parameters are ignored.") % ('OnEarth GRASS', 'time', 'urlparams')) return url def _computeRequestData(self, bbox, t_patt_bbox, tile_span, tile_size): """!Initialize data needed for iteration through tiles. """ epsilon = 1e-15 mat_num_bbox = {} mat_num_bbox['min_row'] = mat_num_bbox['min_col'] = 0 mat_num_bbox['max_row'] = floor( (t_patt_bbox['maxy'] - t_patt_bbox['miny']) / tile_span['y'] + epsilon) mat_num_bbox['max_col'] = floor( (t_patt_bbox['maxx'] - t_patt_bbox['minx']) / tile_span['x'] + epsilon) BaseRequestMgr._computeRequestData( self, bbox, t_patt_bbox, self.tile_span, self.tile_size, mat_num_bbox) def GetNextTile(self): """!Get url for tile request from server and information for merging the tile with other tiles """ if self.i_col > self.t_num_bbox['max_col']: return None x_offset = self.tile_span['x'] * self.i_col y_offset = self.tile_span['y'] * self.i_row query_url = self.url[0] + "&" + "bbox=%s,%s,%s,%s" % ( float(self.query_bbox['minx'] + x_offset), float(self.query_bbox['miny'] - y_offset), float(self.query_bbox['maxx'] + x_offset), float(self.query_bbox['maxy'] - y_offset)) + self.url[1] self.tile_ref['t_cols_offset'] = int( self.tile_size['y'] * (self.i_col - self.t_num_bbox['min_col'])) self.tile_ref['t_rows_offset'] = int( self.tile_size['x'] * (self.i_row - self.t_num_bbox['min_row'])) if self.i_row >= self.t_num_bbox['max_row']: self.i_row = self.t_num_bbox['min_row'] self.i_col += 1 else: self.i_row += 1 return query_url, self.tile_ref