text: # -*- coding: utf-8 -*-
  
  # Copyright 2017 Juan José Martín Miralles
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  # http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  
  # Python modules
  import math
  import warnings
  import datetime
  import datetime as dt
  from datetime import datetime
  
  # Third party modules
  import geopy
  import geopy.distance as geo_dist
  import pandas as pd
  
  TIME_FORMATS = ['%H:%M', '%H%M', '%I:%M%p', '%I%M%p', '%H:%M:%S', '%H%M%S', '%I:%M:%S%p', '%I%M%S%p']
  
  
  class TrackException(Exception):
      """
      Generic exception for TrackAnimation
      """
  
      def __init__(self, msg, original_exception):
          super(TrackException, self).__init__(msg + (": %s" % original_exception))
          self.original_exception = original_exception
  
  
  def getBearing(start_point, end_point):
      """
      Calculates the bearing between two points.
  
      Parameters
      ----------
      start_point: geopy.Point
      end_point: geopy.Point
  
      Returns
      -------
      point: int
          Bearing in degrees between the start and end points.
      """
      warnings.warn("The getBearing function is deprecated and "
                    "will be removed in version 2.0.0. "
                    "Use the get_bearing function instead.",
                    FutureWarning,
                    stacklevel=8
                    )
      return get_bearing(start_point, end_point)
  
  
  def get_bearing(start_point, end_point):
      """
      Calculates the bearing between two points.
  
      Parameters
      ----------
      start_point: geopy.Point
      end_point: geopy.Point
  
      Returns
      -------
      point: int
          Bearing in degrees between the start and end points.
      """
      start_lat = math.radians(start_point.latitude)
      start_lng = math.radians(start_point.longitude)
      end_lat = math.radians(end_point.latitude)
      end_lng = math.radians(end_point.longitude)
  
      d_lng = end_lng - start_lng
      if abs(d_lng) > math.pi:
          if d_lng > 0.0:
              d_lng = -(2.0 * math.pi - d_lng)
          else:
              d_lng = (2.0 * math.pi + d_lng)
  
      tan_start = math.tan(start_lat / 2.0 + math.pi / 4.0)
      tan_end = math.tan(end_lat / 2.0 + math.pi / 4.0)
      d_phi = math.log(tan_end / tan_start)
      bearing = (math.degrees(math.atan2(d_lng, d_phi)) + 360.0) % 360.0
  
      return bearing
  
  
  def getCoordinates(start_point, end_point, distance_meters):
      """
      Calculates the new coordinates between two points depending
      of the specified distance and the calculated bearing.
  
      Parameters
      ----------
      start_point: geopy.Point
      end_point: geopy.Point
      distance_meters: float
  
      Returns
      -------
      point: geopy.Point
          A new point between the start and the end points.
      """
      warnings.warn("The getCoordinates function is deprecated and "
                    "will be removed in version 2.0.0. "
                    "Use the get_coordinates function instead.",
                    FutureWarning,
                    stacklevel=8
                    )
      return get_coordinates(start_point, end_point, distance_meters)
  
  
  def get_coordinates(start_point, end_point, distance_meters):
      """
      Calculates the new coordinates between two points depending
      of the specified distance and the calculated bearing.
  
      Parameters
      ----------
      start_point: geopy.Point
      end_point: geopy.Point
      distance_meters: float
  
      Returns
      -------
      point: geopy.Point
          A new point between the start and the end points.
      """
      bearing = get_bearing(start_point, end_point)
  
      distance_km = distance_meters / 1000
      d = geo_dist.distance(kilometers=distance_km)
      destination = d.destination(point=start_point, bearing=bearing)
  
      return geopy.Point(destination.latitude, destination.longitude)
  
  
  def getPointInTheMiddle(start_point, end_point, time_diff, point_idx):
      """
      Calculates a new point between two points depending of the
      time difference between them and the point index.
  
      Parameters
      ----------
      start_point: DataFrame
      end_point: DataFrame
      time_diff: float
      point_idx: int
          Point index between the start and the end points
  
      Returns
      -------
      point: list
          A new point between the start and the end points.
      """
      warnings.warn("The getPointInTheMiddle function is deprecated and "
                    "will be removed in version 2.0.0. "
                    "Use the get_point_in_the_middle function instead.",
                    FutureWarning,
                    stacklevel=8
                    )
      return get_point_in_the_middle(start_point, end_point, time_diff, point_idx)
  
  
  def get_point_in_the_middle(start_point, end_point, time_diff, point_idx):
      """
      Calculates a new point between two points depending of the
      time difference between them and the point index.
  
      Parameters
      ----------
      start_point: DataFrame
      end_point: DataFrame
      time_diff: float
      point_idx: int
          Point index between the start and the end points
  
      Returns
      -------
      point: list
          A new point between the start and the end points.
      """
      time_proportion = (time_diff * point_idx) / end_point['TimeDifference'].item()
  
      distance_proportion = end_point['Distance'].item() * time_proportion
      time_diff_proportion = end_point['TimeDifference'].item() * time_proportion
      speed = distance_proportion / time_diff_proportion
      distance = time_diff * speed
      cum_time_diff = int(start_point['CumTimeDiff'].item() + time_diff_proportion)
      # date = datetime.strptime(start_point['Date'].item(), '%Y-%m-%d %H:%M:%S') + dt.timedelta(seconds=int(
      # time_diff_proportion))
      date = pd.to_datetime(start_point['Date'].astype(str), format='%Y-%m-%d %H:%M:%S') + dt.timedelta(
          seconds=int(time_diff_proportion))
      altitude = (end_point['Altitude'].item() + start_point['Altitude'].item()) / 2
      name = start_point['CodeRoute'].item()
  
      geo_start = geopy.Point(start_point['Latitude'].item(), start_point['Longitude'].item())
      geo_end = geopy.Point(end_point['Latitude'].item(), end_point['Longitude'].item())
      middle_point = get_coordinates(geo_start, geo_end, distance_proportion)
  
      df_middle_point = ([[name, middle_point.latitude, middle_point.longitude, altitude,
                           date, speed, int(time_diff), distance, None, cum_time_diff]])
  
      return df_middle_point
  
  
  def rgb(value, minimum, maximum):
      """
      Calculates an rgb color of a value depending of
      the minimum and maximum values.
  
      Parameters
      ----------
      value: float or int
      minimum: float or int
      maximum: float or int
  
      Returns
      -------
      rgb: tuple
      """
      value = float(value)
      minimum = float(minimum)
      maximum = float(maximum)
  
      if minimum == maximum:
          ratio = 0
      else:
          ratio = 2 * (value - minimum) / (maximum - minimum)
  
      b = int(max(0, 255 * (1 - ratio)))
      r = int(max(0, 255 * (ratio - 1)))
      g = 255 - b - r
  
      return r / 255.0, g / 255.0, b / 255.0
  
  
  def calculateCumTimeDiff(df):
      """
      Calculates the cumulative of the time difference
      between points for each track of 'dfTrack'.
      """
      warnings.warn("The calculateCumTimeDiff function is deprecated and "
                    "will be removed in version 2.0.0. "
                    "Use the calculate_cum_time_diff function instead.",
                    FutureWarning,
                    stacklevel=8
                    )
      return calculate_cum_time_diff(df)
  
  
  def calculate_cum_time_diff(df):
      """
      Calculates the cumulative of the time difference
      between points for each track of 'dfTrack'.
      """
      df = df.copy()
  
      df_cum = pd.DataFrame()
      grouped = df['CodeRoute'].unique()
  
      for name in grouped:
          df_slice = df[df['CodeRoute'] == name]
          df_slice = df_slice.reset_index(drop=True)
          df_slice['CumTimeDiff'] = df_slice['TimeDifference'].cumsum()
          df_cum = pd.concat([df_cum, df_slice])
  
      df_cum = df_cum.reset_index(drop=True)
  
      return df_cum
  
  
  def isTimeFormat(time):
      """
      Check if 'time' variable has the format of one
      of the 'time_formats'
      """
      warnings.warn("The isTimeFormat function is deprecated and "
                    "will be removed in version 2.0.0. "
                    "Use the is_time_format function instead.",
                    FutureWarning,
                    stacklevel=8
                    )
      return is_time_format(time)
  
  
  def is_time_format(time):
      """
      Check if 'time' variable has the format of one
      of the 'time_formats'
      """
      if time is None:
          return False
  
      for time_format in TIME_FORMATS:
          try:
              datetime.strptime(time, time_format)
              return True
          except ValueError:
              pass
  
      return False
  