<commit_before>""" Handler for Landsat data processed and distributed through ESPA
"""
import glob
import os
import pprint
import textwrap

import arrow
from bs4 import BeautifulSoup
import numpy as np

from .._util import lazy_property
from ..core import Band, BoundingBox
from ..sensors.landsat import MTL


class ESPALandsat(object):
    """ ESPA processed "Level 2" Landsat data

    Args:
        path (str): the path to the root directory of the extracted data
            product

    Raises:
        IOError: raise if MTL or ESPA product metadata cannot be found
    """
    def __init__(self, path, xml_pattern='L*.xml', mtl_pattern='L*_MTL.txt'):
        self.path = path
        self.xml_pattern = xml_pattern
        self.mtl_pattern = mtl_pattern

        self.mtl_file = glob.glob(os.path.join(self.path, self.mtl_pattern))
        if not self.mtl_file:
            raise IOError('Cannot find MTL metadata file in {}'
                          .format(self.path))
        if len(self.mtl_file) > 1:
            raise IOError('Found multiple files matching MTL file search '
                          'pattern ({}): {}'
                          .format(self.mtl_pattern, self.mtl_file))

        self.xml_file = glob.glob(os.path.join(self.path, self.xml_pattern))
        if not self.xml_file:
            raise IOError('Cannot find ESPA XML metadata file in {}'
                          .format(self.path))
        if len(self.xml_file) > 1:
            raise IOError('Found multiple files matching ESPA XML file search '
                          'pattern ({}): {}'
                          .format(self.xml_pattern, self.xml_file))

        self.mtl_file = self.mtl_file[0]
        self.xml_file = self.xml_file[0]

        #: MTL: Landsat "MTL" metadata file
        self.mtl = MTL(self.mtl_file)
        #: BeautifulSoup: Landsat ESPA order XML metadata file
        self.xml = BeautifulSoup(open(self.xml_file), 'lxml')

    def __repr__(self):
        s = """
        EROS Science Processing Architecture (ESPA) Landsat product

        Scene ID: {scene_id}
        Acquisition date time: {time}
        Bounding Box:
            Top:        {uly}
            Left:       {ulx}
            Bottom:     {lry}
            Right:      {lrx}

        Bands: {nbands}
        {band_names}
        """.format(
            scene_id=self.timeseries_id,
            time=self.time,
            uly=self.bounding_box.top,
            ulx=self.bounding_box.left,
            lry=self.bounding_box.bottom,
            lrx=self.bounding_box.right,
            nbands=len(self.bands),
            band_names='\n            '.join([''] + self.band_long_names)
        )
        return textwrap.dedent(s)

    @lazy_property
    def bounding_box(self):
        """ BoundingBox: bounding box of product in latitude, longitude
        """
        _xml = self.xml.find('bounding_coordinates')
        return BoundingBox(
            left=float(_xml.find('west').text),
            top=float(_xml.find('north').text),
            bottom=float(_xml.find('south').text),
            right=float(_xml.find('east').text)
        )

    @lazy_property
    def bands(self):
        """ list: bands (:class:`Band`) contained within ESPA dataset
        """
        bands = []
        for _xml in self.xml.find_all('band'):
            bands.append(self._xml_to_band(_xml))
        return bands

    @lazy_property
    def band_long_names(self):
        """ list: names of bands contained within ESPA dataset
        """
        return [b.long_name for b in self.bands]

    @property
    def timeseries_id(self):
        """ str: ``timeseries_id`` identifying the Landsat acquistion scene ID
        """
        return self.mtl.scene_id

    # @lazy_property
    # def acquisition_date(self):
    #     """ Arrow: scene acquisition date as Arrow
    #     """
    #     return arrow.get(self.xml.find('acquisition_date').text)
    #
    # @lazy_property
    # def scene_center_time(self):
    #     """ Arrow: scene center acqusition time as Arrow
    #     """
    #     return arrow.get(self.xml.find('scene_center_time').text)

    @lazy_property
    def time(self):
        """ Arrow: date and time of acquisition
        """
        ad = self.xml.find('acquisition_date').text
        ct = self.xml.find('scene_center_time').text
        return arrow.get('{}T{}'.format(ad, ct))

    @lazy_property
    def instrument(self):
        """ str: instrument taking acquisition measurement
        """
        return self.xml.find('instrument').text

    @lazy_property
    def platform(self):
        """ str: platform holding instrument for this acquisition
        """
        return self.xml.find('satellite').text

    @lazy_property
    def solar_azimuth(self):
        """ float: solar azimuth angle during acqusition
        """
        return self.xml.find('solar_angles').azimuth

    @lazy_property
    def solar_zenith(self):
        """ float: solar zenith angle during acquisition
        """
        return self.xml.find('solar_angles').zenith

    def _xml_to_band(self, xml):
        """ Parse a bit of XML to a Band """
        def str2dtype(s, dtype):
            if not s:
                return None
            if dtype.kind in ('u', 'i'):
                return int(s)
            else:
                return float(s)
        # Names
        standard_name = xml.get('name')
        long_name = xml.find('long_name').text
        units = xml.get('data_units')
        # Filename path
        path = os.path.join(self.path,<commit_msg>Fix parsing of units<commit_after>""" Handler for Landsat data processed and distributed through ESPA
"""
import glob
import os
import pprint
import textwrap

import arrow
from bs4 import BeautifulSoup
import numpy as np

from .._util import lazy_property
from ..core import Band, BoundingBox
from ..sensors.landsat import MTL


class ESPALandsat(object):
    """ ESPA processed "Level 2" Landsat data

    Args:
        path (str): the path to the root directory of the extracted data
            product

    Raises:
        IOError: raise if MTL or ESPA product metadata cannot be found
    """
    def __init__(self, path, xml_pattern='L*.xml', mtl_pattern='L*_MTL.txt'):
        self.path = path
        self.xml_pattern = xml_pattern
        self.mtl_pattern = mtl_pattern

        self.mtl_file = glob.glob(os.path.join(self.path, self.mtl_pattern))
        if not self.mtl_file:
            raise IOError('Cannot find MTL metadata file in {}'
                          .format(self.path))
        if len(self.mtl_file) > 1:
            raise IOError('Found multiple files matching MTL file search '
                          'pattern ({}): {}'
                          .format(self.mtl_pattern, self.mtl_file))

        self.xml_file = glob.glob(os.path.join(self.path, self.xml_pattern))
        if not self.xml_file:
            raise IOError('Cannot find ESPA XML metadata file in {}'
                          .format(self.path))
        if len(self.xml_file) > 1:
            raise IOError('Found multiple files matching ESPA XML file search '
                          'pattern ({}): {}'
                          .format(self.xml_pattern, self.xml_file))

        self.mtl_file = self.mtl_file[0]
        self.xml_file = self.xml_file[0]

        #: MTL: Landsat "MTL" metadata file
        self.mtl = MTL(self.mtl_file)
        #: BeautifulSoup: Landsat ESPA order XML metadata file
        self.xml = BeautifulSoup(open(self.xml_file), 'lxml')

    def __repr__(self):
        s = """
        EROS Science Processing Architecture (ESPA) Landsat product

        Scene ID: {scene_id}
        Acquisition date time: {time}
        Bounding Box:
            Top:        {uly}
            Left:       {ulx}
            Bottom:     {lry}
            Right:      {lrx}

        Bands: {nbands}
        {band_names}
        """.format(
            scene_id=self.timeseries_id,
            time=self.time,
            uly=self.bounding_box.top,
            ulx=self.bounding_box.left,
            lry=self.bounding_box.bottom,
            lrx=self.bounding_box.right,
            nbands=len(self.bands),
            band_names='\n            '.join([''] + self.band_long_names)
        )
        return textwrap.dedent(s)

    @lazy_property
    def bounding_box(self):
        """ BoundingBox: bounding box of product in latitude, longitude
        """
        _xml = self.xml.find('bounding_coordinates')
        return BoundingBox(
            left=float(_xml.find('west').text),
            top=float(_xml.find('north').text),
            bottom=float(_xml.find('south').text),
            right=float(_xml.find('east').text)
        )

    @lazy_property
    def bands(self):
        """ list: bands (:class:`Band`) contained within ESPA dataset
        """
        bands = []
        for _xml in self.xml.find_all('band'):
            bands.append(self._xml_to_band(_xml))
        return bands

    @lazy_property
    def band_long_names(self):
        """ list: names of bands contained within ESPA dataset
        """
        return [b.long_name for b in self.bands]

    @property
    def timeseries_id(self):
        """ str: ``timeseries_id`` identifying the Landsat acquistion scene ID
        """
        return self.mtl.scene_id

    # @lazy_property
    # def acquisition_date(self):
    #     """ Arrow: scene acquisition date as Arrow
    #     """
    #     return arrow.get(self.xml.find('acquisition_date').text)
    #
    # @lazy_property
    # def scene_center_time(self):
    #     """ Arrow: scene center acqusition time as Arrow
    #     """
    #     return arrow.get(self.xml.find('scene_center_time').text)

    @lazy_property
    def time(self):
        """ Arrow: date and time of acquisition
        """
        ad = self.xml.find('acquisition_date').text
        ct = self.xml.find('scene_center_time').text
        return arrow.get('{}T{}'.format(ad, ct))

    @lazy_property
    def instrument(self):
        """ str: instrument taking acquisition measurement
        """
        return self.xml.find('instrument').text

    @lazy_property
    def platform(self):
        """ str: platform holding instrument for this acquisition
        """
        return self.xml.find('satellite').text

    @lazy_property
    def solar_azimuth(self):
        """ float: solar azimuth angle during acqusition
        """
        return self.xml.find('solar_angles').azimuth

    @lazy_property
    def solar_zenith(self):
        """ float: solar zenith angle during acquisition
        """
        return self.xml.find('solar_angles').zenith

    def _xml_to_band(self, xml):
        """ Parse a bit of XML to a Band """
        def str2dtype(s, dtype):
            if not s:
                return None
            if dtype.kind in ('u', 'i'):
                return int(s)
            else:
                return float(s)
        # Names
        standard_name = xml.get('name')
        long_name = xml.find('long_name').text
        units = xml.find('data_units').text
        # Filename path
        path = os.path.join(self.path,
{'fraction_non_alphanumeric': 0.11035165032826694, 'fraction_numerical': 0.0012591060347153522, 'mean_word_length': 1.9836329487523476, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 76}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3366111', 'n_tokens_mistral': 3358, 'n_tokens_neox': 3220, 'n_words': 800}