{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import pandas as pd\n",
    "import numpy as np\n",
    "import sys\n",
    "sys.path.append('../../Scripts')\n",
    "from preprocess_daily import DailyPreprocessor\n",
    "from generate_signals import SignalGenerator"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "비보호좌회전, 신호우회전, 유턴\n",
    "\n",
    "줄임말 목록\n",
    "- `i` : 교차로번호, `inter_no`\n",
    "- `f` : 진입, from, `inc_edge_id`\n",
    "- `t` : 진출, to, `out_edge_id`\n",
    "- `vec` : 방향벡터, unit vector (`np.array([0.6, 0.8])`)\n",
    "- `dire` : 방위, direction (동, 서, 남, 북, 북동, 북서, 남동, 남서)\n",
    "- `rvec` : 정방향 방향벡터, unit vector to the right direction (`np.array([0,1])`)\n",
    "\n",
    "필요한 객체들 목록\n",
    "\n",
    "- `inter2dire2rvec` : `inter_no` $\\mapsto$ `dire2rvec`\n",
    "  - `dire2rvec` : `dire` $\\mapsto$ `rvec`\n",
    "- `inter2incs` : `inter_no` $\\mapsto$ `inc_edge_ids`\n",
    "- `inter2outs` : `inter_no` $\\mapsto$ `out_edge_ids`\n",
    "- `inter2inc2dire` : `inter_no` $\\mapsto$ `int2dire`\n",
    "  - `inc2dire` : `out_edge_id` $\\mapsto$ `dire`\n",
    "- `inter2out2dire` : `inter_no` $\\mapsto$ `out2dire`\n",
    "  - `out2dire` : `inc_edge_id` $\\mapsto$ `dire`\n",
    "- `inter2inc2vec` : `inter_no` $\\mapsto$ `int2vec`\n",
    "  - `inc2vec` : `out_edge_id` $\\mapsto$ `vec`\n",
    "- `inter2out2vec` : `inter_no` $\\mapsto$ `out2vec`\n",
    "  - `out2vec` : `inc_edge_id` $\\mapsto$ `vec`\n",
    "\n",
    "좌회전 판단\n",
    "\n",
    "Given `inter_no`, `inc_edge_id` and `out_edge_id`, we have `inc_vec = inter2inc2vec[inter_no][inc_edge_id]` and `out_vec = inter2out2vec[inter_no][out_edge_id]`.\n",
    "Rotate `inc_vec` by 90, 180 and 270 degrees clockwise, to define \n",
    "`out_vec_left`, `out_vec_straight` and `out_vec_right`.\n",
    "Define `out_vecs={'right':out_vec_left, 'straight':out_vec_straight, 'right':out_vec_right}`.\n",
    "Select the key that maximize the similarity of the corresponding value of the key and `inc_vec`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1. 데이터를 로드합니다.\n",
      "1-1. 네트워크가 로드되었습니다.\n",
      "1-2. 테이블들이 로드되었습니다.\n",
      "1-3. 네트워크의 모든 clean state requirement들을 체크했습니다.\n",
      "1-4. 테이블들의 무결성 검사를 완료했습니다.\n",
      "1-5. 주요 객체 (리스트, 딕셔너리)들을 저장했습니다.\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>parent_id</th>\n",
       "      <th>child_id</th>\n",
       "      <th>adj_inc_edge_id</th>\n",
       "      <th>adj_out_edge_id</th>\n",
       "      <th>inc_edge_id</th>\n",
       "      <th>out_edge_id</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>i0</td>\n",
       "      <td>u00</td>\n",
       "      <td>-571500487_01</td>\n",
       "      <td>571500487_01</td>\n",
       "      <td>571500487_02</td>\n",
       "      <td>571500487_01.32</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>i2</td>\n",
       "      <td>u20</td>\n",
       "      <td>571542811_02</td>\n",
       "      <td>571542811_01</td>\n",
       "      <td>571542810_01.51</td>\n",
       "      <td>571542810_02</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>i3</td>\n",
       "      <td>u30</td>\n",
       "      <td>571556450_02</td>\n",
       "      <td>571556450_01</td>\n",
       "      <td>571556452_01</td>\n",
       "      <td>571556452_02</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>i3</td>\n",
       "      <td>u31</td>\n",
       "      <td>-571500475_01</td>\n",
       "      <td>571500475_01</td>\n",
       "      <td>571500475_02</td>\n",
       "      <td>571500475_01.26</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>i3</td>\n",
       "      <td>u32</td>\n",
       "      <td>571540303_02.21</td>\n",
       "      <td>571540303_01</td>\n",
       "      <td>571540303_02</td>\n",
       "      <td>-571540303_02</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>i6</td>\n",
       "      <td>u60</td>\n",
       "      <td>571500535_02.18</td>\n",
       "      <td>571500535_01</td>\n",
       "      <td>571500535_02</td>\n",
       "      <td>-571500535_02</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  parent_id child_id  adj_inc_edge_id adj_out_edge_id      inc_edge_id  \\\n",
       "0        i0      u00    -571500487_01    571500487_01     571500487_02   \n",
       "1        i2      u20     571542811_02    571542811_01  571542810_01.51   \n",
       "2        i3      u30     571556450_02    571556450_01     571556452_01   \n",
       "3        i3      u31    -571500475_01    571500475_01     571500475_02   \n",
       "4        i3      u32  571540303_02.21    571540303_01     571540303_02   \n",
       "5        i6      u60  571500535_02.18    571500535_01     571500535_02   \n",
       "\n",
       "       out_edge_id  \n",
       "0  571500487_01.32  \n",
       "1     571542810_02  \n",
       "2     571556452_02  \n",
       "3  571500475_01.26  \n",
       "4    -571540303_02  \n",
       "5    -571500535_02  "
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "self = DailyPreprocessor()\n",
    "self.load_data()\n",
    "self.make_match1()\n",
    "self.make_match2()\n",
    "self.make_match3()\n",
    "self.make_match4()\n",
    "self.make_match5()\n",
    "display(self.u_turn)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {},
   "outputs": [],
   "source": [
    "self.u_turn = pd.merge(self.u_turn, self.u_condition, on='child_id')\n",
    "\n",
    "# p2inc_edge2angle : node_id to inc_edge2angle\n",
    "p2inc_edge2angle = dict()\n",
    "# p2out_edge2angle : node_id to out_edge2angle\n",
    "p2out_edge2angle = dict()\n",
    "# p2inc_angle2edge : node_id to inc_angle2edge\n",
    "p2inc_angle2edge = dict()\n",
    "# p2out_angle2edge : node_id to out_angle2edge\n",
    "p2out_angle2edge = dict()\n",
    "for node_id in self.parent_ids:\n",
    "    m5 = self.match5[self.match5.node_id==node_id]\n",
    "    m5 = m5.dropna(subset=['inc_edge_id', 'out_edge_id'])\n",
    "    # inc_edge2angle : inc_edge_id to inc_angle\n",
    "    inc_edge2angle = dict(zip(m5.inc_edge_id, m5.inc_angle.astype(int)))\n",
    "    p2inc_edge2angle[node_id] = inc_edge2angle\n",
    "    # out_edge2angle : out_edge_id to out_angle\n",
    "    out_edge2angle = dict(zip(m5.out_edge_id, m5.out_angle.astype(int)))\n",
    "    p2out_edge2angle[node_id] = out_edge2angle\n",
    "    # inc_angle2edge : inc_angle to inc_edge_id\n",
    "    inc_angle2edge = dict(zip(m5.inc_angle.astype(int), m5.inc_edge_id))\n",
    "    p2inc_angle2edge[node_id] = inc_angle2edge\n",
    "    # out_angle2edge : out_angle to out_edge_id\n",
    "    out_angle2edge = dict(zip(m5.out_angle.astype(int), m5.out_edge_id))\n",
    "    p2out_angle2edge[node_id] = out_angle2edge\n",
    "\n",
    "self.uid2uindex = dict() # u_turn node id to u_turn index\n",
    "\n",
    "# 각 uturn node에 대하여 (inc_edge_id, out_edge_id) 부여\n",
    "cmatches = []\n",
    "for row in self.u_turn.itertuples():\n",
    "    parent_id = row.parent_id\n",
    "    child_id = row.child_id\n",
    "    condition = row.condition\n",
    "    inc_edge_id = row.inc_edge_id\n",
    "    out_edge_id = row.out_edge_id\n",
    "    adj_inc_edge_id = row.adj_inc_edge_id\n",
    "    adj_out_edge_id = row.adj_out_edge_id\n",
    "    \n",
    "    # self.uid2uindex 지정\n",
    "    inc_edge = self.net.getEdge(inc_edge_id)\n",
    "    out_edge = self.net.getEdge(out_edge_id)\n",
    "    u_turn_conn = inc_edge.getConnections(out_edge)[0]\n",
    "    self.uid2uindex[child_id] = u_turn_conn.getTLLinkIndex()\n",
    "    \n",
    "    # match5에서 부모노드id에 해당하는 행들을 가져옴 (cmatch)\n",
    "    cmatch = self.match5.copy()[self.match5.node_id==parent_id] # match dataframe for a child node\n",
    "    cmatch = cmatch.sort_values(by=['phase_no', 'ring_type']).reset_index(drop=True)\n",
    "    cmatch['node_id'] = child_id\n",
    "\n",
    "    # 진입엣지 각도\n",
    "    inc_angle = p2inc_edge2angle[parent_id][adj_inc_edge_id]\n",
    "\n",
    "    # 이격각도\n",
    "    self.angle_separation = 10\n",
    "\n",
    "    # 진입로 각도 목록\n",
    "    inc_angles = cmatch.dropna(subset=['inc_angle', 'out_angle']).inc_angle.astype(int).unique()\n",
    "    inc_angles = np.sort(inc_angles)\n",
    "    inc_angles = list(inc_angles - 360) + list(inc_angles) + list(inc_angles + 360)\n",
    "    inc_angles = np.array(inc_angles)\n",
    "\n",
    "    # 보행신호시의 진입로 각도\n",
    "    inc_angles_left = inc_angles[inc_angles >= inc_angle + self.angle_separation]\n",
    "    inc_angle_pedes = np.sort(inc_angles_left)[0] % 360\n",
    "\n",
    "    # 보행신호시의 진입로 엣지id\n",
    "    inc_angle2edge = p2inc_angle2edge[parent_id]\n",
    "    inc_edge_id_pedes = inc_angle2edge[inc_angle_pedes]\n",
    "\n",
    "    # 진출로 각도 목록\n",
    "    out_angles = cmatch.dropna(subset=['inc_angle', 'out_angle']).out_angle.astype(int).unique()\n",
    "    out_angles = np.sort(out_angles)\n",
    "    out_angles = list(out_angles - 360) + list(out_angles) + list(out_angles + 360)\n",
    "    out_angles = np.array(out_angles)\n",
    "\n",
    "    # 보행신호시의 진입로 각도\n",
    "    out_angles_right = out_angles[out_angles <= inc_angle - self.angle_separation]\n",
    "    out_angle_pedes = np.sort(out_angles_right)[-1] % 360\n",
    "\n",
    "    # 보행신호시의 진입로 엣지id\n",
    "    out_angle2edge = p2out_angle2edge[parent_id]\n",
    "    out_edge_id_pedes = out_angle2edge[out_angle_pedes]\n",
    "\n",
    "    # 진입엣지/진출엣지 포함 조건\n",
    "    inc_true = (cmatch.inc_edge_id==adj_inc_edge_id)\n",
    "    out_true = (cmatch.out_edge_id==adj_out_edge_id)\n",
    "\n",
    "    # 보행신호시 조건\n",
    "    pedes_flag = (cmatch.inc_edge_id==inc_edge_id_pedes) & (cmatch.out_edge_id==out_edge_id_pedes)\n",
    "\n",
    "    # 좌회전시 조건\n",
    "    right_flag = inc_true & (cmatch.turn_type=='left')\n",
    "\n",
    "    # 보행신호이동류(17) 조건\n",
    "    crosswalk_on = (cmatch.move_no==17) & ~ out_true\n",
    "\n",
    "    # 신호없음이동류(18) 조건\n",
    "    all_redsigns = (cmatch.move_no==18) & ~ out_true\n",
    "\n",
    "    # 보행신호시/좌회전시 진입/진출 엣지id 배정\n",
    "    cmatch[['inc_edge_id', 'out_edge_id']] = np.nan\n",
    "    if condition == \"보행신호시\":\n",
    "        cmatch.loc[pedes_flag, ['inc_edge_id', 'out_edge_id']] = [inc_edge_id, out_edge_id]\n",
    "    elif condition == \"좌회전시\":\n",
    "        cmatch.loc[right_flag, ['inc_edge_id', 'out_edge_id']] = [inc_edge_id, out_edge_id]\n",
    "\n",
    "    uturn_not_assigned = cmatch[['inc_edge_id','out_edge_id']].isna().any(axis=1).all()\n",
    "\n",
    "    if uturn_not_assigned:\n",
    "        # 좌회전시\n",
    "        if right_flag.any():\n",
    "            cmatch.loc[right_flag, ['inc_edge_id', 'out_edge_id']] = [inc_edge_id, out_edge_id]\n",
    "        # 보행신호시\n",
    "        elif pedes_flag.any():\n",
    "            cmatch.loc[pedes_flag, ['inc_edge_id', 'out_edge_id']] = [inc_edge_id, out_edge_id]\n",
    "        # 보행신호이동류(17) 발생시\n",
    "        elif crosswalk_on.any():\n",
    "            cmatch.loc[crosswalk_on, ['inc_edge_id', 'out_edge_id']] = [inc_edge_id, out_edge_id]\n",
    "        # 신호없음이동류(18) 발생시\n",
    "        elif all_redsigns.any():\n",
    "            cmatch.loc[all_redsigns, ['inc_edge_id', 'out_edge_id']] = [inc_edge_id, out_edge_id]\n",
    "        # 진출엣지 미포함시\n",
    "        elif out_true.any():\n",
    "            cmatch.loc[~ out_true, ['inc_edge_id', 'out_edge_id']] = [inc_edge_id, out_edge_id]\n",
    "    cmatches.append(cmatch)\n",
    "\n",
    "# 각 연등교차로(coordination node)에 대하여 (inc_edge_id, out_edge_id) 부여\n",
    "self.coord['inter_no'] = self.coord['parent_id'].map(self.node2inter)\n",
    "self.coord = self.coord.rename(columns={'child_id':'node_id'})\n",
    "self.coord[['inc_dire', 'out_dire', 'inc_angle','out_angle']] = np.nan\n",
    "self.coord['move_no'] = 20\n",
    "self.coord = self.coord[['inter_no', 'phase_no', 'ring_type', 'move_no', 'inc_dire', 'out_dire', 'inc_angle','out_angle', 'inc_edge_id', 'out_edge_id', 'node_id']]\n",
    "\n",
    "cmatches = pd.concat(cmatches)\n",
    "self.match6 = pd.concat([self.match5, cmatches, self.coord]).drop_duplicates().sort_values(by=['inter_no', 'node_id', 'phase_no', 'ring_type'])\n",
    "self.match6 = self.match6.reset_index(drop=True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "u00 4\n",
      "\n",
      "u20 2\n",
      "\n",
      "u30 4\n",
      "\n",
      "u31 4\n",
      "\n",
      "u32 7\n",
      "\n",
      "u60 8\n",
      "\n"
     ]
    }
   ],
   "source": [
    "for child_id in self.u_turn_ids:\n",
    "    print(child_id, self.uid2uindex[child_id])\n",
    "    print()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "sts",
   "language": "python",
   "name": "sts"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.10"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
