Source code for tracktable.render.map_decoration.clock

#
# Copyright (c) 2014-2021 National Technology and Engineering
# Solutions of Sandia, LLC. Under the terms of Contract DE-NA0003525
# with National Technology and Engineering Solutions of Sandia, LLC,
# the U.S. Government retains certain rights in this software.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
# COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

"""Draw a digital clock on a Matplotlib figure"""


from __future__ import print_function, division, absolute_import

import math

from matplotlib import pyplot, collections, lines, patches, text
from six.moves import range

[docs]def draw_analog_clock_on_map(time, offset=None, # should be a datetime.timedelta center=(0.5,0.5), # in image coordinates, not axis coordinates! radius=0.05, # again, in image coordinates axes=None, use_short_side_for_radius=True, long_hand_radius=0.9, short_hand_radius=0.5, tickmarks=True, tickmark_length=0.25, # in fractions of the radius color='white', linewidth=1, zorder=4, label=None, label_placement='top', label_kwargs=dict()): """Draw an analog clock on a Matplotlib figure. Note: THIS INTERFACE IS SUBJECT TO CHANGE. LOTS OF CHANGE. Args: time (datetime.datetime): Time to display Keyword Arguments: offset (datetime.timedelta): Offset for local time zone. This will be added to the 'time' argument to determine the time that will actually be displayed. (Default: None) center (2-tuple in [0,1] * [0, 1]): image-space center of clock (Default: (0.5,0.5)) radius (float in [0, 1]): Radius of clock face (Default: 0.05) axes (matplotlib.axes.Axes): axes to render into (Default: None) use_short_side_for_radius (boolean): Clock radius will be calculated relative to short side of figure (Default: True) long_hand_radius (float in [0, 1]): Radius of minute hand relative to radius of clock face (Default: 0.9) short_hand_radius (float in [0,1]): Radius of hour hand relative to radius of clock face (Default: 0.5) tickmarks (boolean): Whether or not to place tickmarks at every hour around the clock face (Default: True) tickmark_length (float in [0,1]): Length of tickmarks relative to clock face radius (Default: 0.25) color (colorspec): Color for clock face and hands (Default: 'white') linewidth (float): Thickness (in points) of lines drawn (Default: 1) zorder (integer): Ordering of clock in image element stack (higher values are on top) (Default: 4) label (string): Label text to display near clock (Default: None) label_placement (string): One of 'top', 'bottom'. Determines where label will be rendered relative to clock face (Default: 'top') label_kwargs (dict): Arguments to be passed to Matplotlib text renderer for label (Default: dict()) Side Effects: The clock actors will be added to the 'axes' argument or (if no axes are supplied) to the current Matplotlib axes. Returns: A list of Matplotlib artists added to the figure. """ if axes is None: axes = pyplot.gca() transform = axes.transData # Find the appropriate side of the image to measure the clock # against axis_x_bounds = axes.get_xbound() axis_y_bounds = axes.get_ybound() axis_x_span = axis_x_bounds[1] - axis_x_bounds[0] axis_y_span = axis_y_bounds[1] - axis_y_bounds[0] if use_short_side_for_radius: axis_span = min(axis_x_span, axis_y_span) else: axis_span = max(axis_x_span, axis_y_span) actors_to_return = [] clock_radius = axis_span * radius time_as_datetime = time if offset: time_as_datetime += offset hour = time_as_datetime.hour minute = time_as_datetime.minute second = time_as_datetime.second second_of_day = second + (minute*60) + (hour*3600) fraction_of_day = float(second_of_day) / 86400.0 second_of_hour = second + (minute * 60) fraction_of_hour = float(second_of_hour) / 3600 hour_hand_radius = short_hand_radius * clock_radius minute_hand_radius = long_hand_radius * clock_radius # In a polar coordinate system where 0 is along the positive # x-axis, our clock hands rotate in a negative direction starting # at pi/2. minute_hand_angle = (0.5 * math.pi) - (2 * math.pi * fraction_of_hour) # Since we want the hour hand to go twice around in a day instead # of just once we multiply by 4pi hour_hand_angle = (0.5 * math.pi) - (4 * math.pi * fraction_of_day) minute_hand_endpoint = ( minute_hand_radius * math.cos(minute_hand_angle), minute_hand_radius * math.sin(minute_hand_angle) ) hour_hand_endpoint = ( hour_hand_radius * math.cos(hour_hand_angle), hour_hand_radius * math.sin(hour_hand_angle) ) center_in_axis_space = ( center[0] * axis_x_span + axis_x_bounds[0], center[1] * axis_y_span + axis_y_bounds[0] ) if label: my_label_kwargs = { 'horizontalalignment': 'center', 'verticalalignment': 'bottom', 'rotation': 'horizontal', 'size': 12 } if label_placement == 'top': label_anchor = ( center_in_axis_space[0], center_in_axis_space[1] + 1.25 * clock_radius ) my_label_kwargs['verticalalignment'] = 'bottom' elif label_placement == 'bottom': label_anchor = ( center_in_axis_space[0], center_in_axis_space[1] - 1.25 * clock_radius ) my_label_kwargs['verticalalignment'] = 'top' elif label_placement == 'right': label_anchor = ( center_in_axis_space[0] + 1.25 * clock_radius, center_in_axis_space[1] ) my_label_kwargs['horizontalalignment'] = 'left' my_label_kwargs['verticalalignment'] = 'center', my_label_kwargs['rotation'] = 'vertical' elif label_placement == 'left': label_anchor = ( center_in_axis_space[0] - 1.25 * clock_radius, center_in_axis_space[1] ) my_label_kwargs['horizontalalignment'] = 'right' my_label_kwargs['verticalalignment'] = 'center' my_label_kwargs['rotation'] = 'vertical' my_label_kwargs.update(label_kwargs) label_actor = text.Text(x=label_anchor[0], y=label_anchor[1], text=label, **my_label_kwargs) actors_to_return.append(label_actor) axes.add_artist(label_actor) minute_hand_endpoint = ( center_in_axis_space[0] + minute_hand_endpoint[0], center_in_axis_space[1] + minute_hand_endpoint[1] ) hour_hand_endpoint = ( center_in_axis_space[0] + hour_hand_endpoint[0], center_in_axis_space[1] + hour_hand_endpoint[1] ) minute_hand_coords = { 'x': [ center_in_axis_space[0], minute_hand_endpoint[0] ], 'y': [ center_in_axis_space[1], minute_hand_endpoint[1] ] } hour_hand_coords = { 'x': [ center_in_axis_space[0], hour_hand_endpoint[0] ], 'y': [ center_in_axis_space[1], hour_hand_endpoint[1] ] } tickmark_segments = [] for hour in range(12): hour_angle = - (hour / 12.0) * 2 * math.pi center_point = ( center_in_axis_space[0], center_in_axis_space[1] ) vector_to_outside = ( clock_radius * math.cos(hour_angle), clock_radius * math.sin(hour_angle) ) vector_to_inside = ( (1-tickmark_length) * clock_radius * math.cos(hour_angle), (1-tickmark_length) * clock_radius * math.sin(hour_angle) ) new_segment = ( ( center_point[0] + vector_to_inside[0], center_point[1] + vector_to_inside[1] ), ( center_point[0] + vector_to_outside[0], center_point[1] + vector_to_outside[1] ) ) tickmark_segments.append(new_segment) hour_tickmarks = collections.LineCollection(tickmark_segments, linewidths=0.5 * linewidth, colors=color, zorder=zorder, transform=transform) minute_hand = lines.Line2D(minute_hand_coords['x'], minute_hand_coords['y'], linewidth=linewidth, color=color, zorder=zorder, transform=transform) hour_hand = lines.Line2D(hour_hand_coords['x'], hour_hand_coords['y'], linewidth=linewidth, color=color, zorder=zorder, transform=transform) boundary = patches.Circle( center_in_axis_space, clock_radius, zorder=zorder, color=color, linewidth=linewidth, fill=fill_circle, transform=transform ) axes.add_line(minute_hand) axes.add_line(hour_hand) axes.add_artist(hour_tickmarks) axes.add_artist(boundary) return [ boundary, minute_hand, hour_hand, hour_tickmarks ]
# ----------------------------------------------------------------------
[docs]def digital_clock(time, formatter, position, **kwargs): """Add a digital clock (a string representation of time) to the image at a user-specified location Note: This function may go away since it doesn't provide any useful enhancement over just adding a text actor ourselves. Args: time (datetime.datetime): Current timestamp to display formatter (str): Timestamp to use as formatter position (tuple): Location to display the clock kwargs (dict): Arguments to be passed to Matplotlib text renderer for label Returns: A list of Matplotlib figure text added to the figure. """ time_string = formatter(time) return [ pyplot.figtext(position[0], position[1], time_string, **kwargs) ]