From 40d1c6824e71d232332063c1cdf44f96be4ce9ab Mon Sep 17 00:00:00 2001 From: govin08 Date: Mon, 5 Feb 2024 11:56:25 +0900 Subject: [PATCH] produces get_intermediates.py --- Analysis/0202_config_/config.ini | 8 + Analysis/0202_config_/config.json | 1 + Analysis/0202_config_/main.py | 34 + Data/networks/state_300.00.xml.gz | Bin 3759 -> 3757 bytes ...신호생성_단위테스트_내역서.hwp | Bin 0 -> 307712 bytes .../table_definition_v0.8.4.xlsx | Bin 75737 -> 75407 bytes Intermediates/histid/histid_1704389400.csv | 129 -- Intermediates/histid/histid_1704389700.csv | 141 --- Intermediates/histid/histid_1704390000.csv | 138 --- Intermediates/histid/histid_1704390300.csv | 129 -- Intermediates/histid/histid_1704390600.csv | 134 --- Intermediates/histid/histid_1704390900.csv | 129 -- Intermediates/histid/histid_1704391200.csv | 127 -- Intermediates/histid/histid_1704391500.csv | 132 -- Intermediates/histid/histid_1704391800.csv | 136 --- Intermediates/histid/histid_1704392100.csv | 135 --- Intermediates/histid/histid_1704392400.csv | 131 -- Intermediates/histid/histid_1704392700.csv | 134 --- Intermediates/histid/histid_1704393000.csv | 129 -- Intermediates/histid/histid_1704393300.csv | 127 -- Intermediates/histid/histid_1704393600.csv | 132 -- Intermediates/histid/histid_1704393900.csv | 141 --- Intermediates/histid/histid_1704394200.csv | 141 --- Intermediates/histid/histid_1704394500.csv | 132 -- Intermediates/histid/histid_1704394800.csv | 134 --- Intermediates/histid/histid_1704395100.csv | 134 --- Intermediates/histid/histid_1704395400.csv | 127 -- Intermediates/histid/histid_1704395700.csv | 127 -- Intermediates/histid/histid_1704396000.csv | 141 --- Intermediates/histid/histid_1704396300.csv | 141 --- Intermediates/histid/histid_1704396600.csv | 128 -- Intermediates/histid/histid_1704396900.csv | 134 --- Intermediates/histid/histid_1704397200.csv | 134 --- Intermediates/histid/histid_1704397500.csv | 132 -- Intermediates/histid/histid_1704397800.csv | 136 --- Intermediates/histid/histid_1704398100.csv | 136 --- Intermediates/histid/histid_1704398400.csv | 136 --- Intermediates/histid/histid_1704398700.csv | 132 -- Intermediates/histid/histid_1704399000.csv | 134 --- Intermediates/histid/histid_1704399300.csv | 134 --- Intermediates/histid/histid_1704399600.csv | 132 -- Intermediates/histid/histid_1704399900.csv | 132 -- Intermediates/histid/histid_1704400200.csv | 141 --- Intermediates/histid/histid_1704400500.csv | 136 --- Intermediates/histid/histid_1704400800.csv | 126 -- Intermediates/histid/histid_1704401100.csv | 133 --- Intermediates/histid/histid_1704401400.csv | 134 --- Intermediates/histid/histid_1704401700.csv | 132 -- Intermediates/histid/histid_1704402000.csv | 132 -- Intermediates/histid/histid_1704402300.csv | 141 --- Intermediates/histid/histid_1704402600.csv | 132 -- Intermediates/histid/histid_1704402900.csv | 127 -- Intermediates/histid/histid_1704403200.csv | 134 --- Intermediates/histid/histid_1704403500.csv | 134 --- Intermediates/histid/histid_1704403800.csv | 136 --- Intermediates/histid/histid_1704404100.csv | 132 -- Intermediates/histid/histid_1704404400.csv | 141 --- Intermediates/histid/histid_1704404700.csv | 141 --- Intermediates/histid/histid_1704405000.csv | 132 -- Intermediates/histid/histid_1704405300.csv | 127 -- Intermediates/histid/histid_1704405600.csv | 125 -- Intermediates/histid/histid_1704405900.csv | 134 --- Intermediates/histid/histid_1704406200.csv | 136 --- Intermediates/histid/histid_1704406500.csv | 132 -- Intermediates/histid/histid_1704406800.csv | 128 -- Intermediates/histid/histid_1704407100.csv | 118 -- Intermediates/histid/histid_1704407400.csv | 113 -- Intermediates/histid/histid_1704407700.csv | 119 -- Intermediates/histid/histid_1704408000.csv | 128 -- Intermediates/histid/histid_1704408300.csv | 122 -- Intermediates/histid/histid_1704408600.csv | 109 -- Intermediates/histid/histid_1704408900.csv | 112 -- Intermediates/histid/histid_1704409200.csv | 121 -- Intermediates/histid/histid_1704409500.csv | 118 -- Intermediates/histid/histid_1704409800.csv | 116 -- Intermediates/histid/histid_1704410100.csv | 123 -- Intermediates/histid/histid_1704410400.csv | 125 -- Intermediates/histid/histid_1704410700.csv | 128 -- Intermediates/histid/histid_1704411000.csv | 109 -- Intermediates/histid/histid_1704411300.csv | 102 -- Intermediates/histid/histid_1704411600.csv | 121 -- Intermediates/histid/histid_1704411900.csv | 558 ++++----- Intermediates/histid/histid_1704412200.csv | 562 ++++----- Intermediates/histid/histid_1704412500.csv | 117 -- Intermediates/histid/histid_1704412800.csv | 116 -- Intermediates/histid/histid_1704413100.csv | 119 -- Intermediates/histid/histid_1704413400.csv | 118 -- Intermediates/histid/histid_1704413700.csv | 111 -- Intermediates/histid/histid_1704414000.csv | 112 -- Intermediates/histid/histid_1704414300.csv | 114 -- Intermediates/histid/histid_1704414600.csv | 113 -- Intermediates/histid/histid_1704414900.csv | 105 -- Intermediates/histid/histid_1704415200.csv | 110 -- Intermediates/histid/histid_1704415500.csv | 120 -- Intermediates/histid/histid_1704415800.csv | 116 -- Intermediates/histid/histid_1704416100.csv | 109 -- Intermediates/histid/histid_1704416400.csv | 111 -- Intermediates/histid/histid_1704416700.csv | 112 -- Intermediates/histid/histid_1704417000.csv | 112 -- Intermediates/histid/histid_1704417300.csv | 108 -- Intermediates/histid/histid_1704417600.csv | 107 -- Intermediates/histid/histid_1704417900.csv | 115 -- Intermediates/histid/histid_1704418200.csv | 111 -- Intermediates/histid/histid_1704418500.csv | 111 -- Intermediates/histid/histid_1704418800.csv | 110 -- Intermediates/histid/histid_1704419100.csv | 107 -- Intermediates/histid/histid_1704419400.csv | 118 -- Intermediates/histid/histid_1704419700.csv | 112 -- Intermediates/histid/histid_1704420000.csv | 102 -- Intermediates/histid/histid_1704420300.csv | 116 -- Intermediates/histid/histid_1704420600.csv | 119 -- Intermediates/histid/histid_1704420900.csv | 111 -- Intermediates/histid/histid_1704421200.csv | 113 -- Intermediates/histid/histid_1704421500.csv | 108 -- Intermediates/histid/histid_1704421800.csv | 107 -- Intermediates/histid/histid_1704422100.csv | 120 -- Intermediates/histid/histid_1704422400.csv | 114 -- Intermediates/histid/histid_1704422700.csv | 102 -- Intermediates/histid/histid_1704423000.csv | 116 -- Intermediates/histid/histid_1704423300.csv | 117 -- Intermediates/histid/histid_1704423600.csv | 104 -- Intermediates/histid/histid_1704423900.csv | 111 -- Intermediates/histid/histid_1704424200.csv | 115 -- Intermediates/histid/histid_1704424500.csv | 111 -- Intermediates/histid/histid_1704424800.csv | 115 -- Intermediates/histid/histid_1704425100.csv | 110 -- Intermediates/histid/histid_1704425400.csv | 107 -- Intermediates/histid/histid_1704425700.csv | 118 -- Intermediates/histid/histid_1704426000.csv | 112 -- Intermediates/histid/histid_1704426300.csv | 106 -- Intermediates/histid/histid_1704426600.csv | 116 -- Intermediates/histid/histid_1704426900.csv | 115 -- Intermediates/histid/histid_1704427200.csv | 111 -- Intermediates/histid/histid_1704427500.csv | 115 -- Intermediates/histid/histid_1704427800.csv | 110 -- Intermediates/histid/histid_1704428100.csv | 105 -- Intermediates/histid/histid_1704428400.csv | 118 -- Intermediates/histid/histid_1704428700.csv | 114 -- Intermediates/histid/histid_1704429000.csv | 102 -- Intermediates/histid/histid_1704429300.csv | 111 -- Intermediates/histid/histid_1704429600.csv | 124 -- Intermediates/histid/histid_1704429900.csv | 106 -- Intermediates/histid/histid_1704430200.csv | 91 -- Intermediates/histid/histid_1704430500.csv | 107 -- Intermediates/histid/histid_1704430800.csv | 109 -- Intermediates/histid/histid_1704431100.csv | 116 -- Intermediates/histid/histid_1704431400.csv | 119 -- Intermediates/histid/histid_1704431700.csv | 107 -- Intermediates/histid/histid_1704432000.csv | 111 -- Intermediates/histid/histid_1704432300.csv | 118 -- Intermediates/histid/histid_1704432600.csv | 107 -- Intermediates/histid/histid_1704432900.csv | 110 -- Intermediates/histid/histid_1704433200.csv | 116 -- Intermediates/histid/histid_1704433500.csv | 106 -- Intermediates/histid/histid_1704433800.csv | 112 -- Intermediates/histid/histid_1704434100.csv | 119 -- Intermediates/histid/histid_1704434400.csv | 106 -- Intermediates/histid/histid_1704434700.csv | 111 -- Intermediates/histid/histid_1704435000.csv | 119 -- Intermediates/histid/histid_1704435300.csv | 105 -- Intermediates/histid/histid_1704435600.csv | 109 -- Intermediates/histid/histid_1704435900.csv | 118 -- Intermediates/histid/histid_1704436200.csv | 111 -- Intermediates/histid/histid_1704436500.csv | 112 -- Intermediates/histid/histid_1704436800.csv | 119 -- Intermediates/histid/histid_1704437100.csv | 106 -- Intermediates/histid/histid_1704437400.csv | 111 -- Intermediates/histid/histid_1704437700.csv | 117 -- Intermediates/histid/histid_1704438000.csv | 105 -- Intermediates/histid/histid_1704438300.csv | 111 -- Intermediates/histid/histid_1704438600.csv | 118 -- Intermediates/histid/histid_1704440400.csv | 211 ++++ Results/issues_intermediates.txt | 1 + Script/generate_signals.ipynb | 651 +++++++++- Script/get_intermediates.py | 587 +++++++++ Script/get_signals.py | 587 +++++++++ Script/preprocess.ipynb | 1062 +++++++++-------- Script/preprocess_5min.ipynb | 1004 ++++++++++++++++ Script/preprocess_daily.ipynb | 690 +++++++++++ Script/preprocess_daily.py | 431 +++++++ Script/preprocess_daily_1.py | 587 +++++++++ 181 files changed, 5793 insertions(+), 20773 deletions(-) create mode 100644 Analysis/0202_config_/config.ini create mode 100644 Analysis/0202_config_/config.json create mode 100644 Analysis/0202_config_/main.py create mode 100644 Documents/0202_unit_test/신호생성_단위테스트_내역서.hwp create mode 100644 Results/issues_intermediates.txt create mode 100644 Script/get_intermediates.py create mode 100644 Script/get_signals.py create mode 100644 Script/preprocess_5min.ipynb create mode 100644 Script/preprocess_daily.ipynb create mode 100644 Script/preprocess_daily.py create mode 100644 Script/preprocess_daily_1.py diff --git a/Analysis/0202_config_/config.ini b/Analysis/0202_config_/config.ini new file mode 100644 index 000000000..8ed11c80e --- /dev/null +++ b/Analysis/0202_config_/config.ini @@ -0,0 +1,8 @@ +[DEFAULT] +user = example_user +password = example_password + +[MySQL] +host = example_host +database = example_database + diff --git a/Analysis/0202_config_/config.json b/Analysis/0202_config_/config.json new file mode 100644 index 000000000..bfa332081 --- /dev/null +++ b/Analysis/0202_config_/config.json @@ -0,0 +1 @@ +{"user": "example_user", "password": "example_password", "host": "example_host", "database": "example_database"} \ No newline at end of file diff --git a/Analysis/0202_config_/main.py b/Analysis/0202_config_/main.py new file mode 100644 index 000000000..6a5258de3 --- /dev/null +++ b/Analysis/0202_config_/main.py @@ -0,0 +1,34 @@ +import os +import sys +from pathlib import Path + + +if __name__ == '__main__': + + # 현재 .py이 위치한 디렉토리 + script_dir = os.path.dirname(os.path.abspath(__file__)) + script_dir = Path(script_dir) + + # json 예시 + import json + + with open(script_dir / 'config.json', 'r') as config_file: + config = json.load(config_file) + + print('==== json 예시 ====') + print(config) + print() + + # ini 예시 + from configparser import ConfigParser + + config = ConfigParser() + config.read(script_dir / 'config.ini') + + user = config['DEFAULT']['User'] + password = config['DEFAULT']['Password'] + host = config['MySQL']['Host'] + database = config['MySQL']['Database'] + + print('==== ini 예시 ====') + print(user, password, host, database) diff --git a/Data/networks/state_300.00.xml.gz b/Data/networks/state_300.00.xml.gz index 609831f29240f31ad407f248c81bd88435f231db..4ea8b34256d717611a130680a4c88535ab1a5afb 100644 GIT binary patch delta 227 zcmV<90383X9jzUIABzY800000018cwJ5R$f6ovQvii>wY%TQTKJD?UJwJ?+*R%2gM zOUZQ+PDGJ=i1OGgkybpfyep@-mY$3&_WkNWu=Ss8JhFr`K8M{ zB{Qc`vC}yAM#^cL#B?j-(8ydVE#D9OqdQ?|d7v~}a>!uW8;Z}i!_-@c*M1+}#rbDS f=PnB04#K0HlfUHc|97!aw$6S4$$YdXvDwrO?N4n1 diff --git a/Documents/0202_unit_test/신호생성_단위테스트_내역서.hwp b/Documents/0202_unit_test/신호생성_단위테스트_내역서.hwp new file mode 100644 index 0000000000000000000000000000000000000000..a77bba50b430550b6cb9feef5cf9cc400f9b107f GIT binary patch literal 307712 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%0D0GU2)DlMjy)*AHxHv zM>TVzm_xf7y6-5BM5@65szYXiE6%~r{C_F5J=A-MGYp@ioiRV)P!MFc)Ojz_hDZ^N zuL970u+sbV`v90tiVRS8evS_>_bV1cgG`yLdl7p$X|@yw6oxlpwCyx_5Stbh-yv5& z1{t%Tz@ZkDFqHunPQSQp;J(nxj&^%iC^cOjbKJ*GWmWXm%YjG$#siZ9qE275 zwR$Ys{fGN6gsB5#s2xzw0^J<03styL*fOjZkt z=9bnY^*^J@0@sli1Nfe&e(E?vJ6w`!fv*kiR}!HTFB5`VVtRpnw8H$t{7DKF{s&P%ll+F_^+nm4cwqDzNm+lgT?FZJYoU(BF`9pqg_+8_GU zY;0@go_p>6*;&0q`Sm5jjpAT|7MFL!wRE=sK8K&nO$Ame2b2nXK$+u#jugp9#4mL3 zyXL6yT&np#%h1+0;Df9EAA=om_WltZLoi+G|9}8!m>#Zn3cWJuuioXdBbU|=e~w)j zy?zoDv2%@_<{}deG43EupW+HHL#UwtIph#j z>%7zsHHTrjXB8P11@}FSDiAc-*}Cg^9D9+{%L0H#9W19fU3UD)uT@o-K3{sG-euDu zb_H#9i3ieh8qRUZX(qX$Lbi{d4B@dqGeWwgjuRV58PsN4JIoOJnd2=rF&^>Cu7<@m z^~9!5`<$rdAlYP|Lk>$!Sv?hyAphkyGN{dPiTOzyQA^a1Vl=n*JP%XaRG8_Vzhc;6s9S7E4@%;}`V>$!J1&h-jX54?$ahwmGk_>9$MPatMj za5b^UJr^g)IpuX_EP2>-Rn%FJGe^{AWD2A+&cpo%^$D%Q`!Aiguh)4xaKfBvrQ%u+ zb{bIqSG+4k1zMMWC3da1u9Jy{|GtL%5Rp8lL>pkgNC!hng6RqLcH7tAb9hm+A zXOm^7DuVsI^S#?>e4EG|g5_0`txU_-+zWb&SC9;DedtU!R3IE*s8DhNEv$nbpzU3q zEtIT&g`#wYGu`R8_M7%A1h(&Zn8yVAkojnN{RrqOZEoAr%RoX%4lJ(iody(d&fj@A z^t`;v5b@HoY`RZ(_uDi^MLn~vKEF(#yf>gLj3~*IuE<{GP=a55f%!u{WpUwp=#e2;`OOGuqh7oG`MqRmexL zBu)60g7byo<0hrXR}MHmLsBtY^^hpkE95mqATr+fZ7?zBJRnC7MN*3ATT#{SI2}t- z#KJin&9pgvb311v-c;~bMaAzHY5~8I#cIJBFPK8=be2PI(~T*doQKeBnLb~3P;N4E z9j}+w1#!iygTN}4a6`S1+f)-scz8pVt&rcWwK}ynKqWngU0q?Ue@D_4e#TpIxq=zF zj#zQ|79!fKRO8jD$#Y}B>eqN-scWbwI7A$iZOLA%+0zEN+`6M9+Lfc8#KyPp0=p^i zz3dt@cIw{F=(UkyCw)bHR`IJ^yJ&?OHm5xbNp$

8=+$G^c2%!6J2_`fU>B=t;!8 z^kUgt{~b^F?3eN~r!-#!R5LH%QKmK!ymd;Gi%T!WmOraArz|O%G1$+isuTZzjJ*X^ z6lHrU-zl6+-_GBDTm7mcaeyvr1i?H@;%oO!!IzgQM1!Xh3_i%HaVD7*0b}vA0lz9XHoS~+CL33R5>5Drq@M~ z677a4<`r4#oFT9*6exSN6vO8~a^?zh&}jDJK!>{oIx`y1ox2|tQy+Dzd^mq8G*-uJ zWh%;3&S0ez@7MpkKQj-aMRy?oF!fQo%TK9jnUCiRDu>!OD|%ZMyBz;HUGA_@zERM()JC)0|VoaCH)8!h(Yc4( z2|0>@=V_>dPvJI?ccX$)PZ=P_E~|JjuY4u<`49{{T=N}|KZ}$rvwF((@kJ)sN4%a| zu9CDe(z_%|-CAtx`kw!HRSUy>tEe8O%u4+=*KpbUaE?Uaeu@1< z4!%bt5zsg_>C|3$AHl9iUB-KEA?9T zH*pI2Qj{jO7aBdi)^1NzT^K<7%myVe`ywKSd63<)lyb<4=7E_94akG0;m2)?2cYlb z1ExCyOLSz2jKHG;#wF1O0=>EY;>4*rYI^ShC7|kXm*?J&{3{#|?broWjNKKk5&bWr z;ZH>UPqo&<{qX-MDi3O)fl?lFzuX-#_}4iesNII#n~?xp_D@%^TdFWr1#qYRs|cPh zD7qRk=~AUK4iUuF$pCZtui@`zJ6f4rrpF{eG5N#v@B#w;x5o8W$@w$NH=Z z9#j#nGX0lT|4$n*8Sm!n+csd}P%lVbXaXqG_^(R@Z0EbK(%4+>sj125{x2cZ1Uj%F z4M|{-cW1sM{^AY7c10lrq@3dh7vqm_T_&F`n`&2>Y0bF&|yYjN^ zB9wE$E`wgHf+C#nU3YPt=?awhbJNpre|*O$FL{XKNQ-P8sNQq{x#Wg&lL&{(+d6g&m39Yec<@tT%m7*_gtZ7LN~n9` z_t6{N3f!EnLeLrGA4jfkR_e~>yUU=ogQLlBTZsRmiRXQQR4oHf#@|lY|J_ES(c9a| z?5R|Fe`460KS%!SnZ0}dkNVVZa_?;Z>(zi*00Pn-^GQeJ{%ZbfBE8`!ZYT1xCpWmj z6$M22ug>cqD*In&hW8l1pUD3j)+wBQnK6=m}6FvtX0l9e!j7aW%p7L1lqzs^AH3<3xf~q0CTEfbS#C8#X zh)}!NQg=Pwh6JG<3WSE}3!a3J!XHA+Ifo3Vu6b>d*nqKJ7qSo@n5k#%W8z{kvW zH3}F;ZlrRqwpox|ism=}wlc_d?EA`TlE5IN!%Bjq#|<}r%u-}E>6 z96KS)N0hbu6-&e$IHq%P*Nd~lgx>=Z=iNi=f`8Kx2N|UGey$%@-@UsO)jp^gKk#m| zgNpI5?*mt|kGQzp;WJN|j6EDZ+@9}-?anf+Gwr38vOd$rO(@jWL$uPgH-Di(kqhXD zEmNl{*)Ki_JZ^&a#8So@j*LA}5_*vCn4}K&|53(B4f}wSpUjAa?YQXJ!{zm-0ZAp+ z{iSre?YSsQjWzy3wjdT`J2GlvTXZ6U<8ulu?JHYj4zXN%Jt7g^CS~ zyS}LAKQ?<9XIb%GZzkoNm=e1%F`1EJHJo%nq58&4r=wx`OZ$GTaWa(@wyaWKd#RpX z>9(~e?=YhecBM`buNJgJnB0`vmb2<>O+;v~<}i>}nQv!c7Kk!K9R=$6PoIcz&(9Uo`W76!$`6H*b+IitI4<3>*)HT+*3$PESma=5AX_heD*E?&)tdy z9wt!=nj+$gFfs##>KSfx-S?QCU)LLh^Aw4viTqe5a_B+Z17CunX&(UhE3ImOX_ zZv)-%J3oB%BC#icV73i5?vh8xRvFJ`=bAlaoZ>?W?xeqdH7{s@|0=j0#@6UzVr|64 z_6bi0rm4tS!5aKbK%>itgq?7zGvd;5))lum@_0REj@tRtxr{&`cKRM%w3P5cx@jeFr6w}rTUzsB9iV<#O9p$uyEyc{yn8# zF9Vt38Yxsk0N|6~8(`5A=-|X?GFo-YXK=4e;CsD~X)wqzFo1L%Q1FNx)nKrhK7mvW zMn(Cy&SfG_M<|&k2kl)IUsr54=`_bTAS(Z<3+4^EZ$2+VnP7yPUx};0SMKHaG9w(@ zbyZe?{9vGwy4;9ikVk)eL?>Fp5?h2p+Pb~3;Q3zJ`F^Gm{55^5bfOFW0 zQ3umgf~D9%V)qj>N+{}3ivMwZqd|q>IA&ty%fZjv(fin*tddOLo}U7uev_^rOCce; zY{dCW(nl4}JeN8UL*&Qdp$fKER@T_oBRX#yD3gh1FKt$lplJAUK0rcabG6P4Zg|A5 z{%j`>QxBa!yvKJQ87pBBWB5pC-m+%S4fXyJx-TXM%B_K7jnCjHjNuWM);ORPbU2a{ zZcdR>ot8QUM<`?U4MNxRYrf8iaoLekRu9rG_?*~r4Q6tGH7Mn!2n?kgOCc4teFfvt zxB56zz;!oczA1wH++sW$)99H;R4b^{fQ4OL{_Xi#Z?BfP@*E*{DL$*+#=e*L4l=dW zZ&S}t@_fF}>4~|G5b@L3-IAW<;%y97JxgbQ$EVCzK|jRI*_k!6r~8myos2dQ^P>68 z3!N=nH?3Lb#cs0bE9Iw^48NN8QALasH!tRi{I))E|8P~(B~Eld?AA0q(IKZfO0HH) zu5hf9MK!iBIBas&JUV6?z9|)p54j4qA08a+jQ2DZM{4NVNyhMDSzEjO`blg!Rg}^U zS8Y8L&n}dOu;qObMtt0y?koA=Yb9%1Y+@PXMbqO>c_~Q@zX6x?MHS`Wc4K)g!&%)* z209*}SLU_pRyS?}7!Xb-V(M`>xefgXMpZ8+opTEzD^ELjnNunvxfpV19Uj6K8+?LK zL0FfGxon$n-fWKS zLpilRyC~a~4B|4_E)pTOJ|g;^=?Rlxiua<5x5u42s+mN?S$oPVtTb)&JQt`4b4O1J z2-e;DY5VL*^UchY)?SZj^l<8(og-HbmlB}=eA3Y0&&`A6wKPYfzx`Cb-BDl9w5ayk z`2ORsZ199bQOuGD(XfL~1rU;RhQW*NwmPeZdRaqk zF6$qxzCjGBKXVzIx>cWM2V!*ZDUyJRF?Tdj~ zp=OaAn_E#%@$KWu`>|Pc<>02k3bbI@Ot(-aeU)*SP=~G{p@nd?C z`f<-b#TV^v_O1^|CHJ8++N)%Fy~RGBAE0nJB4oO~kh_k{`>=?CLo5L0#oM06q(8;; z;$&)iIxA#-Z7q$*2~`3UPT)BwC(IQtHnuk%0v@0JYS;e$+MR(M4v0JeGEP)Pa#l3R zz(@XL82v}RqsN`7#l5n|-?AC^YDLZTK&1cjURLAQ3Hn<=wI8;Ik+(V()N=lAEF(o-SU&G4IPJ_)t z6HwIvxIJQ2o={15z`DI1D-id(Z4=LLjAPtIxf99B#Rh^a0b^?MyiPYCE8BTObM*=4 z2N@*--1W<3J4>vDRH8u6?x!lQd(w0hNcR2xwWXfTK*#j`xG-A`%)HngFGhQ8Y;31} z|Ia`qq295NK&C!J0gGY>mkfJ^gMyg^1CsY-gzT3`K6Vt|w4PtA5MjDD8WkW&eJJO? zk73-%;b(b9r+E`iRv8lqQ8)H0Hx55+wLVjEj-~uI5;cm%h-1Ft&{d5a9!KMyGc`Xr zvJo>I9n4xb5!_oagfMmVg#vVL>YZakGiq$3f%^x0jM)yhdSXI@<98|df$ z+(OtudzvK}8aQ3i#Ub-3jDTQIYcZ*yJ zd4?Iytr&wTJuU^+U4bTj>Y2|!24RqZ17=|XH&)y8$>b_Y0b`?Lpn&S|g36VJRti?+gCA4xR{8Yk zPmuA5;T>|nYQ?cwcf~z)7Jpe3?LXcfT-Fs$%(;VUlxg^e%8uqo?N1*D`9;QLy_(@W zSM8n&9Q~*qT5xAl{J8}iy*Y#giHR)uY2)`$TwYmGL5^dxNOGNvsex~`gy&5<@qmeI z0_QO9WHmEBN8+UYYJ?+#N@oeX2BDwsTZJAG>XTiw72iOrVteagQ-3oSmNQy;2$N*^ z`R`&egeGCueWhIC=cmkp+#l&K;N9AX&ZKPi5?`0NK6}N@t%+syRK@rg*F$TRnzy70 zEc+!u-z~#Sa%XD(oEfztg&11s6-t zX)USz{FsN*w8$|3?YGZ_O`r+<665RlRB7|>msKA}(PH^Szs9336y<&s`5hvgny!6< z<`uo=!pg-VqtKQXJJK`xsLPP=I5+}NZc&)Mrb@d0*T7VWL+n(=MLD?K6n&LEjZdA> zC_jZx={;^Swb=s*gq?KO@neM;W)w;of8 zU-dP*A=B-oPrb0%ZP_BIW(xo0M(crn6B{Vbyer20=+h5WXR12lS+l)P^R*Y!YWBA5 zo^%T^YH6{%c^a!Q50KM32nVp>;S9-9+@gY5jkQ&aIEy6c5{x97(4{CCBSiWm-`1aD zxH?Rsl#s!mEt8`M{g#!Aaq>br-QOLeW2i%4?zK6T2^XizqpL2jHxf^nu*-OumHr)f z;d6g7T$)|Kx<%B^N*2M>(P|vNZyu%(MN@DoI!yvRbdutr7s@4LD0qh*yAPyl64j~yS5`Vl5*x*4Dn_Ok--@?)5`@|{PdkV zYjuLlKrz0!=G=)s`nRq`i0dgPNRK-Te8XK3lEZ5W%_+mw@=95#b!AB*(UbBx5_Snz zZ@Z0s!!=ntD)(A01f9>D*6Tqn4%$_#v9D-9q$()0_C-y$40FiN;-h9@PSz_AQ?uA` zkGVMvtsVc|y@+YT+yZ(LPQ@9DX=hCy<(Kje+3N15e&fShO63hXCa|N}ZOw!rAKAA% z8zzR14H*+8KV!oAW;XWrCGXDkOk7PaT36`}JZboTN1>v=GpS+FQnW#7?&q9>OvcsN zfv>w#=NinTyxZq@j0hm|0@PcG78!oHoiMBlL7dqd+vRUQJp>g3iS06AymD4C_B~xZ zMAEe*{M?8^jZUJX%54Lw%^7)%Tp}&IfqLu@@>064d-BBVv)MPvmIj33NmCr+%cIGWU+mwRP5(!S zaRgozIEeaq^oTL@A5Ha*t{J!=A|n{+=IfYSC6q`n`UA9>;LlNsMgaMO-?-py?pPZ? zXc4Ge^vbP%@^2Q?KUIrovEn!*x2zL0n#n)W(*HEB{=`6QnEu2-&!_*1fkFu~_lg=I zQrMu$o%%1ZppS>N`T#qn8R=i+i+@<;3V*T4?e9&%|2%WE-5rhQ|IVDGiQ@HVlz(aL z{SkEoO+p1fUVa_Ed(r-F0N?dV1ziGNhnm0N{rBJ0{kB1eTk3w(odpd_zP|;6FaYNT zjx|`!)nH9VMBAf_kLh0v?%?U zdI~5&_NV#i7ZDu_6=nMdCrXGb_f$`2*};C%W2rSh{0%co!A#d{sScv05FJzsA>?^? zYb@poLItVzgR(B-Y{V9XzSWGpD*E%;>`_hVl>m$VZYD>#@(J+Y%u0D}F!ian2 z3pOM3KFaI4xUSWF1-#UUY%|0ciQcgTQVS+7B!w)%|HmrH!r+iYFOl(LAH~WxClfA z>5((pzR?o6^7(_Ay5^=0uijJR1Tc5^+8Is}XJY@}WDTroh8uHN>WK8CnJ0ypDxSkm z=tmz(w;rx5e#DqJ&J{tVBc18#Tl&`Br@)-3l}ffpb}#*Wdb}gasVBUw?RM( z9i|w((f4)1>&A{r8ulBJg_ZWid{!kUBaHPcY*r|Tg4hoGvI!Qwwsv1mMEXg2Jno2H zVo`N7S`(A-3mzyd=Equ>2HAAfe0(#Jc(!Vk%oFj4Pr)8+F}z3op_JP&r-rte8H}{B zX+C6^Fa(*JF(cRs2rZQ2X0z2s{1{G^R=l!`3#STFHcC?OS02*)<;9u3q%}-Yt@6%rzIvfB@002uPc`V$evE8ssW7m*tbMJF z{>uE*iDXKKfpyQOJFw>S>$gLrf4BnmDE?reT&@L{v85f=QiO%$IZHVl35$9O=zTak z-V$mZ5}M!64cEY4W7nLM$$W4B!0DF&YRPYQ9-H!b0q;Bw)t}2gFknO%W?bRgt&50X z4Js!_d%0cxX^tA)dI5GaHu zfwGg_NiuldJG3~{^UCgdtWhmgbVd-HL)K3n<-rKX7Dt)F)J>exWa*=x-w|B-Dg_qF zml6d^Puj@j>|ZAY>P4c(PN7=7h<9tno&&qgP5gFL!JXB|@R~wxTA9SF$~s6Xov&cQ2tlDZorhX=ZRma(Cy|#M0#p6fdDrdG7rD)-GDO zwgxwJgS1=jN}ie8BA8qVnXmgx%8^-88KYu+@pdtEiY7L@@9^{XkU zrzN*bdcpP$&iv%*RH^JSA3@AzntY_22QP0FN`$7W5u(oW&!-a6ZLyo%?l;wMx-C(< zInu~CaZIUDR2aCmCY1TSRrE=jFXTj1;St-X6`|>FbHCYpT;#V;VuMK(zH65qwZsp%TiDSo~nk zBdtWKg3Dw&@sX6*Q-ZUv;2gc#EPXDzEZw?-0jAocSas3d-Y+KFvBP$xMU_IxIvE(R zrArf^5i)E5Y;gN)7orAUWNCj^>kf~;a>@r!rzj&wg6w3@D1W}3t1K*cV)1K%6~RL z@nq)I<9Y{*8vP7#Zk~8x*chn`Ayo~VjBtpxM`93p5Jb$qVGXI;Fk>b%1d9W;~(7iDC!DtF{fEKFvy>ctxQ^<5j2$RPnc z@67}4iGz$>x?h7gGm~=mtdO}gQ;#nj9bd24f)@g>P8+*KaxamXxBcjN8?OS#9 zG%gCM6nw+E9#IW}Qy+WHo8{M`(dr+g?u#|z`!Kx8(E}3wWhZ@r3V&|3R^SbbMu=E8 z?5p&bTS4-EJF$0MSpFI zgI+t)dsb?s;kU&`qPIRP|( z`-f}6v9l~meQXZ<)fk{JZ0MN5(BJ29|J(Arl~Y4qZ2FTgmSngZy&p{`VB-O`kW7My zHz8Wy_v`>bZyt{opBcvc;+`D<*kQMF9(QVE7|R+US}5|r-bY1G5qOs- zhJR<%ud4830&KA#)MT~rRA?k{`il=ONxc)_M@7LMgV2#nSxSz4YU<;C`dV~?6C-`d-HKh2OPL?Co?9{}3iK`c*nKZIKRpFHbFm61 zgRGE|hRu)C?GGxEHx}qu9-l-gOo>L?^js>7ZVr(8V6gmRT7QHVZJ}%BCfjpqlDn|s zo`eID3BK@df|uiGQjb@jZ~~Nnt=KsUDH(K0pftw45A?Uv00KY zwUhQm(<|F=3jTU(%400(5-Af8(9fHgLtzz9)^b%>=Dv(IJRAuGGQ(`bDhm%{*glyE z?v&MoQT&j=F0{|*Bi?A}Ni<0NFKu;f&a3$4zu=FgCftrgf3>#2keaQ^_B-9+P-_k& zTe>3hrVFko?fo4vI(W;byi=TUI1GEGp8~&6X_W=UI@a(QjXUbpivRQFf59_oqlrUD8g^g^Q;3Q0$qiqHRD{ z8y4Gde~NY$#lmWMCWoX1{eq=quB(_QNUCG_qf$y<_ZP00QEI1y0yx^&8_KeWNRcd2 zte(am9&%Z<`Y%)iIyA|d;oY^Sa+2(RO>+Kt;m-64B89lhf-XDqd7EdzL3<)8&Fz4f zjA-_zh4phv=juCE``ork895>WRBrhQvRo2Yu}F8PS$@%)=r6QR6mA5*bYZCuG`Ri1 z$w-T14at~Q>p~b1w*4lH;7(ma?h?Gv8g5|S7)HMYLU$j@NmyWUN%s309;}AIqpiYx zz#LVi%A`g4mRV$Ky4TV+S(@r#nebyT69O?ysu=(MO(0^01>JLj#K0eWcOlP=y#0lX z@2(}7_zAHQ2UI({uDB&NmhEk@S8E)2q!B3(h}rJL>KrdUC!)^jkBTccaJ$fsXZ=VA zri(^s-@hZ>{;?2VWtCtAb0Jlblhpk%B{0@~U&JWzDa(R;4TIY@*l|m&lRVv5Th^FJ z^E;ugi6>;fScJ$vKEt*M?P%8`wv+9?alKo%u%#NNJ` z^5i)r8;qaJBV}>9ipgu;m0w$yTwMwme911}HcOhW3UCX6b^7*I>DZtn8QowC(yAmqJIF4k57LHv(#M2$~L$4_!5BLT|KV=0t@@a(Dqw_OYvRyTb_GZj~ryHkxnN-wCOp3K zzdKFg01>QkrM;kdm#H&;sdR+2*nuh15XpoPU$QHw^5Mk#k?A#r>iiZr88;tDr(@3{OR4>zOxMg-cmR z%g#v;!D(yr&6X&apo?yULHMK-6XU{2S+l!!sS7q*aumALx}n5cb}Z(HhVB~)fhGaYr zctD={Uhxe~CWr%0m24^UzgSZ~NJ7Q{>4x<3-Lf5<{;(a-q(CJt_XUNeD9z+}qCh34 z6RI6$kS!L&i;~KcE8R>F+YE^vSRgnfw3>=BFRDZbv-rUu4Cfsb&PoAKslV1#SeX}_ z;viLEWNcpK(0`S3`X~qnw69u6^^MDBa6GBf*+;`blXa1F31)M782n;9l2i6ZM-T0`OCq#r94syyqVp!igjq|_J^I9Xdx zG6V0!f5@jZ0aS4__O!CJcW^~)*QG&4<}{AUpi*0-9DN08Hk=RZ`5|mhF7H?*81(z=CK?zvSRr8JyYftx(j-e_6GzOK zpiwGE?HA~6yWrS7x=#!}hXM?>Di%xDhZhW}u^Qo4b){FHg!pZ`$XF>Pmw4|9RYqY# zHCWGEc1WMU;(M5SguHlEP<+ik#m)GdhfND%7sta=iR&X7k_nBrp~uI#(U7VAgCpGC z`|10`FVlsmC(uQuj-*xh_NDviO{u_RY8sW10nmsxBVUVQ42&*kJAE>;hI#Fe*Jr?=|ffQQ|932dr;?1RpOSylwW z*Lb26PD03K?*WlIMIT1>%O2xjS;E%-2MYym8D7@53J4la9<-AE1h;_Q|^dHSSkrjYmo|gAEV9Oq1(kn>iTffXC zg(0AYZR+Rw=*z68^M;umC0)|&O-3v#MZ6K~H%s1%@evNY{+X(0d1_Syew27q-(iDE zJ(1&N(^YDzjOINLPVm_d^k19f{(62*i@Eash?>V{N;id3Ss}HCSQ5{fw5EqLY|CSU zLECt3y)pVh0gWm|6}4Yb>g$bspeh7j#lOm7bvw@YCFS+kyI}H;Y734ZId%muZzdAS z!QKpysV}1I@57ZK-?HvLL#BB*R`#yuc25-7=AV?+f%_uw!rjsUR%)l+DLOgJw~dqw z&O!HqWO$AVryr?hLHa*MaoEt|_R!P0DGAtfV)yNJu<5m1Rs~j7kb1ozOE#5~2ILU3 zEOE#x#D2N>tdn|3u5Xw2iY*2~+hu5I{%7pd37{ch@KaM#1@t966T%WFqS*Nw`OkQT z_+pKLujV@s-Pct*_NCY&6SSN}>7y^5`46SUc*HrycY)gOcUhQ$qoO~IB`Qz(GAz^y z=t5IPueOx7)uG3k5tlzFfT6; z1*3Sd^TmYXQ;AgZ6~MEFYE>~uwWSx=xIm4b!R1FBp^fw~J)(Fc-`j`j=Ee{B*B?Lt zfIt920D=KPb&8<9tWU!V~H(1^_#fN=m5044!U0hk6b17H@w8~~{Ev;bfc zz!HFE04o4i0sH{424Ee)27pZf&{yd;fE@ro0YEpvJplUv4geeiI0A4C;1_@s0H*-X z0Q?4U4&Va7C4egc*8pw+fPm{hm|xha3gVcl;$An$osbA%{)CXGWJe$&?GL^T{K3Jp zK=C-19S_jbxAk+lh9~o2Q`pZ8V(UGUVISpsL3Lc0|U`lo5p zOUE}}j=b&^ao`?YYH{m>#xZf)lqqUe752?bU8nFMwt5)_hNRXkStpH|xScpN8`WrS zGkF`NyEo}=;XyC;t)iTO&7Iz!$C+V98d#_ck-=&#K4X>vW~TL}j%{ob9+Y6zO^Zd< zBQECY)Zg;PqR6^IbJ#*};S}5V)QkUnB%Xq>uLZSS4i9X5*tLITE!78Gu{~OOJIgRS zE?jA^G$z56h%(BYUaEvRrhFD@I?q>CiluEHOsq~xR-ssQC2(vCMh@?TMfxm0al6uiTz^$RHEu4CiipPlSHx(D2dZn4zA9A{9 zw18lZ!RY#_CUCei_ubzRf$ndw6_v?EvrX~dXUTJfiVe{|{OgxULFfYdW1Atvl_U~h z)9Cy9WlQ2$q{8yWi2(bBmfHOx(DF27xQv9_E2?SXDG6{s0?3sq1Ri9q`M;mB44d{u zdC&~FvEC0PsrLqFbhkPN1yO+0!_d$U>;!*C`1c|33$3~+<8m;d z#ZurHCbZiD^aEj7fUa4}7}LLRVDxSjfj)phjg80Avkn-bx3V(-MKASlk89wxx1$Bl z2!hSv{Pj*>K&TW@!c`vIgZpQ?pq~>d$;$V6KV2#V=5=>;KFOC0j{WzLwvds?>#D<~=}ll*mq{@g7X zA#`+fwGK2?R7OU|jlDEC=@FH=S~zijm)%bV&hq+;5N+))M9OS8a2tC()&;E`9Aax~ zY7K1WIVp?(i0x)kKWM8Fq!eGZ-M@HDHqh$a9dk3LG($0qC2eKBkeHqKkc~(in4{aZ zAczCP#zWMTob{TD9a^NpZWaX5847}S=k-jQK)ehz+?-|Oj26?1KTjAKD;~}`UjPdO zn7*Fdo2;M=thc3LA0L=PS3yCafB=M4GIkrYX$Y^&Xq`)|sLIbRp$EjG$Kqzq!8PUV zRuic$Yb;Ti%7NQAaRgreVIFGc4j28ToMYH;c?=Yjw}7g^=i0Sdd4T(+@hOaY#9RIn zhdWgKKi4rWB^GdUVDNv>p9o=lbF;T{LqBC=m@$AA?#d2Poo|+y1;Ft z&r7vnOjCry8Gb~C!MSkQ$jnDtpmAdj5D7k}8;Ka_A>Iy5YAg4XNcM``j&M z9)fFQ1z9ihoX2q79Qlwb6K(SXT{_;L_DAV>KqYgl%y0sR*{Sn-lAM90gC|K(Z_S&F zxk~2K6yO7afkgF%w1LqBs^32YLnsk)yhmAx6okmDwPi?MbUoN%9y^qybq%eulmvYW zwuF1~fwmWO<>P7ta#PzrHUAa*16KK&Jq?@^;`S(u+?6a%p~slTgw&(&3q`qjxq^oa z&Z1=lSIc=U+(?yg3LXh*AjkKqJNsGB7S!56r!z13Ven)u`N6ekXCsRlj?SLK;7H*UhgO(E?NW`ZwE8y3hT6U7 zfKc-7_gOmBSO6wMiPyXhj6DXUkS9s3yXaWm3BPAjEt_M1hfNwq@^|K3j1ccg44tFF8I2xQF}wUhFk<%_!_OH7x&FfmL{5am#@BlGHq21;?22fTr% z)rz0Ru}bmFB!!<72rhn;pm%z~8lj6NQ54c{u0At3dQxK0H`+>|HJY;edtmG9lL+0; zmAaJjmpc<59;c)G$8e37$1o8yZn#+!p2Q5&*pBnYf3aMr6E~?Xa51%Ek!L!XlI%^D zCo|9~+K0`kYgE{hr9=Gk^?auw)l~YcggBfVVhWQw4?_NBf-GgBs@xY%a7ab^0mRH* z?3qFp=mM3{s*KdO!Y1DM@j_P%`*lIY-YfBjGoH%|IM~~-Yb>4nsbz&u?I@f=o*d4k zyC14RGuHkCU+fxr8(Ttsk3+0*VN|`>B*nVmI%3svof@-olHh^G(Ij* z&Lxudkx`0LVgB_dMn5=e%9>!EV~ERSbHuu=^lq#;c~`BribML8~STu`bGmgig>D0 z17yp@?T*vROk;*R!se?fo~V7>1|u9;13{SsUNBd0HB`1sBs+zfd)9xxIN1YpZPU9( zYmd%`Xxjx_6^5PXbcf6ZB`4(1V9mPH30V1nn-36fmt!<9YAYB!&;b`s?*yHW&l{(tisaR3wtv=|7x(_!ub?oA@oU3YqTVHt_&F7rV zArNfGdd**LsodtoQ{a=^jn_N2#mJ5Vctxdc-;51MJW%5PZ!_xtEltTo34AE@d5;wg z1twV3!CSq5x&wSlT(%E-)XdHS^I{6E*l&mgUXMVq3!@h%Mca10^`*aq57%SV(7Ybv z>(JKv;nd`H=6`$T8$tDapC^a6XAg+!x7UWLJC=DWZ z*helZpApJ4b-3%WrVu4wuI&>cT-K|PsFWp^h>QyR1?%Ab(vTzPgB4bZ#UFcktQTs} zw^cae{WdVEFXt^ihNL6y6n!DZUVt0#o6i?-uU<*Y1ED(L`IDab$HPU&?V*{ME$XIN)* z`6R0=DgKesN)%hhOPft5mvxli7Ii(D=Z~~Q+h+LqHP_BRfqgj&Wf6Fa&?-c$bnO&9^=3rrsR&GiKumY# z51mSAlnJj^>?f5__a!vnmetSo1(9koKOW+BtAgSCARe)i>Q|kx|Gcfr^K$O``uOC9 zdR@HVx8fE3tyXTZ;s@EvHIoQp4rfu5H{UrQSy+|(|_25Rp{H+1> z0fv5E`WJ7%Omhc19o!6*$A1id`@k*rNjWY?4--wN^0XGkRDW1uO_v9EH&7YO>9Q3$1{PUl5~On!4)~X12!_ul8VK0DZ=OM;fSIX1cLtbgLE4FH&*t^z-vg&d=Hkk5 z@$al14(Cl}=F#c6u{?K3UTePX+p*?O-jUevv#+)nHFsxQ>%KBGzalkA z&<+|GTq`}C#C39con(Re2rI8()D2@X$XEOO#>M+>&FA(`<~J}O5QTW_bOK1}4Bx!A zAQ;p_WJ_bMp=R43?u#&P35`|Q?`O26Hrlu*r?QOomZpk3)UP`llt)<=897BAxUzfu z?cI(9HExIqWt7h91L)-4x>f%;hF##l+`+(j9bkQ^&edj&H#&eVPhTr*J6XF;7xTg^ z=fNn?PocQ1H{_ZX?#>d^nqbYj>P=Qj9j*dv$XS8&QP1lumweTtdTWWGEqdh)<}IH1 z6HM-egC3*B2HIEiCg#!WR0NvJ2GT^AEXO zn9#Y+B{}?O-i|9AB{KdwDyOcSk_>`AaZ~6NNwC(*rgSs6sk~iGLcQcrPqLB5-PzwK zuUJX(W&)VM;<||3Mqws?(AC9KKPm78>f=f0!DOFh54V2E{{6o(7=NVya$7MT{^hgk zb9e-c-+6_vW}HkoXctY#ii?$9cEOQ=`>3JT0_+BNcTykkW(NOoub&iI0Xniwz=px! zNHbVp`&2L;?ym%MyoaQFW@7k28EmCVQ~mSCROSDoK!No4ZX+~5_a8wGWCiIo>xSJC zP_qkUYs%zFhybXh|IUJhwx3R;s$={<$L%A7Lzjn4~9CUpK!1 zHZkC_Z-x2)jH3(yrvwOZg1w{F#aEBZylCJ*S39A(y2&ycZYO0NSCzIE#2KH z(v5V7NT-qlf*{=`NSD(6&dG^hdfm_cyx;rH#^ztAGqctlv(~YGYu}G)fnw`Zo3$C? zsVMp2?P|7mB?vqrCnE6h0dnO21#b|^699PQ2EZFFW%Z#ujsmfi3no3F#@wCRFC-3h zX2Yd8nSvRILQepw%Bp90yT+YCw=Lfhi@S}Oc1Dvtuiio4X zrdk2LsTdEzkM-|?;}`!MM)`wE;GkqK`;VxpYtBz=d+$Z1TH=Z4@j}x$Kx)GTP>ibK zR`|xxp7@4ca7d^he|{gVS)nVF;_JY%P#nosTMPy5GMYedSyHaXiVl8GUjW`kf)3Me z)v4BCH9oRhI-Cn8g zszM-A#mqS8{vcuPtMB$0Z-aOFfuSIukQi29kVokJGthRMG|3m4O-1TJ?<6Q7z0m{y z6VX-0<4q+(E*XzsQzEGH4-x1h%+k^I1>`O>OP*}ebF&3&5N zvhoO#Ew$C%49(j!R5P}lXlCMTf1s`J+*@y04}{c^mst>pMYY&W?7V!Ck`CjAr^`S3 zZ^u3PBkyzNH>|)S7gO93LhjwcDX_{FMqR>9^i#O-tEKP=jAnvyfIfwoc>qe3c|@G- z4G|=}M9TzQp1ZCM?HFJHkc9^P_gPb1`AB1^iXm!6h&p1~FpLt;_h z;C$e*L4Da_@S^YhhIzf7!L_Pk;kAHbcSi4&9k1pxKSyc1Ce^$X`ZX5WJifU0izN-w zcYd_>*~{^w{FO!v1EO?cp-USRg+wvqH9<^)il^XVEV>n;lLb8(`v9+y7I0Irv@a3P zQL^ILkiav|H8^cTVvZC&%&QDJfRT+Npb?y*;sa*;jjLDvx03{HFcs1mcvP(qU zIW!|2*DEX~(+qs%!nX8EpTf4%Nq3^)*`sUP^ym^BOa`^R^M}M3>)l|ux-1lQHeA|< zuFksn;eSXDO-x8Vde%s_oPRKg151W-mH` zbb`nffuAelF5-H23HvEp6vxTW&lv)ayF(9gh%_CE=A5OHp|rNGE3dDOK2ONN_tr8v z44D>q8)SnhWO6*T6D?A2UZ&KQP72_Eu1|bs`a%)6>xSlAcAo^MV-!Ya)f!!WvTX`` z$+A$J27zALE9CKayVXo2`iD_HK8=q9>385$;b3fXO^cVA3H1i|$#v|{1lkDj3B9U(%eVO*;#(Ux^dRssWZijQC7Qq`JRbOcTru_XNa%8-DA@IQCwTf8B+cw0`Y!ay`71v*qCd7LHU9A zJ2O{1X4*mVOXUi9p{N2t(bQQ**NaX0N0N*r{}RrhlPQBL!7mVFwFvvstOMsXI`D#?b#-@cd8XmeR@ z3t4%sKLb;Ly;<1v4)|yUC_@8gwLy^h3O(2;sWGh`BfV=z5Wk3hDtrmn6uu9FTOKtC zU381X{ebhvH9{V=K@U2F3}3nLlEPT+_GE5)&K0nnrx!)q<2_xmrWZ_XjWTZVD0@*X z7#Tk^(S5mw$J$-GaVgOcWRk$eVnrgLM35x}!jVXw5z&?)`NdS1FkmL1Z=>L~OOPKy z!dyb2+Tyt~4Uir|2^^BD1X&hJW=tc)EadQ>CtT(A+Yu@Ul<3RjyTmg(mL%(aASRu% zrcD0Yzdp7yflyDT4NJK%^?lQ5)H#K5I1&rt{Zy~zM7u5DE}WSj)}!(toBQ}U=*LmS z*Yjq^rccMX;!2q8KO}b2V-G6Q!MIbkhXqPme*dW*wlwje-kgB;VpeBKhN5o44)^9}4eY4Sw6 zL}PnT`Z&6KaWd`phs5!Zx*`6lxtmpYpbqTa-w&a4Afo{#79tVAzQYms1;jZD&k>}z z@PZ*!>#F{Sv)KGi))5w>Pn+nWUx&V^oq+h84Ih$bD1Qu>UM6c=PxOKI7|GNaYbY$? zY^4_DPsx35dXo~$EmQIvF4W>W8K*>0-%47`X{Iu{1pr06yDG-IB*6`Tt*v5hWiIsi zGhdlxwugO+M{$J?deiPz2%d2_gfTxJ_QXG{==36Gqfw8IaVOjS(h}O;lN8K7T@8tI zdF&^J^gUNE%IRQMSY$3_FFB2vpM_eoD5C$-S}oa{2pi?63NIz~+QGg#`vXb@)(U}R z1Ho;K5qOmgDWSs`R4*6IboF$LF)zM6ks)1_9wnl>?w6CP3eObRtGh-rt>dl+KK{pj z4u1$orB3zEaJg+x$R_ZjUXunBG58ztH~hN}Q@P(d&|O3Fw6kA^JA?EIL@#=2XOC~B zErcF`DQYitAkemn>P%bn<||VCEA1&=%vsW)Ja{1Dh5mJkHUC?+OK<}Wm6+!MFUVqRvXzr@%m^2IF&YFmYJi7FiXIcp2j zQn%FBY>UF6Jz&(=SGXXs#1fAik}`N~!&z54JPECt$n|JH267o$TQhjo-%1Q$U3iiq zH<`f6Qgx4#Kz5`$UQ(^viwY`r%@ zK1yUYViAT}AkmU&HZZ)qd|o;kdHP}5tl{TnkJ*!IvS$T0;~9yh1`Q%FOA)#=@Yn0V zVMsKH6kh#+GGA@odQZBy8!G8shF@rUG~GdczP06zL(B0I2A96MW19nwE9$L=yN}}< zTHL-KteHe3o_%fZPFnQ+*40ou@C{b<2THRCbn+$AxLBmtX!@GbPSK@F@rIC0`gQ&a z7{w$PtTzvHH(yCFEUC)VMDhB z4_96q5HINa?H_cH8CJ=}sW0BlaKVZA9(>|N+j(>V6KZ1vqnR@#GRT_!kakUzhgtUR zX&^c{jIY}fL2t$#+xhm#9)36a&t8Bbfz<{mMAYhLMZnrrq}+2|=?6TZCWrxcM$|Nw zhX?-IGmowCZ5EBnfZnQVr}JbGJ6_CK0B9rf+XkwkH0UT^KM`*xPo-cA=NRcwI%pOd z*E!q@B7)z5>u#NTBnFx${$4AKV?H-DY@^dxQZh6$Vh!SFv$J?Ml)-0Nq8HdB^AB!P z<0pfYN1NR5wYgN3U>9PjZS*z+7Pjy*O*gxUJT9n3{jeWXeO=c8lHxkw_KDMdfxvzY z68V$kbg9c1+{9pf#*xEc+KcwkU$^leN}9kwH}`QwiBjr0%I_ow743k7{ugJ;Ym)M4 z@~2uX7KBqvl+@#vD=22uy)CLaAg@KfsvKK80d|{8NGUci>giXfc(R!Ad8M@BzHT{A z-}<5MRpbV8!x4yFHCs-mFKbrOLL$}2^-wC_XsX%}Gp@tI0I%1tC-!&m-SaOpj6diX z>1qt19P!wjQ&!!@+hP@c1>6|0>~B={MlE7^4byplGGH}M`AYm3XpngI954IsVW#C4 zSc;i>^7(0G_TRVye9(0s!XtC=Mz7Xt@ZfO9Q2Ntv==O&`Ll~@$xT}!-Kl7XA+~1;^ zwupcD4H!nTfE70I&pKRr7wmDXec)gIFWl-s@E4{Ui1g^6TKMJv zZi?3mcwYXG+XIV^K->yKQ1c#K`HkO!wf~W-f)E0XU zzo>@E6L(#JqjIW`D|3)+fOj@;_XgS{GPY@6DKb{xa2uKg| zi#Pur-zb^N96_Teb?Wy7Bxg?uOhE3L$7=<;91ZnxK(GVZsZnk-s&v|KFH6yeOb<0s zSyCz)xAE(ZQs%G*v!cJ6`&Xn6AK*%eVg6vSHfy*6f!^p7iHMS|gcnHMU@;J0;h03) zjM_Wh##px7Ri)G3N*M~-wu_C`mtbX+)aW{JwHk?EukK&8?JhXOLjD}!%+FrsWQVQ# zAzWS%Hrm{V`Ws*sl;}4)R5>3e44dckHdQqquL7*67zr*EV2HoEh!TzQH-a)wW08(F zP6Ytf8%sjhkS3{Lunl;8zX^HJpM>1WW#FS8DHtF(&X->R0JnCp2{mh}(K?JI2n7=^ zXV!JCOatMez^w)v^p62u_Az#k#vIfG>dr z5d(lAq7V8+AC%Qx_=~__!T2|KbyWG15a_~pCNLpSK*QJuMFwJikUD$tG=#xAkzHfQ zuQ||yqq#mc1HTU)H^(t9-MgD+EBKt0IEbHo8?9gtCEU7xkk5`LBZ)*nnj>%N8ublC ziF=~M$M}$Af2kiZH3+#wY-fgOZqhTtu#DSI;pEe)>nzi`Qf(Kwj`CS4@z(5d5798+ zx}sSlXf)S#A6j1gJU8jJe0VzLl91j0o*KJoU|6^NVF49O z_IRv;Or_7C241XTyoujHh$SOr^p<^b%jz&#{>;qWNiGN~26?!RxAtfA#26N&gAzk4 z_kr)$a$h@^|GG6{&4WmDPsmxdi*Y;gL7Sy=_r*CTEdsyffQPHpXVkimiqtT98nY|< z=*j)!*US{x*}L@uVg4uS6{W?VNmHO%mmH=xS zByHNuIaotx0u&Vou%hq{FPR&x5G3u;8T4RC<**+(`eAancMD?=k~%=`Y8tpBhrg1X zsgV58y?VLeyC6;`K}1CKF$BOhKjZrHCbzGFukt+C(kRZkAv0Otui-#27!;qMK-Cu$TjHyoGNw~YSY~Gd3atslUA3qT4J8n5`s?arJI+K^9&Q!c`N%Hsl*Cf?90#3+o~8o?QASm zxI~8>$>$wQT3u^cO5{G>kJ^Wyaf)M0EVY@zK6w7)weIO-i^);=NF!Ou6O&a(J;v_g z;b6m4-jVf8kLfwBay_(6yDOO5(%Sld*4C2_4z8^|%9jk~FLri&W&R!z$ zcVT+HRkObOSRD!+J-GMzlsXXkESSffO-?jk8SQ~+(a@`~jGv;rJxN}$ymWwmbYKWY zRNW1CV9tiaCLSuygnqGvO-eC7#mLAAzd$17Ezo*npUt3t47P zbLTj$qlb~0Oed;BKc|D9W)wEX`~gu-p!-6$qRO%`arV{Vq{8ytk%olkj)bF5o&w84 zdkA94d;Ea7ZZvhn6eY$XGNCU^d3+3fv@{EAB$T~4dHmh4YgSm-rnQ|vxfyh8b*#K; zNku7D_}R(jCS|^7M9xEl-|2(HGE|Cek`V7`&{@u8h14;Hg=}+nNL#SvfSp3Hs7Epk z`>C(wYX~gL(Y&HIoIPgW;AS-WrLCiYbapi3-~Z?}Q=b zYwSQ)l4DX}?U3^O^O_=)R$NVKeOaUDvNK&6Fx7y7|CQAzV=(kzlw5wWn?1=4Z7j7> zH-qBRLv+mu=Xj-DCh*YiJVacUi6yV|$>Qnvgviey6qCmhYIeV3)fF&Cz3hzMW~|u? zhnP7T!=vV{9}V_L3bqx0#`{FZ8`+XkcuxhBGveJ3HHWrl&#T^^=B^^9jFxP#VkbK} zTQL*T&JHoAVopx7m(PiyN?np6`a%zrZx#Y%H+BokwZrxa2P>nDoW0@Q&o^C%O_35( z&PPkRZ8tu+%VG=Koh z_x5XNO#q#KCkNI??4`u#3b6Jcgr7Ah;(H$-|$b@h5l&N9C?9auzx z=LF2=JD^wNFO?h#+5tYQKmIZ7Bxq8YLkJ{7uOu>Dk<61@_6#yGG&AgcIVXcp8MgGz z5OIEQcV}l|D++TskX$BK-}CJ{KmeV^U{jd8tDvou&ld6tONq*C6w)K z&RE69u#|}yrz#Blv07!?-Tjl+TCAgo%aXB}nC)r0)wE2x zuCkw8+*b>SITOj_qJ`f3V%O=%a?_}ZUdM1hIjOuRJiT?S_SR!8JR6-=hYo``?R>Xo zx`1M{>n2wJMXTrmq3mNO|CqBM96e~^r5YRh$z%*(CDZ7-b-S&uw3bFPWpx&%tii|@ zZr!C$f~_r!#J9(jtL!2*=bu|mmjn?kebO*>{ZB*WEyg02Ql}Mtf zm4>KUQ-C689sRBF=_W(xj#u1tR=edA#;UjPzUMhhIx7)S8W;UQ7*pb@pu*^rs6~dg z8F^QmzUedF%2Eb*)*+EPc8OBz^+WQ|m?&=oB;<-!S#jLC^HVZNvuQNeh!% zCgi(*+T>V)$sh}Cb2-yNjhT^22}=PwWB2^)npd@0*~%ri2v?U}m$wg6ECu$4F`R0t zmm0-B?mjK{z#gx&{Ve#Xx#glmbF=dH>O=55M`7VM!!s}=PzCfMEa*qvdyu8v4ZYAm z{aQqD<{HrQkZ}6X{bkIcnD$G~Aej^p#Uggd9f;xUdjc??X=Cq2sUfO9DV?649!C@w z5x+mW54mY@6t<@42k@+iWXT@aNS&2VMwtpr?!+nX`^^)xK4gpo9 zllKavhn=RH)!{24n4rEo9pdn_3TgT=H}U*x!_!a^Cb`sL|~7)QKSKMjp2|j+CfQZjQ~4 zoiZMc$5d6tQB)h+(9w0-^Lmd)>k6rd`%W{t*VO2h?L0=XoAly+A+zdwfVY-)jYV)M zXTv+VJ}GjzYDZK&f5!fXa!E>1=Taq4u^GetA}^DL0D~tke=~Rp-h;M>Bm`t4+T3BW0p*x6EUEpxXm@wg%IzoA!3ni73=FikiZ9k{|=-mWU6M~ zmC*H95eu2RG;h3ip0D)}qq^A3>Yq-Uz*S*vTeaZ%l4;6O%#`#)rgIBQPHE{?h~tyb zO`(jaJTfq7KgS6!Ne|@4@H~)P(SnJ}i8r$d>QFdY$I0>Ik*t|Euzjp$0>~30Wcjhh z4AZ)mJYzA_wDUQbE=3qqknSgNKgxGvI_ffnuD~wvaoZA3;|rdD{Ag*FVthbs!M~p`5wriQ=Mq*hjV2TGw;{8 z38orXzUDfv&1>fjERSR$wy99)gbb8XOFZO$E;)&*$2iH1%TP;)HX5O7?xbPvQZc; z?c%ClOqrpBs4rj&r41#u^KI4my&xIhGmb`yfoB}(jsqVcp5u5%_@|#Idvtl|Ijekr zF2!0XE`H4s_|vuB5`S7@pp#s#$XF2-g;=r!iiE9t<*XyTV$NYTRjs-BPpH z8m(xnD&OG>j9Qh#(L;}^NHuzBzPX;n@n)X*l(1gdV>%i)hSooDbOtXQufJ!WY*o<8 z<8qD42bbgRu#4exak$OA`Q<6|J5a_N+VioU`fg(WTb&UC`6t|}xpKJUX8N^zys=K+ zzkM-fRi$eFrW1sGaB7C^y{(zBPP-WyL~_i?bedmuQYReO0nmjl(*zi8N(AA?5<=AL zc%e5&kt~|`mPVN1t`)Wj1OgKwtL=&#ducQr3Nrv(MP4>tL{sb1% z@czGHE8CbrY(WVin0X2aWUUO+JlX#Aq0 zbC7@C!m|=Ignt?eugRv6uzn#`TeFf&df#91WZ+KO`48@erBnsxUJ!mmzcpem;OYFo z=*a{@=Gn|5utuzM3Y@)b1|Fq>^ zt)-!IuhxQq;4dn%TAZi2f$&lE|MKu5!JOWC_%1==;+0kM*4s4ptQurd29oXn ze;Toae>7tEWj8>Jia*_CSz`Y;21vCZ*8{ld2ws0zh7kcv6|Y^*P&H}*csxu(h7ZWa z0_Y8e=2CJKV+2Sl0Jq!s#v5hE-7|BTVZ!@(0|ownl4PyFb%~P0=uc;7HGTl$o#^5f z)^xjiwMws9!NBh#p}?6!cdxMmAzwdX2pExwtl;L)A0C?r8XA1af_ZpF84oRewK8RV z6j8g1YNHXGs1a_p{?7>J*VU&s44NnIOsM@!7M;9ebXNAP1oe()7_2}(cM zI0V&rUoU5jvnpU-q)%X}Th9{ifI{@KV2kQgp-kbhWM0R?`N0>j7B2UGK)l7JXwLoV zaN`k~(#xg&C3!@OOj~B=Crtb6Ryn8?3%U@x11`x*KoS7H_Xp){kG4u&?Jpcw>_p9mr^Xp6_@;Ds5 z;!$HEwM=!Og#MXSX7*9VzbXoE_G}bI{zc!h3}vX`(|0bTvh?AJ2+}AI5is1m+vxo^ zIHHp6M6Y+mY3WG5u(&X`ckpFNt7V~Rn+@Yp{)pnG*gtyFb`wsCY5c>}pt~c8#@uai zudJKDUIVwqT1a2$WBlsPUd|)y^<*z%*92Bk`k&ZA-+qX>@?-^+%MfsPHdWM{lpAln z$X>&Eil!T!0@WKic1R9&vLE1c!g=9-cw4c%Vm-Y!_N8`~Sa9i;sa2|I)Y8Cl{O

nGl#khyv+GjARdRd`dVBeGH$}MEG#CbwB`lKFDmT}N$^ZpeDrEct z%IH;&145U@H0{hDxK@q#8WG8=-?K&)`Vgy8VglbLGuldArY!Li*}9fL^_07}PQi$r z@rt0gj34r&bsh{ErH2L?w6WBO*PcvibfpVlZU>@2B-Cl<_{o?qwBLHQ-K6XM9uWcu1|4iybIz@}T?jWKOz4s8GhtML=*azcdnL!t>;oC5e*`b~{V(2YN z(D_y(*Lo73I+2#TqGQ3#iTyle+CAjNFL@*RC`L$%1{zE*>fpswrxkU5eSMtH~w zN!K$>P~r7b)OcV9r&>w!t6ciyM+VED8ynv&(+uH-ERCMNg;DTXp)T4fa4NQ{r!ufu zre^*Y{C0dEJ)rqUNZ@p>fqW6-s-ZWRTd&@H*U7_q$M8BrkJz{Kbl1S9Q;R>sF!o20 zc6gzajL_^cN*Dbek!KIwNb7^l&{&T}IOUl%8|ph@K9D=RcHj5t-{esY6(EoB#jM6Z zh_;nbq6B2$rqkM_%l!il88BltwLp^`Cv$@5EL>72P~m{li9yL{rG`Vgo2;n z$BzU)-7A*D2Uq&3VzjKYQmGZh$ z2_yuPI}8``!DoFjNBbCMA)hc4Y0VxKXUb_4pS8gVT`Vj~GEP;J;7w$;K$z%RmSh5h z@b2SQi2=`yp3fAlnfL&ubr-!&?kpPgnzo7fgapqap6zZWUR`KtSU{(CUdgSKe-}== z@TW(YUZ;jmiruxF&mUVqfwc4Tk*BB+j#p_Z^VCn-t=@&K#Wz`q$B!m=TVcjGWm5{3ai)BB6wn{&qT{7~ zYN&sre4y4*c(YHS`Q289P-ZB2XGDc7;YSc&L-CV@0#R|^3sS=|#0SwQ-Hfg$8`aM4u@?5*iOdNUh^Fvf{DX z@m}c`bMgwu%pY%Z^qA;-qA`vN*W*K7Hj2YhSxdjo>b-(`)VMC7ux(#3^a(-ntlE?B zmA>6_A@Kp98<0-{gG4+?J_}eF0r@P8=bh7R6u93Gw;*d@C~!1DpFWC4KR_Q0cA0g!yp}>pVvw~Rim=J3NuWfye6zZYvr|{Ti&Z^B*nEy5=8of(pkz+?B-hH= zL+LovgeIGx|BHpg2Yk;fmu5HLRhRRTUKf;2yZT7rkO3 z3^l0Q0!k;)N?hdlswXb>bmEA;Hvkb{%-}JHXOiO)twW0POC;ihb818hirf@!m}C)y z7_GI>UDBtTtu;sOB;K*dC|iEhzmj~TqM{Dk(*;Trl)#+*vxEB2N`~y}k9EO=gmS>B zaQ6Vg9@KS+>hxm<*b>D%KczVo5$O^LOxjPv^R)T#__3u$XCPeyWU{a(%c68QN}jzr zAhcI;d_S}58N>rxrg`f{ZbYg=Jf=G3;~dfVnQ^@lI$p-u3EF%n23b{~m^5%l!>d!$ z4;YfWtqJ2xc2kdZR+X^6&WMaR(^75Lo7g?xP=B2x!K(abk+W_}&mv@Yy>&PtCT(x0 z6QY@t9_a@@x1=45pi_BH-NAc3LV)Kte*^Vku+ttY{~8zr5-(CHis|k}rcj2}Pu}HqKON*!-k6-@jN| zFh9OJmjHvcTh^FOW`G=AMV_jB8>=6qzoxwm(;Yn|ot?HYttKfc2OMYsU z!Co29DKfk%Yb3-R+9=*dLD?dhH`QAM28oR0RNiYJWRqX}vI!xaUr%v``PU{dYL}KH z>J1lH7f&*BqzS0!!(|L?<&B6%w@{+%?NRlx&gQ=fsf$d>pt#w6SW9$c;7@5Sw7M)y zWVpA}A$VNR^KkGZY5%kctGvMb>?eBki|-uag?9Sc1$GzpA9`C}p1bUj8(D1z6Q{OC z!(m{ThbNru-+pz}U%gD4a18W7d6JWPvVT<7ytKMPq<;bQ3#8)xP!6ys7W6UnVpSLt zGyA((MTY^jcx|{q5WG$Y8gb* z`{DplH#o|e83G0jSaE%;?O&Wc7E)CILRneioeiMkyq@ih?n_1 znI`U35q)miD<70W-wsWe|8Zo-QkE2oDTl+Hz2v9fHLDiY^X;99DQy*c)lsqu+qlu} zIGNW$S~?U%=7_3xG!zpxuPwG+sy~w{@HBH^M#Q|Ld2iHQ{(Ox9QkLMdc;`pNOMJ5z z?@y5QwnCAPpW?_qdJ}h!s)H9d3I8%wFkEO&yCu4Gaq+DI8h7LN28@6iUptGoy6y^7 z5T0sBx^bFhq1Ky*$nvSi$B*%rKO&GA*LQZ#;X6&KqCFqXFyL9scyh^=O(6Gr?V4c1 zOj>%TD!Db10ZMU;`u17tTlxcgZF=$Ra)Xz;Cw#0&kLjZ5g@G3$!rp_~N8eb^z{7ab zYu#Hcs(&z>=n7B}hu%I|qJ1>nBVxLDdlPkERTnw}m6Z(iX2CP2oO)B&Cdx~#e;G!5T$Savj znB4{Xp!&{esMUFN1&20n+DEUx3H0Vrk5vd&lF~213hY$Je2cJ#!Laej0#*>kMJrFd zB#VDq@})P7b+GNmtQz@dAvg?AhgyyO$k|fLV=Cc_ewnM$?QhQOJ)@|L-~f$nMgdj@;(9?k4W$8WG#w8G(8;s6{o+>Dq`azK@4xVi-@zGB>4Nf3ZCn^e>hv%zpN*Ul%6JudIL7yK9m>?)3fBi|hzgT!(YU ztt@3x1Kyw-$`0stekpNku_%5T#>c~cuC!K1k(WGdYdkDjrjZKW2D{MJa8vo?v#0^a z6BM1WwkBAQvW(kfM0}d41HR`t6EG;#55knz85nJEIb|ON={$sUg9)LY?_;3WTgv+g zr8g}&*T=BhJ1l#)^^oU~O{qa(<@)%9Nxy#n{-M0OUAZ&3fmxrL#2=&q@RvrJ6x}qB zmQLtN#a8^LP64#z*(DeIx+f)ijv+y(qjv_f&G$zgSq|gB#ey+;R)A-5%TZX*V%1&s zZq2Ox?Cd2=AybX<37~f&AsfFEi48Xo)qsRvf2s#?LQ>6|1;Bm^@#^cV9g%I(=MUGF zm={Q;*-*4dyz*@QS^k*#JV=0N1I%@Qff9;81#f`e^Y^A~Ox|{r{hrJ$D_0K7DOp_s%Alg4U#<90@&b?c|A7cc zXA>n@0L6p$c8;_>jFcKJ7e;E{0_B#}nx#i-Ej2Zdd9o@vB1Qn=78zh&E3Uv((5pG2 zV%>1z{<;d-S0TkZLxSS75XaOffsW|?E|ujg1d%I9o;HEy3=z1Y%f(`SkA}7@R8`d} ziT**+u};ci;MkB0X(cQvxg9i#%Z@Rcr2`8>hey-y@X0^&r#r^k@#hnYL5Jel0og|H zpJP6;tSVq&HY@BIK{~35mjqc}w;X}SjMA`*%cv(TufEI=CWiY;{+lryyN;O|kIv+vs*n4wSNR(OvCDlH_+1N@1@|a6iFQdaI18}q!qiF^ z6#oRYm;l7$fR6e@rv>0|JaX`J`L}RUO(XsRo%Wsa)W7h)eiz_nbOzIdf7E_JwJ%VK zF~m|bIY~IXCOC+*ieD~JewVxU-Z@XfD#!aeV!ti75N*%yg^OpOej!qTO(Fj-p7xjX zbP#Z!0y0aH&%&_JhYh^_>uFp}$BRn`o?&4u3IaLKC8&Sx=kMO||K*}`YU9;I@(+Ch zkL*2{{RfiX`+Og2+zA~s0Vt>O3;23%fRm+vetb_N>cJAY>HmVH zf7XJ#-xT>f_VENeiGb~mI>0opD-8&bzaFPPWO2{ruc$d#F=u!b4i3Xh;C+4q@<6{1 z`zz?O1Awc7BJ8jFYOsIpx8Mopn!^|F+LXG1w;jaieI0y$0`Qsv>c!Pfwk zxC9NoyS7=q1jZOuVEr-^@7^n_{<}*`i{D@BU-b4ab%*(v)iff6=9~7^p#Am7XFVCD zyOEfHe6k12w#h+4p*CT{1vG-(>1+wBmH?$-y?;OnsaiM&k>RU6LSl|~K&J}I^#7x( z9bzsJa-kJKfjOWGUBw-o_IeAdq4~Q5!k$wU0y`tuQGiFbvyB-^6(}mkGu?EKSkB0s zn0>MTbRQVhmjK^8=#^gl0F8-|J?BMfn3v8k$WLbS_f7?{l_E|HL;&bketFFxl0J0o z;HsTL2>RI?YGKV4VHdrw+`)&3!|uq!*Md`$2GwG6LtB1CgL}rJt?{Wy1|BGLH6v8* zYgR*!L5m0jb(=^NL9}I&`sw9WfA=e!k>iA%@$$T3jgkQ*tV>35{_4Rw>v1KvnU*57 z3b9TeE-6$33K+%d3L1Z(cba<-QQz)%4rG$Bk4=@*FXF`IzdPM@aT^Z7?H^vmly{SX|_qZ$JXadtI+SQby2q?Z@Z*()vhGE1ig{_ei!a*Dfz<)8Y9scuv2i zd)lUF_vl2u@c@an)E6R}&}3p@tLen9}( zMeu0IgMo=?F+^#|Vj~2%KD`41-CYtlB!8!9#VsiDyd()GF^f-XMlM20%R;8%u<67YDO$N17%kF z@(I9(CN#|~Fc-pM%P0fZdFy3hcpIDHG8zvEw{?X^NeQ%uufk_3KdYyQ`Q~*{BjoI*5k31ya;4lf z0b&@uTgIqaIxlISaD*PfY{gNN)^KUS6l(g2o&9VTvd~9@@;gTyI$YRN#ytOUyKZVr z_argJPdTGQ!Hg`Un0UsF3>=F{o8G3FX`~Yeh;c-O$+(;>m}u$42Ryeas15 z7PM*QM_s78>#?$`qz?(N(Q#_Up($Pu=v{1Msfn7|<#C$rIFNPXz$P_E7rrZq4C(q{ zyW*;-?1_3QKdDpgspKh2xih#+G()-bVuaeXaxo;g?BaGP!XkPl=%O!;k`>>^j0~akr1GggWl*t+JLE zSHi$`3td$N4C=-YCGvn0Y(be6@ByJ7AXx0vm?B5Gn<_fI5kK6GNtc$7o`w+Vwxk=C z)tvWzq+tKKF=Nd4>5g6GdbgjIH2jKTDqZ!D=~8iLJwg`ZG7a%mO;G7(qlRUwQ@A8! z2HC@4zqNLH`0HS!S_BOwKGwsZoIq);^i>p^gKaLCZ51td>$I&C*Azmoa-gX>NRJs; zosXP(zl1ls#MR^#LW>fmnSc7u7W<>EYqNEcS##4-;q3SJFD)kR4H2H`RJKhkaPi6{pbE;U~5F@e$Nl+q(Hy{PS}=9-!ib&4#_KQ1~1CI z@Zep-Gj8;3IE*=AxJvFH6M?unk7~Sagh#~lNp|zm1jTQzZu{lD;M)L}4&pgyRt){A zV}T_0dZ#^K=2C5DO^{ni{nL829(L2X@X=bXAts4qVs94J2-49OaIB{@HDWFJdA7S$ zl{J;{d=%Lbqx47|!{5oH?|jO#)B_{o|1E%ckw;RT5bOHz95ISZ`!)BsU8uA`Q+DM* z_jX6?PUi2D&p4}z5elM`0|M95N2xtWKbMrQ(Wc|<^k@7yOx;wu?pHM)TwWgy?lq-w zl+egIF?{=+zrYWw{PPTRxIPnl8+bE}mm(cHB4W)^biR1CmMr#{o2H*Y+GvZv4eL z+htk(j>&vk)X7fsS66=B)CM;hb&Q_Ww6|%`$ubx}JH{EEybHC>{#IR~4&5`YKfB=D zsM|&nWqX3x>UP=8lxm)ombvy?EQV%aJO)?9|L9S&=WWUg7P(2m`hN8WBG zEMz?rzAuT#gS1x?E*G9~pKf1a#CwDUaV{-YrTe;YmE(PvOfyY_a+%v;e<3$W<#GSb9r&BD2_IPBD^ypD$ zCxh5p$mpC6wxH3=FBZ#KR-*)6S%}Hm>z<65tinkCfv?eW77xPMhF=o+@P0nB7_VIn z)RThgzv}pyHLy7l_k}s?lD)e9c@d!_PZcTsTj2ZfLFD{uk+#F+r_Ht_3qSIGB{3-zgl(1M1O$HxQr8i)n3L(oDv{5spUKo`!t}k~>*4x72}*&1;(HEv z|NE{k7y2LStgJaNkDULJ=b)>BVA69nRLpD;bG{`)~P+I4S6^elMOtb-YObRD}FD^_y{# zlnJ<$kU*k7j#@?PFMB9ctW!BiqlH>T|2n6p)h40$7!atHN~%u!t+N4@Mqv$go#t>E z9e$O$M&bTP^7b!r4$U8%Ki&WDqOby{5|D%rkfuS400mIR=*c99d&R)b{WtgkSjO*) zTL2=+0jr)mbN=sk$^TYa%&Q!H4j@`8JN_-p`gdiqHn@8t0#s@T;sl7Uf8<{OHi<&G zN#DCf&&I%O0uUkrP|H6R%fKW<@6-ZltReV5yj(oNKmHe-_=~mt3-G9^Oo3_8s6bmi zKtx=*fih_jZO(TA^PfF<@BIW>Q?Y(E=WOAw>3?fMf4#Y{b430OH9&k+DFuXeIZzr5 zTLmbr=hRvNBfmiXh91y#Dxuy#xFHb~2ohu%miU9&aH+N(k+1KmIKQfyyc#z<{gTH9 z{KyN)LVt17zaj|agCtF_t1tfsrkm)_w|(b*K*=v{x1ESo%b!cG--c#PNiTlFp ze{q|;zUll^w+WOmQ|7>oAz(2w_(%>ExMMl!?@$Ars88!`Kd^Cpj_2$4p z75J~O(l1t7+3Y@txGTgAGegk_!<~8jx}uqmAcofNCVKhlnBCr`uWweoWR~L zsQ6zmRT)%Cqbi&-G%F<21J*|sdVmO)?8c}Vg$?^TRF@XVXmr9b{BQq!A>7v!$NPsC z({2HHbR<-TqZ;%4rs;J3knG_?Oxh{oy#REA8We~oT1lw&0Nx8j5Z>zmYnfwdwKKFY zAV3-tdY|R;c3^S1gfV2bYo9)xwsC7myd_I;1%3&uy~pWGxrw6$O5zh##u>FfGXyv@ z!?69z5CVMPE@|@=v6rUA!IyCY4~+{oc3do3m7IO}-bkRoHYrziPy6X{;nwWU7v6Zb zqj9aor#(mSUNECR`|w+3c36m1wcpbv%iDd84aM0OsdB+KUM1f)MEtGZ7T_at{Mpv1 zMefZ7wC_s~Ynd~*UlJ%@Zx{>QRu^bqH9}`T_aJ{8oW|}?W_s*+W(#vZs&%mU{rk!H z&qtZ+DBy(>@bAyN)$P5Zb&Pu?4}Pb$J(ze2?hFo+R0+tkn7Xu1(1J*VS&HP14g`eT zU)#UT*vIhkkM&3BA;qN(L6?4#ESfq$ozbcz6JF%!=Nm*5QSnndQGqJXfNONqeKb+F zU|p0~r-aNPz*;pnOkbFamMski0e_XD^mQEj`745yQ#yl^;!Is=URP3s03|cWk7dw$ zdMMYJSm9$Qc@dq+PiN+HYGuDULyqt|yf)J&P2m@FdyP>Ws_lG1ApTCbS3Rn>wGWL? zrMCPVBO(fL`x+@BP2`7~i<#8{=|0XYaMo-g~V%*PKJUBW=-v9L1gSZ#o&AF9u@K z2_{b1A0U3XqdLzw6tzUKYTT~ZdRBsg`Ew_*%>%<@+!ONiF#IardLXa@e;=-dRZHbCf9f!V~~?v+cGm|XN)H$1+) zZPC+7$CPE^w5r&=dzKv34N3i~NRMVtljPE!Fn(3#{eXi~vaOc&WtCvZZld?sH&XqV z#i-QQ-_STxPLD4!+pG=(#ygDNH6PHKe|wv4_W9y)fcfxo4LN&JrOfz^oSEGY@({ra z-4A{(#`_KpOiatWOen3NPLaSzN)!jEqfz|2qY=2(>xt@%%IQA_bu_MlIvRIiD`@a( z@pq{BV1bUtNzX!@TLv6UcyB*={6J%;gt^k?InyHj=$v=nv|G0m9$@x)Q9T>ZcpQN^ zIoJ>{O`T1$@Q^6TkZn(@INkMA9EG4V&-;bi~EjTc-;gm;@Rtx7LcG-KUKicO=3FZ`VnGT~E#Cd+KmjbF_SQ{|*D?eyw>9di1pNAb3 z=ZsRQnE|^fdl2#C0+lJ-ks`P$^DpTNsD~rchw+U~X|HBPSoc!6z zrGA{`qtR?f|E&iBYm`m*EFJlz_zbaa(3z|3M^yc3w;m2*R<~Gx93fX=&YS3eb2^6U zQz(c#g*ebfoJ%ftsXq7o;6>c>B=g~G<2M1NIl9#$KZ|GBD2OMDo`08{?g$a=Hg9|r zA@~fX1r_e%-UX)~vNclekL?n+Tgy421G-S5^B9i<;N$^rEm40~eqb!r;hx%HI$=Dz zk22>Sfguh13UyAv5LEct6bh`jmKVb&b&@#!aduARj~fYmf8l_-rl=suarT4uin2_V zeUa&BL%X}RJ;sdfuX@sr*!JHfWObCORAI;5&SuGxGYRX+ZPDV|??}h%@uaaFKFT1G zpl0fSYV?*P@$+UTPw)}7k16H1)BX1x`aF`QAAZasX_4ft_sp=CBx>=z*_qEDV*VUB zb`cS~@=cGuvKhCZHAl{4qCa?9b>hYgQJ~&~VUp#ztF1Qv#Y23jrNK@|hhFo{Cgr1B z`gF?%d~78G{H^6^FBo(! z3P!MgZtq5yPK9zOiDZyO{nxP85l8Y-9=!YMG3zhFD7J!Lbp-sdlEBKR$%Dn61|n!B zxl&f00exu8l_^;^Wj!9jH!rgCbH@>p>(|o?upW`0MR1t(liu-Oo-qiP_@p|V{pMAB zqoqolVPbt*Q6nd(-oZ8{s%O)e~7 zfJj32x%$xaDrRZ7N}Y64yjrt*q`nyhvxSWk+bQjU3tNH80Daik=Ruh_ewc>IAny zxHpf`J!@YZ+{`TYV^B^62I;DTp%7C&#MK{usYf_fb-8L_{$qkYQ^4cZ1cN2tJ8j)k zIsTGFm?A!6zL|d9*PAE%1wK;c`(G{YBCFnvqETSZF<<6~mf795(XX{Sp4npCexzTxSBhZ5#`?x?T_0O3-9{{~FkXC_i}8)= zN#DZ=ylNNy+E7XvWtJogf=h?NrnNlVpM!n;h8N;J-5!wT5g`NAld=37{YeN~mv99i z4N`iM3$KlhB$CAJ1V_N5tia&$wz$~_CZ&Eiq?Fhcc|i~VML+7y<+%hmfjB~8ffb4l ziqyNkt?#qg9HU8YCdJ+UF6IbY>7(0q{11&|qkA)+zvna%<)|2bD@71}r_iF7vQB&5 z+u*Zw+WS}0Q^=td8HqM|UP?Y6lh6ilJ}jE&`$n_^W4$zL)~mP>$g)gPuB*>MA2ZR_ zM2SQmT{VfXI>4hby%w59dh6-QI{HzLetY)=-t15PVIP_nP6KqcuNo&QQ9Sy<(%#pg znP-gI2$Jz&DsVQ#+5tDe&{tm5DBX2l95q<}jb7hi*ue5w69n=H-Oa|4c#FPR66#JJ zr@MVy??#`AXHx%6q?I&278v}*b!o(z(OWp3G@TR4i-O_*$QrBQ)$JQkVNKSI*HOij z=25-GiHpS^E`Z7%WbT?h7iN<@<=-;f9_Kt1(KGG!;c@Amjnkld5o^TuN;UJgiOwxt zy-?D8vGEsOv*~26%)Rh7%CkXbLLwjDh?xDzqhJ)4EAI0ZlzuHMP9r&f?e^3XvjG8h0NUsavHXQ2vHxUwXwPZeuVNtd z!xN{Jk}44a(p(VJ63nU3h{8Y~)2uWOytBWZ9`#6C4-DF2*nOJJJxD^d1xgy(QD8Xw zJdq63Z0hJdiPT3ji_dk8K*BO5V(kDm!13p?4djgPfeOdV%BaLTft(1o@7sP>7L^#` zMp5|Bk_S!!QPq{Zm5P83i-A}Uo3{kecee2TF+lDJXq3Aud;r)oR6558|5;tQYElDI7*|C|>zNlfq-yc6 z#Rafzd9x5G1`8gq;EQ3{YV!=3%fo^KpTA4kDRKdoW+rwaTjM`Jv;thV$YJ`I)edxF zZVf{W30nLBOE?UjV#cc;MGS?L5WC!9pf&(BO>)Klg^+_T3r*brS_;5M`kf*5cMeA} z!clAYSBPSGc;^ZPy^Qn(-W|M{{@P&&`M2nVfBwlpYC!iJYymJsG}06SzcRGx2wFJ! zkx<^lACvHV!3ZeG8a;akxG;8zL3|O^vq1HWrQaLK#4|;Ko=Xo<&*kvFUj#Sk;-{v7 zEdAdO3V_c2A*PQOWi>F>*6F9h;0zWJMGhFU ziwaFktS*>H6P3%I5JhGkR{EH1_&I!=wkZ?d%8`6ER#vCtQcLQN&P4sI`{Gf0N*FDY zO@&lQfu>z)0jzAW`B{UL#aU+cIe274*-`>e_?!?Emjv~@>iWkC_es9XH$*+wb1(Dt zVQKoJ_F)JIU1hcLonRa%uV0JZ#lrFO{^3$)y<2_FjeX{^A6iw+Ph)AM+`nx)aEuIY zZ+xc zr*EtY*W{}b(nmQpkLvp5?j;NUvsw+u&p%_+8(!-|zSVF5PX)4DE8{98O#ElCV#&PT(T8$h4z;h zCJB>9yk8ik=tONzWAe;>fB2SU`=Z*Ny$n~)HFweZc+zbrFMa9ccz`xB;M`u*2cY&+y_mS3PI*=Pm-G_;5)rX@t%b0t;bFHpvuQ>9xT}K8vSgk zMNgOup?s%L-(mjL6!tEf{AY%_DJ6Gr3zyM@ubPWR13Bv{7mDlm;zkmC-g05mQhRP%XDV?Q@8S3JoVE34S1ujp z0y7Ja*4>JcuggI{)gbcieVDbtjtCrx|6@}2p#4J!w(!_2K-XIzYz3+VV_85Tfjq@) zU#iYjKUH|$Xx{tJpTS%k)t1fXpF-qeE!NXB)*Q?eZ@49cp)#oW~lSi zny-+Cf9{maP@SC46;sdR|7e#k`<`>a`&eDeJL>aVMe^XRr-^7$7&VJ(d}dFB)QyN~ zEOEa@Uy3ic<8oV{grBTtlvFD4YUJmjvcUI`=lvJ@ zDN+7*&KgAxvYGde5U+ zPl>20jJ>7%@eMyOcW>W8^W-nIDp^kq`pn|uDU;)@RolbZD!+`bVF)8N8+&MkX`d1= zHnf=#ey?5DP|dtQl>yP(oDNCN1MQ{su;|3V3VmdJucOf?o3F-kofN3aC$^UGWYVl( zcl+(NDmOIgkI6SU{Yp*QnK#^nXM@!+-mHc!JGe|cX}ug$Xh2Ca2(|5dsni)=NQ4kK zfu69wyW4K_E%D-K-H@Iz(M&`2{1+8P7=ro2Je=bvGow|eDn1g^DFcWJ%R8&0(DYm+LMz5+ZnBSVm(9EhC+eFB zuDd9d_U2?7(_^%n`;_f2L*MQHBiu$F(^m;CnYX#1( z8JH=DCtR66>2sx$<3}bKXE&Y)?#Vb4u0&RSFY$2VceALElVQ*EaC{n{|9p3n4I@-_ z-$r$bga6&`3|r#1gu6s>Q|rc=+3_nJ8;T!lUP6=r&GHBTbLa#Dt)sxEZ4dM2Z#lOX zNq3VMM%X8YhkX-JcifxVPC)mTcUj3d5mmL}K^Mm6=}ObILk*bepQfrh5TN|XmCRsE zdxXpwv-0J8B+d~o3Chw@h3iOoe9-sIg#t~I)5Cnx&`4$zBVW7VX%bP{B+dB7LUP$% z|BK=(i_j!@>^Jm9Epk=N?`3!oL$;%E%)DtvlBJznAHQ;S5ztSH{ZWi7UVDoIJWv0d zO%O`W^nc4=HM4GVm0(y?$}c}mmBQ)IA(4W=cM4W1L5eK^oRBwbZ6C{2hS2I1IM}Pa zgWr&9i^(65Pj+1O2D$x!vTi+Xlqh(*txd-mK-Qu!4>hG3(6K!P{Os_JfENUP3Y}1_ zQ|#G7wmrt#JmQdW;?Zzs)FjcS@~$KBJM;I!DG+!cGX5Qm96IPNMH}^Sd^K9PDKT<< zoy3;(6e%JmYMcO!0u<`e7q8N}QZ07>t+%4EY?Mgo-BWcS^~Qpx-srzl?-d~RuCDi@ z8*Is!#)ub%h(?^o3S7aBplkrdI6$}gdx9f||Jx49@ehhX|F8Q$((|6&Sn~tPNW(P_ zJf_Ng3@}x1Sw60O=8bq)M#2 z7L?}JlnzZ#^MuClSDyX%%oIjIr4r<(J~$4|O#k2L#9V7KpGts@-9bVz&z2$AlRq9R zl?rLR0JLfL(yj!YOZvdD{BOBEG#j1*d!1ziGQgfD`5RLp#J`yy(ly-$W;(!^M0K;{ zs-Ei7u){^OoY~lbvP9SC#wlMMP9V>RUE;1AunH z3^qFZBnT6(JX*PH4eHRCe+xlJ{}{>5RA^!kDAP2X9Az5P&mb#RFG>fq!kD~aQn~o4tWRcEGWVcUUGk3Wq$}x2fR?r%l}cY1Zq?AMGnDX zHc3^a0EFyQok*^nY$uOCWhXQP=nhpUE{z# ziWPVY6w274-xJ75g#!=;Y-Z7^C(KQPI^Nj&B308=&81>%bBSQEKcf&zkoh{`$L~vT zdbyW|B51ISK!Er>@*@9xo)on#ZLpnLX(4ajj^b!rEUK8UFg9&=IWs0_(%eo65C8rq zj=pyw0;V8Y31T3A3#YDV`FCmNdz3P<4RIweU=mM}th%bcX~p;ox-noJWt4c7`TG=d z;l7RaH_g3gva1tPbjS!p^64Oa_T<#Y5$BNSN0ad`+4EyAO#A+e302wbK~nV;55nVG zYNo@5`o=gLQO(nIQA?&VMOO#y0S-#N!(ELtmQ-GpQ(U6U6uO5e1eb=OaslXnuN_w) zg*0Ws9w3A3hZXSYK(7DIdDyQa807myjl%CaPfn>{KA}+qaMt%-*eMSELXlW-L%h@s z=NitsSF9=&H7T(g&=qh6mp*ZYem!mL<#JH07bzI#Rtvpjm%=Jk#~9f)Cq$>ju!-Q! z%@tzPzf9XCl0hfDs>N>}!rW|}Wd4S7^t5m`uDb_+_kov2I+MrPncnL(YhK%@%;i18 zujtgARG$O}<=~3=6h3_2d-!F*p__5Xve3hfB2hx+?TF7T+yrrQ2*ab{?m7zGI}SABYQ-ez1NA;N*~Pp+u$kO_+xzUc#W12;axWasxm#T)`S z{=#dWjq{r3F7Pku5uc|buPN4Twvwhd3atk)QV~|N#)#<6v--bV8Vn~2!o2|^W$&FJ z2(v?21mSHn_5FEw@We%h9hLH_d#S!@1@en)Ka1t%Rllq8lw#{8z#_W;iBP+huMt^nJ0*1@Cu?_?{{8*px`bp+T`5GALDC1KD;>_Hv6Eb zjiZJ}fVn*|oTt-<9BJX~bJyfk^Riur_s=cFPr~}7lwQ>4%}9rNotN!=%4kXttNy{& z#HnLjh5D?kHsihykN@+#il^g3x@*=0N@&&aKa$^s-#GE~hTdSu{1y z)ipm}5?B1g23RX#ux-Z5#++F^L`%|9Ezc%d)KUrm{8bAz0q=y(X2aasw`i}jLqv}a zG|T5DS%!UMA))V$pBjpOw%47Vu0GqzCQM3t_8*Ph7G7B;R~uEQ-Hq1!+BD|MMbW<4E`U~7#gIz)yEZiII4Q-|)_JR0vh%4Hglnj5VU zeAKv$*O+YGNv=P*)ceS5!62UP#+*c*;xvQ8V1&B=y2zdO9=t%e7~ZEsNZketywtD0 zKZ>_I#1!}${qy3xd#wj6*hn2${0w;p@_Al$;d#dVsuR2V8wmq|qJ`chFP1-Uh%j#n z#xqBeWYk^2@@?WrcWZi|6b3>RHJX6$>30uf)z@Od5`a_{;cdw%1;N2XE}5TB@fP)b zc9rRqwdn-~DwQl$4haMawJkK&EZcMp$nZu!xVEzEDIv+pfMu4Fknr&*v;VVfjF=Zh*O4+XrgNsFqh? z8SIzwYW1%A%Vnb(z67e`ZVb5-@2z!~pRsjSzve?;3=C%G_3oyV(L(2O8?)WOQ$~+` zd28B#G2W*WKXrGo@m19ucg>oC)Ry|^FNJ05Kamf&SnDcc>o|{IoOVSc6o-5txU(5n z@qj(N(uDVip@%tFGJTjZ#!Sd1a^=1JHD)?8(hb{%?H!5yEiT~|>VD=5PR#DXVrD~u zA3^(U!-@M%>oBkOJFA{dk316W!jJppND?RDK*{=egl<8iA;j{P*T+uMdIp~bW+%vP zGOQjX671jDXZD*!@H|K&SR@(S)W;O&jfk5yiX5fQiAFBkRrJ%*| zzB(MacL^d&wkN`!UVvs3EP)v010T|k)p!#qci+E%`16Z+tTt{lnik9^o8RH|2!W9{ zU*GV@7iCTv80cHK0-1k4NTOllLqwb+wuh^tCR3ga4R=OLTPg6i*1bV5-ZhoJO1N7H zi+^HlPuA%GBS`*=tCI634v($1p z%`CYJD^g^YYtnu>TYM^1ou}QvG44|O!QI=~iYwJ61BNNfXEm;9Ox8`0fG#PhlH89( zddL~1d>>TO7==~Len~PR=yQ{z8F`4P-5U__t;UbChc^HkM_u680cSklI^0BXMT01w zMMO5AufLi3(eu@QlVe(o*D4I;fegX;XDq#oeC~VfN^eegoGiv}kzs2mCFDM`EFhyt zfuCuve~*%oB<$XAkmNUbPsNo;2mWya*d@>iKBOj4#jERP8Jc@Oi6eDp`5=2_9R zMFD}=dm-?H6=sjW+$mr#wb@a8N;LzA(}ez1{u}!KQJshT&yvmn+7Dvt-sn8#u=12? zUxJ<6ksj8E^irQaopd2&u0f@HyiY6)i|lTXX@y^fVRa)&AUL$`4qS(L&Du19>-l)~ zC|-5N_?J`>FnOrs=RDMF{dZLGFBkBCO2M%T%YYQ$ctB%R=VVuQ7Ssb#{@;oiHGN5r ztFrbd2Y?^>v*ThgdpQHJg(6b~nTb1r)G*RwHN*h3tRs*IjwulzB;O#s${PMI7-5PV z`G3;o4Nruy{>uSn(3Y+LR_0|xBp2ddjVlId=Wjf&TPKpn@+|P!oo7IP1^0bzf=SuG zLipE1J@e3WyZ1^t)nIVX!1*T=D#A!MR_?tTz{v?nVqH}1x9i_7l|rjcpd3&PYFY$z zBmCZ*;?r}-F!etLgm0^IT58?4x%`m0!IYR?q>?YR54L2#oy-6$x&qSOXpS(W*f4Yd z+kHdq&Q#B)htwEg_Mm-tf290Z#S6fw9T3*d zhYIUVja*CXLKlcVB`N|!ei6v(3urQt2Ywqc`&xx`1pO(k z`#ag-w>k~9a|BuddS?x7?>GfU;<^Ye0A0T#bnHyA_#iWRpvKaCay8-2AVV||yW{$o z!Q)X@jEA&*fVwzfQt~y=`)K!(o1g+9|2E?T93Y(z1b|`#aiwp73GU0BGS6`d%gcWb z2oBpmCjM6yc}qpe=VRt+h$-&@}co*Mg|u`|ITc=_ErN%BM*uE z*%V~xe~jj@6aLp}`K>)j?<&Z^`uXoLUj-Q%W*!>mzpZds2wMdtw+sx&485}5gVa5=9m35tOKTLJN}!}&eRS22cxxr*{1O9B`>NOJ?Z7~s=1 zn_M;as5?T}&vhXG)~Dq8SOrqiJMH>EO!%K8g|Lji+m&C17$9N)XOOQ#3=4A#4f5X> z4^-s^)ElJ^RqBRuU59=-f4&6tm%-Bw4khq4m_X~S|2Ey%lLD^$N~K=M_&UPCb_R{{ z-`)@^JPte*8sSAum=e4HobGuN@PX4#;WYN&)BQJHm0)-#u&&BU8DvrY1U3|yFZF+Z zt$zghI>f+s1`YDxhXd9g2mAwnm(`hvPWaUc`W4u!z$Yj~dmJq9qI)Hz4xOlfo^U8f zG7#Z++8mcFfIg(-E5aEAu96_-aP(jh;jVo2`iK%k|MOe~6#bo`cTq07k z9_PLJ&8SWB3;SoIaaDD-OZy{jl#E$+8O2(l!Ue*gRNgLtM-A93dqa zi3bFM?MRC^9`FbajKxrD(4c8WXDb*zrFIsOi(1-aD|3t~c=;ptam_~oYhfnW@_|#7 zDB4#NIEbq@OAd>(=I@C=Rle(SB{c2dTS+`+id$riRO^DT5>%l_&37QiT0@MJAl13Y zf$@qq@badAnUe9mhBw*n22KrA7M9l6kge$)@A(`Gb@Eg<9`p0k{Qj+cCGV=AHl8YL zPkvejT>^HJv{ zk}&LBUREBNdK06F@qF}D4yXQ*@Qb+FWj{#-$@AFI`eAss>g|MHC!)R8*?W8m=mQDlBR?jlkdzGNrBac;|ALda+>O&qq=N5oUBJOpvDI_+rX+ zaCSlQc0X|LNZ7=QE@6D(d&mQ|iqK;N^ij^|t`9=yU{yvG(5 z^A(G{4~vg3IK3X|mm{YU$Ll_9Y7bw3XVUV1YYOR;iN^6A=Hc-i*`2&=bHgeNk%en0C=2fnf4<{~l$}q1V{_3Mvr!C;N?oN!DQ!lr;~J@O{_EqLtr@6k zwlzKx7}z^8UHB$O(`bg9Yufefg2l?&+D6OmtP`p-0p*>nM{O$|I9%AlECVu9+Oaz2 zD(e+Sel8{9t0(0$q_ppQgb@iOR zPH&mJt2Dx%e1;j{V839+bMxD&p~p04x_1k=cgH;>Z&5x_Lske$d8thFu9RoJRx9!4 z6i#&FoTjuV?RL7QcU}Ol!4pG_&K>qDrP15uB|F!L%An8MvR2dyyU(YQ z)2sX7{l)7j1Dh(rh`WLkjLn}e@7aw>1^sBM!RebRZ) zt<=6hAfp|#1oeRx;s$P?YhsQ%y#l1TyA>sP(+6NtH2NdQH+p8Nu%*(MU{N7dRZeL7 zB19QuKARG1<{>DNGI-nhWC@|W8HwndoP^*WURn9!O1~|lC&~-{HhYcsg_XDwLE7SK zK33`(a%y<_%hV&5p5gcS{0xFB>V5-Ec~#7>1d6||KKjD2wWk$c?@)ELzLt*=^yaXI zThb_0c}#l$(Jsmcdqqcfp{2QHj7$Le(Cg0z3X6%K>gbLi$<63AeC^}jU5nuTB=Qwq znDgcAqWKsFrXUWpAk${&j++y9bq$%UozLAKItRO^r|R@0zM&|UrDiO$2Q@i^;htYT zwjqEK@Xv=$(7l|)0&x8NhRpY!U=E!P!y6^f$48!Yab%QA-8-tIMVKKsP+sA_p^btt zEiRmhYKKfojW32sz{=wzf(o}6FG#y}5*1%C5IksIcG-2?t$%|GOPRVypQ*@5^~Jgx z*vVu2YXAh{`IZ33N?WZnCO*PWC*c9BMbD3K@tEg)==jX={ zV0{L+^o^~aWVpr_>8(kK;;Odg8o;56mUVGrixFjQSv9>hyrJ-D%$vXO>#GVD$BTHY zo(p+w9nPL)8taH6R?AFQzvvs>=%EbvRQgs=nETp&BX|?0*M#ER)@GstQND0LG@slD z_^`KO?}@!ED91~=?cRDZ8J!%UOV#M!H(!lZ^u3&!0rYdBbmes=XaT?p$G};9rw^Y8 z-y0>dRRk-fT+-qtN4}(f7N{`JQm(Vcnx=w3Y?yc|oDHw9nbS=DRC16eP)Xy^zn-Lj1D~ShH{| z?VD*PL$w*XUvTV<=8LaNUmzHL zHyuR-Ke*l8J%uzN}hIy8qgTOld6>B*ZXS~1|k!bCl ztTfZ=^Nd2O2D77e8qUf~tLGIdD054i`hGlPYO6d?4;3J{T8M6ViCDdx7(V5pF_m`F^Z~As+FfyZfL{6#E#|VH z_s4}@z)$WM8FyTKQK-H`iUvxJ!GP=H5o}R|+61d53&M?DKR-(R(oz|0-_!|pbrkPl z^}vVXOeB?&ln4mZs1Du%{Wi0BZvf=2;jMw5MijTY2CH;w!tJMO84 zv2+}ot~I>X9>^}$)lH*jvEa(uWA?RW(e_=-m~EAw#9J35GcTNk8^#>;;q4d?^7k?p z-55zOpN*M3BXwUHb&ozGItnX^mEmK0HZgaj)mIsASLxu}<&)$=f}b+m{#LcGqe`W} z)fw2rjo?e)$aG<_*O+PP%dpF?6fH9$o#CA3qpxR_jARcvG`w{Kzq`1vjpWR3%?0Z- z26dDQO_IXY!CY4`!qBus!stXZmCgN^1SqENho+qSmm&5YN{qZ`OU$S5j=_u-CbI(z z{+#T@To4`GeJmgE7G}X&F=Cm)rAj${ic)zBYNSXsBK2bxO2_vv5AW{X~=r>b8JL)l$oy|MfBrW=e%p1FA?!NQH}#h6=z& zn+cG4b=-guPvEi7z}k0|ya=J_Vq`)ct7U52iSXm@EbzIFfHZ%Y6N!?tN-?RH6Cyms zu!!ix(>p4o=q`PC)z0){XG^3$xMRvop|-x4Q=WZ+1KIGEN`V!Jd&GO;S?w3<_lyP~ z->Z$<-Af$HTi=38oq>8LjVG_L0-GNNMHQLcA#Vhs-s0fa38EuBX57c3H%b^qn(_Jx zya~<;G6LKdOzBG6vpG#SH-=x!=6#uSvWj@~{b;c|&%2HC8@`V{b!CSUj@_(%8eIKmT7zO{GuOp zoT-FSJ{6UIG9Az%7f|rv%h3|&+xh0i<6wtp-;4cmP+FEusxIvVY$US6<&L8ses2CU zOildw@hHEPjX>ggh_pf~(Fa(K5n`h%(P|$BIx_#pR8;?!j^Qnh&D%)fb~IhGZ%Bw= za!V6vF*enPyS`+dHz~!DTej{iVeQS#`J53!^(d5;*PtOa1)rDmHI8W8Z9?ALpO#~1 zoe^g;XC7xy+$QXu_6}&e7sIF(OTbD}^{~X7X-9aSl`Tg%IOXS=uI-DfpOSq*^%lsT zluFG(`}GqRxO#D7RAB@ey_Z-lY`V{0qS)Ak`T++nwLbQ1#v-Waah z0G-53ln<2MJ{)bOCha{~Hhbz0fbVca@@%X~7b*V_j+=h1Pd$pq3sei}@o%B*rxiW? z%%v`;H`gkiG>&k7?=d5SCBgCgM5*b8BQ+9V7iUMeyDbH&OCPgwZ{J-Nmej<kNiO_& z%kPrVW$=-yD3|`L?hsru;|Jbm4~=ehAK{1_-gM>}L&v6b^tP>J(TTNa{wJG@E+X;e zDKt{cp`DzQh{k#Y`S!;yY99`UZI-$sX+G+<0#!;-$YVd4x60#ox_O4V`Pl}rVIX2p zVPJUkwRenC;fi%H4y93=Mv8RQ8QPfnQB!MUX*16zX^T{2Gy%Nl!`Cv=;vdzPdWZ4{b7IJ4onF?OPS(U)(cnpV9VJHet>WPNld5-o@U zFZvaR{7qasxJ`!IygyakY}10RHQTRdsR$VkJE$s4;(>q7+qR_FV$F17lP!)a?)*4L zU6kjOv#q7H-=ljPrUpX19Kner&G&Nqz8G0oi%&J!nwq~hXHDwiQHUGe#J(rHimGLG zA#u0yM!54b3m;x6se(RwUw3cY`o}laZcCW?3Kk(bb*Yylml#!=0G~#~l!BIM7nA znn@mEpaaBU+=OLP-EL)KM(#5OyIuyWj47y8>Mv@3jIvceN#z(?imUCSQ;Xp`tAwcy z!m@U-(eG%t?d%RHHHVgiK#WK6$?l-iTZ4IdJb8K&*g*`uUJlvVVx>RwCxtc(R~7XG zGxE(l+AD$oZ2jv>+Oot$i`2$|(Tj0?DI)mf&v(a*5ea95;>#es$tNin#o1F2;^F`B zdkh7a)o6f{AGivoqLg*_cjBM@rfD!lej21?4uI)M2Z}1QB_COAV=4AE@#V%7op5rW zz8jDwV{i!)2GgnQGqMwr^Z>+;-fEnz2AGzO20#@4Zrm+{&mehzp^ki=8r;1r9ecj) z7|1ZfZm*gyeB4@Up9m6)eoHwM9F+|m)$$B-ne?MpwBlnZVMatWn48e&6-d4;v5 zQw9H~x%7L_w)Mov#5egH{({B{1s5+Qt@ze+#-4?+G&$9ciKV9IKS)l#C;tt{=U0!f zO$L})4$qa3s{3@hN(zFynJz-^CFkJMU1l8xnTnTxu?jE>yiaA(w+!gvW+%mhsPJyf z0OLmrcL{ua9bZ!n_Rey8^6VL4Q;;a*kbNL+;zH#NYhmNZcf-OC=vTc9W2^cxdHALq z2viiK_&bXm+Ck>@yYBt)9+9P~zR!k#v^!QzZ4aiB7i5$|8kI;|hWntk4u`06{J0G( z?AO>Hj4{HNPlN3|(}T`63RI;9w#IE_Ww;~pzp2J>4G+AyD_{7*{9C)N`Iv2hws%@w zZ>U$~$ldYMr5RIlG1)AvQr-=M^8Rj^y>ELe{oYPab+cOUW=e+=iZK-Zs8#nnTb ziLam@nA0&R_IVADKp*-WlADoV_ia`NKq_2U|6#(h)WP!lARNF`P6eXI|7`pJFyU7i z!4=Bxdj#?)eO?!kbM;IZ7z)0Bcq;xi`fwFwSeR$fF#pShyJkUw#gdVK82c}#Jb)uZ zUSv=WoHG17lKW5deH~%|N`VIX*P8uHkU^#czZ_Lv|H*IR8gl|9azmkOt>hX?0j{3^ zV7LIn9Dg@l;6<4kD7^PFR^tiw{x;{Q*-8q~MqUkNhyHp;C^&l^=IbB>tqIUD|I38G zD}X|uCC&dm;ebA4k(*=mX`AT(j|smHF@U@NXOOQ#j0q?~L7sVy?L(ofh$#rR527mU zl|q5@d5AN?-)#&aT8VaUb-?Ap6Jqvz=KAC6W4HlKFkCA4v4{Rcky1$&6_wFl01N`{ zVt=ll>mXl;7#PhJuz2<1K;O`94-RbX3&L?TpynQ)yV}qX=X$65lQ04N`*+aRjQIq} zYy5xOP_82kyy1Vw_$tDH$^$gUe;YbvqXJELAykC|_VjOY{F5#L4CV^;b^T8q_ey#N z<*x{yD37Lqj>V$nLvEEzz}Euk2~wS{^}*|}w|&fNz)v2EAJXi=p7>PZ+8d~a)~Yc`(={L zwd+wxj)jcCdQNse(8&=)dHWfTptClViIc_=?0;M8S_ofp#6>6mxA(x!8oaU@k+lc`-#=26 zl&pwp$tf&%sE{FeA!ue*?3GpC$JdPUov}v<2JfZ%<>jjtC0Wk88XoYnDd>i*6QbD+xEW~Qr0MeP=l|VN-E+YbLn_*A!T{_ zXL@>GD(1M@KHzKRh@6ay2Ny%vqbRd{wDE^btQQi<^`t5)%ydP-_36IE#e<0%(HlLo zvPa+=5MN^Ee#S4)<4orZpw-?3FkP}AZxR1xppr<}I}y}uILS33BKoT#hF||TUKTDj zBMQ?*Kj9R35(Lc)|UYw)L>~9g&QWA32zL9i~V=Zr4U=Zow8QdOen%?mX zYdI^(FrCg_Wjk(l+uI&e*ue;X?|m!1d+p`jgxp>ev*`^d*7vbLD~>Rb$Gygo=7cys zU>INNAK(k5Z7rvA8Xx!N2!&IX^2_pv46VH{&$B~%O+PO@a;G#(fZ&j!F6@l&yyX#} zn%(heuj7HTHCi}vWO|5rrRysNYq80%3)H*H!;77DX?_a6RzIHNlj3#dmavl7j}2?Y ztL+-0AG*ww=+M2ah<1D#!DJV`s$uq)J$zJuyOr0Vr4PPY3Mlw6YHS`NN&yPYQ<_gB zlRn1mmr*)%e_~$ss&h)1sW}MMQi~~Bk(xG)e|OO>dYbhpGvT$E?86nKo={gqgJgK6 zyE8`VQC?!){&&5+h?4*oOqY4QwBGq7Gdg-7TB)A($kRs^qo*<~E^lv8lpkxU_XYdT zsDF4#RE6cXFm3FS^5daoh_$fl_bLQ%2$@04PFyGatlhK*Q^U;b7|*X9c$>xGz&wu^Z7^-IGp7uu(Hr zbQDubUvvkpDIQB!`5~;gCP^)AUCgmmt7$bRSOr<#-SxKO3D+BN2#0?=SRH2$j!Aw36x11Z2Yg$}0UDDqkosaTaKz^h7U7H=2$}M_j zSMr{-Am4S@dvwd2;`L3UDnoK=VH3q_!ynhB@VbIV69{`qPHi6)^uHZ{uHB@?IPQW| za?^mOf?lkV_feO%)RUNF`8-N(`2%<#{Z(OSea6gnyWkz?sC4^cFVh?M(pzr6=!`Aj z<3&!AEPFM$`CT$jX2)cirIS^`pfEx^%)Ts^{_@9hpS}0Kdu~r%J$GUn7r9sdnyh)y z#XdpK+aNhHZMK_r+vtJu0(8A`0$pLZ$gOYh+}Z2?@DrohL^pGY=PSC|Ga~$B*2m|c zHj)ko+KHF!Z|0e;Dc<$tizZ;~wGmGuDEr{K_4u=TSDljXN@o@Ct8-!62D%ivpR${C zB;XCmJUH1&R;E%?Mg@Lrir0&TzO^Hbb_nH(aRw^8QOoyK0tMnyd~KIt!1&z&72@xH zV{*dz$)}as+KjkT(odZ!)+*YH z3OOn*DZ#dUYQ}u()UOJQYKzk7d5^t>rT*=yk6%Z#TA%xu0gtkWA4xENV9l#=ZDQR% zmrci_D>k#$?&uGaww-SM$71ldTnK>!tCH11X z@vq-RK9wLXhi=__ko1AOG@|VD8_eg5g8htHJDOGXVsUmKw0WXGm&FdCcZA`aAvtHr z%rSDr6q&vhS3joe{ozrLruQwg*9>D-phIs>rMUi^a25(I1*T}t3Tje#cbKF~TAq1H z#bnk~V^N0(VdJPuHWqvM4o{Kksnhx&b1FO2JkM}_JLb_-Z~G3>ZSrESVUKPk?2gJ) zGNEOs7QUZbLI~jAG!z8y-3|!%Ulmd-hF8p%#f}HiAxi|SEk!c}3#zEE^Tenj@GYK! z*m5BSBe}w@*Z>tH`SCsB5`Gpq{GG>ZnDJ;VEFtyP#5ica_n(#mvbQV!JNL+8ya<2Z z3=$JDD;_mZkX~ukclCs~a%hCD-fEbM!?+Psw|0~DAt4idHO5wiu_gcG}!F5qxr)vLcjR4}UwB(%fTota>#5@qhJNh3@_d5!*TnwMC@qXhv@9MM()giF zc5SR$Y6QGn(u|VEUv6_QCFxDvel)vhR&yIKvVrrF-vO4z*sMzkiHKRZ3Km0;S>??L z^t;Whxi*SxO)z8SaW7bucHVxScgN0=;pWG2Uei;Sb(zG|FtS(n2K~@h$=*E5Lxb}fRW#GRn^&$2pg@!=kMWZ^Ma(qb83Jf{9he~ zMhRI?q7}Yg!CPJ2lr2s}i}giddIgk5)I+jFcmZZKdpo@!&Aox449W!F&=nex7ycSk z$%hGmXWzldU|_@*7*;FaeUGQD4dB5KSQ?umgj^p~p;`t=PdXVLkCgQee}>xH1V4Zb`wQrxjW?KKB$ zoJF*WLudX(Soj{Oi!6f2k+Y2j(uybe>jv5&6h&RZPU=M*z;Vh8_^N=JP;sLxUMORu zEuD|sJzA!9VDOW&7?Bw&hTRy{X3AX4s8sr5iw`)7)VPRIn&LQ_U$X+Ix?}3)|0}lQ z9`jWFtJ(1%kM=tkiMt*g)K{xNqIY_Ov(NzyF+{6w3%BVGDf|`V2yAZF0K0RD{%N65 zc;j8JJbZ41n*(hknxmsDhNaBV7Z8Gu1Qh{kW@~X_q7ni^SH%{MmY%m!i%O$e=)ucJ~i0msyu_8OyNZW~nH?-B1Xxoj;=!Zd0hiOrr^w3C*STKc+ac zk8V%uuYC6X9I7+pja~~;bcX4PhPl6($C0CR^o;V0#hUy!(ar;DOh@z8)<>)p`*6)& z=_<5Gxdg3Fq|8F@b1%xv(>}d5?;%KGB}=qXAU&7DHbsZWJFquMyG{JGX`XA^MZ&1% zCPB#+i9@5NQPw!>_slero*b4*Xw2AzbqGj-lTSe^KBq(;@_wh2{A1H=Nb_}9dXkd~ zC82_4OtDl8Nh@yHFr^BASYp!894YIM^`jk=2?hPV-@l5B8)xFS;IJi5ZA%Yd*-9N{ zWq)8XvIH~`X{#7z;pUq~O`a{kA#McDzFKj`>I1TKQ1tM6A2&51{lxx2`GWaW0yly+A1PVovPC*@iMm?Rs15Bf=W1px9H3@A$j^-kj zA1K5Dw6I$(5W2wVhPN0KDva@(ot>GlL#sHHr2U22nM;uYFfrI8K>j zt7dKAmGqNNdXdf|2p!5MkMGgg-1g^`jB1L&N0DE$cX8j++^{BH&vTDmz8u5IB(rgy z*wUw1-gJ%a*&h~7AA@yTl|O7RPgDJ%vzYmEqm=a`pa~aC8}p8l(gb(7EUMN|dmd3q z#9A`9g+;52`2#u^Db@2iW{ArwsHkO1McjgT%!JZ7PcZ z}90p^kQinkd z{pDfZt5GDD=RY`b=5BkI<}shu4Iub!!B8R7nk4Ht9|EJbPj+GDpUFuG$3iX9`)!4$ zw`n%K#ujXOJ0m7%#7vo_O~8`znch~FYj#a$RyLE+vGDav_gmS{{iyZaSu5@XkP#zX*+VRwTN;3QSN6o}D&+Ofh(cTe?N2n9(8~)=#)^-U%cJfo2 zDefoE3ax|}aj`@7*|+2#I#M*c&1?=3M+zjRs>53r>Oqzt6QvzrAmz@fA(|t_f6(9X zP}&@h{-*e&dZQ%opJV{8S0TEU{U_^(Sj!w3rOXbDQU-z-%WVE^oF#Tu zf7nrSJz;CmWi>!QnJjAM%$6#eqz3=Sx&WTFC-?zX;sO{prC88e9o!fG`)xRe$T(++ z$(dAsJkEqYj`kiFq?p*I(IF7J!koBX6gk%YK*}xDx?1t$Av?~`6^=?kiEWZ%0EbHY zssRG^nS=)RK6?E*2?n`5W)-F;H_m4z)8^ZIOXc7w<=X#Z^Hv%M%?|Yt?aA5 zyakC8YkZzRJ_SkZZJcp{gCglRy!B zXkMYicIp5S%nsXHPSGCuBz{GT_cn%9ouk}dt zq!=!3x)(hFC`z~py^kW}mSdL}b)iX}y5mQPO&gKSgJ=~<+nJ8M?HjaqA>g!p8>12N z&nbz39?FZqnRf}?zjo^voUc9waFJO5-6uqCPs- zEerE2kTj=aJ+!}_dzp7LhcpuCO{B;G4%CILRoR1{VY zmh3G%01*5g$^1VmO>v}uB1u@24BqG8 z$AY%!6_fx44%b~nef121g*Uih3!suJ-XQ}{D?BX35mu*MSm_%H%;fm7h;FSPruh?h zm|d528l9Jvzpp2N&362LL{P);# zh?8yLlYUbq7`@exo%PozOVI)X8^woVdDtaiTpPa{oCjU%?R+vVD}$HC(l1_M!-`k`o~iV882J3%WtmYFOd1+Rgu8?OKSnxnd(K-N=u2KF1=KAC!Km%Jg%hgy4PJQ z;9i|qukUVg42+8T4V&+1FED$ay-&9i0PuM!%&6Fh)ehjI9_GZ+urNt42RaDeX zdBHhoU}x0*0J*>Jd)&X6g#Ub`b#fE{ZQ^6wj-s|`IM|?jPu1oRA0Q@OI^Z5H&y6<% z98zF|6?pXmK3L)R6t2H*?A4&Z6qCR@j+|Ih+PYa#G4C=znUe$akeW-|bSPZGCmBqI+sHxl>&Os%sV0Dlh(onL7E-z&{8-9x?w z#J`mfxv;}(>Z=Y@bJnf<7D`HqE=|AF;7JeuArfS6$o8v<1Ta5k7v++LMFSSB2dfn# z-F+bauW5XzGO_^0vF&(Iae%MKU)L3Uk%QU(zO|sIzs8#b43F=phgH>~1W3}t0HU){ z6ID<}=dOTRU3%XZ_;SioC0-@bD{0=l`6uql+50yq@%_CHvHvRs-Sjr3*)mS@3!{`T zHd8vG)wGRx{vc7RP;=)FBs!{U2hits4H*DeLbLfseFg=H#)C~SZ+#7$POR@zm?PZmM zNJ;>f$mb3PmpB$`+6qFJIIv$24724-8vXcwCOzZ)kZu2 z7Um_-qwrBkxuX%Hp;d_Nh`rqF+9Ff@x?BOKc{En7=f=xtr%vmSx=E@&C6BN~5GA8@ z2B`%}bJ#~=I|<;V>9B-Rt9wf%365^w-Y{%m9^TfF#SDN2doeu^70QA7A8g3BNw)yk zKL-yFQZ_fLB1op!Au1>cgH@E7s+g|a=3GP~5SknV!J_~hihBST9`7jT1mA*&kdWK! zZ7PKZa#dN{A9;|PEO9J!N4#HqAHEDQ;(bTpXEz7ls_7$r7zQJo(*g;%Hr7b z#OlN(SUWIC6}LH_F*s#aGz-- zV5)8RXsHvf?K}%fEZ=zl|=tYcRx4y*1rMW)xFapHHGatx$4lOu89e#QUXj<4>n z^@h{<;;4SjwXSGHTiK`%(QvP`$hwjwMl_jE$sZwKjnRdCO`^%bTI^*x=i_+@ZnoII zFltk6NaaB`{xI{zY*9WJ7|&S4o)A-0MUwz(rL?`Gfy8{=p}pL2L6&+61{L2YgN!6n zbnVif3B!^kbd~3kwFDB&tb6d^NJf8h5ux0w>LeGsDu4B$tEV6ce7WY1EJ|99>Y-os z68Q=gAn!|mrW%Mg-9UGd&!{20uBeki)T~#n}z->Gzd(`h8H3S#- zE}xaI>^^OQ5`A4AXmLWCU0h>DicK6HFkt8?DhL2eO<0gh0R~1vfZ->gqX(#Q`Bd=R%yo zckzf0S!j<(yo%0H^uL-T+ti)V%fH@2^Qjy|>*O!x+QpCbOn>t-PBv#nX`oUD zyC$y|N-j5l;Y${cBek_4C%ZFAoKbQX&4p(fKVMj%*&tgPN9w_S7<^XTnH(*Dgdp4H z_w*T`GvQfO{^Q*_$oKi`Ysi8p>x(ebnBMQ*f8gJ^p8_h2-*Y1WsVaXl$rS8=E$hhq z0%l32U*10eP=(Q^5QX=-sdyuo6fyG{N>Xo@$mUcA)8!e_gn+wS(P3)<-40|Te-lX= zj-K%aUycotgvr`eeYSvz9X@TSn=85eCJ)lIR!kv2LTULpL}@!Kx`i>2DGORbeX?5W zFd!03!nz97gFxEK6YRHtT9nCNFdlYK#`^BTM4K%hx)h~SM8%a4I)LER$V4AtvmA`$7Msg`Q*(f>hS`6e z+JbiOQSncb^ks(@ycAvxJ}9OvFxBtfC$eAb=RfDnrr7z#MnQa{8wS0*5X*=4MeyTV z4$w|r?V z=rU>il7^S?HfWK+;;CX`40xXgU^5sPVY*;2vD)vws?c~8BnXxr8VF#N{ZX#4;aPWp zv8Pw1CX8q;Yabhjfc>`?ka^@AKE{#Z?bqu~w%5I;LV}I>lw{{la?6Wj8j&sYOE4(H zCa#a>c|>Yo!mQ`v_Zf5H749w4J_~u@4P;5aDQugU8y4j4RJ$^kr6i4V-Q<7qxqmAb z2T^oFvGc%;$N)yBXC1e%X9lhrmL|>oDfy?DvETawcF3WnHk)|}_Qt{jRjV^HonepN zm@414VZZ;NF$Gjz;8s6KcQE+YlS(=V4z$4Q7Ilh{&N%& zWjL;h zA9o1Czv{++3bTJzo0Pszr)T!6;U}KPC`Y4QLxu^! z>=ucTXy!O6pw>GL+NIo5W!G8IfFSi%gml8#l=Zv9{Db00X>{E`^b}Sg;3D6bYAV2n zqJ;4+#f7$=$K^8!r;DE#hrh3|OdJ7~PMd@T$v>PobdtPHM-G!5#EXlv3L~F9L2m3V zzZ?+Y@cQ_>OFSCOtDe)3 z14{a#EQn3O7!4Y4%ipOVqzJemP+jbRA5DbU_oPt#9M{g9c22F`|LFNe)U!z2c%9>( ztplXA>JMvJ5z9On_IcLnUlx^K4O}+~Iulw^8ZWH$c4%+=Cc_dxLOrhZGvC>NZ4|m^?8u152zFilv}mQADrSTTB5& zEp1qeIe$gw4YF?RR1}436%}d>f~)ZFj|AExAew#M7&KS0H$z8Af9IXSZ2C-c+RmZ- zi_BlFzkhpSq-J?Nh=CH%#&?muCzMMY5s{c7ZWP-~m6>FGh(`6AADLPfS7j*ynmOfd zqJ8FEZRWRE0i+_cY3h3tQ32JBvfRYkbWx3oI^X)X*yRONAI-l!S9N7c;%5kQpm@yE zo@sXXDBub28uEC1y zzO0On2#%qE6v?xGda-myh0}K&b#3v4Qwhovowu<^QOe62Di_=idUhqieb&QvZDrz` zQd9YSi&9kUKB79}h(z}F5ZZVh938(z@csLDdiLS1eB9oclfku8WwTG|wXvj& zm8g?^i!FYD=1D?at7vyM;xzLw9D+9Mmd;fx7^_UMS|#&ShCDex>bz-97r(Zwnj=Jb z4=2Lzw69VhBdEWPt1L>i{V{;COX)aJ6hhvC)`@kLjUf+rPAg1*fBG^#-{Lu`O0vh5 zEo&|lJ~i9GfriJGn8eF-q~<4l?9LRpI%JJ)V*C7Y`|@xxC_P58m+sG}Z-Z?wLU<0k z{Xc!S!W&qAbk|Py=eR@sRm~@W9n=F~$y?8%+lgCU958#k#^{*!NhJ`H8PU+v3PQzM z`Axp$>4ziuZ(?9USk@rtEm!Gd}zdC2i~a?FlP#`zJ2u+hmhL@bBmi#-kBqBSK9qY^_^; z#wk0w%Oc+X*#El9{WXsBE9{CjtO=&pjrB4FI)(e#&NXKjylkk+TcmWY*eV$x(Xytk zk@@Q|9o&P$xy-gXe`g#UIX}gSv#;AYhT#+npDwr|jz7xxo^?MmhX%>UX6 zKd0=cP5?_fFypD~t>BEX)Qm(UTd$ilW%ao4g!9m!Bi*Pxe>(o;^QHofi<~$4s|q*M zn|`i{=OpOmVrP(e^*faG-+}Pf9X+y@5gl!B)oq&oH<#CE!c{$+4!k6Heh)X}Kae$u zzguP~=xwcFpswox0@(t3pIn3iz({K;*8&>WrAKjTC4ZBk?R=Ksv({w|S+KPwn$EPm zRA&;fy?|lQ*~2j~Y{1!q$iOrA9H@ovnMH~8U2{}5AAs;=T0DYWN=0??z0ebkrosZK zPfQffVbVxsJ}6RF$9ubIDMNoaC@j!W0TY`h00#3jB|pgUtVbaMqX1^K<#Ei%;TB-s zMg+z`z7(`}{PDO3IRNz{fa{4%Inc2yP(t%TJrMwZX%o?$vKM~L%C;0k z#o_TZ{(e#|0b%zlbZ|Kh3T%#D(fF8>35y< zuTCQZlK}K*ULrBoQOUhacS^mY%PqEfKwRB*wq?h%?Fa?y67V)X#KV(nPk?P-OAA1R zkXnX?ktJY~;hu2VZwa8nxGDF$eFE?d$zskAm)|l2r4Tx|hyuyNGA$(e&W?etInf$z4`(Dn_zN}que7iS zX}ib0v|UK^!B+$6JNrx2|67v&%gYhV|5qShC16sxw~##g|HroSpUCZ>K)iRq#o&PL z;JVds2nAx285+)?YA2;rYV~;y1e)JQ{M)An z`uS_A9h_ryaK|L~uL=Zu*kDaYTB)~?`GY)k3V@G+zZK;=gc5~76#tn4g(r}h=0Sn;lB-&Q4}P9(^3{!6HtFD ze^*XgS_wcc4-6{=CN%I8;eh^PvxEjG=d6H$P&o6(|sn>c2Cyg2CpQf1f>!)c<+)^F>9Kr&fO({amUU z1o$Swd(Rf=)C+jg4u;E-8;~WqX4#6K-t1ifF;ktE+Au}n05JD)N@bKXMVd7}B=_+~rKv=vOoLP-| zvAZ(lS{{$$ZhVKjyLWe13CRQ1(2HWTfB||Vh#(R1U&qJim=S#xdks2FaWXePkI)VA zIo-P;CP`OqNf+RtAwBV2ls7bGM2L#qz6zph-gX91{Xlp-l)d%}BkhV?ZWxm)+4hda z)ORR?&2xygH;NpYW{qxa#2~mgeB(Rb-2TN!E2f-5aqJOz4rj815o$kt7?jP?&@Td= zJrR@pJ%b07$WMX_b9s(`#^&WHcdu&d_;g-OVXLNg=Q@!DI1vk}6p3Y}Vl1~u*quaN zFJB}d{y5#DQ0R-ZU8iKIHTKNrgJ0IC6&EY?hC@XHlbSH~o$S|Ws-A7?BZn-KFP&|PR&!KtqaYL4 zGNqtg#_e4N)^9Fkkw0OI%Ffz+2c@PS-3djdArvr(e0(jI7$cF3b{mGa)r>5ifo|jt zJkxh~{FOze|2<^$K3GDF0_f8JNtI9t0q`v6#ewlLWz`11>buo6=WDcBVkk{u{?H(N z4xqzIUjxL&8(SjWN|YESuJe#BWxlOP_dX2xvO+ZoX70 zpv6=%4l!)dNspZJYd=YNu*P3QkP5VAwfkml#VV4FZj|~Xh++)IlW)+7iRhm}z=elm z3pL~w_x~)Vbf7%LW zbG*luSHQ+kz-FO`67NYtp0T;xF7Jt1cEs?Uj&!p@qr6r-&j-_3Q@%lK7Qst#n~qX3 zM1qOB>i#Zop#qksB@~LOx=3g0Dp$gcn<0^P(Npbcgk5XqvzHLDkV55aCdx>ii6`74N@`Z4MCcZ?>>Lyir*&m znoRL>kJw&c>3tbuD(Dm)}|*kY&G_-A7#}wBC+u-(;|qH=i8gChqrkm zjj$-VhZGe;Zj#?-l1Q36Iw5XG6IjQ}t}2Ku^C`AyvOAouPi#`6#_rVAf!03~-86)j zB)vysmQU$TxbZOktc0Sn8-`Me&MWFOgv}?|!-D!;`b4NLED%uxF@}U!tHBu#{O5B+ zUPp-EdpE-b>@i+Re|*hzGU#J&_Qv+)C-*FimgxPl$>R;)c<(XNZY7)xXnwaNC6Dir z_{tn6w4ninE^49$T@+aGAtIf4n2b^(3i>nmb32NS>%#ps_#cAooTrBGwjUPRPh{YIG zAl}6wCOFfjCwA63BuG*jD>@65>qV2gN(&tt=%e=Tya<=mClTlf9f!RKBcw)T44o^8 z&tAxUwjqP<7B~(+tS&@JJXvXyVE#f-8a|@HXu8o^b9XIv@U98Ru5Zb#{HJ!I0rZYH z6sfhdSUd_ebQ~+QouPfq_~H3}{0JR+LE4CitK#W}RiViXNLPdec|WH^`rJ}cbpyDx z<`)z>9wkAD`3?Ln%p?p)Y-kGZ2e^T6!(R{=!V>ceA_m2y%(Eqp@l;UJ%m$aHYGI*X zJtJqK;c`y0wL$pEpfN0qE92SEZ_<^^GqS%RUrUp2hvU^*Tn-H@r=3W?DMep3uA{8j z5wjbNf`l~H?l>Sa2JUWtnmLsjfDAk3i`w% zTNxnF0B3O{%5b2(x6<&{-VKMSSb7RmtDK#XDNH36kZ3lM;A*T;71muu~r}$n2*1d-oQvw?x z+Hq|BBY43z3$iAnniAV72|N2!Ar#F6pmTn8-#Hh#V$hjHSu_s(D9h818c^>g(7nf6 zVTIKKiH zW@m5rt)k$b#WE^BWL`aQ*{htKyB8*+2xqkor54Xg^j@QBV@$_}e>qBZ%aIW(LbO0o zG>v?(-q)KS?MRGB6l7+}sfArjrMN2wf&7ktkpyWbhtd)grXCRtsb2_*aZWc6}RS9OFwIV=4V!p4c!PjoLgvk*SN4} zzj)xBFX=}q#2?!oQJoi#5p{<8ZGK0=T52KkzIC=PT;H$S?0XAiAxHG-`?OVT=B(%K zibYar=^)B@n9r5Ul@W<_ju_RShjX-O9Wo~k6(Q=7V#`--kOeZ4#8@6WJh!=#!@f4@ zU+2wA=hiLS3{L;D!67NDsC%SmUlH0(%3W%S-M5vfIKh@H%(Q5vVj|ACTyn0=wg`h{ zRjbk)%uvBEOd!dmNXEq6Qsrydhi=F2TE%!{(C)WAi?t%cwwG|*_e~Y@?#*2{R+t0@ zpbL-3K6~g{)d49eK;Eu@n#y$mgYEb0B~FORgmMrF6)%pL7XcC%lQ9Zyv8*r@!9OP= z#|4{7TMWKwH|EoG!|TZa2R-l9TwZ;To}Jh0mumt?Vyt4R0RaJ&5H(t(DS+uFOk!)) z$P9rrYj@gYgMB^WJmssuGIMcPpJT%E@7 zUZwZ=T?gkE(i*#Ig~9?H1w(#jO0f*SlvWY?(dI<|DQ<%$c9i73&3BFy2WTDX8%NgL zinx+^Zkm22GqGz3bH%flps$_NkDaejF(bv7K@uxKYD4S zJb{p}&NUmT8DOUxA&V;Gd7wV%D`uZLYny(~&+QW6h6p)Ls2?u-v@yBlXeCxu{e`G~ zS3{`N(ddv=OTrohqDQq-*|Of!$LqTvx!bB>~jdbV?yNnKx}d{gQ$>s zmmNN)J}IPg&xjXl418+ts`G7BH1r=LA1&)Nt6lQzJhx)%gJo}G+Q)NzH>O@zC40bv znPd?2f(LzvnBme;%~sP-N1>&aU}pH(_1ZQZ`r5a(l&7vYtz(YZeD3JW{xLQkKZCa< zI!Cu1ZOiK(!fRgl(8yw^ew@6SPwCNly!!icarr7kC2$9yZA0PNr%^xDX3;aSHO(kx z3GFxCQtk@SU|yxNH%Bt<-&yE zODZc;YPvP)lGD&BQT;S2a#)@B>+ZNi1QYm%A6DScIt7twbgBu+REaQ;Ft~pt!4ul> zZja&D(`Vw{V1KTk`5c?loAwHpdFxT2ly7`$q{}3`{aO8oOZ4c+7W}Je&!+Ft5=QRM zd+tIXVGg+O=?}c2xjV4F{zdEmPswi{rawS*Rfpr_;Am-S{a<^hNIxK6Bt(Xh`4&OcsWJC$=7Gzjj-kG$p*npkCjz z>p{8mN&)?_U^a*WmUhy&Di{9EiU{O*ju+_{NpI0ff1wqT4-J%A=+xB%A0Cr^xY$N~ zzaUm{!XIQH=SPR)H{$4OHG0 za7q;~XHY*WKeL1R7E2x?7}xAi_OVeL$FxV_BLddiPm^BwOQmAM{?n6sPO|t6Lqur; z0?Z56r87P6G;{QzlyZ52Wmz5v#C@F#G8TQ|i(}y&VB8+~r(3ukjNm<>w2_OXSc@<90jM`ZT|A1AC)!$YaM0;mn63|gd5i%+3S zEuZVLon{)ZNa@wy-%wpOJvClQNhunrl-9B|)#E8p z<)$~wgaGj3ofWA&+??lsPz4V{T8qDn{m5W+gWs5^s(K+;>2G=<8O;qd;2(kqvX4RJCU`fG&7ExdudKg}E|Bo^=Xw;@`0U4;S4@95Vd#rA)={Vosi}_&*EW(;i z0L~`>G%OFU*Fb8<&q^ef(J!U*0XX{zuTIbm8C~G}*GI3WZ z|KrRT4aK1|K;!^-5Jy{5%AP4$S%2~oG{p6v=NK3GtJ3`gXD5te^!eH){$wES6tiKx zIVoijHZO(SNDZ~Q9CdO30pXF?#Gbd+EjQo;0r5Wdp-i3v!fWoaaAs905WWm7v8xX( zbPlWJ&2~wG@ZK7Lx4#+~t@JXJ%R~3YQhnXm8ITZtm$UQ>v;WUXo!r3}xl#SSs>G4710$kt+Bo3xRrV{1xrN4l ztDG6glODbzcuA_KeHb29cC^wJUitw%Dm#GVpYhnKJytlV(13164e0qa5}yDgEiP6j zK}2M~7(DQhWuQmpKv-o2Rw!p+uLDMIeEK(0(E6`w8~&TH1$HRav>IWh zPvY`7AnD~E2S@p|eY4G=S^1EU>&eOaX{mkzfY|w~sr0yhPehh5{r#T(=IMYd5+3|7 z_LxYB67pZjdYualnsS}jUfTB;e% zwBoW8o^G{oVKIS92RYJ{Fz{K442|gbfB#=qOxNEx2FTh&{D-Xp_yWW&?S*mhs1is1 z1>Ad;Ie2Uc==9pq86D=Ig_RQXA&mmCB$Z3Gt|@a`Om?fyGH5<+w0p-+gmW(Okcm?Y z#2EdL86%*-XmnB2{a+91FBP+Wfh=aKEfZsYkiN{lhM8n=r~wa4lqyc*hW0?wKZpPg z27oS^5&XxD$P0iO5#OO{prJ>qy(%eSR7l#l?A9`xIGjgNeIbZn`F4~an!Yh+xQ22X zJPMY*DDNFJn%@E~aY-KT@!9QvxW-v;6jjX*EsO}$KMXbXZIpf4=RfNxSn0&}t|wm~ zJxR}Sd^7UwsWhTtt(>9;Akcm0C|ZSvb4(6<8up0gJ0Y`&PvDpthn5`-FTF=V6imJq z2o^{D15=*J_$xYG2`@91PfD&4@AMcNr{VJWQ=yoA*67D~!p1c8NA*>IAQ!deD~03d zi|hrvxgcU($7=>0bL_tPAXbQPLgeS`FGUSOA7ySL={EY&GV-z?(cGxj^C{#7QV|n= zj&0$qD$Bu4YmY1#L88}ZgWVk++4+shFWKi)YY`45ohgf7B3Se-y6_(V*=qEs+ zw)-1Z$fhEiZ=27{y?A_L)^TS4-}aA7Ps~$NMGpjE!`V3AKg<*s%k#09UUzNtL?y|0dq>DG?4;Qr{z)PVDz>xo|R74Yhn_} zQO!2^^}3~>8WKFj`+2WxhR3Vwq`jj#0)?M-v+4!qUO7GOdcBgB$z6b@)R@6dzftDO zr{pHba=@VAI(c|mtwJtgFN%Em*xL3@NdwDAPa6w*`hKY>!_u9k$zjKV}ag!#* z9>$jJo%X=u60sx6!7fEh_LEUG^BLv0G6dP^tNqf;39l$+h`luLURt`Ude6Y#U_29S z9`olZY9>Ga{vsdCe)pD>`{{`vlFtUT>pK+K#M{?cEziAgXYxshlmq*0Fiu(wpHZY+ zM9UHL+Fj|~oz__OqwyNE@-=>zYC=spldFF=d5hxUIsN0MGxbKNH-YLEtDD+cRT8Mn zC+OZ71yI!LZTDi_)%8L66}qW{YD~@`Nl<|nu`V(j<1mnT0a!|UL(BR!xYr&59%5P!dj=}b1Z%_N>6_PlU;dpSclST?vI9fOLF%SF#@CR za-odGknhz|c|&g$+mKFb-?bC!JMLP#997vu;ZG55t{c_pquNXwtp)K$KQ>ax86C2| znVodz$b%tiu&h2fP9RbH+^wxL&dD&?j%n|=KTyJvLwk|Nd7_l};#BC-|#y3DvWWJ{TrGt12E zX>6+B)5~7$ld#`1svA%T<1EWYecPE_{_<6ZDOI+Tj5HIvD9PsH>-+}^$rpYSrCl@m zd<)U~5U*1y#lt;ks4|U6`^fXrg(^mD5O*%a2My3 z_wo5HnYR?P&ms1z&?))v9sDPdF8|;hYH9;O<+N$wIaO3o0_hYYsp&OY&66mUpykZ8 z*qG2MV1oFQAvtsJL+1(heFyc`UHiIhs zI18GB<Pups%Bgo#%rqRI6~xZMADLU~QJ`c$~BW#p1YuU)*&z3)yN_fLvwJ42u&f zddipn_~2aYrJqbnRVz?I0nd5s$C`70Alm|FOgDYbc@OYpY63aUfsiS`tZ{bBKFtWE zu{EVul3=-w#TGbp+0P%47C-b_fTW;jaj^A4qq8Dm(g&2}laa14rJ2om+SKWi%_{-x zS~o{s=g8^Sj%kr4nE2rr@K(YhMFg~|s5mv*5%Ty&9HEG^<*g?n#1eR__-qI%Ue!wo zDM1pPR1JnN0}Tf;j>&H5{o?!&#F{%q227wy#gm~2MgC}wdRVT&x@+edHCxuge~heA zQMj%UQT!>duU9gC_P&7N;typ6Rqm&S!tsl9!Mv}%kHa(cec*M=(0hw zwwU#`mX`+T&Z>39q7uf|I{w=j7)8PUYd+|&q9Fxf8SyMt5JZ)bBYd?0#Xfm=ZcR;f zECSIC1uY&Px#YM@$fig&Th0O^Tt?s8Pt02=AQs1Xu_XorE6Cvl$z5ozJ?+Ac*I2N( ziUKRFdC9s#hY?R8rBpGEbSD;Fr}Wg2`rFhtV~li$sKWjv(GPRT=#d-cbRskn^-6zt zVPD1sv!5bVI79!Yyx@A3}^`mXoFgcHP~ai#q0qOHo4!_(RNw2V2&zIAE^ z8gru!xEfQD@G=jKIIHTQdCo{w#Fk}?=sbE-k8kB@YBwW5Po9SqcFJ}bWEJh-#1ox7 zXOI9)2DYe_q95goc&bhcMW3s^?w3NgtuPDQ0c$~cQQT1$)p=8*=DR;O6=x;+`EHSX zh9b)Vs%jS|V{mWhEwd|r=g6~lH{0tCqzSx0VZHgNJDS<84vn|?KlIJQzrSw!N<-6r z9VqpcdK%w({Agy-xg!b;h;H5eHa-0sYoG#S7Qsmu2m;2KNHC7~&xujx0rwRH2DM)d z2RS0PyAG$v&oIF^B+IZ``(QQZ1X%$NEqTYy$;w|27((M^Z?^U>W77_T;ObE+8;mXR0)?LLIG}5gG5%PEa z3u(OU=UFIzk}#D0n5^n7jv!CE=vF|I8w?Dol@KDkawAg|s7^3R;>gkpN>Ev=6KGE; zeWao)bwOUrlGk|3l0Z#taOlk5Wjf@=X}%yq=HvZjdx`7pfIO%TdUFYCAYD!TOEMLH zUI=bX36+3McYeBOiBEpa^hY561lY1w!O=Bi>z~k@?LX1W zhzK;xhBPMW|ecB*nDuf{ixzH1=z>v#-+St9u{0VDm@5w26b~ed; zc-Bp5H{P+g^3?zV0|N2)5r#z@9zU|&SW)`Lr;z^}c`ou+w8z=1J>I+G{<|1(+S#C7AQP?N#Vd73|N5pOM<{CSs+s^-BaFE!`5& zS+8&F+AN@0R?e)I_{9q17Bg1QR5LzICu$rev<(~O}2JG9Cx zjWElIOv(87$p-L@<-}$2R0mVxP#+ijj46he!wH?#w;mf0*r0x_-)&>m8siNu5dwO% z2Vt1F7+|P+AQyuiewG7qr1zQh$R@ye#{T=Drj2kv3Wq+CC~$(HLR7TKB3PBy!v#P) z6wChbo0?%}bnIeo#OYoZQGb&4L+F};4ZL~q0n~2cCKdSxY~=R_;YUZK$|HktwVIHo z$M>*dA3gHsEwoeH{szhmk}yj;GP+QN-iE6VPbcp)Toi}1Vcdq9ApNmY=W$zg`QV~A zxcjxGXm(i~0%gdw#pyvdp_!!sss$!~$!o#Vvwy)p0sjg%pw%=wynUr*1%2S(YdfFZ-o?Oj^dlih+b0oQSYjSaKQsa=h*PiV9wSo+90h}EPywMWg|!Qb)&(>+je z2s3QS%!xl*S~|GBfopvwLY$Xd|B1ldRMi2W&8>h$Zm5FIqm>jc(H+k)10xA}I?2O> zm;EzAjv3qZYAL^dXf5|-I)oJ#HR%(Pxj0>}rrqetZ#2h^CE@e&%l+Q3D&ik=EyeFu z_Sp#wwS4#PFh>f)G*7w>H!s?z!#!(xw^1&dO%=c~ZKD0^5iO6$D_=KeXmU)3a!&tJ z_V1?zmf4E)L)l00^vUV|SZrUg`3m*ucr4I7f=vQAwGCj2p<(k8k-j5~Zv=iTE}j=$ z7$~!Z`H5aY)eo8>@n!TB3&B8A6xNDMKkYF4)Li^R>G2g|Ak=Z*I2HW!naOWa5klOf zDtp1%$Pu?#*2s;HF>}pd1ZCQ|zQFQ)tAsu`{|c>+-`x7)br`Mu%;m@gbc9OfN87qq zqHc{E$c86lTwKL?3lfpvzao1c%%4k)mf1UxPkoWqisY##&^?R6H@>IT?zy#YpI_Su zka&2nR-xo~r(HqOC((m(b(o@Ku0*3q4vhfj(#2Asy>M}$B`pn7={@V*ni5+L2Q|a+ zz^_X$Jd*L+Y>XL^k33XrgBg!^K598$Ay$Nf69GORhaxVN1?V5PfZ1wao4>X*guwLJ zIa%^NWzNd=Envt{n;^XGU`AVmJPg;A>YWQF)zDF&HHk=x6((@C%&qwlMexCwip9ay z=lQd2>91}M6mR7OO#v&bCWKj8qGQs_X1+HS3cZG=L>N0<9GKK$N|?J{joJ{K3e3A_ zQ&8mdVsa90?Q=+>?-Qn~X;#+i{HP?*)jv6@kG9E4ZqNyna9=5{X-F1Q1^+BnKT~^q zrMWa%cYR5D_xuG!)HhmvcN|qFTx95W8^N?(i)T;7q0dNc z+E`-JsZkeOE`QSZPVilm)P9=Oh1Z?|iyh6CIfk!qMmP0r@|hPygRb)^&tz5h&6+vy zJI6$--JYsF#BAD(_Nft=fG@9~^XQ$~%J?;~LB~RRys}PulZ~q?)!#C{^7I@%65p}* zJF7_Gx(-k+TuHwI%NGAb3yit?H^P;>lVVH=5E|zI{f642b=AbMz0d;WXmbawmlyz1 z7T;1$E6)I}{Hc<P}?WP_M8^+kcoP@#Wh+{QY1#=)QMvz&fdC zY}5iI2VZg~iSKy_}B2xk#)f#0nr5#70w z)tnq2C&~0rgiTNRmjkQ&3b^e0U`M2}t@KNHfTI3hV`+&Qd0Cu-Dv-;gY`H1@|Ksf~ zqq^+cEnd32ySux)q`Nz$Lt0w8yF-xfP61I$q@{=?gW zl%7@nHDTd`SZ(mB0%+wwztdp)rGX}dlhm)GAb@>2|6_oRS0Hj$ zhs>;fz;grCqy08puGiv_XFsYSVuHE((R)th^OnqB<=vs6hyADr31}7 zN6T1UsS9JIjz5E!P*_2yjU|CQAyAbrF|%ftY5TL)aVw7$_kMU1x8mL^p8t-M`3m&F0O4!- z@4nDKgTmqTBp4+2e+smD37OLP(qnJLaPR{G6Y9|q0I zDpcH#f}9KJA(dh(oJnvXn(Xi;?sUl+Dg;OWxk7ofgb^{8D~8%vNIWWlIM>R*|MMM5 zev{t+%mtIbJJblt6~LHZzqC~RG_j0|1~2lHh@bnJ!nDN^|Fj$c@Fi*(*6@4iVwRUz zJazWs-##l8*LZ@!_XqyF+a-5b8L&YN93_C(Pk`OoET*qsr2ng}=*AM7iD2ytRMQiR9R`Hw~XPb4|%-xK<8 z$pt?GnESD7RRs0jl=}IzNgPUP*mMacBJ9?CBL%nVT$iVWk*5X~2I-%!bnKdl^uKZq z6wrP*D*%8$U<>)zVe{cXWbb}8jDQu6P?F+r>f7J^R-_>R2l>WRu){cql*-LwbL5kW zqp|ssdhi(8@YKwyPxu2?s2jY|0XKhW9uq3;z^iH0==09CT@P9*kD*6ABq+(H$MOTe z;Gg;zqU5jc&fg9gn!mQZfAJ>5L_3*ssyUAf-(;5GTj7!8KjDl@0lEX>-V@G9^Yg3y zr~VEl@G+kZB4owMZ#c)Y!otG1X`fMVQX928OIF!W0w1agZLwMQmwo3k%iiYc*V#*c zG?jlk<-7qsK&%#n1q5#^7-`i=qOhRYVMu|wBokPA52|2Q3K&pPJ`9AVV3E|7_+wuf z;eh?FuJ6Z*eXdq%LRW~Z_b-38zmy>Y0gsU?7`NiU`~0DgC3?}LW`4t z<^t+X0C&DMV;S}Y4QsDkcVE23Q3M}>RP3i+7NONqJA(c0Z`~gH zio|V2tP`0gf!7_ELVGKjw`?yOMyXdc;?SA0m3gGcIP=u$Jv8<)b@A z3-02nxOnl&m$|I@a_%i>7aIQzqFgxB{1+@|u?ko*$pwc6OVpZa(g8ov;y7viFJ z_S1HDy_s3xJV)lSh6|EuEb~6ognObiCQ^C|=%~gj!6*C59%x!?-KWpR&Y7!T&v6^F zs2)IT8tX-0t@ATG4WPBnCDW@c+6^*dw;DaIb!ntXfW2Ju5>K$Rvb2k=v|8LLG!&}` zc8mf-MPu9zkyez0las(7BB;t3S(I6%ZGa+4K@KLrEORX*^|a3`(@lv0&pq zl?qXMTq*HY&g{e$6WstA^dz*ay3t|7Ip(?&Ze)_q&jMzI%9a}R^CeU}WRVkOCEL zuOzOIsFodxQk9R57NnR@LKNO~)+N?63Jm(*;cT^-RkyT9{}bS-0hNXa!lnqu!H4mp zpd*K^ol-&q+3auMc1W5@Ic=fJj79g5Igmy)qA*mzQYR+5RsbFj0a5_cGO@`|&;?86 zSn@g*exs|<_REgTjzfb)a*#9-=H-FOwKK1LR30i2f)4m2-_gsvEN&wR)TQ^!=FvFd zy!{DKj$55lSUVlE8ATSi{DQ7@PQurw3aQ+cw(QK zO$LMy8LTp_%ee^QY6AU)NIw``Ej31^EDFTtLLRHm`LF*q1~!zGfcLD0hZMQ$ErKp?5X zgyF{k=Eco)>nB|sCP(I6~OFXW1gCGg^>16J2xSM<>y% z8sB{hyuLyl+&e8i#!Nr{vlsl!BLXx6f!o;yD8Uc7O(Z75U@|LcXi>hcZ#yUxp%~cE z&}F%j_Vl`82p=UOQR`sDQ2Er`V6oDo#B+l@Vcv9JbcQ&#az|7pGeNyy-S{!`QvnH- zWTod5DnPj6Il1-`LW{U{VMX`P)^LcP=V~DQ$fcPk63V^snO!dOxxC}+;dZWusRZjJ zO&y+g$txEGep0g(GRHxp8c;*cP3&xloESZv{KD6)W%;?urSA%y`rSW3u%v-catz!> zBQFqJ`wo%aXpdaYf^pMEkmr9#Yl5+g9*?hEPAq8J)1FY#J?$epD*d$Tx2Yh+PyuJ# zN#fj=1O*}dA^gIr^GHpF1}~~o6jt55NxKva5l<5vj998{ISRYfj3Y5;UY-ll9m0KM z`gQqH*;0330(Y?hGljFunP$gsf_IlSOptRl1OGwx2bMYli-?O0T~om_zGBT_dh~wq zdDm!?_{x{9{>04#2N<~Wo9<2yIQjwB)fQI+Jxec)t`y*#-%_8y{*<#elWROmvEXs& zB>~=?>Lh%4)LEr@NVXKcenZIbOd;r4(9KSmG@taD!*;BwMgnt2%<;%EZ~=fcYJ-mw=iuyO+3Ibl1!lVe_J8JWC+(~<$F#;7%fJdhlUdQ zv1_adCY7m6bj#Q;_(SJKhzQR+Ia4`$LPerApXJTvxb`Cn5)iPbKObVHDZ1C`Q)JmP z1f1BZI+v|oV*)-^K7S4+VLw*ip`ks;7jOpJ>f%a9zbC~7%MBi-1I_ggd3#vQ((`SU z?IACiCDG~g9D@%e=*Tc3#f111vrgf+{fOtR5P~0KsHCPteyDX%x?^GYWwwtudb#1_ z(%uffFX)A$7&=aW4|l-ehKs!$l$U?WT^FYWwn6(wq)4C6hAV(dVMA4UGL=I3JRL%$ zigU0@S!ZxXH>=Io0Zl8OhI!7p$wp1o+-6NHd_l=^e#LJV?W4e$pvRxl3`o&W(r%(Pvoa%EliJ^cNzn zxAUYrnp|&@G$)Lspm5lJFs;PSK{amS+K%sUB1)Z`xSNjF7;Zw;SKp%aUVSW9p zcCn`@>7Zgln)|~Mg69z9;npxr=%wL*B9Vxh>K{S4x#3tuw1IsidYN1g{jlhDZLt?x zGXN`c45i*Q*N{U8Kv0o<6BIEoH|Uqrcmp0;xcV|0T6z7^q`smuIavkQf}}vXh!xI@ z@~QJ5oXt2pr_Ia zN;bE#Ai$f6V)WEoVo1!+56+`B8gSYXcPp{@I3w({`>{Jt!!DTx zWD5MKAPB)=xGHEE|G>`XSzuQq;XKyWaS?7H75UhLFY}Z)bowV5?+jQju`Bnz1z5fg z6%&u*cX%H8bDvo@+Xop1=tfc<_$yD3H5{RQuRRUK4EATaI#0jPGS?CHc^%`9YA9>z zj}uMHtBJ9j6Q8E&e}8=%H_E|NG*aGwwLBLd9S}5t(nI#TBtv!1Kz#H5dDH@sPCy!r zA_hYmeA@yrLLW<@|9W(R0-3>UT|OX5ShP?y6JS;A*2cqO3Wv%Y2W5 zw-GGyc?fp{{)9l3*hS9>?bk>-q@K&0IhW+Cf+Qf+fB{1KTB;ynXkGDA7*41&wyAxY z#aPqrd1&F+4fA$SPe}tX$Pmm|${Z2j9mx!HCPvakNzhpi{Wk8=GaAV-tobj7S^{8ofnt z&TJmfgD3|zTgzK#kg@I_s??o!rWuHnfJV?@fw;JD3eOp#(9@GDPNhQI<^7Uc^Gsfa zmQ>u6DrzKKrB22ZJvroNz3Sp)u+KMF>Z)f1dwHb2FW6(n*VL!&LlgUkub5}gOosGT zTQIy6je_)QYDEQ;C7rO}l&y~%giISYm@x$!&Dey?c>YwDeg}`#RMxq0w&Bi7?l|oY zk~Sj!`MrpyhY z*jmu2l=fJUo8nYtU6A;;GNvplWiCXN_(w^IC^<(IzI5VM=2}6Ic%j4+l{_)hRs4e` z-CnX`KC>CV$x$UxKh7v%UIf8@_<&nh=skwWJg?@dz5WgtG4)m=Ut{CN-55La~}r z_;0b>I*@G4(5vu9=vMA;s0K#SUj+ve7v3aaWmQrK6Us91bj}HMQ>Vo(VT(D*={}kji@r-&wW}v^;8(Y$aIOij=v7ax26AqnJOdK=X2CZEd3x-@ zBTwa4Is<~tjvj@P4=D~0vFs0hX-qC!J0OY>rjLvfuJ3fhqRVv zqHPG$&n9Wzcc>f4(nQe+S%QLDy*8_ur^g$ghnHE!w7Y*PS2|a)*)oPddQ?mgZl1m* zqQdw_(N4{~@R3~(Bh1k(%`U|~vV35rnzq|YmJ5@Jf=5F(dkv$$$9T}7+v=jG{oMzP znKxX<=Q*o4f?Zs-NN;@Jev4R2Lw-A#RUg#}TLdxKMN}Q+w$>N1b`4rEF40ZYb+973 zmSfes*NZR5Gpz4Fr%wexu-}_FKuZ%uz^qXwvr-#lJ&mDn`)-x42~yGnbp=#-GtI~6 z$NN*Ibo6fgVY&Tua(7XW`!D5{>X|bKGd_+twM$aj4HB9|awI=0^v$yNAf3xPG=9`u z3<_|Yt8E1A-srt>jExVc@~a|$ZV;`NP7R+_E!YX-rjVzJTA3TWdF?bPa5jHI@&#gY zhC;{djcq-)XwxE6jY-Sq^ODL8yM&W~dxEe!o^Oks2K$QNwvKTHpd?d*7)lxo$_=Ee zP&%$sZPG5NtSD?3&1vg73Gqlg*O;q437=e(dn1ByK)4j-ihhr#huRthZ6n6V?m>3x zDXNZKWAZCCJsdj<3DvpT@R*oHi@KLctFZa-@MxNnD0O&OWd;4Uc(!E%7z@+dHPQH6%{Dk!DQj97+Kt~`4Dlj zN!=m$&jk~n1B1-y>8Tn5H*Zg(Z>yOyw})f3@maDqL5XAKN*NY%+EhXGUL) zvrJR59n|mPQE}?7JjX!Sy@pJ(ZQ6Q$c;Q3g!&N}ZeqHBwt>Po}<5=sE=yTk`)7qc> zc!~OS$E^>4cn|vN?OV4AVBrTNb*vD#$_s*HOhLkJqZA7>hUIA%*A(4ye(QF04|4}S zA>?ty!--AMD~cpEnN8i#fA>cjBYTzCf--i(@mw$h75Pe zLScel+0OJ>1LS-UN-sG@k|K81N{Ip|4EjK~y`FrTAtR^NTBwmgF+tc*;Sq-^V;G5Pp6@W3hRI2$nPQyZ~#ytVt`X^oC5R{hI0U6 zzZcW={y*WC!Mm1-YYz?FC}}&!;grQ%kBBA`M|3^eCU8>*E)l zzvbho{=ipzXed7xy?)okw*3F{KOrb`Kc*8c5^!o{nV`e}|H%IjKfXti1fcmn@0I^S z%_q8k&e(7Iwx|gsYtVwJpb6l>LAASG7jf+oqVoeJe|?|7t7gAzYB+z_^nxZoKPtr@ z3+1!D-Ju#c=iawd>PYPe zQttn8dSHIuQwHdlOh3K??(8ME=lCaoL5;+6i`W$U%lQCRtpR(Bev!|E{_g~N-i(kdZe=t%{ zvFfU`zhl)OT^KL9BY?%d)9rCe|CPZ$a%5)yY6|ifa|(!62f_MXAHQ0DD#`y>JK_&2 z>LG~y7v=wVLiPVulK+negInkCz-NJSn|=1A-iMNikn7VC@Sm^q>l4C}r)abBJy5ZL zXyY7Ti^MA++BhFjF#x5of4m+4$)^Iz(fkFdy6XPFU-;pxgBXiG#l&w`JiYLL^h^E@ zRrp=4|JRcv-cSCd2z&^&A^@l_h>9f^zrm|+(j~8V5T-R=GF1XWWAw5MZecjbLjvXml1?a4OIF@aEP}yM417K(>Vot|*B-ltQf)kZC~)e$B8Q^J^`6Sod+xJ}7Sg^l^gGHJ}z!9wC7OKI92-F($eB)xvbAqqGA0 z6o}M!D^`!q0bf-_9mMFlYiFB0=SKJaY~^YG(|`dfShaEmjdR_u3tPw*tdd1lH3i9} z6nVAv)_WDk{VT_qPY+Kp>Jkb#IJo90=ua&E_`VT0Cre)Lx? z0+lda!jnl1;3&jGeWVeS`b!)X6F|o3k{}t1$j2*uJ(xldA7`MVra%;Pxk|VPh%}A& zdqvBI0Tf^tmaLv#fNzGLP4j}anb6}?^sq5dLIDImfY?>?Ky6k92n$qV_a-g)Zc!4akz+N9Q z&Jzhms{6b4uuQsUVH{axibNK!s{wRI=3LhS^-!}y5FG(Y$bj*Pd2cdi4+vvUb<=9$ z8{HsA{pUS&z8t7zX6RZf(GjZt11Ga$#`W?UuM&5rm9xrsngaLjS6f($aGHO`Dok6g z@;)}bApTwz>E45Ay8QIlz^^P0$^}XuG$tB}vVOlLpU{h$*F?2#7$og0+1D zdF?B{2ejd;=r<>`TyNcnvI4#x>pzWWa@0RL*@aSVhWfv(Jx1_jvl4G}-!$2L?Ct3r z2rIK1C;7Ln=je3Fs+PRqpQ;Ok#~(gp7nVdxhbs2tOd-^^R8Qu}!(@vU>58xgK-=T< zJJW!Tb91OHWKdjk;*yMzQJrJ|4FO%`g`6yT%nuUgnrm`BH zSd13YRtISE0ej_Bd$326Eg+EEo?YuYA-P-_3|@XQgi*7R1v8yN2`~tW0WEIoV-WgX zM##>=HgpC3r=*G}$KkV~(Z`wY{@cW)p+@=lu>)mFNP6rk+D~gg6C8W{X=l73%$4pp zcCjxm2i&<2!xScagrwLD>>K@nL$bVL%UH|ZIsZ}1&@1}wQv22PY=p=q2jaQ3>tm<~ z&;?_3i>acq9)t>wJJ(-BsYv%2;kiDl9ftp_DObnxF6hpkyS6y%fi|skmj{ak<;wWZ z#c7a{b9xHMSE@aO+VEWy>l?+>#v^bCc6i&0xP|ijH_QcCbk7*U0pxpLtwzu%nU`W3 z!cQwt!9!ZP*`_~Ittk4>^eSiQQY{VZMs1v7D@+!sM?u}chG1@)K1)x@ur)um%`rY0 zyjJZLc-nUOj`Pr*-VR3Q#R6rxdB+K*sxIJ(d;_yL*=kXJLSBY)(4R`$bMYXz21zns zpc12d+V%Ielx)#eP)1u&L?wDXv60C-Am$02lZYndDoWreEo8KjdCyQ(8B}ehlQvM^ z6(dUK*Q3D96J@Fuai9ekg-LcDgh{^_aWh_F9rm76Q1*tqMVh-72C`w-1He{bW{el# z7pY!CWFPtRzSO@hG9G=t{dyS&lSg5~aM) zA(dTJQCnn;sdLNS4W7Ixg?mG=cHEHXhDwex(yobRx>8U^=@99n;rrv~x;DrbqFRTE zlz4}tQ$WsT_!cCyc(>S!3uGV(4;}FhjD znYP-Pwh?GmY&GUyZ{|8(_*t~0G|M*6@J9+-aaGo2mDgeL{t=o6Q3wXV3@ZugS>?oJqjINun&dr!x`d#>jN zn`LpH8VkuS$HmEFylWb_bUR+!5ukJrn|FII(RE^!njIz)VYWs_WN27k>nR+0CYfm= z^WrLS^6J>`@DARmdySzctGnm>7d6N@OE#Rk_THm$eZ8C_^mdVuUL_ zIKP&|(x!G)@Nh(yelu|HE#>^I)RwmM7YHwKSo_CGKMF~o}UmQ*@=Z z^pGgG7Hn(u8kluJUq_r$Wo%zrbk7ZG9S&l{<-mL4>ZBlBrbPX^e{0Ff>4{>DAaB)^ zETv4a!*eqQqbXxEiAk=uIpLG`2G73j@0OHi#lj~(2Zch#j-gdC?O~+tY7QKi6iGyb zOJiWd*ttW!hMZS|4s|*pSLurBZh7l6lJR0BBg14hMIy&6!MKp%Yr4=?b zb0q<^o}7_7HJ()LIEnfd7Tp{a9icA0eH#3Ti?mfBa_h0IQ$Yc{o*#s>YCDp$F};Q* z2kgFzqcgI}1Oc}qYBX|SR}vF>hx~_|j=7#Kx3r6S5XuHiyLXlS-&d6dY-Zh9xB zP&N=>!4Z66`Fs zb9e+nZer79+m~b6XW_Da?gLjv{=SP?TxxGqW5BA{r_zcATF&mWYhV4i-X%O3eKX!a z+AA#ehM?&^`JLox;`qtk&k$;XRoFOmp*N!{Inorjem}waC&TkP=Ef>`RjS62BJMvG zV2hD;uzWMk#~+y~MGVzmG#iKZS8d%8$AMZCquTjCeMwuc0aB!F!SaeB{~O0Ryd<0$ z&c__>Pvhn5Z?}HnG%`M`%AcsHXfvNYbm#+Kx;Z#$OTi2n2M%$=IUk)eEKQB@h zXPs_jAc0xxRGZbkK)E~Vyga2P7L)g=viA0tdGL+j!e?SC4nOlVZ{0p)3};*TFiS+8 z3#fUjv^ZWRiOxYBOvH?#TyY|+ z>lQP(Cw|I*$5VYfiX)RgMbqP=x*WQX{~^k z%!mF>wm3oj_Va<tZ8^qomI#(xPX^ z`-64eHI01Nz_BA#R_%s|TR)PcHkx}VBC{@7MR9jWgPFmOp1ck9HmR&QT_qm3X`5fg zV(1`GMuQW)l-Vw{kw!C%_=)LPX-@<>h@g2k2(ojj2oB_b$5qlS^AK=T;S_UF1>Kwa}>yh^lUUp+@J^x$*5*V3vO;` z31Z>`p}}yx9M)_{L`>SAB6>~^H$g`Jc{!SkBEG`0eZLl^w<*z#yZ`MzN2Fd0S`h3! z&Zig_nGqq^()oVRp@?12W_#Pr1hMT)IG#^@#m~A=YUkM7kKgAJw12>kDefjhVf>6o zwWLAj;T8}lMelCifb`SqrO}O(_8~Xo4?2WYBSfq(H?N{_jQv6Hk+m68i@iR4ebJ=* z^Bh{Iacb|&S>20E=fEPA>&fw&#vC=cDamI z3l?pllWIlU(O8|}C_t+$mey@?&iD*?At{5SntaawT)%aUltlM+I1z64$aMDiLa!u; zFN$&4a-#<2ZFFdYC0Rh#ohXZ$-;Xqg>NVvn0k1GCRY|amFVD z$zZLAH78&KVvNrp@ipIh;mC4u{l~s`HGn-9s!Bf*v@@B zP6TJJyC-4|s|{yRqE!PO-4<8zNn~;x%u@GE6E7K~sN1S6gJgi3uX1#~PU|Z=gcL1z zoIXxLCFUBJ*>N|!{-%+5{78;|Hme*l{@!WNv+>?$J55L9?o=`xOW~aNz57ntKei5} zVzVrnkL#Bc=%QDIXI?Pt+I{^=|4sNF_4cVcq$&C^g-1RdsTM+f)%T(^#NvQP4wM*+ z+GiA-#WS4|MW*Fabv>qXDI z7x7}-CxjO7Jx?$8vo7Bgf{EOJeDd8U@VYobp&J!*F^<&MIM>X9s)$6(zW`@qMmX9?G>U-CUKvsr$Na8#5nom#?s zN*XiFah=Y>fEZ03#Cu%BhG=Av*+r>4!*m>Di=o9H&0B8n?(^E==ZK4$o<3W<>e9#* z5tMGV`S{5y%-8fPm&8xCYG6bNJv3kTm4pp_@hqGaLDw!vKkf5JyxeoOd4o2)GQ0?( zyUZ%5|M~mQt!F&C#V{g;pG}H0)rU6LmWWA?tXT2}Pa3@R0=Vy7!}L@BpMiey=kM-x zAF(j1NWE>SB&qE%FWGqdl?Y@-$AnayB)pRen zAnV@Po}8U}UtR3-9JcIZfjt33#QpM+ zQV1&NydSCGhp^l9)u^cr)3-_@A5H?_;S!z{q1u8Vt0x|l(Dso@O_7F6i7#fP{7_ zSRNIYMHlX4yGLW5@Mt zTSvKtY;ECBGfrH!vEU|wD!k7-BSxNp%tkLGo7!PUZtg5 zz@ev7pcq{@PXS>bhlt8f%j5|+7h~@P&roR5EIBIA-Xhl1!)mqpWk#=Gw>Klo)54G9 z;&7EM3eqttMIkm`wqoaFe&i*5Q>u+z_13F&+bG?M3_}3R5R$747Dj{(q$`yyd1hCt zz5^3zcWEx_EtgBm%nXN9oibHGe3=*3e&o$dnsuUtr;mHI;q3MNC&>|;^eN@#R==m1 zu`|giX_H*JhpKPqVN`=QSCbLPd=Xty{j*BS!BMUgL!wfPc_zu z^RS*MP@w293PyV^=y43BA9@{;M0TI%pPdDhz|xKaODu(HvN{DPrce0Vn06Uj3U#VJ z0*)XJ8f97-HT=0R%pF)UIQYV+1B=s=#sIV{LibH>e&A0#KRm)pE6?p3CeE;N){$;T ztBVTTp7Da5Wt86Q#1!6lI;Druhb=(}H??|7VRb_m;`PNKLfMIw{5rb|YW%NYT<}uu z(pcKI;Pxg=`?Q~#sK@J_stmc$jV*t5-1agtr`9lMxm=LJC_X6*DL1mG2Ajc8EB1`o z52CfxnwwIAo?N1IY;Q5_01K;S)>&kjai)X(Ny97Ck2F-Y9-NSp1*WCmhbejDXEOJi zUxc($nLET}jwc_c5lo&grh4%P`F@j0ka{V^@PHevLg--kZO6ZrC zuQ|m*_6m#;Mhyz-orN;^6l?a-V_z|MT&nokoV#hTi+WAo|E#sB-QuTg#|u%luT*E} z(X2s$y5kt@3t$n+BnIJpMs;d7#mEc(x@jh!ZFU}}mHN+(Qt^R(-^tK^vs<^V4qv+T&|0>sn@HOR2(uN_} z=fpgeS18@@?p8xSbhx%ytr_8?ZH3Pr5*Es^sP38lbu{^vUO2VL?|)%h7zpnU`(E}VwZ|&ql_m^V#_e(SmwUf3BI9>#C$KBV zNasbCUCdb%SyZo6j%_Ma3^`n1WA~%_D*4Jt=vQ6no$Om~5*E~LGLUM%R_8QT$VkHZ z>UGYEH2oz{H)0&Jo~38aq^0}pct4c*i(>^(C(Ugz+f?G0FflsM#t?8>*NbJ23zX?v z3@K{#FM~nxC9DeB-;9^>d;+RN8l;%dUo`775)_dikUD5`Urf45$$h|uG-)G&y+mvq zFsQpmkB-}5tv)?w&&!EwzR*YvUp(Eu1q~pa1`6gRaJJ(`Np_ocu$=7ie$U9xn^?d+?>1)(Ph?$pZ&+@y}dm>@{hoMFn7@$>R=cHKJ?0nsl_Q? zbn2ZXO5fF2PX9L4K}c!e2xJ(#Or}4->);U@ za+@rZWTr!|qtsFp0`-6G$@(H40gNC6nd~IxBeUbXG+4WI9?b7*H7axcP?5zG8r3)r z$=u&+!@e79ep0>=zgEs$+=J~WcnvaIj`=JvuP3jAjk{fd$5xqQ?bHh`oI$Ujq?}T5 zu>!^m(&eSF;?mib14M94&TP#>kdW3(^EYqXoJliMV)de#f;I2jIZ#LqDNRgx55BOS zMT5CjV7TIowS0E1+#8T7CV)sB6oE)oPjZMyx-QNfY_hs2&9jhclpGW-&B|gRLe(`T z)cf{=C5&-Ay3(90Wj}#SV=imVKqbSw87hv7)}t0@On6g`xY!J)+ZDgTfH^iLMtCNb0BO1~qcCFK!Mfk|;;WHks*iWOAJ27A zfKkp3+xdyWd1zO~GitRh1CQ{HBa}PeHS3yMq;v5Obx*%KN>znc;aRzlkTG|gc8ex8 z#R5pkzSYVKZCUR;taxGdM%QuT6UAi)2*Q5cA#Ln5=FzD$G;xk&y{x4CW`)i%|~C zY+sz+5r}ndd|p&Z;|b4$mP4y!raQdg8?LX*UrLL74%Z$>qtEwxjRV3VIqsg7SKd8) zbs*ectVJaV=O^$fZpCFL7@O`eJ`_)MPZ)BA++d znU|D7XZ_e(gxowlsyEbPTJ7YXc{!Q!{-+VOwD?oB$)2XDtr}qUM?RwQKt*-BSZN{& z;0hyvqZIR!+16FR=l!$C9uY(?V| zbVETH__x?%9*%EZ&#I-y`SIyS(K2#9uyE43V!fR*-9CAK=K(V;Ft5P-Dki96_Tt?- zijF%f#z(5{IS4kTbl$K4cSc+CX!hqdEy{GfTnpt1)l)$%w%i5=b{Q2pwd?ag(q=vw z!Ij3f3deY|$Tg=gKaawV!m|n>&d@J*d+ljX{71V;eg21aGtX%`Eh2_sa zOPDA(N#JRtVY#>2`EjUkv_HfpJ2w}8Cq)q@jr$4y$@%7J`qMfw7*XB29E4((qyd}~ zc9_f-Yx`Er=`K05u;=ypc~;Sl-Hc`0NhI`O3y8j^Lu82FO?yR)-`5j7!+85agVpiI zsr|*Hn-|DpJ2bNNm(ed}Xhy*F)g8h+?GK^8{H#=F8g57G>&{N#TZ0s7`O&(t&!olI z^NjW@cJQFxC)-5oa)NNJ(H7wek3(wjz6q1Oswk5^BLy`7p6-$udA&}wZbaekyWu3R zkA5B?5>!zEYw{Tb;C?B3s0WBP;N}i*x%_RDjzV^pICm;L3HLGQyN#sosDB+v9=C;& zIGSKFTGiKrXNf_|U@}NV5-J{s@1$TzNlygUR!AY_DO2L9pb(1I#!7P~hS24ZG+3yk zdePwGmf_Ct!q<>{kq8ODEUjHLG^@h+ge_fLSXf$m@Sj5XU&SPCebdSFRkeyTMr>(! z@z-N*0aUa^U?dIiIZ=f_xmi#Ox0-$(x-~HgCQ%c_K7Gc}6(8+} zK)bOdgQ`}?>z;n*UORiZ7CDg5iq8kws&(i|OlzzR48S$bdOBi7 z;d&#RVCod;l|M2eR&O;UAYaG3bE737Io=Gt3z3r9H`+yTyK0*X2 zVoC}-Ro@cDT3xr)m-r@jhFHNr6k}&i3&`*dQv(oT(~vTm`{4|d3*|fFF71=#q38q{ zO4m&`ziIY&$Y_#8cG{0X%76^ zEgYguVwx++GQ~-@U#fCexT}}laX{=i?WDTAQ(E;nOB}$ojASac%10Lrdv%zBx&B&y z>3YJj@TjP&w)Mm!pI6P!YBfSp@yitDLHwJSt;{sXG%w!&7!pR9QnuU=n&Dvno1XJQ7sye}fAy_+rRu#M$ z8G$f0TVZ%2zZoKD=pFItd-l%&ORG& zpJv_O_*`jxdIKa1i7&=Scbv{23P)1ERcp=!zm;Q6i=wLL@Uv!A5g^V1TVjhFq)F#< zt3wdMiB1%NM+hyYQK!&<729{Vm-*#+@{ud2tOAmTAl5~dQ+Zk@gFY ?pav^Ey^n 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 G3;aJ#%CHRp literal 0 HcmV?d00001 diff --git a/Documents/1127_table_definition/table_definition_v0.8.4.xlsx b/Documents/1127_table_definition/table_definition_v0.8.4.xlsx index 8a908ff4d6282ecdec66850c58a99074b91280c6..615f6c0d9e58d62b9edadf17ee57cddcc939fce2 100644 GIT binary patch delta 30704 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 YwC~j50uWSR)9R?f$)FX9L1bY62ME9al>h($ delta 31022 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 j|1G~snAFg?Fv|o" ] @@ -259,9 +258,34 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "{'c30': 4,\n", + " 'i0': 3,\n", + " 'i1': 4,\n", + " 'i2': 4,\n", + " 'i3': 4,\n", + " 'i6': 4,\n", + " 'i7': 4,\n", + " 'i8': 4,\n", + " 'i9': 4,\n", + " 'u00': 3,\n", + " 'u20': 4,\n", + " 'u30': 4,\n", + " 'u31': 4,\n", + " 'u32': 4,\n", + " 'u60': 4}" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# node2num_cycles : A dictionary that maps a node_id to the number of cycles\n", "def get_node2num_cycles(plan, node_ids):\n", @@ -274,7 +298,6 @@ " node2numcycles = {node_id : inter2num_cycles[node2inter[node_id]] for node_id in node_ids}\n", " with open('../Intermediates/node2numcycles.json', 'w') as file:\n", " json.dump(node2numcycles, file, indent=4)\n", - "\n", " return node2numcycles\n", "node2num_cycles = get_node2num_cycles(plan, node_ids)\n", "node2num_cycles" @@ -305,14 +328,41 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "data": { + "text/plain": [ + "{'c30': 4,\n", + " 'i0': 3,\n", + " 'i1': 4,\n", + " 'i2': 4,\n", + " 'i3': 4,\n", + " 'i6': 4,\n", + " 'i7': 4,\n", + " 'i8': 4,\n", + " 'i9': 4,\n", + " 'u00': 3,\n", + " 'u20': 4,\n", + " 'u30': 4,\n", + " 'u31': 4,\n", + " 'u32': 4,\n", + " 'u60': 4}" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "node2num_cycles" + ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -360,7 +410,326 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "

\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
node_idstart_unixphase_sumodurationstatestart_dt
0i71704411320044GGrggGG2024-01-05 08:35:20
1i71704411320144rrrggrr2024-01-05 08:35:20
2i71704411320226GGrggGG2024-01-05 08:35:20
3i71704411320326rrrggrr2024-01-05 08:35:20
4c301704411350038rrrrrr2024-01-05 08:35:50
.....................
410u201704412330113ggrggg2024-01-05 08:52:10
411u201704412330238ggGggg2024-01-05 08:52:10
412u201704412330318ggrggg2024-01-05 08:52:10
413i91704412340026GGGG2024-01-05 08:52:20
414i91704412340164rrrr2024-01-05 08:52:20
\n", + "

415 rows × 6 columns

\n", + "
" + ], + "text/plain": [ + " node_id start_unix phase_sumo duration state start_dt\n", + "0 i7 1704411320 0 44 GGrggGG 2024-01-05 08:35:20\n", + "1 i7 1704411320 1 44 rrrggrr 2024-01-05 08:35:20\n", + "2 i7 1704411320 2 26 GGrggGG 2024-01-05 08:35:20\n", + "3 i7 1704411320 3 26 rrrggrr 2024-01-05 08:35:20\n", + "4 c30 1704411350 0 38 rrrrrr 2024-01-05 08:35:50\n", + ".. ... ... ... ... ... ...\n", + "410 u20 1704412330 1 13 ggrggg 2024-01-05 08:52:10\n", + "411 u20 1704412330 2 38 ggGggg 2024-01-05 08:52:10\n", + "412 u20 1704412330 3 18 ggrggg 2024-01-05 08:52:10\n", + "413 i9 1704412340 0 26 GGGG 2024-01-05 08:52:20\n", + "414 i9 1704412340 1 64 rrrr 2024-01-05 08:52:20\n", + "\n", + "[415 rows x 6 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
node_idstart_unixphase_sumodurationstatestart_dt
66c301704411509038rrrrrr2024-01-05 08:38:29
67c301704411509139GGGGGG2024-01-05 08:38:29
68c301704411509241GGGGGG2024-01-05 08:38:29
69c301704411509321GGGGGG2024-01-05 08:38:29
128c301704411670038rrrrrr2024-01-05 08:41:10
.....................
247u601704411970024ggggggggr2024-01-05 08:46:10
248u601704411970119ggggggggr2024-01-05 08:46:10
249u601704411970239ggggggggG2024-01-05 08:46:10
250u601704411970365ggggggggr2024-01-05 08:46:10
251u601704411970423ggggggggr2024-01-05 08:46:10
\n", + "

238 rows × 6 columns

\n", + "
" + ], + "text/plain": [ + " node_id start_unix phase_sumo duration state start_dt\n", + "66 c30 1704411509 0 38 rrrrrr 2024-01-05 08:38:29\n", + "67 c30 1704411509 1 39 GGGGGG 2024-01-05 08:38:29\n", + "68 c30 1704411509 2 41 GGGGGG 2024-01-05 08:38:29\n", + "69 c30 1704411509 3 21 GGGGGG 2024-01-05 08:38:29\n", + "128 c30 1704411670 0 38 rrrrrr 2024-01-05 08:41:10\n", + ".. ... ... ... ... ... ...\n", + "247 u60 1704411970 0 24 ggggggggr 2024-01-05 08:46:10\n", + "248 u60 1704411970 1 19 ggggggggr 2024-01-05 08:46:10\n", + "249 u60 1704411970 2 39 ggggggggG 2024-01-05 08:46:10\n", + "250 u60 1704411970 3 65 ggggggggr 2024-01-05 08:46:10\n", + "251 u60 1704411970 4 23 ggggggggr 2024-01-05 08:46:10\n", + "\n", + "[238 rows x 6 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "display(sigtable)\n", + "display(Sigtable)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -414,7 +783,259 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
node_idstart_unixphase_sumodurationstatestart_dt
100i117044117401__r1grrGGGrgrr2024-01-05 08:42:20
101i117044117401_g88grrGGGGgrr2024-01-05 08:42:20
102i117044117401_y4grryyyygrr2024-01-05 08:42:20
103i117044117402__r1grrrrrrgrr2024-01-05 08:42:20
104i117044117402_g35grrrrrrgGG2024-01-05 08:42:20
105i117044117402_y4grrrrrrgyy2024-01-05 08:42:20
106i117044119100__r1grrrrrrgrr2024-01-05 08:45:10
107i117044119100_g32gGGGGGrgrr2024-01-05 08:45:10
108i117044119100_y4gyyGGGrgrr2024-01-05 08:45:10
109i117044119101__r1grrGGGrgrr2024-01-05 08:45:10
110i117044119101_g88grrGGGGgrr2024-01-05 08:45:10
111i117044119101_y4grryyyygrr2024-01-05 08:45:10
112i117044119102__r1grrrrrrgrr2024-01-05 08:45:10
113i117044119102_g35grrrrrrgGG2024-01-05 08:45:10
114i117044119102_y4grrrrrrgyy2024-01-05 08:45:10
115i117044120800__r1grrrrrrgrr2024-01-05 08:48:00
116i117044120800_g32gGGGGGrgrr2024-01-05 08:48:00
117i117044120800_y4gyyGGGrgrr2024-01-05 08:48:00
118i117044120801__r1grrGGGrgrr2024-01-05 08:48:00
119i117044120801_g88grrGGGGgrr2024-01-05 08:48:00
\n", + "
" + ], + "text/plain": [ + " node_id start_unix phase_sumo duration state start_dt\n", + "100 i1 1704411740 1__r 1 grrGGGrgrr 2024-01-05 08:42:20\n", + "101 i1 1704411740 1_g 88 grrGGGGgrr 2024-01-05 08:42:20\n", + "102 i1 1704411740 1_y 4 grryyyygrr 2024-01-05 08:42:20\n", + "103 i1 1704411740 2__r 1 grrrrrrgrr 2024-01-05 08:42:20\n", + "104 i1 1704411740 2_g 35 grrrrrrgGG 2024-01-05 08:42:20\n", + "105 i1 1704411740 2_y 4 grrrrrrgyy 2024-01-05 08:42:20\n", + "106 i1 1704411910 0__r 1 grrrrrrgrr 2024-01-05 08:45:10\n", + "107 i1 1704411910 0_g 32 gGGGGGrgrr 2024-01-05 08:45:10\n", + "108 i1 1704411910 0_y 4 gyyGGGrgrr 2024-01-05 08:45:10\n", + "109 i1 1704411910 1__r 1 grrGGGrgrr 2024-01-05 08:45:10\n", + "110 i1 1704411910 1_g 88 grrGGGGgrr 2024-01-05 08:45:10\n", + "111 i1 1704411910 1_y 4 grryyyygrr 2024-01-05 08:45:10\n", + "112 i1 1704411910 2__r 1 grrrrrrgrr 2024-01-05 08:45:10\n", + "113 i1 1704411910 2_g 35 grrrrrrgGG 2024-01-05 08:45:10\n", + "114 i1 1704411910 2_y 4 grrrrrrgyy 2024-01-05 08:45:10\n", + "115 i1 1704412080 0__r 1 grrrrrrgrr 2024-01-05 08:48:00\n", + "116 i1 1704412080 0_g 32 gGGGGGrgrr 2024-01-05 08:48:00\n", + "117 i1 1704412080 0_y 4 gyyGGGrgrr 2024-01-05 08:48:00\n", + "118 i1 1704412080 1__r 1 grrGGGrgrr 2024-01-05 08:48:00\n", + "119 i1 1704412080 1_g 88 grrGGGGgrr 2024-01-05 08:48:00" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "SIGTABLE[100:120]" + ] + }, + { + "cell_type": "code", + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -437,7 +1058,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -492,7 +1113,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 16, "metadata": {}, "outputs": [ { @@ -505,8 +1126,8 @@ } ], "source": [ - "for m in range(105, 107):\n", - " generate_signals(m)" + "generate_signals(105)\n", + "generate_signals(106)" ] } ], diff --git a/Script/get_intermediates.py b/Script/get_intermediates.py new file mode 100644 index 000000000..6d5cf56a8 --- /dev/null +++ b/Script/get_intermediates.py @@ -0,0 +1,587 @@ +import pandas as pd +import numpy as np +import os, sys +import json +import sumolib, traci +from tqdm import tqdm + +class DailyPreprocess(): + def __init__(self): + self.path_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + self.issues = [] + + # 1. 데이터 불러오기 + def load_data(self): + self.load_networks() + self.load_tables() + self.check_networks() + self.check_tables() + print('1. 모든 데이터가 로드되었습니다.') + + # 1-1. 네트워크 불러오기 + def load_networks(self): + self.net = sumolib.net.readNet(os.path.join(self.path_root, 'Data', '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.path_table = os.path.join(self.path_root, 'Data', 'tables') + + self.inter_info = pd.read_csv(os.path.join(self.path_table, 'inter_info.csv'), dtype=loading_dtype) + self.angle = pd.read_csv(os.path.join(self.path_table, 'angle.csv'), dtype=loading_dtype) + self.plan = pd.read_csv(os.path.join(self.path_table, 'plan.csv'), dtype=loading_dtype) + self.inter_node = pd.read_csv(os.path.join(self.path_table, 'inter_node.csv'), dtype=loading_dtype) + self.uturn = pd.read_csv(os.path.join(self.path_table, 'child_uturn.csv'), dtype=loading_dtype) + self.coord = pd.read_csv(os.path.join(self.path_table, 'child_coord.csv'), dtype=loading_dtype) + self.nema = pd.read_csv(os.path.join(self.path_table, 'nema.csv'), encoding='cp949', dtype=loading_dtype) + 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_root, 'Data', '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_inter_info() + self.check_angle() + self.check_plan() + print("1-4. 모든 테이블들의 무결성을 검사했고 이상 없습니다.") + pass + + # 1-4-1. 교차로정보(inter_info) 검사 + def check_inter_info(self): + # 1-4-1-1. inter_lat, inter_lon 적절성 검사 + # self.inter_info.loc[0, 'inter_lat'] = 38.0 # 에러 발생을 위한 코드 + self.max_lon, self.min_lon = 127.207888, 127.012492 + self.max_lat, self.min_lat = 37.480693, 37.337112 + 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-1-1. 위도 또는 경도가 범위를 벗어난 교차로가 있습니다: inter_no : {row['inter_no']}" + self.issues.append(msg) + # 교차로목록 정의 + self.inter_nos = sorted(self.inter_info.inter_no.unique()) + + # 1-4-2. 방위각정보(inter_info) 검사 + def check_angle(self): + # 1-4-2-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-2-1. angle의 inter_no 중 교차로 목록(inter_nos)에 포함되지 않는 항목이 있습니다: {missing_inter_nos}" + self.issues.append(msg) + + # 1-4-3. 신호계획(plan) 검사 + def check_plan(self): + # 1-4-3-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-3-1. plan의 inter_no 중 교차로 목록(inter_nos)에 포함되지 않는 항목이 있습니다: {missing_inter_nos}" + self.issues.append(msg) + + # 1-4-3-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-3-2. plan에 잘못된 형식의 start_time이 존재합니다: {start_hour, start_minute}" + self.issues.append(msg) + + # 1-4-3-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-3-3. 음수이거나 200보다 큰 현시시간이 존재합니다. : {invalid_inter_nos}" + + # 1-4-3-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-3-4. inter_no:{inc_inter_no}, start_hour:{start_minute}, start_hour:{start_minute}일 때, cycle이 유일하게 결정되지 않습니다." + self.issues.append(msg) + + # 1-4-3-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-4-5. inter_no:{inc_inter_no}, A링현시시간의 합과 B링현시시간의 합이 일치하지 않거나, 현시시간의 합과 주기가 일치하지 않습니다." + self.issues.append(msg) + + # 2. 중간산출물 만들기 + def get_intermediates(self): + self.get_matches() + # self.get_movements() + 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() + + # 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_root, 'Data', '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='이동류정보 불러오는 중 : match1')] + self.match1 = pd.concat(moves).drop_duplicates().sort_values(by=['inter_no','phas_A','phas_B']).reset_index(drop=True) + self.match1.to_csv(os.path.join(self.path_root, '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'])) + + self.match5 = self.match4.copy() + # 진입진출ID 매칭 + for index, row in self.match5.iterrows(): + node_id = self.inter2node[row.inter_no] + node = self.net.getNode(node_id) + # 교차로의 모든 (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) + out_dirs = [] + 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) + # 진입각, 진출각 불러오기 + 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) + + # 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 별로 진입방향, 진출방향A, 진출방향B 정함. + - 상술한 directions를 활용하여 정함. + - (진입방향, 진출방향A, 진출방향B)을 고려하여 (현시, 링) 별로 진입엣지id, 진출엣지id를 정함. + - ex) cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['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'])) + + child_ids = self.inter_node[self.inter_node.inter_type=='child'].node_id.unique() + ch2pa = {} # child to parent + for child_id in 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방향 + + # 각 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에서 parent_id에 해당하는 행들을 가져옴 + 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 + + # condition 별로 inc_dire, out_dire_A, out_dire_B를 정함 + ind = directions.index(direction) + if condition == "좌회전시": + inc_dire = direction + out_dire_A = out_dire_B = directions[(ind + 2) % len(directions)] + elif condition == "직진시": + inc_dire = direction + out_dire_A = out_dire_B = directions[(ind + 4) % len(directions)] + elif condition == "보행신호시": + inc_dire = directions[(ind + 2) % len(directions)] + out_dire_A = directions[(ind - 2) % len(directions)] + out_dire_B = directions[(ind - 2) % len(directions)] + + # (inc_dire, out_dire_A, out_dire_B) 별로 inc_edge_id, out_edge_id를 정함 + if condition == '보행신호시': + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + # 이동류번호가 17(보행신호)이면서 유턴노드방향으로 가는 신호가 없으면 (inc_edge_id, out_edge_id)를 부여한다. + cmatch.loc[(cmatch.move_no==17) & (cmatch.out_dir!=direction), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + else: # '직진시', '좌회전시' + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + # 유턴신호의 이동류번호를 19로 부여한다. + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), 'move_no'] = 19 + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), 'move_no'] = 19 + 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_root, '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 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 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 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 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 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(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_root, 'Intermediates', 'matching.csv')) + + # 2-2 + def get_movements(self): + movements_path = os.path.join(self.path_root, 'Intermediates', 'movement') + movements_list = [pd.read_csv(os.path.join(movements_path, file), index_col=0) for file in tqdm(os.listdir(movements_path), desc='이동류정보 불러오는 중 : movements')] + movements = pd.concat(movements_list) + movements = movements.drop(columns=['start_unix']) + movements = movements.drop_duplicates() + movements = movements.sort_values(by=['inter_no', 'phas_A', 'phas_B']) + movements = movements.reset_index(drop=True) + movements.to_csv(os.path.join(self.path_root, 'Intermediates', 'movements.csv')) + return movements + + # 2-3 node2num_cycles : A dictionary that maps a node_id to the number of cycles + def get_node2num_cycles(self): + # node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no'])) + self.node_ids = sorted(self.inter_node.node_id.unique()) + + 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'])) + node2numcycles = {node_id : inter2num_cycles[self.node2inter[node_id]] for node_id in self.node_ids} + with open(os.path.join('Intermediates','node2numcycles.json'), 'w') as file: + json.dump(node2numcycles, file, indent=4) + return node2numcycles + + # 3. 이슈사항 저장 + def write_issues(self): + path_issues = os.path.join(self.path_root, "Results", "issues_intermediates.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 = DailyPreprocess() + self.main() \ No newline at end of file diff --git a/Script/get_signals.py b/Script/get_signals.py new file mode 100644 index 000000000..df8f1c91f --- /dev/null +++ b/Script/get_signals.py @@ -0,0 +1,587 @@ +import pandas as pd +import numpy as np +import os, sys +import json +from tqdm import tqdm +from datetime import datetime + +class SignalGenerator(): + def __init__(self): + self.path_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + self.issues = [] + + # 1. 데이터 불러오기 + def load_data(self): + self.load_networks() + self.load_tables() + self.check_networks() + self.check_tables() + print('1. 모든 데이터가 로드되었습니다.') + + # 1-1. 네트워크 불러오기 + def load_networks(self): + self.net = sumolib.net.readNet(os.path.join(self.path_root, 'Data', '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.path_table = os.path.join(self.path_root, 'Data', 'tables') + + self.inter_info = pd.read_csv(os.path.join(self.path_table, 'inter_info.csv'), dtype=loading_dtype) + self.angle = pd.read_csv(os.path.join(self.path_table, 'angle.csv'), dtype=loading_dtype) + self.plan = pd.read_csv(os.path.join(self.path_table, 'plan.csv'), dtype=loading_dtype) + self.inter_node = pd.read_csv(os.path.join(self.path_table, 'inter_node.csv'), dtype=loading_dtype) + self.uturn = pd.read_csv(os.path.join(self.path_table, 'child_uturn.csv'), dtype=loading_dtype) + self.coord = pd.read_csv(os.path.join(self.path_table, 'child_coord.csv'), dtype=loading_dtype) + self.nema = pd.read_csv(os.path.join(self.path_table, 'nema.csv'), encoding='cp949', dtype=loading_dtype) + 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_root, 'Data', '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_inter_info() + self.check_angle() + self.check_plan() + print("1-4. 모든 테이블들의 무결성을 검사했고 이상 없습니다.") + pass + + # 1-4-1. 교차로정보(inter_info) 검사 + def check_inter_info(self): + # 1-4-1-1. inter_lat, inter_lon 적절성 검사 + # self.inter_info.loc[0, 'inter_lat'] = 38.0 # 에러 발생을 위한 코드 + self.max_lon, self.min_lon = 127.207888, 127.012492 + self.max_lat, self.min_lat = 37.480693, 37.337112 + 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-1-1. 위도 또는 경도가 범위를 벗어난 교차로가 있습니다: inter_no : {row['inter_no']}" + self.issues.append(msg) + # 교차로목록 정의 + self.inter_nos = sorted(self.inter_info.inter_no.unique()) + + # 1-4-2. 방위각정보(inter_info) 검사 + def check_angle(self): + # 1-4-2-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-2-1. angle의 inter_no 중 교차로 목록(inter_nos)에 포함되지 않는 항목이 있습니다: {missing_inter_nos}" + self.issues.append(msg) + + # 1-4-3. 신호계획(plan) 검사 + def check_plan(self): + # 1-4-3-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-3-1. plan의 inter_no 중 교차로 목록(inter_nos)에 포함되지 않는 항목이 있습니다: {missing_inter_nos}" + self.issues.append(msg) + + # 1-4-3-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-3-2. plan에 잘못된 형식의 start_time이 존재합니다: {start_hour, start_minute}" + self.issues.append(msg) + + # 1-4-3-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-3-3. 음수이거나 200보다 큰 현시시간이 존재합니다. : {invalid_inter_nos}" + + # 1-4-3-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-3-4. inter_no:{inc_inter_no}, start_hour:{start_minute}, start_hour:{start_minute}일 때, cycle이 유일하게 결정되지 않습니다." + self.issues.append(msg) + + # 1-4-3-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-4-5. inter_no:{inc_inter_no}, A링현시시간의 합과 B링현시시간의 합이 일치하지 않거나, 현시시간의 합과 주기가 일치하지 않습니다." + self.issues.append(msg) + + # 2. 중간산출물 만들기 + def get_intermediates(self): + self.get_matches() + # self.get_movements() + 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() + + # 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_root, 'Data', '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='이동류정보 불러오는 중 : match1')] + self.match1 = pd.concat(moves).drop_duplicates().sort_values(by=['inter_no','phas_A','phas_B']).reset_index(drop=True) + self.match1.to_csv(os.path.join(self.path_root, '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'])) + + self.match5 = self.match4.copy() + # 진입진출ID 매칭 + for index, row in self.match5.iterrows(): + node_id = self.inter2node[row.inter_no] + node = self.net.getNode(node_id) + # 교차로의 모든 (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) + out_dirs = [] + 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) + # 진입각, 진출각 불러오기 + 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) + + # 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 별로 진입방향, 진출방향A, 진출방향B 정함. + - 상술한 directions를 활용하여 정함. + - (진입방향, 진출방향A, 진출방향B)을 고려하여 (현시, 링) 별로 진입엣지id, 진출엣지id를 정함. + - ex) cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['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'])) + + child_ids = self.inter_node[self.inter_node.inter_type=='child'].node_id.unique() + ch2pa = {} # child to parent + for child_id in 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방향 + + # 각 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에서 parent_id에 해당하는 행들을 가져옴 + 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 + + # condition 별로 inc_dire, out_dire_A, out_dire_B를 정함 + ind = directions.index(direction) + if condition == "좌회전시": + inc_dire = direction + out_dire_A = out_dire_B = directions[(ind + 2) % len(directions)] + elif condition == "직진시": + inc_dire = direction + out_dire_A = out_dire_B = directions[(ind + 4) % len(directions)] + elif condition == "보행신호시": + inc_dire = directions[(ind + 2) % len(directions)] + out_dire_A = directions[(ind - 2) % len(directions)] + out_dire_B = directions[(ind - 2) % len(directions)] + + # (inc_dire, out_dire_A, out_dire_B) 별로 inc_edge_id, out_edge_id를 정함 + if condition == '보행신호시': + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + # 이동류번호가 17(보행신호)이면서 유턴노드방향으로 가는 신호가 없으면 (inc_edge_id, out_edge_id)를 부여한다. + cmatch.loc[(cmatch.move_no==17) & (cmatch.out_dir!=direction), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + else: # '직진시', '좌회전시' + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + # 유턴신호의 이동류번호를 19로 부여한다. + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), 'move_no'] = 19 + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), 'move_no'] = 19 + 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_root, '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 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 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 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 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 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(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_root, 'Intermediates', 'matching.csv')) + + # 2-2 + def get_movements(self): + movements_path = os.path.join(self.path_root, 'Intermediates', 'movement') + movements_list = [pd.read_csv(os.path.join(movements_path, file), index_col=0) for file in tqdm(os.listdir(movements_path), desc='이동류정보 불러오는 중 : movements')] + movements = pd.concat(movements_list) + movements = movements.drop(columns=['start_unix']) + movements = movements.drop_duplicates() + movements = movements.sort_values(by=['inter_no', 'phas_A', 'phas_B']) + movements = movements.reset_index(drop=True) + movements.to_csv(os.path.join(self.path_root, 'Intermediates', 'movements.csv')) + return movements + + # 2-3 node2num_cycles : A dictionary that maps a node_id to the number of cycles + def get_node2num_cycles(self): + # node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no'])) + self.node_ids = sorted(self.inter_node.node_id.unique()) + + 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'])) + node2numcycles = {node_id : inter2num_cycles[self.node2inter[node_id]] for node_id in self.node_ids} + with open(os.path.join('Intermediates','node2numcycles.json'), 'w') as file: + json.dump(node2numcycles, file, indent=4) + return node2numcycles + + # 3. 이슈사항 저장 + def write_issues(self): + path_issues = os.path.join(self.path_root, "Results", "issues_intermediates.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 = DailyPreprocess() + self.main() \ No newline at end of file diff --git a/Script/preprocess.ipynb b/Script/preprocess.ipynb index 5a6019f91..b22f4040b 100644 --- a/Script/preprocess.ipynb +++ b/Script/preprocess.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 103, + "execution_count": 27, "metadata": {}, "outputs": [], "source": [ @@ -24,7 +24,7 @@ }, { "cell_type": "code", - "execution_count": 104, + "execution_count": 28, "metadata": {}, "outputs": [], "source": [ @@ -49,7 +49,7 @@ }, { "cell_type": "code", - "execution_count": 105, + "execution_count": 29, "metadata": {}, "outputs": [], "source": [ @@ -74,7 +74,7 @@ }, { "cell_type": "code", - "execution_count": 106, + "execution_count": 30, "metadata": {}, "outputs": [], "source": [ @@ -98,7 +98,7 @@ }, { "cell_type": "code", - "execution_count": 107, + "execution_count": 31, "metadata": {}, "outputs": [], "source": [ @@ -140,7 +140,7 @@ }, { "cell_type": "code", - "execution_count": 108, + "execution_count": 32, "metadata": {}, "outputs": [], "source": [ @@ -234,7 +234,7 @@ }, { "cell_type": "code", - "execution_count": 109, + "execution_count": 33, "metadata": {}, "outputs": [], "source": [ @@ -360,7 +360,7 @@ }, { "cell_type": "code", - "execution_count": 110, + "execution_count": 34, "metadata": {}, "outputs": [], "source": [ @@ -457,7 +457,7 @@ }, { "cell_type": "code", - "execution_count": 111, + "execution_count": 35, "metadata": {}, "outputs": [], "source": [ @@ -470,6 +470,244 @@ "matching = make_matching(match6)" ] }, + { + "cell_type": "code", + "execution_count": 36, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
inter_nophase_noring_typemove_noinc_dirout_dirinc_angleout_angleinc_edgeout_edgenode_id
01751A8179004-571542797_02571500487_01i0
11751B4003176-571500487_01571542797_02i0
21752A7001095-571500487_01571545870_01i0
31752B3179270-571542797_02571510153_01i0
41753A6090270571545870_02571510153_01i0
....................................
32102B2270090NaNNaNu60
42103A7359090NaNNaNu60
52103B4000180NaNNaNu60
62104A8180000NaNNaNu60
72104B3180270NaNNaNu60
\n", + "

116 rows × 11 columns

\n", + "
" + ], + "text/plain": [ + " inter_no phase_no ring_type move_no inc_dir out_dir inc_angle out_angle \\\n", + "0 175 1 A 8 남 북 179 004 \n", + "1 175 1 B 4 북 남 003 176 \n", + "2 175 2 A 7 북 동 001 095 \n", + "3 175 2 B 3 남 서 179 270 \n", + "4 175 3 A 6 동 서 090 270 \n", + ".. ... ... ... ... ... ... ... ... \n", + "3 210 2 B 2 서 동 270 090 \n", + "4 210 3 A 7 북 동 359 090 \n", + "5 210 3 B 4 북 남 000 180 \n", + "6 210 4 A 8 남 북 180 000 \n", + "7 210 4 B 3 남 서 180 270 \n", + "\n", + " inc_edge out_edge node_id \n", + "0 -571542797_02 571500487_01 i0 \n", + "1 -571500487_01 571542797_02 i0 \n", + "2 -571500487_01 571545870_01 i0 \n", + "3 -571542797_02 571510153_01 i0 \n", + "4 571545870_02 571510153_01 i0 \n", + ".. ... ... ... \n", + "3 NaN NaN u60 \n", + "4 NaN NaN u60 \n", + "5 NaN NaN u60 \n", + "6 NaN NaN u60 \n", + "7 NaN NaN u60 \n", + "\n", + "[116 rows x 11 columns]" + ] + }, + "execution_count": 36, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "match6" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -479,7 +717,7 @@ }, { "cell_type": "code", - "execution_count": 112, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -492,7 +730,7 @@ }, { "cell_type": "code", - "execution_count": 113, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -550,7 +788,7 @@ }, { "cell_type": "code", - "execution_count": 114, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -568,7 +806,7 @@ }, { "cell_type": "code", - "execution_count": 115, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -616,7 +854,7 @@ }, { "cell_type": "code", - "execution_count": 116, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -645,7 +883,7 @@ }, { "cell_type": "code", - "execution_count": 117, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -667,7 +905,7 @@ }, { "cell_type": "code", - "execution_count": 118, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -689,7 +927,7 @@ " row1['end_unix'] = program_start\n", " row2['end_unix'] = program_start + cycle\n", " rhistory = pd.concat([rhistory, row1, row2]).reset_index(drop=True)\n", - "\n", + " # present_time + adder 의 시각에 한 주기의 신호 추가\n", " for inter_no in set(whole_inter_nos):\n", " program_start, prow = load_prow(timetable, inter_no, present_time)\n", " cycle = prow.cycle.iloc[0]\n", @@ -698,7 +936,6 @@ " row3['end_unix'] = present_time + adder\n", " rhistory = pd.concat([rhistory, row3]).reset_index(drop=True)\n", "\n", - "\n", " # 2. 시작 유닉스 타임컬럼 생성 후 종류 유닉스 타임에서 현시별 현시기간 컬럼의 합을 뺀 값으로 입력\n", " # - 현시시간의 합을 뺀 시간의 +- 10초 이내에 이전 주기정보가 존재하면 그 유닉스 시간을 시작 유닉스시간 값으로 하고, 존재하지 않으면 현시시간의 합을 뺀 유닉스 시간을 시작 유닉스 시간으로 지정\n", " for i, row in rhistory.iterrows():\n", @@ -727,7 +964,7 @@ }, { "cell_type": "code", - "execution_count": 119, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ @@ -834,7 +1071,7 @@ }, { "cell_type": "code", - "execution_count": 120, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -872,7 +1109,7 @@ }, { "cell_type": "code", - "execution_count": 121, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ @@ -891,7 +1128,7 @@ }, { "cell_type": "code", - "execution_count": 122, + "execution_count": 21, "metadata": {}, "outputs": [], "source": [ @@ -925,11 +1162,16 @@ }, { "cell_type": "code", - "execution_count": 123, + "execution_count": 22, "metadata": {}, "outputs": [], "source": [ - "def make_histid(present_time, movedur, inter2node):\n", + "def make_histid(hrhists, movement_updated, present_time, inter2node):\n", + " # movements and durations\n", + " movedur = pd.merge(hrhists, movement_updated, how='inner', on=['inter_no', 'start_unix', 'phas_A', 'phas_B'])\n", + " movedur = movedur.sort_values(by=['start_unix', 'inter_no', 'phas_A','phas_B'])\n", + " movedur = movedur[['inter_no', 'start_unix', 'phas_A', 'phas_B', 'move_A', 'move_B', 'duration']]\n", + "\n", " # 이동류 매칭 테이블에서 진입id, 진출id를 가져와서 붙임.\n", " for i, row in movedur.iterrows():\n", " inter_no = row.inter_no\n", @@ -938,7 +1180,7 @@ " move_A = row.move_A\n", " if move_A in [17, 18]:\n", " inc_edge_A = np.nan\n", - " out_edge_A = np.nan\n", + " outhedge_A = np.nan\n", " else:\n", " match_A = matching[(matching.inter_no == inter_no) & (matching.move_no == move_A)].iloc[0]\n", " inc_edge_A = match_A.inc_edge\n", @@ -961,19 +1203,15 @@ " histid = movedur.copy() # history with edge ids (incoming and outgoing edge ids)\n", " histid['node_id'] = histid['inter_no'].map(inter2node)\n", " histid = histid[['inter_no', 'node_id', 'start_unix', 'phas_A', 'phas_B', 'duration', 'inc_edge_A', 'out_edge_A', 'inc_edge_B', 'out_edge_B']]\n", - " histid_start = present_time - 1200\n", + " histid_start = present_time - 600\n", " histid = histid[histid.start_unix > histid_start]\n", " return histid\n", - "# movedur\n", - "movedur = pd.merge(movement_updated, hrhists, how='inner', on=['inter_no', 'start_unix', 'phas_A', 'phas_B']) # movements and durations\n", - "movedur = movedur.sort_values(by=['start_unix', 'inter_no', 'phas_A','phas_B'])\n", - "movedur = movedur[['inter_no', 'start_unix', 'phas_A', 'phas_B', 'move_A', 'move_B', 'duration']]\n", - "histid = make_histid(present_time, movedur, inter2node)" + "histid = make_histid(hrhists, movement_updated, present_time, inter2node)" ] }, { "cell_type": "code", - "execution_count": 124, + "execution_count": 23, "metadata": {}, "outputs": [ { @@ -1018,69 +1256,69 @@ " \n", " \n", " \n", - " 1621\n", - " 177\n", - " i2\n", - " 1704410710\n", + " 1768\n", + " 206\n", + " i7\n", + " 1704411320\n", " 1\n", " 1\n", - " 40\n", - " -571542809_01\n", - " 571542811_01\n", - " 571542811_02\n", - " 571542809_01\n", + " 44\n", + " -571511538_02\n", + " 571542073_02\n", + " 571542073_01\n", + " 571511538_02\n", " \n", " \n", - " 1622\n", - " 177\n", - " i2\n", - " 1704410710\n", + " 1769\n", + " 206\n", + " i7\n", + " 1704411320\n", " 2\n", " 2\n", - " 25\n", - " 571542811_02\n", - " 571542107_01\n", - " -571542809_01\n", - " 571542809_01\n", + " 44\n", + " NaN\n", + " 571542073_02\n", + " NaN\n", + " NaN\n", " \n", " \n", - " 1623\n", - " 177\n", - " i2\n", - " 1704410710\n", + " 1770\n", + " 206\n", + " i7\n", + " 1704411320\n", " 3\n", " 3\n", - " 71\n", - " NaN\n", - " NaN\n", - " NaN\n", - " NaN\n", + " 26\n", + " -571511538_02\n", + " 571542073_02\n", + " 571542073_01\n", + " 571511538_02\n", " \n", " \n", - " 1624\n", - " 177\n", - " i2\n", - " 1704410710\n", + " 1771\n", + " 206\n", + " i7\n", + " 1704411320\n", " 4\n", " 4\n", - " 34\n", - " -571542809_01\n", - " 571542811_01\n", - " 571542107_02\n", - " 571542809_01\n", + " 26\n", + " NaN\n", + " 571542073_02\n", + " NaN\n", + " NaN\n", " \n", " \n", - " 876\n", - " 176\n", - " i1\n", - " 1704410720\n", + " 1772\n", + " 178\n", + " i3\n", + " 1704411350\n", " 1\n", " 1\n", - " 37\n", - " -571542810_01\n", - " -571542797_02.99\n", - " 571542797_02.99\n", - " 571542810_01\n", + " 38\n", + " 571540304_02\n", + " 571556450_01\n", + " 571556450_02\n", + " 571540304_01\n", " \n", " \n", " ...\n", @@ -1096,7 +1334,7 @@ " ...\n", " \n", " \n", - " 1671\n", + " 1978\n", " 201\n", " i8\n", " 1704412330\n", @@ -1109,7 +1347,7 @@ " 571500617_01\n", " \n", " \n", - " 1672\n", + " 1979\n", " 201\n", " i8\n", " 1704412330\n", @@ -1122,7 +1360,7 @@ " 571500569_01\n", " \n", " \n", - " 1673\n", + " 1980\n", " 201\n", " i8\n", " 1704412330\n", @@ -1135,7 +1373,7 @@ " 571500569_01\n", " \n", " \n", - " 1486\n", + " 1981\n", " 202\n", " i9\n", " 1704412340\n", @@ -1148,7 +1386,7 @@ " 571510152_01.65\n", " \n", " \n", - " 1487\n", + " 1982\n", " 202\n", " i9\n", " 1704412340\n", @@ -1156,46 +1394,46 @@ " 2\n", " 64\n", " NaN\n", - " NaN\n", + " -571510152_01\n", " NaN\n", " NaN\n", " \n", " \n", "\n", - "

343 rows × 10 columns

\n", + "

215 rows × 10 columns

\n", "" ], "text/plain": [ " inter_no node_id start_unix phas_A phas_B duration inc_edge_A \\\n", - "1621 177 i2 1704410710 1 1 40 -571542809_01 \n", - "1622 177 i2 1704410710 2 2 25 571542811_02 \n", - "1623 177 i2 1704410710 3 3 71 NaN \n", - "1624 177 i2 1704410710 4 4 34 -571542809_01 \n", - "876 176 i1 1704410720 1 1 37 -571542810_01 \n", + "1768 206 i7 1704411320 1 1 44 -571511538_02 \n", + "1769 206 i7 1704411320 2 2 44 NaN \n", + "1770 206 i7 1704411320 3 3 26 -571511538_02 \n", + "1771 206 i7 1704411320 4 4 26 NaN \n", + "1772 178 i3 1704411350 1 1 38 571540304_02 \n", "... ... ... ... ... ... ... ... \n", - "1671 201 i8 1704412330 3 3 18 571500617_02 \n", - "1672 201 i8 1704412330 4 4 58 571500617_02 \n", - "1673 201 i8 1704412330 5 5 18 571500583_01 \n", - "1486 202 i9 1704412340 1 1 26 571510152_02 \n", - "1487 202 i9 1704412340 2 2 64 NaN \n", + "1978 201 i8 1704412330 3 3 18 571500617_02 \n", + "1979 201 i8 1704412330 4 4 58 571500617_02 \n", + "1980 201 i8 1704412330 5 5 18 571500583_01 \n", + "1981 202 i9 1704412340 1 1 26 571510152_02 \n", + "1982 202 i9 1704412340 2 2 64 NaN \n", "\n", - " out_edge_A inc_edge_B out_edge_B \n", - "1621 571542811_01 571542811_02 571542809_01 \n", - "1622 571542107_01 -571542809_01 571542809_01 \n", - "1623 NaN NaN NaN \n", - "1624 571542811_01 571542107_02 571542809_01 \n", - "876 -571542797_02.99 571542797_02.99 571542810_01 \n", - "... ... ... ... \n", - "1671 571500618_01 571500618_02 571500617_01 \n", - "1672 571500618_01 571500617_02 571500569_01 \n", - "1673 571500617_01 571500583_01 571500569_01 \n", - "1486 -571510152_01 571510152_01 571510152_01.65 \n", - "1487 NaN NaN NaN \n", + " out_edge_A inc_edge_B out_edge_B \n", + "1768 571542073_02 571542073_01 571511538_02 \n", + "1769 571542073_02 NaN NaN \n", + "1770 571542073_02 571542073_01 571511538_02 \n", + "1771 571542073_02 NaN NaN \n", + "1772 571556450_01 571556450_02 571540304_01 \n", + "... ... ... ... \n", + "1978 571500618_01 571500618_02 571500617_01 \n", + "1979 571500618_01 571500617_02 571500569_01 \n", + "1980 571500617_01 571500583_01 571500569_01 \n", + "1981 -571510152_01 571510152_01 571510152_01.65 \n", + "1982 -571510152_01 NaN NaN \n", "\n", - "[343 rows x 10 columns]" + "[215 rows x 10 columns]" ] }, - "execution_count": 124, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -1275,7 +1513,7 @@ " movedur = movedur.sort_values(by=['start_unix', 'inter_no', 'phas_A','phas_B'])\n", " movedur = movedur[['inter_no', 'start_unix', 'phas_A', 'phas_B', 'move_A', 'move_B', 'duration']]\n", " # histid\n", - " histid = make_histid(present_time, movedur, inter2node)\n", + " histid = make_histid(hrhists, movement_updated, present_time, inter2node)\n", " histid.to_csv(f'../Intermediates/histid/histid_{fmins[m]}.csv')\n", " return histid\n", "preprocess(105)" @@ -1283,27 +1521,9 @@ }, { "cell_type": "code", - "execution_count": 126, + "execution_count": 24, "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "2024-01-05 08:45:00\n" - ] - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAh4AAAHCCAYAAACg18CDAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjcuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/OQEPoAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA/x0lEQVR4nO3deXhU5eH28XuSwIRANghLCEkIS0FkE3DBjUUUapHWBX9CVSi2EOuWUjfADRUBX6WKrbRaBdSKWPc1gOKCBQUUREBFQSllkRjJwpaQzPP+QTNlkskyycwzc8L3c1252plzzpz7eYZkbs+cM+MyxhgBAABYEBXuAAAA4PhB8QAAANZQPAAAgDUUDwAAYA3FAwAAWEPxAAAA1lA8AACANRQPAABgDcUDAGoxcuRI3XbbbdUu/+ijjxQbG2sxEeBcFA/AEpfLpVdeeaXW9Tp27KiHHnoo5HkQGGOMysrK/P6Ul5eHOx7gGBQPAA2ycOFC9evXT82aNVNycrJuvPFGn+Vff/21RowYoRYtWiglJUVXX321Dhw44LNOYWGhJk2apJSUFDVv3lw///nPtXXr1jpn2LRpk9q3b69du3b53L9lyxadccYZfgufMUb/7//9P2VlZSk2NlYnnXSSli5dWu0+7rvvPjVp0sTvz+DBg6usv2vXLrVr105ffPFFnccBHA8oHgDq7c4779TUqVN1/fXXa926dXrnnXc0bNgw7/K9e/dq8ODBat++vVatWqVnn31Wb731lq666iqfx7nooou0YcMGvfrqq/rwww915MgRnXvuuTp06FCtGTwej6644grNmjVL7du3lyRt2LBBkyZNUt++fbVy5Uq/291zzz168MEHNWfOHH322WcaNGiQLrjgAm3YsMHv+pMnT1ZeXp7fnzfeeKPK+u3bt9f999+vK6+8Uh6Pp9ZxAMcNA8AKSebll1+udb3MzEzzpz/9qUH7Ki8vb9D2dbFu3ToTGxtrvv7662rXufXWW03fvn198uTm5hqXy2W+/fZb72232212797tXSc/P9/ExcWZJ554otYcL7/8sunatavPPgYNGmROO+00s2LFCr/z/tNPP5lmzZqZl156yef+gQMHmiuuuKLKPn7xi1+Ym2++2RQXF/v9Wbp0qXG73VW2Ky8vN126dKnT8w4cLzjiAQRg+/bt+t3vfqesrCzFxcWpd+/eevPNN33WMcZo1qxZ3kP4AwYM0Mcff+z38Z544gmdcMIJcrvd6tGjh9//cpak3NxcDRgwQLGxscrMzNScOXN8lg8ePFg5OTl64IEHlJSUpLFjxwZnwDWYO3euxo4dq5/97GfVrvPKK69o7Nixior635+a8847T4mJiXrnnXe865xzzjlq166dd52WLVtq2LBhWrZsWa055s2bp2uuucZnH4sWLdKqVat05pln+t1m6dKliomJ0ahRo3zuHz16dLX7vP/++xUfH+/357zzzvO7TVRUlK699lo9+uijtY4DOF5QPIAAvP7664qNjdUzzzyjNWvW6IwzztCll16q3bt3e9e5+eabdd9992nq1Klas2aNrrvuOo0bN67KY/3lL3/R1VdfrQkTJmjNmjWaMWOGbrzxRv34448+67355psaNWqUzj//fK1evVrTp0/XHXfcoWeeecZnvZUrV2r16tV6//33NW3atBrHce+99yo2Nrbanw8//LDWuVi6dKnOOussXXfddUpNTVWbNm00fvx4/fTTT5Kk0tJSbdmyRb169fLZzuVyqXv37vr2228lSRs3bqyyjiT16NHDu051SktLtWLFCg0fPtzn/tTU1Bq327hxo3r06KHo6Ogq+9yzZ4/279/vc/8LL7yg4uLiGn/y8/P97mv48OH66KOPVFpaWmMm4HgRE+4AgJNMnDhRTZs29d5+6KGHNH/+fK1YsUKXXnqpduzYoT/96U969tlndemll0qSevXqpaSkJP3qV7/ybnfw4EFNmzZN9957r2666SZJUu/evdWpUyf17dvXZ5833HCDJk6cqLvvvtu73o4dOzRr1ixdfvnl3vW+++47rVixQm63u9ZxZGdn65JLLql2eUZGRo3bFxcXa+fOnZo7d64GDx6sV155Rf/5z39044036rLLLtPSpUv1008/yePxqFWrVlW2b9mypQoLCyVJeXl5ta5TnQ0bNqhZs2bq3r17jetVVtM+JamoqEjNmzevchJsTYwx3sISGxurmJijf167d++uZs2aacOGDRowYEBAOYHGiOIBBKBp06Y6fPiwPv74Y3355Zf69ttvFRUVpT179kiSli1bptjYWI0ePdpnu8qH4j/++GMVFhZq/PjxPvf36dNHbdq08d7esmWLtm7dqgkTJvisd9ZZZ+muu+7SkSNH1KRJE0nS0KFD61Q6JCklJUUpKSl1WtefoqIiSVLfvn29b/uceuqp6tChg0477TStWbPGe9Th2LdAKrhcLrlcLklSWVlZretUZ8+ePT5v0dRVTfus+N/t27crKysr4MeWjr79k52d7b3dtm1b/fDDD/V6LKCxoXgAAVi0aJGys7PVoUMH9erVS506dVJsbKz3qoU9e/aoQ4cOVV4wmzVr5nN7z549crvdPiXD37p5eXmSpNNPP91nHWOMPB6P9uzZo/T0dElHX9xsqSg7559/vs/9p556quLj47Vx40Z169ZNkvwetSgoKFDv3r0lSQkJCdWu4++oxLEKCwuVnJwccP6EhARt2bLF7z5dLpeSk5PldrtVXFzsd/snnnhCTzzxRLVXzFT+MLHk5ORaj94AxwvO8QDqaN++fbrqqqv0yCOPaNOmTXruued03333eQ+pS1Lr1q2rnKMhqcp/7bZu3VolJSVVXtiMMd6yIUlJSUmSjp7nsX79eu/P559/ri+//NLnv/b9/Rd8dRp6jkfr1q3VokULFRQUVFlWUboSEhLUtm1bffXVV1XG+PXXX6tnz56SpJ/97GdV1pGkL7/80rtOdeLj471HXwJR0z67dOmi2NhYuVwutWjRQi1atNDcuXP1xRdfeG+73W5FRUV5b1f+OfbfhHS0IMXHxwecE2iMKB5AHX377bc6dOiQzjnnHO99n3/+uU9R6Nu3r/Lz87V8+XKfbZ9//nmf2z179lRMTIz++c9/+tz/xhtv6ODBg97b3bt3V0pKirZt26bu3btX+ak48hCo7OxsnyJT+ae2cxFcLpeGDRumRYsW+dy/YsUKFRcX64wzzpB09MTK5557zmed5cuXa//+/d4TQocPH64lS5b4lJiCggItW7ZMv/zlL2vM0aZNm3q9hXHeeefpxx9/rPI8/fOf//S7z2effVabNm0KeD8V9u7dq9atW9d7e6Ax4a0WoI66dOmi5s2b64477tDkyZO1bds23X333d4TEiXp5JNP1vDhw/XrX/9aDz/8sE444QS9++67evHFF30eKzU1VVdddZVycnLk8Xi850XMnTvX59yL6Oho3XbbbfrjH/+oAwcOaOjQoTp48KA+/PBDlZeXa8qUKfUaS0PP8ZCk2267TaeffromTZqkSZMmaceOHbr22ms1fvx47yW2t9xyi/r376+cnBxNnDhR//73v5Wdna2bb77Z+zbK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72rxeDzatWuX4uPj5XI544t5jlVUVKT09HTt2LGj0X3XTGMem9S4x8fYnKsxj4+xOZe/8RljVFxcrPbt2ysqquFnaFg74hEVFaUOHTrY2l3IJCQkNMp/bFLjHpvUuMfH2JyrMY+PsTlX5fEF40hHBU4uBQAA1lA8AACANRSPOnK73brzzjvldrvDHSXoGvPYpMY9PsbmXI15fIzNuWyMz9rJpQAAABzxAAAA1lA8AACANRQPAABgDcUDAABYQ/GQtHPnTl1wwQVKS0tTp06ddM8993iXrVu3TqeddpoyMzPVo0cPLVu2zGfbhx56SF26dFFaWpouvPBC5efn245fhTFGTz31lAYOHOhzf0PGkp+fr9GjRysjI0OZmZl68MEHrYzFH3/jO3LkiO6++2716tVL6enpOuuss7R+/Xqf7RYtWqQTTjhBHTp00JAhQ/Tdd995lx06dEgTJ05UZmamOnTooJtvvjlo30sQiOqeuwoHDhxQ69atNWvWLJ/7nfDcVTc2Y4zmzJmjbt26KSMjQ126dNGRI0e8y50wNqn68b3yyis68cQTlZGRoVNOOUUfffSRz/JIH9/y5ct1xhlnqEuXLurcubMeeeQR77Lvv/9e5557rjIzM9WlSxc988wzPts64XeuuvEZY/Too4+qT58+yszMVL9+/bR8+XKfbd955x316dNHGRkZGjBggD777DPvMo/HoylTpqhjx45KS0vThAkTdPjw4YgY27GMMerZs6eys7N97g/pc2dghg4dam6++Wbj8XhMfn6+6dOnj5k/f74pKioyaWlpZtmyZcYYY95//32TmJhodu/ebYwxZvHixeakk04y+fn5pqyszGRnZ5uLLroonEMxb7/9tunZs6fp3Lmz6datm/f+ho7l5z//ubnrrruMx+MxO3fuNJmZmea1116zOzhT/fg2btxobr/9drN//35jjDF//etfTYcOHUxpaakxxpiVK1eajh07mu3btxtjjJkxY4bp37+/d/urr77aXHXVVebIkSOmoKDADBgwwMydO9fiyKof27Fmz55toqOjzcyZM733OeG5q2ls99xzjxk0aJD54YcfjDHG7Ny505SXlxtjnDE2Y6of37Zt20x8fLxZs2aNMcaYpUuXmuTkZFNQUGCMccb4rr/+evPVV18ZY4zZunWrSUtLM2+//bYpKyszPXv2NPPnzzfG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", - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], + "outputs": [], "source": [ "# import matplotlib.pyplot as plt\n", "# import matplotlib as mpl\n", @@ -1325,370 +1545,248 @@ }, { "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "preprocess(105)\n", - "preprocess(106)" - ] - }, - { - "cell_type": "code", - "execution_count": 23, + "execution_count": 25, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ - "30\n", - "2024-01-05 02:30:00\n", - "31\n", - "2024-01-05 02:35:00\n", - "32\n", - "2024-01-05 02:40:00\n", - "33\n", - "2024-01-05 02:45:00\n", - "34\n", - "2024-01-05 02:50:00\n", - "35\n", - "2024-01-05 02:55:00\n", - "36\n", - "2024-01-05 03:00:00\n", - "37\n", - "2024-01-05 03:05:00\n", - "38\n", - "2024-01-05 03:10:00\n", - "39\n", - "2024-01-05 03:15:00\n", - "40\n", - "2024-01-05 03:20:00\n", - "41\n", - "2024-01-05 03:25:00\n", - "42\n", - "2024-01-05 03:30:00\n", - "43\n", - "2024-01-05 03:35:00\n", - "44\n", - "2024-01-05 03:40:00\n", - "45\n", - "2024-01-05 03:45:00\n", - "46\n", - "2024-01-05 03:50:00\n", - "47\n", - "2024-01-05 03:55:00\n", - "48\n", - "2024-01-05 04:00:00\n", - "49\n", - "2024-01-05 04:05:00\n", - "50\n", - "2024-01-05 04:10:00\n", - "51\n", - "2024-01-05 04:15:00\n", - "52\n", - "2024-01-05 04:20:00\n", - "53\n", - "2024-01-05 04:25:00\n", - "54\n", - "2024-01-05 04:30:00\n", - "55\n", - "2024-01-05 04:35:00\n", - "56\n", - "2024-01-05 04:40:00\n", - "57\n", - "2024-01-05 04:45:00\n", - "58\n", - "2024-01-05 04:50:00\n", - "59\n", - "2024-01-05 04:55:00\n", - "60\n", - "2024-01-05 05:00:00\n", - "61\n", - "2024-01-05 05:05:00\n", - "62\n", - "2024-01-05 05:10:00\n", - "63\n", - "2024-01-05 05:15:00\n", - "64\n", - "2024-01-05 05:20:00\n", - "65\n", - "2024-01-05 05:25:00\n", - "66\n", - "2024-01-05 05:30:00\n", - "67\n", - "2024-01-05 05:35:00\n", - "68\n", - "2024-01-05 05:40:00\n", - "69\n", - "2024-01-05 05:45:00\n", - "70\n", - "2024-01-05 05:50:00\n", - "71\n", - "2024-01-05 05:55:00\n", - "72\n", - "2024-01-05 06:00:00\n", - "73\n", - "2024-01-05 06:05:00\n", - "74\n", - "2024-01-05 06:10:00\n", - "75\n", - "2024-01-05 06:15:00\n", - "76\n", - "2024-01-05 06:20:00\n", - "77\n", - "2024-01-05 06:25:00\n", - "78\n", - "2024-01-05 06:30:00\n", - "79\n", - "2024-01-05 06:35:00\n", - "80\n", - "2024-01-05 06:40:00\n", - "81\n", - "2024-01-05 06:45:00\n", - "82\n", - "2024-01-05 06:50:00\n", - "83\n", - "2024-01-05 06:55:00\n", - "84\n", - "2024-01-05 07:00:00\n", - "85\n", - "2024-01-05 07:05:00\n", - "86\n", - "2024-01-05 07:10:00\n", - "87\n", - "2024-01-05 07:15:00\n", - "88\n", - "2024-01-05 07:20:00\n", - "89\n", - "2024-01-05 07:25:00\n", - "90\n", - "2024-01-05 07:30:00\n", - "91\n", - "2024-01-05 07:35:00\n", - "92\n", - "2024-01-05 07:40:00\n", - "93\n", - "2024-01-05 07:45:00\n", - "94\n", - "2024-01-05 07:50:00\n", - "95\n", - "2024-01-05 07:55:00\n", - "96\n", - "2024-01-05 08:00:00\n", - "97\n", - "2024-01-05 08:05:00\n", - "98\n", - "2024-01-05 08:10:00\n", - "99\n", - "2024-01-05 08:15:00\n", - "100\n", - "2024-01-05 08:20:00\n", - "101\n", - "2024-01-05 08:25:00\n", - "102\n", - "2024-01-05 08:30:00\n", - "103\n", - "2024-01-05 08:35:00\n", - "104\n", - "2024-01-05 08:40:00\n", - "105\n", "2024-01-05 08:45:00\n", - "106\n", - "2024-01-05 08:50:00\n", - "107\n", - "2024-01-05 08:55:00\n", - "108\n", - "2024-01-05 09:00:00\n", - "109\n", - "2024-01-05 09:05:00\n", - "110\n", - "2024-01-05 09:10:00\n", - "111\n", - "2024-01-05 09:15:00\n", - "112\n", - "2024-01-05 09:20:00\n", - "113\n", - "2024-01-05 09:25:00\n", - "114\n", - "2024-01-05 09:30:00\n", - "115\n", - "2024-01-05 09:35:00\n", - "116\n", - "2024-01-05 09:40:00\n", - "117\n", - "2024-01-05 09:45:00\n", - "118\n", - "2024-01-05 09:50:00\n", - "119\n", - "2024-01-05 09:55:00\n", - "120\n", - "2024-01-05 10:00:00\n", - "121\n", - "2024-01-05 10:05:00\n", - "122\n", - "2024-01-05 10:10:00\n", - "123\n", - "2024-01-05 10:15:00\n", - "124\n", - "2024-01-05 10:20:00\n", - "125\n", - "2024-01-05 10:25:00\n", - "126\n", - "2024-01-05 10:30:00\n", - "127\n", - "2024-01-05 10:35:00\n", - "128\n", - "2024-01-05 10:40:00\n", - "129\n", - "2024-01-05 10:45:00\n", - "130\n", - "2024-01-05 10:50:00\n", - "131\n", - "2024-01-05 10:55:00\n", - "132\n", - "2024-01-05 11:00:00\n", - "133\n", - "2024-01-05 11:05:00\n", - "134\n", - "2024-01-05 11:10:00\n", - "135\n", - "2024-01-05 11:15:00\n", - "136\n", - "2024-01-05 11:20:00\n", - "137\n", - "2024-01-05 11:25:00\n", - "138\n", - "2024-01-05 11:30:00\n", - "139\n", - "2024-01-05 11:35:00\n", - "140\n", - "2024-01-05 11:40:00\n", - "141\n", - "2024-01-05 11:45:00\n", - "142\n", - "2024-01-05 11:50:00\n", - "143\n", - "2024-01-05 11:55:00\n", - "144\n", - "2024-01-05 12:00:00\n", - "145\n", - "2024-01-05 12:05:00\n", - "146\n", - "2024-01-05 12:10:00\n", - "147\n", - "2024-01-05 12:15:00\n", - "148\n", - "2024-01-05 12:20:00\n", - "149\n", - "2024-01-05 12:25:00\n", - "150\n", - "2024-01-05 12:30:00\n", - "151\n", - "2024-01-05 12:35:00\n", - "152\n", - "2024-01-05 12:40:00\n", - "153\n", - "2024-01-05 12:45:00\n", - "154\n", - "2024-01-05 12:50:00\n", - "155\n", - "2024-01-05 12:55:00\n", - "156\n", - "2024-01-05 13:00:00\n", - "157\n", - "2024-01-05 13:05:00\n", - "158\n", - "2024-01-05 13:10:00\n", - "159\n", - "2024-01-05 13:15:00\n", - "160\n", - "2024-01-05 13:20:00\n", - "161\n", - "2024-01-05 13:25:00\n", - "162\n", - "2024-01-05 13:30:00\n", - "163\n", - "2024-01-05 13:35:00\n", - "164\n", - "2024-01-05 13:40:00\n", - "165\n", - "2024-01-05 13:45:00\n", - "166\n", - "2024-01-05 13:50:00\n", - "167\n", - "2024-01-05 13:55:00\n", - "168\n", - "2024-01-05 14:00:00\n", - "169\n", - "2024-01-05 14:05:00\n", - "170\n", - "2024-01-05 14:10:00\n", - "171\n", - "2024-01-05 14:15:00\n", - "172\n", - "2024-01-05 14:20:00\n", - "173\n", - "2024-01-05 14:25:00\n", - "174\n", - "2024-01-05 14:30:00\n", - "175\n", - "2024-01-05 14:35:00\n", - "176\n", - "2024-01-05 14:40:00\n", - "177\n", - "2024-01-05 14:45:00\n", - "178\n", - "2024-01-05 14:50:00\n", - "179\n", - "2024-01-05 14:55:00\n", - "180\n", - "2024-01-05 15:00:00\n", - "181\n", - "2024-01-05 15:05:00\n", - "182\n", - "2024-01-05 15:10:00\n", - "183\n", - "2024-01-05 15:15:00\n", - "184\n", - "2024-01-05 15:20:00\n", - "185\n", - "2024-01-05 15:25:00\n", - "186\n", - "2024-01-05 15:30:00\n", - "187\n", - "2024-01-05 15:35:00\n", - "188\n", - "2024-01-05 15:40:00\n", - "189\n", - "2024-01-05 15:45:00\n", - "190\n", - "2024-01-05 15:50:00\n", - "191\n", - "2024-01-05 15:55:00\n", - "192\n", - "2024-01-05 16:00:00\n", - "193\n", - "2024-01-05 16:05:00\n", - "194\n", - "2024-01-05 16:10:00\n", - "195\n", - "2024-01-05 16:15:00\n", - "196\n", - "2024-01-05 16:20:00\n", - "197\n", - "2024-01-05 16:25:00\n", - "198\n", - "2024-01-05 16:30:00\n", - "199\n", - "2024-01-05 16:35:00\n" + "2024-01-05 08:50:00\n" ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
inter_nonode_idstart_unixphas_Aphas_Bdurationinc_edge_Aout_edge_Ainc_edge_Bout_edge_B
1756202i917044116101146571510152_02-571510152_01571510152_01571510152_01.65
1757202i9170441161022114NaN-571510152_01NaNNaN
1758175i017044116291140-571542797_02571500487_01-571500487_01571542797_02
1759175i017044116292242-571500487_01571545870_01-571542797_02571510153_01
1760175i017044116293329571545870_02571510153_01571545870_02571542797_02
.................................
1972201i817044126405517571500583_01571500617_01571500583_01571500569_01
1973206i717044126601125-571511538_02571542073_02571542073_01571511538_02
1974206i717044126602225NaN571542073_02NaNNaN
1975206i717044126603315-571511538_02571542073_02571542073_01571511538_02
1976206i717044126604415NaN571542073_02NaNNaN
\n", + "

221 rows × 10 columns

\n", + "
" + ], + "text/plain": [ + " inter_no node_id start_unix phas_A phas_B duration inc_edge_A \\\n", + "1756 202 i9 1704411610 1 1 46 571510152_02 \n", + "1757 202 i9 1704411610 2 2 114 NaN \n", + "1758 175 i0 1704411629 1 1 40 -571542797_02 \n", + "1759 175 i0 1704411629 2 2 42 -571500487_01 \n", + "1760 175 i0 1704411629 3 3 29 571545870_02 \n", + "... ... ... ... ... ... ... ... \n", + "1972 201 i8 1704412640 5 5 17 571500583_01 \n", + "1973 206 i7 1704412660 1 1 25 -571511538_02 \n", + "1974 206 i7 1704412660 2 2 25 NaN \n", + "1975 206 i7 1704412660 3 3 15 -571511538_02 \n", + "1976 206 i7 1704412660 4 4 15 NaN \n", + "\n", + " out_edge_A inc_edge_B out_edge_B \n", + "1756 -571510152_01 571510152_01 571510152_01.65 \n", + "1757 -571510152_01 NaN NaN \n", + "1758 571500487_01 -571500487_01 571542797_02 \n", + "1759 571545870_01 -571542797_02 571510153_01 \n", + "1760 571510153_01 571545870_02 571542797_02 \n", + "... ... ... ... \n", + "1972 571500617_01 571500583_01 571500569_01 \n", + "1973 571542073_02 571542073_01 571511538_02 \n", + "1974 571542073_02 NaN NaN \n", + "1975 571542073_02 571542073_01 571511538_02 \n", + "1976 571542073_02 NaN NaN \n", + "\n", + "[221 rows x 10 columns]" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ - "for m in range(30, 288):\n", - " print(m)\n", - " histid = preprocess(m)" + "preprocess(105)\n", + "preprocess(106)" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "metadata": {}, + "outputs": [], + "source": [ + "# for m in range(30, 288):\n", + "# print(m)\n", + "# histid = preprocess(m)" ] } ], diff --git a/Script/preprocess_5min.ipynb b/Script/preprocess_5min.ipynb new file mode 100644 index 000000000..1a7c91a6a --- /dev/null +++ b/Script/preprocess_5min.ipynb @@ -0,0 +1,1004 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import os\n", + "from tqdm import tqdm\n", + "from datetime import datetime" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "datetime.datetime(2024, 1, 5, 11, 55, 13, 99135)" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "now = datetime.now()\n", + "now = now.replace(month=1, day=5)\n", + "now" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "# 5초 단위로 이동류번호 저장 및 신호이력에서 유닉스시각 가져와서 표시, 한시간동안의 데이터만 보관\n", + "midnight = int(datetime(2024, 1, 5, 0, 0, 0).timestamp())\n", + "next_day = int(datetime(2024, 1, 6, 0, 0, 0).timestamp())\n", + "fsecs = range(midnight, next_day, 5) # fsecs : unix time by Five SECondS\n", + "fmins = range(midnight, next_day, 300) # fmins : unix time by Five MINuteS" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
inter_nophas_Aphas_Bmove_Amove_Bstart_unix
017711841704408330
117722731704408330
21773317181704408330
317744511704408330
420111831704408330
.....................
70017844611704411830
70120111831704411850
70220144611704411850
70320155741704411850
7042062217181704411880
\n", + "

705 rows × 6 columns

\n", + "
" + ], + "text/plain": [ + " inter_no phas_A phas_B move_A move_B start_unix\n", + "0 177 1 1 8 4 1704408330\n", + "1 177 2 2 7 3 1704408330\n", + "2 177 3 3 17 18 1704408330\n", + "3 177 4 4 5 1 1704408330\n", + "4 201 1 1 8 3 1704408330\n", + ".. ... ... ... ... ... ...\n", + "700 178 4 4 6 1 1704411830\n", + "701 201 1 1 8 3 1704411850\n", + "702 201 4 4 6 1 1704411850\n", + "703 201 5 5 7 4 1704411850\n", + "704 206 2 2 17 18 1704411880\n", + "\n", + "[705 rows x 6 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "history = pd.read_csv('../Data/tables/history.csv', index_col=0)\n", + "m = 105\n", + "present_time = fmins[m]\n", + "\n", + "# - 아래 절차를 5초마다 반복\n", + "for fsec in range(midnight, present_time + 1, 5): # fsec : unix time by Five SECond\n", + " # 1. 상태 테이블 조회해서 전체 데이터중 필요데이터(교차로번호, A링 현시번호, A링 이동류번호, B링 현시번호, B링 이동류번호)만 수집 : A\n", + " # move = time2move[fsec]\n", + " move = pd.read_csv(f'../Data/tables/move/move_{fsec}.csv', index_col=0)\n", + " # 2. 이력 테이블 조회해서 교차로별로 유닉스시간 최대인 데이터(교차로변호, 종료유닉스타임)만 수집 : B\n", + " recent_histories = [group.iloc[-1:] for _, group in history[history['end_unix'] < fsec].groupby('inter_no')] # 교차로별로 유닉스시간이 최대인 행들\n", + " if not recent_histories:\n", + " rhistory = pd.DataFrame({'inter_no':[], 'end_unix':[]}) # recent history\n", + " else:\n", + " rhistory = pd.concat(recent_histories)\n", + " recent_unix = rhistory[['inter_no', 'end_unix']]\n", + " # 3. 상태 테이블 조회정보(A)와 이력 테이블 조회정보(B) 조인(키값 : 교차로번호) : C\n", + " move = pd.merge(move, recent_unix, how='left', on='inter_no')\n", + " move['end_unix'] = move['end_unix'].fillna(0).astype(int)\n", + " move = move.drop_duplicates()\n", + " # 4. C데이터 프레임에 신규 컬럼(시작 유닉스타임) 생성 후 종료유닉스 타임 값 입력, 종료 유닉스 타임 컬럼 제거\n", + " move = move.rename(columns = {'end_unix':'start_unix'})\n", + " # 5. 이동류 이력정보 READ\n", + " # - CSV 파일로 서버에 저장된 이동류정보를 읽어옴(파일이 없는 경우에는 데이터가 없는 프레임 D 생성)\n", + " try:\n", + " if isinstance(movement, pd.DataFrame): # movement가 존재할 경우 그걸 그대로 씀.\n", + " pass\n", + " else: \n", + " movement = pd.DataFrame()\n", + " except NameError: # movement가 존재하지 않는 경우 생성\n", + " movement = pd.DataFrame()\n", + " # 6. 이동류 이력정보 데이터테이블(D)에 C데이터 add\n", + " movement = pd.concat([movement, move])\n", + " # 7. D데이터 프레임에서 중복데이터 제거(교차로번호, 시작 유닉스타임, A링 현시번호, B링 현시번호 같은 행은 제거)\n", + " movement = movement.drop_duplicates(['inter_no','phas_A','phas_B','start_unix'])\n", + " # 8. D데이터 보관 시간 기준시간을 시작 유닉스 타임의 최대값 - 3600을 값으로 산출하고, 보관 시간 기준시간보다 작은 시작 유닉스 타임을 가진 행은 모두 제거(1시간 데이터만 보관)\n", + " movement = movement[movement.start_unix > fsec - 3600]\n", + " movement = movement.sort_values(by=['start_unix','inter_no','phas_A','phas_B']).reset_index(drop=True)\n", + "\n", + "display(movement)" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def make_splits(plan):\n", + " # split, isplit : A,B 분리 혹은 통합시 사용될 수 있는 딕셔너리 \n", + " splits = {} # splits maps (inter_no, start_hour, start_minute) to split \n", + " for i, row in plan.iterrows():\n", + " inter_no = row.inter_no\n", + " start_hour = row.start_hour\n", + " start_minute = row.start_minute\n", + " cycle = row.cycle\n", + " cums_A = row[[f'dura_A{j}' for j in range(1,9)]].cumsum()\n", + " cums_B = row[[f'dura_B{j}' for j in range(1,9)]].cumsum()\n", + " splits[(inter_no, start_hour, start_minute)] = {} # split maps (phas_A, phas_B) to k\n", + " k = 0\n", + " for t in range(cycle):\n", + " new_phas_A = len(cums_A[cums_A < t]) + 1\n", + " new_phas_B = len(cums_B[cums_B < t]) + 1\n", + " if k == 0 or ((new_phas_A, new_phas_B) != (phas_A, phas_B)):\n", + " k += 1\n", + " phas_A = new_phas_A\n", + " phas_B = new_phas_B\n", + " splits[(inter_no, start_hour, start_minute)][(phas_A, phas_B)] = k\n", + "\n", + " isplits = {} # the inverse of splits\n", + " for i in splits:\n", + " isplits[i] = {splits[i][k]:k for k in splits[i]} # isplit maps k to (phas_A, phas_B)\n", + " return splits, isplits\n", + "\n", + "def make_timetable(plan):\n", + " # timetable\n", + " timetable = plan[['start_hour', 'start_minute']].drop_duplicates()\n", + " timetable['start_seconds'] = midnight + timetable['start_hour'] * 3600 + timetable['start_minute'] * 60\n", + " return timetable\n", + "\n", + "# inter2node\n", + "inter_node = pd.read_csv('../Data/tables/inter_node.csv', index_col=0)\n", + "inter_node = inter_node[inter_node.inter_type=='parent']\n", + "inter2node = dict(zip(inter_node['inter_no'], inter_node['node_id']))\n", + "\n", + "hours = np.array(range(midnight - 7200, next_day + 1, 3600)) # 정각에 해당하는 시각들 목록" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def calculate_DS(rhist, curr_unix, hours, timetable):\n", + " program_starts = np.array(timetable.start_seconds)\n", + " idx = (program_starts <= present_time).sum() - 1\n", + " program_start = program_starts[idx]\n", + " if list(hours[hours <= curr_unix]):\n", + " ghour_lt_curr_unix = hours[hours <= curr_unix].max() # the greatest hour less than or equal to curr_unix\n", + " else:\n", + " ghour_lt_curr_unix = program_start\n", + " start_unixes = rhist.start_unix.unique()\n", + " start_unixes_lt_ghour = np.sort(start_unixes[start_unixes < ghour_lt_curr_unix]) # start unixes less than ghour_lt_curr_unix\n", + " # 기준유닉스(base_unix) : curr_unix보다 작은 hour 중에서 가장 큰 값으로부터 다섯 번째로 작은 start_unix\n", + " if len(start_unixes_lt_ghour) > 5:\n", + " base_unix = start_unixes_lt_ghour[-5]\n", + " # start_unixes_lt_ghour의 길이가 5 미만일 경우에는 맨 앞 start_unix로 base_unix를 지정\n", + " else:\n", + " base_unix = rhist.start_unix.min()\n", + " D_n = curr_unix - base_unix\n", + " S_n_durs = rhist[(rhist.start_unix > base_unix) & (rhist.start_unix <= curr_unix)] \\\n", + " [[f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]]\n", + " S_n = S_n_durs.values.sum() // 2\n", + " return D_n, S_n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def load_prow(plan, timetable, inter_no, time):\n", + " '''\n", + " load planned row\n", + " '''\n", + " # 프로그램 시작시각\n", + " program_starts = np.array(timetable.start_seconds)\n", + " idx = (program_starts <= time).sum() - 1\n", + " program_start = program_starts[idx]\n", + "\n", + " # 최근 프로그램 시작시각에 대한 신호계획\n", + " start_hour = timetable.iloc[idx].start_hour\n", + " start_minute = timetable.iloc[idx].start_minute\n", + " prow = plan[(plan.inter_no==inter_no) & (plan.start_hour==start_hour) & (plan.start_minute==start_minute)] # planned row\n", + " return program_start, prow" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "def make_rhistory(plan, timetable, history, present_time, adder):\n", + " # 1. 조회시점의 유닉스 타임 이전의 신호이력 수집\n", + " rhistory = history.copy() # recent history\n", + " rhistory = rhistory[(rhistory.end_unix <= present_time) & (rhistory.end_unix > present_time - 9000)] # 두 시간 반 전부터 현재까지의 신호이력을 가져옴. 9000 = 3600 * 2.5\n", + "\n", + " # rhistory에 모든 교차로번호가 존재하지 않으면 해당 교차로번호에 대한 신호이력을 추가함 (at 최근 프로그램 시작시각)\n", + " whole_inter_nos = sorted(history.inter_no.unique())\n", + " recent_inter_nos = sorted(rhistory.inter_no.unique())\n", + " if not whole_inter_nos==recent_inter_nos:\n", + " for inter_no in set(whole_inter_nos) - set(recent_inter_nos):\n", + " program_start, prow = load_prow(plan, timetable, inter_no, present_time - 9000)\n", + " cycle = prow.cycle.iloc[0]\n", + " row1 = prow.drop(['start_hour', 'start_minute'], axis=1).copy()\n", + " row2 = prow.drop(['start_hour', 'start_minute'], axis=1).copy()\n", + " # prow에서 필요한 부분을 rhistory에 추가\n", + " row1['end_unix'] = program_start\n", + " row2['end_unix'] = program_start + cycle\n", + " rhistory = pd.concat([rhistory, row1, row2]).reset_index(drop=True)\n", + " # present_time + adder 의 시각에 한 주기의 신호 추가\n", + " for inter_no in set(whole_inter_nos):\n", + " program_start, prow = load_prow(plan, timetable, inter_no, present_time)\n", + " cycle = prow.cycle.iloc[0]\n", + " row3 = prow.drop(['start_hour', 'start_minute'], axis=1).copy()\n", + " # prow에서 필요한 부분을 rhistory에 추가\n", + " row3['end_unix'] = present_time + adder\n", + " rhistory = pd.concat([rhistory, row3]).reset_index(drop=True)\n", + "\n", + " # 2. 시작 유닉스 타임컬럼 생성 후 종류 유닉스 타임에서 현시별 현시기간 컬럼의 합을 뺀 값으로 입력\n", + " # - 현시시간의 합을 뺀 시간의 +- 10초 이내에 이전 주기정보가 존재하면 그 유닉스 시간을 시작 유닉스시간 값으로 하고, 존재하지 않으면 현시시간의 합을 뺀 유닉스 시간을 시작 유닉스 시간으로 지정\n", + " for i, row in rhistory.iterrows():\n", + " inter_no = row.inter_no\n", + " end_unix = row.end_unix\n", + " elapsed_time = row[[f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]].sum() // 2 # 현시시간 합\n", + " # 이전 유닉스 존재하지 않음 : 현시시간 합의 차\n", + " start_unix = end_unix - elapsed_time\n", + " pre_rows = history[:i] # previous rows\n", + " if inter_no in pre_rows.inter_no.unique(): # 이전 유닉스 존재\n", + " pre_unix = pre_rows[pre_rows.inter_no == inter_no]['end_unix'].iloc[-1] # previous unix time\n", + " # 이전 유닉스 존재, abs < 10 : 이전 유닉스\n", + " if abs(pre_unix - start_unix) < 10:\n", + " start_unix = pre_unix\n", + " # 이전 유닉스 존재, abs >=10 : 현시시간 합의 차\n", + " else:\n", + " pass\n", + " rhistory.loc[i, 'start_unix'] = start_unix \n", + " rhistory[rhistory.isna()] = 0\n", + " rhistory['start_unix'] = rhistory['start_unix'].astype(int)\n", + " rhistory = rhistory[['inter_no', 'start_unix'] + [f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)] + ['cycle']]\n", + " return rhistory\n", + "adder = 600" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "def processing(plan, rhistory, timetable, hours):\n", + " rhists = []\n", + " for inter_no in sorted(rhistory.inter_no.unique()):\n", + " rhist = rhistory.copy()[rhistory.inter_no==inter_no]\n", + " rhist = rhist.drop_duplicates(subset=['start_unix']).reset_index(drop=True)\n", + "\n", + " # D_n 및 S_n 값 정의\n", + " rhist['D_n'] = 0 # D_n : 시간차이\n", + " rhist['S_n'] = 0 # S_n : 현시시간합\n", + " for n in range(len(rhist)):\n", + " curr_unix = rhist.iloc[n].start_unix # current start_unix\n", + " rhist.loc[n, ['D_n', 'S_n']] = calculate_DS(rhist, curr_unix, hours, timetable)\n", + "\n", + " # 이전시각, 현재시각\n", + " prev_unix = rhist.loc[0, 'start_unix'] # previous start_unix\n", + " curr_unix = rhist.loc[1, 'start_unix'] # current start_unix\n", + "\n", + " # rhist의 마지막 행에 도달할 때까지 반복\n", + " while True:\n", + " n = rhist[rhist.start_unix==curr_unix].index[0]\n", + " cycle = rhist.loc[n, 'cycle']\n", + " D_n = rhist.loc[n, 'D_n']\n", + " S_n = rhist.loc[n, 'S_n']\n", + " # 참값인 경우\n", + " if (abs(D_n - S_n) <= 5):\n", + " pass\n", + " # 참값이 아닌 경우\n", + " else:\n", + " # 2-1-1. 결측치 처리 : 인접한 두 start_unix의 차이가 계획된 주기의 두 배보다 크면 결측이 일어났다고 판단, 신호계획의 현시시간으로 \"대체\"\n", + " if curr_unix - prev_unix >= 2 * cycle:\n", + " # prev_unix를 계획된 주기만큼 늘려가면서 한 행씩 채워나간다.\n", + " # (curr_unix와의 차이가 계획된 주기보다 작거나 같아질 때까지)\n", + " while curr_unix - prev_unix > cycle:\n", + " prev_unix += cycle\n", + " # 신호 계획(prow) 불러오기\n", + " start_seconds = np.array(timetable.start_seconds)\n", + " idx = (start_seconds <= prev_unix).sum() - 1\n", + " start_hour = timetable.iloc[idx].start_hour\n", + " start_minute = timetable.iloc[idx].start_minute\n", + " prow = plan.copy()[(plan.inter_no==inter_no) & (plan.start_hour==start_hour) & (plan.start_minute==start_minute)] # planned row\n", + " # prow에서 필요한 부분을 rhist에 추가\n", + " prow['start_unix'] = prev_unix\n", + " prow = prow.drop(['start_hour', 'start_minute', 'offset'], axis=1)\n", + " cycle = prow.iloc[0].cycle\n", + " rhist = pd.concat([rhist, prow])\n", + " rhist = rhist.sort_values(by='start_unix').reset_index(drop=True)\n", + " n += 1\n", + "\n", + " # 2-1-2. 이상치 처리 : 비율에 따라 해당 행을 \"삭제\"(R_n <= 0.5) 또는 \"조정\"(R_n > 0.5)한다\n", + " R_n = (curr_unix - prev_unix) / cycle # R_n : 비율\n", + " # R_n이 0.5보다 작거나 같으면 해당 행을 삭제\n", + " if R_n <= 0.5:\n", + " rhist = rhist.drop(index=n).reset_index(drop=True)\n", + " if n >= rhist.index[-1]:\n", + " break\n", + " # 행삭제에 따른 curr_unix, R_n 재정의\n", + " curr_unix = rhist.loc[n, 'start_unix']\n", + " R_n = (curr_unix - prev_unix) / cycle # R_n : 비율\n", + "\n", + " # R_n이 0.5보다 크면 해당 행 조정 (비율을 유지한 채로 현시시간 대체)\n", + " if R_n > 0.5:\n", + " # 신호 계획(prow) 불러오기\n", + " start_seconds = np.array(timetable.start_seconds)\n", + " idx = (start_seconds <= curr_unix).sum() - 1\n", + " start_hour = timetable.iloc[idx].start_hour\n", + " start_minute = timetable.iloc[idx].start_minute\n", + " prow = plan[(plan.inter_no==inter_no) & (plan.start_hour==start_hour) & (plan.start_minute==start_minute)] # planned row\n", + " # 조정된 현시시간 (prow에 R_n을 곱하고 정수로 바꿈)\n", + " adjusted_dur = prow.copy()[[f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]] * R_n\n", + " int_parts = adjusted_dur.iloc[0].apply(lambda x: int(x))\n", + " frac_parts = adjusted_dur.iloc[0] - int_parts\n", + " difference = round(adjusted_dur.iloc[0].sum()) - int_parts.sum()\n", + " for _ in range(difference): # 소수 부분이 가장 큰 상위 'difference'개의 값에 대해 올림 처리\n", + " max_frac_index = frac_parts.idxmax()\n", + " int_parts[max_frac_index] += 1\n", + " frac_parts[max_frac_index] = 0 # 이미 처리된 항목은 0으로 설정\n", + " # rhist에 조정된 현시시간을 반영\n", + " rhist.loc[n, [f'dura_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]] = int_parts.values\n", + " rhist.loc[n, 'cycle'] = int_parts.sum().sum() // 2\n", + "\n", + " if n >= rhist.index[-1]:\n", + " break\n", + " prev_unix = curr_unix\n", + " curr_unix = rhist.loc[n+1, 'start_unix']\n", + "\n", + " # 생략해도 무방할 코드\n", + " rhist = rhist.reset_index(drop=True)\n", + " rhist = rhist.sort_values(by=['start_unix'])\n", + "\n", + " # D_n 및 S_n 값 재정의\n", + " for n in range(len(rhist)):\n", + " curr_unix = rhist.iloc[n].start_unix # current start_unix\n", + " rhist.loc[n, ['D_n', 'S_n']] = calculate_DS(rhist, curr_unix, hours, timetable)\n", + " rhists.append(rhist)\n", + " rhists = pd.concat(rhists).sort_values(by=['start_unix','inter_no'])\n", + " rhists = rhists[rhists.start_unix >= present_time - 3600]\n", + " rhists = rhists.drop(columns=['D_n', 'S_n'])\n", + " return rhists" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "def make_hrhists(rhists, isplits, timetable):\n", + " # 계층화된 형태로 변환\n", + " hrhists = [] # hierarchied recent history\n", + " for i, row in rhists.iterrows():\n", + " inter_no = row.inter_no\n", + " start_unix = row.start_unix\n", + "\n", + " ind = (timetable['start_seconds'] <= row.start_unix).sum() - 1\n", + " start_hour = timetable.iloc[ind].start_hour\n", + " start_minute = timetable.iloc[ind].start_minute\n", + " isplit = isplits[(inter_no, start_hour, start_minute)]\n", + " phas_As = [isplit[j][0] for j in isplit.keys()]\n", + " phas_Bs = [isplit[j][1] for j in isplit.keys()]\n", + " durs_A = row[[f'dura_A{j}' for j in range(1,9)]]\n", + " durs_B = row[[f'dura_B{j}' for j in range(1,9)]]\n", + " durations = []\n", + " for j in range(1, len(isplit)+1):\n", + " ja = isplit[j][0]\n", + " jb = isplit[j][1]\n", + " if ja == jb:\n", + " durations.append(min(durs_A[ja-1], durs_B[jb-1]))\n", + " else:\n", + " durations.append(abs(durs_A[ja-1] - durs_B[ja-1]))\n", + " new_rows = pd.DataFrame({'inter_no':[inter_no] * len(durations), 'start_unix':[start_unix] * len(durations),\n", + " 'phas_A':phas_As, 'phas_B':phas_Bs, 'duration':durations})\n", + " hrhists.append(new_rows)\n", + " hrhists = pd.concat(hrhists)\n", + " hrhists = hrhists.sort_values(by = ['start_unix', 'inter_no', 'phas_A', 'phas_B']).reset_index(drop=True)\n", + " return hrhists" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "def update_movement(hrhists, movement, movements):\n", + " # 중복을 제거하고 (inter_no, start_unix) 쌍을 만듭니다.\n", + " hrhists_inter_unix = set(hrhists[['inter_no', 'start_unix']].drop_duplicates().itertuples(index=False, name=None))\n", + " movement_inter_unix = set(movement[['inter_no', 'start_unix']].drop_duplicates().itertuples(index=False, name=None))\n", + "\n", + " # hrhists에는 있지만 movement에는 없는 (inter_no, start_unix) 쌍을 찾습니다.\n", + " missing_in_movement = hrhists_inter_unix - movement_inter_unix\n", + "\n", + " # 새로운 행들을 생성합니다.\n", + " new_rows = []\n", + " if missing_in_movement:\n", + " for inter_no, start_unix in missing_in_movement:\n", + " # movements에서 해당 inter_no의 데이터를 찾습니다.\n", + " new_row = movements[movements['inter_no'] == inter_no].copy()\n", + " # start_unix 값을 설정합니다.\n", + " new_row['start_unix'] = start_unix\n", + " new_rows.append(new_row)\n", + "\n", + " # 새로운 데이터프레임을 생성하고 기존 movement 데이터프레임과 합칩니다.\n", + " new_movement = pd.concat(new_rows, ignore_index=True)\n", + " movement_updated = pd.concat([movement, new_movement], ignore_index=True)\n", + " else:\n", + " movement_updated = movement\n", + " return movement_updated" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "def make_movements():\n", + " movements_path = '../Intermediates/movement/'\n", + " movements_list = [pd.read_csv(movements_path + file, index_col=0) for file in tqdm(os.listdir(movements_path))]\n", + " movements = pd.concat(movements_list)\n", + " movements = movements.drop(columns=['start_unix'])\n", + " movements = movements.drop_duplicates()\n", + " movements = movements.sort_values(by=['inter_no', 'phas_A', 'phas_B'])\n", + " movements = movements.reset_index(drop=True)\n", + " movements.to_csv('../Intermediates/movements.csv')\n", + " return movements" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "def make_histid(present_time, hrhists, movement_updated, inter2node, matching):\n", + " # movements and durations\n", + " movedur = pd.merge(hrhists, movement_updated, how='inner', on=['inter_no', 'start_unix', 'phas_A', 'phas_B'])\n", + " movedur = movedur.sort_values(by=['start_unix', 'inter_no', 'phas_A','phas_B'])\n", + " movedur = movedur[['inter_no', 'start_unix', 'phas_A', 'phas_B', 'move_A', 'move_B', 'duration']]\n", + "\n", + " # 이동류 매칭 테이블에서 진입id, 진출id를 가져와서 붙임.\n", + " for i, row in movedur.iterrows():\n", + " inter_no = row.inter_no\n", + " start_unix = row.start_unix\n", + " # incoming and outgoing edges A\n", + " move_A = row.move_A\n", + " if move_A in [17, 18]:\n", + " inc_edge_A = np.nan\n", + " outhedge_A = np.nan\n", + " else:\n", + " match_A = matching[(matching.inter_no == inter_no) & (matching.move_no == move_A)].iloc[0]\n", + " inc_edge_A = match_A.inc_edge\n", + " out_edge_A = match_A.out_edge\n", + " movedur.loc[i, ['inc_edge_A', 'out_edge_A']] = [inc_edge_A, out_edge_A]\n", + " # incoming and outgoing edges B\n", + " move_B = row.move_B\n", + " if move_B in [17, 18]:\n", + " inc_edge_B = np.nan\n", + " out_edge_B = np.nan\n", + " else:\n", + " match_B = matching[(matching.inter_no == inter_no) & (matching.move_no == move_B)].iloc[0]\n", + " inc_edge_B = match_B.inc_edge\n", + " out_edge_B = match_B.out_edge\n", + " movedur.loc[i, ['inc_edge_B', 'out_edge_B']] = [inc_edge_B, out_edge_B]\n", + "\n", + " # 이동류 컬럼 제거\n", + " movedur = movedur.drop(['move_A', 'move_B'], axis=1)\n", + "\n", + " histid = movedur.copy() # history with edge ids (incoming and outgoing edge ids)\n", + " histid['node_id'] = histid['inter_no'].map(inter2node)\n", + " histid = histid[['inter_no', 'node_id', 'start_unix', 'phas_A', 'phas_B', 'duration', 'inc_edge_A', 'out_edge_A', 'inc_edge_B', 'out_edge_B']]\n", + " histid_start = present_time - 600\n", + " histid = histid[histid.start_unix > histid_start]\n", + " return histid" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "def preprocess(m):\n", + " '''\n", + " 통합테이블(histid)를 만드는 함수\n", + "\n", + " input : m\n", + " - m ranges from 0 to 287, but 0 makes an error where 288 = 86400//300\n", + " - present_time = fmins[m] : 현재시점\n", + "\n", + " output : histid (통합테이블, HISTory with edge_IDs)\n", + " - 컬럼 : inter_no, node_id, start_unix, phas_A, phas_B, duration, inc_edge_A, out_edge_A, inc_edge_B, out_edge_B\n", + "\n", + " 주요 데이터, 중간산출물 및 결과물 :\n", + " # 데이터\n", + " - history : 신호이력 (inter_no, end_unix, dura_Aj, dura_Bj, cycle, offset)\n", + " - plan : 신호계획 (inter_no, start_hour, start_minute, dura_Aj, dura_Bj cycle, offset)\n", + " # 중간산출물\n", + " - rhists (recent history)\n", + " - history에서 현재 시각 이전의 데이터를 가져옴.\n", + " - end_unix를 start_unix로 변환\n", + " - 참값판단 프로세스(결측·이상치 처리)\n", + " - 컬럼 : inter_no, start_unix, dura_Aj, dura_Bj, cycle\n", + " - hrhists (hierarchized recent history)\n", + " - rhists를 계층화\n", + " - 컬럼 : inter_no, start_unix, phas_A, phas_B, duration\n", + " - movements\n", + " - 각 교차로에 대하여 현시별로 이동류를 정해놓음.\n", + " - join시 사용하기 위함.\n", + " - 한 번 만들어놓고 두고두고 사용함.\n", + " - 컬럼 : inter_no, phas_A, phas_B, move_A, move_B\n", + " - movement\n", + " - 현재 시점에서의 이동류정보\n", + " - 컬럼 : inter_no, phas_A, phas_B, move_A, move_B, start_unix\n", + " - movement_updated\n", + " - movement와 hrhists를 join하기 전에, movement에는 없지만 hrhists에는 있는 start_unix에 대한 이동류 정보를 가져와 movement에 붙임\n", + " - 이동류정보는 앞서 정의한 movements에서 가져옴.\n", + " - 컬럼 : inter_no, phas_A, phas_B, move_A, move_B, start_unix\n", + " - movedur\n", + " - hrhists와 movement_updated를 join\n", + " - 컬럼 : inter_no, phas_A, phas_B, move_A, move_B, start_unix, duration\n", + " # 결과 : histid\n", + " - 신호생성에 직접적으로 사용되는 데이터프레임\n", + " - 컬럼 : inter_no, node_id, start_unix, phas_A, phas_B, duration, inc_edge_A, out_edge_A, inc_edge_B, out_edge_B\n", + " - 한글컬럼 : 교차로번호, 노드id, 시작유닉스, A현시번호, B현시번호, 현시시간, 진입엣지(A), 진출엣지(A), 진입엣지(B), 진출엣지(B)\n", + " '''\n", + " midnight = int(datetime(2024, 1, 5, 0, 0, 0).timestamp())\n", + " next_day = int(datetime(2024, 1, 6, 0, 0, 0).timestamp())\n", + " fmins = range(midnight, next_day, 300) # fmins : unix time by Five MINuteS\n", + " # 현재시각\n", + " present_time = fmins[m]\n", + " print(datetime.fromtimestamp(present_time))\n", + " # 사용할 표준 테이블 목록\n", + " plan = pd.read_csv('../Data/tables/plan.csv', index_col=0)\n", + " history = pd.read_csv('../Data/tables/history.csv', index_col=0)\n", + " matching = pd.read_csv('../Intermediates/matching.csv', index_col=0)\n", + " # 참고할 딕셔너리, 데이터프레임, 리스트 등 목록\n", + " splits, isplits = make_splits(plan)\n", + " timetable = make_timetable(plan)\n", + " inter_node = pd.read_csv('../Data/tables/inter_node.csv', index_col=0)\n", + " inter_node = inter_node[inter_node.inter_type=='parent']\n", + " inter2node = dict(zip(inter_node['inter_no'], inter_node['node_id']))\n", + " hours = np.array(range(midnight - 7200, next_day + 1, 3600)) # 정각에 해당하는 시각들 목록\n", + " # rhistory, rhists, hrhists\n", + " adder = 600\n", + " rhistory = make_rhistory(plan, timetable, history, present_time, adder)\n", + " rhists = processing(plan, rhistory, timetable, hours)\n", + " hrhists = make_hrhists(rhists, isplits, timetable)\n", + " # movements, movement, movement_updated\n", + " movements = pd.read_csv('../Intermediates/movements.csv')\n", + " movement = pd.read_csv(f'../Intermediates/movement/movement_{present_time}.csv', index_col=0)\n", + " movement_updated = update_movement(hrhists, movement, movements)\n", + " # movedur\n", + " movedur = pd.merge(movement_updated, hrhists, how='inner', on=['inter_no', 'start_unix', 'phas_A', 'phas_B']) # movements and durations\n", + " movedur = movedur.sort_values(by=['start_unix', 'inter_no', 'phas_A','phas_B'])\n", + " movedur = movedur[['inter_no', 'start_unix', 'phas_A', 'phas_B', 'move_A', 'move_B', 'duration']]\n", + " # histid\n", + " histid = make_histid(present_time, hrhists, movement_updated, inter2node, matching)\n", + " histid.to_csv(f'../Intermediates/histid/histid_{fmins[m]}.csv')\n", + " return histid" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "2024-01-05 08:45:00\n", + "2024-01-05 08:50:00\n" + ] + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
inter_nonode_idstart_unixphas_Aphas_Bdurationinc_edge_Aout_edge_Ainc_edge_Bout_edge_B
655202i917044116101146571510152_02-571510152_01571510152_01571510152_01.65
656202i9170441161022114NaN-571510152_01NaNNaN
657175i017044116291140-571542797_02571500487_01-571500487_01571542797_02
658175i017044116292242-571500487_01571545870_01-571542797_02571510153_01
659175i017044116293329571545870_02571510153_01571545870_02571542797_02
.................................
871201i817044126405517571500583_01571500617_01571500583_01571500569_01
872206i717044126601125-571511538_02571542073_02571542073_01571511538_02
873206i717044126602225NaN571542073_02NaNNaN
874206i717044126603315-571511538_02571542073_02571542073_01571511538_02
875206i717044126604415NaN571542073_02NaNNaN
\n", + "

221 rows × 10 columns

\n", + "
" + ], + "text/plain": [ + " inter_no node_id start_unix phas_A phas_B duration inc_edge_A \\\n", + "655 202 i9 1704411610 1 1 46 571510152_02 \n", + "656 202 i9 1704411610 2 2 114 NaN \n", + "657 175 i0 1704411629 1 1 40 -571542797_02 \n", + "658 175 i0 1704411629 2 2 42 -571500487_01 \n", + "659 175 i0 1704411629 3 3 29 571545870_02 \n", + ".. ... ... ... ... ... ... ... \n", + "871 201 i8 1704412640 5 5 17 571500583_01 \n", + "872 206 i7 1704412660 1 1 25 -571511538_02 \n", + "873 206 i7 1704412660 2 2 25 NaN \n", + "874 206 i7 1704412660 3 3 15 -571511538_02 \n", + "875 206 i7 1704412660 4 4 15 NaN \n", + "\n", + " out_edge_A inc_edge_B out_edge_B \n", + "655 -571510152_01 571510152_01 571510152_01.65 \n", + "656 -571510152_01 NaN NaN \n", + "657 571500487_01 -571500487_01 571542797_02 \n", + "658 571545870_01 -571542797_02 571510153_01 \n", + "659 571510153_01 571545870_02 571542797_02 \n", + ".. ... ... ... \n", + "871 571500617_01 571500583_01 571500569_01 \n", + "872 571542073_02 571542073_01 571511538_02 \n", + "873 571542073_02 NaN NaN \n", + "874 571542073_02 571542073_01 571511538_02 \n", + "875 571542073_02 NaN NaN \n", + "\n", + "[221 rows x 10 columns]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "preprocess(105)\n", + "preprocess(106)" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "# for m in range(30, 288):\n", + "# print(m)\n", + "# histid = preprocess(m)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "rts", + "language": "python", + "name": "python3" + }, + "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 +} diff --git a/Script/preprocess_daily.ipynb b/Script/preprocess_daily.ipynb new file mode 100644 index 000000000..796421874 --- /dev/null +++ b/Script/preprocess_daily.ipynb @@ -0,0 +1,690 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import numpy as np\n", + "import os\n", + "import json\n", + "import sumolib\n", + "from tqdm import tqdm" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "def make_match1(path_move):\n", + " '''\n", + " 신호 DB에는 매 초마다 이동류정보가 업데이트 된다. 그리고 이 이동류정보를 매 5초마다 불러와서 사용하게 된다.\n", + " '../Data/tables/move/'에는 5초마다의 이동류정보가 저장되어 있다.\n", + "\n", + " return : 통합된 이동류정보\n", + " - 모든 inter_no(교차로번호)에 대한 A, B링 현시별 이동류정보\n", + "\n", + " match1을 만드는 데 시간이 소요되므로 한 번 만들어서 저장해두고 저장해둔 것을 쓴다.\n", + " '''\n", + " # [이동류번호] 불러오기 (약 1분의 소요시간)\n", + " csv_moves = os.listdir(path_move)\n", + " moves = [pd.read_csv(path_move + csv_move, index_col=0) for csv_move in tqdm(csv_moves)]\n", + " match1 = pd.concat(moves).drop_duplicates().sort_values(by=['inter_no','phas_A','phas_B']).reset_index(drop=True)\n", + " match1.to_csv('../Intermediates/match1.csv')\n", + " return match1" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "def make_match2(match1):\n", + " '''\n", + " match1을 계층화함.\n", + " - match1의 컬럼 : inter_no, phas_A, phas_B, move_A, move_B\n", + " - match2의 컬럼 : inter_no, phase_no, ring_type, move_no\n", + " '''\n", + " # 계층화 (inter_no, phas_A, phas_B, move_A, move_B) -> ('inter_no', 'phase_no', 'ring_type', 'move_no')\n", + " matchA = match1[['inter_no', 'phas_A', 'move_A']].copy()\n", + " matchA.columns = ['inter_no', 'phase_no', 'move_no']\n", + " matchA['ring_type'] = 'A'\n", + " matchB = match1[['inter_no', 'phas_B', 'move_B']].copy()\n", + " matchB.columns = ['inter_no', 'phase_no', 'move_no']\n", + " matchB['ring_type'] = 'B'\n", + " match2 = pd.concat([matchA, matchB]).drop_duplicates()\n", + " match2 = match2[['inter_no', 'phase_no', 'ring_type', 'move_no']]\n", + " match2 = match2.sort_values(by=list(match2.columns))\n", + " return match2" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "def make_match3(match2, nema):\n", + " '''\n", + " 각 movement들에 방향(진입방향, 진출방향)을 매칭시켜 추가함.\n", + " - match2의 컬럼 : inter_no, phase_no, ring_type, move_no\n", + " - match3의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir\n", + "\n", + " nema : \n", + " - 컬럼 : move_no, inc_dir, out_dir\n", + " - 모든 종류의 이동류번호에 대하여 진입방향과 진출방향을 매칭시키는 테이블\n", + " - 이동류번호 : 1 ~ 16, 17, 18, 21\n", + " - 진입, 진출방향(8방위) : 동, 서, 남, 북, 북동, 북서, 남동, 남서\n", + " '''\n", + " # nema 정보 불러오기 및 병합\n", + " match3 = pd.merge(match2, nema, how='left', on='move_no').drop_duplicates()\n", + " return match3" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "def make_match4(match3, angle):\n", + " '''\n", + " 방위각 정보를 매칭시켜 추가함.\n", + " - match3의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir\n", + " - match4의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle\n", + "\n", + " angle_original : \n", + " - 컬럼 : inter_no, angle_Aj, angle_Bj (j : 1 ~ 8)\n", + " - 모든 종류의 이동류번호에 대하여 진입방향과 진출방향을 매칭시키는 테이블\n", + " - 이동류번호 : 1 ~ 16, 17, 18, 21\n", + " - 진입, 진출방향(8방위) : 동, 서, 남, 북, 북동, 북서, 남동, 남서\n", + " '''\n", + "\n", + " # 계층화\n", + " angles = []\n", + " for i, row in angle.iterrows():\n", + " angle_codes = row[[f'angle_{alph}{j}' for alph in ['A', 'B'] for j in range(1,9)]]\n", + " 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()})\n", + " angles.append(new)\n", + " angles = pd.concat(angles)\n", + " angles = angles.dropna().reset_index(drop=True)\n", + "\n", + " # 병합\n", + " six_chars = angles.angle_code.apply(lambda x:len(x)==6)\n", + " angles.loc[six_chars,'inc_angle'] = angles.angle_code.apply(lambda x:x[:3])\n", + " angles.loc[six_chars,'out_angle'] = angles.angle_code.apply(lambda x:x[3:])\n", + " angles = angles.drop('angle_code', axis=1)\n", + " match4 = pd.merge(match3, angles, how='left', left_on=['inter_no', 'phase_no', 'ring_type'],\n", + " right_on=['inter_no', 'phase_no', 'ring_type']).drop_duplicates()\n", + " return match4" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "def make_match5(match4, net, inter_node, inter_info):\n", + " '''\n", + " 진입엣지id, 진출엣지id, 노드id를 추가함 (주교차로).\n", + " - match4의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle\n", + " - match5의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle, inc_edge, out_edge, node_id\n", + " \n", + " 사용된 데이터 : \n", + " (1) net\n", + " - 성남시 정자동 부근의 샘플 네트워크\n", + " (2) inter_node\n", + " - 교차로번호와 노드id를 매칭시키는 테이블.\n", + " - parent/child 정보도 포함되어 있음\n", + " - 컬럼 : inter_no, node_id, inter_type\n", + " (3) inter_info\n", + " - 교차로 정보. 여기에서는 위도와 경도가 쓰임.\n", + " - 컬럼 : inter_no, inter_name, inter_lat, inter_lon, group_no, main_phase_no\n", + "\n", + " 진입엣지id, 진출엣지id를 얻는 과정 :\n", + " - match5 = match4.copy()의 각 열을 순회하면서 아래 과정을 반복함.\n", + " * 진입에 대해서만 서술하겠지만 진출도 마찬가지로 설명될 수 있음\n", + " - 해당 행의 교차로정보로부터 노드ID를 얻어내고, 해당 노드에 대한 모든 진출엣지id를 inc_edges에 저장.\n", + " * inc_edge(진입엣지) : incoming edge, out_edge(진출엣지) : outgoing_edge\n", + " - inc_edges의 모든 진입엣지에 대하여 진입방향(inc_dires, 2차원 단위벡터)을 얻어냄.\n", + " - 해당 행의 진입각으로부터 그에 대응되는 진입각방향(단위벡터)를 얻어냄.\n", + " - 주어진 진입각방향에 대하여 내적이 가장 작은 진입방향에 대한 진입엣지를 inc_edge_id로 지정함.\n", + " '''\n", + "\n", + " # parent node만 가져옴.\n", + " inter_node1 = inter_node[inter_node.inter_type == 'parent'].drop('inter_type', axis=1)\n", + " inter_info1 = inter_info[['inter_no', 'inter_lat', 'inter_lon']]\n", + " inter = pd.merge(inter_node1, inter_info1, how='left', left_on=['inter_no'],\n", + " right_on=['inter_no']).drop_duplicates()\n", + "\n", + " inter2node = dict(zip(inter['inter_no'], inter['node_id']))\n", + "\n", + " match5 = match4.copy()\n", + " # 진입진출ID 매칭\n", + " for index, row in match5.iterrows():\n", + " node_id = inter2node[row.inter_no]\n", + " node = net.getNode(node_id)\n", + " # 교차로의 모든 (from / to) edges\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", + " # 교차로의 모든 (from / to) directions\n", + " inc_dirs = []\n", + " for inc_edge in inc_edges:\n", + " start = inc_edge.getShape()[-2]\n", + " end = inc_edge.getShape()[-1]\n", + " inc_dir = np.array(end) - np.array(start)\n", + " inc_dir = inc_dir / (inc_dir ** 2).sum() ** 0.5\n", + " inc_dirs.append(inc_dir)\n", + " out_dirs = []\n", + " for out_edge in out_edges:\n", + " start = out_edge.getShape()[0]\n", + " end = out_edge.getShape()[1]\n", + " out_dir = np.array(end) - np.array(start)\n", + " out_dir = out_dir / (out_dir ** 2).sum() ** 0.5\n", + " out_dirs.append(out_dir)\n", + " # 진입각, 진출각 불러오기\n", + " if not pd.isna(row.inc_angle):\n", + " inc_angle = int(row.inc_angle)\n", + " out_angle = int(row.out_angle)\n", + " # 방위각을 일반각으로 가공, 라디안 변환, 단위벡터로 변환\n", + " inc_angle = (-90 - inc_angle) % 360\n", + " inc_angle = inc_angle * np.pi / 180.\n", + " inc_dir_true = np.array([np.cos(inc_angle), np.sin(inc_angle)])\n", + " out_angle = (90 - out_angle) % 360\n", + " out_angle = out_angle * np.pi / 180.\n", + " out_dir_true = np.array([np.cos(out_angle), np.sin(out_angle)])\n", + " # 매칭 엣지 반환\n", + " inc_index = np.array([np.dot(inc_dir, inc_dir_true) for inc_dir in inc_dirs]).argmax()\n", + " out_index = np.array([np.dot(out_dir, out_dir_true) for out_dir in out_dirs]).argmax()\n", + " inc_edge_id = inc_edges[inc_index].getID()\n", + " out_edge_id = out_edges[out_index].getID()\n", + " match5.at[index, 'inc_edge'] = inc_edge_id\n", + " match5.at[index, 'out_edge'] = out_edge_id\n", + " match5['node_id'] = match5['inter_no'].map(inter2node)\n", + " match5 = match5.sort_values(by=['inter_no','phase_no','ring_type']).reset_index(drop=True)\n", + " return match5" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "def make_match6(match5, inter_node, uturn, coord):\n", + " '''\n", + " 진입엣지id, 진출엣지id, 노드id를 추가함 (부교차로).\n", + " - match6의 컬럼 : inter_no, phase_no, ring_type, move_no, inc_dir, out_dir, inc_angle, out_angle, inc_edge, out_edge, node_id\n", + " \n", + " 사용된 데이터 : \n", + " (1) inter_node\n", + " - 교차로번호와 노드id를 매칭시키는 테이블.\n", + " - parent/child 정보도 포함되어 있음\n", + " - 컬럼 : inter_no, node_id, inter_type\n", + " (2) uturn (유턴정보)\n", + " - 컬럼 : parent_id, child_id, direction, condition, inc_edge, out_edge\n", + " - parent_id, child_id : 주교차로id, 유턴교차로id\n", + " - direction : 주교차로에 대한 유턴노드의 상대적인 위치(방향)\n", + " - condition : 좌회전시, 직진시, 직좌시, 보행신호시 중 하나\n", + " - inc_edge, out_edge : 유턴에 대한 진입진출엣지\n", + " (3) coord (연동교차로정보)\n", + " - 컬럼 : parent_id, child_id, phase_no, ring_type, inc_edge, out_edge\n", + " - parent_id, child_id : 주교차로id, 연동교차로id\n", + " - 나머지 컬럼 : 각 (현시, 링)별 진입진출엣지\n", + "\n", + " 설명 :\n", + " - match5는 주교차로에 대해서만 진입엣지id, 진출엣지id, 노드id를 추가했었음.\n", + " 여기에서 uturn, coord를 사용해서 부교차로들(유턴교차로, 연동교차로)에 대해서도 해당 값들을 부여함.\n", + " 유턴교차로 :\n", + " - directions를 정북기준 시계방향의 8방위로 정함.\n", + " - 이를 통해 진입방향이 주어진 경우에 좌회전, 직진, 보행 등에 대한 (진입방향, 진출방향)을 얻어낼 수 있음.\n", + " - 예) 진입방향(direction)이 '북'일 때, \n", + " - 직진 : (북, 남)\n", + " * 남 : directions[(ind + 4) % len(directions)]\n", + " - 좌회전 : (북, 동)\n", + " * 동 : directions[(ind + 2) % len(directions)]\n", + " - 보행 : (서, 동)\n", + " * 서 : directions[(ind - 2) % len(directions)]\n", + " - uturn의 각 행을 순회하면서 아래 과정을 반복함\n", + " - match5에서 parent_id에 해당하는 행들을 가져옴(cmatch).\n", + " - condition 별로 진입방향, 진출방향A, 진출방향B 정함.\n", + " - 상술한 directions를 활용하여 정함.\n", + " - (진입방향, 진출방향A, 진출방향B)을 고려하여 (현시, 링) 별로 진입엣지id, 진출엣지id를 정함.\n", + " - ex) cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", + " - 순회하면서 만든 cmatch를 cmatchs라는 리스트에 저장함.\n", + "\n", + " 연동교차로 :\n", + " - 연동교차로의 경우 coord에 (현시, 링)별 진입엣지ID, 진출엣지ID가 명시되어 있음.\n", + " - 'inc_dir', 'out_dir', 'inc_angle','out_angle'와 같은 열들은 np.nan을 지정해놓음.\n", + " - 이 열들은, 사실상 다음 스텝부터는 사용되지 않는 열들이기 때문에 np.nan으로 지정해놓아도 문제없음.\n", + "\n", + " match6 :\n", + " - 이렇게 얻은 match5, cmatchs, coord를 모두 pd.concat하여 match6을 얻어냄.\n", + " '''\n", + "\n", + " node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no']))\n", + "\n", + " child_ids = inter_node[inter_node.inter_type=='child'].node_id.unique()\n", + " ch2pa = {} # child to parent\n", + " for child_id in child_ids:\n", + " parent_no = inter_node[inter_node.node_id==child_id].inter_no.iloc[0]\n", + " sub_inter_node = inter_node[inter_node.inter_no==parent_no]\n", + " ch2pa[child_id] = sub_inter_node[sub_inter_node.inter_type=='parent'].iloc[0].node_id\n", + " directions = ['북', '북동', '동', '남동', '남', '남서', '서', '북서'] # 정북기준 시계방향으로 8방향\n", + "\n", + " # 각 uturn node에 대하여 (inc_edge_id, out_edge_id) 부여\n", + " cmatches = []\n", + " for _, row in uturn.iterrows():\n", + " child_id = row.child_id\n", + " parent_id = row.parent_id\n", + " direction = row.direction\n", + " condition = row.condition\n", + " inc_edge_id = row.inc_edge\n", + " out_edge_id = row.out_edge\n", + " # match5에서 parent_id에 해당하는 행들을 가져옴\n", + " cmatch = match5.copy()[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", + " cmatch[['inc_edge', 'out_edge']] = np.nan\n", + "\n", + " # condition 별로 inc_dire, out_dire_A, out_dire_B를 정함\n", + " ind = directions.index(direction)\n", + " if condition == \"좌회전시\":\n", + " inc_dire = direction\n", + " out_dire_A = out_dire_B = directions[(ind + 2) % len(directions)]\n", + " elif condition == \"직진시\":\n", + " inc_dire = direction\n", + " out_dire_A = out_dire_B = directions[(ind + 4) % len(directions)]\n", + " elif condition == \"보행신호시\":\n", + " inc_dire = directions[(ind + 2) % len(directions)]\n", + " out_dire_A = directions[(ind - 2) % len(directions)]\n", + " out_dire_B = directions[(ind - 2) % len(directions)]\n", + "\n", + " # (inc_dire, out_dire_A, out_dire_B) 별로 inc_edge_id, out_edge_id를 정함\n", + " if condition == '보행신호시':\n", + " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", + " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", + " # 이동류번호가 17(보행신호)이면서 유턴노드방향으로 가는 신호가 없으면 (inc_edge_id, out_edge_id)를 부여한다.\n", + " cmatch.loc[(cmatch.move_no==17) & (cmatch.out_dir!=direction), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", + " else: # '직진시', '좌회전시'\n", + " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", + " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id]\n", + " # 유턴신호의 이동류번호를 19로 부여한다.\n", + " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), 'move_no'] = 19\n", + " cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), 'move_no'] = 19\n", + " cmatches.append(cmatch)\n", + "\n", + " # 각 coordination node에 대하여 (inc_edge_id, out_edge_id) 부여\n", + " coord['inter_no'] = coord['parent_id'].map(node2inter)\n", + " coord = coord.rename(columns={'child_id':'node_id'})\n", + " coord[['inc_dir', 'out_dir', 'inc_angle','out_angle']] = np.nan\n", + " coord['move_no'] = 20\n", + " coord = coord[['inter_no', 'phase_no', 'ring_type', 'move_no', 'inc_dir', 'out_dir', 'inc_angle','out_angle', 'inc_edge', 'out_edge', 'node_id']]\n", + " \n", + " # display(coord)\n", + " cmatches = pd.concat(cmatches)\n", + " match6 = pd.concat([match5, cmatches, coord]).drop_duplicates().sort_values(by=['inter_no', 'node_id', 'phase_no', 'ring_type'])\n", + " match6.to_csv('../Intermediates/match6.csv')\n", + " return match6" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "def make_matching(match6, inter_node, nema):\n", + " '''\n", + " 이동류 매칭 : 각 교차로에 대하여, 가능한 모든 이동류 (1~18, 21)에 대한 진입·진출엣지ID를 지정한다.\n", + " 모든 이동류에 대해 지정하므로, 시차제시 이전과 다른 이동류가 등장하더라도 항상 진입·진출 엣지 ID를 지정할 수 있다. \n", + " - matching의 컬럼 : inter_no, move_no, inc_dir, out_dir, inc_edge, out_edge, node_id\n", + " \n", + " 설명 : \n", + " - 필요한 리스트, 딕셔너리 등을 정의\n", + " (1) 가능한 (진입방향, 진출방향) 목록 [리스트]\n", + " (2) 각 교차로별 방향 목록 : pdires (possible directions) [딕셔너리]\n", + " (3) 각 (교차로, 진입방향) 별 진입id 목록 : inc2id (incoming direction to incoming edge_id) [딕셔너리]\n", + " (4) 각 (교차로, 진출방향) 별 진출id 목록 : out2id (outgoing direction to outgoing edge_id) [딕셔너리]\n", + " (5) 각 교차로별 가능한 (진입방향, 진출방향) 목록 : pflow (possible flows) [딕셔너리]\n", + " - matching은 빈 리스트로 지정.\n", + " - 모든 노드id에 대하여 다음 과정을 반복\n", + " - 해당 노드id에 대한 모든 가능한 (진입방향, 진출방향)에 대하여 다음 과정을 반복\n", + " - (노드id, 진입방향)으로부터 진입엣지id를 얻어냄. 마찬가지로 진출엣지id도 얻어냄\n", + " - 얻어낸 정보를 바탕으로 한 행(new_row)을 만들고 이것을 matching에 append\n", + " '''\n", + "\n", + " match7 = match6.copy()\n", + " match7 = match7[['inter_no', 'move_no', 'inc_dir', 'out_dir', 'inc_edge', 'out_edge', 'node_id']]\n", + "\n", + " parent_ids = sorted(inter_node[inter_node.inter_type=='parent'].node_id.unique())\n", + " child_ids = sorted(inter_node[inter_node.inter_type=='child'].node_id.unique())\n", + "\n", + " # (1) 가능한 (진입방향, 진출방향) 목록 \n", + " flows = nema.dropna().apply(lambda row: (row['inc_dir'], row['out_dir']), axis=1).tolist()\n", + " # (2) 각 교차로별 방향 목록 : pdires (possible directions)\n", + " pdires = {}\n", + " for node_id in parent_ids:\n", + " dires = match7[match7.node_id == node_id][['inc_dir','out_dir']].values.flatten()\n", + " dires = {dire for dire in dires if type(dire)==str}\n", + " pdires[node_id] = dires\n", + " # (3) 각 (교차로, 진입방향) 별 진입id 목록 : inc2id (incoming direction to incoming edge_id)\n", + " inc2id = {}\n", + " for node_id in parent_ids:\n", + " for inc_dir in pdires[node_id]:\n", + " df = match7[(match7.node_id==node_id) & (match7.inc_dir==inc_dir)]\n", + " inc2id[(node_id, inc_dir)] = df.inc_edge.iloc[0]\n", + " # (4) 각 (교차로, 진출방향) 별 진출id 목록 : out2id (outgoing direction to outgoing edge_id)\n", + " out2id = {}\n", + " for node_id in parent_ids:\n", + " for out_dir in pdires[node_id]:\n", + " df = match7[(match7.node_id==node_id) & (match7.out_dir==out_dir)]\n", + " out2id[(node_id, out_dir)] = df.out_edge.iloc[0]\n", + " # (5) 각 교차로별 가능한 (진입방향, 진출방향) 목록 : pflow (possible flows)\n", + " pflow = {}\n", + " for node_id in parent_ids:\n", + " pflow[node_id] = [flow for flow in flows if set(flow).issubset(pdires[node_id])]\n", + " # (6) 가능한 이동류에 대하여 진입id, 진출id 배정 : matching\n", + " node2inter = dict(zip(match7['node_id'], match7['inter_no']))\n", + " dires_right = ['북', '서', '남', '동', '북'] # ex (북, 서), (서, 남) 등은 우회전 flow\n", + " matching = []\n", + " for node_id in parent_ids:\n", + " inter_no = node2inter[node_id]\n", + " # 좌회전과 직진(1 ~ 16)\n", + " for (inc_dir, out_dir) in pflow[node_id]:\n", + " move_no = nema[(nema.inc_dir==inc_dir) & (nema.out_dir==out_dir)].move_no.iloc[0]\n", + " inc_edge = inc2id[(node_id, inc_dir)]\n", + " out_edge = out2id[(node_id, out_dir)]\n", + " new_row = pd.DataFrame({'inter_no':[inter_no], 'move_no':[move_no],\n", + " 'inc_dir':[inc_dir], 'out_dir':[out_dir],\n", + " 'inc_edge':[inc_edge], 'out_edge':[out_edge], 'node_id':[node_id]})\n", + " matching.append(new_row)\n", + " # 보행신호(17), 전적색(18)\n", + " new_row = pd.DataFrame({'inter_no':[inter_no] * 2, 'move_no':[17, 18],\n", + " 'inc_dir':[None]*2, 'out_dir':[None]*2,\n", + " 'inc_edge':[None]*2, 'out_edge':[None]*2, 'node_id':[node_id]*2})\n", + " matching.append(new_row)\n", + " # 신호우회전(21)\n", + " for d in range(len(dires_right)-1):\n", + " inc_dir = dires_right[d]\n", + " out_dir = dires_right[d+1]\n", + " if {inc_dir, out_dir}.issubset(pdires[node_id]):\n", + " inc_edge = inc2id[(node_id, inc_dir)]\n", + " out_edge = out2id[(node_id, out_dir)]\n", + " new_row = pd.DataFrame({'inter_no':[inter_no], 'move_no':[21],\n", + " 'inc_dir':[inc_dir], 'out_dir':[out_dir],\n", + " 'inc_edge':[inc_edge], 'out_edge':[out_edge], 'node_id':[node_id]})\n", + " matching.append(new_row)\n", + " matching.append(match7[match7.node_id.isin(child_ids)])\n", + " matching = pd.concat(matching)\n", + " matching = matching.dropna().sort_values(by=['inter_no', 'node_id', 'move_no']).reset_index(drop=True)\n", + " matching['move_no'] = matching['move_no'].astype(int)\n", + " matching.to_csv('../Intermediates/matching.csv')\n", + " return matching" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "def make_movements():\n", + " movements_path = '../Intermediates/movement/'\n", + " movements_list = [pd.read_csv(movements_path + file, index_col=0) for file in tqdm(os.listdir(movements_path))]\n", + " movements = pd.concat(movements_list)\n", + " movements = movements.drop(columns=['start_unix'])\n", + " movements = movements.drop_duplicates()\n", + " movements = movements.sort_values(by=['inter_no', 'phas_A', 'phas_B'])\n", + " movements = movements.reset_index(drop=True)\n", + " movements.to_csv('../Intermediates/movements.csv')\n", + " return movements" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "# node2num_cycles : A dictionary that maps a node_id to the number of cycles\n", + "def get_node2num_cycles(plan, inter_node):\n", + " node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no']))\n", + " node_ids = sorted(inter_node.node_id.unique())\n", + "\n", + " Aplan = plan.copy()[['inter_no'] + [f'dura_A{j}' for j in range(1,9)] + ['cycle']]\n", + " grouped = Aplan.groupby('inter_no')\n", + " df = grouped.agg({'cycle': 'min'}).reset_index()\n", + " df = df.rename(columns={'cycle': 'min_cycle'})\n", + " df['num_cycle'] = 300 // df['min_cycle'] + 2\n", + " inter2num_cycles = dict(zip(df['inter_no'], df['num_cycle']))\n", + " node2numcycles = {node_id : inter2num_cycles[node2inter[node_id]] for node_id in node_ids}\n", + " with open('../Intermediates/node2numcycles.json', 'w') as file:\n", + " json.dump(node2numcycles, file, indent=4)\n", + " return node2numcycles" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "def preprocess_daily():\n", + " path_move = '../Data/tables/move/'\n", + "\n", + " inter_info = pd.read_csv('../Data/tables/inter_info.csv', index_col=0)\n", + " angle = pd.read_csv('../Data/tables/angle.csv', index_col=0, dtype = {f'angle_{alph}{j}':'str' for alph in ['A', 'B'] for j in range(1,9)})\n", + " plan = pd.read_csv('../Data/tables/plan.csv', index_col=0)\n", + " inter_node = pd.read_csv('../Data/tables/inter_node.csv', index_col=0)\n", + " uturn = pd.read_csv('../Data/tables/child_uturn.csv')\n", + " coord = pd.read_csv('../Data/tables/child_coord.csv')\n", + " nema = pd.read_csv('../Data/tables/nema.csv', encoding='cp949')\n", + "\n", + " net = sumolib.net.readNet('../Data/networks/sn.net.xml')\n", + "\n", + " match1 = make_match1(path_move)\n", + " match2 = make_match2(match1)\n", + " match3 = make_match3(match2, nema)\n", + " match4 = make_match4(match3, angle)\n", + " match5 = make_match5(match4, net, inter_node, inter_info)\n", + " match6 = make_match6(match5, inter_node, uturn, coord)\n", + " matching = make_matching(match6, inter_node, nema)\n", + " movements = make_movements()\n", + " node2num_cycles = get_node2num_cycles(plan, inter_node)\n", + " return match6, matching, movements, node2num_cycles" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + " 0%| | 0/17280 [00:00= 0) & (durations <= 100)).all(axis=1)\n", + "sorted(plan[~valid_indices].inter_no.unique())\n", + "# durations" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "rts", + "language": "python", + "name": "python3" + }, + "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 +} diff --git a/Script/preprocess_daily.py b/Script/preprocess_daily.py new file mode 100644 index 000000000..f0de51881 --- /dev/null +++ b/Script/preprocess_daily.py @@ -0,0 +1,431 @@ +import pandas as pd +import numpy as np +import os +import json +import sumolib +from tqdm import tqdm + +def check_inter_info(inter_info): + print(inter_info) + print('check') + +def make_match1(path_root): + ''' + 신호 DB에는 매 초마다 이동류정보가 업데이트 된다. 그리고 이 이동류정보를 매 5초마다 불러와서 사용하게 된다. + '../Data/tables/move/'에는 5초마다의 이동류정보가 저장되어 있다. + + return : 통합된 이동류정보 + - 모든 inter_no(교차로번호)에 대한 A, B링 현시별 이동류정보 + + match1을 만드는 데 시간이 소요되므로 한 번 만들어서 저장해두고 저장해둔 것을 쓴다. + ''' + # [이동류번호] 불러오기 (약 1분의 소요시간) + path_move = os.path.join(path_root, 'Data', '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='이동류정보 불러오는 중 : match1')] + match1 = pd.concat(moves).drop_duplicates().sort_values(by=['inter_no','phas_A','phas_B']).reset_index(drop=True) + match1.to_csv(os.path.join(path_root, 'Intermediates', 'match1.csv')) + return match1 + +def make_match2(match1): + ''' + 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 = match1[['inter_no', 'phas_A', 'move_A']].copy() + matchA.columns = ['inter_no', 'phase_no', 'move_no'] + matchA['ring_type'] = 'A' + matchB = match1[['inter_no', 'phas_B', 'move_B']].copy() + matchB.columns = ['inter_no', 'phase_no', 'move_no'] + matchB['ring_type'] = 'B' + match2 = pd.concat([matchA, matchB]).drop_duplicates() + match2 = match2[['inter_no', 'phase_no', 'ring_type', 'move_no']] + match2 = match2.sort_values(by=list(match2.columns)) + return match2 + +def make_match3(match2, nema): + ''' + 각 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 정보 불러오기 및 병합 + match3 = pd.merge(match2, nema, how='left', on='move_no').drop_duplicates() + return match3 + +def make_match4(match3, angle): + ''' + 방위각 정보를 매칭시켜 추가함. + - 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 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) + match4 = pd.merge(match3, angles, how='left', left_on=['inter_no', 'phase_no', 'ring_type'], + right_on=['inter_no', 'phase_no', 'ring_type']).drop_duplicates() + return match4 + +def make_match5(match4, net, inter_node, inter_info): + ''' + 진입엣지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 = inter_node[inter_node.inter_type == 'parent'].drop('inter_type', axis=1) + inter_info1 = 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() + + inter2node = dict(zip(inter['inter_no'], inter['node_id'])) + + match5 = match4.copy() + # 진입진출ID 매칭 + for index, row in match5.iterrows(): + node_id = inter2node[row.inter_no] + node = net.getNode(node_id) + # 교차로의 모든 (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) + out_dirs = [] + 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) + # 진입각, 진출각 불러오기 + 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() + match5.at[index, 'inc_edge'] = inc_edge_id + match5.at[index, 'out_edge'] = out_edge_id + match5['node_id'] = match5['inter_no'].map(inter2node) + match5 = match5.sort_values(by=['inter_no','phase_no','ring_type']).reset_index(drop=True) + return match5 + +def make_match6(match5, inter_node, uturn, coord, path_root): + ''' + 진입엣지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 별로 진입방향, 진출방향A, 진출방향B 정함. + - 상술한 directions를 활용하여 정함. + - (진입방향, 진출방향A, 진출방향B)을 고려하여 (현시, 링) 별로 진입엣지id, 진출엣지id를 정함. + - ex) cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['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을 얻어냄. + ''' + + node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no'])) + + child_ids = inter_node[inter_node.inter_type=='child'].node_id.unique() + ch2pa = {} # child to parent + for child_id in child_ids: + parent_no = inter_node[inter_node.node_id==child_id].inter_no.iloc[0] + sub_inter_node = inter_node[inter_node.inter_no==parent_no] + ch2pa[child_id] = sub_inter_node[sub_inter_node.inter_type=='parent'].iloc[0].node_id + directions = ['북', '북동', '동', '남동', '남', '남서', '서', '북서'] # 정북기준 시계방향으로 8방향 + + # 각 uturn node에 대하여 (inc_edge_id, out_edge_id) 부여 + cmatches = [] + for _, row in 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에서 parent_id에 해당하는 행들을 가져옴 + cmatch = match5.copy()[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 + + # condition 별로 inc_dire, out_dire_A, out_dire_B를 정함 + ind = directions.index(direction) + if condition == "좌회전시": + inc_dire = direction + out_dire_A = out_dire_B = directions[(ind + 2) % len(directions)] + elif condition == "직진시": + inc_dire = direction + out_dire_A = out_dire_B = directions[(ind + 4) % len(directions)] + elif condition == "보행신호시": + inc_dire = directions[(ind + 2) % len(directions)] + out_dire_A = directions[(ind - 2) % len(directions)] + out_dire_B = directions[(ind - 2) % len(directions)] + + # (inc_dire, out_dire_A, out_dire_B) 별로 inc_edge_id, out_edge_id를 정함 + if condition == '보행신호시': + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + # 이동류번호가 17(보행신호)이면서 유턴노드방향으로 가는 신호가 없으면 (inc_edge_id, out_edge_id)를 부여한다. + cmatch.loc[(cmatch.move_no==17) & (cmatch.out_dir!=direction), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + else: # '직진시', '좌회전시' + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + # 유턴신호의 이동류번호를 19로 부여한다. + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), 'move_no'] = 19 + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), 'move_no'] = 19 + cmatches.append(cmatch) + + # 각 coordination node에 대하여 (inc_edge_id, out_edge_id) 부여 + coord['inter_no'] = coord['parent_id'].map(node2inter) + coord = coord.rename(columns={'child_id':'node_id'}) + coord[['inc_dir', 'out_dir', 'inc_angle','out_angle']] = np.nan + coord['move_no'] = 20 + coord = 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) + match6 = pd.concat([match5, cmatches, coord]).drop_duplicates().sort_values(by=['inter_no', 'node_id', 'phase_no', 'ring_type']) + match6.to_csv(os.path.join(path_root, 'Intermediates', 'match6.csv')) + return match6 + +def make_matching(match6, inter_node, nema, path_root): + ''' + 이동류 매칭 : 각 교차로에 대하여, 가능한 모든 이동류 (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 + ''' + + match7 = match6.copy() + match7 = match7[['inter_no', 'move_no', 'inc_dir', 'out_dir', 'inc_edge', 'out_edge', 'node_id']] + + parent_ids = sorted(inter_node[inter_node.inter_type=='parent'].node_id.unique()) + child_ids = sorted(inter_node[inter_node.inter_type=='child'].node_id.unique()) + + # (1) 가능한 (진입방향, 진출방향) 목록 + flows = nema.dropna().apply(lambda row: (row['inc_dir'], row['out_dir']), axis=1).tolist() + # (2) 각 교차로별 방향 목록 : pdires (possible directions) + pdires = {} + for node_id in parent_ids: + dires = match7[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 parent_ids: + for inc_dir in pdires[node_id]: + df = match7[(match7.node_id==node_id) & (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 parent_ids: + for out_dir in pdires[node_id]: + df = match7[(match7.node_id==node_id) & (match7.out_dir==out_dir)] + out2id[(node_id, out_dir)] = df.out_edge.iloc[0] + # (5) 각 교차로별 가능한 (진입방향, 진출방향) 목록 : pflow (possible flows) + pflow = {} + for node_id in parent_ids: + pflow[node_id] = [flow for flow in flows if set(flow).issubset(pdires[node_id])] + # (6) 가능한 이동류에 대하여 진입id, 진출id 배정 : matching + node2inter = dict(zip(match7['node_id'], match7['inter_no'])) + dires_right = ['북', '서', '남', '동', '북'] # ex (북, 서), (서, 남) 등은 우회전 flow + matching = [] + for node_id in parent_ids: + inter_no = node2inter[node_id] + # 좌회전과 직진(1 ~ 16) + for (inc_dir, out_dir) in pflow[node_id]: + move_no = nema[(nema.inc_dir==inc_dir) & (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]}) + 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}) + 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]}) + matching.append(new_row) + matching.append(match7[match7.node_id.isin(child_ids)]) + matching = pd.concat(matching) + matching = matching.dropna().sort_values(by=['inter_no', 'node_id', 'move_no']).reset_index(drop=True) + matching['move_no'] = matching['move_no'].astype(int) + matching.to_csv(os.path.join(path_root, 'Intermediates', 'matching.csv')) + return matching + +def make_movements(path_root): + movements_path = os.path.join(path_root, 'Intermediates', 'movement') + movements_list = [pd.read_csv(os.path.join(movements_path, file), index_col=0) for file in tqdm(os.listdir(movements_path), desc='이동류정보 불러오는 중 : movements')] + movements = pd.concat(movements_list) + movements = movements.drop(columns=['start_unix']) + movements = movements.drop_duplicates() + movements = movements.sort_values(by=['inter_no', 'phas_A', 'phas_B']) + movements = movements.reset_index(drop=True) + movements.to_csv(os.path.join(path_root, 'Intermediates', 'movements.csv')) + return movements + +# node2num_cycles : A dictionary that maps a node_id to the number of cycles +def get_node2num_cycles(plan, inter_node, path_root): + node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no'])) + node_ids = sorted(inter_node.node_id.unique()) + + Aplan = 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'])) + node2numcycles = {node_id : inter2num_cycles[node2inter[node_id]] for node_id in node_ids} + with open(os.path.join('Intermediates','node2numcycles.json'), 'w') as file: + json.dump(node2numcycles, file, indent=4) + return node2numcycles + +def main(): + path_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + + inter_info = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'inter_info.csv')) + check_inter_info(inter_info) + angle = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'angle.csv'), dtype = {f'angle_{alph}{j}':'str' for alph in ['A', 'B'] for j in range(1,9)}) + plan = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'plan.csv')) + inter_node = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'inter_node.csv')) + uturn = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'child_uturn.csv')) + coord = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'child_coord.csv')) + nema = pd.read_csv(os.path.join(path_root, 'Data', 'tables', 'nema.csv'), encoding='cp949') + + net = sumolib.net.readNet(os.path.join(path_root, 'Data', 'networks', 'sn.net.xml')) + + match1 = make_match1(path_root) + match2 = make_match2(match1) + match3 = make_match3(match2, nema) + match4 = make_match4(match3, angle) + match5 = make_match5(match4, net, inter_node, inter_info) + match6 = make_match6(match5, inter_node, uturn, coord, path_root) + matching = make_matching(match6, inter_node, nema, path_root) + movements = make_movements(path_root) + node2num_cycles = get_node2num_cycles(plan, inter_node, path_root) + +if __name__ == '__main__': + main() diff --git a/Script/preprocess_daily_1.py b/Script/preprocess_daily_1.py new file mode 100644 index 000000000..23f80587a --- /dev/null +++ b/Script/preprocess_daily_1.py @@ -0,0 +1,587 @@ +import pandas as pd +import numpy as np +import os, sys, traci +import json +import sumolib +from tqdm import tqdm + +class DailyPreprocessor(): + def __init__(self): + self.path_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + self.issues = [] + + # 1. 데이터 불러오기 + def load_data(self): + self.load_networks() + self.load_tables() + self.check_networks() + self.check_tables() + print('1. 모든 데이터가 로드되었습니다.') + + # 1-1. 네트워크 불러오기 + def load_networks(self): + self.net = sumolib.net.readNet(os.path.join(self.path_root, 'Data', '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.path_table = os.path.join(self.path_root, 'Data', 'tables') + + self.inter_info = pd.read_csv(os.path.join(self.path_table, 'inter_info.csv'), dtype=loading_dtype) + self.angle = pd.read_csv(os.path.join(self.path_table, 'angle.csv'), dtype=loading_dtype) + self.plan = pd.read_csv(os.path.join(self.path_table, 'plan.csv'), dtype=loading_dtype) + self.inter_node = pd.read_csv(os.path.join(self.path_table, 'inter_node.csv'), dtype=loading_dtype) + self.uturn = pd.read_csv(os.path.join(self.path_table, 'child_uturn.csv'), dtype=loading_dtype) + self.coord = pd.read_csv(os.path.join(self.path_table, 'child_coord.csv'), dtype=loading_dtype) + self.nema = pd.read_csv(os.path.join(self.path_table, 'nema.csv'), encoding='cp949', dtype=loading_dtype) + 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_root, 'Data', '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_inter_info() + self.check_angle() + self.check_plan() + print("1-4. 모든 테이블들의 무결성을 검사했고 이상 없습니다.") + pass + + # 1-4-1. 교차로정보(inter_info) 검사 + def check_inter_info(self): + # 1-4-1-1. inter_lat, inter_lon 적절성 검사 + self.inter_info.loc[0, 'inter_lat'] = 38.0 # 에러 발생을 위한 코드 + self.max_lon, self.min_lon = 127.207888, 127.012492 + self.max_lat, self.min_lat = 37.480693, 37.337112 + 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-1-1. 위도 또는 경도가 범위를 벗어난 교차로가 있습니다: inter_no : {row['inter_no']}" + self.issues.append(msg) + # 교차로목록 정의 + self.inter_nos = sorted(self.inter_info.inter_no.unique()) + + # 1-4-2. 방위각정보(inter_info) 검사 + def check_angle(self): + # 1-4-2-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-2-1. angle의 inter_no 중 교차로 목록(inter_nos)에 포함되지 않는 항목이 있습니다: {missing_inter_nos}" + self.issues.append(msg) + + # 1-4-3. 신호계획(plan) 검사 + def check_plan(self): + # 1-4-3-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-3-1. plan의 inter_no 중 교차로 목록(inter_nos)에 포함되지 않는 항목이 있습니다: {missing_inter_nos}" + self.issues.append(msg) + + # 1-4-3-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-3-2. plan에 잘못된 형식의 start_time이 존재합니다: {start_hour, start_minute}" + self.issues.append(msg) + + # 1-4-3-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-3-3. 음수이거나 200보다 큰 현시시간이 존재합니다. : {invalid_inter_nos}" + + # 1-4-3-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-3-4. inter_no:{inc_inter_no}, start_hour:{start_minute}, start_hour:{start_minute}일 때, cycle이 유일하게 결정되지 않습니다." + self.issues.append(msg) + + # 1-4-3-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-4-5. inter_no:{inc_inter_no}, A링현시시간의 합과 B링현시시간의 합이 일치하지 않거나, 현시시간의 합과 주기가 일치하지 않습니다." + self.issues.append(msg) + + # 2. 중간산출물 만들기 + def get_intermediates(self): + self.get_matches() + # self.get_movements() + 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() + + # 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_root, 'Data', '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='이동류정보 불러오는 중 : match1')] + self.match1 = pd.concat(moves).drop_duplicates().sort_values(by=['inter_no','phas_A','phas_B']).reset_index(drop=True) + self.match1.to_csv(os.path.join(self.path_root, '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'])) + + self.match5 = self.match4.copy() + # 진입진출ID 매칭 + for index, row in self.match5.iterrows(): + node_id = self.inter2node[row.inter_no] + node = self.net.getNode(node_id) + # 교차로의 모든 (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) + out_dirs = [] + 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) + # 진입각, 진출각 불러오기 + 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) + + # 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 별로 진입방향, 진출방향A, 진출방향B 정함. + - 상술한 directions를 활용하여 정함. + - (진입방향, 진출방향A, 진출방향B)을 고려하여 (현시, 링) 별로 진입엣지id, 진출엣지id를 정함. + - ex) cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['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'])) + + child_ids = self.inter_node[self.inter_node.inter_type=='child'].node_id.unique() + ch2pa = {} # child to parent + for child_id in 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방향 + + # 각 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에서 parent_id에 해당하는 행들을 가져옴 + 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 + + # condition 별로 inc_dire, out_dire_A, out_dire_B를 정함 + ind = directions.index(direction) + if condition == "좌회전시": + inc_dire = direction + out_dire_A = out_dire_B = directions[(ind + 2) % len(directions)] + elif condition == "직진시": + inc_dire = direction + out_dire_A = out_dire_B = directions[(ind + 4) % len(directions)] + elif condition == "보행신호시": + inc_dire = directions[(ind + 2) % len(directions)] + out_dire_A = directions[(ind - 2) % len(directions)] + out_dire_B = directions[(ind - 2) % len(directions)] + + # (inc_dire, out_dire_A, out_dire_B) 별로 inc_edge_id, out_edge_id를 정함 + if condition == '보행신호시': + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + # 이동류번호가 17(보행신호)이면서 유턴노드방향으로 가는 신호가 없으면 (inc_edge_id, out_edge_id)를 부여한다. + cmatch.loc[(cmatch.move_no==17) & (cmatch.out_dir!=direction), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + else: # '직진시', '좌회전시' + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), ['inc_edge', 'out_edge']] = [inc_edge_id, out_edge_id] + # 유턴신호의 이동류번호를 19로 부여한다. + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_A), 'move_no'] = 19 + cmatch.loc[(cmatch.inc_dir==inc_dire) & (cmatch.out_dir==out_dire_B), 'move_no'] = 19 + 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_root, '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 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 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 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 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 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(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_root, 'Intermediates', 'matching.csv')) + + # 2-2 + def get_movements(self): + movements_path = os.path.join(self.path_root, 'Intermediates', 'movement') + movements_list = [pd.read_csv(os.path.join(movements_path, file), index_col=0) for file in tqdm(os.listdir(movements_path), desc='이동류정보 불러오는 중 : movements')] + movements = pd.concat(movements_list) + movements = movements.drop(columns=['start_unix']) + movements = movements.drop_duplicates() + movements = movements.sort_values(by=['inter_no', 'phas_A', 'phas_B']) + movements = movements.reset_index(drop=True) + movements.to_csv(os.path.join(self.path_root, 'Intermediates', 'movements.csv')) + return movements + + # 2-3 node2num_cycles : A dictionary that maps a node_id to the number of cycles + def get_node2num_cycles(self): + # node2inter = dict(zip(inter_node['node_id'], inter_node['inter_no'])) + self.node_ids = sorted(self.inter_node.node_id.unique()) + + 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'])) + node2numcycles = {node_id : inter2num_cycles[self.node2inter[node_id]] for node_id in self.node_ids} + with open(os.path.join('Intermediates','node2numcycles.json'), 'w') as file: + json.dump(node2numcycles, file, indent=4) + return node2numcycles + + # 3. 이슈사항 저장 + def write_issues(self): + path_issues = os.path.join(self.path_root, "Results", "issues_intermediates.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() \ No newline at end of file