{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "import requests\n",
    "import io\n",
    "from io import BytesIO\n",
    "from PIL import ImageDraw, ImageFont\n",
    "from PIL import Image, ImageDraw, ImageFont\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class MyDraw():\n",
    "    def __init__(self, height, width):\n",
    "        self.height = height\n",
    "        self.width = width\n",
    "        pass\n",
    "\n",
    "    def save_image(self, filename):\n",
    "        self.image.save(filename)\n",
    "        pass\n",
    "\n",
    "    def start_draw(self, background_color=None):\n",
    "        if background_color is None:\n",
    "            background_color = self.theme_params['background_color']\n",
    "\n",
    "        self.image = Image.new(\"RGB\", (self.width, self.height), background_color)\n",
    "        self.draw = ImageDraw.Draw(self.image)\n",
    "        pass\n",
    "\n",
    "    def add_image(self, obj, position):\n",
    "        image_to_add = obj.get_image()  # Obtiene la imagen del objeto pasado como argumento\n",
    "        if image_to_add:\n",
    "            self.image.paste(image_to_add, position)\n",
    "        pass\n",
    "\n",
    "    def get_draw(self):\n",
    "        return self.draw\n",
    "    \n",
    "    def get_image(self):\n",
    "        return self.image\n",
    "\n",
    "    def set_params(self,params):\n",
    "        self.prms = params\n",
    "        pass\n",
    "\n",
    "    def set_theme(self,mode='day'):\n",
    "        if(mode=='day'):\n",
    "            self.start_day_mode()\n",
    "        else:\n",
    "            self.start_night_mode()\n",
    "        pass\n",
    "\n",
    "    def start_day_mode(self):\n",
    "        self.theme_params = {\n",
    "            'background_color': 'white',\n",
    "            'text_color': 'black',\n",
    "            'poster_line_color': 'black',\n",
    "        }\n",
    "        pass\n",
    "\n",
    "    def start_night_mode(self):\n",
    "        self.theme_params = {\n",
    "            'background_color': 'black',\n",
    "            'text_color': 'white',\n",
    "            'poster_line_color': 'gray',\n",
    "        }\n",
    "        pass\n",
    "    \n",
    "    def load_barlow(self, font_size=None):\n",
    "        # Ruta a la fuente TTF personalizada\n",
    "        font_path = \"/app/data/Barlow-Medium.ttf\"\n",
    "        # Carga la fuente\n",
    "        if font_size is None:\n",
    "            self.font = ImageFont.truetype(font_path, self.prms['font_size'])\n",
    "        else:\n",
    "            self.font = ImageFont.truetype(font_path, font_size)\n",
    "        pass\n",
    "\n",
    "    def preview(self):\n",
    "        plt.imshow(self.image)\n",
    "        plt.axis('off')\n",
    "        plt.show()\n",
    "\n",
    "    def crop_image(self, top_cut, bottom_cut):\n",
    "        width, height = self.image.size\n",
    "        self.image = self.image.crop((0, top_cut, width, height - bottom_cut))\n",
    "        pass\n",
    "\n",
    "    def resize_image(self, target_width=None, target_height=None):\n",
    "        \"\"\"\n",
    "        Ajusta el tamaño de la imagen mientras mantiene las proporciones.\n",
    "        \n",
    "        Args:\n",
    "            image (PIL.Image.Image): La imagen a redimensionar.\n",
    "            target_width (int, opcional): El ancho objetivo deseado.\n",
    "            target_height (int, opcional): La altura objetivo deseada.\n",
    "            \n",
    "        Returns:\n",
    "            PIL.Image.Image: La imagen redimensionada.\n",
    "        \"\"\"\n",
    "        width, height = self.image.size\n",
    "        aspect_ratio = width / height\n",
    "        \n",
    "        if target_width is None and target_height is None:\n",
    "            raise ValueError(\"Debes proporcionar al menos una de las dimensiones objetivo.\")\n",
    "        \n",
    "        if target_width is not None and target_height is None:\n",
    "            new_width = target_width\n",
    "            new_height = int(target_width / aspect_ratio)\n",
    "        elif target_width is None and target_height is not None:\n",
    "            new_width = int(target_height * aspect_ratio)\n",
    "            new_height = target_height\n",
    "        else:\n",
    "            new_width = target_width\n",
    "            new_height = target_height\n",
    "        \n",
    "        self.image = self.image.resize((new_width, new_height))\n",
    "        pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class BusPoster(MyDraw):\n",
    "\n",
    "    def start_draw(self):\n",
    "        return super().start_draw()\n",
    "\n",
    "    def set_colors(self):\n",
    "        width_border = self.prms['width_border']\n",
    "        proportion = self.prms['proportion']\n",
    "        fill_color_l = self.prms['letter_background_color']\n",
    "        fill_color_n = self.prms['number_background_color']\n",
    "\n",
    "\n",
    "        self.draw.rounded_rectangle(\n",
    "            (0, 0, self.width-width_border, self.height-width_border),\n",
    "            fill=fill_color_l,\n",
    "            outline=self.theme_params['poster_line_color'],\n",
    "            width=width_border,\n",
    "            radius=5)\n",
    "        \n",
    "        self.draw.rounded_rectangle(\n",
    "            (0, 0, proportion*self.width-width_border, self.height-width_border),\n",
    "            fill=fill_color_n,\n",
    "            outline=self.theme_params['poster_line_color'],\n",
    "            width=width_border,\n",
    "            radius=5)\n",
    "        pass\n",
    "\n",
    "    def set_bus_number(self, bus_number=\"11\"):\n",
    "        text_color = 'black'\n",
    "        width_border = self.prms['width_border']\n",
    "        text_bbox = self.font.getbbox(str(bus_number))\n",
    "        font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]\n",
    "        offset_width = np.round((self.prms['proportion']*self.width-width_border)/2) - np.round(font_width/2)\n",
    "        text_position = (offset_width,0)\n",
    "        self.draw.text(\n",
    "            text_position,\n",
    "            bus_number,\n",
    "            fill=text_color,\n",
    "            font=self.font,\n",
    "            # align =\"center\"\n",
    "        )\n",
    "        pass\n",
    "\n",
    "    def set_bus_letter(self, bus_letter=\"E\"):\n",
    "        proportion = self.prms['proportion']\n",
    "        width_border = self.prms['width_border']\n",
    "        text_color = 'white'\n",
    "        text_bbox = self.font.getbbox(str(bus_letter))\n",
    "        font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]\n",
    "        offset_width = np.round((proportion*self.width-width_border)) + 0.75*np.round(font_width/2)\n",
    "        text_position = (offset_width,0)\n",
    "        self.draw.text(\n",
    "            text_position,\n",
    "            bus_letter,\n",
    "            fill=text_color,\n",
    "            font=self.font,\n",
    "            # align =\"center\"\n",
    "        )\n",
    "        pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class TimeAnnouncement(MyDraw):\n",
    "\n",
    "    def start_draw(self):\n",
    "        super().start_draw()\n",
    "        self.border = 1\n",
    "\n",
    "    def set_background(self):\n",
    "        self.draw.rounded_rectangle(\n",
    "            (0, 0, self.width-0.5*self.border, self.height-0.5*self.border),\n",
    "            fill=\"#dcdcdc\",\n",
    "            outline=\"gray\",\n",
    "            width=self.border,\n",
    "            radius=1)\n",
    "        pass\n",
    "\n",
    "    def set_base_text(self):\n",
    "        text = \"Tiempo aprox\"\n",
    "        text_color = self.theme_params['text_color']\n",
    "        self.load_barlow(font_size=11)\n",
    "        text_bbox = self.font.getbbox(text)\n",
    "        font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]\n",
    "        # print(font_width, font_height)\n",
    "        offset_width = (np.round((self.width-self.border)) - np.round(font_width))/2\n",
    "        text_position = (offset_width,5)\n",
    "        # text_position = (0, 0)\n",
    "        self.draw.text(\n",
    "            text_position,\n",
    "            text,\n",
    "            fill=text_color,\n",
    "            font=self.font,\n",
    "            align =\"center\"\n",
    "        )\n",
    "        pass\n",
    "\n",
    "    def set_min_max_text(self, min_time, max_time):\n",
    "\n",
    "        text = \"Tiempo aprox\"\n",
    "        text_color = self.theme_params['text_color']\n",
    "        self.load_barlow(font_size=11)\n",
    "        text_bbox = self.font.getbbox(text)\n",
    "        base_font_width, base_font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]\n",
    "\n",
    "        if (int(max_time) <= 1):\n",
    "            text = f\"< 1 min\"\n",
    "        else:\n",
    "            text = f'{min_time} a {max_time} min'\n",
    "            \n",
    "        self.load_barlow(font_size=18)\n",
    "        text_bbox = self.font.getbbox(text)\n",
    "        font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]\n",
    "        # print(font_width, font_height)\n",
    "        offset_width = (np.round((self.width-self.border)) - np.round(font_width))/2\n",
    "        offset_height = (np.round((self.height-self.border)) - np.round(base_font_height))/2\n",
    "        text_position = (offset_width,5+offset_height)\n",
    "        # text_position = (0, 0)\n",
    "        self.draw.text(\n",
    "            text_position,\n",
    "            text,\n",
    "            fill=text_color,\n",
    "            font=self.font,\n",
    "            align =\"center\"\n",
    "        )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class DistanceAnnouncement(MyDraw):\n",
    "\n",
    "    def start_draw(self):\n",
    "        super().start_draw()\n",
    "        self.border = 1\n",
    "\n",
    "    def set_background(self):\n",
    "        self.draw.rounded_rectangle(\n",
    "            (0, 0, self.width-0.5*self.border, self.height-0.5*self.border),\n",
    "            fill=\"#dcdcdc\",\n",
    "            outline=\"gray\",\n",
    "            width=self.border,\n",
    "            radius=1)\n",
    "        pass\n",
    "\n",
    "    def set_base_text(self):\n",
    "        text = \"Distancia\"\n",
    "        text_color = self.theme_params['text_color']\n",
    "        self.load_barlow(font_size=11)\n",
    "        text_bbox = self.font.getbbox(text)\n",
    "        font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]\n",
    "        # print(font_width, font_height)\n",
    "        offset_width = (np.round((self.width-self.border)) - np.round(font_width))/2\n",
    "        text_position = (offset_width,5)\n",
    "        # text_position = (0, 0)\n",
    "        self.draw.text(\n",
    "            text_position,\n",
    "            text,\n",
    "            fill=text_color,\n",
    "            font=self.font,\n",
    "            align =\"center\"\n",
    "        )\n",
    "        pass\n",
    "\n",
    "    def set_distance_text(self, distance):\n",
    "\n",
    "        text = \"Distancia\"\n",
    "        text_color = self.theme_params['text_color']\n",
    "        self.load_barlow(font_size=11)\n",
    "        text_bbox = self.font.getbbox(text)\n",
    "        base_font_width, base_font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]\n",
    "\n",
    "\n",
    "        text = f'{distance} km'\n",
    "        self.load_barlow(font_size=18)\n",
    "        text_bbox = self.font.getbbox(text)\n",
    "        font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]\n",
    "        # print(font_width, font_height)\n",
    "        offset_width = (np.round((self.width-self.border)) - np.round(font_width))/2\n",
    "        offset_height = (np.round((self.height-self.border)) - np.round(base_font_height))/2\n",
    "        text_position = (offset_width,5+offset_height)\n",
    "        # text_position = (0, 0)\n",
    "        self.draw.text(\n",
    "            text_position,\n",
    "            text,\n",
    "            fill=text_color,\n",
    "            font=self.font,\n",
    "            align =\"center\"\n",
    "        )"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "\n",
    "class BusPlate():\n",
    "    def __init__(self, url=None) -> None:\n",
    "        if url is None:\n",
    "            self.url = \"https://matriculasdelmundo.com/gRCH1.php\"\n",
    "        pass\n",
    "\n",
    "    def set_url(self, url):\n",
    "        self.url = url\n",
    "        pass\n",
    "\n",
    "    def get_image(self):\n",
    "        if hasattr(self, 'image'):\n",
    "            return self.image\n",
    "        else:\n",
    "            print(\"Error: No se ha generado ninguna imagen aún.\")\n",
    "            return None\n",
    "\n",
    "    def request_bus_plate(self, bus_plate=None):\n",
    "        if bus_plate is None:\n",
    "            self.bus_plate = \"AABB11\"\n",
    "        else:\n",
    "            self.bus_plate = bus_plate\n",
    "\n",
    "        params = {\n",
    "            \"textRCH1\": self.bus_plate[0:2],\n",
    "            \"textRCH1A\": self.bus_plate[2:4],\n",
    "            \"textRCH1B\": self.bus_plate[4:],\n",
    "            \"textRCH1C\": \"\"\n",
    "        }\n",
    "\n",
    "        self.response = requests.get(self.url, params=params)\n",
    "        pass\n",
    "\n",
    "    def save_bus_plate_image(self):\n",
    "        if self.response.status_code == 200:\n",
    "            filename = f\"/app/data/output/plate_{self.bus_plate}.png\" \n",
    "            with open(filename, \"wb\") as f:\n",
    "                f.write(self.response.content)\n",
    "            print(f\"Imagen generada guardada como '{filename}'\")\n",
    "        else:\n",
    "            print(\"Error al guardar la imagen generada\")\n",
    "        pass\n",
    "\n",
    "    def generate_image(self):\n",
    "        image_bytes = io.BytesIO(self.response.content)\n",
    "        self.image = Image.open(image_bytes)\n",
    "        self.image = self.image.convert(\"RGBA\")  # Convertir a formato RGBA\n",
    "        pass\n",
    "\n",
    "    def resize_image(self, new_size):\n",
    "        proportion = np.min([self.image.size[0]/new_size[0], self.image.size[1]/new_size[1]])\n",
    "        nx, ny = int(np.round(image.size[0]/proportion)), int(np.round(image.size[1]/proportion))\n",
    "        self.image = self.image.resize((nx, ny))\n",
    "        pass\n",
    "\n",
    "    def preview(self):\n",
    "        plt.imshow(self.image)\n",
    "        plt.axis('off')\n",
    "        plt.show()\n",
    "        pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "class BusImage(MyDraw):\n",
    "    def __init__(self):\n",
    "        pass\n",
    "    \n",
    "    def load_image_from_url(self, url=None):\n",
    "        if(url is None):\n",
    "            # URL de la imagen en línea\n",
    "            url = \"https://img.freepik.com/iconos-gratis/autobus_318-574563.jpg\"\n",
    "\n",
    "        # Descarga la imagen desde la URL\n",
    "        response = requests.get(url)\n",
    "        image_data = response.content\n",
    "        # Crea un objeto Image desde los datos descargados\n",
    "        self.image = Image.open(BytesIO(image_data))\n",
    "        # Crear una imagen en blanco del mismo tamaño que loaded_image con fondo blanco\n",
    "        background = Image.new(\"RGB\", self.image.size, self.theme_params['background_color'])\n",
    "\n",
    "        # Pega la loaded_image en la imagen en blanco\n",
    "        background.paste(self.image, (0, 0), self.image)\n",
    "        \n",
    "        self.image = background\n",
    "\n",
    "        # Calcula la posición para agregar la imagen cargada\n",
    "        image_position = (0, 0)  # Cambia esto según tu diseño\n",
    "\n",
    "        # Agrega la imagen a la imagen creada\n",
    "        self.image.paste(background, image_position)\n",
    "        pass"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "theme = 'night'\n",
    "\n",
    "panel_height, panel_width = 40, 80\n",
    "n_panels = 3\n",
    "full_panel = MyDraw(n_panels*panel_height, n_panels*panel_width)\n",
    "full_panel.set_theme(theme)\n",
    "full_panel.start_draw()\n",
    "# full_panel.preview()\n",
    "\n",
    "bp = BusPlate()\n",
    "plate = \"WXYZ88\"\n",
    "bp.request_bus_plate(bus_plate=plate)\n",
    "bp.generate_image()\n",
    "bp.resize_image((40,80))\n",
    "\n",
    "dist_anmc = DistanceAnnouncement(50, 80)\n",
    "dist_anmc.set_theme(theme)\n",
    "dist_anmc.start_draw()\n",
    "# dist_anmc.set_background()\n",
    "dist_anmc.set_base_text()\n",
    "dist_anmc.set_distance_text(distance=5)\n",
    "\n",
    "time_anmc = TimeAnnouncement(50, 80)\n",
    "time_anmc.set_theme(theme)\n",
    "time_anmc.start_draw()\n",
    "# time_anmc.set_background()\n",
    "time_anmc.set_base_text()\n",
    "time_anmc.set_min_max_text(min_time=2, max_time=3)\n",
    "\n",
    "poster = BusPoster(30, 60)\n",
    "poster.set_theme(theme)\n",
    "poster.start_draw()\n",
    "\n",
    "poster_params = {\n",
    "    'proportion': 0.6,\n",
    "    'width_border': 1,\n",
    "    'font_size': 25,\n",
    "    'number_background_color': 'yellow',\n",
    "    'letter_background_color': 'green',\n",
    "}\n",
    "\n",
    "poster.set_params(poster_params)\n",
    "poster.load_barlow()\n",
    "poster.set_colors()\n",
    "poster.set_bus_number(bus_number=\"20\")\n",
    "poster.set_bus_letter(bus_letter=\"L\")\n",
    "\n",
    "bm = BusImage()\n",
    "bm.set_theme(theme)\n",
    "bm.load_image_from_url()\n",
    "bm.crop_image(top_cut=165, bottom_cut=165)\n",
    "bm.resize_image(target_width=80)\n",
    "\n",
    "full_panel.add_image(bp, (120, 80))\n",
    "full_panel.add_image(dist_anmc, (90, 10))\n",
    "full_panel.add_image(time_anmc, (160, 10))\n",
    "full_panel.add_image(poster, (40, 80))\n",
    "full_panel.add_image(bm, (5,20))\n",
    "full_panel.get_image()\n",
    "full_panel.save_image('/app/data/output.png')"
   ]
  }
 ],
 "metadata": {
  "language_info": {
   "name": "python"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
