Source code for grass.semantic_label.reader

import os
import sys
import json
import glob
import re
from collections import OrderedDict

import grass.script as gs

# Semantic label can have any form. Explanatory metadata can be stored
# separately. It is suggested to follow some standard e.g. remote
# sensing band names should be STAC common names, see
# https://stacspec.org/
# https://github.com/radiantearth/stac-spec/blob/master/extensions/eo/README.md#band-object


[docs]class SemanticLabelReaderError(Exception): pass
[docs]class SemanticLabelReader: """Semantic label reader""" def __init__(self): self._json_files = glob.glob( os.path.join(os.environ["GISBASE"], "etc", "i.band.library", "*.json") ) if not self._json_files: raise SemanticLabelReaderError("No semantic label definitions found") self._read_config() def _read_config(self): """Read configuration""" self.config = dict() for json_file in self._json_files: try: with open(json_file) as fd: config = json.load(fd, object_pairs_hook=OrderedDict) except json.decoder.JSONDecodeError as e: raise SemanticLabelReaderError( "Unable to parse '{}': {}".format(json_file, e) ) # check if configuration is valid self._check_config(config) self.config[os.path.basename(json_file)] = config @staticmethod def _check_config(config): """Check if config is valid :todo: check shortcut uniqueness :param dict config: configuration to be validated """ for items in config.values(): for item in ("shortcut", "bands"): if item not in items.keys(): raise SemanticLabelReaderError( "Invalid band definition: <{}> is missing".format(item) ) if len(items["bands"]) < 1: raise SemanticLabelReaderError( "Invalid band definition: no bands defined" ) @staticmethod def _print_label_extended(label, item): """Print label specific metadata :param str label: label identifier :param str item: items to be printed out """ def print_kv(k, v, indent): if isinstance(v, OrderedDict): print("{}{}:".format(" " * indent * 2, k)) for ki, vi in v.items(): print_kv(ki, vi, indent * 2) else: print("{}{}: {}".format(" " * indent * 2, k, v)) indent = 4 print("{}label: {}".format(" " * indent, label)) for k, v in item[label].items(): print_kv(k, v, indent) def _print_label(self, semantic_label=None, tag=None): sys.stdout.write(semantic_label) if tag: sys.stdout.write(" {}".format(tag)) sys.stdout.write(os.linesep)
[docs] def print_info(self, shortcut=None, band=None, semantic_label=None, extended=False): """Prints semantic label information to stdout. Can be filtered by semantic label identifier. :param str shortcut: shortcut to filter (eg. S2) or None :param str band: band (eg. 1) or None :param str semantic_label: semantic_label filter (eg. S2_8A) or None :param bool extended: print also extended metadata """ if semantic_label: try: shortcut, band = semantic_label.split("_") except ValueError: shortcut = semantic_label band = None found = False for root in self.config.values(): for item in root.values(): try: if shortcut and re.match(shortcut, item["shortcut"]) is None: continue except re.error as e: raise SemanticLabelReaderError("Invalid pattern: {}".format(e)) found = True if band and band not in item["bands"]: raise SemanticLabelReaderError( "Band <{}> not found in <{}>".format(band, shortcut) ) # print generic information if extended: for subitem in item.keys(): if subitem == "bands": # bands item is processed bellow continue print("{}: {}".format(subitem, item[subitem])) # print detailed band information if band: self._print_label_extended(band, item["bands"]) else: for iband in item["bands"]: self._print_label_extended(iband, item["bands"]) else: # basic information only if band: self._print_label( semantic_label=item["shortcut"], tag=item["bands"][band].get("tag"), ) else: for iband in item["bands"]: self._print_label( semantic_label=item["shortcut"], tag=item["bands"][iband].get("tag"), ) # print warning when defined shortcut not found if not found: gs.warning( "Metadata for semantic label <{}> not found".format(semantic_label) )
[docs] def find_file(self, semantic_label): """Find file by semantic label. Match is case-insensitive. :param str semantic_label: semantic label identifier to search for (eg. S2_1) :return str: file basename if found or None """ try: shortcut, band = semantic_label.split("_") except ValueError: # raise SemanticLabelReaderError("Invalid band identifier <{}>".format( # semantic_label # )) shortcut = None for filename, config in self.config.items(): for root in config.keys(): if ( shortcut and config[root]["shortcut"].upper() == shortcut.upper() and band.upper() in map(lambda x: x.upper(), config[root]["bands"].keys()) ): return filename return None
[docs] def get_bands(self): """Get list of band identifiers. :return list: list of valid band identifiers """ bands = [] for root in self.config.values(): for item in root.values(): for band in item["bands"]: bands.append("{}_{}".format(item["shortcut"], band)) return bands