Exemple #1
0
    def sunset(
        self,
        date: datetime.date = None,
        local: bool = True,
        observer_elevation: Elevation = 0.0,
    ) -> datetime.datetime:
        """Calculates sunset time (the time in the evening when the sun is a
        0.833 degrees below the horizon. This is to account for refraction.)

        :param date: The date for which to calculate the sunset time.
                     If no date is specified then the current date will be used.

        :param local: True  = Time to be returned in location's time zone;
                      False = Time to be returned in UTC.
                      If not specified then the time will be returned in local time

        :param observer_elevation: Elevation of the observer in metres above
                                   the location.

        :returns: The date and time at which sunset occurs.
        """

        if local and self.timezone is None:
            raise ValueError(
                "Local time requested but Location has no timezone set.")

        if date is None:
            date = self.today(local)

        observer = Observer(self.latitude, self.longitude, observer_elevation)

        if local:
            return astral.sun.sunset(observer, date, self.tzinfo)
        else:
            return astral.sun.sunset(observer, date)
Exemple #2
0
    def night(
        self,
        date: datetime.date = None,
        local: bool = True,
        observer_elevation: Elevation = 0.0,
    ) -> Tuple[datetime.datetime, datetime.datetime]:
        """Calculates the night time (the time between astronomical dusk and
        astronomical dawn of the next day)

        :param date: The date for which to calculate the start of the night time.
                     If no date is specified then the current date will be used.

        :param local: True  = Time to be returned in location's time zone;
                      False = Time to be returned in UTC.
                      If not specified then the time will be returned in local time

        :param observer_elevation: Elevation of the observer in metres above
                                   the location.

        :returns: A tuple containing the start and end times
        """

        if local and self.timezone is None:
            raise ValueError(
                "Local time requested but Location has no timezone set.")

        if date is None:
            date = self.today(local)

        observer = Observer(self.latitude, self.longitude, observer_elevation)

        if local:
            return astral.sun.night(observer, date, self.tzinfo)
        else:
            return astral.sun.night(observer, date)
Exemple #3
0
    def midnight(self,
                 date: datetime.date = None,
                 local: bool = True) -> datetime.datetime:
        """Calculates the solar midnight (the time when the sun is at its lowest
        point.)

        :param date: The date for which to calculate the midnight time.
                     If no date is specified then the current date will be used.

        :param local: True  = Time to be returned in location's time zone;
                      False = Time to be returned in UTC.
                      If not specified then the time will be returned in local time

        :returns: The date and time at which the solar midnight occurs.
        """

        if local and self.timezone is None:
            raise ValueError(
                "Local time requested but Location has no timezone set.")

        if date is None:
            date = self.today(local)

        observer = Observer(self.latitude, self.longitude)

        if local:
            return astral.sun.midnight(observer, date, self.tzinfo)
        else:
            return astral.sun.midnight(observer, date)
Exemple #4
0
    def rahukaalam(
        self,
        date: datetime.date = None,
        local: bool = True,
        observer_elevation: Elevation = 0.0,
    ) -> Tuple[datetime.datetime, datetime.datetime]:
        """Calculates the period of rahukaalam.

        :param date: The date for which to calculate the rahukaalam period.
                     A value of ``None`` uses the current date.

        :param local: True  = Time to be returned in location's time zone;
                      False = Time to be returned in UTC.

        :param observer_elevation: Elevation of the observer in metres above
                                   the location.

        :return: Tuple containing the start and end times for Rahukaalam.
        """

        if local and self.timezone is None:
            raise ValueError(
                "Local time requested but Location has no timezone set.")

        if date is None:
            date = self.today(local)

        observer = Observer(self.latitude, self.longitude, observer_elevation)

        if local:
            return astral.sun.rahukaalam(observer, date, self.tzinfo)
        else:
            return astral.sun.rahukaalam(observer, date)
Exemple #5
0
def sunrise_sunset(latitude: float, longitude: float,
                   date: date) -> Tuple[datetime, datetime]:
    """Get sunrise and sunset."""
    return cast(
        Tuple[datetime, datetime],
        sun.daylight(Observer(latitude, longitude), date),
    )
Exemple #6
0
def is_daylight(location):
    """True if it's daylight at the location."""
    now = datetime.now(timezone(timedelta(hours=-10)))
    lat = float(location['latitude'])
    lon = float(location['longitude'])
    obs = Observer(latitude=lat, longitude=lon)
    today = now.date()
    s = sun(obs, date=today)

    return s['sunrise'] <= now < s['sunset']
Exemple #7
0
def getCurrentSchedule() -> WeeklySchedule:
    '''Current HoP weekly schedule.'''
    # Observer at HoP: Rochester_HoP,USA,43°09'N,77°23'W,US/Eastern,170
    # Note: elevation (in meters) is just a guess based on ROC airport. We may adjust it.
    hop_observer = Observer(latitude=43.1606355,
                            longitude=-77.3883843,
                            elevation=170)
    result = WeeklySchedule(hop_observer)
    result.addEvent(calendar.SUNDAY, ScheduleEvent(time(16, 45), time(19, 0)))
    result.addEvent(calendar.TUESDAY, ScheduleEvent(time(18, 30), time(22, 0)))
    result.addEvent(calendar.WEDNESDAY, ScheduleEvent(time(18, 45),
                                                      time(21, 0)))
    result.addEvent(calendar.FRIDAY, ScheduleEvent(time(18, 45), time(21, 0)))
    return result
Exemple #8
0
    def time_at_elevation(
        self,
        elevation: float,
        date: datetime.date = None,
        direction: SunDirection = SunDirection.RISING,
        local: bool = True,
    ) -> datetime.datetime:
        """Calculate the time when the sun is at the specified elevation.

        Note:
            This method uses positive elevations for those above the horizon.

            Elevations greater than 90 degrees are converted to a setting sun
            i.e. an elevation of 110 will calculate a setting sun at 70 degrees.

        :param elevation:  Elevation in degrees above the horizon to calculate for.

        :param date: The date for which to calculate the elevation time.
                     If no date is specified then the current date will be used.

        :param direction:  Determines whether the time is for the sun rising or setting.
                           Use ``SunDirection.RISING`` or ``SunDirection.SETTING``.
                           Default is rising.

        :param local: True  = Time to be returned in location's time zone;
                      False = Time to be returned in UTC.
                      If not specified then the time will be returned in local time

        :returns: The date and time at which dusk occurs.
        """

        if local and self.timezone is None:
            raise ValueError(
                "Local time requested but Location has no timezone set.")

        if date is None:
            date = self.today(local)

        if elevation > 90.0:
            elevation = 180.0 - elevation
            direction = SunDirection.SETTING

        observer = Observer(self.latitude, self.longitude, 0.0)

        if local:
            return astral.sun.time_at_elevation(observer, elevation, date,
                                                direction, self.tzinfo)
        else:
            return astral.sun.time_at_elevation(observer, elevation, date,
                                                direction)
Exemple #9
0
    def draw_sun(self, pos, width):
        observer = Observer(latitude=self.settings.position['lat'],
                            longitude=self.settings.position['lon'])

        tz = tzlocal()
        s = sun(observer, tzinfo=tz)
        rise_time = s['sunrise'].strftime(self.settings.time_format)
        set_time = s['sunset'].strftime(self.settings.time_format)

        self.display.draw_icon_text_centered(pos, width // 2, self.res.sunrise,
                                             rise_time, self.res.tiny_font,
                                             self.display.RED,
                                             self.display.BLACK)
        self.display.draw_icon_text_centered(
            (pos[0] + width // 2, pos[1]), width // 2, self.res.sunrise,
            set_time, self.res.tiny_font, self.display.RED, self.display.BLACK)
Exemple #10
0
    def golden_hour(
        self,
        direction: SunDirection = SunDirection.RISING,
        date: datetime.date = None,
        local: bool = True,
        observer_elevation: Elevation = 0.0,
    ) -> Tuple[datetime.datetime, datetime.datetime]:
        """Returns the start and end times of the Golden Hour when the sun is traversing
        in the specified direction.

        This method uses the definition from PhotoPills i.e. the
        golden hour is when the sun is between 4 degrees below the horizon
        and 6 degrees above.

        :param direction:  Determines whether the time is for the sun rising or setting.
                           Use ``SunDirection.RISING`` or ``SunDirection.SETTING``.
                           Default is rising.

        :param date: The date for which to calculate the times.

        :param local: True  = Times to be returned in location's time zone;
                      False = Times to be returned in UTC.
                      If not specified then the time will be returned in local time

        :param observer_elevation: Elevation of the observer in metres above
                                   the location.

        :return: A tuple of the date and time at which the Golden Hour starts and ends.
        """

        if local and self.timezone is None:
            raise ValueError(
                "Local time requested but Location has no timezone set.")

        if date is None:
            date = self.today(local)

        observer = Observer(self.latitude, self.longitude, observer_elevation)

        if local:
            return astral.sun.golden_hour(observer, date, direction,
                                          self.tzinfo)
        else:
            return astral.sun.golden_hour(observer, date, direction)
Exemple #11
0
    def solar_azimuth(
        self,
        dateandtime: datetime.datetime = None,
        observer_elevation: Elevation = 0.0,
    ) -> float:
        """Calculates the solar azimuth angle for a specific date/time.

        :param dateandtime: The date and time for which to calculate the angle.
        :returns: The azimuth angle in degrees clockwise from North.
        """

        if dateandtime is None:
            dateandtime = astral.sun.now(self.tzinfo)
        elif not dateandtime.tzinfo:
            dateandtime = self.tzinfo.localize(dateandtime)

        observer = Observer(self.latitude, self.longitude, observer_elevation)

        dateandtime = dateandtime.astimezone(pytz.utc)  # type: ignore
        return astral.sun.azimuth(observer, dateandtime)
Exemple #12
0
    def twilight(
        self,
        date: datetime.date = None,
        direction: SunDirection = SunDirection.RISING,
        local: bool = True,
        observer_elevation: Elevation = 0.0,
    ):
        """Returns the start and end times of Twilight in the UTC timezone when
        the sun is traversing in the specified direction.

        This method defines twilight as being between the time
        when the sun is at -6 degrees and sunrise/sunset.

        :param direction:  Determines whether the time is for the sun rising or setting.
                           Use ``astral.SUN_RISING`` or ``astral.SunDirection.SETTING``.

        :param date: The date for which to calculate the times.

        :param local: True  = Time to be returned in location's time zone;
                      False = Time to be returned in UTC.
                      If not specified then the time will be returned in local time

        :param observer_elevation: Elevation of the observer in metres above
                                   the location.

        :return: A tuple of the UTC date and time at which twilight starts and ends.
        """

        if local and self.timezone is None:
            raise ValueError(
                "Local time requested but Location has no timezone set.")

        if date is None:
            date = self.today(local)

        observer = Observer(self.latitude, self.longitude, observer_elevation)

        if local:
            return astral.sun.twilight(observer, date, direction, self.tzinfo)
        else:
            return astral.sun.twilight(observer, date, direction)
Exemple #13
0
    def dusk(
        self,
        date: datetime.date = None,
        local: bool = True,
        observer_elevation: Elevation = 0.0,
    ) -> datetime.datetime:
        """Calculates the dusk time (the time in the evening when the sun is a
        certain number of degrees below the horizon. By default this is 6
        degrees but can be changed by setting the
        :attr:`solar_depression` property.)

        :param date: The date for which to calculate the dusk time.
                     If no date is specified then the current date will be used.

        :param local: True  = Time to be returned in location's time zone;
                      False = Time to be returned in UTC.
                      If not specified then the time will be returned in local time

        :param observer_elevation: Elevation of the observer in metres above
                                   the location.

        :returns: The date and time at which dusk occurs.
        """

        if local and self.timezone is None:
            raise ValueError(
                "Local time requested but Location has no timezone set.")

        if date is None:
            date = self.today(local)

        observer = Observer(self.latitude, self.longitude, observer_elevation)

        if local:
            return astral.sun.dusk(observer, date, self.solar_depression,
                                   self.tzinfo)
        else:
            return astral.sun.dusk(observer, date, self.solar_depression)
 def sunrise_sunset(date, lat, lon):
     """
     Calculate sunrise and sunset times in utc for given date,
     lat and lon.
     Parameters
     ----------
     date : datetime.date
         Date in yyyy-mm-dd.
     lat : float
         Latitude.
     lon : float
         Longitude.
     Returns
     -------
     sunrise : datetime
         Sunrise time.
     sunset : datetime
         Sunset time.
     """
     obs = Observer(latitude=lat, longitude=lon, elevation=0.0)
     sunrise = sun.sunrise(observer=obs, date=date)
     sunset = sun.sunset(observer=obs, date=date)
     return sunrise, sunset
Exemple #15
0
    def sun(
        self,
        date: datetime.date = None,
        local: bool = True,
        observer_elevation: Elevation = 0.0,
    ) -> dict:
        """Returns dawn, sunrise, noon, sunset and dusk as a dictionary.

        :param date: The date for which to calculate the times.
                     If no date is specified then the current date will be used.

        :param local: True  = Time to be returned in location's time zone;
                      False = Time to be returned in UTC.
                      If not specified then the time will be returned in local time

        :param observer_elevation: Elevation of the observer in metres above
                                   the location.

        :returns: Dictionary with keys ``dawn``, ``sunrise``, ``noon``,
            ``sunset`` and ``dusk`` whose values are the results of the
            corresponding methods.
         """

        if local and self.timezone is None:
            raise ValueError(
                "Local time requested but Location has no timezone set.")

        if date is None:
            date = self.today(local)

        observer = Observer(self.latitude, self.longitude, observer_elevation)

        if local:
            return astral.sun.sun(observer, date, self.solar_depression,
                                  self.tzinfo)
        else:
            return astral.sun.sun(observer, date, self.solar_depression)
Exemple #16
0
 def test_bad_longitude(self):
     with pytest.raises(ValueError):
         Observer(1, "o", 1)
Exemple #17
0
 def test_bad_elevation(self):
     with pytest.raises(ValueError):
         Observer(1, 1, "o")
Exemple #18
0
 def test_latitude_outside_limits(self):
     obs = Observer(90.1, 0, 0)
     assert obs.latitude == 90.0
     obs = Observer(-90.1, 0, 0)
     assert obs.latitude == -90.0
Exemple #19
0
 def test_longitude_outside_limits(self):
     obs = Observer(0, 180.1, 0)
     assert obs.longitude == 180.0
     obs = Observer(0, -180.1, 0)
     assert obs.longitude == -180.0
Exemple #20
0
 def test_default(self):
     obs = Observer()
     assert obs.latitude == 51.4733
     assert obs.longitude == -0.0008333
     assert obs.elevation == 0.0
Exemple #21
0
def main(df, loc_main_outputs):
    print("starting script 2 (Database summary)")
    today = str(date.today())

    # Create new pandas data frame for processed output.
    df_pro = pd.DataFrame(df)

    # Add new columns, populated with 'nan'.
    df_pro = pd.concat([
        df_pro,
        pd.DataFrame(columns=[
            'binomial_species', 'species_short', 'plot_area_from_field_length',
            'solar_elevation', 'solar_noon_elevation',
            'solar_elevation_offset', 'solar_noon_utc',
            'minutes_offset_from_solar_noon'
        ])
    ],
                       sort=False)

    # Extract three-character site code from survey_code and add new column.
    df_pro['site_code'] = df_pro['survey_code'].str[-3:]

    # Concatenate genus and species into binomial species and species_short.
    df_pro['binomial_species'] = df_pro['plot_genus'] + " " + \
        df_pro['plot_species']

    df_pro['species_short'] = df_pro['plot_genus'].str[0] + ". " + \
        df_pro['plot_species']

    # Calculate harvest plot area from side length
    df_pro['plot_area_from_field_length'] = (df_pro['plot_side_length']**2)

    # Calculate solar parameters (elevation and timing) using the Astral library
    for index_label, row in df_pro.iterrows():
        latitude = row[
            'SiteLatitude']  # Define latitude parameter for observation.
        longitude = row[
            'SiteLongitude']  # Define longitude parameter for observation.
        elevation = row[
            'SiteElevation']  # Define elevation parameter for observation (supports more advanced sun calculations in future) # noqa
        survey_datetime = row['SurveyDateTimeUTC']

        location = Observer(latitude, longitude,
                            elevation)  # specify observer location for Astral.

        solar_noon = astral.sun.noon(
            location, survey_datetime)  # Calculate time of solar noon.

        solar_elevation = astral.sun.elevation(
            location,
            survey_datetime)  # Calculate solar elevation during survey.
        solar_elevation = round(solar_elevation, 2)  # Tidy elevation value.

        solar_noon_elevation = astral.sun.elevation(
            location, solar_noon)  # Calculate solar elevation at noon.
        solar_noon_elevation = round(solar_noon_elevation, 2)

        solar_elevation_offset = solar_noon_elevation - solar_elevation
        solar_elevation_offset = round(solar_elevation_offset, 2)

        # Determine the absolute number of seconds between the survey and solar noon, and convert into minutes.
        time_offset = abs(survey_datetime - solar_noon).seconds / 60
        time_offset = round(time_offset, 0)

        # Write new values to dataframe
        df_pro.at[index_label, 'solar_elevation'] = solar_elevation
        df_pro.at[index_label, 'solar_noon_elevation'] = solar_noon_elevation
        df_pro.at[index_label,
                  'solar_elevation_offset'] = solar_elevation_offset
        df_pro.at[index_label, 'solar_noon_utc'] = solar_noon
        df_pro.at[index_label, 'minutes_offset_from_solar_noon'] = time_offset

    # Create new dataframe of observations sampled at peak biomass
    df_pro_peak = pd.DataFrame(df_pro[df_pro['PeakBiomass'] > 0])

    # Summarize database
    n_of_surveys = df['survey_code'].nunique(
    )  # Returns the number of unique survey codes.
    list_of_surveys = list(set(
        df['survey_code']))  # Returns a list of unique survey codes.
    list_of_surveys.sort()  # Sorts list into ascending order
    list_of_surveys_sep = '\n'.join(
        list_of_surveys)  # Add line returns to list of surveys.
    n_of_sites = df_pro['SiteName'].nunique(
    )  # Returns the number of unique sites.

    n_of_surveys_peak = df_pro_peak['survey_code'].nunique(
    )  # Returns n of unique survey codes.
    # list_of_surveys_peak = list(set(df_pro_peak['survey_code']))  # Returns list of unique survey codes.
    # list_of_surveys_peak.sort()  # Sorts list into ascending order
    # list_of_surveys_peak_sep = '\n'.join(list_of_surveys_peak)  # Add line returns to list of surveys.

    n_of_contributing_teams = df['Investigators'].nunique(
    )  # Returns the number of contributing teams.
    n_of_observations = df['RecordID'].nunique(
    )  # Returns n of observations in the data set.
    n_of_observations_peak = sum(
        df_pro_peak['PeakBiomass']
    )  # Returns n of observations in the data set.
    sum_of_harvested_biomass = round(
        (df['AGB'].sum()) / 1000, 2)  # Returns sum of harvested biomass in g.
    sum_of_harvested_peak_biomass = round(
        df.loc[df['PeakBiomass'] > 0, ['AGB'][0]].sum() / 1000, 2)
    n_of_sites_peak = df_pro_peak['site_code'].nunique(
    )  # Returns the number of sites with peak observations.
    mean_offset_from_solar_noon = df_pro_peak[
        'minutes_offset_from_solar_noon'].sum() / len(
            df_pro_peak['minutes_offset_from_solar_noon'])  # noqa
    mean_offset_from_solar_noon = round(mean_offset_from_solar_noon, 0)
    max_offset_from_solar_noon = df_pro_peak[
        'minutes_offset_from_solar_noon'].max()

    # n_of_edited_plots = database['manual_filtering_required'].values.sum()  # noqa # Count the number of edited point clouds
    n_of_edited_plots_peak = df_pro_peak[
        'manual_filtering_required'].values.sum(
        )  # noqa # Count the number of edited point clouds

    n_of_igbp = df['IGBP_class'].nunique(
    )  # count number of IGBP classes sampled
    n_of_igbp_peak = df_pro_peak['IGBP_class'].nunique(
    )  # count number of IGBP classes sampled

    # Plant Functional_Type summaries.
    # list_of_groups = list(set(database_pro['plant_functional_type'].dropna()))
    n_of_graminoid_plots = len(
        df_pro[(df_pro['plant_functional_type'] == 'Graminoid')])  # noqa
    n_of_shrub_plots = len(
        df_pro[(df_pro['plant_functional_type'] == 'Shrub')])
    n_of_forb_plots = len(df_pro[(df_pro['plant_functional_type'] == 'Forb')])
    n_of_tree_plots = len(df_pro[(df_pro['plant_functional_type'] == 'Tree')])
    n_of_fern_plots = len(df_pro[(df_pro['plant_functional_type'] == 'Fern')])
    n_of_succulent_plots = len(
        df_pro[(df_pro['plant_functional_type'] == 'Succulent')])  # noqa

    n_of_graminoid_plots_peak = len(df_pro_peak[(df_pro_peak.AGB > 0) & (
        df_pro_peak.plant_functional_type == 'Graminoid')])  # noqa
    n_of_shrub_plots_peak = len(
        df_pro_peak[(df_pro_peak.AGB > 0)
                    & (df_pro_peak.plant_functional_type == 'Shrub')])  # noqa
    n_of_forb_plots_peak = len(
        df_pro_peak[(df_pro_peak.AGB > 0)
                    & (df_pro_peak.plant_functional_type == 'Forb')])  # noqa
    n_of_tree_plots_peak = len(
        df_pro_peak[(df_pro_peak.AGB > 0)
                    & (df_pro_peak.plant_functional_type == 'Tree')])  # noqa
    n_of_fern_plots_peak = len(
        df_pro_peak[(df_pro_peak.AGB > 0)
                    & (df_pro_peak.plant_functional_type == 'Fern')])  # noqa
    n_of_succulent_plots_peak = len(df_pro_peak[(df_pro_peak.AGB > 0) & (
        df_pro_peak.plant_functional_type == 'Succulent')])  # noqa
    n_of_bryophyte_plots_peak = len(df_pro_peak[(df_pro_peak.AGB > 0) & (
        df_pro_peak.plant_functional_type == 'Bryophyte')])  # noqa

    # Summarise sampled families
    list_of_families = sorted(list(set(
        df['plot_family'].dropna())))  # Returns a list of unique families.
    remove_list = ['TBC', 'Unknown']  # Create list of 'species' to remove.
    list_of_families = [i for i in list_of_families if i not in remove_list
                        ]  # Remove invalid 'families' from list
    n_of_families = len(
        list_of_families)  # Returns the number of unique records.
    list_of_families_sep = '\n'.join(
        list_of_families)  # Add line returns to list.

    # Summarise sampled families for peak biomass
    list_of_families_peak = sorted(
        list(set(df_pro_peak['plot_family'].dropna())))
    remove_list = ['TBC', 'Unknown']  # Create list of 'species' to remove.
    list_of_families_peak = [
        i for i in list_of_families_peak if i not in remove_list
    ]
    n_of_families_peak = len(
        list_of_families_peak)  # Returns the number of unique records.
    list_of_families_peak_sep = '\n'.join(
        list_of_families_peak)  # Add line returns to list.

    # Summarise sampled species
    list_of_species = sorted(list(set(df_pro['binomial_species'].dropna()))
                             )  # noqa # Returns list of unique species/groups.
    remove_list = ['TBC TBC',
                   'Unknown spp.']  # Create list of 'species' to remove.
    list_of_species = [i for i in list_of_species if i not in remove_list
                       ]  # Remove invalid 'species' from list
    n_of_species = len(
        list_of_species)  # Returns the number of unique records.  # noqa
    list_of_species_sep = '\n'.join(
        list_of_species)  # Add line returns to list.

    # Summarise sampled species for peak biomass
    list_of_species_peak = sorted(
        list(set(df_pro_peak['binomial_species'].dropna()))
    )  # noqa # Returns list of unique species/groups.
    remove_list = ['TBC TBC',
                   'Unknown spp.']  # Create list of 'species' to remove.
    list_of_species_peak = [
        i for i in list_of_species_peak if i not in remove_list
    ]  # noqa # Remove invalid 'species' from list
    n_of_species_peak = len(
        list_of_species_peak)  # Returns the number of unique records.
    list_of_species_peak_sep = '\n'.join(
        list_of_species_peak)  # Add line returns to list.

    # Summary of wind conditions.
    wind_all_mean = round(sum(df['wind_speed']) / len(df['wind_speed']), 2)
    wind_all_max = round(max(df['wind_speed']), 2)
    wind_all_min = round(min(df['wind_speed']), 2)
    wind_all_mean_peak = round(
        sum(df_pro_peak['wind_speed']) / len(df_pro_peak['wind_speed']), 2)
    wind_all_max_peak = round(max(df_pro_peak['wind_speed']), 2)
    wind_all_min_peak = round(min(df_pro_peak['wind_speed']), 2)

    # Summary of species that were sub-sampled for determination of moisture content, for reporting in the methods.
    # Filter dataset by peak biomass = True and 'EntirePlantDried' = False
    temp = df_pro[(df_pro['PeakBiomass'] > 0)
                  & (df_pro['EntirePlantDried'] == 0)]
    # Extract list of species, sorted alphabetically.
    species_dry_subsample = sorted(list(set(
        temp['binomial_species'].dropna())))
    # Insert 'and', if needed, and remove quotation marks from list.
    if len(species_dry_subsample) > 1:
        species_dry_subsample.insert(-1, "and")
        species_dry_subsample = (', '.join(species_dry_subsample[:-2]) + ' ' +
                                 ' '.join(species_dry_subsample[-2:]))
    else:
        species_dry_subsample = ', '.join(list(species_dry_subsample))

    # Summarise drones used for sampling.
    n_of_drones = df['drone_platform'].nunique()
    n_of_drones_peak = df_pro_peak['drone_platform'].nunique()
    list_of_drones = sorted(list(set(df['drone_platform'].dropna())))
    list_of_drones = ', '.join(
        list(list_of_drones))  # Remove quotation marks from list.
    list_of_drones_peak = sorted(
        list(set(df_pro_peak['drone_platform'].dropna())))
    list_of_drones_peak = ', '.join(
        list(list_of_drones_peak))  # Remove quotation marks from list.

    # Summarise cameras used for sampling.
    n_of_cameras = df['camera_sensor'].nunique()
    n_of_cameras_peak = df_pro_peak['camera_sensor'].nunique()
    list_of_cameras = sorted(list(set(df['camera_sensor'].dropna())))
    list_of_cameras = ', '.join(
        list(list_of_cameras))  # Remove quotation marks from list.
    list_of_cameras_peak = sorted(
        list(set(df_pro_peak['camera_sensor'].dropna())))
    list_of_cameras_peak = ', '.join(
        list(list_of_cameras_peak))  # Remove quotation marks from list.

    # Output summary report.
    outf = open(loc_main_outputs + "Report 3 - project summary.txt", "w+")
    outf.write("Drone Allometry Experiment: Summary Report" + '\n')
    outf.write("Andrew Cunliffe <*****@*****.**>" + '\n')
    outf.write("Generated on " + today + '\n' + '\n')
    outf.write(
        '---------------------------------------------------------------------------------'
        + '\n')
    outf.write(
        'For the drone allometry project, focussing on seasonal biomass peaks, we have sampled '
        + str(n_of_observations_peak) + ' individual harvest plots, from ' +
        str(n_of_sites_peak) + ' sites with ' + str(n_of_surveys_peak) +
        ' individual photogrammetric reconstructions' + '\n' + '\n')
    outf.write(
        str(sum_of_harvested_peak_biomass) +
        ' kg of aboveground biomass has been measured.' + '\n' + '\n')
    outf.write('Sampling was undertaken using ' + str(n_of_drones_peak) +
               ' types of drone: ' + str(list_of_drones_peak) + '.' + '\n' +
               '\n')
    outf.write('Sampling was undertaken using ' + str(n_of_cameras_peak) +
               ' types of camera: ' + str(list_of_cameras_peak) + '.' + '\n' +
               '\n')
    outf.write(
        'For the largest taxa (' + str(species_dry_subsample) +
        '), freshly harvested biomass was weighed ' +
        'in the field and representative sub-samples were then dried to determine the moisture content '
        + 'for each partition (Cunliffe et al., 2020).' + '\n' + '\n')
    outf.write(
        'In a few instances where plot infrastructure (e.g., posts or flags) were visible in the point cloud '
        + '(n=' + str(n_of_edited_plots_peak) +
        ' plots), these points were manually assigned to a noise class ' +
        'and excluded from canopy height calculations.' + '\n' + '\n')
    outf.write(
        'On average (across all harvest plots), surveys were conducted ' +
        str(mean_offset_from_solar_noon) +
        ' minutes from solar noon. The maximum gap between the survey and solar noon was '
        + str(max_offset_from_solar_noon) + ' minutes.' + '\n' + '\n')
    outf.write('Across peak biomass surveys, the mean wind speed was ' +
               str(wind_all_mean_peak) + ' m s-1 (ranging from ' +
               str(wind_all_min_peak) + ' to ' + str(wind_all_max_peak) +
               ' m s-1).' + '\n' + '\n')

    outf.write('Sampling has occurred in ' + str(n_of_igbp_peak) +
               ' IGBP classes. ')
    outf.write('Sampled taxa include ' + str(n_of_species_peak) + ' species:' +
               '\n')
    outf.write(str(n_of_shrub_plots_peak) + ' shrub plots' + '\n')
    outf.write(str(n_of_graminoid_plots_peak) + ' graminoid plots' + '\n')
    outf.write(str(n_of_succulent_plots) + ' succulent plots' + '\n')
    outf.write(str(n_of_forb_plots_peak) + ' forb plots' + '\n')
    outf.write(str(n_of_tree_plots_peak) + ' tree plots' + '\n')
    outf.write(str(n_of_succulent_plots_peak) + ' succulent plots' + '\n')
    outf.write(str(n_of_bryophyte_plots_peak) + ' bryophyte plots' + '\n')
    outf.write(str(n_of_fern_plots_peak) + ' fern plots' + '\n' + '\n')
    outf.write('Families: (N = ' + str(n_of_families_peak) + ')' + '\n')
    outf.write(str(list_of_families_peak_sep) + '\n' + '\n')
    outf.write('Species (N = ' + str(n_of_species_peak) + '):' + '\n')
    outf.write(str(list_of_species_peak_sep) + '\n' + '\n')
    outf.write(
        '---------------------------------------------------------------------------------'
        + '\n')
    outf.write(
        'In total (including samples from non-seasonal biomass peak), we have sampled '
        + str(n_of_observations) + ' individual harvest plots, from ' +
        str(n_of_sites) + ' sites, consisting of ' + str(n_of_surveys) +
        ' surveys contributed by ' + str(n_of_contributing_teams) + ' teams.' +
        '\n')
    outf.write('Sampling has occurred in ' + str(n_of_igbp) + ' IGBP classes.')
    outf.write('Sampled taxa include ' + str(n_of_species) + ' species:' +
               '\n')
    outf.write('In total ' + str(sum_of_harvested_biomass) +
               ' kg of dry biomass has been surveyed and then harvested.' +
               '\n')
    outf.write(
        'Across all harvest plots and surveys, the mean wind speed was ' +
        str(wind_all_mean) + ' m s-1 (ranging from ' + str(wind_all_min) +
        ' to ' + str(wind_all_max) + ' m s-1).' + '\n')
    outf.write('Sampling was undertaken using ' + str(n_of_drones) +
               ' types of drone/sensor. (' + str(list_of_drones) + ')' + '\n')
    outf.write('\n')
    outf.write('Sampled taxa include:' + '\n')
    outf.write(str(n_of_shrub_plots) + ' shrub plots' + '\n')
    outf.write(str(n_of_graminoid_plots) + ' graminoid plots' + '\n')
    outf.write(str(n_of_succulent_plots) + ' succulent plots' + '\n')
    outf.write(str(n_of_forb_plots) + ' forb plots' + '\n')
    outf.write(str(n_of_fern_plots) + ' fern plots' + '\n')
    outf.write(str(n_of_tree_plots) + ' tree plots' + '\n')
    outf.write('\n')
    outf.write('Species (N = ' + str(n_of_species) + '):' + '\n')
    outf.write(str(list_of_species_sep) + '\n' + '\n')
    outf.write('Families: (N = ' + str(n_of_families) + ')' + '\n')
    outf.write(str(list_of_families_sep) + '\n' + '\n')
    outf.write('\n')
    outf.write(
        "---------------------------------------------------------------------------------"
        + '\n')
    outf.write('There are ' + str(n_of_surveys) + ' survey codes in use.' +
               '\n')
    outf.write(
        '(Date of survey (YYYYMMDD), Investigator initials (_XX), and three-digit site code (_XXX)), these are:'
        + '\n')
    outf.write(list_of_surveys_sep + '\n' + '\n')
    outf.write(
        "---------------------------------------------------------------------------------"
        + '\n')
    outf.write('END OF REPORT')
    outf.close()

    # Generate survey description table:
    # Subset only peak biomass surveys
    survey_summary = df[df.PeakBiomass]

    # Subset columns to keep.
    col_keep_list = [
        'survey_code', 'SiteLatitude', 'SiteLongitude', 'SiteElevation', 'MAT',
        'MAP', 'KoppenCC', 'IGBP_class', 'wind_speed', 'sky_conditions_code',
        'drone_platform', 'camera_sensor'
    ]

    survey_summary = pd.DataFrame(survey_summary[col_keep_list])

    # Drop duplicate rows from dataframe.
    survey_summary = survey_summary.drop_duplicates(['survey_code'],
                                                    keep='last')

    # Export summary of surveys.
    survey_summary.to_csv(loc_main_outputs + 'summary of surveys.csv',
                          na_rep='NA',
                          index=False)  # noqa

    return df_pro
Exemple #22
0
def test_Elevation_Above85Degrees():
    d = datetime.datetime(2001, 6, 21, 13, 11, 0)
    assert sun.elevation(Observer(86, 77.2),
                         d) == pytest.approx(23.102501151619506, abs=0.001)
Exemple #23
0
    if config.has_option('global', 'serial'):
        ports = config['global']['serial'].split(',')
        useSerial = True
        if debug:
            print('DEBUG: pySerial version: ' + serial.__version__)
        for port in ports:
            ser[port] = serial.Serial()
            ser[port].port = port
            ser[port].baudrate = 9600
            ser[port].timeout = 1

    for ind in ser:
        print('using COM port: ' + str(ser[ind].port))
    # Initialize astronomical calculator and timezone finder
    sun.solar_depression = 'civil'
    location = Observer()
    timeZoneFinder = timezonefinder.TimezoneFinder()

    try:
        # infinite loop
        while True:
            # check if we are connected to iracing
            check_iracing()

            # if we are, then process data
            if state.ir_connected:
                loop()

            # sleep for 1 second
            # maximum you can use is 1/60
            # cause iracing update data with 60 fps
Exemple #24
0
def check_iracing():

    if state.ir_connected and not (ir.is_initialized and ir.is_connected):
        state.ir_connected = False
        # don't forget to reset all your in State variables
        state.date_time = -1
        state.tick = 0
        state.latitude = -1
        state.longitude = -1
        state.elevation = -1
        state.timezone = ''
        state.mqttdict = {}

        # Close serial port to buttonbox
        for ind in ser:
            if ser[ind].is_open:
                ser[ind].close()

        # we are shut down ir library (clear all internal variables)
        ir.shutdown()
        print('irsdk disconnected')
        mqtt_publish('state', 0)
    elif not state.ir_connected:
        # Check if a dump file should be used to startup IRSDK
        if config.has_option('global', 'simulate'):
            is_startup = ir.startup(test_file=config['global']['simulate'])
            print('starting up using dump file: ' +
                  str(config['global']['simulate']))
        else:
            is_startup = ir.startup()
            if debug:
                print('DEBUG: starting up with simulation')

        if is_startup and ir.is_initialized and ir.is_connected:
            state.ir_connected = True
            # Check need and open serial connection
            if config.has_option('global', 'serial'):
                for ind in ser:
                    try:
                        ser[ind].open()
                        print('Serial port ' + ind + ' open')
                        if debug:
                            print('DEBUG: ' + str(ser[ind]))
                    except Exception:
                        print('Unable to open port ' + ser[ind].port +
                              '. Serial communication is disabled')

            print('irsdk connected')
            if state.mqttConnected:
                mqtt_publish('state', 1)

            # Get geographical track information and track timezone for
            # astronomical calculations
            state.latitude = float(
                str(ir['WeekendInfo']['TrackLatitude']).rstrip(' m'))
            state.longitude = float(
                str(ir['WeekendInfo']['TrackLongitude']).rstrip(' m'))
            state.elevation = float(
                str(ir['WeekendInfo']['TrackAltitude']).rstrip(' m'))
            closestTimezone = timeZoneFinder.closest_timezone_at(
                lng=state.longitude, lat=state.latitude)
            state.timezone = pytz.timezone(closestTimezone)

            location = Observer(state.latitude, state.longitude,
                                state.elevation)
            print('Location: ', location)

            locationInfo = LocationInfo(ir['WeekendInfo']['TrackCity'],
                                        ir['WeekendInfo']['TrackCountry'],
                                        state.timezone, state.latitude,
                                        state.longitude)
            print('LocationInfo: ', locationInfo)
Exemple #25
0
#!/usr/bin/python3
from fronius2influx import Fronius2Influx
from influxdb_client import InfluxDBClient
from astral import Observer
import pytz

city = Observer(50.118890, 10.675173, 15)
client = InfluxDBClient.from_config_file('../conf/config.ini')
bucket = 'grafana'
tz = pytz.timezone("Europe/Berlin")
endpoints = [
    'http://10.0.0.210/solar_api/v1/GetInverterRealtimeData.cgi?Scope=Device&DataCollection=3PInverterData&DeviceId=1',
    'http://10.0.0.210/solar_api/v1/GetInverterRealtimeData.cgi?Scope=Device&DataCollection=CommonInverterData&DeviceId=1',
    'http://10.0.0.210/solar_api/v1/GetInverterRealtimeData.cgi?Scope=Device&DataCollection=MinMaxInverterData&DeviceId=1',
    'http://10.0.0.210/solar_api/v1/GetMeterRealtimeData.cgi?Scope=Device&DeviceId=0',
    'http://10.0.0.210/solar_api/v1/GetLoggerInfo.cgi'
]
z = Fronius2Influx(client, endpoints, bucket, city, tz)
z.IGNORE_SUN_DOWN = False
z.run()
Exemple #26
0
 def observer(self) -> Observer:
     return Observer(self.latitude, self.longitude, 0.0)
Exemple #27
0
# The order of the pixel colors - RGB or GRB. Some NeoPixels have red and green reversed!
# For RGBW NeoPixels, simply change the ORDER to RGBW or GRBW.
ORDER = neopixel.GRB

#Create pixels object
pixels = neopixel.NeoPixel(pixel_pin,
                           num_pixels,
                           brightness=0.03,
                           auto_write=False,
                           pixel_order=ORDER)

#Create sun object for Oxford today
longitude = -1.2577
lattitude = 51.7520
s = sun(Observer(lattitude, longitude),
        date=datetime.today(),
        dawn_dusk_depression=6.0)

#Print sun times for info
print((f'Dawn:    {s["dawn"]}\n'
       f'Sunrise: {s["sunrise"]}\n'
       f'Noon:    {s["noon"]}\n'
       f'Sunset:  {s["sunset"]}\n'
       f'Dusk:    {s["dusk"]}\n'))

#Obtain sun times as UNIX timestamps
dawn = s["dawn"].timestamp()
sunrise = s["sunrise"].timestamp()
sunset = s["sunset"].timestamp()
dusk = s["dusk"].timestamp()
Exemple #28
0
def add_solar_variable(obj,
                       latitude=None,
                       longitude=None,
                       solar_angle=0.,
                       dawn_dusk=False):
    """
    Calculate solar times depending on location on earth.

    Astral 2.2 is recommended for best performance and for the dawn/dusk feature as it
    seems like the dawn calculations are wrong with earlier versions.

    Parameters
    ----------
    obj : act object
        ACT object
    latitude : str
        Latitude variable, default will look for matching variables
        in object
    longitude : str
        Longitude variable, default will look for matching variables
        in object
    solar_angle : float
        Number of degress to use for dawn/dusk calculations
    dawn_dusk : boolean
         If set to True, will add values 2 (dawn) and 3 (dusk) to the solar variable

    Returns
    -------
    obj : act object
        ACT object
    """

    variables = list(obj.keys())

    # Get coordinate variables
    if latitude is None:
        latitude = [s for s in variables if "latitude" in s]
        if len(latitude) == 0:
            latitude = [s for s in variables if "lat" in s]
        if len(latitude) == 0:
            raise ValueError(
                "Latitude variable not set and could not be discerned from the data"
            )

    if longitude is None:
        longitude = [s for s in variables if "longitude" in s]
        if len(longitude) == 0:
            longitude = [s for s in variables if "lon" in s]
        if len(longitude) == 0:
            raise ValueError(
                "Longitude variable not set and could not be discerned from the data"
            )

    # Get lat/lon variables
    lat = obj[latitude[0]].values
    lon = obj[longitude[0]].values

    # Set the the number of degrees the sun must be below the horizon
    # for the dawn/dusk calculation. Need to do this so when the calculation
    # sends an error it is not going to be an inacurate switch to setting
    # the full day.
    if ASTRAL:
        astral.solar_depression = solar_angle
    else:
        a = astral.Astral()
        a.solar_depression = 0.

    # If only one lat/lon value then set up the observer location
    # for Astral.  If more than one, it will get set up in the loop
    if lat.size == 1 and ASTRAL:
        loc = Observer(latitude=lat, longitude=lon)

    # Loop through each time to ensure that the sunrise/set calcuations
    # are correct for each time and lat/lon if multiple
    results = []
    time = obj['time'].values
    for i in range(len(time)):
        # Set up an observer if multiple lat/lon
        if lat.size > 1:
            if ASTRAL:
                loc = Observer(latitude=lat[i], longitude=lon[i])
            else:
                s = a.sun_utc(pd.to_datetime(time[i]), lat[i], lon[i])
        elif ASTRAL is False:
            s = a.sun_utc(pd.to_datetime(time[i]), float(lat), float(lon))

        # Get sunrise and sunset
        if ASTRAL:
            sr = sunrise(loc, pd.to_datetime(time[i]))
            ss = sunset(loc, pd.to_datetime(time[i]))
        else:
            sr = s['sunrise']
            ss = s['sunset']

        # Set longname
        longname = 'Daylight indicator; 0-Night; 1-Sun'

        # Check to see if dawn/dusk calculations can be performed before preceeding
        if dawn_dusk:
            try:
                if ASTRAL:
                    dwn = dawn(loc, pd.to_datetime(time[i]))
                    dsk = dusk(loc, pd.to_datetime(time[i]))
                else:
                    if lat.size > 1:
                        dsk = a.dusk_utc(pd.to_datetime(time[i]), lat[i],
                                         lon[i])
                        dwn = a.dawn_utc(pd.to_datetime(time[i]), lat[i],
                                         lon[i])
                    else:
                        dsk = a.dusk_utc(pd.to_datetime(time[i]), float(lat),
                                         float(lon))
                        dwn = a.dawn_utc(pd.to_datetime(time[i]), float(lat),
                                         float(lon))
            except ValueError:
                print(
                    'Dawn/Dusk calculations are not available at this location'
                )
                dawn_dusk = False

        if dawn_dusk and ASTRAL:
            # If dawn_dusk is True, add 2 more indicators
            longname += '; 2-Dawn; 3-Dusk'
            # Need to ensure if the sunset if off a day to grab the previous
            # days value to catch the early UTC times
            if ss.day > sr.day:
                if ASTRAL:
                    ss = sunset(
                        loc, pd.to_datetime(time[i] - np.timedelta64(1, 'D')))
                    dsk = dusk(
                        loc, pd.to_datetime(time[i] - np.timedelta64(1, 'D')))
                else:
                    if lat.size > 1:
                        dsk = a.dusk_utc(
                            pd.to_datetime(time[i]) - np.timedelta64(1, 'D'),
                            lat[i], lon[i])
                        s = a.sun_utc(
                            pd.to_datetime(time[i]) - np.timedelta64(1, 'D'),
                            lat[i], lon[i])
                    else:
                        dsk = a.dusk_utc(
                            pd.to_datetime(time[i]) - np.timedelta64(1, 'D'),
                            float(lat), float(lon))
                        s = a.sun_utc(
                            pd.to_datetime(time[i]) - np.timedelta64(1, 'D'),
                            float(lat), float(lon))
                    ss = s['sunset']

                if dwn <= pd.to_datetime(time[i], utc=True) < sr:
                    results.append(2)
                elif ss <= pd.to_datetime(time[i], utc=True) < dsk:
                    results.append(3)
                elif not (dsk <= pd.to_datetime(time[i], utc=True) < dwn):
                    results.append(1)
                else:
                    results.append(0)
            else:
                if dwn <= pd.to_datetime(time[i], utc=True) < sr:
                    results.append(2)
                elif sr <= pd.to_datetime(time[i], utc=True) < ss:
                    results.append(1)
                elif ss <= pd.to_datetime(time[i], utc=True) < dsk:
                    results.append(3)
                else:
                    results.append(0)
        else:
            if ss.day > sr.day:
                if ASTRAL:
                    ss = sunset(
                        loc, pd.to_datetime(time[i] - np.timedelta64(1, 'D')))
                else:
                    s = a.sun_utc(
                        pd.to_datetime(time[i]) - np.timedelta64(1, 'D'), lat,
                        lon)
                    ss = s['sunset']
                results.append(
                    int(not (ss < pd.to_datetime(time[i], utc=True) < sr)))
            else:
                results.append(
                    int(sr < pd.to_datetime(time[i], utc=True) < ss))

    # Add results to object and return
    obj['sun_variable'] = ('time', np.array(results), {
        'long_name': longname,
        'units': ' '
    })

    return obj
Exemple #29
0
options.add_argument("-t", "--tzname", help="Timezone name")
options.add_argument("latitude", type=float, help="Location latitude (float)")
options.add_argument("longitude",
                     type=float,
                     help="Location longitude (float)")
options.add_argument("elevation",
                     nargs="?",
                     type=float,
                     default=0.0,
                     help="Elevation in metres (float)")
args = options.parse_args()

loc = LocationInfo(args.name, args.region, args.tzname, args.latitude,
                   args.longitude)

obs = Observer(args.latitude, args.longitude, args.elevation)

kwargs: Dict[str, Any] = {}
kwargs["observer"] = obs

if args.date is not None:
    try:
        kwargs["date"] = datetime.datetime.strptime(args.date,
                                                    "%Y-%m-%d").date()
    except:  # noqa: E0722
        kwargs["date"] = datetime.date.today()

sun_as_str = {}
format_str = "%Y-%m-%dT%H:%M:%S"
if args.tzname is None:
    tzinfo = pytz.utc
Exemple #30
0
def test_Azimuth_Above85Degrees():
    d = datetime.datetime(2001, 6, 21, 13, 11, 0)
    assert sun.azimuth(Observer(86, 77.2), d) == pytest.approx(276.2148,
                                                               abs=0.001)