diff --git a/Analysis/0405_unprotected_left/0405_unprotected_left.ipynb b/Analysis/0405_unprotected_left/0405_unprotected_left.ipynb
index 2a2fb7b95..833d8045e 100644
--- a/Analysis/0405_unprotected_left/0405_unprotected_left.ipynb
+++ b/Analysis/0405_unprotected_left/0405_unprotected_left.ipynb
@@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
- "execution_count": 10,
+ "execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
@@ -57,7 +57,7 @@
"name": "stderr",
"output_type": "stream",
"text": [
- "이동류정보 불러오는 중: 100%|██████████| 17280/17280 [00:15<00:00, 1138.40it/s]\n"
+ "이동류정보 불러오는 중: 100%|██████████| 17280/17280 [00:13<00:00, 1252.17it/s]\n"
]
}
],
@@ -72,6 +72,1497 @@
"self.make_matching()"
]
},
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['북', '북동', '동', '남동', '남', '남서', '서', '북서']"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "self.directions"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 7,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'북': array([6.123234e-17, 1.000000e+00]),\n",
+ " '북동': array([0.70710678, 0.70710678]),\n",
+ " '동': array([1., 0.]),\n",
+ " '남동': array([ 0.70710678, -0.70710678]),\n",
+ " '남': array([ 6.123234e-17, -1.000000e+00]),\n",
+ " '남서': array([-0.70710678, -0.70710678]),\n",
+ " '서': array([-1.0000000e+00, -1.2246468e-16]),\n",
+ " '북서': array([-0.70710678, 0.70710678])}"
+ ]
+ },
+ "execution_count": 7,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "\n",
+ "# dictionary that maps dire(북) to dir(np.array([0,1]))\n",
+ "self.dire2dir = dict()\n",
+ "theta = np.pi/2\n",
+ "for dire in self.directions:\n",
+ " self.dire2dir[dire] = np.array([np.cos(theta), np.sin(theta)])\n",
+ " theta -= np.pi/4\n",
+ "self.dire2dir"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 22,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "[(10315.19, 11460.86),\n",
+ " (10314.85, 11439.86),\n",
+ " (10314.54, 11439.86),\n",
+ " (10314.9, 11460.86)]"
+ ]
+ },
+ "execution_count": 22,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "node_id = 'i5'\n",
+ "node = self.net.getNode(node_id)\n",
+ "inc_edges = [edge for edge in node.getIncoming() if edge.getFunction() == ''] # incoming edges\n",
+ "out_edges = [edge for edge in node.getOutgoing() if edge.getFunction() == ''] # outgoing edges\n",
+ "inc_edge = inc_edges[0]\n",
+ "loc_inc_edge = inc_edge.getShape()[-1]\n",
+ "node.getShape()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "2-2. 비보호우회전(g)을 배정했습니다.\n"
+ ]
+ }
+ ],
+ "source": [
+ "self.initialize_state()"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 15,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "c30\n",
+ "571542116_01\n",
+ "571542116_02.96\n",
+ "i0\n",
+ "-571500487_01\n",
+ "-571542797_02\n",
+ "571510153_02\n",
+ "571545870_02\n",
+ "i1\n",
+ "-571542810_01\n",
+ "571542797_02.99\n",
+ "571543469_02\n",
+ "i2\n",
+ "-571542809_01\n",
+ "571542107_02\n",
+ "571542811_02\n",
+ "i3\n",
+ "-571500475_01\n",
+ "571540303_02.21\n",
+ "571540304_02\n",
+ "571556450_02\n",
+ "i6\n",
+ "-571542115_01\n",
+ "571500535_02.18\n",
+ "571500585_02\n",
+ "571511538_02.121\n",
+ "i7\n",
+ "-571511538_02\n",
+ "571542071_02\n",
+ "571542073_01\n",
+ "i8\n",
+ "-571500569_01\n",
+ "571500583_01\n",
+ "571500617_02\n",
+ "571500618_02\n",
+ "i9\n",
+ "571510152_01\n",
+ "571510152_02\n",
+ "u00\n",
+ "571500487_01\n",
+ "571500487_02\n",
+ "u20\n",
+ "571542810_01.51\n",
+ "571542811_01\n",
+ "u30\n",
+ "571556450_01\n",
+ "571556452_01\n",
+ "u31\n",
+ "571500475_01\n",
+ "571500475_02\n",
+ "u32\n",
+ "571540303_01\n",
+ "571540303_02\n",
+ "u60\n",
+ "571500535_01\n",
+ "571500535_02\n"
+ ]
+ }
+ ],
+ "source": [
+ "for node_id in self.node_ids:\n",
+ " print(node_id)\n",
+ " node = self.net.getNode(node_id)\n",
+ " inc_edges = [edge for edge in node.getIncoming() if edge.getFunction() == ''] # incoming edges\n",
+ " out_edges = [edge for edge in node.getOutgoing() if edge.getFunction() == ''] # outgoing edges\n",
+ " inc_edge_ids = [edge.getID() for edge in node.getIncoming() if edge.getFunction() == ''] # incoming edge ids\n",
+ " out_edge_ids = [edge.getID() for edge in node.getOutgoing() if edge.getFunction() == ''] # outgoing edge ids\n",
+ " # print(node)\n",
+ " for inc_edge_id in inc_edge_ids:\n",
+ " print(inc_edge_id)"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/html": [
+ "
\n",
+ "\n",
+ "
\n",
+ " \n",
+ " \n",
+ " | \n",
+ " inter_no | \n",
+ " move_no | \n",
+ " inc_dir | \n",
+ " out_dir | \n",
+ " inc_edge | \n",
+ " out_edge | \n",
+ " node_id | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " 0 | \n",
+ " 175 | \n",
+ " 1 | \n",
+ " 동 | \n",
+ " 남 | \n",
+ " 571545870_02 | \n",
+ " 571542797_02 | \n",
+ " i0 | \n",
+ "
\n",
+ " \n",
+ " 1 | \n",
+ " 175 | \n",
+ " 2 | \n",
+ " 서 | \n",
+ " 동 | \n",
+ " 571510153_02 | \n",
+ " 571545870_01 | \n",
+ " i0 | \n",
+ "
\n",
+ " \n",
+ " 2 | \n",
+ " 175 | \n",
+ " 3 | \n",
+ " 남 | \n",
+ " 서 | \n",
+ " -571542797_02 | \n",
+ " 571510153_01 | \n",
+ " i0 | \n",
+ "
\n",
+ " \n",
+ " 3 | \n",
+ " 175 | \n",
+ " 4 | \n",
+ " 북 | \n",
+ " 남 | \n",
+ " -571500487_01 | \n",
+ " 571542797_02 | \n",
+ " i0 | \n",
+ "
\n",
+ " \n",
+ " 4 | \n",
+ " 175 | \n",
+ " 5 | \n",
+ " 서 | \n",
+ " 북 | \n",
+ " 571510153_02 | \n",
+ " 571500487_01 | \n",
+ " i0 | \n",
+ "
\n",
+ " \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ " ... | \n",
+ "
\n",
+ " \n",
+ " 71 | \n",
+ " 210 | \n",
+ " 21 | \n",
+ " 북 | \n",
+ " 서 | \n",
+ " 571511538_02.121 | \n",
+ " 571500535_01 | \n",
+ " i6 | \n",
+ "
\n",
+ " \n",
+ " 72 | \n",
+ " 210 | \n",
+ " 21 | \n",
+ " 서 | \n",
+ " 남 | \n",
+ " 571500535_02.18 | \n",
+ " 571500585_01 | \n",
+ " i6 | \n",
+ "
\n",
+ " \n",
+ " 73 | \n",
+ " 210 | \n",
+ " 21 | \n",
+ " 남 | \n",
+ " 동 | \n",
+ " 571500585_02 | \n",
+ " 571542115_01 | \n",
+ " i6 | \n",
+ "
\n",
+ " \n",
+ " 74 | \n",
+ " 210 | \n",
+ " 21 | \n",
+ " 동 | \n",
+ " 북 | \n",
+ " -571542115_01 | \n",
+ " 571511538_01 | \n",
+ " i6 | \n",
+ "
\n",
+ " \n",
+ " 75 | \n",
+ " 210 | \n",
+ " 5 | \n",
+ " 서 | \n",
+ " 북 | \n",
+ " 571500535_02 | \n",
+ " -571500535_02 | \n",
+ " u60 | \n",
+ "
\n",
+ " \n",
+ "
\n",
+ "
76 rows × 7 columns
\n",
+ "
"
+ ],
+ "text/plain": [
+ " inter_no move_no inc_dir out_dir inc_edge out_edge node_id\n",
+ "0 175 1 동 남 571545870_02 571542797_02 i0\n",
+ "1 175 2 서 동 571510153_02 571545870_01 i0\n",
+ "2 175 3 남 서 -571542797_02 571510153_01 i0\n",
+ "3 175 4 북 남 -571500487_01 571542797_02 i0\n",
+ "4 175 5 서 북 571510153_02 571500487_01 i0\n",
+ ".. ... ... ... ... ... ... ...\n",
+ "71 210 21 북 서 571511538_02.121 571500535_01 i6\n",
+ "72 210 21 서 남 571500535_02.18 571500585_01 i6\n",
+ "73 210 21 남 동 571500585_02 571542115_01 i6\n",
+ "74 210 21 동 북 -571542115_01 571511538_01 i6\n",
+ "75 210 5 서 북 571500535_02 -571500535_02 u60\n",
+ "\n",
+ "[76 rows x 7 columns]"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "self.matching"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 5,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "['북', '북동', '동', '남동', '남', '남서', '서', '북서']"
+ ]
+ },
+ "execution_count": 5,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "self.directions"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 10,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "'-571542797_02'"
+ ]
+ },
+ "execution_count": 10,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "list(self.node_id2inc_edge2dir['i0'])[1]"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 25,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'-571500487_01': array([-0.00936558, -0.99995614]),\n",
+ " '-571542797_02': array([0.00318883, 0.99999492]),\n",
+ " '571510153_02': array([0.99927006, 0.03820147]),\n",
+ " '571545870_02': array([-0.99951221, 0.03123044])}"
+ ]
+ },
+ "execution_count": 25,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "self.node_id2inc_edge2dir['i0']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 45,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'북': array([6.123234e-17, 1.000000e+00]),\n",
+ " '북동': array([0.70710678, 0.70710678]),\n",
+ " '동': array([1., 0.]),\n",
+ " '남동': array([ 0.70710678, -0.70710678]),\n",
+ " '남': array([ 6.123234e-17, -1.000000e+00]),\n",
+ " '남서': array([-0.70710678, -0.70710678]),\n",
+ " '서': array([-1.0000000e+00, -1.2246468e-16]),\n",
+ " '북서': array([-0.70710678, 0.70710678])}"
+ ]
+ },
+ "execution_count": 45,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "self.dire2dir"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 56,
+ "metadata": {},
+ "outputs": [
+ {
+ "data": {
+ "text/plain": [
+ "{'-571500487_01': array([-0.00936558, -0.99995614]),\n",
+ " '-571542797_02': array([0.00318883, 0.99999492]),\n",
+ " '571510153_02': array([0.99927006, 0.03820147]),\n",
+ " '571545870_02': array([-0.99951221, 0.03123044])}"
+ ]
+ },
+ "execution_count": 56,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "self.node_id2inc_edge2dir['i0']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": 53,
+ "metadata": {},
+ "outputs": [
+ {
+ "name": "stdout",
+ "output_type": "stream",
+ "text": [
+ "4\n",
+ "남\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[-0.00936558 -0.99995614]\n",
+ "-0.999956141961442\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[-0.00936558 -0.99995614]\n",
+ "-0.7136982364459966\n",
+ "\n",
+ "[1. 0.]\n",
+ "[-0.00936558 -0.99995614]\n",
+ "-0.00936558346224636\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[-0.00936558 -0.99995614]\n",
+ "0.7004533012941507\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[-0.00936558 -0.99995614]\n",
+ "0.999956141961442\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[-0.00936558 -0.99995614]\n",
+ "0.7136982364459966\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[-0.00936558 -0.99995614]\n",
+ "0.009365583462246483\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[-0.00936558 -0.99995614]\n",
+ "-0.7004533012941506\n",
+ "\n",
+ "0\n",
+ "북\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[0.00318883 0.99999492]\n",
+ "0.9999949156539597\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[0.00318883 0.99999492]\n",
+ "0.7093580325944496\n",
+ "\n",
+ "[1. 0.]\n",
+ "[0.00318883 0.99999492]\n",
+ "0.0031888346194069504\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[0.00318883 0.99999492]\n",
+ "-0.7048483394275195\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[0.00318883 0.99999492]\n",
+ "-0.9999949156539597\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[0.00318883 0.99999492]\n",
+ "-0.7093580325944496\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[0.00318883 0.99999492]\n",
+ "-0.0031888346194070727\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[0.00318883 0.99999492]\n",
+ "0.7048483394275193\n",
+ "\n",
+ "2\n",
+ "동\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[0.99927006 0.03820147]\n",
+ "0.03820147100728745\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[0.99927006 0.03820147]\n",
+ "0.7336031530235902\n",
+ "\n",
+ "[1. 0.]\n",
+ "[0.99927006 0.03820147]\n",
+ "0.9992700573983387\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[0.99927006 0.03820147]\n",
+ "0.6795781146224817\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[0.99927006 0.03820147]\n",
+ "-0.038201471007287324\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[0.99927006 0.03820147]\n",
+ "-0.7336031530235901\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[0.99927006 0.03820147]\n",
+ "-0.9992700573983387\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[0.99927006 0.03820147]\n",
+ "-0.6795781146224819\n",
+ "\n",
+ "6\n",
+ "서\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[-0.99951221 0.03123044]\n",
+ "0.031230438222358273\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[-0.99951221 0.03123044]\n",
+ "-0.6846786075562368\n",
+ "\n",
+ "[1. 0.]\n",
+ "[-0.99951221 0.03123044]\n",
+ "-0.9995122108951142\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[-0.99951221 0.03123044]\n",
+ "-0.728845116849151\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[-0.99951221 0.03123044]\n",
+ "-0.0312304382223584\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[-0.99951221 0.03123044]\n",
+ "0.6846786075562367\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[-0.99951221 0.03123044]\n",
+ "0.9995122108951142\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[-0.99951221 0.03123044]\n",
+ "0.7288451168491511\n",
+ "\n",
+ "0\n",
+ "북\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[0.01954731 0.99980893]\n",
+ "0.9998089329965109\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[0.01954731 0.99980893]\n",
+ "0.7207937149812274\n",
+ "\n",
+ "[1. 0.]\n",
+ "[0.01954731 0.99980893]\n",
+ "0.019547314403227895\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[0.01954731 0.99980893]\n",
+ "-0.6931496378442115\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[0.01954731 0.99980893]\n",
+ "-0.9998089329965109\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[0.01954731 0.99980893]\n",
+ "-0.7207937149812274\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[0.01954731 0.99980893]\n",
+ "-0.019547314403228017\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[0.01954731 0.99980893]\n",
+ "0.6931496378442114\n",
+ "\n",
+ "4\n",
+ "남\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[-0.00324512 -0.99999473]\n",
+ "-0.9999947345997597\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[-0.00324512 -0.99999473]\n",
+ "-0.7093977009603124\n",
+ "\n",
+ "[1. 0.]\n",
+ "[-0.00324512 -0.99999473]\n",
+ "-0.0032451152146071934\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[-0.00324512 -0.99999473]\n",
+ "0.7048084150123516\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[-0.00324512 -0.99999473]\n",
+ "0.9999947345997597\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[-0.00324512 -0.99999473]\n",
+ "0.7093977009603124\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[-0.00324512 -0.99999473]\n",
+ "0.0032451152146073157\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[-0.00324512 -0.99999473]\n",
+ "-0.7048084150123515\n",
+ "\n",
+ "2\n",
+ "동\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[ 0.99974606 -0.02253478]\n",
+ "-0.02253477852193767\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[ 0.99974606 -0.02253478]\n",
+ "0.690992723527485\n",
+ "\n",
+ "[1. 0.]\n",
+ "[ 0.99974606 -0.02253478]\n",
+ "0.9997460596356292\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[ 0.99974606 -0.02253478]\n",
+ "0.7228617129382832\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[ 0.99974606 -0.02253478]\n",
+ "0.022534778521937793\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[ 0.99974606 -0.02253478]\n",
+ "-0.6909927235274849\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[ 0.99974606 -0.02253478]\n",
+ "-0.9997460596356292\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[ 0.99974606 -0.02253478]\n",
+ "-0.7228617129382833\n",
+ "\n",
+ "0\n",
+ "북\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[0.0188774 0.99982181]\n",
+ "0.9998218060534435\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[0.0188774 0.99982181]\n",
+ "0.7203291148904717\n",
+ "\n",
+ "[1. 0.]\n",
+ "[0.0188774 0.99982181]\n",
+ "0.018877397596868892\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[0.0188774 0.99982181]\n",
+ "-0.6936324431866705\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[0.0188774 0.99982181]\n",
+ "-0.9998218060534435\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[0.0188774 0.99982181]\n",
+ "-0.7203291148904717\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[0.0188774 0.99982181]\n",
+ "-0.018877397596869013\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[0.0188774 0.99982181]\n",
+ "0.6936324431866704\n",
+ "\n",
+ "5\n",
+ "남서\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[-0.71131147 -0.70287694]\n",
+ "-0.7028769433114078\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[-0.71131147 -0.70287694]\n",
+ "-0.9999822145459303\n",
+ "\n",
+ "[1. 0.]\n",
+ "[-0.71131147 -0.70287694]\n",
+ "-0.7113114666313288\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[-0.71131147 -0.70287694]\n",
+ "-0.0059641086355921675\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[-0.71131147 -0.70287694]\n",
+ "0.7028769433114078\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[-0.71131147 -0.70287694]\n",
+ "0.9999822145459303\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[-0.71131147 -0.70287694]\n",
+ "0.7113114666313289\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[-0.71131147 -0.70287694]\n",
+ "0.005964108635592357\n",
+ "\n",
+ "4\n",
+ "남\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[-0.00514794 -0.99998675]\n",
+ "-0.9999867492848744\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[-0.00514794 -0.99998675]\n",
+ "-0.7107375526307316\n",
+ "\n",
+ "[1. 0.]\n",
+ "[-0.00514794 -0.99998675]\n",
+ "-0.005147936933367546\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[-0.00514794 -0.99998675]\n",
+ "0.7034572704013218\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[-0.00514794 -0.99998675]\n",
+ "0.9999867492848744\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[-0.00514794 -0.99998675]\n",
+ "0.7107375526307316\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[-0.00514794 -0.99998675]\n",
+ "0.005147936933367668\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[-0.00514794 -0.99998675]\n",
+ "-0.7034572704013217\n",
+ "\n",
+ "6\n",
+ "서\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[-0.99937585 0.03532571]\n",
+ "0.035325714272748256\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[-0.99937585 0.03532571]\n",
+ "-0.6816863899147254\n",
+ "\n",
+ "[1. 0.]\n",
+ "[-0.99937585 0.03532571]\n",
+ "-0.9993758521753067\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[-0.99937585 0.03532571]\n",
+ "-0.7316444941397628\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[-0.99937585 0.03532571]\n",
+ "-0.03532571427274838\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[-0.99937585 0.03532571]\n",
+ "0.6816863899147253\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[-0.99937585 0.03532571]\n",
+ "0.9993758521753067\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[-0.99937585 0.03532571]\n",
+ "0.731644494139763\n",
+ "\n",
+ "2\n",
+ "동\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[ 0.99959605 -0.02842068]\n",
+ "-0.028420683815972996\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[ 0.99959605 -0.02842068]\n",
+ "0.6867246877003452\n",
+ "\n",
+ "[1. 0.]\n",
+ "[ 0.99959605 -0.02842068]\n",
+ "0.9995960507782293\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[ 0.99959605 -0.02842068]\n",
+ "0.7269176042048118\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[ 0.99959605 -0.02842068]\n",
+ "0.02842068381597312\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[ 0.99959605 -0.02842068]\n",
+ "-0.6867246877003451\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[ 0.99959605 -0.02842068]\n",
+ "-0.9995960507782293\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[ 0.99959605 -0.02842068]\n",
+ "-0.7269176042048119\n",
+ "\n",
+ "0\n",
+ "북\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[0.00401066 0.99999196]\n",
+ "0.9999919572590873\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[0.00401066 0.99999196]\n",
+ "0.7099370610648998\n",
+ "\n",
+ "[1. 0.]\n",
+ "[0.00401066 0.99999196]\n",
+ "0.004010662930190224\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[0.00401066 0.99999196]\n",
+ "-0.7042651271549178\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[0.00401066 0.99999196]\n",
+ "-0.9999919572590873\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[0.00401066 0.99999196]\n",
+ "-0.7099370610648998\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[0.00401066 0.99999196]\n",
+ "-0.0040106629301903465\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[0.00401066 0.99999196]\n",
+ "0.7042651271549177\n",
+ "\n",
+ "4\n",
+ "남\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[-0.02221674 -0.99975318]\n",
+ "-0.99975317783161\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[-0.02221674 -0.99975318]\n",
+ "-0.7226418571477022\n",
+ "\n",
+ "[1. 0.]\n",
+ "[-0.02221674 -0.99975318]\n",
+ "-0.022216737285151163\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[-0.02221674 -0.99975318]\n",
+ "0.6912226459673614\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[-0.02221674 -0.99975318]\n",
+ "0.99975317783161\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[-0.02221674 -0.99975318]\n",
+ "0.7226418571477022\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[-0.02221674 -0.99975318]\n",
+ "0.022216737285151285\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[-0.02221674 -0.99975318]\n",
+ "-0.6912226459673613\n",
+ "\n",
+ "6\n",
+ "서\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[-0.99990726 0.01361849]\n",
+ "0.013618485596221176\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[-0.99990726 0.01361849]\n",
+ "-0.69741148350594\n",
+ "\n",
+ "[1. 0.]\n",
+ "[-0.99990726 0.01361849]\n",
+ "-0.9999072641250615\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[-0.99990726 0.01361849]\n",
+ "-0.7166709305350987\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[-0.99990726 0.01361849]\n",
+ "-0.013618485596221298\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[-0.99990726 0.01361849]\n",
+ "0.6974114835059398\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[-0.99990726 0.01361849]\n",
+ "0.9999072641250615\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[-0.99990726 0.01361849]\n",
+ "0.7166709305350988\n",
+ "\n",
+ "2\n",
+ "동\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[ 0.99979737 -0.02013015]\n",
+ "-0.020130148349706223\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[ 0.99979737 -0.02013015]\n",
+ "0.692729334344831\n",
+ "\n",
+ "[1. 0.]\n",
+ "[ 0.99979737 -0.02013015]\n",
+ "0.9997973680338526\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[ 0.99979737 -0.02013015]\n",
+ "0.721197663153568\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[ 0.99979737 -0.02013015]\n",
+ "0.020130148349706348\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[ 0.99979737 -0.02013015]\n",
+ "-0.6927293343448309\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[ 0.99979737 -0.02013015]\n",
+ "-0.9997973680338526\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[ 0.99979737 -0.02013015]\n",
+ "-0.7211976631535681\n",
+ "\n",
+ "0\n",
+ "북\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[0.02097793 0.99977994]\n",
+ "0.9997799391161097\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[0.02097793 0.99977994]\n",
+ "0.7217848077280572\n",
+ "\n",
+ "[1. 0.]\n",
+ "[0.02097793 0.99977994]\n",
+ "0.02097792508776536\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[0.02097793 0.99977994]\n",
+ "-0.6921175415584926\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[0.02097793 0.99977994]\n",
+ "-0.9997799391161097\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[0.02097793 0.99977994]\n",
+ "-0.7217848077280572\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[0.02097793 0.99977994]\n",
+ "-0.02097792508776548\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[0.02097793 0.99977994]\n",
+ "0.6921175415584925\n",
+ "\n",
+ "4\n",
+ "남\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[-0.0178916 -0.99983993]\n",
+ "-0.9998399325579537\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[-0.0178916 -0.99983993]\n",
+ "-0.7196448663590954\n",
+ "\n",
+ "[1. 0.]\n",
+ "[-0.0178916 -0.99983993]\n",
+ "-0.017891597539252523\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[-0.0178916 -0.99983993]\n",
+ "0.6943423264665635\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[-0.0178916 -0.99983993]\n",
+ "0.9998399325579537\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[-0.0178916 -0.99983993]\n",
+ "0.7196448663590954\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[-0.0178916 -0.99983993]\n",
+ "0.017891597539252645\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[-0.0178916 -0.99983993]\n",
+ "-0.6943423264665634\n",
+ "\n",
+ "0\n",
+ "북\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[0.03173005 0.99949648]\n",
+ "0.9994964752953672\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[0.03173005 0.99949648]\n",
+ "0.7291872667376466\n",
+ "\n",
+ "[1. 0.]\n",
+ "[0.03173005 0.99949648]\n",
+ "0.031730046834780104\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[0.03173005 0.99949648]\n",
+ "-0.684314204169167\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[0.03173005 0.99949648]\n",
+ "-0.9994964752953672\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[0.03173005 0.99949648]\n",
+ "-0.7291872667376466\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[0.03173005 0.99949648]\n",
+ "-0.03173004683478023\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[0.03173005 0.99949648]\n",
+ "0.6843142041691669\n",
+ "\n",
+ "6\n",
+ "서\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[-0.99996279 0.00862609]\n",
+ "0.00862608913718171\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[-0.99996279 0.00862609]\n",
+ "-0.7009809068725814\n",
+ "\n",
+ "[1. 0.]\n",
+ "[-0.99996279 0.00862609]\n",
+ "-0.9999627946009778\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[-0.99996279 0.00862609]\n",
+ "-0.7131800391206231\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[-0.99996279 0.00862609]\n",
+ "-0.008626089137181832\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[-0.99996279 0.00862609]\n",
+ "0.7009809068725813\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[-0.99996279 0.00862609]\n",
+ "0.9999627946009778\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[-0.99996279 0.00862609]\n",
+ "0.7131800391206232\n",
+ "\n",
+ "4\n",
+ "남\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[-0.01269051 -0.99991947]\n",
+ "-0.9999194722128006\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[-0.01269051 -0.99991947]\n",
+ "-0.7160233863906494\n",
+ "\n",
+ "[1. 0.]\n",
+ "[-0.01269051 -0.99991947]\n",
+ "-0.012690511797166758\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[-0.01269051 -0.99991947]\n",
+ "0.6980762924936404\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[-0.01269051 -0.99991947]\n",
+ "0.9999194722128006\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[-0.01269051 -0.99991947]\n",
+ "0.7160233863906494\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[-0.01269051 -0.99991947]\n",
+ "0.012690511797166881\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[-0.01269051 -0.99991947]\n",
+ "-0.6980762924936403\n",
+ "\n",
+ "0\n",
+ "북\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[0.01501082 0.99988733]\n",
+ "0.9998873313095755\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[0.01501082 0.99988733]\n",
+ "0.7176413642873244\n",
+ "\n",
+ "[1. 0.]\n",
+ "[0.01501082 0.99988733]\n",
+ "0.015010818985489397\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[0.01501082 0.99988733]\n",
+ "-0.6964128604957177\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[0.01501082 0.99988733]\n",
+ "-0.9998873313095755\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[0.01501082 0.99988733]\n",
+ "-0.7176413642873244\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[0.01501082 0.99988733]\n",
+ "-0.01501081898548952\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[0.01501082 0.99988733]\n",
+ "0.6964128604957176\n",
+ "\n",
+ "4\n",
+ "남\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[-0.01245578 -0.99992242]\n",
+ "-0.9999224237500915\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[-0.01245578 -0.99992242]\n",
+ "-0.7158594937446279\n",
+ "\n",
+ "[1. 0.]\n",
+ "[-0.01245578 -0.99992242]\n",
+ "-0.012455781057098932\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[-0.01245578 -0.99992242]\n",
+ "0.6982443592437287\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[-0.01245578 -0.99992242]\n",
+ "0.9999224237500915\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[-0.01245578 -0.99992242]\n",
+ "0.7158594937446279\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[-0.01245578 -0.99992242]\n",
+ "0.012455781057099055\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[-0.01245578 -0.99992242]\n",
+ "-0.6982443592437285\n",
+ "\n",
+ "6\n",
+ "서\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[-0.99986689 0.01631585]\n",
+ "0.016315849579498894\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[-0.99986689 0.01631585]\n",
+ "-0.695475608674601\n",
+ "\n",
+ "[1. 0.]\n",
+ "[-0.99986689 0.01631585]\n",
+ "-0.9998668876668031\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[-0.99986689 0.01631585]\n",
+ "-0.7185497044315678\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[-0.99986689 0.01631585]\n",
+ "-0.01631584957949902\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[-0.99986689 0.01631585]\n",
+ "0.6954756086746009\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[-0.99986689 0.01631585]\n",
+ "0.9998668876668031\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[-0.99986689 0.01631585]\n",
+ "0.718549704431568\n",
+ "\n",
+ "2\n",
+ "동\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[ 0.99992218 -0.01247524]\n",
+ "-0.012475239731228712\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[ 0.99992218 -0.01247524]\n",
+ "0.6982304283525206\n",
+ "\n",
+ "[1. 0.]\n",
+ "[ 0.99992218 -0.01247524]\n",
+ "0.999922181168939\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[ 0.99992218 -0.01247524]\n",
+ "0.7158730815742801\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[ 0.99992218 -0.01247524]\n",
+ "0.012475239731228834\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[ 0.99992218 -0.01247524]\n",
+ "-0.6982304283525205\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[ 0.99992218 -0.01247524]\n",
+ "-0.999922181168939\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[ 0.99992218 -0.01247524]\n",
+ "-0.7158730815742802\n",
+ "\n",
+ "2\n",
+ "동\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[ 0.99986806 -0.01624397]\n",
+ "-0.016243967759571678\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[ 0.99986806 -0.01624397]\n",
+ "0.6955272643837823\n",
+ "\n",
+ "[1. 0.]\n",
+ "[ 0.99986806 -0.01624397]\n",
+ "0.999868058051374\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[ 0.99986806 -0.01624397]\n",
+ "0.71849970389612\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[ 0.99986806 -0.01624397]\n",
+ "0.016243967759571803\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[ 0.99986806 -0.01624397]\n",
+ "-0.6955272643837822\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[ 0.99986806 -0.01624397]\n",
+ "-0.999868058051374\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[ 0.99986806 -0.01624397]\n",
+ "-0.7184997038961201\n",
+ "\n",
+ "6\n",
+ "서\n",
+ "[6.123234e-17 1.000000e+00]\n",
+ "[-0.99986809 0.01624217]\n",
+ "0.016242171657398802\n",
+ "\n",
+ "[0.70710678 0.70710678]\n",
+ "[-0.99986809 0.01624217]\n",
+ "-0.6955285550518142\n",
+ "\n",
+ "[1. 0.]\n",
+ "[-0.99986809 0.01624217]\n",
+ "-0.9998680872294363\n",
+ "\n",
+ "[ 0.70710678 -0.70710678]\n",
+ "[-0.99986809 0.01624217]\n",
+ "-0.7184984544920996\n",
+ "\n",
+ "[ 6.123234e-17 -1.000000e+00]\n",
+ "[-0.99986809 0.01624217]\n",
+ "-0.016242171657398927\n",
+ "\n",
+ "[-0.70710678 -0.70710678]\n",
+ "[-0.99986809 0.01624217]\n",
+ "0.6955285550518141\n",
+ "\n",
+ "[-1.0000000e+00 -1.2246468e-16]\n",
+ "[-0.99986809 0.01624217]\n",
+ "0.9998680872294363\n",
+ "\n",
+ "[-0.70710678 0.70710678]\n",
+ "[-0.99986809 0.01624217]\n",
+ "0.7184984544920997\n",
+ "\n"
+ ]
+ },
+ {
+ "data": {
+ "text/plain": [
+ "{'-571500487_01': '남',\n",
+ " '-571542797_02': '북',\n",
+ " '571510153_02': '동',\n",
+ " '571545870_02': '서'}"
+ ]
+ },
+ "execution_count": 53,
+ "metadata": {},
+ "output_type": "execute_result"
+ }
+ ],
+ "source": [
+ "import numpy as np\n",
+ "\n",
+ "# dictionary that maps dire(북) to dir(np.array([0,1]))\n",
+ "self.dire2dir = dict()\n",
+ "theta = np.pi/2\n",
+ "for dire in self.directions:\n",
+ " self.dire2dir[dire] = np.array([np.cos(theta), np.sin(theta)])\n",
+ " theta -= np.pi/4\n",
+ "\n",
+ "# dictionary that maps node_id and inc_edge_id to direction(북)\n",
+ "self.node_id2inc_edge2dire = dict()\n",
+ "for node_id in self.parent_ids:\n",
+ " inc_edge2dire = dict()\n",
+ " node = self.net.getNode(node_id)\n",
+ " inc_edges = [inc_edge.getID() for inc_edge in node.getIncoming()]\n",
+ " out_edges = [out_edge.getID() for out_edge in node.getOutgoing()]\n",
+ "\n",
+ " for inc_edge in inc_edges:\n",
+ " inc_dir_true = self.node_id2inc_edge2dir[node_id][inc_edge]\n",
+ " inc_dirs = [self.dire2dir[inc_dire] for inc_dire in self.directions]\n",
+ " inc_index = np.array([np.dot(inc_dir, inc_dir_true) for inc_dir in inc_dirs]).argmax()\n",
+ " print(inc_index)\n",
+ " print(self.directions[inc_index])\n",
+ " for inc_dir in inc_dirs:\n",
+ " print(inc_dir)\n",
+ " print(inc_dir_true)\n",
+ " print(np.dot(inc_dir, inc_dir_true))\n",
+ " print()\n",
+ " inc_edge2dire[inc_edge] = self.directions[inc_index]\n",
+ " self.node_id2inc_edge2dire[node_id] = inc_edge2dire\n",
+ "self.node_id2inc_edge2dire['i0']"
+ ]
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
+ {
+ "cell_type": "code",
+ "execution_count": null,
+ "metadata": {},
+ "outputs": [],
+ "source": []
+ },
{
"cell_type": "code",
"execution_count": 8,
@@ -447,7 +1938,7 @@
},
{
"cell_type": "code",
- "execution_count": 113,
+ "execution_count": 121,
"metadata": {},
"outputs": [],
"source": [
@@ -461,7 +1952,7 @@
},
{
"cell_type": "code",
- "execution_count": 118,
+ "execution_count": 120,
"metadata": {},
"outputs": [],
"source": [
@@ -471,98 +1962,18 @@
},
{
"cell_type": "code",
- "execution_count": 119,
+ "execution_count": 122,
"metadata": {},
"outputs": [
{
- "name": "stdout",
- "output_type": "stream",
- "text": [
- "i0\n",
- "i0\n",
- "i0\n",
- "i0\n",
- "i0\n",
- "i0\n",
- "i0\n",
- "i0\n",
- "i0\n",
- "i0\n",
- "i0\n",
- "i0\n",
- "i0\n",
- "u00\n",
- "i1\n",
- "i1\n",
- "i1\n",
- "i1\n",
- "i1\n",
- "i1\n",
- "i1\n",
- "i1\n",
- "i2\n",
- "i2\n",
- "i2\n",
- "i2\n",
- "i2\n",
- "i2\n",
- "i2\n",
- "i2\n",
- "i2\n",
- "u20\n",
- "i3\n",
- "i3\n",
- "i3\n",
- "i3\n",
- "i3\n",
- "i3\n",
- "i3\n",
- "i3\n",
- "i3\n",
- "i3\n",
- "i3\n",
- "i3\n",
- "i3\n",
- "i3\n",
- "i3\n",
- "i3\n",
- "u30\n",
- "u31\n",
- "u32\n",
- "i8\n",
- "i8\n",
- "i8\n",
- "i8\n",
- "i8\n",
- "i8\n",
- "i8\n",
- "i8\n",
- "i8\n",
- "i8\n",
- "i8\n",
- "i8\n",
- "i8\n",
- "i9\n",
- "i9\n",
- "i9\n",
- "i9\n",
- "i7\n",
- "i7\n",
- "i7\n",
- "i7\n",
- "i6\n",
- "i6\n",
- "i6\n",
- "i6\n",
- "i6\n",
- "i6\n",
- "i6\n",
- "i6\n",
- "i6\n",
- "i6\n",
- "i6\n",
- "i6\n",
- "u60\n"
+ "ename": "KeyError",
+ "evalue": "'i0'",
+ "output_type": "error",
+ "traceback": [
+ "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
+ "\u001b[1;31mKeyError\u001b[0m Traceback (most recent call last)",
+ "Cell \u001b[1;32mIn[122], line 17\u001b[0m\n\u001b[0;32m 15\u001b[0m out_edge \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mnet\u001b[38;5;241m.\u001b[39mgetEdge(out_edge)\n\u001b[0;32m 16\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m conn \u001b[38;5;129;01min\u001b[39;00m inc_edge\u001b[38;5;241m.\u001b[39mgetConnections(out_edge):\n\u001b[1;32m---> 17\u001b[0m \u001b[43mnode2allocated_conns\u001b[49m\u001b[43m[\u001b[49m\u001b[43mnode_id\u001b[49m\u001b[43m]\u001b[49m\u001b[38;5;241m.\u001b[39mappend(conn)\n\u001b[0;32m 18\u001b[0m index \u001b[38;5;241m=\u001b[39m conn\u001b[38;5;241m.\u001b[39mgetTLLinkIndex()\n\u001b[0;32m 19\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m index \u001b[38;5;241m>\u001b[39m\u001b[38;5;241m=\u001b[39m \u001b[38;5;241m0\u001b[39m:\n",
+ "\u001b[1;31mKeyError\u001b[0m: 'i0'"
]
}
],
@@ -583,7 +1994,7 @@
" inc_edge = self.net.getEdge(inc_edge)\n",
" out_edge = self.net.getEdge(out_edge)\n",
" for conn in inc_edge.getConnections(out_edge):\n",
- " # node2allocated_conns[node_id].append(conn)\n",
+ " node2allocated_conns[node_id].append(conn)\n",
" index = conn.getTLLinkIndex()\n",
" if index >= 0:\n",
" state[index] = 'G'\n",
diff --git a/Results/sn_1704416700.add.xml b/Results/sn_1704416700.add.xml
index e9fb412ec..f83fa06f5 100644
--- a/Results/sn_1704416700.add.xml
+++ b/Results/sn_1704416700.add.xml
@@ -1,62 +1,59 @@
-
-
-
-
+
+
+
+
-
-
+
+
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
+
+
-
-
+
+
+
+
+
+
+
+
-
-
+
+
-
-
-
-
-
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
+
-
+
-
+
@@ -68,10 +65,10 @@
-
+
-
+
@@ -83,17 +80,12 @@
-
-
-
-
-
-
-
+
+
-
+
@@ -124,12 +116,9 @@
-
-
-
-
+
@@ -172,120 +161,112 @@
-
-
-
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
+
+
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
+
+
-
+
@@ -328,72 +309,66 @@
-
-
-
-
-
-
+
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
+
@@ -412,12 +387,9 @@
-
-
-
-
+
@@ -454,263 +426,244 @@
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
+
+
+
+
-
-
+
+
-
-
+
+
+
+
+
-
-
+
+
+
+
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
+
+
-
-
+
+
-
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
+
+
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
+
+
-
-
-
-
-
+
+
\ No newline at end of file
diff --git a/Scripts/__pycache__/preprocess_daily.cpython-38.pyc b/Scripts/__pycache__/preprocess_daily.cpython-38.pyc
index 5ac8e54f1..33f5057cc 100644
Binary files a/Scripts/__pycache__/preprocess_daily.cpython-38.pyc and b/Scripts/__pycache__/preprocess_daily.cpython-38.pyc differ
diff --git a/Scripts/preprocess_daily.py b/Scripts/preprocess_daily.py
index d78ed5f33..3aaf26195 100644
--- a/Scripts/preprocess_daily.py
+++ b/Scripts/preprocess_daily.py
@@ -432,7 +432,7 @@ class DailyPreprocessor():
self.child_ids = sorted(self.inter_node[self.inter_node.inter_type=='child'].node_id.unique())
self.uturn_ids = sorted(self.uturn.child_id.unique())
self.coord_ids = sorted(self.coord.child_id.unique())
-
+
# ids
ids = {'node_ids' : self.node_ids,
'parent_ids': self.parent_ids,
@@ -443,13 +443,12 @@ class DailyPreprocessor():
with open(os.path.join(self.path_intermediates, 'ids.json'), 'w') as file:
json.dump(ids, file)
-
ch2pa = {} # child to parent
for child_id in self.child_ids:
parent_no = self.inter_node[self.inter_node.node_id==child_id].inter_no.iloc[0]
sub_inter_node = self.inter_node[self.inter_node.inter_no==parent_no]
ch2pa[child_id] = sub_inter_node[sub_inter_node.inter_type=='parent'].iloc[0].node_id
- directions = ['북', '북동', '동', '남동', '남', '남서', '서', '북서'] # 정북기준 시계방향으로 8방향
+ self.directions = ['북', '북동', '동', '남동', '남', '남서', '서', '북서'] # 정북기준 시계방향으로 8방향
# 각 uturn node에 대하여 (inc_edge_id, out_edge_id) 부여
cmatches = []
@@ -468,18 +467,18 @@ class DailyPreprocessor():
cmatch[['inc_edge', 'out_edge']] = np.nan
# 보행신호시/좌회전시 진입/진출방향
- ind = directions.index(direction)
- inc_dire_pedes = directions[(ind + 2) % len(directions)]
- out_dire_pedes = directions[(ind - 2) % len(directions)]
+ ind = self.directions.index(direction)
+ inc_dire_pedes = self.directions[(ind + 2) % len(self.directions)]
+ out_dire_pedes = self.directions[(ind - 2) % len(self.directions)]
inc_dire_right = direction
- out_dire_right = directions[(ind + 2) % len(directions)]
+ out_dire_right = self.directions[(ind + 2) % len(self.directions)]
# 보행신호시/좌회전시 조건
pedes_exists = (cmatch.inc_dir==inc_dire_pedes) & (cmatch.out_dir==out_dire_pedes)
right_exists = (cmatch.inc_dir==inc_dire_right) & (cmatch.out_dir==out_dire_right)
# 보행신호시/좌회전시 진입/진출 엣지id 배정
- ind = directions.index(direction)
+ ind = self.directions.index(direction)
if condition == "보행신호시":
cmatch.loc[pedes_exists, ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]
elif condition == "좌회전시":
diff --git a/Scripts/preprocess_daily_0408_backup.py b/Scripts/preprocess_daily_0408_backup.py
new file mode 100644
index 000000000..a5aa4a5ae
--- /dev/null
+++ b/Scripts/preprocess_daily_0408_backup.py
@@ -0,0 +1,818 @@
+# python .\Scripts\preprocess_daily.py
+import pandas as pd
+import numpy as np
+import os, sys, copy
+import json
+import sumolib, traci
+from tqdm import tqdm
+
+class DailyPreprocessor():
+ def __init__(self):
+ # 루트폴더 지정
+ self.path_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
+ with open(os.path.join(self.path_root, 'Scripts', 'config.json'), 'r') as config_file:
+ config = json.load(config_file)
+ # 주요 폴더 경로 지정
+ self.paths = config['paths']
+ self.path_data = os.path.join(self.path_root, *self.paths['data'])
+ self.path_intermediates = os.path.join(self.path_root, *self.paths['intermediates'])
+ self.path_results = os.path.join(self.path_root, *self.paths['results'])
+ self.path_tables = os.path.join(self.path_root, *self.paths['tables'])
+ self.path_networks = os.path.join(self.path_root, *self.paths['networks'])
+ self.path_scripts = os.path.join(self.path_root, *self.paths['scripts'])
+
+ # 이슈사항 목록
+ self.issues = []
+
+ # 1. 데이터 불러오기
+ def load_data(self):
+ print('1. 데이터를 로드합니다.')
+ self.load_networks()
+ self.load_tables()
+ self.check_networks()
+ self.check_tables()
+
+ # 1-1. 네트워크 불러오기
+ def load_networks(self):
+ self.net = sumolib.net.readNet(os.path.join(self.path_networks, 'sn.net.xml'))
+ print("1-1. 네트워크가 로드되었습니다.")
+
+ # 1-2. 테이블 불러오기
+ def load_tables(self):
+ # 모든 컬럼에 대하여 데이터타입 지정
+ loading_dtype = {
+ 'inter_no':'int', 'start_hour':'int', 'start_minute':'int', 'cycle':'int','offset':'int',
+ 'node_id':'str', 'inter_type':'str', 'parent_id':'str','child_id':'str',
+ 'direction':'str', 'condition':'str', 'inc_edge':'str', 'out_edge':'str',
+ 'end_unix':'int', 'inter_name':'str', 'inter_lat':'float', 'inter_lon':'float',
+ 'group_no':'int', 'main_phase_no':'int', 'phase_no':'int','ring_type':'str'
+ }
+ for alph in ['A', 'B']:
+ for j in range(1,9):
+ loading_dtype[f'angle_{alph}{j}'] = 'str'
+ loading_dtype[f'dura_{alph}{j}'] = 'int'
+
+ # 테이블 불러오기
+ self.inter_info = pd.read_csv(os.path.join(self.path_tables, 'inter_info.csv'), dtype=loading_dtype)
+ self.angle = pd.read_csv(os.path.join(self.path_tables, 'angle.csv'), dtype=loading_dtype)
+ self.plan = pd.read_csv(os.path.join(self.path_tables, 'plan.csv'), dtype=loading_dtype)
+ self.inter_node = pd.read_csv(os.path.join(self.path_tables, 'inter_node.csv'), dtype=loading_dtype)
+ self.uturn = pd.read_csv(os.path.join(self.path_tables, 'child_uturn.csv'), dtype=loading_dtype)
+ self.coord = pd.read_csv(os.path.join(self.path_tables, 'child_coord.csv'), dtype=loading_dtype)
+ self.nema = pd.read_csv(os.path.join(self.path_tables, 'nema.csv'), encoding='cp949', dtype=loading_dtype)
+
+ # 교차로목록, 노드목록 정의
+ self.inter_nos = [int(x) for x in sorted(self.inter_info.inter_no.unique())]
+ self.node_ids = sorted(self.inter_node.node_id.unique())
+
+ print("1-2. 테이블들이 로드되었습니다.")
+
+ # 1-3. 네트워크 무결성 검사
+ def check_networks(self):
+ # https://sumo.dlr.de/docs/Netedit/neteditUsageExamples.html#simplify_tls_program_state_after_changing_connections
+ if 'SUMO_HOME' in os.environ:
+ tools = os.path.join(os.environ['SUMO_HOME'], 'tools')
+ if tools not in sys.path:
+ sys.path.append(tools)
+ else:
+ raise EnvironmentError("please declare environment variable 'SUMO_HOME'")
+ traci.start([sumolib.checkBinary('sumo'), "-n", os.path.join(self.path_networks, 'sn.net.xml')])
+ nodes = [node for node in self.net.getNodes() if node.getType()=='traffic_light']
+ for node in nodes:
+ node_id = node.getID()
+ from_xml = len([c for c in node.getConnections() if c.getTLLinkIndex() >= 0])
+ from_traci = len(traci.trafficlight.getRedYellowGreenState(node_id))
+ if from_xml != from_traci:
+ sub = {'id': node_id, 'type': 'node', 'note': '유효하지 않은 연결이있음. netedit에서 clean states 필요.'}
+ self.issues.append(sub)
+ traci.close()
+ print("1-3. 네트워크의 모든 clean state requirement들을 체크했습니다.")
+
+ # 1-4. 테이블 무결성 검사
+ def check_tables(self):
+ self.check_plan()
+ self.check_inter_info()
+ self.check_angle()
+ print("1-4. 테이블들의 무결성 검사를 완료했습니다.")
+ pass
+
+ # 1-4-1. 신호계획(plan) 검사
+ def check_plan(self):
+ # 1-4-1-1. inter_no 검사
+ # self.plan.loc[0, 'inter_no'] = '4' # 에러 발생을 위한 코드
+ missing_inter_nos = set(self.plan.inter_no) - set(self.inter_nos)
+ if missing_inter_nos:
+ msg = f"1-4-1-1. plan의 inter_no 중 교차로 목록(inter_nos)에 포함되지 않는 항목이 있습니다: {missing_inter_nos}"
+ self.issues.append(msg)
+
+ # 1-4-1-2. 시작시각 검사
+ # self.plan.loc[0, 'start_hour'] = 27 # 에러 발생을 위한 코드
+ for _, row in self.plan.iterrows():
+ start_hour = row.start_hour
+ start_minute = row.start_minute
+ if not (0 <= start_hour <= 23) or not (0 <= start_minute <= 59):
+ msg = f"1-4-1-2. plan에 잘못된 형식의 start_time이 존재합니다: {start_hour, start_minute}"
+ self.issues.append(msg)
+
+ # 1-4-1-3. 현시시간 검사
+ # self.plan.loc[0, 'dura_A1'] = -2 # 에러 발생을 위한 코드
+ durations = self.plan[[f'dura_{alph}{j}' for alph in ['A','B'] for j in range(1, 9)]]
+ valid_indices = ((durations >= 0) & (durations <= 200)).all(axis=1)
+ invalid_inter_nos = sorted(self.plan[~ valid_indices].inter_no.unique())
+ if invalid_inter_nos:
+ msg = f"1-4-1-3. plan에 음수이거나 200보다 큰 현시시간이 존재합니다. : {invalid_inter_nos}"
+ self.issues.append(msg)
+
+ # 1-4-1-4. 주기 일관성 검사
+ # self.plan.loc[0, 'cycle'] = 50 # 에러 발생을 위한 코드
+ inconsistent_cycle = self.plan.groupby(['inter_no', 'start_hour', 'start_minute'])['cycle'].nunique().gt(1)
+ if inconsistent_cycle.any():
+ inc_inter_no, start_hour, start_minute = inconsistent_cycle[inconsistent_cycle].index[0]
+ msg = f"1-4-1-4. 한 프로그램에 서로 다른 주기가 존재합니다. inter_no:{inc_inter_no}, start_hour:{start_minute}, start_hour:{start_minute}일 때, cycle이 유일하게 결정되지 않습니다."
+ self.issues.append(msg)
+
+ # 1-4-1-5. 현시시간 / 주기 검사
+ # self.plan.loc[0, 'duration'] = 10 # 에러 발생을 위한 코드
+ right_duration = True
+ for (inter_no, start_hour, start_minute), group in self.plan.groupby(['inter_no', 'start_hour', 'start_minute']):
+ A_sum = group[[f'dura_A{j}' for j in range(1, 9)]].iloc[0].sum()
+ B_sum = group[[f'dura_B{j}' for j in range(1, 9)]].iloc[0].sum()
+ # A_sum = group[group['ring_type']=='A']['duration'].sum()
+ # B_sum = group[group['ring_type']=='B']['duration'].sum()
+ cycle = group['cycle'].unique()[0]
+ if not (A_sum == B_sum == cycle):
+ right_duration = False
+ inc_inter_no = inter_no
+ if not right_duration:
+ msg = f"1-4-1-5. inter_no:{inc_inter_no}, A링현시시간의 합과 B링현시시간의 합이 일치하지 않거나, 현시시간의 합과 주기가 일치하지 않습니다."
+ self.issues.append(msg)
+
+ # 1-4-2. 교차로정보(inter_info) 검사
+ def check_inter_info(self):
+ # 1-4-2-1. inter_lat, inter_lon 적절성 검사
+ # self.inter_info.loc[0, 'inter_lat'] = 38.0 # 에러 발생을 위한 코드
+ self.max_lon, self.min_lon = 127.3, 127.0
+ self.max_lat, self.min_lat = 37.5, 37.2
+ for _, row in self.inter_info.iterrows():
+ latbool = self.min_lat <= row['inter_lat'] <= self.max_lat
+ lonbool = self.min_lon <= row['inter_lon'] <= self.max_lon
+ if not(latbool and lonbool):
+ msg = f"1-4-2-1. 위도 또는 경도가 범위를 벗어난 교차로가 있습니다: inter_no : {row['inter_no']}"
+ self.issues.append(msg)
+
+ # 1-4-3. 방위각정보(inter_info) 검사
+ def check_angle(self):
+ # 1-4-3-1. inter_no 검사
+ # self.angle.loc[0, 'inter_no'] = '4' # 에러 발생을 위한 코드
+ missing_inter_nos = set(self.angle.inter_no) - set(self.inter_nos)
+ if missing_inter_nos:
+ msg = f"1-4-3-1. angle의 inter_no 중 교차로 목록(inter_nos)에 포함되지 않는 항목이 있습니다: {missing_inter_nos}"
+ self.issues.append(msg)
+
+ # 1-4-3-2. 각도 코드 검사
+ angle_codes = self.angle[[f'angle_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]].values.flatten()
+ angle_codes = [code for code in angle_codes if not pd.isna(code) and code != 'stop']
+ of_length_6 = [len(code)==6 for code in angle_codes]
+ if not all(of_length_6):
+ msg = f"1-4-3-2. 여섯자리가 아닌 각도코드가 존재합니다."
+ self.issues.append(msg)
+ angle_codes = [[code[:3],code[3:]] for code in angle_codes]
+ angle_codes = [int(item) for sublist in angle_codes for item in sublist]
+ angle_codes = [0<=code<360 for code in angle_codes]
+ if not all(angle_codes):
+ msg = f"1-4-3-2. 0과 359 사이의 값을 벗어나는 방위각이 존재합니다."
+ self.issues.append(msg)
+
+ # 2. 중간산출물 만들기
+ def get_intermediates(self):
+ print('2. 중간산출물을 생성합니다.')
+ self.get_matches()
+ self.initialize_state()
+ self.assign_signals()
+ self.get_node2num_cycles()
+
+ # 2-1 매칭테이블 생성
+ def get_matches(self):
+ self.make_match1()
+ self.make_match2()
+ self.make_match3()
+ self.make_match4()
+ self.make_match5()
+ self.make_match6()
+ self.make_matching()
+ print('2-1. 매칭 테이블들을 생성했습니다.')
+
+ # 2-1-1
+ def make_match1(self):
+ '''
+ 신호 DB에는 매 초마다 이동류정보가 업데이트 된다. 그리고 이 이동류정보를 매 5초마다 불러와서 사용하게 된다.
+ '../Data/tables/move/'에는 5초마다의 이동류정보가 저장되어 있다.
+
+ return : 통합된 이동류정보
+ - 모든 inter_no(교차로번호)에 대한 A, B링 현시별 이동류정보
+
+ match1을 만드는 데 시간이 소요되므로 한 번 만들어서 저장해두고 저장해둔 것을 쓴다.
+ '''
+ # [이동류번호] 불러오기 (약 1분의 소요시간)
+ path_move = os.path.join(self.path_tables, 'move')
+ csv_moves = os.listdir(path_move)
+ moves = [pd.read_csv(os.path.join(path_move, csv_move), index_col=0) for csv_move in tqdm(csv_moves, desc='이동류정보 불러오는 중')]
+ df = pd.concat(moves).reset_index(drop=True)
+ self.match1 = []
+ for i, group in df.groupby(['inter_no', 'phas_A', 'phas_B']):
+ inter_no, phas_A, phas_B = i
+ pairs_array = np.array(group[['move_A', 'move_B']])
+ unique_pairs, counts = np.unique(pairs_array, axis=0, return_counts=True)
+ frequent_pair = unique_pairs[np.argmax(counts)]
+ self.match1.append(pd.DataFrame({'inter_no':[inter_no], 'phas_A':[phas_A], 'phas_B':[phas_B],
+ 'move_A':[frequent_pair[0]], 'move_B':[frequent_pair[1]]}))
+ self.match1 = pd.concat(self.match1).reset_index(drop=True)
+ self.match1.to_csv(os.path.join(self.path_intermediates, 'match1.csv'))
+
+ # 2-1-2
+ def make_match2(self):
+ '''
+ match1을 계층화함.
+ - match1의 컬럼 : inter_no, phas_A, phas_B, move_A, move_B
+ - match2의 컬럼 : inter_no, phase_no, ring_type, move_no
+ '''
+ # 계층화 (inter_no, phas_A, phas_B, move_A, move_B) -> ('inter_no', 'phase_no', 'ring_type', 'move_no')
+ matchA = self.match1[['inter_no', 'phas_A', 'move_A']].copy()
+ matchA.columns = ['inter_no', 'phase_no', 'move_no']
+ matchA['ring_type'] = 'A'
+ matchB = self.match1[['inter_no', 'phas_B', 'move_B']].copy()
+ matchB.columns = ['inter_no', 'phase_no', 'move_no']
+ matchB['ring_type'] = 'B'
+ self.match2 = pd.concat([matchA, matchB]).drop_duplicates()
+ self.match2 = self.match2[['inter_no', 'phase_no', 'ring_type', 'move_no']]
+ self.match2 = self.match2.sort_values(by=list(self.match2.columns))
+
+ # 2-1-3
+ def make_match3(self):
+ '''
+ 각 movement들에 방향(진입방향, 진출방향)을 매칭시켜 추가함.
+ - match2의 컬럼 : inter_no, phase_no, ring_type, move_no
+ - match3의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir
+
+ nema :
+ - 컬럼 : move_no, inc_dir, out_dir
+ - 모든 종류의 이동류번호에 대하여 진입방향과 진출방향을 매칭시키는 테이블
+ - 이동류번호 : 1 ~ 16, 17, 18, 21
+ - 진입, 진출방향(8방위) : 동, 서, 남, 북, 북동, 북서, 남동, 남서
+ '''
+ # nema 정보 불러오기 및 병합
+ self.match3 = pd.merge(self.match2, self.nema, how='left', on='move_no').drop_duplicates()
+
+ # 2-1-4
+ def make_match4(self):
+ '''
+ 방위각 정보를 매칭시켜 추가함.
+ - match3의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir
+ - match4의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle
+
+ angle_original :
+ - 컬럼 : inter_no, angle_Aj, angle_Bj (j : 1 ~ 8)
+ - 모든 종류의 이동류번호에 대하여 진입방향과 진출방향을 매칭시키는 테이블
+ - 이동류번호 : 1 ~ 16, 17, 18, 21
+ - 진입, 진출방향(8방위) : 동, 서, 남, 북, 북동, 북서, 남동, 남서
+ '''
+
+ # 계층화
+ angles = []
+ for i, row in self.angle.iterrows():
+ angle_codes = row[[f'angle_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]]
+ new = pd.DataFrame({'inter_no':[row.inter_no] * 16, 'phase_no':list(range(1, 9))*2, 'ring_type':['A'] * 8 + ['B'] * 8, 'angle_code':angle_codes.to_list()})
+ angles.append(new)
+ angles = pd.concat(angles)
+ angles = angles.dropna().reset_index(drop=True)
+
+ # 병합
+ six_chars = angles.angle_code.apply(lambda x:len(x)==6)
+ angles.loc[six_chars,'inc_angle'] = angles.angle_code.apply(lambda x:x[:3])
+ angles.loc[six_chars,'out_angle'] = angles.angle_code.apply(lambda x:x[3:])
+ angles = angles.drop('angle_code', axis=1)
+ self.match4 = pd.merge(self.match3, angles, how='left', left_on=['inter_no', 'phase_no', 'ring_type'],
+ right_on=['inter_no', 'phase_no', 'ring_type']).drop_duplicates()
+
+ # 2-1-5
+ def make_match5(self):
+ '''
+ 진입엣지id, 진출엣지id, 노드id를 추가함 (주교차로).
+ - match4의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle
+ - match5의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle, inc_edge, out_edge, node_id
+
+ 사용된 데이터 :
+ (1) net
+ - 성남시 정자동 부근의 샘플 네트워크
+ (2) inter_node
+ - 교차로번호와 노드id를 매칭시키는 테이블.
+ - parent/child 정보도 포함되어 있음
+ - 컬럼 : inter_no, node_id, inter_type
+ (3) inter_info
+ - 교차로 정보. 여기에서는 위도와 경도가 쓰임.
+ - 컬럼 : inter_no, inter_name, inter_lat, inter_lon, group_no, main_phase_no
+
+ 진입엣지id, 진출엣지id를 얻는 과정 :
+ - match5 = match4.copy()의 각 열을 순회하면서 아래 과정을 반복함.
+ * 진입에 대해서만 서술하겠지만 진출도 마찬가지로 설명될 수 있음
+ - 해당 행의 교차로정보로부터 노드ID를 얻어내고, 해당 노드에 대한 모든 진출엣지id를 inc_edges에 저장.
+ * inc_edge(진입엣지) : incoming edge, out_edge(진출엣지) : outgoing_edge
+ - inc_edges의 모든 진입엣지에 대하여 진입방향(inc_dires, 2차원 단위벡터)을 얻어냄.
+ - 해당 행의 진입각으로부터 그에 대응되는 진입각방향(단위벡터)를 얻어냄.
+ - 주어진 진입각방향에 대하여 내적이 가장 작은 진입방향에 대한 진입엣지를 inc_edge_id로 지정함.
+ '''
+
+ # parent node만 가져옴.
+ inter_node1 = self.inter_node[self.inter_node.inter_type == 'parent'].drop('inter_type', axis=1)
+ inter_info1 = self.inter_info[['inter_no', 'inter_lat', 'inter_lon']]
+ inter = pd.merge(inter_node1, inter_info1, how='left', left_on=['inter_no'],
+ right_on=['inter_no']).drop_duplicates()
+
+ self.inter2node = dict(zip(inter['inter_no'], inter['node_id']))
+
+ # node_id, inc/out_edge가 주어질 때 해당되는 방향벡터를 매칭하는 딕셔너리
+ self.node_id2inc_edge2dir = dict()
+ self.node_id2out_edge2dir = dict()
+
+ # 진입진출ID 매칭
+ self.match5 = self.match4.copy()
+ for index, row in self.match5.iterrows():
+ node_id = self.inter2node[row.inter_no]
+ node = self.net.getNode(node_id)
+ self.node_id2inc_edge2dir[node_id] = dict()
+ self.node_id2out_edge2dir[node_id] = dict()
+ # 교차로의 모든 (from / to) edges
+ inc_edges = [edge for edge in node.getIncoming() if edge.getFunction() == ''] # incoming edges
+ out_edges = [edge for edge in node.getOutgoing() if edge.getFunction() == ''] # outgoing edges
+ # 교차로의 모든 (from / to) directions
+ inc_dirs = []
+ for inc_edge in inc_edges:
+ start = inc_edge.getShape()[-2]
+ end = inc_edge.getShape()[-1]
+ inc_dir = np.array(end) - np.array(start)
+ inc_dir = inc_dir / (inc_dir ** 2).sum() ** 0.5
+ inc_dirs.append(inc_dir)
+ self.node_id2inc_edge2dir[node_id][inc_edge.getID()] = inc_dir
+ out_dirs = []
+ self.out_edge2dir = dict()
+ for out_edge in out_edges:
+ start = out_edge.getShape()[0]
+ end = out_edge.getShape()[1]
+ out_dir = np.array(end) - np.array(start)
+ out_dir = out_dir / (out_dir ** 2).sum() ** 0.5
+ out_dirs.append(out_dir)
+ self.out_edge2dir[out_edge] = out_dir
+ self.node_id2out_edge2dir[node_id][out_edge.getID()] = out_dir
+ if not pd.isna(row.inc_angle):
+ # 진입각, 진출각 불러오기
+ inc_angle = int(row.inc_angle)
+ out_angle = int(row.out_angle)
+ # 방위각을 일반각으로 가공, 라디안 변환, 단위벡터로 변환
+ inc_angle = (-90 - inc_angle) % 360
+ inc_angle = inc_angle * np.pi / 180.
+ inc_dir_true = np.array([np.cos(inc_angle), np.sin(inc_angle)])
+ out_angle = (90 - out_angle) % 360
+ out_angle = out_angle * np.pi / 180.
+ out_dir_true = np.array([np.cos(out_angle), np.sin(out_angle)])
+ # 매칭 엣지 반환
+ inc_index = np.array([np.dot(inc_dir, inc_dir_true) for inc_dir in inc_dirs]).argmax()
+ out_index = np.array([np.dot(out_dir, out_dir_true) for out_dir in out_dirs]).argmax()
+ inc_edge_id = inc_edges[inc_index].getID()
+ out_edge_id = out_edges[out_index].getID()
+ self.match5.at[index, 'inc_edge'] = inc_edge_id
+ self.match5.at[index, 'out_edge'] = out_edge_id
+ self.match5['node_id'] = self.match5['inter_no'].map(self.inter2node)
+ self.match5 = self.match5.sort_values(by=['inter_no','phase_no','ring_type']).reset_index(drop=True)
+
+ # 정북기준 시계방향으로 8방향
+ self.directions = ['북', '북동', '동', '남동', '남', '남서', '서', '북서']
+
+
+ # 2-1-6
+ def make_match6(self):
+ '''
+ 진입엣지id, 진출엣지id, 노드id를 추가함 (부교차로).
+ - match6의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle, inc_edge, out_edge, node_id
+
+ 사용된 데이터 :
+ (1) inter_node
+ - 교차로번호와 노드id를 매칭시키는 테이블.
+ - parent/child 정보도 포함되어 있음
+ - 컬럼 : inter_no, node_id, inter_type
+ (2) uturn (유턴정보)
+ - 컬럼 : parent_id, child_id, direction, condition, inc_edge, out_edge
+ - parent_id, child_id : 주교차로id, 유턴교차로id
+ - direction : 주교차로에 대한 유턴노드의 상대적인 위치(방향)
+ - condition : 좌회전시, 직진시, 직좌시, 보행신호시 중 하나
+ - inc_edge, out_edge : 유턴에 대한 진입진출엣지
+ (3) coord (연동교차로정보)
+ - 컬럼 : parent_id, child_id, phase_no, ring_type, inc_edge, out_edge
+ - parent_id, child_id : 주교차로id, 연동교차로id
+ - 나머지 컬럼 : 각 (현시, 링)별 진입진출엣지
+
+ 설명 :
+ - match5는 주교차로에 대해서만 진입엣지id, 진출엣지id, 노드id를 추가했었음.
+ 여기에서 uturn, coord를 사용해서 부교차로들(유턴교차로, 연동교차로)에 대해서도 해당 값들을 부여함.
+ 유턴교차로 :
+ - directions를 정북기준 시계방향의 8방위로 정함.
+ - 이를 통해 진입방향이 주어진 경우에 좌회전, 직진, 보행 등에 대한 (진입방향, 진출방향)을 얻어낼 수 있음.
+ - 예) 진입방향(direction)이 '북'일 때,
+ - 직진 : (북, 남)
+ * 남 : directions[(ind + 4) % len(directions)]
+ - 좌회전 : (북, 동)
+ * 동 : directions[(ind + 2) % len(directions)]
+ - 보행 : (서, 동)
+ * 서 : directions[(ind - 2) % len(directions)]
+ - uturn의 각 행을 순회하면서 아래 과정을 반복함
+ - match5에서 parent_id에 해당하는 행들을 가져옴(cmatch).
+ - condition 별로 진입방향, 진출방향을 정함.
+ - 상술한 directions를 활용하여 정함.
+ - (진입방향, 진출방향)을 고려하여 (현시, 링) 별로 진입엣지id, 진출엣지id를 정함.
+ - ex) cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]
+ - 순회하면서 만든 cmatch를 cmatchs라는 리스트에 저장함.
+
+ 연동교차로 :
+ - 연동교차로의 경우 coord에 (현시, 링)별 진입엣지ID, 진출엣지ID가 명시되어 있음.
+ - 'inc_dir', 'out_dir', 'inc_angle','out_angle'와 같은 열들은 np.nan을 지정해놓음.
+ - 이 열들은, 사실상 다음 스텝부터는 사용되지 않는 열들이기 때문에 np.nan으로 지정해놓아도 문제없음.
+
+ match6 :
+ - 이렇게 얻은 match5, cmatchs, coord를 모두 pd.concat하여 match6을 얻어냄.
+ '''
+
+ self.node2inter = dict(zip(self.inter_node['node_id'], self.inter_node['inter_no']))
+
+ self.parent_ids = sorted(self.inter_node[self.inter_node.inter_type=='parent'].node_id.unique())
+ self.child_ids = sorted(self.inter_node[self.inter_node.inter_type=='child'].node_id.unique())
+ self.uturn_ids = sorted(self.uturn.child_id.unique())
+ self.coord_ids = sorted(self.coord.child_id.unique())
+
+ # ids
+ ids = {'node_ids' : self.node_ids,
+ 'parent_ids': self.parent_ids,
+ 'child_ids' : self.child_ids,
+ 'uturn_ids' : self.uturn_ids,
+ 'coord_ids' : self.coord_ids,
+ 'inter_nos' : self.inter_nos}
+ with open(os.path.join(self.path_intermediates, 'ids.json'), 'w') as file:
+ json.dump(ids, file)
+
+ ch2pa = {} # child to parent
+ for child_id in self.child_ids:
+ parent_no = self.inter_node[self.inter_node.node_id==child_id].inter_no.iloc[0]
+ sub_inter_node = self.inter_node[self.inter_node.inter_no==parent_no]
+ ch2pa[child_id] = sub_inter_node[sub_inter_node.inter_type=='parent'].iloc[0].node_id
+
+ # 각 uturn node에 대하여 (inc_edge_id, out_edge_id) 부여
+ cmatches = []
+ for _, row in self.uturn.iterrows():
+ child_id = row.child_id
+ parent_id = row.parent_id
+ direction = row.direction
+ condition = row.condition
+ inc_edge_id = row.inc_edge
+ out_edge_id = row.out_edge
+
+ # match5에서 부모노드id에 해당하는 행들을 가져옴 (cmatch)
+ cmatch = self.match5.copy()[self.match5.node_id==parent_id] # match dataframe for a child node
+ cmatch = cmatch.sort_values(by=['phase_no', 'ring_type']).reset_index(drop=True)
+ cmatch['node_id'] = child_id
+ cmatch[['inc_edge', 'out_edge']] = np.nan
+
+ # 보행신호시/좌회전시 진입/진출방향
+ ind = self.directions.index(direction)
+ inc_dire_pedes = self.directions[(ind + 2) % len(self.directions)]
+ out_dire_pedes = self.directions[(ind - 2) % len(self.directions)]
+ inc_dire_right = direction
+ out_dire_right = self.directions[(ind + 2) % len(self.directions)]
+
+ # 보행신호시/좌회전시 조건
+ pedes_exists = (cmatch.inc_dir==inc_dire_pedes) & (cmatch.out_dir==out_dire_pedes)
+ right_exists = (cmatch.inc_dir==inc_dire_right) & (cmatch.out_dir==out_dire_right)
+
+ # 보행신호시/좌회전시 진입/진출 엣지id 배정
+ ind = self.directions.index(direction)
+ if condition == "보행신호시":
+ cmatch.loc[pedes_exists, ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]
+ elif condition == "좌회전시":
+ cmatch.loc[right_exists, ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]
+
+ # 신호없음이동류발생시/보행신호이동류발생시 조건
+ all_redsigns = cmatch.move_no == 18
+ crosswalk_on = cmatch.move_no == 17
+
+ # 만약 어떤 유턴신호도 배정되지 않았다면
+ # 좌회전시 → 보행신호시 → 보행신호이동류발생시 → 신호없음이동류발생시 순으로 진입/진출 엣지id 배정
+ uturn_not_assigned = cmatch[['inc_edge','out_edge']].isna().any(axis=1).all()
+ if uturn_not_assigned:
+ # 좌회전시
+ if right_exists.any():
+ cmatch.loc[right_exists, ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]
+ # 보행신호시
+ elif pedes_exists.any():
+ cmatch.loc[pedes_exists, ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]
+ # 보행신호이동류(17) 발생시
+ elif crosswalk_on.any():
+ cmatch.loc[crosswalk_on & (cmatch.out_dir!=direction), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]
+ # 신호없음이동류(18) 발생시
+ elif all_redsigns.any():
+ cmatch.loc[all_redsigns & (cmatch.out_dir!=direction), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]
+ cmatches.append(cmatch)
+
+ # 각 연등교차로(coordination node)에 대하여 (inc_edge_id, out_edge_id) 부여
+ self.coord['inter_no'] = self.coord['parent_id'].map(self.node2inter)
+ self.coord = self.coord.rename(columns={'child_id':'node_id'})
+ self.coord[['inc_dir', 'out_dir', 'inc_angle','out_angle']] = np.nan
+ self.coord['move_no'] = 20
+ self.coord = self.coord[['inter_no', 'phase_no', 'ring_type', 'move_no', 'inc_dir', 'out_dir', 'inc_angle','out_angle', 'inc_edge', 'out_edge', 'node_id']]
+
+ # display(coord)
+ cmatches = pd.concat(cmatches)
+ self.match6 = pd.concat([self.match5, cmatches, self.coord]).drop_duplicates().sort_values(by=['inter_no', 'node_id', 'phase_no', 'ring_type'])
+ # self.match6.to_csv(os.path.join(self.path_intermediates, 'match6.csv'))
+
+
+ # 2-1-7
+ def make_matching(self):
+ '''
+ 이동류 매칭 : 각 교차로에 대하여, 가능한 모든 이동류 (1~18, 21)에 대한 진입·진출엣지ID를 지정한다.
+ 모든 이동류에 대해 지정하므로, 시차제시 이전과 다른 이동류가 등장하더라도 항상 진입·진출 엣지 ID를 지정할 수 있다.
+ - matching의 컬럼 : inter_no, move_no, inc_dir, out_dir, inc_edge, out_edge, node_id
+
+ 설명 :
+ - 필요한 리스트, 딕셔너리 등을 정의
+ (1) 가능한 (진입방향, 진출방향) 목록 [리스트]
+ (2) 각 교차로별 방향 목록 : pdires (possible directions) [딕셔너리]
+ (3) 각 (교차로, 진입방향) 별 진입id 목록 : inc2id (incoming direction to incoming edge_id) [딕셔너리]
+ (4) 각 (교차로, 진출방향) 별 진출id 목록 : out2id (outgoing direction to outgoing edge_id) [딕셔너리]
+ (5) 각 교차로별 가능한 (진입방향, 진출방향) 목록 : pflow (possible flows) [딕셔너리]
+ - matching은 빈 리스트로 지정.
+ - 모든 노드id에 대하여 다음 과정을 반복
+ - 해당 노드id에 대한 모든 가능한 (진입방향, 진출방향)에 대하여 다음 과정을 반복
+ - (노드id, 진입방향)으로부터 진입엣지id를 얻어냄. 마찬가지로 진출엣지id도 얻어냄
+ - 얻어낸 정보를 바탕으로 한 행(new_row)을 만들고 이것을 matching에 append
+ '''
+
+ self.match7 = self.match6.copy()
+ self.match7 = self.match7[['inter_no', 'move_no', 'inc_dir', 'out_dir', 'inc_edge', 'out_edge', 'node_id']]
+
+ # parent_ids = sorted(self.inter_node[self.inter_node.inter_type=='parent'].node_id.unique())
+ # child_ids = sorted(self.inter_node[self.inter_node.inter_type=='child'].node_id.unique())
+
+ # (1) 가능한 (진입방향, 진출방향) 목록
+ flows = self.nema.dropna().apply(lambda row: (row['inc_dir'], row['out_dir']), axis=1).tolist()
+ # (2) 각 교차로별 방향 목록 : pdires (possible directions)
+ pdires = {}
+ for node_id in self.parent_ids:
+ dires = self.match7[self.match7.node_id == node_id][['inc_dir','out_dir']].values.flatten()
+ dires = {dire for dire in dires if type(dire)==str}
+ pdires[node_id] = dires
+ # (3) 각 (교차로, 진입방향) 별 진입id 목록 : inc2id (incoming direction to incoming edge_id)
+ inc2id = {}
+ for node_id in self.parent_ids:
+ for inc_dir in pdires[node_id]:
+ df = self.match7[(self.match7.node_id==node_id) & (self.match7.inc_dir==inc_dir)]
+ inc2id[(node_id, inc_dir)] = df.inc_edge.iloc[0]
+ # (4) 각 (교차로, 진출방향) 별 진출id 목록 : out2id (outgoing direction to outgoing edge_id)
+ out2id = {}
+ for node_id in self.parent_ids:
+ for out_dir in pdires[node_id]:
+ df = self.match7[(self.match7.node_id==node_id) & (self.match7.out_dir==out_dir)]
+ out2id[(node_id, out_dir)] = df.out_edge.iloc[0]
+ # (5) 각 교차로별 가능한 (진입방향, 진출방향) 목록 : pflow (possible flows)
+ pflow = {}
+ for node_id in self.parent_ids:
+ pflow[node_id] = [flow for flow in flows if set(flow).issubset(pdires[node_id])]
+ # (6) 가능한 이동류에 대하여 진입id, 진출id 배정 : matching
+ # node2inter = dict(zip(self.match7['node_id'], self.match7['inter_no']))
+ dires_right = ['북', '서', '남', '동', '북'] # ex (북, 서), (서, 남) 등은 우회전 flow
+ self.matching = []
+ for node_id in self.parent_ids:
+ inter_no = self.node2inter[node_id]
+ # 좌회전과 직진(1 ~ 16)
+ for (inc_dir, out_dir) in pflow[node_id]:
+ move_no = self.nema[(self.nema.inc_dir==inc_dir) & (self.nema.out_dir==out_dir)].move_no.iloc[0]
+ inc_edge = inc2id[(node_id, inc_dir)]
+ out_edge = out2id[(node_id, out_dir)]
+ new_row = pd.DataFrame({'inter_no':[inter_no], 'move_no':[move_no],
+ 'inc_dir':[inc_dir], 'out_dir':[out_dir],
+ 'inc_edge':[inc_edge], 'out_edge':[out_edge], 'node_id':[node_id]})
+ self.matching.append(new_row)
+ # 보행신호(17), 전적색(18)
+ new_row = pd.DataFrame({'inter_no':[inter_no] * 2, 'move_no':[17, 18],
+ 'inc_dir':[None]*2, 'out_dir':[None]*2,
+ 'inc_edge':[None]*2, 'out_edge':[None]*2, 'node_id':[node_id]*2})
+ self.matching.append(new_row)
+ # 신호우회전(21)
+ for d in range(len(dires_right)-1):
+ inc_dir = dires_right[d]
+ out_dir = dires_right[d+1]
+ if {inc_dir, out_dir}.issubset(pdires[node_id]):
+ inc_edge = inc2id[(node_id, inc_dir)]
+ out_edge = out2id[(node_id, out_dir)]
+ new_row = pd.DataFrame({'inter_no':[inter_no], 'move_no':[21],
+ 'inc_dir':[inc_dir], 'out_dir':[out_dir],
+ 'inc_edge':[inc_edge], 'out_edge':[out_edge], 'node_id':[node_id]})
+ self.matching.append(new_row)
+ self.matching.append(self.match7[self.match7.node_id.isin(self.child_ids)])
+ self.matching = pd.concat(self.matching)
+ self.matching = self.matching.dropna().sort_values(by=['inter_no', 'node_id', 'move_no']).reset_index(drop=True)
+ self.matching['move_no'] = self.matching['move_no'].astype(int)
+ # self.matching.to_csv(os.path.join(self.path_intermediates, 'matching.csv'))
+
+ # 2-2 신호 초기화
+ def initialize_state(self):
+ '''
+ 비보호우회전신호 (g) 배정
+
+ input :
+ (1) net : 네트워크
+ (2) nodes : 노드 목록
+ (3) histids : 모든 교차로에 대한 시작유닉스 (시작유닉스, A현시, B현시)별 현시시간, 진입·진출엣지
+
+ output : node2init
+ - 각 노드를 초기화된 신호로 맵핑하는 딕셔너리
+ - 초기화된 신호란, 우회전을 g로 나머지는 r로 지정한 신호를 말함.
+ '''
+ self.nodes = [self.net.getNode(node_id) for node_id in self.node_ids]
+ self.node2init = {}
+ # 모든 노드들을 순회
+ for node in self.nodes:
+ node_id = node.getID()
+ # 모든 connection
+ conns = [(c.getJunctionIndex(), c) for c in node.getConnections()]
+ conns = [c for c in conns if c[0] >= 0]
+ conns = sorted(conns, key=lambda x: x[0])
+ state = []
+ # i번째 connection : ci
+ for i, ci in conns:
+ if ci.getTLLinkIndex() < 0:
+ continue
+ are_foes = False
+ # j번째 connection : cj
+ # 합류지점이 다르면서 상충되는 cj가 존재하면 are_foes = True (r)
+ # 그외의 경우에는 are_foes = False (g)
+ for j, cj in conns:
+ # ci, cj의 합류지점이 같으면 통과
+ if ci.getTo() == cj.getTo():
+ continue
+ # ci, cj가 상충되면 are_foes를 True로 지정.
+ if node.areFoes(i, j):
+ are_foes = True
+ break
+ state.append('r' if are_foes else 'g')
+ self.node2init[node_id] = state
+
+ # 어떤 연결과도 상충이 일어나지는 않지만, 신호가 부여되어 있는 경우에는 r을 부여
+ for _, row in self.matching.iterrows():
+ node_id = row.node_id
+ move_no = row.move_no
+ inc_edge = row.inc_edge
+ out_edge = row.out_edge
+ if move_no != 21:
+ inc_edge = self.net.getEdge(inc_edge)
+ out_edge = self.net.getEdge(out_edge)
+ for conn in inc_edge.getConnections(out_edge):
+ index = conn.getTLLinkIndex()
+ if index >= 0:
+ self.node2init[node_id][index] = 'r'
+
+ # 연등교차로
+ for _, row in self.coord.iterrows():
+ node_id = row.node_id
+ inc_edge = row.inc_edge
+ out_edge = row.out_edge
+ if not (pd.isna(inc_edge) and pd.isna(out_edge)):
+ inc_edge = self.net.getEdge(inc_edge)
+ out_edge = self.net.getEdge(out_edge)
+ for conn in inc_edge.getConnections(out_edge):
+ index = conn.getTLLinkIndex()
+ if index >= 0:
+ self.node2init[node_id][index] = 'r'
+ # 유턴교차로
+ for _, row in self.uturn.iterrows():
+ node_id = row.child_id
+ inc_edge = row.inc_edge
+ out_edge = row.out_edge
+ if not (pd.isna(inc_edge) and pd.isna(out_edge)):
+ inc_edge = self.net.getEdge(inc_edge)
+ out_edge = self.net.getEdge(out_edge)
+ for conn in inc_edge.getConnections(out_edge):
+ index = conn.getTLLinkIndex()
+ if index >= 0:
+ self.node2init[node_id][index] = 'r'
+
+ # json 파일로 저장
+ with open(os.path.join(self.path_intermediates, 'node2init.json'), 'w') as file:
+ json.dump(self.node2init, file)
+
+ print('2-2. 비보호우회전(g)을 배정했습니다.')
+
+ # 2-3 신호배정
+ def assign_signals(self):
+ # assign signals on matching
+ self.matching['init_state'] = self.matching['node_id'].map(self.node2init)
+ self.matching['state'] = self.matching['init_state'].map(lambda x:''.join(x))
+ # matching의 각 행을 순회
+ for row in self.matching.itertuples(index=True):
+ node_id = row.node_id
+ move_no = row.move_no
+ inc_edge = row.inc_edge
+ out_edge = row.out_edge
+ state = copy.deepcopy(self.node2init)[node_id]
+
+ if move_no != 21:
+ inc_edge = self.net.getEdge(inc_edge)
+ out_edge = self.net.getEdge(out_edge)
+ for conn in inc_edge.getConnections(out_edge):
+ index = conn.getTLLinkIndex()
+ if index >= 0:
+ state[index] = 'G'
+ self.matching.at[row.Index, 'state'] = ''.join(state)
+
+ self.matching = self.matching.dropna(subset='state')
+ self.matching = self.matching.reset_index(drop=True)
+ self.matching = self.matching[['inter_no', 'node_id', 'move_no', 'state']]
+
+ # assign signals on match6
+
+ self.match6 = self.match6.reset_index(drop=True)
+ self.match6['init_state'] = self.match6['node_id'].map(self.node2init)
+ self.match6['state'] = self.match6['init_state'].map(lambda x:''.join(x))
+
+ # match6의 각 행을 순회
+ for i, row in self.match6.iterrows():
+ node_id = row.node_id
+ move_no = row.move_no
+ inc_edge = row.inc_edge
+ out_edge = row.out_edge
+ state = copy.deepcopy(self.node2init)[node_id]
+ if (pd.isna(inc_edge)) or (pd.isna(out_edge)):
+ continue
+ if (move_no != 21):
+ # print(i, node_id, move_no, ''.join(state))
+ inc_edge = self.net.getEdge(inc_edge)
+ out_edge = self.net.getEdge(out_edge)
+ for conn in inc_edge.getConnections(out_edge):
+ index = conn.getTLLinkIndex()
+ if index >= 0:
+ state[index] = 'G'
+ # print(i, node_id, move_no, index, ''.join(state))
+ self.match6.at[i, 'state'] = ''.join(state)
+
+ self.match6 = self.match6.dropna(subset='state')
+ self.match6 = self.match6.reset_index(drop=True)
+ self.match6 = self.match6[['inter_no', 'node_id', 'phase_no', 'ring_type', 'move_no', 'state']]
+ self.match6.to_csv(os.path.join(self.path_intermediates, 'match6.csv'))
+ self.matching.to_csv(os.path.join(self.path_intermediates, 'matching.csv'))
+ print('2-3. 직진 및 좌회전(G)을 배정했습니다.')
+
+ uid2uindex = {}
+ for uid in self.uturn_ids:
+ states = self.match6[self.match6.node_id==uid].state.unique()
+ for state in states:
+ if 'G' in state:
+ index = state.index('G')
+ uid2uindex[uid] = index
+ break
+
+ # json 파일로 저장
+ with open(os.path.join(self.path_intermediates, 'uid2uindex.json'), 'w') as file:
+ json.dump(uid2uindex, file)
+
+ # 2-4 node2num_cycles : A dictionary that maps a node_id to the number of cycles
+ def get_node2num_cycles(self):
+ Aplan = self.plan.copy()[['inter_no'] + [f'dura_A{j}' for j in range(1,9)] + ['cycle']]
+ grouped = Aplan.groupby('inter_no')
+ df = grouped.agg({'cycle': 'min'}).reset_index()
+ df = df.rename(columns={'cycle': 'min_cycle'})
+ df['num_cycle'] = 300 // df['min_cycle'] + 2
+ inter2num_cycles = dict(zip(df['inter_no'], df['num_cycle']))
+ node2num_cycles = {node_id : inter2num_cycles[self.node2inter[node_id]] for node_id in self.node_ids}
+ with open(os.path.join(self.path_intermediates,'node2num_cycles.json'), 'w') as file:
+ json.dump(node2num_cycles, file, indent=4)
+ print("2-2. node2num_cycles.json를 저장했습니다.")
+
+ # 3. 이슈사항 저장
+ def write_issues(self):
+ print('3. 이슈사항을 저장합니다.')
+ path_issues = os.path.join(self.path_results, "issues_preprocess_daily.txt")
+ with open(path_issues, "w", encoding="utf-8") as file:
+ for item in self.issues:
+ file.write(item + "\n")
+ if self.issues:
+ print("데이터 처리 중 발생한 특이사항은 다음과 같습니다. :")
+ for review in self.issues:
+ print(review)
+
+ def main(self):
+ # 1. 데이터 불러오기
+ self.load_data()
+ # 2. 중간산출물 만들기
+ self.get_intermediates()
+ # 3. 이슈사항 저장
+ self.write_issues()
+
+if __name__ == '__main__':
+ self = DailyPreprocessor()
+ self.main()