# AUTOGENERATED! DO NOT EDIT! File to edit: 00_map.ipynb (unless otherwise specified).

__all__ = ['UHIOptions', 'MapOptions', 'Transect', 'Map']

# Cell
import cartopy.crs as ccrs
import cartopy.io.img_tiles as cimgt

from mpl_toolkits.axes_grid1.inset_locator import inset_axes
from mpl_toolkits.axes_grid1 import make_axes_locatable

from matplotlib.patches import PathPatch

from matplotlib.ticker import MaxNLocator
import matplotlib.tri as tri
import matplotlib.pyplot as plt
from matplotlib import image
import matplotlib.lines as mlines
from matplotlib.path import Path as PathPlt

from matplotlib_scalebar.scalebar import ScaleBar

import numpy as np
from scipy.spatial import Delaunay
import json
import pandas as pd

# Cell
class UHIOptions:
  def __init__(self, hasBorders=None,vMin=None,vMax=None,transparency=.7,showPoints=False,contour="contourLine",drawMinMaxT=False,LON_LABEL="Longitud",LAT_LABEL="Latitud",Z_LABEL="°C",Z_UNIT="°C"):
    self.hasBorders=hasBorders
    self.vMin=vMin
    self.vMax=vMax
    self.transparency=transparency
    self.showPoints=showPoints
    self.contour=contour
    self.drawMinMaxT=drawMinMaxT
    self.LON_LABEL=LON_LABEL
    self.LAT_LABEL=LAT_LABEL
    self.Z_LABEL=Z_LABEL
    self.Z_UNIT=Z_UNIT

class MapOptions:
  def __init__(self,show=False,file='HeatMap.png',hasImage=True, hasScaleBar=True, hasCompassRose=True,hasLegend=True, hasColorbar=True):
    self.show=show
    self.file=file
    self.hasImage=hasImage
    self.hasScaleBar=hasScaleBar
    self.hasCompassRose=hasCompassRose
    self.hasLegend=hasLegend
    self.hasColorbar=hasColorbar

class Transect:
    def __init__(self, path, x,y,color,name):
      self.path = path
      self.x = x
      self.y = y
      self.color = color
      self.name = name

# Cell
class Map:
    """Represents a map of urban heat island

  Attributes:
    df: numpy's dataframe.
    transects: array of strings.
    legend_stations_label: string.
    otherDf: numpy's dataframe.
    mapbox_username: mapbox's username to plot background map.
    mapbox_token: mapbox's token to plot background map.
    mapbox_map_id: mapbox's map id to plot background map.
  """
    def __init__(self, df,transects=[],legend_stations_label="Estaciones",otherDf=None,mapbox_username=None,mapbox_token=None,mapbox_map_id="ck2hwr0522eal1cm297dkqzis"):
      self.df = df
      self.transects = transects
      self.legend_stations_label = legend_stations_label
      self.otherDf = otherDf
      self.mapbox_username = mapbox_username
      self.mapbox_token = mapbox_token
      self.mapbox_map_id = mapbox_map_id

    def plot(self,extent,uhiOptions=UHIOptions(),mapOptions=MapOptions()):

      request = cimgt.MapboxStyleTiles(username=self.mapbox_username,map_id=self.mapbox_map_id,access_token=self.mapbox_token)
      projection=request.crs
      fig = plt.figure(figsize=(13,10))
      ax = plt.axes(projection=projection)

      fontSize=23
      ax.set_extent(extent,crs=ccrs.PlateCarree())

      gl=ax.gridlines(draw_labels=True, alpha=0.2)
      gl.top_labels = gl.right_labels = False
      gl.xlabel_style = {'size': fontSize, 'color': 'black'}
      gl.ylabel_style = {'size': fontSize, 'color': 'black'}
      locator=MaxNLocator(nbins=7)
      gl.xlocator=locator

      tricf=self.ic(ax,uhiOptions)

      if mapOptions.hasCompassRose:
        axins2 = inset_axes(ax, width="20%", height="20%", loc="lower right")
        axins2.imshow(image.imread("https://upload.wikimedia.org/wikipedia/commons/thumb/b/b8/Simple_compass_rose-fr.svg/600px-Simple_compass_rose-fr.svg.png"))
        self.blank_axes(axins2)

      if mapOptions.hasScaleBar:
        scalebar = ScaleBar(dx=1) # 1 pixel = 0.2 meter
        ax.add_artist(scalebar)

      if mapOptions.hasImage:
        ax.add_image(request,14)

      for transect in self.transects:
        xynps = ax.projection.transform_points(ccrs.Geodetic(), transect.x, transect.y)
        x=xynps[:,0]
        y=xynps[:,1]
        ax.plot(x,y, color=transect.color,marker="o",linestyle='-', markersize=5)

      if mapOptions.hasLegend:
        axins3 = inset_axes(ax, width="20%", height="40%", loc="upper left")
        self.legend(axins3)

      if mapOptions.hasColorbar:
        divider = make_axes_locatable(ax)
        ax_cb = divider.new_horizontal(size="5%", pad=0.1, axes_class=plt.Axes)
        fig.add_axes(ax_cb)
        cbar=fig.colorbar(tricf, cax=ax_cb)
        ticklabs = cbar.ax.get_yticklabels()
        cbar.ax.set_yticklabels(ticklabs, fontsize=fontSize)
        ax_cb.set_title(uhiOptions.Z_UNIT, fontsize=fontSize)
      if not mapOptions.show:
        plt.close()
      fig.savefig(mapOptions.file,dpi=220,bbox_inches='tight', pad_inches=0)
      plt.close(fig)
      del(tricf)
      del(self.df)

    def blank_axes(self,ax):
      ax.spines['right'].set_visible(False)
      ax.spines['top'].set_visible(False)
      ax.spines['bottom'].set_visible(False)
      ax.spines['left'].set_visible(False)
      ax.yaxis.set_ticks_position('none')
      ax.xaxis.set_ticks_position('none')
      ax.tick_params(labelbottom='off', labeltop='off', labelleft='off', labelright='off' ,
                      bottom='off', top='off', left='off', right='off' )
      ax.set_axis_off()

    def legend(self,ax5):
      self.blank_axes(ax5)

      # handles is a list of patch handles
      handles = []
      # names is the list of corresponding labels to appear in the legend
      names = []

      if self.otherDf is not None:
        line1 = mlines.Line2D(range(1), range(1), color="white", marker='*',markerfacecolor="black",markersize=17)
        handles.append(line1)
        names.append(self.otherDf.name)

      line2 = mlines.Line2D(range(1), range(1), color="white", marker='o',markerfacecolor="black")
      handles.append(line2)
      names.append(self.legend_stations_label)

      for transect in self.transects:
        tr1 = mlines.Line2D([], [], color=transect.color, ls="-", linewidth=1.5,marker='o')
        handles.append(tr1)
        names.append(transect.name)

      ax5.legend(handles, names)

    def ic(self,ax,options):
      if self.otherDf is not None and self.otherDf.df is not None:
        xMaq=self.otherDf.df[optionsLON_LABEL].values
        yMaq=self.otherDf.df[options.LAT_LABEL].values
        xynpsMaq = ax.projection.transform_points(ccrs.Geodetic(), xMaq, yMaq)
        xMaq=xynpsMaq[:,0]
        yMaq=xynpsMaq[:,1]
      x=self.df[options.LON_LABEL].values
      y=self.df[options.LAT_LABEL].values
      xynps = ax.projection.transform_points(ccrs.Geodetic(), x, y)
      x=xynps[:,0]
      y=xynps[:,1]
      dely = Delaunay(np.column_stack((x,y)))
      triang = tri.Triangulation(x=x, y=y, triangles=dely.vertices)
      if options.showPoints:
        ax.triplot(triang, marker="o",linewidth=0,color='k',markersize=4)
      if self.otherDf is not None and self.otherDf.df is not None:
          ax.plot(xMaq,yMaq, color='k',marker="*",  markersize=10)
      if options.drawMinMaxT:
        max_=self.df[self.df[options.Z_LABEL]==self.df[options.Z_LABEL].max()]
        min_=self.df[self.df[options.Z_LABEL]==self.df[options.Z_LABEL].min()]
        self.annotate(max_,ax,options.LON_LABEL,options.LAT_LABEL,options.Z_LABEL,options.Z_UNIT)
        self.annotate(min_,ax,options.LON_LABEL,options.LAT_LABEL,options.Z_LABEL,options.Z_UNIT)
      if self.otherDf is not None and self.otherDf.df is not None:
          self.annotate(self.otherDf.df,ax,options.LON_LABEL,options.LAT_LABEL,options.Z_LABEL,options.Z_UNIT)
      if options.contour=="contourLine":
        temperature=self.df[options.Z_LABEL].values
        levels=50
        if options.vMin!=None and options.vMax!=None:
          levels=np.linspace(options.vMin,options.vMax,50)
        tricf=ax.tricontourf(triang,temperature, levels=levels, cmap="jet",alpha=options.transparency)
        if self.otherDf is not None and self.otherDf.df is not None:
          ax.plot(xMaq,yMaq, color='k',marker="*",  markersize=10)
        if options.hasBorders!=None:
          self.border(ax,tricf,options.hasBorders)
        return tricf
      elif options.contour=="triangulation":
        return plt.triplot(triang, marker=None)
      return None

    def annotate(self,df_to_annotate,ax,LON_LABEL,LAT_LABEL,Z_LABEL,Z_UNIT):
        df_t=df_to_annotate[Z_LABEL].values
        df_x=df_to_annotate[LON_LABEL].values
        df_y=df_to_annotate[LAT_LABEL].values
        for i, txt in enumerate(df_t):
          xynps_ = ax.projection.transform_points(ccrs.Geodetic(), df_x,df_y)
          df_x=xynps_[:,0]
          df_y=xynps_[:,1]
          ax.annotate("   {} {}".format(txt,Z_UNIT), (df_x[i], df_y[i]),fontsize='medium',weight='bold')

    def border(self,ax,tricf,path):
      data = json.load(open(path))
      df = pd.DataFrame(data["features"])
      df=df["geometry"].values
      polygon=[]
      for i in df:
        polygon += i["coordinates"][0]
      polygon=np.array(polygon)
      x=polygon[:,0]
      y=polygon[:,1]
      xynps = ax.projection.transform_points(ccrs.Geodetic(), x, y)
      x=xynps[:,0]
      y=xynps[:,1]
      polygon=np.c_[x, y]
      clippath = PathPlt(polygon)
      patch = PathPatch(clippath, facecolor='none',linewidth=0)
      ax.add_patch(patch)
      for c in tricf.collections:
        c.set_clip_path(patch)