diff options
author | lim <lim@stsci.edu> | 2012-03-29 11:31:13 -0400 |
---|---|---|
committer | lim <lim@stsci.edu> | 2012-03-29 11:31:13 -0400 |
commit | af99439c057871ea8974f5e4cdd674b8c6110dfe (patch) | |
tree | f82170a77c8cb63691f1f235e46ee14664895eb7 /lib/test | |
parent | 69e439b92cece8d8a2def7d38c2cb38b639eab0d (diff) | |
download | stsci.sphere-af99439c057871ea8974f5e4cdd674b8c6110dfe.tar.gz |
lim added files to sphere branch
git-svn-id: http://svn.stsci.edu/svn/ssb/stsci_python/stsci_python/branches/sphere@15878 fe389314-cf27-0410-b35b-8c050e845b92
Former-commit-id: b51412b5b545598ae101152eeaed470b08616176
Diffstat (limited to 'lib/test')
-rw-r--r-- | lib/test/__init__.py | 1 | ||||
-rw-r--r-- | lib/test/benchmarks.py | 39 | ||||
-rw-r--r-- | lib/test/data/1904-66_TAN.fits | 375 | ||||
-rw-r--r-- | lib/test/data/2chipA.fits.REMOVED.git-id | 1 | ||||
-rw-r--r-- | lib/test/data/2chipB.fits.REMOVED.git-id | 1 | ||||
-rw-r--r-- | lib/test/data/2chipC.fits.REMOVED.git-id | 1 | ||||
-rw-r--r-- | lib/test/test.py | 260 | ||||
-rw-r--r-- | lib/test/test_intersection.py | 167 | ||||
-rw-r--r-- | lib/test/test_union.py | 169 | ||||
-rw-r--r-- | lib/test/test_util.py | 14 |
10 files changed, 1028 insertions, 0 deletions
diff --git a/lib/test/__init__.py b/lib/test/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/lib/test/__init__.py @@ -0,0 +1 @@ + diff --git a/lib/test/benchmarks.py b/lib/test/benchmarks.py new file mode 100644 index 0000000..9711d7b --- /dev/null +++ b/lib/test/benchmarks.py @@ -0,0 +1,39 @@ +import os +import sys +import time + +import numpy as np +from sphere import * +from test_util import * + +def point_in_poly_lots(): + poly1 = SphericalPolygon.from_wcs( + os.path.join(ROOT_DIR, '1904-66_TAN.fits'), 64, crval=[0, 87]) + poly2 = SphericalPolygon.from_wcs( + os.path.join(ROOT_DIR, '1904-66_TAN.fits'), 64, crval=[20, 89]) + poly3 = SphericalPolygon.from_wcs( + os.path.join(ROOT_DIR, '1904-66_TAN.fits'), 64, crval=[180, 89]) + + points = get_point_set(density=25) + + count = 0 + for point in points: + if poly1.contains_point(point) or poly2.contains_point(point) or \ + poly3.contains_point(point): + count += 1 + + assert count == 5 + assert poly1.intersects_poly(poly2) + assert not poly1.intersects_poly(poly3) + assert not poly2.intersects_poly(poly3) + +if __name__ == '__main__': + for benchmark in [point_in_poly_lots]: + t = time.time() + sys.stdout.write(benchmark.__name__) + sys.stdout.write('...') + sys.stdout.flush() + benchmark() + sys.stdout.write(' %.03fs\n' % (time.time() - t)) + sys.stdout.flush() + diff --git a/lib/test/data/1904-66_TAN.fits b/lib/test/data/1904-66_TAN.fits new file mode 100644 index 0000000..09c3929 --- /dev/null +++ b/lib/test/data/1904-66_TAN.fits @@ -0,0 +1,375 @@ +SIMPLE = T BITPIX = -32 / IEEE (big-endian) 32-bit floating point data NAXIS = 2 NAXIS1 = 192 NAXIS2 = 192 BUNIT = 'JY/BEAM ' CTYPE1 = 'RA---TAN' CRPIX1 = -2.680658087122E+02 CDELT1 = -6.666666666667E-02 CRVAL1 = 0.000000000000E+00 CTYPE2 = 'DEC--TAN' CRPIX2 = -5.630437201085E-01 CDELT2 = 6.666666666667E-02 CRVAL2 = -9.000000000000E+01 LONPOLE = 1.800000000000E+02 / Native longitude of celestial pole LATPOLE = -9.000000000000E+01 / Native latitude of celestial pole EQUINOX = 2.000000000000E+03 / Equinox of equatorial coordinates BMAJ = 2.399999936422E-01 / Beam major axis in degrees BMIN = 2.399999936422E-01 / Beam minor axis in degrees BPA = 0.000000000000E+00 / Beam position angle in degrees RESTFRQ = 1.420405750000E+09 / Line rest frequency, Hz HISTORY Parkes Multibeam continuum map HISTORY Formed on Mon 2004/02/09 01:23:37 GMT by "pksgridzilla" which was HISTORY compiled on Feb 9 2004 12:08:02 (local time) within HISTORY AIPS++ version 19.405.00 dated . HISTORY Polarization mode: A and B aggregated HISTORY Gridding parameters: HISTORY Method: WGTMED HISTORY Clip fraction: 0.000 HISTORY Tsys weighting: applied HISTORY Beam weight order: 1 HISTORY Beam FWHM: 14.4 arcmin HISTORY Beam normalization: applied HISTORY Smoothing kernel type: TOP-HAT HISTORY Kernel FWHM: 12.0 arcmin HISTORY Cutoff radius: 6.0 arcmin HISTORY Beam RSS cutoff: 0.0 HISTORY Input data sets: HISTORY 97-10-09_0356_193558-66_206a.sdfits HISTORY 97-10-12_0142_182123-66_193a.sdfits HISTORY 97-10-12_0151_182707-66_194a.sdfits HISTORY 97-10-12_0200_183252-66_195a.sdfits HISTORY 97-11-07_0510_183836-66_196a.sdfits HISTORY 97-11-07_0519_184420-66_197a.sdfits HISTORY 97-11-07_0528_185004-66_198a.sdfits HISTORY 97-11-07_0537_185548-66_199a.sdfits HISTORY 97-11-07_0546_190132-66_200a.sdfits HISTORY 97-11-07_0556_190717-66_201a.sdfits HISTORY 97-11-07_0645_191301-66_202a.sdfits HISTORY 97-11-07_0654_191845-66_203a.sdfits HISTORY 97-11-07_0703_192429-66_204a.sdfits HISTORY 97-11-07_0712_193013-66_205a.sdfits HISTORY 97-11-07_0724_194142-66_207a.sdfits HISTORY 97-11-18_0256_193815-66_206c.sdfits HISTORY 97-11-18_0306_194359-66_207c.sdfits HISTORY 97-11-19_0447_182341-66_193c.sdfits HISTORY 97-11-19_0456_182925-66_194c.sdfits HISTORY 97-11-19_0507_190350-66_200c.sdfits HISTORY 97-11-19_0516_190934-66_201c.sdfits HISTORY 97-11-19_0525_191519-66_202c.sdfits HISTORY 97-11-19_0534_192103-66_203c.sdfits HISTORY 97-11-19_0544_192647-66_204c.sdfits HISTORY 97-11-19_0553_193231-66_205c.sdfits HISTORY 97-11-19_0602_183509-66_195c.sdfits HISTORY 97-11-19_0612_184053-66_196c.sdfits HISTORY 97-11-19_0622_184638-66_197c.sdfits HISTORY 97-11-19_0631_185222-66_198c.sdfits HISTORY 97-11-19_0640_185806-66_199c.sdfits HISTORY 98-03-24_2107_193706-66_206b.sdfits HISTORY 98-03-24_2116_194251-66_207b.sdfits HISTORY 98-03-25_2020_190826-66_201b.sdfits HISTORY 98-03-25_2029_191410-66_202b.sdfits HISTORY 98-03-25_2038_191954-66_203b.sdfits HISTORY 98-03-25_2047_192538-66_204b.sdfits HISTORY 98-03-25_2056_193122-66_205b.sdfits HISTORY 98-03-26_2048_190459-66_200d.sdfits HISTORY 98-03-27_2034_191627-66_202d.sdfits HISTORY 98-03-27_2043_192212-66_203d.sdfits HISTORY 98-03-27_2052_192756-66_204d.sdfits HISTORY 98-03-27_2102_193340-66_205d.sdfits HISTORY 98-03-27_2111_193924-66_206d.sdfits HISTORY 98-03-27_2120_194508-66_207d.sdfits HISTORY 98-03-27_2130_191043-66_201d.sdfits HISTORY 98-05-10_2123_182232-66_193b.sdfits HISTORY 98-05-10_2133_182816-66_194b.sdfits HISTORY 98-05-10_2142_183400-66_195b.sdfits HISTORY 98-05-10_2151_183945-66_196b.sdfits HISTORY 98-05-10_2200_184529-66_197b.sdfits HISTORY 98-05-10_2209_185113-66_198b.sdfits HISTORY 98-05-10_2219_185657-66_199b.sdfits HISTORY 98-05-10_2228_190241-66_200b.sdfits HISTORY 98-05-13_2132_182450-66_193d.sdfits HISTORY 98-05-13_2151_183034-66_194d.sdfits HISTORY 98-05-13_2200_183618-66_195d.sdfits HISTORY 98-05-13_2210_184202-66_196d.sdfits HISTORY 98-05-13_2219_184746-66_197d.sdfits HISTORY 98-05-13_2228_185331-66_198d.sdfits HISTORY 98-05-13_2237_185915-66_199d.sdfits HISTORY 98-05-25_1711_182559-66_193e.sdfits HISTORY 98-05-25_1720_183143-66_194e.sdfits HISTORY 98-05-25_1729_183727-66_195e.sdfits HISTORY 98-05-25_1738_184311-66_196e.sdfits HISTORY 98-05-25_1747_184855-66_197e.sdfits HISTORY 98-05-25_1756_185439-66_198e.sdfits HISTORY 98-05-25_1806_190024-66_199e.sdfits HISTORY 98-05-25_1815_190608-66_200e.sdfits HISTORY 98-05-25_1824_191152-66_201e.sdfits HISTORY 98-05-25_1833_191736-66_202e.sdfits HISTORY 98-05-25_1842_192320-66_203e.sdfits HISTORY 98-05-25_1851_192905-66_204e.sdfits HISTORY 98-05-25_1901_193449-66_205e.sdfits HISTORY 98-05-25_1910_194033-66_206e.sdfits HISTORY 98-05-25_1919_194617-66_207e.sdfits HISTORY Original FITS filename "1904-66_TAN.continuum.fits". HISTORY Noise level of continuum map: 59 mJy (RMS) END sPwľ'O$`zģz@ +żF*p,E־F/[ k>V詤Q"UXҨHp^K+F5;*nҽD`:HtCʍmaO<߽X<P=tY=8;B=yH=ѯf=>k>68½@ѽj1wXLϽ
-`n˔Yۋм߽g<wH=й=-<cڽ5 a<<H={Ƚ`K _WojAC;r+p?;4sn<k<L=0L*=>μj<С<<#= +̂;5===u>/N>H>?y?.z)?,,>_>>W[$T>?1 +CfTv¼=[=.={<(;<
=Uf'=~=cMq3 O+l,c{7yt<2o=AJ==
>8=1N=y< <$=]==T> +5>5>GuC>W#?S?v?K>>,=Ӽw2rT|
<\uFGk=!5=zO=Jͺ=Vq=HK{|k+4~7:=`d=Q=.P=<=><=t;el>&t=@<ޠ=M=%>y> >o0>==g3==&=Gi<>j>l=Ħ=17}I<cIͽ*. |V z\sЍK=0=X`>= =g; *J=f~=m>}6>>9>p>??S?=> m=\t='n䱽`A;^=S: =&i=/C<ܽo7m#c oL@9&ɽOV<ټdh=kb=zoi0{y6"=OJ= 9==&&-<=<;r==<Һm
<=؈=>Cv>X%>c
s>j>> x==>!s>ɝ>r>Zko=-<O,<yќ>&on75*$%y=N=n>=l<Z[ϲASQ-=e>=>90`>>>û-f=qS&P!ƽ kT弨ۚ<&=t='=S<eZ$£-)mviأ܈=VS=;1u=KR8(h:zWqgս7G<Q&K_c ==@"=T ;=&R==P<T=Xh=p=4=QF=Q=;/==l>-΅>H>>b->:<=>>;>0)T>2>,j=V5<Ԭ`<'G1e1Y罖7ԽRAfvl=%XE>(>P=m=K$Ž{Qo佩ýo8+ =W_<:AϽq{F%ɽ?=11]=!=>Λ>,=<H,G=AO!nԾOC<KJs-7{<Žc˽CuM}p⽚L<¼]Añq,;Iyp_kCjkaw==;>x=
==9=p6=
J +6l<s==ae=)=a==
|=מ<=>>>> =&H=v +=>w>E=<l&;L; =PbJl}V˽늽9=7><䗣XHą`=7=='A=>k==B酐𫽟"xoỼ3:ʰTxhҫ/ֽ?Jս+:y=3=sV>p>T +D>,v>A=R9w;Jl<=]='i<?;7ˌuzD>%zv +ȽV9pɽQAU~4rUY>R~e}:9gzO϶D;"bwμ.=
W=ކ==z=+=.<D̽Fi|ݽ = +jHO<y\̽GG弧e(R8J2m=^q>Z>Je> m>n==Lt==X='k= +K +ٻ/==xϽUF7ae]>:====D?(F=^=^<> +M^>I=<ϼ&R\Fw/C
ܾZƾ(IHF$WF`[6crG@kyAἢGvA7==h=ʙ= =y=lɻɻJ;r'۾JQ
=}<=oɼASitzn0[pؽ>HS<n<=[Ѽ*Z@=>g>UN%>u>zA>a>Z?>b=> '2>&L>2^==M$>3+k%Z]<=7===놡=Y>! +t4ۼh=="C;};⋯9wG߫,?z-cd=c<=!뼚\=> +[=){=>l=o:<_<-<<Za<;.el:LhӖ^Y@$m<<Ӡ=D=9>Ic>vx>vc>3=V2=c_<,<&h{F<[<wP;悽P]p4P"ƽ\Tl<B-!<&7%#K!w>|Qn<u(Y5^>U&p: +/A=Y=3r=4 <<Nl1<=:*=r==sc=YG5;Z\1W.QͲjGffHS],CNGfn'þDA!iE9 +~.m˜<=.= H=y=8;0<=v=9=@=5Y==ϛz>x֯>>>>b->T>Q>N>A6=== +N=Gi=Y=2=hD=2<C=HJ=\u=<w<=dR3<R=U=#=y=\=CU>:==&3Rp>i?#z>h>U>;g>"ql=П=0= +=Ze9=>M?>Yn>y.>s2>Z>"]v=؊(<}弇:_ۮH$u _;Fcͼe\`F R{p<ѭ'=y=]> |>e>I>=ƌ;׀Dz/AyƋ +ڽn]rS9Ge[ѽ\~Erټ61弒*T~:H#Cv ƾT"ݼ7Ǣ- +$=L7=<66<W<A
==)1===!<6;kL:%YaF- +тx㽽e +Gݽ#b'ΑP꽃u7뢽N{ƽ9ý#!,rǽ(,.ν'@f'"Q| +(ҽjKl)XKz8vD⽋%3u^pg;WXusykKU;=O=B> 9=M==Fy<g<`=1=)v=&,0:Zo 콡[9y}BHuo+6<FdOO澽#`>*9z7k@˽Umýq%ݽ/:(#
T; rz;N<rS<=(===~\=79qE3n҆c-:Ǘͽ䧽
쎽G$pYtڽjT:(;~<= ^=w=$n<iS8O +")\#õ9Q=!>&ڸ>,>t>>?+? 0>> S>k>>>9`>)>
%E>0 +w
ė½ޓݽ@~[G0wd:FgmCU8ǽtW0{:a<x=5= =4*p=ݷ=o%==o=V=7>Jsżȼħ}5`ܽ|$.^4ǽJxk}d>(*)㟽QC7f7*)"e1
<+;:`<z<=$Z=h{<#<ѕxQ$pW>H_EϽٽ;[1y4ܽ:-kvlN*z <6<V]$<s=/ލ=`X<;/V;Z!?@JoM'֯jڻ3<<=3`>>@,>0>">To@>c\>'=1=^= +>%I>>cC>c>;=͐ +=n;=ཟ=4=#== K;EK=#'{<H2{^27H"ֽg [GG˪}N<CRɾ}|a=uX>3>Ll\=<ϻ>,t>6V>2[>1v>1=@f=aA<(=;><>v}>@0>A +놽Ŗ6˅ƎhɃн +M=$>;>_G>r>?91?Vl>_>[>y== +żԷ&<VOWDx9)3ҽ%5M; <] <üaV"4>)]>4>>aO>.=/Pq=>N>-t><={>==C<j=n?==}$=qI=)=ټ(@(iŽ/Hʽ#.ְK9T3eklu9oMiWf@ɽж2XP7.}:-~Tǻ<GQ<0=j<tGux5u.OT;;]*4`S,O/ڽ+]{J(D]tq#2L'35,gmcTw3fL<M2= =&%=*<<-h/ge|5:Ƚ/㽜NkλC:<3:ǻu&VLYkܯxbh8ChjٽD̞
<;`ٺ<!#91N<j:$Oe<'-=җ=n>3nj><>)?Y@5@x@@U)@/?a>ٰ>\=!;>D0>G=y<m<d9=/<SoG<;&.;<Nw4<yV=N<w=W;*k=YȻ_Ύ<I<= f=,<u_l=}m=G|=>=+_=b=jE=1=>b>2`}>2b2>O=/>=d=Q=m=\
=^==;F5<=P&
<=E~=u==);OգmYƽ܇~!?aýS4l +?p6ս֑}
jdEYyӱ:xͼ'S8}=0*Lj-<I<oaU;!{xSd2ʜL;k; `<0O;H(:ߵ<!<Ѧw=*~=d=pD<>ƻXB!/#XshVϽ
gX̼{ȼ +ͻ =4:=%=E=vO<}(?Hd0˽x0^5<@>z;B<=#*==e;dϽ扽!cڼ n<Tƽ_{h<9mO;қ<<TM<L <g<1=,=Ż>=$>k>?~@?r@^,@Ͱ@_@'@ +@6W?/>>&
>Hi>J>19=Q=|AW=&=z/<;yۼUmi2gټdx;Yo_<ym +H=(O=\r=ki>9T=c=;m>"|~<=<*<R;;e<#һUؼWO廓#<$<<x;:B_R\yBQ?,/kuH?ǘʽg~>
X!VxJ.y'鵽p@mּ⽊V7JNnOһM5/xd<485`%;RdB<1=n +>^R>?T@Y(@!A +t5ht"V:DR:)8 &7S`><>O=R=$R=P]=9;z==S=K=JE<F;: P! +GLlAV,dPB/Ni¼#5(:$KAF +pe"<#=2Y=(Q=5^=<==@=>{>m?x?T?1?\*>x=jM4C˺ٽVYϽ}ltos,꽞2Dۦ<Ƽ*q\ dm\ofyK:;<g=o=[+=<&=T=->n9>>Ņ>f=bw<ٽLGEK轀NF,Iz>g8;rX=4=Ķ>"">j>(?yVz@'@@͞AfA-q@~W@ز?`>f>b>,=ya>
s>*> t>/ܳ>*=]:nĽr૽ھu߾2BX=>p/'eJP"پc,^>LCJؽ[`Rtm ^<n=#=Z2=?!=*=jD=SEsi+ 9.%Bݽ5#L5#Fw.:G=^e>I<NL<(N<@<.<f}*ul7$QG +;(meWD;ܼt_mhZRS??oF 3+_eW0ڲ༼<>=9=́=8==l=ņ>>?p???p?q?"K`>W9]˼A+ƽˍ̝XzIT䢽Aʽ+@Ҡp}>6qNAPK1l#rażϠ<<)<O<'=o>UO>֙>I>>>I?>;fB E+q)"3;,+̽L'b?7t;I<[%=K>m>?.?d@<@ckM@@Q@X@<@E?|>=P="ʼ;g-=[Y>5>C>.>N>*<彅UѧF'.<[KF+9p6 uƆr_Aa<|+y6v^dn3~{zRg=P:=i=)=3=ȹ=
p(;Y
eRyyry竽@QX<a=EN=OV=2===A|꼮X[l!l1<=]=̬=WZM<[20ĽK*潔65ڷ|ޠ4>;Z369[1DCgw96lf~6v<B=z==ev=3=[==>>Uh?n$?3@N@?X?a;>."wLSGG W +<ɒ6!PqٽĽL +>
>+?J?X?Q?)}>?Q>?p=1<.+1Ent9 ý\7!h[q29a=^&>ׄ?d ??R@'@/A@O}@@@Q?>T=Zl=18!9ý(\1<<=sT>4N>:&>=`O&.ăwdlǠF9I37NqоR1v9vm +W"h?-*+Mǽ(WJU4>o>0>o<dഽL?5~;9zFͼ<=L=LR=6=O1==T=]I='d}R;&u=>)>ӌ=Ja< 7̽7>Fo;P<{G<(ҽJ;GVF +SϼA?wyir;i\Y%Ii<=F7=;A< <O=a=>s?[d???̟??dY>qû]佾 #})kDʽҨ3>:.Ui9vmI ὧzu-=h>X%>?&?ch?Zy?U>>#s=Ѿ=<*11ؽ⽡}彅k@G +Dc;}>~fO?4?ǒ(?E@@,@ Z?Z?ːr???Z>}'K +i`.w\ۧ A48y +Rl]={6>=?)?V?+?B>}>6.><rTlWνH@Yײ'VDC`ڽD뽔 |e^R:OψI̽nھоt"Ӿ SW`Zf|<T=l>R +Fj
S0͟=ݫ}oֽ6%-,s}e
sQ<4J#=>]>!? t?5? +۹>>>>N=h=GSսhǽg5,SjL<kB>#?.V?)~@܁@@
??U?X>>'=8VTԼ|,ܗ½.o'Mft;d<ח</&+4 +d<i<Ļߒ0rļx0rн~4&ܾ}F]ʼ8C'6-Z^p瀼k[>8T{==(V=k=3=->u=s>&=^= .<=9Ι= ɺN_/N¼s%B#<G4<t*<<3='=#%<6μ{ш)ޛ:^<[===S= +1ѽϼ5<O'<
= +x;6żFпs?֟%߽?|1F\{0I7^>|˾Ӿ +,)aUE;=l%>>>^F>>Z>6L><ۼHм{nqGG +JC&}3]4ʽ>½S?cf1)k}fjNԼf{!}<{=?>l'>@>&>Z>a->|>>>Lp>9| ߺr2~ؽHf"$Ƃ`< >mz?l)?GG?e??4%>=`=.x+q)սmG]{gH+\i@/ :z;=,<7<No8;?\=[Tgcr
dyx_,*& 2iAYsۼ6Y>C8b>>">-O==>=> $>tE>XR>SH>#>s==^,E=qK=n=;C:@;ť<Zhb="Lz=X=Pk=N={=b;ҽ\e-%w<]V<¼n<>x;7S3x۠B!pNɮs1\2guTսlRJRk؈}t8F@&ƅ52=@+>>e>>?>[9>%*=f;gm^ԽF|;b;LD( "t*: +B~dgZp|h+:;;.r;I;;+ͼjF?8<=N$>&{>Y>}>2L>>>U>r>Yz>5H=x<m=7 +Xٽ +41o'}sͽkS]QvT=>,>$?
>3>~>=W^)?3k*<ˏ~ھ&;߽Ű۽4*Ľ;/%XؼC<jT;B<H8Hfh5hKp/y +><GP#a'<FK<<fO ⼪nFF8vu_3.l⼘k"<1==`=oT=@s8=<<m=& =>'>,>+>#<>>3R>,ݸ=R4=ʭ<Q
Fzf
좽Cy[~_nY潿(F
no'=s_>#>`=B&zlͽHȯJ; +`==S4=j<u ;t/gls:FUخƼoOZ}JKEu;MT-kGbcWz>nk%n[%Խp +i
<R=9N==@:rļdp̽R^1I +.g½Eb=J$:g:?},?[?<?EA>4>E>>}F>7a>F>"~==Q=<}5<<w=B<A=;;ف=6E=8=kGH==*+U<I<k4J=<:\` +<;>;]7>f~hhW/ZI
R伿gQ$Ӽ0!˟ +ȼB(dt턼26.lV/Q?YֽE<|ֽ!RB]H+<D =1>=Ԙ=:;KTȼ~ݽC)^Vn*{-g=7t +=d>(>0>=`=j=-"=mo=9=Jwe==Ax=>1>8>"=q>t=hq=#z<\
=MZ<u;1AwU'+˼{5dk/&;_T;%Z<y;;SνD^~b{cRнc_GVk6{O5ȁ <=4<m<LZug$2żQl~=g= >O=;*~EǶ뽥!LϾ1@u|P)l!<=p?y?@W>9>"f>ط>p%>%=C=+==o=m2<<U<4<<k=4>=x +=XS_=`=A3=?= +6=¼>#♼=7\;:t];<%E<{cw"_\ɹϭ_'c;[EOP.8>a.̷MCHEˉ~X +f +λ2<]<4<|;s_a<7Z½zνm[DApR{~⼢)]νg@r+7z< +$Z=@ +<.;; ;= <J <B6`FIG3!4dAQϼ' y1 +O㲽WA""216n٭k5=M<->}>>};)>y>U.>B"><?=7={:=4s<E4'⚼~ey'&;<=8< 86=Bu>uJZSUܼ/W!|<R%=@P=:<D;{7Pp=gZ/9.JhHdzؽ\ǴƻI~4ػ,;?byzn>6)ǽG)gM/ּ|DihӼq;=7<<;jvf;]4佑11sS>м:=W>>uX>f>*J>#>>V +>2֥>!w>8>~>/>
>>?>`=Ѹ=;;][<z:̼TV}2U(
K@9bcY+ս.vTK4ᢻx;u;I/y"ƽ Ƚ>($$ʽ8)ؽ.`9/;zbzrCyO:@{T.5]1D;2^ܼQ:sm;K;%:|8;<=NNo=ANY<tťO⽴'<{ĽZӷOXo=t<A=>@w>:XF=,>MrP>CF->x>2d=tn=T=C@="HNA85/M{QoDj*onV*.<K`<١=)<t7RF1Xi4QO_-8mO<=R(D=J==V;ּB +ܼGph_fX!pPa埐]땼oɻyuT¼ޭHDԩx)t}EMۼqFIQ`_ռL`;<uemӽBitW+GloV=5=q[>4\>w>j>>1>{X>D>1>YR>U> +%t;tEk<ɲ<#=U<$<=EO q+q)uQ<[ +<kl(<]<"=`ޫ==[>Ŝ>ف==fSݸ +==]̼1 +[TA*3y7*-N<H9<=5>ej>y=<z¼ĵpin=ƛ2{սo6Bń<=T=($=&8<9Ƽ>x:'XhYCyü"-2n
/Q̪(KԛRpS=(oiݿiDr&;9ۼǃUZʽU93°q;ͼyM;Ƹ<= =Q>!n>X>DG>>u>MS>"h>R8>) +>Q>??>~n>]=t0<}Fz49Dշ#w;@b㽼FyQ彤Ľn_"Yսms
㨽y^;!D<#<];WJ4m7輧μz11;s;]-t$6R.Pڽ7=v ( ս<N5.IѽOp)G/3;J֚<- +>tG>H>L>>U>;=z{;J/ D2Ahü3½IUݽ}Ž@?/ +vm<La=7=nF*=Evt<=*Q<ۡu_#xq"s)[8T@֗])q}Ijuj\n;7ԻiK<P<\<Y&ʻ%y
<AF<֠<oFJR1ܩCʽ:m*]}c揽ݽVm%ֽ7bY=
>Ć??? +>Y=C~νh&D6 +` +==Q<;a81n +MAXG0'-;d==0b>^*>bdy>c=I=q`κ ڼ<#JG=17=ͫ>8|>v>Q>2o=y====.==yf<qW8۽ !\Y0.]sp?ݼt_SNc:WY.hKg2ud3нý@D !;D[<l<l<P<=ڑ;Ԗp;el<D3<^d<ghM;<8=e=8x===?6;|ϼpW-I\}kE7҂1 6R ZpyQ;ze=F-мGgr=ds=;σ;K:2z= +n^̐FCYC;=0y=s(=4f=JQN<>;K:ÓI⼞'Ͻ.ݽ0,naoW=>z>>h?B>B>A+=m.G;Rt- +R;m=>>B=<w5><3;я:=.=y³==z<w<Op<oe1uLS +R8r!nooPI⽑%;ǽB^BDH಼45<>h<<D<;ld#<F<;7@<<r=$J< +(<:#=B'=t`=Q>=C=3<Y H;lнfS=Buk<g*Rem]mFק}6Hu+y(5 +M뷿;<|!=#o?=M=&<⹙+<0<B=)ө<io<;sɻ~`qE<=y=<<:wglAdDJt뽏
½6>(~?;?v??-P?>>T=u`T·ʽlr#2j:t2`;=y=K=RV=a=p(<GȽH&_ܳ +{j::;T<?=D=+=9S=eD=Sb=c=?=s=|<^;ٽ?c;ZQ;Ov<<FɼuU:B~mJٰ{
F$V[ؽ$Ԍ8<">N$?Q%?G?ֶ?ћ?v>?=6ukнٽٽϽp:W+}`=<>=> >%>>=[R'GY|)< =5n=qC=jԸT;;go9۹ʽ蔧tr(EY4r{5]4~f<%<i~u<k;_;H;n<pOv=|=;T=l)=1AF=<Cf=O=PH=3;3@k@t'1,\`gqqj8ܭ 틽S߈OO87µ&$=<^<='-=&=.=Z=2f=P5'Uoj=<8~,0-)$8ܾ|Hž/9&},"6ts(w7սOm +^M=Ͻ|ӽPySmB="¼*;G; ;<g<g=8O='=U=5====Eh=<G</<<n<J;}zU#ƼK9A8)V<9eS=eb<+ڼQ8ȼglQ +#8]T3J)KY6BS2ozsb8,)C0sjڼEź[)j<{=6e=b=?֣}<TJsٽc$CqIWgG +;=|I=o=B==Ҫ=GI=!= +<Uo<-ltR<ROB,b##'"k;CF<<UW<)=K=Ah= +='=K=,V:Ps˻̻`h1'JR. +Rν"$lX>I\^q嶼5_$t<<̜d<L/=2; =U==,=^,<Qe*Iٽ]9iAPC<<B=<j黟hv>+<֡i
KsC +c>"$>ua>> +J>U`>\P=?=H<|8<=="r<(x{l"ׯ#;y;w!<Im=
<c+Vؼ!i
bB;uuJ6G/{ڃӽ6-4YDBneo}Ԗ>u=Ӻ=/>->a/>_9>"=$v{<h=Ė>(>$> +BidD5;(<0*<Q;ͼ_ƼѽBd$#PK˼۔f~Hb<FѺC۹@m;d-t`A +(͟C:QܽSͽ%I{<=;\hg)'=Dp |~[7`<:3<<s<ypƼ箼SՏ#>#T>r=ʯ=<8:M|яSu
ӽF[1ν牽FȔr"ҽKHh@ɬP-=z%=>>C>*>
έ>Q=u=W=8a=%=hA=y=sv==^=5(==w=H%=9===)>
=>c>M5>]1>|>4>kV>1g=j~<BJ<.G<J<<\ǺLϼ+伄üi:.si;A==d<eK#L̼{و?PӽWve܆=-EMBCѻbGoi+сcxǽI$<u>}>>>>y>0=H;4<MH< =B_> +\._dY4155<=I=U8=b==۩<'g +(<7,#Lhz,0__=5ڽ~hNxM5%*{;!$-Bg|k2:_=d=>s> ?>rD>va=7=4==n=|y>,>vJb>j>G=+<݄d2 +h>@%=(=s=A}C>a</ةLs<`$QJ,yhFs'C +ݽWp1/Y_ӽM*R<'h<=n +9R98H^J2$Ӽ>Z.5Oͽsܽq\˽,|rp!@:PFh:P;<}[b +R>\>Vׂ>- +=E<~(gLZlnO2]&sv5s{E8Q齆;Hͼ7@P芼k^SIGT\d9UJmD!Q8wݼƽ0cw̲(";:;
5:ț;0<=8\= 1<Nh<=4y=W=>,g>h&8>zZ>Sl>4!=1V=<7<6y˼d<rNǜv۽8b=7L=jbѪq<<*= ==v(<YVD/溿Zh<=4=G=Q5=*<= +Z@=O=r>P>d>{S?> >>j====Β==
=E= +%<ɎU=^=2h=e.=X=E=<;tQQA:;<E<Ӓ_<;ǻ]}N!9j}V(SY2xki~bFrk9-KF0}@|9<VH;\T<N<<<%1^)I驱mй =m>; >
>>>Ey==@R<<r(=!F=n>e>S==<raݶ@tv!;"MfKRSu̽aCHi꽚Z +_lv/9½9E%[S7kSM;kkSCʗ;U䭝*mBP_;i<<X<a<hKQ<<=b=Sb>>b>u>b)e>>!Kp=>=SՇNցƼ3ýuvt̽4mqCӽ@ڼ<<*<[<<a<+<I̻7jzڼ&M< +=)=W}=3*=Q6:ļ< =q==3=a:T<g<E3}VθYFv|OY>0EݽAc< +R;#:
-6.lg3:zJ[XN;L;3Y|= eC=+W=>>ET3>?8>>9==+g얽6?0ɓ5Rὔ罖D%!½HO{A9h= +=b=HI<ͪ/<nD9<.+$
dE @N^pt<b +'<=v=Wn=g\>\??N?Wg?: + D¼>7=Ub=> +L> +>#=|<<\*P;7VF橼:39l8!27VD~L] @~PNfY",ڽ|'#kVؽϽ?[P;M@@1Vݽ^~O?ڽI/f'Cn{;ڽ +9 +|q(Co +T=\#TS^6HxM^;=>2%>=^>=5>9R>
|t=:*==~=~E<Y?#$?Lb95M0żHe_w:qd;{$V<}b<H#.{FvIUw}Ժ=i +۽ے;_< 9==[=y4=p>,->h{>uk>U>Z=fv:;e)U:\KQѦM(|wUk8l,e<<<9T;;VZHqL֪;UctȔJp +.UYҽ==>==<=\w;琽5w
n
c>&sD^i<oTPo2];~P<YU<<ե<橫=ס=`2=!.<g=X(=u=D<*˼*:ޥ]h̼#>ּܔ*racEFzTJȞڽZ彷ƥ +<!C<Ȗ'</z;6Xq35ټ:N
!!`^ +, =Mؤi<P +mLW`qUԷ>G==:=>Ў=L <j= ====m:=R=|Q=/<<}@E-O8B1<A,;Jq<*E<67<o<|=l==6=!G5<6)<;ݼ`( r~һ蓻c_$u8o<~L=>4Z>gگ>n!>#= r~K#a!!M6)G,K[kT>5
S<:<z<㯦<翤<z<></{ +=^="=> a=.==ݩ=S<jF<M/SǿQ齂zBQ b
wFF%0zNt0k~u=7n=w=5= +V== +O%s;B$=5{= +S,yܔcH;R<=gK=&=q@F鼊¼r=u}<4̡<!<el<:!lӒjP<(<U==F=T~=X==v=K> +>#}">,G>==<!٪;fwJZ9o:E憼 I.gLѽ(@e{w)&
![Wʽ">g۽Ru=A~<f=WΈ>̏>SD>E<>94>=s>>>v>p===&[<^<~<N=,;^{Cx[ԽP34+`;<Mh<f{u<<M<3E
WƼL'.4ݠ&i"3*a2]==ԩ=x=j===9==黠==P==]==v=<E:;WVmX<L= += +C==T==ż==B=>y<Z59̻;<T1=i=q:=r=C=%;$<(1<E=
G`==='=~;WGͽj2NAj`zw +;T=9s=R=NH=S=2;P4p2FO
%Q"巼*yB[*U;%h;9Vvgk꼳Pj ~4Gs<%=L=>g5<CAy;Hn<+ۥ<1=s=)=0[=a==m=J=fn=c=+=a=
mc<"|;p}&Rmω|`)<U/xX}i$6ȼ6H +D3ŽLEO3s;p5\۽s;a=\P>&>O>[.>Oi>>`>v=ܳ=(^=a=Z:8^Zؼ\6'-`vY7ⲽzVӼ$q;Y=$"=UM="==bʒ<y<<O#(;ʍ<F ;;h㢒p=$==T=1>t=ذ==J=<J>ϧ>V>= D=<&<Z:<(L===Q==g(7=W=G=t{=U=I=_<`+<I'='=_>ߓ>E|>1~>=K6<3&>=< =C!j=]=8?m4ڵqB +JYL(ֽ&⽇AԼ%<= +Q<mлi}y} +6h 1bÕ3;Vl2=P=9=i8=6>7=§=_=3L==G=Z=J]=E=kxfٶU6n#<==_= +<)Լ
9Ƽ-x ZZҼ&?缣=NqKu%fýgܽ=E$Fͼ.80Tz; +;4<Fzy;%[<fU_<,<z5<AM;O<Z<c͠pPh2v߹աjp#XD :pK}#ͽqf|Jf+bkj>y$U=Aku½ iT ;̻0z<=h=R=c=ƶ=J==h=(><`^j24B
?M\5+J\ʼBkݽ0%a3%伊ٺ +s=r>>\>b=.&=O|====Z>!jy>*`齛&<S<G= <=dy=====>qo><> +"=K%:\Xc50Z<u2Ĩ( +k銽k"Dq e3FlȽ$OMgiL&=<====8=;=@Ϝ=; ;;)<T<5<Ssl;"㻢 +0hμ@xѩfϼ'v0)9Cp Q;;i6`ټEJƼKΙ<Z^<{<;E;4]"~]؝+j?Ua7MuOhh`.ɽwRoo4ܽUA?P"=QC̽C鞼g8;RҼü5#Iu䕼绉<5 <B=3$8= +\;gvW&L?Ɔ͙<XA.r%arӼt+FW5-XicX<<\=J1=#0>@>*=O&=l^=h=Jy=J}=7= [>P=
<e1f<<<=8 =k*/=@=b=Z=ni= +%=5s=Q\=e +=P==e;寧V);g<k<D<^<<7~<_4<<V<u<+_C;N;IX;X<>(>Ma2>~=>>P>
u=;YIC26ƽ(= +O\<ʟ̄ƻsmQt*n< +9콳b\ǔd$1;ɽPI4½罶t큽rXϋڽný<j=A>R>,>>i>>n֥><=Ń=xٹ==Ys=w=e="=XK<;Tȼzr69&_軁!E `MO½%ռj.:i8;*;ϼ;!;< =Cn=^W=OK<gA<Կ;ڃFgz{Y5{)뙽eؽ`9Ǽ?>_] +<b<G;Ǽ@Вpdm48ʼ + %T|"E;
jS&I4q\u_ulSI I8=R>> +<X<b<8*N<_=`==<2= +97<Ky%w(B/N
]o<<<ງ @ +s96 +7>c>/=^;==/t`[8krh`-'мKD*o9mx)`<<d<Q<h<j<j:< <=JM==zI>M*=F=(<< ڛ<!b<:AGdo5;QIetR켆*<:ǹ*2:<<H=3=>>>;=26<Kj༡*< <<<¼|tHN}KH:ùo: o;N;A;W< +$.5\GuC=dU弰i#'üy`aAټWII%dnex +_HAk=qLCh=qYV/QۘLQl{i2sG1̔uʛY!罠ѻY=>@>#>>>0>=ﲕ<)>1̑¼b1F)>ha⽈;sc&\ϼ_:C;1<<@<<s;Y.9:jcvī:Źv<d<e=#=A=w7==,=;;̧7;;Ý;Q%8k,&:_^9?g;զ;<%j:ܛ<:y;;լiz<=y>[>9w=e<(PfT|Q) &^A42R4]Վr?Lļ1&[KiJ'`(rFY3/FJ_zܽ\jT<lHS~_3݈)4-,V@2vQ;C[ݼ #ŻEĭ<<<\c^A㖼ү;߽XF1'_¼μ,ۢ;$3:<XG<</9.]Zm<ҽqi?l8*%ƽ
Q,@"Q;|`\:Ė
aď֩
q]#sͽqнI۽P@Hi
mn5)
A>][5ҽhսh> +>;>AEf>5=Bm<>ERcC٢--:~V2(팽N +g;N%.q*Q8~J'3(ڼW:_I5s :Q=;^fO1 yyýr-rDqkL|K<.Ǡ<Z<<;,r_hm`)",~Zx: +L57'Eؽš||fU~>⽡9ϽQ2l'н3Z F +=zzr=<Aϼ^;-;*jŽ@!%C<@Ľd=cS#>fz9>*><ԕ>]=6=Hr=(=<=L<,<o=';UμP<SH<w=HN={<փm}~Җ^J{zgd}>I"vG#7࠻O;;H,^H8
:lٻ<q<=ʽ<yw:#\j:쐾M/v!J;
==>&>h>O>}@==b=h==x.XԼt+q)~XkO;0heIa+DԽXps1 +[幷*_?c\Dt==>1HF>>>g>R}>W<KDļʻ_<=$>:p>TQ>l>j>e>=U=0eꩡCK*»oǻT6Aqj0xeh sC +>Zu>>Z=G==_={=B<<<lzkټ⛽1u1u<Nz=%"齴X^%o i 0iG齂UaQ +=3<=k===<=,v=N<<A3o!
нodǼF@]D
<pe?="_=(>'٪>Iō>Z>;ۿ>g=R=SS;]*4.A&"wmhO (@r UPq=MRj~+^&9:92;1<*=w>g>=>g >{UH>1G=n<;Z$Z7ҽMBf;i-:@#ߴ +<M;=ò>>2?F?Z?.>b>X=C1<< +LikW,T;(;<{j'<$Q༟9μm6ؼżxȼgҼdkZ(+}v{"<<Ȳ=n +=ʞX>J>`>s~>>v6><01==t=V0=1<Ƶ:*[j:!l^~Ԣ;Z;Rb<>'d>o?<}?h[?"??:>=W;_|3
<N=t>2>OZ>a>,>ik>3j>
=:==5<><=7$2==T^U<N-#ؽZHsڽ`YXd>d=ʢ)=~==*=#[<<!;8 t@髛g"'fN%_i:-,x@`GNZ@zؽ\
?;+=\>N>Wa>r4>s>: >>N>*
>r=LU=k@=Bˏ<s<Cn9b`kJp驼,Ѽ%E<5S=S=y>44>==$'= ;#O5]L:o<<C<|8LIh[Q7<x,;':q[<</=>`?$!?P?`?}?2>e=Zn=<P<;< +,+<N]<=!<J<żzwz{D{?pVxX0dhB$I"L[;'<V=4=D=;>Rn>=և>x>Ю>B,>=GZ=.1=og3=9R#<<<@:e%<,W<l<<?PF< vL<k>}>y?!?u??ږ?:,>(>D;6lT;&^ļo<E.=>/2>8>=V>====M@\;˭*@<0,=)=w={=E}<X^'5yl+WN=ݿ=$=*!<<3-v
Y>A>]ҽQ䨻
ϻ<:@=/==Ͳn=°;2fP2h
#^B=-%>9C>{>>~>>>K>2>A +>N4]>YӜ>7==AJ+<<<fx:79i/uuX:PCۼM<y <ğ==T;F===g<]r]OyR;<x<<;b,Ի55; <<=U;nu/<LpW<]K=i>>U?o3\?b?X?]?c>[>7&=Q<Ƕ<> *ѻ;>S;P< +c==>\MO>.?)?;K?1 +>?>j=:=阽H,xὧ>Cyif<Is==Z+=Ee=_;=O}=i<<WR<2;KϼZ<r]=Y=z^>mE==6N<w'Br2 >p
=!|<a;2;7DcW4Dpnq>?di@PCr՟3<8=f>%$>,>=|CJ`Y}x6# =4>XP>0H>>T>X>f>Z>V&1>]Ŝ>P>![==7<|^ +> >!``<\<-)<<`jd;Ph<D<U +;~J;(1(jZݐ}r
k+Źbӽڗy<%]<$<<;< <[=R=A7===|\=ڼ=R}=o==~Z=6*<<@<% +<w=2x=%F<]E<{=.&>|>[>8>G> f=_;[rN۽~$v}&4yI<
<Lv<^|J;vɼ1o:҂;x;==R!=>3yk>BO>'$=|x<&;(J)~ۅǽ]=xkP=_<Ϲ<Mb<9C轑Ľg59}˽Z͕3;="K=>S">B>2U= +! glR6>;9=Ts>D>{>\>z>>9>zG>G>7AE>5=0=a=!z;s_ȦT7eWCD|5+4Bc
4QJ-HQ)災}q-n\<Y`<`5<a<U";;^:ٹ<G==:L=Ic=g=M<!<g<g|<;Z<W/<C?w<<e:=D>H>_??!IY?>>=y<E<;4<5\0❼' :(~ļ0W} 轖Iet v#59&I[<G=21=5<<,';<Nd֭<)<@$=%=W-=J= W=ι=p==x== ,<= =oCV=w=L\=@]ؽ)KH;==%1==d=G<Cɺ_*ogY5.vs7bsIm >[>#}6<<S<==@C=9>ax>j>op>I +ʾv8M|=Wh=>R>;>))<!;q Neв2̻0sZ]n4Ǎ3,+i<߫=x>
\B>=פ=Z). ؼ=ʼ?XN=S=>3=>oFO>5>:>z7>N><ј=R=5<;%<ũ<'9;:`hM^8mt9[% OY$k9`o$<=M=]=^L=N=v6=~M=f=A?=5 ++=X[=\Cf<%:ʼn1 +^XĽ1 +kAteI;<f<=UN=Nl=N==p=0ȴb. 7j»gFYx3̐E=Vug&cTT8~UN~gu=Ul4)=/;=~=\K=o;DOⰼ,.W_ZW/:<n<3</@=MJ=8|=%=u=;=i=3= +3<^ +;< <`;J! <<#6;5{䘄:;52B;XcT>!;AdҽgDF +F))~ʃ輟T8:iS幝Ch6<x=O_'=,ϼR~G$lݮ
7[Yu}j;';ұ<=)==Q=;=_K='<<n[R<<A~I伪 +=k=KQ#=$=V=]-<hqWߐU7oCq~_I&mr;J< =!={p=ɀr=R=݆=v=15<}y?*Dq<=
;=>=!{<N]BG?]t]7hٽt̽LTo&rJ>ӽ8ξ\<8=.|2=w=[==L.=l<;;YKѼ}wC+=(+ԅ;λ#9;;<<$;qr:|;n<9ݷ<zXM<3f +]ż_j;gT9@~<6%;T:$ڍãN2hNZ:?fM-/4R'gϽ9é)ΛŘ #C1584+<!=#=K=xУ=~C=L<<V8_<xrےwka}۽PyZAd +|NV&G:s]џ:)govp5*\μ睬`nvq:w$J4<+)<A<<X< <~84笽4xv4==â=~YF=Hٕ= ?]<==^=8뻖ry̳W 4Q(R*?l +~wi;+I<k=A=?oZ={=J=<9"dȻT+鼑:˽P˛DH8v<i%<+;K +q`.| Tcg;h<ﻉ?R:ArMF;eV;g<<<+<N<I;T9B@h%ݶ bPP'ǽ.н Qbbܼ
<fO<;t5;U:[ViD6q +pkսa1_p̽B~y=AKw ԼBۼ樽Լm4B74<]o<Xo=F=t#=u=Z<<1<<A<vi$'Xiz,,/T;v-g%?r)p kp"7kۑFBH/"ۘ½AʡН(:;<<%<<<]<<Uy퉽ʽĻ`=P=@=y=ĥ;d=I|===9unǐ(<<*8]J Yoz: 2=%]=4=/<BT<u!<&< ;l;O6G&<GɽR}P ) B%F6 JsEJճl|;G= +*ʽrT)Jh-s{Ӈ0弼vWPlr߽c0K/] +g +8 5饼}MHi]զ)ϼGpD;R<=E =V3=kC<<o.;mt ( _Խ=7v;<=\s" Kg3p=KqҢC#*9<O<ϼ +̄qz+fZvb;< @Zzɜ$G9; _:SXjoF>?6`r +7qP3!Q +%=Me=:=8^>X=i=e<CD +ڼϸ؋ l? ༳8jSƼTfM˼53F`3M%뼎UyzV9\<E=5==o<:1ꩽǽC & =a20=!=1<L0^wc(/rR i 8{aI:~K;-6@~iKKӟxw;*%o)% +MkJDJ k"Qc]`뻭3;P_(O颽<: w su]n;/<K<+S<<=j=@>f(>0>rc=g= +繽༸ռb;;6b<ш%<<=i5=j=e=a=<}y$crKÆ\$h8亽RDj$ +`ՅL/U!;<}#ۑj:|23<p<z_='=-s<`= <%]<4<_?9ԩ:W]o=!^99N;z5[=*Υ=U=>J=ˬr=佼S9p&"#+C3nH<lo<h =L=kR==dZ=]=&='=A%<:}ϽEcpHA2;7ꈼE.;&⼱V}Q3/t +k==j>8>?!3v?D>ŚI>sPb=1=< 8ͅZ߂vtJu#;#W8ܼ8*鏮tT:н$ ݽPEoh,Qh̽wT>XRb7y<!e<2<}<e{m7
ཉZݽ\< 9< :<=&C{>۽Oƽw{#W<
+=:f=N=T@r +c<l<;Y<+;J<b;[I͐d9`Ү9%:#S0=#>>&2>(G!> @=AAeżn_&Ѽ#8;1<e<CqN:hg< =Y=,==h==H=cm=5h<z;njP|dBk4$f +,p2<G^,݂Ѽ!BqHu=N=Uȼv7?'D˼nĽ3fS<=]W==c@=',<:͇˼>GS,ORݼ5l1軆嬼RgN5 +Uُ+|ؼ;HwHPF~pMהļV&d ּ5=og;֊<~c<$<ߛ<<a9A;0<:p;c;::` ֆ,ƪ&y<9=->x>D>C+>g=l +=$Vk9BF+7k5P|Lk +3>==Cu: +HڗK(!DT<L<c9<]::<RU<=vw=q=1=N=r<;N`Z ʗ:Ç3:-s\|<<lo<Ɵ?<u)UÖ0r{-CH>*mϿ*IOüN2<=^4>'>>>>]?L>&8>]>=G'\<+ZlShxʼmݼ91h0;
f<Ir<vs<G; +LOhR);v<Ր=CS=, +/iMc$<=s<z=j<=J<ν=XD<<;;~C='QjOx~;Dt<.;Ɋ<=V<=5==>F>j>@>>_D:> +7;RD<ڝ<6:Y;sF<Q<l+<&4<~(= +<<<2c)`;Q;;;'(3_P(7'PU2ͽN/@;XݽkUd\g}< +>iE>hu>o>,>>MA>3==h=v=(s+eRXb;/K$O"ev:S;ovjr$Xӽ&J!mXl(9K;;L;
qBetba+(L^%c:OL3I_:9x;a|L[!=6Ď +Gj{8m::O;є;!l;)v$3%/#z+*B8LPaQz) RYI:k½2伅?:]/<C< ]ǻfP6vʎD5e(Gw1`9Y/MȼugB<\<P<><*<j<s<c<k<d<~"<m=̺<S`;i_U%ޮ&8}$ֻ-s +ử;qWwWNu^̽cE署Od,ȧV<!\<NH< a<<ɸ=%mi=Ic=El<<zC;`;=ڠYrջ,d0Ŭ d%ѽ2GrѼ¯A:q +>z~t߽_ڽ
`ƒ;t=->C>y&s>a'a>o>K=,Y>{>t>0p>*e>#M==[=4iTռX zϼnV +ݽ! +4<M<{H<<o<<w<ĻM%mz
8 +N(\r.I 9μ+J՞.:-;A<F2=g=#<<>M<Y<Rc<=B<0ܼ̂a༴sDNX~*`YԼWPad.<'ߔܯꀫ볽[`ogk~|ܽ\GCGl/-#y2A6(QE.5IB%rJ_/ǽILm;LJ4n@Mb$֢Z*z7m; +Z#.k;k[;Q<,=%=(<x<i<H7<><B#;0K<<}k0=T<Jt<=}ܻTae'?rre63L0HDͽ%I\ *$>&?*쇽w3➼"r;8:iYRM=@)@)]M<x<T<`F=kv=u=#
=CG=^<-=-
=R=_<P)->&ͼvJƽ0(:f6:Q(̱; +e|CT|[Pc%#Pv<}=.==kz=4bz<=>==뛐>'>"2=)M=9:ݽ~H IŽi;IyBǽ&ډŦFbfSP/rս&Ԝ˼7{/ +:=G< ;;ek;;JAh"fwJ)I=)+L琽^P|2Kt.:z-6~R]2쵽pD0b(6>rǼr>} zr
7Z*-EBI)jE4necO+15(%C*WVo)O8E7&t|=)Ks[܌v=ļ̽7q蘼*"?nHX;K%<^0= ?~=&n(=#2=6=1=<<Mg;j4<<n%<4><<Mv?Y[~"EXw"}BtF:DiW!?龽 +4H +Q^'aY:E +l;0<aD<yS<ǺoVPԻ<ro*ֽ +~DEZ7WPVOȽG?m h:J9'G?M#dۻ2'XG&ҽ +;q<Is,;elN:z伒2)T$Q +Mⲽl;"-Lˈ<:=ds<Õ<`<oY< >3F>~sX=>==r=X2==o&=#=@`=.SGquh*f80z7H<-uX:t:ekZqzBo}콆ͽD%Sλ5<5< +<h<sNyƽfܼuv +<U +1>?>j>;ai>e=ۋD=R:wz]q::襻^PLIcmQ0]˽WTż}Sn
*f~bcּ)<r<y;B<ފ<Wz==M}>/K7>[6>]q>>=ŦE==l>=ڙ={l=<VJw~zi؊s#iŽ*TU4g<5`=_)=i==<QksTuؽ2"l˽;<s=`*H=A8=%K=;Z< ;w#l/ABRҽOFwüDֻgX#wZ^a1i磽k^1q1,vwxR1%ҽ*? Ja-y;d"<=WFQ>8 >>1>>yX>?@=w=:<<;&!:`F+/$◼?꼑uax <l:=QD=_=<"<k;a;;><<մ<;qBFOc&T<<P<<;p;;Nq켔8;S6Udܦ +waf3k𧽄$rEӽR\e(7|K2t`q\kw/़LuF"x:;6fUjMH)ϼT*<N=_ +=,>==>D5>o=;==C|du'ǯ;Jw;yL6<NZ8cż[ż aQfż: + +&K8m`{*ŽH:<U=*ڄ>:n>:|>}q="=y;WKXʼ;V_"躅c<Ƣ7=4= ==K=9'<?f;;3dRt((ࣽ>#[L0t~)̽`ji9ýa_ㇽet)+NȽYq;˽u2|'aOSfa~/p꼝Ru<DW=K>T?eC?'?>Y>W>>=g<;=Mɼ4O<#o=8on}K<8l=@=v?=Զ=="</<V<L7<#< ȼ_9u%:<T='<(w<&;R;z;:o<b;ok/ ~c[qb2h'tzX ;ݾ0;-H)owqY<_T +^{3;O;K;"bԼ]TL)S?;4<y=7=C==c=1==Gb;ƭq+F.<er=,=b^=y<9}BA**ʼ.1F@k=gO=#C<I;%;;9<?<=T7=Q>}>*=ۈ>AO>>^)>0I>"
=H=<F==J=" <uZwaToŤĠ< ?k=:>(>T>>4>W8==Se<y;́λ+
|:=>=_=o>=3=;ӻe*<>St<<*2AeUJnؽ jŽ +|O(ag%7A*7O'4ͽm.wu6<j=˝=f$=*,="X=95-= +6igHDBTP@^Bfy>3C98~&;͘<%=O= &==m=;==h==X=h=y{==l<<sEԼ3^VEj%=='<;c
E;O?;<K;a< +=B= +>81&>D==+=>5Β==l=V=<<|;<n<mՑаQ"(>f3-=#=>I>>T>>.> $=bv<fa<;u<6z<G~=+V=\l=:h=i=r=7u<7;n;/(<4<< s1ٽ.ԽgܽPս;ܽ#ƽN80yeVx^fsD9>WY?˧=<R<=$>)>>6>>KA>>< =e=?S=9\伹漗M nK2&Uh6Ƚ|Xak;BB^=*3==䝌=!9=y=v<
=
<<D<=|Gӫ{9W<T2<I==Jd=J<<)<fC<<C<*z=G=!=&<)eT<=U=5===h<:%G^( +"y +p̼v6((:Qլмх\i};ͷ+&;$ˤ;+1<]=]=+>p>|.=ޥ`=&=AN=pW={=j= = +<;3<N̼ޘ꼨μ<B<ݥ0=+:ս&};"Q=^<|L<^<,s=> +<8=
Yd<K<<,=&;"[ؼY켯k1<!}=3>>1h>=#<\wM~;Յ<<:2BJbc3ROt;n;TZ;|36A;;P<2<=C=s>!R>XA>N9>)d==
N=%%= +u<1=&=4$<V<>`uF.jMѼ>cͺnN^{;ZH<4;ʈ=+̽<<<1=v>,Vۼƻo;NMbVo].Z6GمTf^dzڻ=
=H=>"D>m>>> +<I<GUּkpֽ괽6YRrʼ2w[Ya½wSUY[%8C([5]x}1Q<_;=Xǻ>mIط7ٻ"Q<Y7*Nh
9;3:K;e<F=^ +bcס@K۽0ν@i69B3 Ƚ2"_غR<=i=_B=l-==>x>F4>=Im<B=
D0<Oݹ|| +\:=ϼ20u7Sw c{|h1WX悔 {헫=z07+ʽZƂ;ἙU;!+<H<9t:G=b=>=J<hL<$<<<x=n=>P=?:YX0'R;c +
ȼYkK<x"=6h=$Q=<x<Y<u=Ky=2
<Ʀ<<;ƻ_ +꼜sL
<@0<:<'<'<ž<<ސ<=!=k<[;ՙ<7 +;U;*{;;S<'ē=5=-fs=*g=%<λ4i;[x")bAy;|;jqb;[5;8;ɻ=K:<a͈=w +> s>D==n<z<5 <8< <[ϐR2/=2͖T<9<Z;@ʻۻ3;7<f<:Z<=)7=dn>?p>c>>>;>]@L>q5= +ҏ<0;$< +<:͵<q;cs<':j7{мGR=[û +=<B<Q<%=,-=$=
=3i=l<R<pUL]~,(x.۽^f.%x^߆o㸮<.=^=l((=`1@=;:e<<A[=;<O<k<0ٸ_W/4)ql2~{nȂʽ
?ZxUҽrTֹ_C3Q4Lxf=l=!Z=~<zw WY-j<|=Ѕ=J=D<=Jn= Q5<=<M===^O?<ɸ;W<;v4M9|+<c<ņ<%<B;l:\<o<B<o<\<};ΓR$hhN<9<h<=\/=<C\<<:;=I<7<9(<<|;Y <q)<Rm;R;ҩ;s;<0<tf;E7;U.eWxG6X aMw'!:#;Șc:u߮
džӼ)<q=}=="=L<S<e<!;k;}:zٻ* uMbJˉ&o +>>j>N>M>$=tM;WbJ92ځtO$y3=;&_ +yU;u<<=?<ٿ=Q=-?<l<=/=!z*cpD]l]#=R8ݽ߽π";r<oa=K3=2:<Ŵ/8t;<O<53QM\C+ +E\VB. !,PϽ쌽Nl^:.0t[x=^=zx=<Cɽ);m%<QL=yq===Q=:[=n=O=>==1=c<2<<,cL<]<?-<n#;pF;;!:*.c9;8';\< I7y1Wzֻ!Z+<= 9==3=a< ;PKiqm^G<i6 Eeۃ?}n;R +9Ļ7A))ؼb;a)<=f=[T=F<G<֮<qHd;%4M+F0<nP< 0Ɨ|s>ǽ8 +jRApRvC2=>J>3>}E>V[>>{9 +nO:<7:u~/= =ȼ +Yzsiɼ6)7[:¼a?PTIŻU#;$<o<6ϼIY0A,y'H0C9}K&U5H0=;ѻռY!7="=6Xw<ٍ<T8j=PxjwZ==V==j<<=,K=3=x=i=4<ݫX<<&<i<~R<;u4ἎBλ;KC8;co)ݼ^.B;x;;@=+M==>|== :`lP kҊ$Ƽ<։.hq;#<I'<]4\<9;o>'Tg+mۻr0u֩;ά:<><=h==?=#S<c;mD;;Ӥ8Hթ; +:z;<-x=1=&2O<n{<Aia<*Qo/ =n<P,/ۣW_:C8ֽ, 5m̼Gn !2nA=)>>@>|7>w>H$V>M`==ǐ6 -暽 ټy
o&;I7LE@[ +1)~(FΫ;} +l=2=L=7>; 4=1>k> *=6=>=1S<=D=#==v=aB$OR-HAiq-=^Խؽaʽ6+4F~</= H<[<ƃl?ڵ"ͽ[;+3;C~<7M%<2N9#ʼH=J&_%v<:!h;9yoL ;?SS"rȼ9m8#Cdg$ȼDjcܻ6FX=&=;,p<[=Wj=W=;=zl<P<I<,V;;L<;;Dċdy+/-m
:İ 1o
rT;;=ٳ;5<33K<=s=n>(%==A=2.:x9jzAV+1+ +s<A;h:|;Sq;^K<1\<'Hu<.562 ++òѼVf?=+6:ԽIGG^*`TEݼTF!<0J=K>n4>u>J%>*#;> ={=<3\<
<5029\wyNOepż +Z}3K(x/,<-<<<::9{#r";!g +4;<<J_=U=<;]"46N-AB"eX?sѼ>JU` +:ӼD@ۅIA ػMӻμF'V_<aW<v<~=ٹ=S==R=|%=R=I<`=D='Ej=:H;wcܼ@-FqoD};<!<w<?==
a<%w<EB<2==C>6>0>'S==@~;XTI(Fͽ:h<l{0<I;!uQ69<s_;A<E<<i<Z<d"<YF<<ȶ<z<s=D,==np=^]="<bI):ݼ+zf;3G;_ARZ +yD<;{;O`Իм&WEDyԔX;h4Nk^mf%0nin=w=b=(==f=u=7==<;=I;<;<9oknjJ:Z:O.<j&;[f;O;BL<;)<<=$>=]= /=x8=</<v +O(=R==@<fϼfv
x-o(
Suj'|{;2V};;;ڔ<tZؼD`=(8F<X=*=)?=h&$Pٽ#Z⽉ܳ{\oC8z~X" +k=z==e= ==P<e5
< +={=b;0˼/ +Ye"\;J<~<<E;S8h;<>d=Zc=z=w=uA=/7=
<w
`<h;<<Sc<Lj=W='w= i<%NI㼸8`a_dw!TI2⼾1f{?\|6kټ*X09c۽*;$Aٽs>`m~s}
ONڻ(=Wff=*= +r=q==A=r<<<fz;@<X<cm:o-;_`y=7=*V%;;=;9B< +=#=<QgN=I<x/yU]+itؽֽ퍱o|ғʽn9 +=3=&=Y=rIT<8
So!=ʠO'u<[H<ܙ|=<= +=,,>
4"$ DXw60]39=(zA˖w欏奻0q;
k<N;[ˉ]YlvD;<Y=J=8<<H<K<= =\>$*>Y +E>K7>=sA=s~<Xz;g;|;Bؼ.'3^<{<V<5<AJ):J<O=TO=>1>>5W>1>y={<S#F#c(ٽQ!C<<<Bu~: =?===z,==5y<X;xq ;0u3;w;7;Ԗ<<{5,V`lZٽ_ټż5Y{˽^{(|I|ѽv+ؽ +w\(p@uTm=bp=<0<ܮ===^b=j<n+ؼIH;6$5<!<g,7=̻qi⛽* +~ȼ#q NzG*6+&/==U)<&)
ޤx[J̎Ҽ\M`oΙʀH˶ᅟ罿ƽQ;߶>0Y>Kg>)>ӳ>PH=Y=;k*<&3c<F +<ˣ\=.>2>t1>zf>== =$&==04<Քcduۼ':d4o"_<=VH=>~>=§=<;(;Žd\˽ ⼐'&g;zg;tA*4; + +% spɻq&ݼuXսPr;xl<ٜ<'<<0.<=~=Ҽ=p=CI^<CJ<~c<,<3#PP;SX+Dҽsーp.5X`wVPȽ\*}=!= + *(l7p̼:⻥Ӽ-#¼0Oڨ|4R +0< <Y< +<ػ[\0-Ty7:2{"c,(F0dNB@_}]s4u^h(~`)+;<l-8:d=ƻ 弢ct`&<vaٺ)Ey0=5=Ll=<'<~=@@=zQ==/=RН=?<R<<Yp<_;<%>(8t6$מo3z +オb=]j<:J +ͻ<;o<<tr8= +FL>! +Pټ/ռ;W<Q< $<{]f +;齜:̯<D=K=a# =>>Z>}>2>K(==n;I;)b<@?=<J<t6bͺYؼծu{R*s-''-;1< +;J)V7k$=>@p>1=l=/;M`ں:e<<2ڼPuO
ý +X+=<>=fp=UL=<;p';+==4>$f(>P_>pj>~>[>}><A=m= +mPh1>>Z
=c<;<<;<u<(<,]y&p,?x]i0b +r缇ɼ*Z=G18=r-=٫==q>'=s=Y=t==}>/:>=[!U=%=ݷ=B|==SZ=h=<c</$;q;Ɯ;k +l7<ou=< +=2=u=],u<;+/d==N +=);i2<p<;k<I<0ON
D[VKbNJjSx kupV<F<L<Q<s<a==b==!<VջYP̐x@żwo7<Y+1=LA=lW=O<XȻ[c%{]<ý+̬}s\]/:YpD|Ei U>< +2
A㼽7;@< +<_-;A8o2'ցQL
#KXKȽ/;ѽҳ?7)ơ.ɽcU;1Hv&Լw˼"E;<n=y=ˌ>6=,=<9v,;=J=>S>i>g>M],>;==כU=<[L<+<F<}=6<t=y<-Ҿȃ)6q;s|ƽ<%$F*tν|ts=uսI/r +鼅~:g8u_+<Z<0Ի/I~ʽ +U="=Bi<(Xս0BY~&;k;&0[͜Xl]^gXsR.j~nݽ/L}T@"<s>>_z%>&==bn,wQȽ +=*=<˸ɽ3D'z)648j؟|(mZYcp;z<w<`3=5=a|{=>=`$<d<1);~ ;><Z<CѼJj1!#E('8Ƚ9 +Sy\,m;6ԺI9nH9)_֚iɼӷ(DQ'L.~s7WIf;+DI<Un<=ld=V=Fe=L*8=V1;͘CC:Ӽ)Gm+<"6=F=q
==DMm<% +]3Mo:v,% +ځm k;7)Qr:ڼDӫ<A1<=z =|B=A#=U1<`"P;&iffn<;3{>?(1hlt
8#,şqt-9૽cר/dT潛pO
H㼄a{R#Ӽ +MD";h=z>>Bw8>1>e>8 =]&;;Cw˼!='===7=Wl=,=o,N~:e`̽ڽYk27e=@yqzΰd,JXM:LP녽7.(N4V<j;`&>q>d>=i
=wB=`O#y<OIWX,i*ý9+u<tcq#JJCRQVʛ;i=</)=gr==U=Y=<A;R8xwx{.x_Rb٪4B5 +ЧD7 b
+Il#}Nl\Ҽ9&[$p1ͼ<!<yg(=N= +=ÌT=x=8H<y<<Z< <<0<$<F<x,&F +p۴V&BR3ⷽVUFy9S@AA^<t=2$= 7s=<<5\BȀy\<=RI>t>Fo>z>z>1$=\;%cHI=J=n=v=ʖ=}Q=J<l;;C)q\~+нia0٫{{N`|jcO<m~y)i0G=AYӽR쳽{"GE>7>?>q==W>#P=J;[vI84:~(+U6g7`4~,r տ*tC<AW$=y*==_<i<:P<QEnW.k'-]C3>. e)\Sj^ +; +<T0<@ +hz4Ѽ^SC=@=-D@<C=3=!<q>ZpR1J=!>&>]>,j>]=>hy>!0oT兽IqK:U/Yz"w^auI'"쓽-;mq<<9%i .R* čT?=AN튽<Pqr=&{MjѽcS%)e| +*B'<=˽6~,7T? j1]ɯ<ܻ<E="HP<:<#<Ui<Il<)<;MJ<<_<R!<l<6`=Z<פ<C:[ܽ"t.%l!%3yx*8W2(нgwwq/;=x=N<> +4=C==*"<ݼt:1s-_9=u4=\ҼD\6h<bC<=JA=n =`<@^:\ӼQ+e]vSn舽p9ǽ8zGa,S8
RüA93=S={=ͣ=.a=g%=C;澽z%wӽ>b0==Ec>,4>>X>^M><e=1p<8ݸk>?zIǽY_e#ņ}9;9Pd(jF9䝼s.N꾽a{l:zyѽja&`uzUz{R^FݽIǽ<:}9样)<=B
>p8><==U=O|H +ǼV;V< ;3Z;G69C~,L"kVEX!x+f2$Հ; +St +Qνѽכ/`-2VDV:<<g6=E=J=W{< +Ⱥ;)#{;<;8KARX& +g~ebd!ż)s-@<.9!dV, +3l}F 4ý#)uĽ9hgBrdof$'9<^H=EN=I=9=h8=Kb;_;!:'.:t;
<rp='}o=\=T===!<Jc̡,Hټ
锻rű<1<<<`n|JhW"(k;<Z=<=+>3>?h>_e{>g==<s8zl)ܽXZl֜NXVI"ټ)WNJ*O;W;]kj nPao}QD xwe:<g!<Je֔;;SA?m<a<66<ܵ<pZ<^:=<F=T=9==U=4=<Y>5K=ú>DvqEoƾG}̼Žy1";H;/t:[/$A;< <[FmF!= +"ڙ=nf;]`F{?g$t"7Ts.D~w=D&t>V>N>w>>>=S+溝7MBcM#@("?|k;8<<<<tT\4]ݠ7熼S~T:!;}JmKNSW&!rNջw7ni:M:̓A=Y=a===T=U=o;;B:Wv<Ou<=-?===<S=mr=I";\N:_P;"<{ϻམ=43=5W=.)=$9n<@"ݻ~8u5P<Ć=\=P>|>>/?>|>wۜ>=o;ѼR'N!ǽ҂
ڇ?ɲj2_LSJ0_.eT;U;h:;UN~9Mm KEӽmսCݼVp듽iQaɼ?qb<<p</7ѻ<$<_<<iZ<ȑ==N=~=.t=r=}=l=e<QIཤ⡼pbnRYXOja㽒.v`C>m<i;lO:ʏ;+'ż+c2UUٽQn{=μ@~Q2uc] +F{/7<*F=?3<Q=V<Fm<C;cȺW3-vC彀#W|dc6n9>$=m='A=y=65A=*2=<a<<=W=w<%<&=%=l=v2=qe]=#IGK +p_}ԼkϽ +\1ٽ2(;S</mY<<O7(<#<[<J\Ye.RQz;<-;<q:,-<3k!"Bx:=/I>>>->>L>$>=>%<dcjAlUbxJ<=aC=ƅ>Z=Kn==v<<G=Rp<nj:#x2
u\kp%M6-Zͽ4Zi4=G= /=o#==SZ=H`=='s=M=t=x=};F*<h*= +4<<c<f<fO]/V]$p"ϼPؼhfARv\8ϓ䶴˒֨@'ꗻ +=+E=)=R=]x<]
<6> ==D<۪<eM<vT"(<Dc==Q6ݽܸn7r <n%Yo4ֽ75z<<=|EӼNֿe=ف"഼k|y;}:I<= +q(v +)Ѽ.,q:@<`K=AdU=fc==S=ϫ=J=$N;sl0+?l7
'JEȽkwٶZB,'K;rM<.:=eO= r#<=t=4 +'=n=oX<Ῥ<<:l#:;<<t<c;76:xMgwÈͽN'B屽<ٽ@q-uJڋ9UͽYн?zH;мҜ2gٽQcʬ<c=>f`>}>xJ>]C>m>g;=C=8<kPgDov0[=<@=E@i= Z==[<ֲ<<:al=n>+>"b=]==K:k;q=.=P=v>==Ǭ=Fo"\?:(<hn0Ld<y;m-=Jս'f <$=<B<y==w$===y='a<=M< Ӽ0Ҽ~<: +2<I=K=I=R8=q=?H=7q=o= +e5Nj0Gh.ҽNd^jp\琽>a9';==n$={j=Ȫ===D= v==[-=t=°o=f=L3=xOK5c<K=|5={<;f&iTpغ]y==V=Q=B==LM<:=⬐<&g=<Ưo=5s<F<nd=Y<A<`F+9Ὄabɨ,1)g^&Qy\NiI+exEo%D~x60K&.<'=ȟ>*s>FR>>s=<= M=q<-S磼iW缳ۭ:87"J< +=c=^=<;Ѽ*"<֞=Z=F<Ek<>> +>={=?b=ѧ>>MO>N +$A'p3rJk^hT6;,U;ӎ=!:Pwr̙gqL<$*==;>._">Taq>@> v>V== +=P=ӔS=F=h;#L7-&,*̼@0*wGa<<=s=Fs==x8==|=j=Z<?V<====:>:j>>&>$>6a=ӻkT_[\\o¼ݽ5罽#Q̽O\<_=jz>>$>E>K>J>=<a[L~&&c_@<۶=&=_"=3=4;= +;;9q<o={=+==R=L"=q=-<|m<Eg8p/:yg;<s=f:>>6>mi>|>b=CА?FL^|nrk~M7x.C=9Y>%97>?4?|?oɸ?,>>=e=}n<LQ1<=>A>I-B===F}ĬI%^Ѽ-59lLnFAKPM/>4+pN$:ھ$4=D^==2=5< \ɔ;n<\f<rq>=pK====Ê=S=<:8< d;<g(< +M<\<M<Ą<ɘ==<<(/:(P<J</<ă==<<N%<';QˇF`o]@be;le=8=Wo=_i=a@>)>/w> l=2Y#rнѼTٻq3= t=R>=-=tJ=
=S=V; =s+;Qq`Lv%_ +Հ=gy==a=Ա=<<;'H7ua;L7<=K>>$\>=ڏ<<o>ҽZ\D0Q0FlѭD+;t=>9X?OC??"??b̫?<>O==GLɼ; +=0>;">>">v>6u'=6ɐ* zu5K꼽VMCz=w}p}S4!U,M?ɾ*XVyP9t<ż#:T
=t=G=zD;!;6S=_=S= +=}=-o<a<xo?<<l<J'<gL<<e8n;:<]j<`=pUU=\<=/<ht,<
qrcl[Όr5N?;(=+==>!>">L>n=*Z۽)|}/
:
2=UZ=/>>ؾ=s=<R:"wyq^jbEm"IQV':%5K>KL){GU+:}E<@=-=;==\3<;;lȼع7 +dPӼ"5&ֽ+aɽB+$[w?gSϼSa;d"<mq==9*=,;x':Y]aI|V;W<O<<+s4_Iؽ>|2u{B6WnQqB꽃ڽH}ν)뿜=H>{??;<?ŝ?&~?@?r>s=<PлkJ#3;.<
=e>/)>#>V>uA>S=Xp<iffIV1 Lǽy0/[_ ^M +=8Cs95 =J7=|=U`=f7=-<&=Y$=J=P=C=\$LVI7SL;[<= GI<5Ћ<S<=ߊ=/=O<p:M볼p40[3,9rl:<' =W=[=@ ='< )<<Y.J<&Pd||I]a=!A=ߙ>>#>=w==<9T`[ѽo8b+[B-ǽ< +7t̽XX1o +<`Q<C<|J&<<1b=/"<ھ3;i Q*7!F+eԼ5ytX-zZ
I=QcVL@p?X0ͽI&wk7Mwٯ%W{/+XW]*eż8νOf*5",j'P[OýcͽsнbJʖ#ۻlV!X=9+>>>az>'K#=s<<a-פM@ed>y/;l<y<<nE;<(Xpo<^=L==<O\=t 02ͽA$,7˼9V^<Jѽľ'Ym=$===,|==c=p[Y4*/G<iub`d
K%[=d= +<uD<lt1U=q)=W<]@<=/%=<\<9;GucK/tNҕ,1Qn<;<X\<fl<s; +tYlWʽf@Aܽ7 e2 ;j҆Ӻ+Ż{Y>e#-5iٽYjJ
/3F͊B08w$/W!Y +Ľh奮>=!j==N=<;ν0
kֽ%;<xM<[W<v<;ݶ=,o6nIF3<VG<<<i<<=<6E;^;j4+H)Y',Iea国V>DS;:<
)OÚ꽑B +=,=T< +{3Y1==X=;"<]b IнQuo=58=={> == +뼒6n+;<=h<<:;C<} ǁ^G +ټtĘZyN
W)~:=wٔU;fF7y|韼B.'y&b彡j_^dB%rR%=>3Z>+W>%>I>>Bk>>k>>N==s +s;c:<a>=%=˕=C=Ԙ=;1ͽ'YxxMXsҽ|ͽфƮ=D(_uF;^6<F<x<<zAH:#vr#ZνrDqEo̽JR⯽w^⽍g;3ӽ +kvKDR<f<_$?kfL$E2VD2}/=NQ`>n#<*0ݽ@z*FD߽)߽SrIyq1A@a':5;_]<+<9lPL], kOؼ=ӼW~kQɺ5._;3+<$B;x;
;y((~){+6rSWІ!87B`+$<)>.CH>;b>Wf?I?yc0?^J?A$?
i>>[~>>m=;u<V=,=y=e=΄=L'a;̽>tmMjsyVC1G@֦|ڢ=c^P<pl<=<`=<w&+4v<*ݽ㺽!58I^aL2gPld~X~2,wҽ<Z[Q9Lϑ}'qRJJ5;#@+¾:⾅9 +~O`$:N +4bDͶ]K8ݱt'/Z*
;-<ںT +=hs>?@5@@@@"]@??>c>^3=<Q$Nؽ@M< +#,aּa Fp=ɓ<?[̧$uj<]_<;=݅M__5A3cTT}a
ʼҝ [ Ƚw}GЅT=}Gbd<";µ=W==y<%;ڵ< +:,Md~[$=x<=q^e=Qq?e@@ԳA|CA-A"@@.3?/>;>~=(;./
]ҽ56ĽÑvld
Iy_:[6Ͻ,w8촚@E;=?<a#<1e}E1Wڼs*ksސ ;<R^;F<<Þ<r&hս?Լ̼`= =KV=->a=(=$=4սVս2"|y^ZD +i=Xm=(6f@¢n2$<:&uf==\=t&,%ݡ<^|= +R=6=#]<`9h'漗u9dHۼz*I;;4p<ID<ۯ<@<'F$q( +43*9$W=ڼ=XY<#ED0e; +X==<iX<6<O;}㒼;%]+5j65]3W;vk;/|'Fg;@< +!L]R^$
!J*CV43z~@n*1`:¹=rjo=>T>Q>?ܜ?ɾ@7?9?>}>[===O< +˽S;~ٽV\[wG½ĵȽd걼ë"{=
=<)<@U<NY<%=M=O=23-=<yU^/3).Ob:PJ:;v[;5;A<u<_<J}:kD
<wZ<f=VA=ս>>t>xb>':%MOF,$־!@<ߦ?a]ZֻEQ=sj=&e=_;b0 o==eQ= +Ľ' +ռ|.ɽ νH̽m=ɼûxxlN< <2=s>>]>q +>x>|>v>><>==T<pWҽyZ*(y"UB[uŽz%<r<b9V<Yx<O</<~<<+,=Q<`r;獑5Tշ1`
><>j<i<<<Q<<b-;Z;);<$:AUp<)3:茌=U==V*>|P>N=>-V.A6ݖKV=7>#=Z}!;٬<:D<=U,=V7<=,c= +O==m`?=:=-y=2=}=eM=w3%=<A<?!!O&pveݿӼ˧¼4܁ἝWThć wq786i;O= +=Us> +3 "p=V ΠH8s):ww:<ڹ= .=u<;[WfGR+´@&E߽{+hr|q"}9<cѼ;G<vC= +'<<'ջu-ӼSoS;M3%;<=#{q==r=>\=A=Ϡ=>!%>l>f>=X0غ<=Ct{== +:j;;=_=2h=1
"/^SuW~Pyw=6=x<A;ėWl<FQ* TL;;0peνld*05_&%KC; +Sρϼ,ȼrX6¥qWmd`ri$bUϐE#ժm9jG-vb/K <^<=;l;J=+<X<-=== +5D/`p5 :84I]qCrż~gsWG`ۼּmkqVƅ`Riӽwн
eBQ'齟IUfmXi=S+* +ҽ!~` +ֽv!x}n<> +ߺ>B=ؽ$!eN(<);<A=i?=W+l=*=~=a=i=w=7@<5 +=/=d=9r|=oV==-=kv> +=0dRqn<U1`dMf+N|<J=!:<c<5=3=Q>>>C diff --git a/lib/test/data/2chipA.fits.REMOVED.git-id b/lib/test/data/2chipA.fits.REMOVED.git-id new file mode 100644 index 0000000..50242ca --- /dev/null +++ b/lib/test/data/2chipA.fits.REMOVED.git-id @@ -0,0 +1 @@ +fe2343477504847a56ac369c96fbe2d1b9d835db
\ No newline at end of file diff --git a/lib/test/data/2chipB.fits.REMOVED.git-id b/lib/test/data/2chipB.fits.REMOVED.git-id new file mode 100644 index 0000000..1d70532 --- /dev/null +++ b/lib/test/data/2chipB.fits.REMOVED.git-id @@ -0,0 +1 @@ +8a32e201693484546f631c52e3774dfbf278151b
\ No newline at end of file diff --git a/lib/test/data/2chipC.fits.REMOVED.git-id b/lib/test/data/2chipC.fits.REMOVED.git-id new file mode 100644 index 0000000..7afd241 --- /dev/null +++ b/lib/test/data/2chipC.fits.REMOVED.git-id @@ -0,0 +1 @@ +167e2502832be1609cdde29c52e9dc8c571cc3ae
\ No newline at end of file diff --git a/lib/test/test.py b/lib/test/test.py new file mode 100644 index 0000000..c6c1fcd --- /dev/null +++ b/lib/test/test.py @@ -0,0 +1,260 @@ +import os +import random + +import numpy as np +from numpy.testing import assert_almost_equal, assert_array_less + +from sphere import graph +from sphere import great_circle_arc +from sphere import polygon +from sphere import vector + +from .test_util import * + + +graph.DEBUG = True + + +def test_normalize_vector(): + x, y, z = np.ogrid[-100:100:11,-100:100:11,-100:100:11] + xn, yn, zn = vector.normalize_vector(x.flatten(), y.flatten(), z.flatten()) + l = np.sqrt(xn ** 2 + yn ** 2 + zn ** 2) + assert_almost_equal(l, 1.0) + + +def test_radec_to_vector(): + npx, npy, npz = vector.radec_to_vector(np.arange(-360, 360, 1), 90) + assert_almost_equal(npx, 0.0) + assert_almost_equal(npy, 0.0) + assert_almost_equal(npz, 1.0) + + spx, spy, spz = vector.radec_to_vector(np.arange(-360, 360, 1), -90) + assert_almost_equal(spx, 0.0) + assert_almost_equal(spy, 0.0) + assert_almost_equal(spz, -1.0) + + eqx, eqy, eqz = vector.radec_to_vector(np.arange(-360, 360, 1), 0) + assert_almost_equal(eqz, 0.0) + + +def test_vector_to_radec(): + ra, dec = vector.vector_to_radec(0, 0, 1) + assert_almost_equal(dec, 90) + + ra, dec = vector.vector_to_radec(0, 0, -1) + assert_almost_equal(dec, -90) + + ra, dec = vector.vector_to_radec(1, 1, 0) + assert_almost_equal(ra, 45.0) + assert_almost_equal(dec, 0.0) + + +def test_intersects_poly_simple(): + ra1 = [-10, 10, 10, -10, -10] + dec1 = [30, 30, 0, 0, 30] + poly1 = polygon.SphericalPolygon.from_radec(ra1, dec1) + + ra2 = [-5, 15, 15, -5, -5] + dec2 = [20, 20, -10, -10, 20] + poly2 = polygon.SphericalPolygon.from_radec(ra2, dec2) + + assert poly1.intersects_poly(poly2) + + # Make sure it isn't order-dependent + ra1 = ra1[::-1] + dec1 = dec1[::-1] + poly1 = polygon.SphericalPolygon.from_radec(ra1, dec1) + + ra2 = ra2[::-1] + dec2 = dec2[::-1] + poly2 = polygon.SphericalPolygon.from_radec(ra2, dec2) + + assert poly1.intersects_poly(poly2) + + +def test_intersects_poly_fully_contained(): + ra1 = [-10, 10, 10, -10, -10] + dec1 = [30, 30, 0, 0, 30] + poly1 = polygon.SphericalPolygon.from_radec(ra1, dec1) + + ra2 = [-5, 5, 5, -5, -5] + dec2 = [20, 20, 10, 10, 20] + poly2 = polygon.SphericalPolygon.from_radec(ra2, dec2) + + assert poly1.intersects_poly(poly2) + + # Make sure it isn't order-dependent + ra1 = ra1[::-1] + dec1 = dec1[::-1] + poly1 = polygon.SphericalPolygon.from_radec(ra1, dec1) + + ra2 = ra2[::-1] + dec2 = dec2[::-1] + poly2 = polygon.SphericalPolygon.from_radec(ra2, dec2) + + assert poly1.intersects_poly(poly2) + + +def test_hard_intersects_poly(): + ra1 = [-10, 10, 10, -10, -10] + dec1 = [30, 30, 0, 0, 30] + poly1 = polygon.SphericalPolygon.from_radec(ra1, dec1) + + ra2 = [-20, 20, 20, -20, -20] + dec2 = [20, 20, 10, 10, 20] + poly2 = polygon.SphericalPolygon.from_radec(ra2, dec2) + + assert poly1.intersects_poly(poly2) + + # Make sure it isn't order-dependent + ra1 = ra1[::-1] + dec1 = dec1[::-1] + poly1 = polygon.SphericalPolygon.from_radec(ra1, dec1) + + ra2 = ra2[::-1] + dec2 = dec2[::-1] + poly2 = polygon.SphericalPolygon.from_radec(ra2, dec2) + + assert poly1.intersects_poly(poly2) + + +def test_not_intersects_poly(): + ra1 = [-10, 10, 10, -10, -10] + dec1 = [30, 30, 5, 5, 30] + poly1 = polygon.SphericalPolygon.from_radec(ra1, dec1) + + ra2 = [-20, 20, 20, -20, -20] + dec2 = [-20, -20, -10, -10, -20] + poly2 = polygon.SphericalPolygon.from_radec(ra2, dec2) + + assert not poly1.intersects_poly(poly2) + + # Make sure it isn't order-dependent + ra1 = ra1[::-1] + dec1 = dec1[::-1] + poly1 = polygon.SphericalPolygon.from_radec(ra1, dec1) + + ra2 = ra2[::-1] + dec2 = dec2[::-1] + poly2 = polygon.SphericalPolygon.from_radec(ra2, dec2) + + assert not poly1.intersects_poly(poly2) + + +def test_point_in_poly(): + point = np.asarray([-0.27475449, 0.47588873, -0.83548781]) + points = np.asarray([[ 0.04821217, -0.29877206, 0.95310589], + [ 0.04451801, -0.47274119, 0.88007608], + [-0.14916503, -0.46369786, 0.87334649], + [-0.16101648, -0.29210164, 0.94273555], + [ 0.04821217, -0.29877206, 0.95310589]]) + inside = np.asarray([-0.03416009, -0.36858623, 0.9289657]) + poly = polygon.SphericalPolygon(points, inside) + assert not poly.contains_point(point) + + +def test_point_in_poly_lots(): + import pyfits + fits = pyfits.open(os.path.join(ROOT_DIR, '1904-66_TAN.fits')) + header = fits[0].header + + poly1 = polygon.SphericalPolygon.from_wcs( + header, 1, crval=[0, 87]) + poly2 = polygon.SphericalPolygon.from_wcs( + header, 1, crval=[20, 89]) + poly3 = polygon.SphericalPolygon.from_wcs( + header, 1, crval=[180, 89]) + + points = get_point_set() + count = 0 + for point in points: + if poly1.contains_point(point) or poly2.contains_point(point) or \ + poly3.contains_point(point): + count += 1 + + assert count == 5 + assert poly1.intersects_poly(poly2) + assert not poly1.intersects_poly(poly3) + assert not poly2.intersects_poly(poly3) + + +def test_great_circle_arc_intersection(): + A = [-10, -10] + B = [10, 10] + + C = [-25, 10] + D = [15, -10] + + E = [-20, 40] + F = [20, 40] + + correct = [0.99912414, -0.02936109, -0.02981403] + + A = vector.radec_to_vector(*A) + B = vector.radec_to_vector(*B) + C = vector.radec_to_vector(*C) + D = vector.radec_to_vector(*D) + E = vector.radec_to_vector(*E) + F = vector.radec_to_vector(*F) + + assert great_circle_arc.intersects(A, B, C, D) + r = great_circle_arc.intersection(A, B, C, D) + assert r.shape == (3,) + assert_almost_equal(r, correct) + + assert np.all(great_circle_arc.intersects([A], [B], [C], [D])) + r = great_circle_arc.intersection([A], [B], [C], [D]) + assert r.shape == (1, 3) + assert_almost_equal(r, [correct]) + + assert np.all(great_circle_arc.intersects([A], [B], C, D)) + r = great_circle_arc.intersection([A], [B], C, D) + assert r.shape == (1, 3) + assert_almost_equal(r, [correct]) + + assert not np.all(great_circle_arc.intersects([A, E], [B, F], [C], [D])) + r = great_circle_arc.intersection([A, E], [B, F], [C], [D]) + assert r.shape == (2, 3) + assert_almost_equal(r[0], correct) + assert np.all(np.isnan(r[1])) + + # Test parallel arcs + r = great_circle_arc.intersection(A, B, A, B) + assert np.all(np.isnan(r)) + + +def test_great_circle_arc_length(): + A = [90, 0] + B = [-90, 0] + A = vector.radec_to_vector(*A) + B = vector.radec_to_vector(*B) + assert great_circle_arc.length(A, B) == 180.0 + + A = [135, 0] + B = [-90, 0] + A = vector.radec_to_vector(*A) + B = vector.radec_to_vector(*B) + assert great_circle_arc.length(A, B) == 135.0 + + A = [0, 0] + B = [0, 90] + A = vector.radec_to_vector(*A) + B = vector.radec_to_vector(*B) + assert great_circle_arc.length(A, B) == 90.0 + + +def test_great_circle_arc_angle(): + A = [1, 0, 0] + B = [0, 1, 0] + C = [0, 0, 1] + assert great_circle_arc.angle(A, B, C) == 90.0 + + # TODO: More angle tests + + +def test_cone(): + random.seed(0) + for i in range(50): + ra = random.randrange(-180, 180) + dec = random.randrange(20, 90) + cone = polygon.SphericalPolygon.from_cone(ra, dec, 8, steps=64) diff --git a/lib/test/test_intersection.py b/lib/test/test_intersection.py new file mode 100644 index 0000000..81512d1 --- /dev/null +++ b/lib/test/test_intersection.py @@ -0,0 +1,167 @@ +from __future__ import print_function + +# STDLIB +import functools +import itertools +import math +import os +import random +import sys + +# THIRD-PARTY +import numpy as np +from numpy.testing import assert_array_almost_equal + +# LOCAL +from sphere import polygon + +GRAPH_MODE = False +ROOT_DIR = os.path.join(os.path.dirname(__file__), 'data') + + +class intersection_test: + def __init__(self, lon_0, lat_0, proj='ortho'): + self._lon_0 = lon_0 + self._lat_0 = lat_0 + self._proj = proj + + def __call__(self, func): + @functools.wraps(func) + def run(*args, **kwargs): + polys = func(*args, **kwargs) + + intersections = [] + num_permutations = math.factorial(len(polys)) + step_size = int(max(float(num_permutations) / 20.0, 1.0)) + if GRAPH_MODE: + print("%d permutations" % num_permutations) + for method in ('parallel', 'serial'): + for i, permutation in enumerate( + itertools.islice( + itertools.permutations(polys), + None, None, step_size)): + filename = '%s_%s_intersection_%04d.svg' % ( + func.__name__, method, i) + print(filename) + + intersection = polygon.SphericalPolygon.multi_intersection( + permutation, method=method) + intersections.append(intersection) + areas = [x.area() for x in permutation] + intersection_area = intersection.area() + assert np.all(intersection_area < areas) + + if GRAPH_MODE: + fig = plt.figure() + m = Basemap(projection=self._proj, + lon_0=self._lon_0, + lat_0=self._lat_0) + m.drawparallels(np.arange(-90., 90., 20.)) + m.drawmeridians(np.arange(0., 420., 20.)) + m.drawmapboundary(fill_color='white') + + intersection.draw(m, color='red', linewidth=3) + for poly in permutation: + poly.draw(m, color='blue', alpha=0.5) + plt.savefig(filename) + fig.clear() + + lengths = np.array([len(x._points) for x in intersections]) + assert np.all(lengths == [lengths[0]]) + areas = np.array([x.area() for x in intersections]) + assert_array_almost_equal(areas, areas[0]) + + return run + + +@intersection_test(0, 90) +def test1(): + import pyfits + fits = pyfits.open(os.path.join(ROOT_DIR, '1904-66_TAN.fits')) + header = fits[0].header + + poly1 = polygon.SphericalPolygon.from_wcs( + header, 1, crval=[0, 87]) + poly2 = polygon.SphericalPolygon.from_wcs( + header, 1, crval=[20, 89]) + + return [poly1, poly2] + + +@intersection_test(0, 90) +def test2(): + poly1 = polygon.SphericalPolygon.from_cone(0, 60, 8, steps=16) + poly2 = polygon.SphericalPolygon.from_cone(0, 68, 8, steps=16) + poly3 = polygon.SphericalPolygon.from_cone(12, 66, 8, steps=16) + return [poly1, poly2, poly3] + + +@intersection_test(0, 90) +def test3(): + import pyfits + fits = pyfits.open(os.path.join(ROOT_DIR, '1904-66_TAN.fits')) + header = fits[0].header + + poly1 = polygon.SphericalPolygon.from_wcs( + header, 1, crval=[0, 87]) + poly3 = polygon.SphericalPolygon.from_wcs( + header, 1, crval=[175, 89]) + + return [poly1, poly3] + + +def test4(): + import pyfits + import pywcs + + A = pyfits.open(os.path.join(ROOT_DIR, '2chipA.fits.gz')) + B = pyfits.open(os.path.join(ROOT_DIR, '2chipB.fits.gz')) + + wcs = pywcs.WCS(A[1].header, fobj=A) + chipA1 = polygon.SphericalPolygon.from_wcs(wcs) + wcs = pywcs.WCS(A[4].header, fobj=A) + chipA2 = polygon.SphericalPolygon.from_wcs(wcs) + wcs = pywcs.WCS(B[1].header, fobj=B) + chipB1 = polygon.SphericalPolygon.from_wcs(wcs) + wcs = pywcs.WCS(B[4].header, fobj=B) + chipB2 = polygon.SphericalPolygon.from_wcs(wcs) + + Apoly = chipA1.union(chipA2) + Bpoly = chipB1.union(chipB2) + + X = Apoly.intersection(Bpoly) + + +def test5(): + import pyfits + import pywcs + + A = pyfits.open(os.path.join(ROOT_DIR, '2chipA.fits.gz')) + B = pyfits.open(os.path.join(ROOT_DIR, '2chipB.fits.gz')) + + wcs = pywcs.WCS(A[1].header, fobj=A) + chipA1 = polygon.SphericalPolygon.from_wcs(wcs) + wcs = pywcs.WCS(A[4].header, fobj=A) + chipA2 = polygon.SphericalPolygon.from_wcs(wcs) + wcs = pywcs.WCS(B[1].header, fobj=B) + chipB1 = polygon.SphericalPolygon.from_wcs(wcs) + wcs = pywcs.WCS(B[4].header, fobj=B) + chipB2 = polygon.SphericalPolygon.from_wcs(wcs) + + Apoly = chipA1.union(chipA2) + Bpoly = chipB1.union(chipB2) + + Apoly.overlap(chipB1) + + + +if __name__ == '__main__': + if '--profile' not in sys.argv: + GRAPH_MODE = True + from mpl_toolkits.basemap import Basemap + from matplotlib import pyplot as plt + + functions = [(k, v) for k, v in globals().items() if k.startswith('test')] + functions.sort() + for k, v in functions: + v() diff --git a/lib/test/test_union.py b/lib/test/test_union.py new file mode 100644 index 0000000..66abda8 --- /dev/null +++ b/lib/test/test_union.py @@ -0,0 +1,169 @@ +from __future__ import print_function + +# STDLIB +import functools +import itertools +import math +import os +import random +import sys + +# THIRD-PARTY +import numpy as np +from numpy.testing import assert_array_almost_equal + +# LOCAL +from sphere import polygon + +GRAPH_MODE = False +ROOT_DIR = os.path.join(os.path.dirname(__file__), 'data') + + +class union_test: + def __init__(self, lon_0, lat_0, proj='ortho'): + self._lon_0 = lon_0 + self._lat_0 = lat_0 + self._proj = proj + + def __call__(self, func): + @functools.wraps(func) + def run(*args, **kwargs): + polys = func(*args, **kwargs) + + unions = [] + num_permutations = math.factorial(len(polys)) + step_size = int(max(float(num_permutations) / 20.0, 1.0)) + if GRAPH_MODE: + print("%d permutations" % num_permutations) + for method in ('parallel', 'serial'): + for i, permutation in enumerate( + itertools.islice( + itertools.permutations(polys), + None, None, step_size)): + filename = '%s_%s_union_%04d.svg' % ( + func.__name__, method, i) + print(filename) + + union = polygon.SphericalPolygon.multi_union( + permutation, method=method) + unions.append(union) + areas = [x.area() for x in permutation] + union_area = union.area() + assert np.all(union_area >= areas) + + if GRAPH_MODE: + fig = plt.figure() + m = Basemap(projection=self._proj, + lon_0=self._lon_0, + lat_0=self._lat_0) + m.drawmapboundary(fill_color='white') + m.drawparallels(np.arange(-90., 90., 20.)) + m.drawmeridians(np.arange(0., 420., 20.)) + + union.draw(m, color='red', linewidth=3) + for poly in permutation: + poly.draw(m, color='blue', alpha=0.5) + plt.savefig(filename) + fig.clear() + + lengths = np.array([len(x._points) for x in unions]) + assert np.all(lengths == [lengths[0]]) + areas = np.array([x.area() for x in unions]) + # assert_array_almost_equal(areas, areas[0], 1) + + return run + + +@union_test(0, 90) +def test1(): + import pyfits + fits = pyfits.open(os.path.join(ROOT_DIR, '1904-66_TAN.fits')) + header = fits[0].header + + poly1 = polygon.SphericalPolygon.from_wcs( + header, 1, crval=[0, 87]) + poly2 = polygon.SphericalPolygon.from_wcs( + header, 1, crval=[20, 89]) + poly3 = polygon.SphericalPolygon.from_wcs( + header, 1, crval=[175, 89]) + poly4 = polygon.SphericalPolygon.from_cone( + 90, 70, 10, steps=50) + + return [poly1, poly2, poly3, poly4] + + +@union_test(0, 90) +def test2(): + poly1 = polygon.SphericalPolygon.from_cone(0, 60, 7, steps=16) + poly2 = polygon.SphericalPolygon.from_cone(0, 72, 7, steps=16) + poly3 = polygon.SphericalPolygon.from_cone(20, 60, 7, steps=16) + poly4 = polygon.SphericalPolygon.from_cone(20, 72, 7, steps=16) + poly5 = polygon.SphericalPolygon.from_cone(35, 55, 7, steps=16) + poly6 = polygon.SphericalPolygon.from_cone(60, 60, 3, steps=16) + return [poly1, poly2, poly3, poly4, poly5, poly6] + + +@union_test(0, 90) +def test3(): + random.seed(0) + polys = [] + for i in range(10): + polys.append(polygon.SphericalPolygon.from_cone( + random.randrange(-180, 180), + random.randrange(20, 90), + random.randrange(5, 16), + steps=16)) + return polys + + +@union_test(0, 15) +def test4(): + random.seed(64) + polys = [] + for i in range(10): + polys.append(polygon.SphericalPolygon.from_cone( + random.randrange(-30, 30), + random.randrange(-15, 60), + random.randrange(5, 16), + steps=16)) + return polys + + +def test5(): + import pyfits + import pywcs + + A = pyfits.open(os.path.join(ROOT_DIR, '2chipA.fits.gz')) + + wcs = pywcs.WCS(A[1].header, fobj=A) + chipA1 = polygon.SphericalPolygon.from_wcs(wcs) + wcs = pywcs.WCS(A[4].header, fobj=A) + chipA2 = polygon.SphericalPolygon.from_wcs(wcs) + + null_union = chipA1.union(chipA2) + + +def test6(): + import pyfits + import pywcs + + A = pyfits.open(os.path.join(ROOT_DIR, '2chipC.fits.gz')) + + wcs = pywcs.WCS(A[1].header, fobj=A) + chipA1 = polygon.SphericalPolygon.from_wcs(wcs) + wcs = pywcs.WCS(A[4].header, fobj=A) + chipA2 = polygon.SphericalPolygon.from_wcs(wcs) + + null_union = chipA1.union(chipA2) + + +if __name__ == '__main__': + if '--profile' not in sys.argv: + GRAPH_MODE = True + from mpl_toolkits.basemap import Basemap + from matplotlib import pyplot as plt + + functions = [(k, v) for k, v in globals().items() if k.startswith('test')] + functions.sort() + for k, v in functions: + v() diff --git a/lib/test/test_util.py b/lib/test/test_util.py new file mode 100644 index 0000000..47a3444 --- /dev/null +++ b/lib/test/test_util.py @@ -0,0 +1,14 @@ +import os + +import numpy as np +from sphere import vector + +ROOT_DIR = os.path.join(os.path.dirname(__file__), 'data') + +def get_point_set(density=25): + points = [] + for i in np.linspace(-85, 85, density, True): + for j in np.linspace(-180, 180, np.cos(np.deg2rad(i)) * density): + points.append([j, i]) + points = np.asarray(points) + return np.dstack(vector.radec_to_vector(points[:,0], points[:,1]))[0] |