s2/0000755000176200001440000000000014124561362010602 5ustar liggesuserss2/NAMESPACE0000644000176200001440000001073214122646640012025 0ustar liggesusers# Generated by roxygen2: do not edit by hand S3method("[",s2_xptr) S3method("[<-",s2_cell) S3method("[<-",s2_geography) S3method("[<-",s2_lnglat) S3method("[<-",s2_point) S3method("[[",s2_xptr) S3method("[[<-",s2_cell) S3method("[[<-",s2_geography) S3method("[[<-",s2_lnglat) S3method("[[<-",s2_point) S3method(Math,s2_cell) S3method(Ops,s2_cell) S3method(Summary,s2_cell) S3method(as.character,s2_cell) S3method(as.character,s2_geography) S3method(as.data.frame,s2_lnglat) S3method(as.data.frame,s2_point) S3method(as.data.frame,s2_xptr) S3method(as.matrix,s2_lnglat) S3method(as.matrix,s2_point) S3method(as_s2_cell,character) S3method(as_s2_cell,s2_cell) S3method(as_s2_cell,s2_geography) S3method(as_s2_cell,s2_lnglat) S3method(as_s2_cell,s2_point) S3method(as_s2_geography,WKB) S3method(as_s2_geography,blob) S3method(as_s2_geography,character) S3method(as_s2_geography,logical) S3method(as_s2_geography,s2_geography) S3method(as_s2_geography,s2_lnglat) S3method(as_s2_geography,s2_point) S3method(as_s2_geography,wk_wkb) S3method(as_s2_geography,wk_wkt) S3method(as_s2_lnglat,character) S3method(as_s2_lnglat,matrix) S3method(as_s2_lnglat,s2_geography) S3method(as_s2_lnglat,s2_lnglat) S3method(as_s2_lnglat,s2_point) S3method(as_s2_lnglat,wk_wkb) S3method(as_s2_lnglat,wk_wkt) S3method(as_s2_point,matrix) S3method(as_s2_point,s2_geography) S3method(as_s2_point,s2_lnglat) S3method(as_s2_point,s2_point) S3method(as_wkb,s2_geography) S3method(as_wkb,s2_lnglat) S3method(as_wkt,s2_geography) S3method(as_wkt,s2_lnglat) S3method(c,s2_xptr) S3method(format,s2_cell) S3method(format,s2_geography) S3method(format,s2_lnglat) S3method(format,s2_point) S3method(is.na,s2_cell) S3method(is.numeric,s2_cell) S3method(print,s2_xptr) S3method(rep,s2_xptr) S3method(rep_len,s2_xptr) S3method(sort,s2_cell) S3method(str,s2_xptr) S3method(unique,s2_cell) export(as_s2_cell) export(as_s2_geography) export(as_s2_lnglat) export(as_s2_point) export(new_s2_cell) export(s2_area) export(s2_as_binary) export(s2_as_text) export(s2_boundary) export(s2_bounds_cap) export(s2_bounds_rect) export(s2_buffer_cells) export(s2_cell) export(s2_cell_area) export(s2_cell_area_approx) export(s2_cell_boundary) export(s2_cell_center) export(s2_cell_child) export(s2_cell_contains) export(s2_cell_debug_string) export(s2_cell_distance) export(s2_cell_edge_neighbour) export(s2_cell_invalid) export(s2_cell_is_face) export(s2_cell_is_leaf) export(s2_cell_is_valid) export(s2_cell_level) export(s2_cell_max_distance) export(s2_cell_may_intersect) export(s2_cell_parent) export(s2_cell_polygon) export(s2_cell_sentinel) export(s2_cell_to_lnglat) export(s2_cell_vertex) export(s2_centroid) export(s2_centroid_agg) export(s2_closest_edges) export(s2_closest_feature) export(s2_closest_point) export(s2_contains) export(s2_contains_matrix) export(s2_coverage_union_agg) export(s2_covered_by) export(s2_covered_by_matrix) export(s2_covers) export(s2_covers_matrix) export(s2_data_cities) export(s2_data_countries) export(s2_data_timezones) export(s2_difference) export(s2_dimension) export(s2_disjoint) export(s2_disjoint_matrix) export(s2_distance) export(s2_distance_matrix) export(s2_dwithin) export(s2_dwithin_matrix) export(s2_earth_radius_meters) export(s2_equals) export(s2_equals_matrix) export(s2_farthest_feature) export(s2_geog_from_text) export(s2_geog_from_wkb) export(s2_geog_point) export(s2_geography) export(s2_interpolate) export(s2_interpolate_normalized) export(s2_intersection) export(s2_intersects) export(s2_intersects_box) export(s2_intersects_matrix) export(s2_is_collection) export(s2_is_empty) export(s2_is_valid) export(s2_is_valid_detail) export(s2_length) export(s2_lnglat) export(s2_make_line) export(s2_make_polygon) export(s2_max_distance) export(s2_max_distance_matrix) export(s2_may_intersect_matrix) export(s2_minimum_clearance_line_between) export(s2_num_points) export(s2_options) export(s2_perimeter) export(s2_point) export(s2_project) export(s2_project_normalized) export(s2_projection_filter) export(s2_projection_mercator) export(s2_projection_plate_carree) export(s2_rebuild) export(s2_rebuild_agg) export(s2_simplify) export(s2_snap_distance) export(s2_snap_identity) export(s2_snap_level) export(s2_snap_precision) export(s2_snap_to_grid) export(s2_sym_difference) export(s2_touches) export(s2_touches_matrix) export(s2_union) export(s2_union_agg) export(s2_unprojection_filter) export(s2_within) export(s2_within_matrix) export(s2_x) export(s2_y) importFrom(Rcpp,sourceCpp) importFrom(wk,as_wkb) importFrom(wk,as_wkt) useDynLib(s2, .registration = TRUE) s2/tools/0000755000176200001440000000000014062332704011737 5ustar liggesuserss2/tools/version.c0000644000176200001440000000015513774601521013576 0ustar liggesusers#include #if OPENSSL_VERSION_NUMBER < 0x10000000L #error OpenSSL version too old #endif s2/tools/winlibs.R0000644000176200001440000000063114062332704013531 0ustar liggesusers# Build against openssl libraries that were compiled with the Rtools gcc toolchain. if(!file.exists("../windows/openssl-1.1.1k/include/openssl/ssl.h")){ if(getRversion() < "3.3.0") setInternet2() download.file("https://github.com/rwinlib/openssl/archive/v1.1.1k.zip", "lib.zip", quiet = TRUE) dir.create("../windows", showWarnings = FALSE) unzip("lib.zip", exdir = "../windows") unlink("lib.zip") } s2/README.md0000644000176200001440000001762414122644264012074 0ustar liggesusers # s2 ![R-CMD-check](https://github.com/r-spatial/s2/workflows/R-CMD-check/badge.svg) [![codecov](https://codecov.io/gh/r-spatial/s2/branch/master/graph/badge.svg)](https://codecov.io/gh/r-spatial/s2) [![CRAN](http://www.r-pkg.org/badges/version/s2)](https://cran.r-project.org/package=s2) [![Downloads](http://cranlogs.r-pkg.org/badges/s2?color=brightgreen)](https://www.r-pkg.org/pkg/s2) The s2 R package provides bindings to Google’s [S2Geometry](https://s2geometry.io) library. The package exposes an API similar to Google’s [BigQuery Geography API](https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions), whose functions also operate on spherical geometries. Package [sf](https://cran.r-project.org/package=sf) uses this package by default for nearly all its geometrical operations on objects with ellipsoidal (unprojected) coordinates; in cases where it doesn’t, such as `st_relate()`, it emits a warning. This package is a complete rewrite of an earlier CRAN package s2 with versions up to 0.4-2, for which the sources are found [here](https://github.com/spatstat/s2/). ## Installation You can install the released version of s2 from [CRAN](https://CRAN.R-project.org) with: ``` r install.packages("s2") ``` And the development version from [GitHub](https://github.com/) with: ``` r # install.packages("remotes") remotes::install_github("r-spatial/s2") ``` ## Example The s2 package provides geometry transformers and predicates similar to those found in [GEOS](https://trac.osgeo.org/geos/), except instead of assuming a planar geometry, s2’s functions work in latitude and longitude and assume a spherical geometry: ``` r library(s2) s2_contains( # polygon containing much of the northern hemisphere "POLYGON ((-63.5 44.6, -149.75 61.20, 116.4 40.2, 13.5 52.51, -63.5 44.6))", # ...should contain the north pole "POINT (0 90)" ) #> [1] TRUE ``` The [sf package](https://r-spatial.github.io/sf/) uses s2 for geographic coordinates with `sf::sf_use_s2(TRUE)`, and will become the default after sf version 1.0.0. The sf package also supports creating s2 vectors using `as_s2_geography()`: ``` r library(dplyr) library(sf) nc_s2 <- read_sf(system.file("shape/nc.shp", package = "sf")) %>% mutate(geometry = as_s2_geography(geometry)) %>% as_tibble() %>% select(NAME, geometry) nc_s2 #> # A tibble: 100 × 2 #> NAME geometry #> #> 1 Ashe 2 Alleghany 3 Surry 4 Currituck 5 Northampton 6 Hertford 7 Camden 8 Gates 9 Warren 10 Stokes # … with 90 more rows ``` Use accessors to extract information about geometries: ``` r nc_s2 %>% mutate( area = s2_area(geometry), perimeter = s2_perimeter(geometry) ) #> # A tibble: 100 × 4 #> NAME geometry area perimeter #> #> 1 Ashe 2 Alleghany 3 Surry 4 Currituck 5 Northampton 6 Hertford 7 Camden 8 Gates 9 Warren 10 Stokes # … with 90 more rows ``` Use predicates to subset vectors: ``` r nc_s2 %>% filter(s2_contains(geometry, "POINT (-80.9313 35.6196)")) #> # A tibble: 1 × 2 #> NAME geometry #> #> 1 Catawba % mutate(geometry = s2_boundary(geometry)) #> # A tibble: 100 × 2 #> NAME geometry #> #> 1 Ashe 2 Alleghany 3 Surry 4 Currituck 5 Northampton 6 Hertford 7 Camden 8 Gates 9 Warren 10 Stokes # … with 90 more rows ``` Finally, use the WKB or WKT exporters to export to sf or some other package: ``` r nc_s2 %>% mutate(geometry = st_as_sfc(s2_as_binary(geometry))) %>% st_as_sf() #> Simple feature collection with 100 features and 1 field #> Geometry type: GEOMETRY #> Dimension: XY #> Bounding box: xmin: -84.32385 ymin: 33.88199 xmax: -75.45698 ymax: 36.58965 #> CRS: NA #> # A tibble: 100 × 2 #> NAME geometry #> #> 1 Ashe POLYGON ((-81.45289 36.23959, -81.43104 36.26072, -81.41233 36.2… #> 2 Alleghany POLYGON ((-81.17667 36.41544, -81.15337 36.42474, -81.1384 36.41… #> 3 Surry POLYGON ((-80.45301 36.25709, -80.43531 36.55104, -80.61105 36.5… #> 4 Currituck MULTIPOLYGON (((-75.94193 36.29434, -75.95751 36.25945, -75.9137… #> 5 Northampton POLYGON ((-77.14196 36.41706, -77.13932 36.45648, -77.12733 36.4… #> 6 Hertford POLYGON ((-76.7075 36.26613, -76.74135 36.31517, -76.92408 36.39… #> 7 Camden POLYGON ((-76.01735 36.33773, -76.03288 36.33598, -76.04395 36.3… #> 8 Gates POLYGON ((-76.46035 36.3739, -76.50246 36.45229, -76.49834 36.50… #> 9 Warren POLYGON ((-78.13472 36.23658, -78.10963 36.21351, -78.05835 36.2… #> 10 Stokes POLYGON ((-80.02406 36.54502, -80.0481 36.54713, -80.43531 36.55… #> # … with 90 more rows ``` ## Acknowledgment This project gratefully acknowledges financial [support](https://www.r-consortium.org/projects) from the s2/data/0000755000176200001440000000000013774601521011515 5ustar liggesuserss2/data/s2_data_tbl_countries.rda0000644000176200001440000040323413774601521016464 0ustar liggesusersBZh91AY&SYsbwμ0ۣvd1VٲH2Ӯwi`;lw5ٕiWcӶ]1gg.-GZ)uЧT]{ Yݻۓi׹꾄۴ۨ;s/6)ћ{vooo{] +ejg.rz۽=Vq=dkW`zs{{wKw\ WnW9ıBˮ.ڭ]PR0yTD>ޝ-jqg{;=vy\S5=zzy8gN]{=rnݜ=*\u`{^=FXpmۂٞRj7X6خvnseݛMsVmww,*;njVۄMk>O`1kvi6ybtsfB-X6Pl>rTE5Z YۮѣCۤv"JmgwpFj5TI-۔]j56@h5T[2vlmJUb(J&MvvK);2kIP̊V#cU(PRnΛj*Ν jU5mJۦ흷aeGM]ܻ[F&NZR5 5gt@ ({*Fj>]] %mWX-]QΝUkU"nEj0BfSkihj.˳*VA띲l3l]u1-bkBfmBE h Rp@ 3 v;҅PNM&@ h444 4 `L`Li T@h`0@L&! #A24hi ɀL` 4ѓLM'&M?@?MfM@f~ 4&&a4CjxѠ0hFO =2M23DMm)Mi46SЛGDlI"@@L#FLdhd`0#MO d #F&44LLbA0JcVLL333 $0jTԼL(B t$S"i7B 0L=& s 0`Nbass qKG! EêFl ?@32z0 *]0HsH)^Qe"pHp!2Là=%dQﭱ=]'.!;g I&'L8x?}&P0Nfd330$3&g &d3`g3292d32F' |ȦN;iP]6.;" g8S,w}Kpp8w8{+..H`fMPq>`$I".D G-n(vbJ7$^-'(T4#))$O‰oH:hK ݓ"!^t":2zZb(m\|#`IELa+yOpOucʃz`BacҌspʄ><%J1BV=4J L'MW8I[F GL|V>W ?g ?g8 H$L2LC &dd02fd $2a a X EfHYcK0 0 !2IHIHf`f@2Ha9 5a8]A]Ln% IAod KJ y ( a#Ia3sŽK+ tb,d2fa0EA'҉L33f0Ic:bO SM)Q 78@(T$ Y˞iw%t<(YLa' UgOP(&Ѻ)/%O#K FɆ0!O{ F2)vġB*8H2A &afBIffzC$6@/a*0ʈX&@Hˎ/C[J4KdgQr $hl8'yA[p#Y0:'$Wnу,t܃*`;NFLcC3Iz]e ,hfLw ^ˊ |;'-;ϛ' PF.MϻeG=uro'qN#;Lqf gv y! ';7%ª~qF\ ?-dc8tz%ܛT87?9AQ/=La;HvpAU.Ku->`>C:.A}GzxD`{k2=OX^S.Fj0xWϹҙv}|$fѱJ:̵`Lēd^FRSSOdyI(g Ǵq:7 )^{] ]_~ʕ Ζ\+uzI/Lf06We(V d֖5ռvpjcd!!Z"pc4вfg8a98ss0aÙ0ɜNa 3 9d8{ 000!!g2d39{8{sD LfNa6_b 𾃠9.Et6,1V4M)h S.ԸKˊ(!uB(L$@$A u@QP zJͩo&%ZRc}0o+  E !@(nrZ b+N=Dԏ\h]*$(z|#9kPGchqZy&T:3ɿ7s'&qҎR^tn)h1?KgI7X/g8R%~ #3-2Hbn&]:ӹ`1J'e ۙnYbN0xq(<16%qqOF) PSR$ۃ{AFn,kBS@f j06]d\_ ؜z nN xc9z!{~:A[r/k;("O5lbݹHwiS?lze}TI5?xk5C&7w 0mb8\VF=p&]HgL<]z??5BaYJCק )&;Ƴ:~NpN 3f[xw.ؐӤ>n~qO x5Q9"´_*WSBW2,NKïC>C >ʸn&̍e5K=ήWNbdUQLSbXNx_q_G%kpMB0aʷR+)wEfb)b ѸnVh` <0 a*ed׳V T.YR4xv1\Lv\q v}lp]F;=l݉ 8pV2Vt)߬f!ifpMCW}sEDدՂ(B+)Y`& +iw s}T?0֫?`#juS.\Ĵ| G'Q'x XٟoƭgeJ _u@q=q5㉿q0;vݱI$)4Os]-żhxwFн(ԟśdr Pæ 6 9x/@t~ Kz;RG>lț3]ǵ;?6'7Q 6ƜT-ē43zP,:fFÂմqU.] +*"eXSog[Ly>cdGW/$:>H&LHgm-{+G18ۋ{LgVNX՞l5鍄(Hlhx1<"p k.6vx sP ih[S&@$)6lfY!6OKOwLVv/ ѓ5|-66Vg7L&gN6@NaZMj ųԼK~Fڜr}Y/:pk7sHjBfx:@>]txڰE +!/Q G?d1 r*#l6,, Ca_^ehfQ©c\I0 Wn9X(Ph78'Ej> BAkjBM&ɔ-* Buw0<a,QDؐ|˙)hV߄jaTMa~~i}v6O*10Yq`;R^| W-y1JJckx4N)J\vC29߅JزwpRR%pFY犼#Y64 O()fD&b6uvV"C'6lf<:š~nzu4jEǝ_]22 o @ʄ005%+|%q`AX׈)JAIO]"_M=ɴUMzFި`Z`O='4#Gxgԓѡ2̬IV\/~,- &/=ݙ;h,WbV-i?5_/qi$|E +. Lst <2(cv=1aůH~ q^Rf2w%roykK;uJbG>+][k6MUZz5hzG9RjN\?3<߶B4cB7qLXBˋDEARfF˔ )j٬Ԙ{7XBܑdN0ϧ_ĹLB$gy #l(NB"L~j/<8[;Mdb"ahA[wȗ&WȋYբA \čLnf;ot L5ay) ֏ۖTH*aBU!',o*Iȿ$Q?Ġ;3xJM 䰼~RiPso^{AT>V'rrT_yC ,Q|fx0mHZ[E㒔A%,+t< ^.yj3}t0-쒚jڍvEqwR % Jp=6mIb1Ϙ*&Ќ/(NЋK;1X J=/ )wro9/$q4W*O4G&BhE'1V;S0)V?XJL)0ȴ0 T8lzfUDGcݢK*e] xR(C岿qSeN+y_1 ŃqPLzFn‘>1:ʗ G'Xҳ!:!͚_1\Lpmƶ*{VunfXWx%5H$l9Ds;*!D2 rXIo|Н twb{-ٹo)@~ڣt-a ;Ize yѪK"Hj v@6Q߶7L*8e[nM3&y%n7X'h7h)<o#F+ڒ"dO-`.*A#lܬL)Ĕͯ.P9هTRk)J ^54D¦19 H3Hň{\\nDo׌() M[FuX ީ gZjjQ3dba0xͷ7 ס -v 4B7Ƞ; IP@8tH^6H-X>3=Nt) NKN>ٍ HH r`#iU W^)!I2l>YR(X"9UCRl;\F"t6ACQgOUA!ģOLb4 / !}+&;^gv| 0(fmLH 5[f tQR/gLe.Kr)B` h]?[]Lz- ~ ]Tq΄Qejyd!zCuJ+kY&k,ivƈ/L)W@.=(X(z撄D6VL乪XA&JPf!Vb{;uymXҞQ_0A%ه3`d 9 H~OH;+-l>(ܢӎ&lNj:#;kbZdƱ'\i i㓭T8*'X8_ڊܘ=Z ' 8  i&9䞙= Hs)8y{sc%"9FW7_P&̓Z]kݴ][D0S9fD0՘#FC|pn03)Yz Ya3 'G\.)kң|=G*@xA,Cs{Cɷ!GE0Zup7؀݄M.$|X c$2,eNWWO@dRŖ2MKaߖ!ҡ$ӟҞlD^BO Ջbjy2B6aM-uQsfҁ987oD|YM!fV~H$Radl[G0IJ>e\m  s:SjFqG{TD8~4@Σ8{"?Ѽ'abbdeVPL/*Hz 8xa0:Ӣ7u`X-jsGܜp#Ch_LJ_Kp:GHe&I(&C\HX=iJΩKa->5U'tg(#ǡD1֌i3bψE~9PoQB3ؑL]I&6}(eğ=ܞŸjIfDZB$Q&arD1p%+l.C}wOzOȒNrĕ- zi+;>n cN9ClJnI K-7Ǝ<ǫo+:ĒCoc:Q"'M%TaZmBEϒhH3q863{Hܖ"~2RIӀu~suL M hR4TqRGlMR6|.YzyX "YMk.G!?龬Adgt#*y&Bok]j/- '"5uGe)lƂXRY$83VBk^I+FXvRNq Ry̘:_&a'H@ EW.glO`(Io8 .pta;J!9ͼMOU1[C$yȆ'HYE腌H2۶xud/Nba6b&+,n;`/|<@6MA>7ֽlWUm䇉ၟw$C+ГII:\x_O@OFm`՛2_+Qҷ}iýP+Br<ZSa1c20\*˺11{{n-$#{"`R7 .9r([e_m5W=2Lv,QfrMs̉`gIJB(_⟐^T#Thl 5jQ.0M)=Pqķ"L'RN]FY&̓J`>I!yel C8&4+ ~eb`ސ3~>k6( 9$$ <؉ƿ:+@; =9/ iM=pkdF !I'coQ猋|Ogg8%kBc]YC, Io/BUs|Cj'vr4z ^OYP$qS8V3wa3&ςE|ގ䷙Κɉ]ʗuɵb M|}@JoXק9 @ᕠQE^톑Rq͐o0o˦pla{v Q I2c{5V_$zַH~,V(GlX |5iFBVN%F}4/e/"{1~?Ä7D:Хwpq=vHdM` <"~^c^(oOǭ2:F ~k}ݓ9pUDZ{OnΠg~ڻv4^"w>wX wG!t2Q?I> ͪNn'24vUw vDwu)NKT G4^)VSv,=nA?p5Cf#8Ol:?,U}%kr!d"덧<&m8U !]TQ?.=FbX$*Z>m@C30,"xePDn|mk  ֎f*?*>>@`1P-g=Cj.o)y>˞ŎQu&!YH8:}݂|CBm/l }qfNˊ0ĝ2ؗGQ1TSBF%0F-%p3,İ5;-;HrM,$e/ElϐOϐ;񠞥iW -&*m 3)8hm}>Bl)5A:m[R9+Gd R%S/0SsBKR> ;v>!:W0úθ/= A.BbI&6s+]O  1;JRE@8lMENANɞ׎ƴF91e#̠ظ**Z :L9- v&+4 q bk2XAEu%v"N҅H};uΟ^5aZvw( =ZqhICc]5,t+mjes{-9f#cQ֠+g'G'kL3r8j˪<2w-з@#X^"?čtZ~zDeas_RF#9,99<-FФIflcLC 41ܼ9M^%URs-KC}+3ֹ!Xk/dcuocO!qU3ɔs-u)r򅈈A*UDtKy tWF4qF{M8rU=Yc|K%GVsr^hbTua`s_srQȼ=)SEf( ;Pyw YKp^B?RJZ)ۆF8|tem*w4/[gϣƗ,WwN{_ϜyJQ/ܢ/>.xqm ҹRWGI:i*S'2 iqo>g$pBHb^Y R9f9H}`X.LS7`; [o񥴡h fq];X^P̓[a2:bŜS -H߱SNC<Rh! DÃmErJדU6@kG+Dxw>#sfu,՛ku_`N 0*S2Bf)Al }09e%y=Gtԣ h킁 T:YIx&ǎhM;m $yb@#e=(Uis鍳 Y;-'tOkpNҢaW A䚜_AacNT]V:rfPHe\QL-L!OڨtGV(<p\ܘÎ|L9N0[IXe;q:&C];R&JLԖsy 8{>>+t@,oFFU_2au3Eyt̪]ѵ EeK(_e$Z9q}L[Q+/zBInC$lѸc?eۑ0BcℭI=g)׏m[M{sW\Q0ˑ8s $=//w3XCeJ(ۚ{rL9?&PiEj&NmZьN]Ɂ/& ܸn(pȣjh9;3&/v^6(m"_$l]2D2?1s=eP)XL"h3]U Xq(n=a=BO}$iRCuM?tIvΠʩ]XrWơ*,>D`B8NU3Z$iay,|\PS|:T1\4.Zk ۉKJ\ȳ&Ԣd'$H-M:F{NhK3K]Ǟ,(h.q7x%^TΕW6eݭ2-ׂ½ EWK- ,r'N Al\ׄWF{1$ᔓ(} ƌ$|%y/}YpD$Os/CQ1%wg*%u4zxwl;=Dt;Ja~K1HGn,Z_ >$vȢ5(_ ךdo&x–IGiTkbf#):<|۠$9(]Z-?A\@`DϊrCQRCUH 8̼aymwKIp=#ɳcx[uDQ# N4`W_>&jgc2"rXԱ_a3g8[Ej&4τv*0 D)(ȯW_E}Sٌ[RENÛ$%HxNdir ͮi,i*m_W~KQmQHB A+x-ESӔ.d;ێ_{qfvԱЪ_0Zm-%ÑrG{Ghus(S$[-lND*YڊrުJ7®ʮj׼Ƭ.}[Tn.]ud0{/ms::#~!kZ<]FoaK\GY|jjQ&h  X伷HN]aKX1"ՍXl,S3%cV[&NE6(ZD;@ghlTOvE(R 7CzD5̰c{83uZAüő@jQ]8 /cs"L1KPUGWw cGŅ:XH o2[BZktOIA8MʣA[X_ɨQ-t]1[4u]#yD#.ȮG8?x4G6 :^W=d>aU"r%{,s|RzLNC695 gj1_R>xp/3>B!2l :k1>6Zpb~'ɁC/Le^ɏ̇q&$2fݿ.Wlӓ`48W}^p5ϪINx9ʉe)M8 Eob"X3dXF\}K靆ӡ{IyLMc>*>$y /:){JaNdmڊM ׄe2^o[T%+;"j6Dncڑxmr@4cs&G6N"i1}xa-j%jn(ϗebTqJm*žPUrZàݛF"V{Q đ~Y1;3ܣ)&ь[ERvJ? O1IÚ:G Ou '(s<dt:1|2,3$N6SK;=E{F +Jp|{\-&C8uj9%sb &k\53|8+aǗ5Ōs(?܌~͆C+;2&HXs=/q8P 綄^|G1~)Or{2bKwri)h8ͨQ`r%]53x4/+ݶv37rsCwYt-8V4tC1kk3Re A QD-DX^uGՋ+_i}Du˫_y^SXa'T0eFv pY[`+';Qxn}mS{=:{I(f3}0ժeky1I3Ӕ DtJ{f\b # Q;AՐY3h9$+ٮiJ%k8n H":dFi㘘H?Ro Dɺ{+Ybr.Đ8iw6n<#0btz 4=c B+vGC\2)qHf`u,YJq:!VlL Xa1K }hcIrdc I/<&!D涰jV혷xy˫t<'aSni7r8d6bL7?(arIN@`ԋnIrfϨ.20 ;muЬ_#q¼9cMƏaP,au0VvȬ{r'WDbسv{fkD^H7!MawڰǏXet !5\GDnkԨ_ypK&&ܻ+۴5 >l{fng(D)@לEh1"m ZQZDLp)ku1!t,`G(QIܞslk feܵj\_hU, jOzk6JYEQ" .*m(З h@uB``"Rv:h $D{ (1n^.ށA=Ǽzω{(V$q]H "4GG΍E0I XgN+b6mZu8ȈG ;t25 -9 FҦTOdi9;Ն'6FCGըh4sf`7:6$ _O4˙,NSgْ Z$F'1aoE.gtnq~6PdBIb' g<8f؁-aDȍ78ZY6BIKr?'"j%1FQY?\< P ۢ=?LWn΂ wqOS!5eTvhYIMT{ 7s>^iqc-uX8ŭ.v9{K̐FA_up?sUcIYN2/9k4'[Y*ktN*_ 5sRb彗T_7&歅(QANy7Cqb1%5+ut^: $XvՒWx+ePP,9P7ǔ^,_B@_BE ܔy,OAP $ iWwԑdFE|T1'?n+[[2M{fqE)LoHaw!KaٚTNaz [ឰZf<͂c,d1pE?_4SKAg0eZg0v7~K4ڦjqS=>r.'``ʯ#&6>TNIlT!.3IzB{X)lĆgǁ23>f [QlrݳֈOGF5m wY>R :DM1|1טuULG SŬ#I_ʮm6:`# :n\'%H+ NhV"^E)ѸJ,w G_ep ޘh\}9fjD5xLg>KIk9O@1]-ha|aB}P_YƲ>j%f_l뢬6$ O-Y褲 ?#K/BÄj!1Yֳ']cLk|yп}>aBGg\,_/ p¯_!p5Ut4oN90m 6q6iucu"1}ב ?SA0X.c̉l8_,-Jb qEc0VjvR7TG&Q0YJ٤F^AXnfm3†_o_=*Ag@l+qeWJT |c>Ώd2u7*!~,J6yFeO_r.{BM"jn5?SY jStR:E⧞cp,{&}65oxX58T Iቂ}3LȾӮ塹Ha Y~x)ЩS:֑0OPz;lg"fg,f) Y>H9\),[CfK^xt1?7(Zt&$bUuq5kİBIOmMY3c'T]V"My@FaOP(;pA-QSR=>@;+=H%mQ(ܖ'`FJ3Д`֒.>+ytѐ ayY^V|4*n;x4y|.Uj'G6igy]}^[|[SlQzC1~-.ґl(O_:~N%:Brhüz I!"n52uE<}+F:,SD}r}d>I>b$s~uwL e&heHhQ)Lg(uҝ&?R8 OBDDx5:cr^i|de5}Hn0ft yWuUZ8A: jc=r ǝQnU\GPĀJ);{Gog]6`j^i n͌(v;8yP*ȵ[ꍒ .X {X9)=fWxo$-R[)New%mvZQ _=>̡3lun>R~a`4瑅O?ƽm =jS8+p,(=³+&D]e{T 6 vH-qdg޽_ܬqoC`?=aK!^ '"e-M a |~ .H-L J4Yڎp.l6=3dߛ b_׈$k̆ZYMܢpR)a2 J_B$($v4>\+:N8i)~EQaa(ɛE(Et ' @&Ē5 +A'#W8KJҊrMYmue|u;:K$7ǻy6o׻,*P.Bmv9kOa *OiX(,1, ؆;a}aXK%&D{i%qx#yc / fsQ,7(lL't.v OF\(:>휕v3XC+eIb | o@nϑ?geMJIU8cAdl73 [gt/ vcuvN`1 Ɯ {iͿ{/WQo.g+ՑՐv)d%A)iI;(_{ ऋڳ9:_M\`U>g ΂q,g>=GdlٻP{.*03JSܓvt ܯ Qkj/Y`$Pb,-a2lJOL3{Qע|yp:o1kx'}<)RY IGdr9 A sL1O::Y(6D]1 $R_0(dE?vo4ZCX͔&dq>tBaJVB|7xfZįqgx+!75Uɑ+gDl)mEZrUX0'3>>xѻ3_7IuyE/òOѷ(l׎w\q GLrSKO4u+$'V2;r㳏p-g#rojm"lɦXG9R֛-Sr X27q坉\<,P?h8\TL}]zJCbP$;cl?X 6W$@r)#J$V_)=I8&;570NթiR9x4Ъf^3sKeAeՃ0bbm J,/o&GGJh27$ߑ[.qlM9}'5s(|0TM{a(vhˏ$;2cW] ;3No[FTd5X3+?\os̯$\b0;0tbLJ_:u[Fny56*^tfSQ5V35S%3U͌^XZ>%7QbԳ=UWv~}zq`;zUѯPAc-L幞"n4[DRktZ9yz- Нs}"]F.^z_Bvo5Sq_ȗcmw; ]JDd]Gki*;AJRdΓXbg ׄTqI;Ugr$aO4FXDCMe oZMPlkBZ1s*ou* AGv\ ܅K)k*W :)DؘO5웗ѱ-NfAҒ1VI1I=q)q!ƨ4i;fʠUp2 ڡ0rԋ)]XbMky(b!痳 r˪L]E p!+^~Æ]S6W@a1Rož\yO7 vQ<^k4/Sx,aE7'͜w~g,i"m_sŭzT ˬ74eҫz1z%Lɗe!#r,85ǹ}F[pk -StyLKTb6eje| \ LFVU"{ #hmu:Vi12)]?Y|gFjW6e,ɜ|x)煪(3cZ@zaM-))E\)|?g#կijWpL2Ҟ QH,bVS~Hݭq៙ީd6%d~#L&D"GLK"HFgiV2 Um՜pe5>~f K\ԧ-V;EhbI넫ecpe.j7Se?0Vo@վWn!Zs;s"E·89Fp'5c¼BZ Se&A Lº|C z~ɄO`(|q?|Szn]EwI*U%qK@w}RyLm>{N*ANen="h,g~?kiD,P^KGDZ7b6~IAO4sw8 (EmT?薇8h#~t!Wsg mVm[Ҷ13ttY0 &hpG/XW>>nZhUh D*7M`[7?7+5uػ ^&fKk&TRMTг^mtx5* eʍNaqNL:da]E휿 =s F Х=-qńxAaW iпn3Xst5Hk{?5sqn%8q}z6ٯ)pytiDGBF]82⫧$rk:SGw!Ƙ(gWy-olə2a=VRz+&D f2 J6ag)('Dz KS{i nQd{Gw[- UETla%?R`5 $UQ۴w!|4|m_އ֥dD{NCѸ#g^փvKۂ<4>Pղ{IpSysF0Pt@35ח4C˜R|ov%: FV?ÔgW7>+cqfwNkz̒J$\2 2&#{0xlR~ @0K'J1Lp^>;ŮƖ )2?{/"<^Ջ&0e(Q hpR ?$a6W.}*3s5 lsiq *^P^Țeص/1p^%Xy \"kc`Y 2c(aw?0ѵl׼LEJ{L嫲јT \xHoZjZ嬑ץ$fR8y &ĤvÖB0P\Rt:CAƏ92JY)g$Ó@Li&AފŒPҋJ|/ԣ j_J|yWɼ!90i{S↜# rk67nL>i|?+ms͜>p`+V &Q8MM @ oak>)"(DΪ qd*ʩދXW̔̚6YhɄx]{pPG|Cf$JlZEdyq3a20tzSj6ddKvJL`?h >j|$D >bMc jnCS &X0 B>k̤o;K-K.BNr+̩3C${D8:³~:%)7$6Hϯё"۳5 7H$j"$KP|˱ɭvV^HuMG5:u乎@AE~,Jyyđ_kéYa^z0SnM.$c {aGY8;/N6nR@5o՘!7cUik< E#Z| z[Ӛd00mF̈ nAѰ?ϓ!jS_hs|`{NJt@` k{ݸ ˯/R_A{F-J^rgm͡>f(Kr^r<\~ u{<5N]ІΨ dbSBgS//D%Ik `ͅOT%VRHr`yC3XsB򑧺4 ˍ'cm^J,br_;O hjLjeסNߨSjR0 /M<\+~fSh8@SPd(z5tڙtT@<2uc[6n7d&pQ{IӜvI#RBWE7RF؄Z4)[j z҄inŠ;?8ZfS:"s r/|.P^Fy4+ξD$0d3l#n;c3pR6QBh[w-W͠^7fcj vx>.8]85 *~UXjE~}k)ldg"WG i3M B,ۀ4$xSb6+:Gaa!۪4Kٖ̠hNp?YvgcjҖf3+cob3/]ޓbG_Eciƾw5b(k+x+8m ȳgX/WfKԔ)ź8՝b(@}&񒂸^_J= TtAz㟻 y[Vqd֞G%ap*՘Ј%>CL;dXXAT4PEX&[~<4ɐ H,}܏I-4aQ|f`M㚪f[y=c/E*Y굃za|klϮ*a^ŀOvh9tIjR lfzU!عg&\.sWWE_hZ,Z7G.Pa.CpI獌C9(0ޓ:$iQ0ǥ ^ FX:y`2rqVXqEuqd9-A\;,]3?6,zu+c,Y ~DbTFLx4dij3~ű'XީL#8@B1?"&Ts%-N[,C4k^00&2Ŝ jJ`,cjOPL~e%ss9#{q.=q)+Sn 7)~ss%])/h,몄GEr"eQ 9Ksh H*9mf-["qMQ6X:z~ -.&3}Bb\tI| < pdV6h>玟V/Nw{S3zi6F /H3U1rsŎJ8 zoAR~JX4K4EVb{,Ż&YAB1g@ ԕVT_KwpDZy毱w.3{SUr8ؿ)e8q>}*K2JK,Exj;#+x#մ|(gd>&ƋẄ]v7!Y?QR :7bI,PRTf bü!´HNo; $X/w||jIA#&%8'Jt}g`^ ݙi~\[Ůa$zWQmN:*o Rl56G%UY(>N o[v5Xn]$'Yӹh qXD״c̉ P@^Ɩ] zxG۶=e@JQ(f<_ySk9Cw{(6>,QPIRR'/}HJzi vFߙYEAt?paܢa c9R~KҟVB +@OX6 V ;̄б61_4,;(UWODC%;f( <F' ,'4hn,sEŅ0;d;ʎevOMq`+`ދ {s_ Y#7wEw#?DI9DrO:ז|2nΡE@| :d~^{<9{NvuYQ)HW} MIeZNOL<6NuGکV>GtKZpQ5Xk+jarm sYeW ׵OtuCxZCeSy-6}S;ti~Pɾڔ?aS3QxZF>B,,: 9cL3P26<\D 8_fGlO `d8uEO"e*jMť9Yn(lan_/ XX'b9F6QLbZXŋ(ՅkFUV?Y_=tȗR+Rj-}EЏ*s3̒$rbNBizꙺG_a۝1ї&}=օ=L Lb89zRWrC7t5ļl :,9mcf|[ˢͺaıx'%b(,1Jj#w`CPzI6L10wD_B~9}1NraR2B.vqEƶKb.KbP(͏/d74), 3[NG/6MZFׄℲ ܰ@ |rnw +DILu0`YB[j {NliH>(45鏲ǥ,q?OnaDBQ^*)ݡ֡L DxFU*sRIDM59q7oZ-UbeK)Ŋ؍Ibɾ=9&O&H ah,H?0r0w+1W5*‹0r&2Ǧtq!Fg0!4)uw>ڷ3u1$9Pfpxig; .,' 'J `/D V.e2Β0C؁)1 #ƄGL!bUS$D\̷|fd /s8xD$+(mv^'y i5B!(&=kĹ T/v5tNt]R=-B/>2vX%xf`Źj{:c"sIZР2aYj̺8fc>j\Bv*+zrEy%4 RuQ#3QXر6'`PFJe+H ڧ%OjEʞE|NM)(GWÊQΏ+9g,mڱ:JUVc\o88B0V>X_Qԩev y[A4?ÇGc/S$yjt0gǛ$,DPY+XRJ+T ѷx~c0rl]3G Y&aed37[1wMH6/Yu2AJg2މrGˊT!hːH;`nQ#z΢ѝ;—è)\!t5*7$;.7{ Ni{@D(Ffp ^8o䌑2}T 6׫Oow49xOL S\3#:YCa+KQۉXSmݎn~Q7@19w݅׈OjX<"{<y~H_<19oW {Hq[:߀x6PChў?Y"?՞^Q 0l(] ;%`j;h0 i3!Z?qW >\fN3/C[fzũ5$C0P^dqL [@}<56?`Ÿߙ`߭F-ϏGz3W=&cv;w6Xgy*dYx.X;HxΏub{Ma!ZI]a}=;aM1Zs)Bj6Ys4RSr=U>F' K50@ƈuΈ^ȴcYVoL巣chs>\nI_1NА% 0v^ ~ TtG@\EظS0ч?#BjW7[!/gsaMwhP `8T< }0n# X|2*\<5?|6"RSӈ#˒ҩU|(eo({k]uͬAQ--i5V=ԕ؝gGH[<*P~/69Dnz JEᲦ,vQ!3F\ٵ(&Ɂ#2i2faZV@B*Rl,[/9m/7Cc?3AH=u(tJ% QQ[m(e̯>.6dA8ˑ}Z] sgQ.t9GC`VA-Vk350yPvêMK ݽ=axGYJ2BE3h$ڢG IЧZȄI ISqE4I(Y"kY4BLSƾvZf |y +usONb,WB:УB3)B;t BBƹsoI([3d^tHF Aixt۹I7xhSդ R?}gk'GPȐ'.GXo5ۛ;a5/a^S65u.ٞ2ELruы7[LK$X^-ŕayAx/Nפdk=j;1QrЎvՁM)03I%q<^LV!Է&FEgDzԒ5]<Pw;FN%SkOlǣ7Cg+ۑ0e7/,D:&mGd<oG|j(2cph^mPtQ_aM7oK)QLgŢOG b2+Yj?݋lfleC=Ydۄ!Yn]/䉱<K; Vh+Y[vBJ|}U: \Y;'Ј˺r`RHLjhZ7̌\b[0"ög/{*I C=׉{LyfT%.ӸY ٞJ'K:6e}m+ uJ_.`flFZX3 #v6EMկ>C b2E9ߚ?Z˄42I} 6jmdz ~fzYJ*r{$x xL䘔¶.&+3zη6agގN':$va.{Tzdӝ(oẍ́Cs=Wy'!*nN-Gz1p9=LQXt^0^"5vw`v[BeqDž\ʕ"c~ q>qkno|uD8X>:!h8!e柎ϘmɦSaq9)> ,fy/(~ k: )*':EZ~aY/c(]18qߘ[IQw>CO 0_Ȏߡol5ؓ+4/}u l{tȕ#5R zwO[XܩYͶ6zȾCɪS):%"(I}[`hp 05 |딮24W;QYIg% FH#;)>k8naYgkiO{a14rTDz[-khr\]y qFcm[|z-4%}]g/gA@FЊ8e!My',5m}ieD~-3FN=ޏB, Eȹ#*4T̈b{ՄT4[5+A2([6k+/jO$ U;A\<~Gs^Lh$1S ߏI|%J.{;GhLLN]豮+UNpn. y+e'լ#np?#/0>>+U:f~QDKx8H;=8]!4'Gf(MV%,c+Z c&DOCa_MO~O.adKB+QUG#s?HHzvߢA1~z"Ңoȩ*![Ȩ7LT&q0r\wX~3npco * (ByL2"DֶƴH(μ\DtI3@x?m4 Q^MB+MHm/$pBM4>S @R@k8$0TD$Hq6Rd= QNKKsĐ Z&ZxU];X L 6-56 OZ~ DZcWU`W}8ظ7 뤡)g]al 7&L4{?MWK+ )m*|\LBvYRSc`aM)pRl/^Z=g afTyc(CH8H~q[  IES&Jڲg H1YAEJUPqDX@i% H"  ,LC? ro&, #bh?(G B\|0xH`h~.; ĤgVOuN5nW uD1/do,N/PXG"3S n`>zs*U{'oI5G3y87u;\ I6f`xEMu8F"B G[QﯦK.6'π,@gh`ES&A+A"TK$O_[8B#I.!Y<' c( qnGhԊ:- f,YBDɐ"$&xH]A4`. Hۖ…ſɧHyݝa#],#1wF s$>sNb/n,)1_I%mt+Ov0X /_,f'_F#䃐l[JPSo`"7:l 9" Ad#E%E,O]rHߛM †XA<ʼF'4d$YW]B{ Gի`>)9Y!rΗ)+!4G0YBxx*HPL*u૘ FypP{@Zp89 nq h=+n2 ̧8b5qDrHsFBvH(pJ!.z! r2HEgaӚ!zDd ~ʵD ?8ϒhЌCg%̩k"h<ԑBo(+Lv~ȄBu9PĪ *_3|/ǠZ[c[.VweF9~6Q<,C`ElGx4q%72n~_@ī/=FR)C\+q w&iS=>D%7G4%@lJu6l>)ĶI1!iZntnp8h !@{-ށp#y,$t}NH]XC2iJ0'F #L!ޡ'Np! A |^=!ؠ<0Fd<]qws-AvzX8>*l"mWe(KFg_LѤGP6kQ&\J!= s&o𬙘<@E| B1 bZƈq.?zTsbZqwo"/f4\+,c2DyM͕=儵5ek hm\{ [AC"QI]H"T>b~j7P8?~TsшJ:"IBH4­ԓrr/\h§ƺ?7b+W a| " aUe!x D]Ec "[B#@ې|[~vn>Ju{Wa~DZÓ/e3K4xĔ|Q{ X`iM+adnJ9LftZla之MBA ?`Vƙ~l6(~? {X $/&L؏ q½V3, 2)ѢE:9 fQ9!Lw݉Ȼ$2>\GeB(99 aed"JI|9:9؂@%?!LollaQ(RC+7r!YQŒ$CGMÃ1B2k詜m=Ws?q0G/ȕ[+bai^N|2~b^XQ$|#T$)+^m(?XEx&(&EDF*~0ԐAMAԶ2RYmB mchD@&<WcT&>xdW^ IzB$c*\Ip"UQh`T`rf_i`L/K| k#klb,l)Xp珳X@҃TGJ6Q_|B|]ā | *s@󸋩owƨ?$o7urpvX3E'~l1Ι0A{(yZ~*6'h7rR\X18 :\ ֹ5T(mT $2QV3Tkc"}.&* asyW鄈L]8Nbb\ R^B*T0[rfix$pR |;&TW`D8d$l!C tH9ǐ)7KQ| FvHr3 "b,ʦAق?1$ % ~E bF.FxN Ȓ z<2e4۹tf+;MQA΅1FTDhcB(|i矐q)uEI:JYR85A>(ܸ CȫxoqyLR2Ó 1AI; դN~_Ȋ 7loa ($ԫ_$PNߌ +jT LK_>wV_iKKpNt؉^as3i X݇kZQ jY//Oɼ))ш;@yvWuD"ȍsC.й~eN5Ɇ,5-:,rG^ö5px#0mmЀ?)`QBBz`}j/4RW6ɺy"Ä='ܞ[qH6 {x>CxogLӣg\&BV V %2}P<| Jx&B!ƏvR/e+y&)i؂F_l8XcXXxjI Ixm\PŀfD^li7suKFblps6*ODa;;I_@【q$*W1u;뛌o jvŴ.6c!6l7ǎ liϥ@Ųg" voR~DbF8eu(iyiDuAY6Wls!apΨivTK|dE<0:uidM^{KJ8EcN4<'t醈Kp8ͬ7 N[nT49VT t<`y9Y,K3Ǟ&оFkA"Tq|>7Í`\(Y22YԹ}FD8٘хKuw.n}8+ Fb<"0yPE*7MHID^Nk2é}uylƸ$X:}l懱OZ")0e&9a&ȵFvB&s!Ŧ-)6O6H-T`gEq0i\T: ~$>DI3(G޵Xv" Dl &k I]83Oi3\v#E 7?obyNx^HX%0\JO`- p3ihNW"t4[W#Pqc a +wǽ_2p( DOkMnd  r1'Q}ъ Dz.СYZx$E"KtD?c>FrcEL 6)QDpɽ̠J *1q 4 ጂIGZtn=6s H!;Lc\Dg#hlzeNHR8qSלZ\'nWJ*0Xr*r[. P B$3syFqv!+խqK&_4| Fx3 bЄUyb)l"B\6ih'ค@Ӊ,!$t(F}^*fx y4SH9LZ$G|[7%aWqc9[`yb7?NɛQzHKn HkL$0Tj}zT0I_qK㛅zU`Cykhh$ʮbF;CbIBP@ZP" (+x8F6h0rtA ̏,,eh.hBcI .QvJzq-z`9J-Bq;נ՘1{jT8 x j|HGV%:9;ܿ5}2㪳Լ|R6,` FoJoXM (<3AMKH a-AF{5TXaai>~!?kP]#{5xEDbA~n)|bBÏf֭|,seq 0b)RC&Æ $aWP EnD$n[Z,.1 /Y[( 1j I9ig.PvEEKb٪im C"1JVmU[kZ[ B!- bsyq<A7`6Z'`(>|D{@/] *z+j<˭Z`.k6[x'< HR.=՚9. `Չh1/a k;gKAT} p ®1l)t%AN@\.3Sc)I!T%' FP[ŴT g -)IwXTXAkޱ΁{x/S ZX^,RvȎ&HVBH >L=?;\n_EUh PHZ˺ȩd 1<3nZ(d$mBWh(vp -!A0E}j@- (xהEŔat$3o$+b)$Ltк@фՅfORFӞʷGUis{'rjɇGV1\1@ EVx<5:"qu"D:.S 1B){گtM ZD]02,&B[ɗOsEw;( \to39 :rǝ㗟:xTi3 Z2 gp19AJ>Na1ҖQ"~Y4nF:]n6 T]LZ5C,/ TW4ˤx}p@iK$cmS'uz3=҈3OP0_ U5JvV|pQ|?y@ECȂMc%2)jhGpS4[[ҥDWauuP zGT>vF 3fa8P $U(Cwd!IC Pob""Tr3(\pUĎܬOBCHR`xD Dz }o%wuW$IO>E:FOk7^Z:51V.ć0NA< %˥0ðgp(LV@g"Pg_ĘvP7Q e;xN5Z,2D@ZbP^9u OeY3d2, JN87lB/{4K6;bS!U٬y ξ .nMa6L*b7^2dzx|=NRjQbU@%<,GF/6onzkҖ3;q"/Z1^[2mpVLLW[|^Yi!=E!8M0'ITLF5MV {@%78dp^5 `z%1uKut12P ()|B'ϝiP &bf$yx^-LSB :9a7M̅)"L^EȘ ^A5kJTQ`h8J<k ќ(ZuwS r wZܧ~0~'4CnU I8 Xe>~Ԩ|*ҟ;5wzlD\q F$]+NMsL^:pj&Z w?#Qm (Zza=޼1aX\0 nZÓTlQ#:$RaV$^X&5.jQW1_A!:j aPvn܈Hb6 Py+<:h$Aڃ[A ?-FHӲ[hQ>GVC9qFX[sz8'C[ȰMNԂ_2dyo SY޶1S۸tM(xC 8/T-j#}+ߪ 4"6;FL8ChoS/l?.hgE=F0ꤓuP#h~*ޝG"6{ P iMġ/ E@qIH·"1"Ԉ F u{IgDm|@6 p6_pi}36 fda.#NFh`ft[xfՐ%9 ,8,]baN ڄ:qP X=8 AG7KDpuAVynqcqp܎ؙ,Eg{;jmt8q 3Y?xYD; 2Ujs#ޥ):+֑# Iի`>b'f !6ҀrHLL ܓ,e.@C |{ @,D~XV#\pVXGlp`]bD 7V,(0YBe./"gt0s:]!!AAG0 \OqD+%ç 4="l$p&l, e Q+٦ CUIHv Z֡=a1QC=Dcɟa4hF MSÊF'%^)$7ٓnuFPhc)%1,|׌pǿB1cvgm%=nJGJro>.8/ ~%P`~2 R$M0 0-R"w`M AkA!=UG"l $C{FR ʃ &Li_oˎ(P$epE7%:b~C¡@V|j]Y諷\S0`h2Dk`ZRkc)X PE古u٪ihg'cB]U5!΍ =:8D@xq(B4Ǹ`,O'[8r(a"x1&N7]`xAq>#z4.~#* {uz$g=g" p&h0l\Å11(?xbjE]=FVr Ca0ԚdL3(!v߅ @H$ۥ! xUNaY4UaiW $U**6yP\B JCדn=;ch B>?ȀGA &+z9 6>z [fIjt.HX>da8Eab**[-gp%ZXM!,EFb l3,GA# upW_C5OɕHXjYŢ!lA s4g4- K$V] MY:F<xr-;MzڇqA]T m濜On Hbs!r"(;Nq16f'Ck0. TIb$;NyH)RRP@AĹ3OþU|$[z;M!H*7+T>C ۱ ڣ'c Τ>Տ>t7B҅ڭwroRC`e !m!D'd74qTkva袆Ek1GZŔhRr5(etY*Da?C?VOCbbvByȰ9vbHI%&8[ɟ!j+&sgKZϧWTN 7Zuu@JyN*Sc: W싻`%}Bj܎>Z $(40n #E!~/[]8vUSQX5Hwv|T}=;,UM}^`w- fZSE1q"--tEF,[c4$8tN(Bbѷ3x."I}'ע"UԼ Ă;IF ]rˀ| ?YY, rUv7  dO}4\O!< VJ}p9 |IUYuqg~3Bnki~%ۺmUYj֜ȦokHw[n:/(s誑r&!ti_*>y ⎐D&q|/da7 "Z7ce?:el-TVWXNQps2rR+y֥,g r!&~k&lxj.+<~^7niop,˼Л`+%VӺ WH2&W2r/guWSv% DZ b":WBtd>r̸ѷ+~GmJчYnÍ}W4fC1گ'xXr@MX^&<]Z KCRbnAQPDǼI >`h5Ee;:˜bKE`Z5 Xl8gϋ(0OCz& }`{mJ$FVI)VEw1$XFo"dyFT{I]}['Sj<>teyl=.z6O4M%~@6Cc\;Z7C"%"`o3ojt)yO4dOQq ]bhnlWgDvJzV"qaJ]+>{B>O:=-=ǝܦqV+j::y4,EPQ.W B=梧;hUpų$$54Ȣ/ҥ3_QZ*:YYtU]d&O|D_N3^ق2@mo9 OaZt5:<}4i8xt$NhM(`-,{Cڑ_/Q d)!G$;a_.Ҫ.w swIotnB/ t]$d4Cp"Cbe$r]\jN3iOtI5zp&_dq:(yV*t wq9f"g~4W(+'c3x _b8c]xe_R:.Us)#De+mC 3'xvI=1>QUQُTU}ԚུCtb˖ZLOLmtؖcc1W2ȹd-94D.|eG__;bNQP$ylndobลo+H)'a*̿jua"i@g=;yKzxL׼wUE&: on.TkhMofE_Ow±n*C\{>[:[OTI{j 'E1Jol^ u6:]1UCjw83fDX$fVX5y g%cd ;& "ɉYx2haP(vب, EίZq=S䶘]F !0~0S"Ki ޷K~rVI1}x%W324 l/]7+ѩM`J)IU'OPDu1 EF@b+5^{"+ χե8d w|Kik*p:[Mu`PT* -}7/,&~Z2(xEje7Vc !=$9FB}&ph*3J e f%,"&eRUP:ll:3xm* MCV=#B5H~ QQlG`mV;.v6zm+%%͈~YcS$z[LhMM`cxƏ/hWB!C'2Lxlv Fa4gY ZߩBidi'/LfzsTv%#6J>p)W7C;h;zW'Z?yFXWSq|d+^4gLc>FS*<&)?)?MbyX㝣 efor:QXƻqTdM.q}ugs1O %ֹ-m Qlf\S96oHQ-ڥI>}%zс%`JڴX6)'%X55z~isDž/='P8˫aE Y^ʍm2Ԙu)HRmOhE{#O,HDX0KftvmqjDOni\ĂM&_#2<ǭO;f-D $~JxQH:`Q8cȞ8=^5Ect̥u-?S+Gi-=lj h\Xr1zA(^zNŠeF>SrYu<)k nOY!O|^se0=l͓dx,>M>V-y7u:v.Eꂂ'^DO:ՖPJ̒hI8nKyqL  qftXb y[YvZ=uZ'L16\M*3uJT0lR?VAL[דtљ=91Ƴe 8٤\<(vlͼ/5FUX &Le VY 0' Vz'fVP0ɖXʅ U!r |QTtNA,8M$/>08Ղ.a%' nB-5b8Vrz)?si>>.}Z?Oٹ}ԗ|@ "V7܋ș$ֺJ\&wej }ŒY!1*M02Sj͚1u͸ -;:=ѡg&@"O gu,UzCw6 hVV]-V[,SU2}NA]RFPۥtnO;0uUfNEmw~S+}| Wn="HC$, *,]cb)U ' lW%4r3Kmm@AfܡڎBA% oO >f\t>UBlM}>4n u+DrY 3ß;ߔR?o'Y,.bt)U9ɺՈ|ɑoh]/:" NPK Dei.h`6]gØťZ{JN"djp%(QoQ%T}[ꯓ .qN,/UlτcNn~)R\-xnKuQ1~uHk(CNYY3!?VarGT}vÔEN o[Go3@ן 7| +sFC+0w;OAltMv~wAmn 1+ \S8sxWKh\ն_r˅C]EN _|!ʽ_D/]sŠQE[؈9+cnʶ2;W`a Ak"~(Mrh_!v(G!x\- S F*̂ 5'9 NfY݇9@C[ӊb2⺴$^އ92_26ORTT8 (cȅaC~4N3oso9|),뒾KOzfm18ԼFj}PYJ$haܟQÔ+Qu:-XsQA_~MxtH[g+M hvI?|DX8["$jɃ-^NxeME\\EY̊^Kyxݍ9܌:cʐR hl2c=AB3?V:7{wߌwK'VxϾvjys3Uv !lw紭 ֥#4(^N*ypZ fa /)FVohKy5d:`4/ڃD!5嚁0 leRDwxhⷤ X[V>{` J-9)"ک#׮kZ;CXG*;tv}':tN#|2^'Pk]W3+YNEa;%ɲ_  BZ?bE K4fڵع+=]̤Oz}m }W޵{qQS鉯mbA'in珲S!/U9V;CA8GAz0ߤ!ȝUVFCt\y\lj-j CTs<)#DB l(q r!c#mvAJ3GYr3[<srhvĠ8E&6ZX̿ `^;hns {puh9M<`1}sXwG䆝yttU\>bEp؞+['jVLھtEwmܺe4Y],#Gt apڮZI :c\339[<.g"{1!0,K]fd8{/Zc(:S;5'3$R+hFcO{7%&&`kQS/gJ"ryC4N t)N,SLU7~:Ot=gFq5=UM8z9,o=văb勱r7+h<;6_̈Rz~%88k:(N{ssd $ lg w%s[c~ϰMGգ^# o?w9!fel$f|NZ@֢5"k_/F`/BCخ+ƴXy3Ѩ܍(>Ǹ(w/Mkr@L,w*ypc֮5]v'N+^Ŷ.V+۽au\3:EiOk3 =6;=YF$cg6<% zEvb2^pZY1\kft,{#>r;GfV;fvE9S/aP>/=| w7C?$a= :WO>Fd 0{iGefE 1h\J"i^HG>\QN)7cPuђ`V܉WG WH>1w)Lq73jዖE %"~( t,e5m#^ >L20hЗwaUi 7R^:8pi&dɹ4zYVGnkHcnsVOuwD{X95JG߽^mg:U)(fp/cSA. 8Qmd1=rJ5DW,)5:w؇c ,?XL 64("Lڈ#ߢϾ IңX9lYۦ\sI4ǖZ[b68ݶNGaBcV9[t^5R4ys9u _  ߵAd I `ϯp՚߈+;jEEfrEts5c@}69񸺝# H~b=0XN3f rGT.iPٍQ %]+;IʶCTC6fvhȞ\rdaB%t^ ë+84\_,(F;MrBK1pToVqqïHoC5MM5B$iѺsbʒeɶE\I|V?{tK/dV:vI0nn]}sZˉΆsZk7uH 1 U4vV %E.d9-"ľIJ)o}06n:J~D6"pBnC>4w+ܯn;6>^Pʡ6W+wj4EMTW8 2mSP \V>Z9*7ʭ|>.hCKS0t"??ߓڸ 3CluP<$Mw|fi_酕? qbp*>ʺ?Eˡ FN(t4QMɘbx.(m)fҜ3HL*h~F 8_oGwԇŬ:CXMWDCj-c(R(c.8"B裃?F\}w8G8Kcgzz9I2[gZ\abȓ|hH)`BLjuWH dϿˁ-n;#ZFCk4 TlIYO/͵7>xk e W15km3P?em͝Plv7#X=v%!o;2; ooir!!dH}9!7ikzIwdQ'4KOåZg>3D_0}*7pxEx7 AKTu¢ z>;))/@Ky|?OKr)A\eZ~8XU`wHi)x9Α[3uΝ!ԴЀx~½EXhqK ֜K`Zz] ir)($O9!aPIJ{).~Ň |<…㷳2t b:jeh 4@ ̈A]{Ŝm!WLl4ȱDiDy/c ӄ>VwKϫKY|J7VS֬;TX!*^ \Y-2tCbOTn.ǏȥdYŚC%2:j_?$Ʊ9՛B Bpt9a^b fG;*WSl7B+Mtx/SuUi7`bQ =A[y*FM}ObU`Y9].( !m`):^Qk99jlRmt>uL?l0#O,\;@sx3gTKp 9dOjWΧyi5Z>.}uF2Eks?R --m;е4_Dɺ ҷyFw-5w|?ƦrH׌X~1&/S"u'X"#5>Ѭٍ,G跶=4˴_]̶FDwr}:#^u냠y𣊺}gO[%3I/(, )8uZu({;kuLirP2%h8ZMMYYdbBFiÂ, {Eב|mLaK]C(f(L̋ޟOT/zHs$db<۩m*r:QQ w^;\oPjcnt˧e@}#_I;u$~8+Z3,W 2+Ib7X5p+>~I0m̡|_@TOzk/8bsOq=K:3Xnd}؞EAƂxB3|wZo4.K3oJg,/ Pfn%S P־^G}vnvW=-ЋlsQzdG!w,iV`y_M&9k=lty. r~:>KS/*Cw\< R!9l_OWIx^ 384NRkV,GlzE7j3c#H|y7c"\:jQ6DbvHu90er^ oo3ؤ#:GBmhĄdqRJ0g} +.1'%ΕSBƣf;#QAI͛eOٜ쀲K͏𧾼N:~6n1Wޠ;9p]K%Kڇ+LQ~;vv |DpN8T淏__郐Coڃ[~cx$)x_T>{hDc w00%T2#fi|qzJe?tD>|[!rJdOE<ɘ:Z5;s *̹*pฯ-žc7<@{vUfe-B@iHċ:{1&'.i$q@ѧ ﶾUE|9qnǵtd_yDYiM#$RgiiuGzr>Άaa`i=nsn;qn.gAvjEbъoюB _\Tl&ks}ئ,^].r>#AI2& ]v1\Rk9,>gKQҳ9mu'BAxU)ws\}dAOKZ(N7C e$JqlScJdl ܼ a*{4. ,9w K:XsG왧r)Uo%Pw^~0l?d?6Fc8QZ\BCaڛ *mV[5X Vy~_]ғRV & /ݿlvki&qa>P_ǔE2?|9B2( 80?l|>thy NkJw_{P;ϟQ':\'ǭKw/T#A?ՓOF9"Ģz6 \쾨P0?YN|)բ[RCu^EJ]nщg>UσvE$1onwyE8% YIloaOԮOM؏aK_%C5JOx%(ojؘȄgVlOi-|7Cl"w\8B E9S !G&(\ߚifLޗa}WѿJL(}V.0W1 f|pi-aZ.]س8`aLI$G hBq}m]::h㟣u:R8?G>ԯ-6PvcCo!E]̽˵`CJ⇮/3׺a6PU>k%mć#fUE6xZgiW0mib5o=y=kܭG'#ָP>~ɱ|OgN6$z--:*?3ÍN0;Mae1WM}\>q9tX|_߀$ClU.Eb xDDex{./\zFSi̲Aptb4d?@WWr<ߑl9b,~sv51b5wY2v7 i#er[3B Q6CwWHOA.Q1&rLKaZV6fsXM㔷ڞ-kĢbghUCeLGal^Y_Xp,:f?nrU>U7pb?l/ORӣaTƚ.D4/.U7kR;J]ny*J)7#"| _T̞j1Dx6//R)#%x* b%#zLZ?FvZ H>cMhwOywsaWȅ%1g{Q)=4qJhq[ڃ"~ƿ2ځ\>7M[evuCK@CuOK'Jse2Ku.W.n?2od A:jJ.E ^⠽.㠾!9o`[w4=G>=çOjYoUIAWd{0q(\KvLmpY5$E,q?9L[4Qp%|VVuݓQַ/.}R/ρx8KCWtԢ6܀jQ@ӂ-&n߽,$ 㳻[MmiGi>Dk??~,zdy7xhN;xn?Du5:YTU0|㢾۔],Q{["8Ӌz}O@or&$Ch]hP̧kIqO&6yQhb$]z_łݢWǒswy"CMahFo3#M:dSUO_>1s S¸2N.x,9IY57xJ HMvQ0'>Ҿ3o.ޞsK>#S ]<8IGg訡{=0>_7?e߄D~+[UO7czOc~crXؔŽk;;(X($WQp42mmH=FvM"3 G2mx[VVG7تN%Tuք8YqYn];e0#(ϖ4Ig&/[.C-16Ffߩ!8+M?@E~mU8ᄑ fjG̸sXNb@@ B^Ib=*:\ؾ@ΚG\Wtxszp~4k9"x6^+7v/8BH|;>5M ­$d *qB·yⅈ1ÖWr8%*e|toH-rvnP{QUUXa :NV2ќ1RX{CO }}^ stދI`;eCk{Y$SgɱIc'sLyÎ44FKXȡ1oZ٘#?pWhw7-WPo*v=U'pő^ HYF]0vu<<\a/"AQb2L=Ū\?iGµס>Wz&:䒵:6s+Y3r~Az]zH$LTJ30M"Aa8a`ߛ|?rɬ$r/Sg2iGF^Y4Lz1e<0D*B_ '*|! a5#~$~KаVUaaG]+Q568· ^&qL%F@N bh .7ZaF5+FВq]:sT}$}G=sSZ$,a򠂆$2PoVj"a3WE-(21@!@"@ƔUuP/e2c=D ts1앲vȆOeIxL>!xX C^}PcT  FN95ьj}d#7Y#F d䍱HQ< ^1PӪU L8y`TN'4Ʊb4mqReAL} }LA6XX~=zrbT(0(<^45XVun3zXiLABccb /͇܏wl~=\6#-dfL4юFn#~єR1z\ٍ)2Qio}*m% 6G3j,|K/*:|fH`ʁ4"7ݿ:9tczD Fʽpk۔s7+>MOohzwddW5cbeVD 3OQvR=_hSʞ{3<#A9]u2SƖ|3džPL`_`? ;ix&`>RhV9 95ZDwSbsh͈8D,1uֹIby1F j D͌2QJ˳-Q. @)dj#%e1^,ELX`ipLnIVw%;ߘ~{BGL7"q2c_4T־ޕag= P\؜w_ߟ rϦ XPKE4u#wZyS,8vXk8/=`{ڼTŞY]/=Kɯ,C7_,l/XzN!E8R•f5Md5OEB f|5̔27W!L(Ȳƭm7T]',K-(ԱsF%2sRjwg7ڌ{iCvόTW wC0.kʤy?cȫYH-Es,hb YK-/ Qs@$I^kbjG~Xn #Xk!yQlDpu85e!7@1'P) +rJ,&2CMQgXHvY2ʡ|w&(>Q}&|7do𚖖\ Uhݛ$w`cׇd\s0XYꉔU+XՓ?t `;gt%,IT0CH1ODž.L SjgFESo4#g):nWt6ZcOyk&|2br-i>Hw/l$ۗ> *>jƣF pvL̤H 4gxL&=ձ残Dx+ <9^O#&*ov@_MT2 ?CsVtK~O#G7RZheZj>mTToC^v$TW!ƜzgĨIj3{RmVXTD_aJD?i1Hdҗ8!X~ϊ؛IϪ̀bQ}VqJ(UN+iLRh:fRI9ϬfGMX4JYEHp0 X0>7?5c-L)_}TJ.$Ϧ&H0ʘUdamvYm1gLs' Oŵ-/>>(a,u%OrXn2w<஢ЫcHiJ?_VQr Bvs\L1F ;Ձȿ,L+*f@ȯRy7[@{F:OF0Y]gn2Rl ccN/9%wT9l+7'u Ԑ)ŋNbQ,{: ~Ьe ||6_b wL`mo op^[=GM!ߺQQ*6ʩFo$ᑅY6cA6?&GLV3Լ.+i6Ҕ*fZy+\KѪWwd)#|%$b8GLN]l2]&M̐&T`IIak/H6^)K$"WhX;Q 1zlǵt;~2CS2nmxzn(7VY Gfjo)+@ /yN۩4틟GNF5e%/LT Dg}hQ߻&#g5jx4XgՎLfBg(/PggQ~_4&W'9W{fYuk%cO|Jϗ>d`VFq잶]_ݲzqOz2PMRII@˷AH62$<ɘ9ق$:ȗSWVFA%q!qV\` -}(CllBfVUx!In+4-E$̒)xXΝ>ɬ*19j2*o5&La1-Sq,›hPӝǟ!z ~HB>^Og ~{)XO6vKBe?{kҏtqi+{W&wzclG H :0nёο >$si8M;icKDŽҩUrvK!lxVJic6ph<ƒӲ3~ &y; }WF +1 s`\U}E`pl31CN_ }H4!ċ4Lh(4-7MHͪW6:t#̿mz+U6ieLD#6zzdǑO?,.eS-|ALу,k\!̭\Y%tu'NPDnɄX)l|JSeo7Fo=tV}L@WV"h!Ҳm-1$oO#.zoO7' T.sͨVXxꋴhv:o_18:tjr :1 F#G^="ϯThU0I]5)EN+otC>mNrBA/U-Q AcJ 7Oix&KP!Rb_2̵hM(Vilt_=&-K&L 02d0@dX0fL 3 ̙aa33 33'?!|fVbpaX?7#0ffd[Ҿ]7 9/^,MKrḓ'7$78N|™9%?rsE@n"f@~E9sUBnhRH\P Dn:!@)?gMbh:? iU7 ȆNX0%UYtMWoi^<ɤ g-v#hA?N[ͽvrEcGB,3GVla"{97}E&QugFWӜ6C#XpvIjKܵqJ/Ț|7~d;2~kc) f}`BIڢ^A3FݜZs7V~6PKz9\òN0PR:(hEUNZDbZ1Q__L={7w51<ݬO6FL 2[_BTcٳiF @RF4a3}C1UDy&Dy'.LכH=5$'gx|ƾL52]</cqnMdik%voڔQM>Œoл~( "VP2-($Yl~EpTQ0-96dHӊ,8SlVWiKTN2Qt. _Q0rVvZiD Obx̆NGQ+!Zg[wш_i>.G~_ٵUN5l59JS<ЈtARp&^qDA1}F؉cτYa"+N|ح3laۡ =>H댙?g^3oljw&KTFKg/ߟnG Sø)U%b Aòxv_sw^Ksh% ͙1B}~WI jLH3E?8U+* xmUO%krvoŽBfAN}ʨϢ3hnR/Śx}F+ !'$:ƍS)ƾq:w~>Nϸ`1=_S\Q%J$7s<3'ԏkv\/)^{ፉw&e4"#DCJT_ \2Y otpIĻ U;Aǎ 9bL!t}sxծ27LH(L7qW /=bnk,0Iz|qƒaki0rhsE0)i/hP- CqN`=*+O!mF,/{/8rC?ɥr򵿻_Dg?+NsiT*|eFof.\oqxͷ0: Ym hx>mCx)2~Xi(ti@~;oP*,Si$w2y.M%7!k^0<$Ʀr kkiU{ə`|x2aԌpy E<<^^]dMaWv3eR]_qC8 F֬O:7rS>]VpFeam$,qh/w.m7+ \OCxBonx8շbY]@ KwaoyD;U>UlSI8m*dCcX.\XR2pv67g;ceØ:VIԋ6V0= 9L]x 5ɒԧ@(QYсe%GltR%YŁ6N^ 8nC ˕az*Wұ"Z/SԠڻZkUIʠ۾ҬȼZ"U̦  d$10s)7K=,k &k -Ge0`s BN"]'+:czU9dt-^o|d/\^\ )yWJ26oeF;LxS fqyĀT ߩs̥U/$ںmaV.+thM†Sٺ #`ʨO`휌.zFIsg$Ӥ ڼ%azL"IX|kr7v,)x󵄆Sa"!fk x' gnW$CÅ'`"ƭenMyR[᩾x72 cqyyΙ ? ְnvC4Og`8H&(C&C2d0j}knZY_=y(cMx[ E-)?PVMi6gC塴v?$+l fI@1;[ۇ,{,B5b%^CUp}MeStA^$TN_э[Bg9%~3J G#g2p{sۻ 3C,r2ӵcIu'XcVv#"7 z"+S9RHIƃbؕmC9䛶6?bwR7tyºN|sC 5G2tj鲴vք͛3hF8X#i A@ {#MWFEiu+;^".`X!0I+/Y!z΅E Q\w !Ge>rˑ ;=~ikB5 a&&ypߍT%f&t HWL/,z.EڙhZ"$ /LJQHadjr4(1FҋuNHWV$RF;&Hc il$vG+(-'hG uI?$q:UTq% @r˟\ёjD5 ic8WM/éZt`J[V])yWRSJ:=ZUG 4Б L蛞+ɲː.7}6 @_c-7MSQnU7BJA:s!N`fffCD|t!(wR kdR*̩3ԷT_~#gc'ōWU੮YYTq}9=l0A$X'_Ԧp.`MqK0`r)ΌʢW,{0B8i2E-D]ʍ!zR\Ne=j")ܳݝෘ{BF^o }z(6m(u-&-uc>:0C]/;W #mbTVr@s 5*R-3 V9ٗ$[F>9_F.g'pD:Vݾw?u>F(hL쓧='??'[M,^|d$m=I>,|jܴG[ܴQ~RPCx~ւ|'|:kag\EbJǯeוՁc:0YlP<r_{8P(S5^LjQ J9)gDOWҤ,f ƋyW9~cI $- 8I];GbUۗr4>Q9wG~;IEy<(Ƒf+RKTZ.CV3mZVBկ[IWoՅ&PrZMl-lF)/9 +{P^Jێ6m#l,iM"cEn}iBm8 0ldډt}y׳d׻y e|yg-sNwѤD=t_<('oUi)fwf gV#|}[ ҴKi0P$]uv[Ɗ̵dAX%x#x k /d]}~QPuf+="s`1 59> ؒV'Tcԗ w+mo`{e˥Pӣ ǫU~IB57E R=sxj~z<5Qpg|>85O0aþjǁre"9=w0][k& w4f8k=rRnTU-}Ir#NY󇜙:!{>O_pq8څ&!:"KD8$JMS °`Z^c+#.q` 3We:>A {d3'i;{]p>ŽŠU&]%/4 ګ(k=\]NT=8Tք;2i$[ǿaw-8O  mqɏ.lSmzA B4DVe_EAnߜU=΍Dชf8NqX}&E{A#jHa!HލuMI>\>A%u)ٱX \q&̀ߛRH'!G5 T22d -$['Lw__>\͗5uʚS!)N*RWQPl,MEnd~LI5L#)cY&eUb+ΪX3q xFv,~tX' [ŏAO]I1ibCpۄ2pANd;7=U@U@6rq(.!x0$d*=-#ƚv!8wvИdVL_'Q^@&M9Ea908߁skm'41A4ܼCq,-`;@0v\=c]+2yiuNjEcqn Y_,]Ц&zv',U ɫl8'Zs< Ǭ=ny;t/*⚛ rڠ R(Rfzs_6iG{Jd}eȋ=dBJH>o-`}{=UG"V1kOBrd=Y$S=-% Seډ^N^co.j^<x"Yi|M-*/_u8"RAtV#Y9X)uM9~#P~U_~\x-ke5M($'Z &v"KAl?g'lx8`3hP޸Y=K'RIGcK.R/NL.T38i|.2{ɡ.,^V!\F9Iq,:X^_Y\d;%wpaC&fC!d ffffa 30334]挽8tZ4ӎYi|i ,s4tY۟Cއ/'JKy.H-cg+4$lp\ya)m u/esE + sK:ji%21#LH(l,\ɄŠ8>50Zg?A Re"oމBZODb*}?I#ӡ6K|BKޭ y0D9Qq f//NMp71## 2;aC(_4-41` -2,xUb+gԩˀw)^1NBYp+y4c7$V6ia/fU<- ]xr/!.J dQ=v27RLJ% C\V)|sʛdlM vdߎv]9*0qL5$UmH=y9a$o:9&lr<[$GO9^X۸>֤b/# ΪU꠲iI?\?dr(%Jq0̘ff`dəߑJ{H3נFq ĉ_-:97+9YzZˁf%<@Z ន[aY,u5U⟌@=+ Zsģ\b[ dVldXbx)fMF>2TI/1qBaj1O8LΞ+*_&4ŵr%9-<2wXrG<1v6aTћCE 앐JI"+hh<SNPV="-$-Lr$C㉑ r6_Y!Blt%=忶hPP! ?ʳ6MCNavMcD[Gwtyz6˒4Nx#-P(ݪ"7i~-A1)fL^  {7Ǣ ,::yaxh)g,4Ӧ?l'1sk4ǒEz>e[PFmPR;?P 2܇K-AqCD{%9(AR^$}݈QD0 adpA\k-(Z0aami^iD0^ǩ s'aFֹ~GC]w)yFdH+T`O]U3:nE2gS1G ` G&nd[>,z1vx:8K@su?id" ]>/n>]RWToD̉ɍCu/GV/fRZsKHFF_v;'MW^ w)vS庇V} k}R^[\;2h˹4qyW>F!e0m.ִXw'(hsaAj?e]2?-d}LlJp7FLEC9?$;YW}̽SL΀Fx[41&U\-1#'hOZ@t `bFo ,p ֊#So(&.bNvCxYLu&j-( 6h}ԊYsIdBss*P\[F 6ws1_f &bBV/W)#LLoO~]su_DjI6Z=o~̽Xb;l ֊Ց#rafd5Pv"c/$z5ZճVA7--ս䷐kQDQq3YvzaN) «V:0>\6E^J\\{Y3:BI:&i=AL#*+5XMd'r7K2#ْ]2IHmʜfg҅bG_NEVN 0fŚ!ߊjS_[SIi/s`:Os;B G@ёdtl cfnF}4Hd)ѥ‡v)L_bA5ܹ5W#/$A8" æ0m5# &|J:g: ޚ" $e^1_ g6fIΆb`ҖRfDXkHޚ8·tOhp?[T˪Vd І=nA;#ꏇ%_*B!0ATETP#(6LdjPt'BK1 _*%Tr̡ 'R LQ V_d{6zg|n|yNR7CUl6+)#f!ls'd':Ujf I+n4Z)87TL\P.D GLN{ӥ280Gѣٻz'vb Art D(][,}ו4"q4WΝh9+Ě%rlhӪ.~0 ȟYd ֓bF0C( m=뢹M`?k c r)MTRSXc͚8va:Td8P"|V8NUFy$0T:]A'z'I8m _̿/`epI<ܐ6~ъU?аtN'."mrI^$ON(]JBpŒRAN&`Y;Mw*Q jw֓\.g LG1#?g1Hc( 6Cq=BS,`RPߏjIfd:_T2߯]+W&v"X~MҤZ]lLOWE e%31img3Ⱥs{YR'טޮoڬzmj''~뺁b3/ 0Gor U #m3,Mk*hYsرlurܒmՇK~l2m#J eLjjtIc< ua2tKUx-gʬVziw ^#V|nnפPO}3-+י]Ι4voe`cx)` AΒLݗP_ãt29'rkR"\,4Lڋ"ZRo/˥L|7o珦#5bUL)t?84?2C~p7t'sMS΀UlE"?dW WoMt6:W7q%⓽9p$W5s ʸJgAr Ӫ/e획_m84 NmůAQl}V=l)Z~݁mgֵ[D8Gغca\+G uHQ_7`e06 / iSf[ ކ,RE=p9,$%F  >a0z%WϿz>yoHJz̩%w}K^TJ"m,Q0Ο[e=!-Hq@~d]k@UIS߳#=AlEp{S뿾U{wO#Ă+M G+~$mGV N&B+I3i6~`jLة566D0R,fkJ0{ غӅ)Fjkqr=x*z׸f͓u1 qmJ+ ,3'kB;:\r#3xYƛZDs/C->#N ƒi( z*QYΪGAI )PtT=I3^b\XKVJK!w6,þ"GQ-+qb6qSUNIL[ ;?ycFo *ZWN#J8GPBfoLolr2f'y1l>x:T=dzg[`nX~&j/Ak 珥( +o* T[a1*Z0]xY(" sbу&5m k9T{%niƒh<@ԅ!uzC_)>S0}{X) ;֮.;ԑ2/m#-5; w5 .09#^8'aj$~T5ec?HL^ cܴ;hyxtBGA׽bnx| atD٫Uw$g +tNI#;ў:.E|L~*/QaY|q/x>'҄8G٫?hS㝥3)!E>{`&.SQTê|ZCUx̜ɓ{!0.{$-d5a72b8U1mw!Y3da4蘟ᅴ7ם܋<\fArǗv)#AWBff^Ef>RK-++^_lf~3x,O.P5tZ^J&d{ZRA,p$9t2T_/\)8__&~Bʪ#Tc"~ +yl)L]ŏ2l ˜b a~D)b2Ȇ'ٱ7 "ڂ,QP^ȔFP-8k*ȗ跣6"ghBId<e'a=MH@R^,CbjE}cfܦm~Kf*!-H?jK^%L$UH!:>o)-Z$iLI=ѣ"Qe$X,S}:ߒ`tiꕞgڤFQ (V?0| (l7/S?O=dm>R7q2oF aiAOyߖF@Y86DEۿ Q֎f:<鹶ː%\E)=^8.jIVBK)t^drĎ#CV$T=VCE yU9#KN&.: }}H xfc#{h:_ Uz}K) w8-{ӭaZ!,hMUoy*S_Tcc1%ѫj8/õSjA_JYn˩XC 8 L}WG>"/OXA:#l\dXs/5Nύ1͛RDH=`t#A8%&sig,|5.pWm4L~tqQON"wNܽ>.wVXUd:^U~p,6 c1[@ZÓZw;3a4I?' M$u,gUlRwI[2lĻ tҩ. k ʧo1w U)`yvqpoZhKoQF /l r,>x1抇FUlmO2MĕK2xFg5Zl?z *K 8O><= quhqT)}MO@fiԷ? jc +c'w~7y8sG f\I<YSDU<.5_Qz~ATsI\nplYڰe9_ĽAD{'4^oI1*&Ɛf 8I,sߵr< 99Ip~դ ^r_VT4;ö6@X.[~A/ N0y*ug ~]Vt%z_N%?uȟ-Ʊڼ#Aku z:"V tŋ kҮƋ{Inpҡ9Tr oZTG2`K4mGa~LiG΍3~gE|}Or8UB Lnh8 $Yk!(e>R"nK`ŞogA'yb &3QDB]na<3"w)]{;'%Ż ?IpG=Dq.E) -$3Z 2#;Rp.v?qxHCi !3!4T|ܶ9 it C%Rd)eiY / "OΑPpKyMˋ!o ЏພMقxqcRi# ӕ?1vŽa acShLk Ekȑ351dNlkv,$ [lEKfS`&}Ю8t^`?6X 2/3 4jGx6R+"% e/xUwcLnt#qLEͬ0{'dֵO"N<:R^%;q,N󤐼 80NXԌ5eاB>aKfzA8h+i6z3Ԇr %-M-(_D62?/'KwÚ,aMsݛ,+Hn١6Z^c$mk%/DNErN)hI 3jJȹ~%=&w6Ȭ$![4&(ocXx=ǚRKaF,28Gsq֨z?FF99zR7Ć!&.KRmj_ $xu5e-er{噻x.ryԂ s\wXp?iNٷ͇uFaԯZʈ\႘߈+8n v a=e1 qHw*ό5]剑ȞJ;ymN%EPdf{NavR?C]neM*[yiZh)&;"4&H1΋^B%tT>X@֏ɆKE 0W e m EݕƼng?9R*,嗒P?'4*7C/["%>q?#+l|\z kl`zptmy1Uf[Rkjë=s3.mdOPlHc+pkq e>/@$fbָkiGn;liq9iϟVJukx,|_6* s7Fq̍#V^{@G[PjvJޕٌ =޳:qsAdWJb1pq0t2sw}--ƭ'ʐSx= iX:+87SS)#ϳQ+ ^O)E̡0?62{P*q"<WFhuk;gMEُ*}b6x7ș'ì.HO݂2 Գx.Ʒ&T`wwԲl)-?usm?9ڭAl ɶFn秋4vz+sy닾5 MwSch0$7tw_)Z-+{Ȳeb"2^qL[O.e\SLKũ$_-/xW,q)/`YD8=*B-ҠS{řtgNkXu^Q,m4/5/TgXꋊ{RMj5$;=5q4ԋ J(b P Qja( OJ Ѓ /e O`{-t2nOxG0.Dpf2'>cC(IC!M>Gq:,sT ֥%Z~U((lj @?Hqx95u,8E+]4'D[T|a4q?YwgɈUaEd'N8ܣ2Eu֩{:?|;VϨ%, `'8n).#s &l_.[qkrH65d§G[9).a3f p0גWųݡ '4Qt\嬿/ S%S-Uz 'ch/ڭOGCk$=$C bOVv _}E]Bl Zvf{譐(nSc&\Z[ ֤tX!y%^!>FH huR%Uco;+Z2V͎ՎdqXN+zvXW#ˉ;fg3J5X!dk\Cyf Acv c2֪Yfp׭T)GHGh+"p FnOc zҡ\L6[+v幫'{ʹ-&DaII隆/8t\EϚ~ﱧ]ܜ3es 39b$Q}m]\2R^|;v U"93J@z:%AXcMz<ߟ.1SFV(VH:]$IĕAׅ-iSZjg~_jr7h˼n2XFTlPi'O)j8U>S+1ql(9Ⱦ0!4ˢ=*|ܡ؋PǛrS30a5L9|qR$`呩K=Dxը)KRśO HiD%ڮ3-b6]>#k('[Q J?3%q]]}Ɂ]E+IL>q qC8 lڇDzm~ޞ"u5)#xLBQ܊En !9]1i Zj'~_⚛{9NIC)Brѐʼn5&Q8]loi:.úŰY{mQ~f,mOD>B%]xP.DE<2,tKq}-P.</&-y:bb+: M骿1~:Ll_9٬ 2})]Tsq6smJl={,!9>4&k[75(fT㍩q!{'["roi3%;qח2!E8El1t&M4( hJt.W 35Z2IpC Y*}q}9S|qKhAE`id PҜ5N (x+nDrFA,sC5AiаLd#)N'[E7#_;۶bw M&]AgJVv 3.s/+VD)I3>9L*QBd.gχ Clkn ěԐĩ)(̶`YIwE rwl܏L׋eGDyImJspZ4 c~n vIנYxu| $Q*rLUe} h|d?;>A1}XA,0oj^?T|jhFzW;E(fwC1TX#USjU>[JyE&5ulj%Ko6!4@ܰo{AswaNX^HJ :N?i cy+ۤİ349u5;GvUs A1F`;{0z}f%o,E&[aJ͡hJ:?cL-FY)u~[i~nPy(3yC9 E4Z"E/?!H=N7! OK\ZKM?]42~zDa"_!!qr/dl1FlOhc&V*,9܂O*Ԛ;%v+c|1좪?)=Q B˼]Ed)=:3vw&'vDC?'cD1B @O{"i\o&'‰) Z1{o樿-{x۴/Jy##a}S鐴 4"Gk%% ,WrEVkfĩ8-Dr-/8{Ÿ#/mdJl1fOǵA:H Eyk?܆EĆ|adRpJ~%nud:÷skmu_14yE): k)qN86k.045%1?XtXcr1 GX'RGp{Y.nc)A^\Y+ J[Y? Nq{b-p ohYG:!TC>r<"ߞqUYdzxsk%@xkW' )6~D^%p24S拟f-o­NLjvJl\Mu4ݦDVQۓ5qJ؊!Gjo $$U)ƛ|c[b 1Byޞ4E,r BhW L49 9m/~z-Pb{iWQIxxxR0o-PLcCK`VϡR'6ШܗA]%g+7;P-0fy?VTg$ȟ!0E+ 'x=nfg+sԊ}8=x"ڇtJYoԽ:}QbeO1nfMhk ΍}g4RuҖcf[SKf==+^k_T@,k!oR!ˢkZKCg 0l'"`7G<sAGʖqi#ڨUR5oIj`?H䊐/ݓ>eQx pi9Mx4 >N%Sl}6.}̞OgZ4{Y=!8[<3w~+R_6tڶvx==jK9=FzђME\r?{ UԗҔx0u,PLp."6iaK>'Jz,M1ӚòX0b I81Γ'55REFRlфͼm.̲CS EA3l,ʇMV2$F69`ZeAWS}YD_XFdU #=ݢ9*ozu\cߎ4YLԚK4G7CEfZV7,rBdVn H!ON!~GYiE51 ghLH^) |Py.nic^ grvv rk! X{ܳ,өIi{󔛴~" ^~29⎛Mu)@2b*\(WԿ^c$J~\Y~YUǨv *eMXmuh%xSq#fȄݔ ZaZMRcԝygďAQ42^׊BCɻ|72`:.D Zq_ ߭K~w dxM"OFr\Q-P.q #}c T2xaɀ;_jm~'*<*4‹>)rپXqbLjՐ8(ΞӬ@yi h7-xjTnKsC n1F/G10 ,oza bu:L P10eUi'Nc1Uǖ~ ǡ% k잼<>.$kmϡ7?:8i4ƙob%ƓuPJW=IϨ3hΔEV` &*)V` p\\ݓnY;qOn9'CZ KB/%[bf=STV wGT{IO~ۘT.'ڳN>,U!6\g`1cSubLx3b:+oK^FnJPAsx]<ھj$ k09b[cˌ*]C?Ǹ6,=$~eWG.״Sa0)ttԒIP76YRx{:Y(s]9#opUӝC\~쌘yMRM6*l)ap|!X!}>-d=치!aP8N*,D6\?>f!_N]n6v<М]hB܍d1Ʊl0M>4GgI=/#G !GRNSPCq?,Hnj< 稶M m@s0gW#N 2a\ݢGQĂj41~$ ^c6al1ifwn2`ڽFqM+7N,rdG-Zpמcm8lQ&Ӗf -d:_8p8+wijɉ]'dJM[=hA.9MT?0%1%LTh" ZΉPL_{F ɝ n27CRŁ%]\.rp(&Y{Xy- Nv-v!݋aEǭL)~aֽ#!5OO1P,)jn}$'m9TiŔ8tYAv-GZg'#DU<•V)yAє<9>wіN&ZfN!'Җ=b,%ڔ>4{'c2MK#B ,X7a!,P}DDr PGa$]uf,3 ͯ] ;œoJ䘥HJ/, 5^"MΒB?ENkbOIcqz#wSѾoQDc9llnh(a8pNqY&or{WQV1$=qOQQ~ȅhȕ&#pбrUvl]PyS<ʘ2$WytKcק3@[w8_gNJZVMG_gc8'_@0RWc F֍dL64;$Rm/PDBOETwf0nٱUܱF6?8x&w( skh3>]y%=S5(kp&:,*״}b_Lj@)CC~$;̒_a4L=^Eɚkv4yg?1.i֥q}_ .4$ + EZ,3/:vMWjQa.S9>,j.%srzw"q(;#ܒ-GpC ,!sPvS^B}GS9PDŽ0UOx߫CC't}+g?LjBo L\I^**>EnЩr\Ke11fW50Bj KCp+a&bM 2uh;M4/Q:`BE`U=g/[gj;#DFE4 AT59|Q25LzF;*."ĕ6~Ŕ;Z|JYNJ>;9!Nf!/Y{yÃ,b}q1a E(H~*9ƶkԬߖ}IE!ԬM}0q"vġ{y_zdn4Jl3}Z_ I/eުo_HBh"+yѴc[X?B:21JгTJߎ>6[62ǨX*^Wͬj0d0­mnN7 eodsTEw&Ŏ qF^S)l{(ػZ ; G#4j8ږi h 󚏄Ġ"O&fq02E 2b8~S(rУT-'=Фifi: 0 G`I&~1rn+BE>ÚvTUn2oϘ2ۻNz?B(> N}-5C$/T>4ywd~xrd[=F?RoF1n̑$yJFX{mnvU\Yo6vϛxrýEBҌz |NXv&Jm,-:G#w ˰?V+#d;%E@tyޤ>i`$6kvOT41_!1j4?Z9-BDIo(d?޹)an]HT2F6u<|.X@G߆QQ W ]Q<Fsx(vؿU>M}!m+~ܭ܂b,z5 m1I̢1H謊-p5j%'07%/tˮ>eצ}Q;;|djcE^) ^:提Ÿ$0H~E,~^rZdG{P-(Yg9RRW$ )'d'E[S?a't>龈0&@᱂4))]4CXs#'݇2`Zʫs3>K9:`y{[dd#ZF^yf"S8hYg'ԉp(qխV\F8o`ع хm9]0Tner^2}:iMH0y#Jޱ}IZ35IM= ʬHұ\[V%cc 92ǯIcIvݴlgVZsz!vc^(7IHNXx~|2 @ E| 8SÒHo7rd||5̧Nq_fK3^Ν;!e'9ƜIr̽/cȣaUYcŐ'#(Z\I8qUƦ/NEǥG M#uG_,FQ0Y U*.f^S6QIO֋9}ޠwcQqVp|ќ1SDC0|- hBbO~~3OitG&¨_=TDփy$G-qGe$&TfDӵ۸D c'㩸Auž 2DZ># kHkA`Z;fw'g_{b6Ʋz=< eoҀ9ԛa0 m \G-_}#B"4]s2F إxw϶H2%3:En&f'p ɋPP³|Z[vly`3A"Y&.M`CŽSAo]Ra}F{ 96qbi)?)$PκyIG{` >Yє&wr,g,Nn@K)gҜ׾5+-7RIuC~^Kӱl 7$j vTx/CGjjt ê嬳J'0m)q iGp;.Qڎ"yA09:C"2fb^Ko~(权E}q/ݱ>:r0sݠ|HrNH"nٲӺTQDisZ8[||4BpmmIG]-"O LITUgbfqQ#L2ȷ(֥G wjnaBw!l#D5<ʦ:$}E_PZSLH&~/4uZmDN:"͆ ďĖtqߖ+a`"Q*jYRzDb Ya/t(Ѫ:\T\A[͏ݶqj_ver&PJIrR+$ș2. 5(A"W|!t'73Ed^eLI03A$䈐ӘqQ_;l]O S AFqc4){y^U(%f書LGX׈[=*%RE_4ʍoU=OiAV@g&s3K:xY^YcUaX>=MX,mئ|HynB1Kl^_lWk 2oq|}CO`ȩ3ƒHQ#zY:L\!6g8PV{ Yd$V:yѝ6K;/Y$b$]Gpa7v˃ ƖK kl1?ۆ ٳT߳ӂ<~_JiɌa;֓x"nL/;`H/Ft.$ceG^j$,ү7޼lB',m]J9(_Z2qqSLT~S=Pn}z#Ka:` +`^݁:7ƪCڻDqt e'oE`3i=#HyH`2uh4J lgAO#G6uFgmgRe}`]D٣0܊ Zr__VF?rd*M M+yʵ|˶Un.3xBGuO w",tD`5ҟ%TZv]+ÈގTɻx=̇TmF ZEƒ2`Щonnf{)e!vj?q3e}S]2g܆zLZ\Fa]`(yD{yȩ("<S7 K98IiIR; 3F(wmҽGFGa\kn*B6g+^+gY þ #J(C= ^79PxV 4^84h9)`= L%wx;R+AtzBwń:2$h}س$6TOא7J쾛\dF7^Myw)婹fZ7)FT_̔hmSԃݔ`枅 ȱ>7a!o[NdI@&ekuGOi8(1`ԺQ+@)7irS:lU#3(G#?2D)mz5E" }jIQ#fyڛ%Cc\d1Aq!+EK4V1LT9xo!$t #, +᡽8t{_(18&Tk7&$fQwSTMxxs"h.!ґ]*C7FD1\j-H9]m|*v`dʊmj8URJP"EEH[Iq: 1g4r>xȲRNrQe&}"Kb4#ri}(,kc⑊RPAG^uր?}~,K\OrNνu5Wkjcl9#]kGҬȆ&`INĨ3sDA_rôR#_NnhxHPrEB7D?[j2CXkىVIEi<'Ӌ q_ /iR;G6pgX-jrr-CNf;Rwiy妈 m"fȯy2v#0TT!R`N=GNy8DMUzKb8 pQQ*-!Eűs9)_?yA"kL0~*ˏ#s6f%4^hiy"]4C5nӭ9`Nlՠ'FK;{']>B>aÔ؎A"PYzgǿ&'G$Ib8jiGc*d'6x}zizTjQ)h?i #2ݗiw_$H7{x>Y"k!QK C{#өr(S,O ;]7%L'*Ws;"GmTbphI]8%e1}aҶ\:IȰs {쮒6[I7jhY C؍w 3JdQ7::pWWJNFS$oA3< A 7t[? Uοt0^VFdɨamZ$ I2uIH\jȵwд$;F4'!2:l D7Cvi' iT?vgQ6w7 9ZhIˎ`O;pŝr':ؕcRuU~Sf?Uׄf$Ft$ {;Y䟂~׊^3Ex-S7!YTr.BG / M9y=Fy) 8m2CrJGN RF[.E˳.^B37BDzcE(XęߌNc)'I_@ o%`Z=(C' 2M=wFM_E"3:ɢkC\ӯ؊5]E!zcYђ(ޡΜkI/)e 0qE6g4=w([F+b_yFC^]C5ȊqY&P"(F $πVe'-kmdLõ-p#gn!Y9P9ϭ3Z1q=T.CFQe?`c mDe4mΙWWuE:8&I7]I;troe hݒEvLт6$`N!1ט!Sg_6i [fvK k/1Eќ±s ū X܃a܂"IWhm !M&~F2#Lةס:wbfoňx{,API lFe]Yӱ~PdV`El߭3QȬAtYoItCA V5ra8 }=o p|+G݂iI1"ooY2Z{ ^: AKRsz]&lǚb'837f\жW?-Dx;Lޓa٭tl0xD+si̇x "{EjЗ!G ?㶐}RćR]0ÑnYedR țl6b*d_\0TJ J߯eYuo{W/e"|I&fLC/R i)BF>t3=};q!=&|\K#56oGT "Tg.C_϶@d_JqH ԭ^zy>´ GwEenrN^O[('߳ԕ'hC*dcRE{i8<ܙr< &uk?hB- )T0#f?L14 0.&zSS`Yw,? u?1~{^OyoI:5q=rylsVȴ-SܟN%z}נwףOShԇ *0"1&'F31E>S0̚H% $.8+|3yhVS Yt&,zܭӌZ^Dza D( a/ƐJ˒GͲc<kmX㋝|7)}gQ\*U릪9hȜ7:vb:z=PA h/H+<4fw^4ILS]l`5IU1-n$De띺c֫j2G'nO؞;Uv,ٌmW4Ͷ)&.Ȥ6Q^ y!K9CsBU$ ݠxB> $lb~"wj91OӣHtZi fPTskǹ7<%WXYF)n,pZPu~\dXGrV)mԃ=B#hf^*&F иמ^]*82k0R LHBs}(v*+N}R 褲g%l;z}MYyMeM(D܉?ɭ1zIQC['?W}QYǽII=%b?~T=en:uQk^z**)Mc*jHB]a0NJʎz#۱:^՗%:#7WV>N̟1:ܡaDIKQg-6{XμL[LP@:AHF1UDe&t6$! _uo6 y(G (C0ZJ"9W|`c|y D0 2o# >XpJlt p:|!c,i\5]A&y,lĹ0eWGXokoVoz{^˯j?- G_*RO+<9s5+璺jS邴 $$G`7P ?b4F^W[U J+K,Iħ[ &gp[y%x M-نuG{jzi rn`knS?D_/ôY7l +O-@G6vXݯ//B."|T}$ g_R誔,'.JOc\d#;D%(o@'4C@&:aɘ?X)R(R-^P_K:qq|`WMkT]-"~' )THX&T > G.LF6r.heue3vDBx?⿝c!F NEmx|9C >/6?`>x}^kBc o`&@}>vXÔp(CA5s9._-?Lσ@]Ԏ_lA $! PfIRR+tG,A=M(X*PO/\n(q+K_gꤹI:&a*$Ay\8D1G/o$7KtW2ڰvЎo{IJ|FxS/{*ˇUz3Bh2<4GB{Q-PyP"OaoO/s1!bWA` 0eϒه)q_:47%x,R+PQђfq#bu\Z R! :-- eZfj V,gSrX|P&F&ҬV1GI2Tqu MJ3u `,ԡa!؉:p(Nh+vI0!Afx2g' cbg7T}z22oHRUt0 $:kA8Pc~ @V S~=208<_"qw} &A.ĚBc$,A|PW$6`]X$.~aSM?p=􁨋q\ 1;0;S*`vٞн2<8'?bfQYduME0m?WRK 5r{A F4AP.h%}mH2 }u.ٰ&D4Ɩݸc>IDiNo*Y9Jآ1#={܊?WDX$7oթrBPK= &a@&ERmFs 4 tYVc ̂N# &nN̐:参 >i8gO> CDl0@z``}!7"#;CYfpǯYlKJEGt Q{M;HPL}=A;D"Tk1뱬ꏂ=DL-Bbi*΃yW@= 7h9$^pQ!v7!Q2`DkKeJA_љDž 6.Zu ^Ϟ! rx*pZ)n9 M: mVgF~}M }%!{3j#\_eywRZPK}ʍ # t?Lu:Kۜ &cl?O\t_ oe"Cݍ>_WZ#QTIIb®t<Ch XT`#Y0ANJ''^K 1!Cٴ𤎲UfXLJ#&ؐc!B [:z髒i.]^Qh}5 ɨ@:}Aˁ~?/zzڃcG:`d:em"Q6$|Bz ">b? PrRBQ,oxUVR h|M957L"_p1Vnc %`6e F@3YtISN P4i=f4'y?څ'[-[J( Rkow`OzD=gb*J,5}kc[KpVIɘ/, T[ɩ`za'/Ơ79p;tF [IFJG 9dF O 5YX"p^e]-eQv| q#w۱,`x=<ѺI}T_b5^4~h)祡g׀7e~RB @Bc=yЄb)ŸDUdO7_h>?|7R_+LcfxA aΆ\\"G{q`A87p%Su?D@5"A^ =S'qBh|_q=ȆTE8qJ>2'9pQ΀pKNSb4ˁ6QU@Pӂ>hy} " ,jRow2 E{7|KB*La#1";|.:Q(Db/lqtXO{顎,y3o?އ Ҿq7d>D0K3{ VGT ˁ]/2`K*{~MX, )(CKvi=dAI|LX Ӣ+%F}seD!&M FmApM%mυY NfZ3yASk_fda]q+K 0Jh$WY=0kJ,%O_MPoƌU']=kZ\yy DC:[R1.²I5RG4jXyܠ\RRɤ ҏCfNŒv?So7m?|5' fXb*0b>~aY=*t/${0]49ruV]7z1YvH;(¦94x21Ѕ$t+r+g/CO[O@͵,YGXYۘBQ"8֝HIz>tg4R4V'&2JY0r"UccŔvmưV];f SSG=Hi[&2EQɓa_H'D *kSQETne@\ܹ}qݽ0dlDA(5⟓>LvtWЉM O1⏭uJ|TE M2_t)Ow58bRIYNo{8@X˅z1Q|.Hes(Js7J4VuDC>`' kLY~c~t0cG{O J|,IV Y͍g&9]]T$NY}"RĄA-'$ҘK^xRI;'1~@~*5"̐L;GPA*Nڊ) %=Qjˌ1>H<N0f>sw5GZ:-DӝT]0g}Il5>GxKV'yULsV/gŘDU,,"砞xI$ BS~S-7wTk{TGaO<Ԫ{>eQ*fziwn_ +3$K0F0{1f@y^ d`a:5R&^ iC0}P0JrչŐ87 ? \[[o0gDG;M##"'i%h2R^#. T OLP97@ZU`ߜkOț^I^=;Cki ~%Ѵ#sQ0O%%ӽM@Y`e4jx>:$ *Ec'If I]7d^6WfO6xzFaZ0aC@тpU) ]>Ejcg D~̚OXDNY*[I]^C*ub%QVmL^*UKwܧ1'̱KվY F8Uq4׶^8heh'`]L*THny%m#~iq8(nN0"U:?{3-Gn cDюiT` {_3|)4cZq=p6.]'vSػ VB!xnLR c0fOW?1-RwgHQd_4;qI{Ɩ|Y2MNхvgo'EHyY= ן0)kYZ ,?dlV#F$”*/0pђI3<\vgSMȏVAAvD |`= E0= R{;<0clF{'TT{urg&# úCS2<2 B\#9B X/*?`E+ҍʈUܰ'Tw!ZF Xt֣޾в^GY vYsԦ#鈺&% |]< :Pv*xe_j_XDKffg쐦~S&y3AQvIkxJо bC\~D ZQV2RzKGܮ"e+x'4B.Evz05vm7'_4ϧgY9i6]L Yj߸tFTm 4t: GG񎹐 Ges(LDA[3;.rLkR791n<1ҺKQPc.[YAaQ4.P لhOsp&tk\qεrc$㱬NfܲGwŒVo~(k!FpYskPe 231<ǵ(d.HHy"053[Pd0v-[ʥuz]2I¡27'; aP߷)^Z>~<ިb$h7/fH^9؞(W?X-" Efg&dߝ'ngfBPxRANz$諪6s}Q.԰+|dXKYA.9Cd5$l⦏mQ`o.H~@s"a!x76XF d%44yTV>T!%)o03c.Q ih'd&DC=m0GێlaĆNݥdzz"%qTE ꓟbbgPZ|L0"&^JZݡjTy8K3"!‘[62ME tZ{Ԍ1P:y"͔JP%."95<8~pUBxv|e*凯;ZwIJ])9銝Z|1PEp])\0oȉѺ{ECUe ]sUVE͝-+[G$r*E\dA)^FnY9A<& @ѕm1 !ܢ:Oy8Ge&vK–Ѣ%OZ.)4bJjω:tz*FI7~ exEn /:Qkwk)ZA\NDcLy/+Rv+v\ FaIeP൛}NA O' V O=1ѾART|[E#>eR۪Ž l2%sZK &LAX¹lE9+6S/ UQF 1ЪZX $qéEdLc9=̑` ^Ďh}qw,g׌="tvNˤ=Q_zGyd{v+f:&Ɵ$%w1 WݴBA.۴l>ZįkRW67.ԛ28\iГ LEQa#pX> là VcMG`&?dsJ{͙sQ"Cj'A&;~>&h)_)DYaYqm=!P@(8`/)j&aN4K$:C:Q_b0mC[޸wa-}$Sf P~- bTnc#(Oҍ XGeC.xz]2 G9WyNt4 _.l[eq`1(oyBu6ƚdF.'D`ڞo'I!sҰVob×*y( Nx|Ҩv.IuH`-zX^ WJSĸ`u:/t֟:ߑ倷Dԃ-1WҠ}y{$r$.$zK]ބaG7ha6V/B!@9vYf $N)fTX~0t'τ0P٘dBQɱh[ȜY.cpLFwDH-<8\ *͆rΣXp[vh[<%td{zdIXW,za,İ0j%ۍ)r0]M}KX۵L3M` 8VZ\˛l0,U6sz.MغYfq¥nƣ}:MjE\Lڂ;.q[,_]V0'Tťm}/8_OJ16c,ZDZU(PͶNI5^*ssv>iGAy4)/S24xhRDv)ĉx_@eIsZM,W~d/z͸Maa.Or&ɘϞ>`r]MqKQeBuFo 4`n9hA91!}ђӼ+PzGAOv{Mjy=,98*-yrhh=Y;u!zk2Fn;ApPEŭcof3+&ALPfc7.R7اF!ِV\b*QkP'8]`# rU۲ ,64ݘsU2Ur\>i %䎚WکhvwD?\OqDlb!;lv!b-+,\D&GCndyp?XW(C09B_b$9 Ue,]V2hm2}1_X,0ߜmPgǛDF~lY|',-`^ D4346 \M y8-֨ }n!5sՌ F0ef q oJ4<Ya?JdH.ϭ~CY;%;ݎŎ4v4 'xځ`7GPk,'!yRux{DvTK}_5hK)+)7|+  'LDS <۽3չ)J%)9%vzp|~P=hoZ? 'k#rv w'B<uFҐ 5%3{a==GHw;V,}y}믻!yL%}'r OՖA1tU&sW;krbU;ߜ_'Jgyn+mL`֨80ʤ&?-X~"j2F@uLp'# Ǧ fCph7WG.Is"o6{9293m*_m}݀2&,ڣм`z8%t) &pc^b}9q?T1ZF24wk*8߮O ޣQ]}Ii-'GbfE}ٛC1{YrzΊ ;ٱ>sAGRsM|5;n'⢖MLHԛO0&V~{^~g3[dޜgU;_ Fi5# \wptE˓#b<|9Lo}dԪD,J0]ݤ)Y=uٯ}8 29QA}'7diGc+0VF1/*Sr$,x9(kc+{l܋ I` p}ۙ N/l4+̆Mw]Vש}p&Lhy3߿3Mq,$AcH&BF!  q[#QϩbqEJygbd[eE8lfg/ec<Nʌᇃ9CM3zBc=ԯY@;RFxy@g:3NZ ߷/䱣>ߒ+u9uJW,KێdZĢ)dOVZDZqK% H Iݼ#%BY u:ߘ8̺&8gf4 ScQ9ÎߗPoڑPx+`INM C6~;>V*,/` >GFnrqy$eVMl5(D *T޳O&`=*qsmxMw]nD~{O1#|s=ڣ7AKpK J>ȖetQ#fk$ue6E>2Pby3Cc֨1BaP2;y2&ʶ/}p_&ykL|m];^qX1{:8 y(gw8$&6 {cr97 Lao*Tr1o֋Np+J8?a9Ѥ&V'GTgيy8RPT1 "oo{+ 1}p$'t{q|7R{9d.bD>qTTz}pK^:A$6xh1u"a@(?NH,abFD45;r3dQ ;V%Ȥ0*嫚\Q⧿\(“`mW[@{+POArj\= 霿O{UIG'9jD͎V7؂QXtC^!0d,[c3|пf#&oQlH2gjJIE5&;}3|`bf" b+ѤŒ#jfRذ=zqt я5 %C'8غ 9!9U:_j,D'2 D&7L!ZJR,dtq+ٜG 7:{?C9R8/gEQCšrWeSlǐ֊@X4TBs臊r ^y:Y]67gXva,#:Qg TO/+Ւco8]tc+N_ a"grlJ`ů-WI'3jtuV`d jȬʪE"?:/%bd6& #Jg 82k+_;n-|U7 _tg1XAk`/)mPpB,%[.Jh`b3HqWI1_8-N i+Ls|s1+7VS&48&evcoꑣVa' 6=WE#oj5=&tUc7#ULdF~ UrꢟHYqB뫼>b6/b|C' ['&Lr]>o~C1$[NWqqh[ A_P^XxƤ(GAex-0Go_,cK HKnDwx1軛b\NֹDGj'ܱFr >fi$&p8H.߁h1R&In.6N2tIE?#kH&t_D2Y㪣 );*lxkXWhxYG|TDN}oAބCZ7YΏt%(2^l 4cL†Z USk< v?&.Й3ۧwsm.Dj4WXAj9~Ùj_k͙ 9/EYl*VaE {r,Jh3 @9kFIRozƀ hEZ<Hd=TI*[.(X AΧ+lT(L($fLj'tm`YH/M& 4?G:xAqŘK2,RXf35hh)a=E>B #}f KLE_LDӄ<)w/L~.z\3#|A2%dI"2m}3UFH&WFCoV>製;<Ѣ?GUfPZ|æUR['LWopgM~reSKSa5~+#GN$"RȁpMɥ2D{a-#“bu0O;Wf/+H9\K&'%%PRƜv/B`DbX99E}\ƅ0T{Ȣ@UG.,9b$YIعH%őb䇸*ft%AfR_@`ϧXHj; WbԶd*nfI RnF)M;#6,&G@޸B]#+5l4x}3b0CB1HH윱YGopH+}/9Ț]SVmd܈}-f|\c6ݷ}$3)L`]Fc8g Zv:nEs}+! aK@2:`N„љAZ,㾋)МCu7](QTNbpܡkMϥr hFCw~]2-\|?#C_П3 1ck4V$adoi")d>'(!\q;x cOk/=#gOHAQj)ƓLw}dP;FXsx1 % iܼ9v q0_cH"[~O/b! r3- CBi%0 kd~81vbb\])fcy`2e7nz%ye=(o>HeuYQD>i@9~LdPrCN!ZbOO_&J\OOwc:w??u&GC^E Q/fDmmM)G` lN{-Yfqw9Hߦߜ1,alNi,ykRZdRl$S+}CmLȍy•JQB8R0 .iE HȽc&$K ?k|II1!LN%Sz`V _dr!y$*TB_eK˖}y.Z ߣώzZGݶRJ`u><~ 4ycO n /=<ːGl8}d~#qeq!F!&a΅oRkroUZ@/hތn4nͺGyQBpILLIdxTI;n~~PUV޹zȚttdf3g]oY/XQ8#i^>3xOI ec|S><;q:4Մ T?ׅM# xEsb 'M@EZX`H'pmZH(Nnn$GE '2OvUtRܶ| ުt6\O%Ģyֵ@/".Q -Qڋq3B(H1K5;bLgPm 10ly4I@2&6PۙI[!,g@< q!LġwQ25\eO<  s[9ܔ{;W/;iVPXSd[X]xٞXTrtΈ1#]#gXz;_RnIjԝXr*I:LnZ31~#d)7' @& Izjډ@:|ae?H9=>ub'- Wner7Q8d`~غiǗƊO OʃpOzuG 'hVVlz wH..hN?QZ;9c"he!L97u+NL\]tˁ?')vdOGn!|_bjʗG ɁNoJ~GZȜ:Mݕ#5N\X`V.2D\; NlIC^7Ȯ3rɕ71"~tr9呁vAo[Oy 9ap0ĈfeLUUNǂ;#qmײ,׻Qm5l0ȳj8*ghV#vkF# A4?pGW3F79kVu@;YGPV~3t,Qz"3.k!+`M>~_6)-;(aa&xS%m:mrki*2ID%-e#Nd=2=$^..c u<]]ɝ6_ƻ Qq-;vB\x}m=GLLjɖ:9bm.UD;>㶉._b&YV_ : +U˰K޳;`ܔLzR@?Zj$V.D6}ʋj36g+/CWIFۉD0 '9 .3_ 0(_ڧ ~_s4ZB6 %q&L T屰 M)R3VQ  A!BY y6ʹu1yv}a`D΁Cj5}Q]cV7 W:q^&xuE#yFr,?MF$n)CCceJ={qK06H'sYCC]u),,"X9VvC{":3b ]KhPK!N"D,6=LɟQ3<5UtQ,,fEhraLX-SH. 8e43p?wۣK_HTJ/?n?&F"LB/ńXy1* M&l+$Q\ƒXA|ȣg4F6,pXFЭeAX2,INM@v`;ŢK+ "Tht2#peʟޔoqk8Gf1BAУPF&U,kŹ2G%7 {bM1#lWUe˕!kq¾w+L*LO*)LUlՑ>H!lh'ay,L0&fK,Mq*E_r̸z_$+mo3#HMA qZqjNwkKJ'G#CL^~#niҦ(ׯO0-ah䒄#4|d'㴆Ʌ]2FCJ$v܁|Y157qc,m+L/5fI|vp^ҹr!츣QlD k"q66ˠ2PsҼ6Ȃv/DFQ(HM͵=[1!ڗlnJD֜%rCDˆJM؁d9R%GP5aPU1} ηĵ9{kD̬٤ًí/dStz4ijV__A!ץtۖ.!mwX2oSK)ʭTJIR}+\H)AA;c䞢3Y>`є ࠸!`(h RP|:>RKˡhNO!z3q?V«Z<؝9Պ+r+2* !3bʛ'@;V])ֹз<Qg6AH;r,/ih_Jpp%1cM1+cـ0ID)>`EZN/L䜑Dtg]#R9aẈ rtXrz~E7!!4.HuerE{1̵e' 1KՓ9V~iMPGi\6{O@^,` C)R(`k ٙ&EOdD²; HvEJMV*=!H*CJ~QcХ$f(!ጲuW#@ܨc5'ū@Ե4fT!ȵ,]I5k{"ܒqW<#nQ$y[^'4s$dKxad9^E14 uFTN'SdȘaj:_Ւ.apEWs4ǐ/&ű:ᬦׯI/]]ʫhTU+r4Ҕ}׿|s%ҷ\<Ҙx-rU&gIYPJɺІ$7ѯf㾂JiQBlQ9(|ä!b [`i&F OH8x+TCg rΜare 2\C_xʑ?ڽYK{>+(5$K@HF$ E̺2_4u8Ɲ+QrFxDP_aK cq|7E9.\:"5Wg z1<$/oq=EHg'^}>);bS5+S455㎅=*N~[ºS}L7Y}6XO {Ѽufa۹eZRhV)lFrD3:^U=2#+i,}a?f{o* ji7a8.QkqnpLTRӽ\ 0ٍEXxGFtrH7]GSgpZ3X|-8,gQϔgmqQȷC3N86\sM?&*-#Тٍp %uB]Ҙ?Cy?v1ăBkc-HXS2as? ڐ?(=G7AMKU1GڛXh Wej |Yb7[e .>~0\ؕ՜SzZo?jسs\7y1jqz^Dzp+M,/ؚ5RڶvIxQriܙ1;+S0R;} }ktE,O.w3òq dtL}W!Q a$g=rB>l+ qBCKoszEL`2^eTN0jV┪Qy=,C~lEɅ0Y2:5U~㎗ x'IFpJE[9@5QU&!ow?o˱G=XLm8ZtS9cҳD|PWÿ4CWYgU5nBZBi\}Ȟş'4 EIETZ;1 ,k4P24z%lɂkҹ:a[xGt(O[C$<)4?yBޙ=nTaιf%{m@3v?N^t@IeK@1AVOS/#Z r%ƶqq nnwp>]qorZjyr!b I)ݘ HrEsz A?u 8v9q=X>Z%_!7 9Vƌc*^5)"_q$4!W]IH| KZRu~0 ;;|]LG:WrȐ!m k9%Yr"^h ;[ɮT|=!-:z*plo6xf^H5M]Bb˲M oɘg;Z.up`}cGa&̲I'?dɓ" ש+LM1>E #c;cr$^hT0Fފ~u9w!I>jtQiBfvL)%ұmS3d;k7+'?,:6JaJe_Б[~X"zQLGZӛj=+ޅz gXR@)'gbx}$5 ENT{ rM=3ڝEݢv ຟ\dќ`Aw 4&}cC^et BrVs?\>X.r5-qe)0pU |7͎p7W^FX\Y1f1\G#14hL mtH QURˣ BBI>~HW'(Țӂ1/@ ɸ<{_1<52"ۆ&UQފ.]"&k'kcǕÛiŦI/@WHz(_F ?b.}@&LŠۜJe 4|,D\N̛xK4شB&) ro |R+jV/ޱ$D15eIA|Dj 1s@)}'U9rB>Fd6F,)?Gtn^sbTzb_q"%YD(\M8\Qp?˘hc+R bQ fHRiJ#OccDIi:2Ix)\ Fی`83g4gPP똮{W3O~JX ݩbŗmOb:ઇmҦJ"v/mb|A߄9]_PܜoQ񣂯1L'>t]meb˻ 2">3'ܗ3TN=6mY.-kٷr\/uT:I+^‡ NW9U!c%c2,G$|T_>!gy+ԏpu(V壺ܳT.^s$ti-i}Lt30`E=0"x7t ۼbYUar`ՌvW Ғ/LrT-j.At_s+W; 9z8U_ W`MC !1֥z򡔈.h$)9Ϳt)[Y4ly̫7je>&/.ؐ?,ŲI[f dmcH~Hþ $WfE1w Tk o7J%#s{8nHw=U(EJhe49l~Qu1ZKn1JYv |$QjhVwDWj^`4q.)H7'69Brm*``lSg_MWR#KG=]a*̲HއXQx$SM"Ί&RD}X^ʂd[ NpW!?|ނS_ȭ*~|.6+L4dFdd^eLͮu>I="-RijHm0ߩ=%fui\=M8TtB ZCzҔCR3[ ًF pGdzÂEYd&K mpl  ^\dRF)6~Z p[@w6_rϕ9> eNLyNԼ .QЌ%01XڥwX+I"]譖lĿIJ6<_'z4Vފ ɝqNi0o$R6TɍP֟& C^v3rPgMQAP{UNd;#v^E1I߯@P@.Pd\.!G"b+.nW?wHi2*9_Xɼ!3!iPKޙ05B&hT/@KOmNJb=…s21v?('᫜"ƒ'cX922x>af "[%as}yw!P-;%u.=u`Rc".6]x4peEhBJ1WxyC=©_9jGUq1$rTQ(gWљWkJy!J1]=>Ʃb_)/!LJ@~; _Uʲ!7z'>I&1ϝ53vSN&h >;ulFPq nE]Ԧt=UlU0Xk$U'ȕ;* Ɋ.Z1d;du:Bx\8bwI0&@j<>pP ގa"|PcR9a>iu.#4ȻB!%Y,ZE)+OL Zca<1:2eš?# &o.Lc+²0GeCSt-Xi`}Ay :*-'Tr)G0G@.rιK6C :S>2Z?~Y ,hZWP6q2 y/«A%h \E6?86;0fh;!Y iW_ /}] 0-μv^_ 㴗K'bnS*=&Ӓ31z&2)ĩ"Bt_' .z|4::lX $Tퟲe)$lZDu/5ExќS_6ZOT"g\ LNX =\v*Z!D?ZqH[IncdEP1qڮiKiś$~UŜC<ϊO yX\Leǔ픘d3^EFhӘ&ȹo$ b:N|/v~+ aQG0>֦2F6cW,/c'JJT~K&PD=P8Brl E$*9ŵ.YUp; i(~5s;diY:_ 1ƈ8j' t tv73&&S(5hcGE]TeD\)RV7c> shc3}72~*;xRkY R:D11V]^Q*2^:1/&%wY=9u c{d@۟hN)3k8pOrhM/T/X#Wr0߲AꄫH(1alkGOi92[3b5t!ВrM4e˹5RLyUyMxt&:d~}M Pk F{ēKc=dbP}" ^u3Y+Qۡ8pC£=BD]=@s7Cy*W !,oxazQוw9P"p5}d}q{5Mz<PA@w=v{g.\zfy舽 0tH=@ahG s:FlKc0@!@$ @4(@.g٥Ve䍭z}`  h@[@P(( ( @(P@v@( @@$ ((( &MsҀwxMQT+@x@mDJ}8mzT %yx(ҪP=PP"yDRQJIQ%,6zfIJR$w r 5UOh&`&Li4 ɑL4 h4 ~& `&&Li&&FCLPjm&iM@bL) US` dh&Fh 4)ɦ0hi!& OFSS`&@&&dP*` 4@iFhi1OCQb?SbS4?TO*T`#C@0`&&LLM2&4hё@d 42dLiz|8&%!*< QDP(d ϯ>03vfdKnt3kS,0MFjP↦ )'A^w ʳ$f ]Vu$G1T,/" 2 rexV)Lq̮H`.[Ze,E- Ws?/BsS3bDDB"S&"R!R)""`H P֯fY0)YFكFvTDSi2A2&L249bzG.QL^M?:©B"/ᖢ6\KZ]aw,4*o #>P8ԊAD~6* TBi!yM/.emujɋ Q,SqJ\o~ucUdq E,B)HbBykm.VxCimw B )E" E)H""SR@3ګ]B$4 z(EB"B!aDB DV)JER)H"eHܴ%-rKLTU ]6 ]VfRe PNTJ8)E"U As!6!f 4,EW"T"RW,a4`SJ$ Q)JRu+DB"{Dr0(09D%ES%`mM)jUK!4,JB)"Q2YEJaf%<"1H)x0$!)Q PDER,% : H!T$R(E)X*P~| Ii(+aJ"i,JhfT`"JDZ\Ei% %IK6bM+$*JPib8ZqZR"NVg~T-`&QL VʪRUbBGt Xe"HQFqٕ/"R BL&R& Jhʋ*b*E 94)4=Co{;e,[)K^ьNbR)ī%RRKaU0D,A(<쥮A)\`BuZG"`:aZ,wUPVi٪D",ʒ4d{NՔ)rIJ"v!ad1jRUHOَAY%#*. DEhN7K-%(A$%_ԕ/QJ:$Rzon*xϙEx:ojEfղl}OpW}(׋DM ~Z2QKO7,mZ)Nͭ z ztV֊ֱUhм`K:Ҩ {^[Ԗļhm)5na.ŘKcXBempJ/7 b5ϩ{=WP2bex*޺By-My%~w^k!ESb/M!<pvk % 177""br6ȼV'T{zOc|%| .aJI^h!F .oi$B%a}^fЗ*ksJ}]ANRgb5иnNнKtgijRrUyj0PRj2vjCBBS:BB)6 ھ!JlC8Vc7[T1Iip(%R%~5lC) b3b׫x]"Ԫ)L 4*ڐP(.Ud65R`\4\\ĵ?)&S=ʪHgZRt!CjI /BYt5B00$ TfS$BP!)&_4Ri%(L!cT9M(>QHBxIXYiӐZ{ҒBB(pS2ZBuJȩ)R)IR`BaI JA 5U(B! &햛RSJi) O> S" R^FU E)RRp%&!Ъ}X)MU4!!j )i(j54h_g7*Zi95e aB"R\`oKU-M!D:|j"aVPK`l"JiK\BP:O%PRRܐ bJGBPB%٠m`Pi "!ڕPP&A&IbSU`UBHA~)RW(?RوKJFXIJ13ؕ-eR5)$T*8}*,E"]EKeM)U[C2/q"!5H!/Ұ!V*®4=$* ty[L)j q]!KI5*X,rIA&ڔ(jB$)(sWJ\)(t)4r%z+(QBU)J&TJfȥPXd) ]l*ݒ~tAIE,C9Kkf֪ö&i^AU%YHE:$-P5DS;sK!-gIzJeJAS`fХ?UR!I*eJ4>t`8WLb4A!J$R@!$2 *UX0P! {XaEf1kh>d)~!l9wV3"q?5eHBRbl[.ԯJQ9Г%s|"}zC\"n~~.+-Q_ݤ8'|bQOu(o`^S^IL96$RNΡ6fю:>R!_bl397lYASPUyD5cZglkY^^&"t_:AVw5rL׉pQ[]4QO]WXyff==Cv]DJ4Ϥ-#bQ*[ =we baFzkZSajcr9b]5ALQ5tc=gTj9oY*~U1E10*ҼGqst%-K"7l[]xL:n*& ,.FkA| zQDu07Jkq_aRx養-@&92FruoK {.򠜽VJ5ȩW52Z!Gz/JjүMDQ&4Dcy u u B{3կ,9W9Su7V"yzӪ-kQvAGUgjsWv "<=(גP3>0hc+ (~"0}q+meT_L/Hu-lb[.8kʆRqJ4weVad'Y.UPۣm}LG}+ަG(Ei7$9hjJQ_IFqzVYfu|ywД)5}K T#RQ3eIӟUV5zmX&҉J%![J\ӵ,!D'uun~(I?V @F+)d/װ"з4h WS5Ohқ=DQNYt<}2D]_RUgg;=q_W'W]DdӒﶪ*%2fVHl*S*Itk{Nzԓs$C!þdQvI&r wtY#Xvyk{#23o$ pCyfWTR294"K\DũnvVS Fy[`[*iE[v5!'JJ"y 5c\B( uD(}u3D*,%>QtV|mMmA١gܰ2S], 3/.c|ci(͒?8 *f *+8B\!`߹>;J3}#dnL3o؍d5B'a~ ݍl?rlٞVzrfm]\er'Q4FQhO +Dwz=!@ұ:? liYv!qF#jh_mu8L+Z׾Oy7]VbW7S=e;kTתN K;)'&輪j<ƙW+^VRc'㘋moeM[Xl#ys\GnWJ3T!A`SN"UZysM:9) Z؜K6eD/m5GjI9ˢT;HfNm5M6R/˻,$va6LfrОC/mUE}j]Drڦm eKDe2>g*X'{N$mwR%M_+~eOW[>k7 TQ\i=υapۆ1\ٗ"_g'OUZy_}dܭjc-}e]Tso(]\g)r} ^]`x2̚e 91yZ%%gMإՉ:S:.ZԪگ [$1襃o78?J$=tspY'io`;lR{-#a|nuoU7=_u%wm^N_о >KLdgeVu \Ev=#oA/E4cܥFo)]$ػD~ ~5AI'*SűH+TVEE.-Oi`j ^Q_זu! }:4J*䉡qr-=[UG޾±oפ7KS<ޕ@z+sO'Qm1%%EBBSkڠ7ՎQ|J*Hg-BwOиJR'mj&"hfԵf#B_~/ĉaFU[!Or||ozdL"L*=6sh%yOjQ規k*ʒ٢dU{U&[JI'`?%3%);MWҪTE=9niӤ0˺α_z?AY`_|ZxUb'EM6ɢ<4g$Ϫ;W>Yx3ך)NQRyBiGfbޑr3Ξ-_̵'8 6o2 aeuV]bij#i&aHTvd$Y @./r}15ZCoLb閻@[ ̪?t;lJviRi.֢[^_;0俦jlD%ҭ&uqxPU|TxꔓJs5nQ_Z4q޾zdZNI5hE4J0dd\LbH2wZ^?wC`~'LdڥkÄIOb=4We?b70Ж90.n_Ɂ{Xew.w>6؇n aX[%.̢6|2JAT :*߈*BF9 %;-yMFA=PXO&pIXPj F::g2If}È=F^qh۫\4mM?3d4k;'2M_%Lؚe͉`[,/w))y6flx* Ǵ6 'Ҟڒ؈PBI]aSaդ*N7zAQ[N_#iQwW7w܅#\WDJQS hǤI`U}-mGZI3 4!zB|]D8lfd.;.RkNLg[과TZaeLDLe/F Tͱ!8΢5iIFgыrػ1'>U4EmCqtZ|aoi,hDQ?6j'xX6 `_"}6}u,$ڸcio}mjqJIk~]BRYcjf$5omb">jh(ݛWr*%{n.ǪW>Z{,ͪ`6Q\1f1*)ɱ/n|*@1_T&ޠTR1U)kkW_+I+G_j7}{MzC&5}NguC%SK;\Fv%V%gN43 zXʶE)"²oYf;x]*koLbׄ[RüGؓ5Xfϙ*ZTwαWޱOZ]Z{x컧k5G }l8]?; w*ܒ̎fqQp奱ZI Z@Jf^j79A ~wb!\@Q-1FdB~ da¤!7QYzJ#}ò S@Ue[e~=hN,ul*DBܬS_{6ĺCVvXvojH|,h~67GX˪llJ"P/M U˜""(ŭ=b-6cЬ4|uKAF98^8] 6\1+`!G\?{:~J_Ոr.aU=umcL韫꿅-Uۅ!23NYtp2~S-jݺy9E?CU6c?kۈo ^)'U0ƽtVAyXWTr"H-mHEh>lXv؍b'R+JڬzE2U/|dTVD 2rA+DOSfh4nNdoPM]'M3e9eľ+IPл],`k■,jĴfn _އw#')KF%R3#vm'/jzG~7 9o]KVW{t4rNԆ6y+ ~ Zkofe'^ظ.Sh6ͅD '8/ayH{%yV@UJ/t׹lߥ+6xkBG7{[nUug7udmnM ڂtQ=^"p/Epԛ8u԰կR'sD vpw2RAVOɈzwMچ-SC޲ĵ4l]@fuM=8/ŝy_O\3vhpY>I8Mޛ0OM:D>Ud}~ 'u7rSƹAxb¡MSUtzh# B<)E oVXI'!iM:n˶?#b.ji$ '/IL!xS<Y9bWu7UEbmՉ"ivV!/)Ô~' >+1skv%' <)P'̤|rAZ|avE}L5wޫVrE6J#T5!O7kt6Tj'x^\SDѪK'uYUEEo\9qiblf~ɼrjUPe&mUeq}LJK6)(("\/^֩ufuwU6gĂ_^XgiNj?{mS["5%RNo%%c՚jpݲe&غ  \y46RrOZtzꮡ'K.婊„X{~&kŽu4ZRc*iK}wV,+$WI#䜲odO'nUI$ߢUz>Ds^Vszpp XDr}4jfyM  WڢS+'V͹PN 9Fv4"RɏxcNΑ<8BNډo5A+ysp4᠂Sd{hNV^crjO"bo$ýY82ǝϋf18s;FW]#6+Uy-wvPڥ{UCDJsrΧg~칬lZ';Tg3 [QYHMSbWtCbVRi-,$xyQ2 1FHqWWUŴ{M[޳Rȗ)T՝^h= pyja}dQex/Ist,bRh?k_kާ7̪tn7EISEh;C!nT«ih',t9Zte>\T'Ũ CJŖڮ fPnROyBwڧxv?e_zɿĝrŽĢֳ -ܯ8w͢9bMj&+քVwK2GM*?4J;wicpc8ꉪ|ˌ2TS׎i赬 NN&O4iQ3:;fy7`_؛J9hWܧƣ$LZ&iJkRnOD4g*)=tdѓu%=E[#_G(w}{GH¥X֡6\e&}gX=vu!JEdZ |4O~>x ̥)o-I@Yh톈,tJf'cN(JEl04 h)eNgf;/9ԄFIOP7#"WFBFb®S[mwyjl?;TYȽ7hhd1M2/5i&\AN\vQWx_[fgiBrVUYP!bENr15")G3IE ]O5c#o1gIC~B/*Q`!qѩviYlR~*ms迧Q/m5}=Q֧;Xàfo,W:ggfʨk#2"SD5F%݈ 5"CTaQ'O2XYEac9meECZU1mU`]1 #FuIbJ<%}B_2*f&~!96sJ"qIhs(Cr/֗g/DAeAOa -2% DYkϱH+j,8OJͩ~#=O\"spS'n'OAyaZVM) _$pJ&TOƮ2e$xm>O<&)6XKϏ=;j{M>48cN8ld&wOCrBˆ\˫谲,xi@E)z/nK&̷+; 1?1 c=VN7j".n}ʝ[)Z& *bfW(Խ!=җTɱOiʫhwԾbh'3]VKJ>w~HQ(؉<]u{E뛎jfGPj<_bNJO52J6)e$D']pQ sOLM:Ų "ֆ1xm%yp-Wj^*`]]uXWu턛,JYe}(Iz{$˽QR&-՚hԼᴨǚdVgRo2#X*/L:fe_:"7Ъ>R2-hyRص}h.m->IeHoVQ ˠwBVji4 }?B\Żr*)E0K;{sߵ't(SJ5x6U>UUrZQLrND~ޘNV%UtkH8BٮI`ud,̳x Ң̲2Tu}FTMZ~ ;X.J:RTI -XAWZO) ZQm5e|fYK @(7G"{J<'a.3Set%$懲긯aKeZv ¼粦ךr֡Pٷ=Wt6tYn"jvq`X%Q]׵F{u_}wt_R]fhqVdsXaG6 U^%.pcbPD:>-r-LcKSXzoyI@:h%Va=F&azJMӽ?/?NٚS 1WU =VIy "Z_&넦Y5SC>%O2ٖ95x  K~Gc[Ii'-⦯zT/Jts5;mXK4 iz|QWCQIzn^[H1ڳ2O]_iJM紴(ӈ5lU3Nҭ:}L;xSgqny&_7mDzAN Բi$х='¤>uIz0A@U`By5ŀ݉/!ż yHW΂u6cI5:o1\rO?@1:X #Ӳ' Q,<تwIÇ tkdX5^cdW_[ݦ_ǣ}_tk݃h7s~yuęˡ_\ny;q䇙}V% =%vSc_%YR25lڠݼ4,'uy*21?n|=ګ|jke~C0ض:sd,Oϲ,M_:ImD`-^;^&׾l5exsoS}3V$E]n`tfwQu>6ڏAl6c(,-1禷+%ngUc|.ÌWgaͫy}n?ب Ku:Oz9L|fbp,YY||Rʾgh=LqSqTb3Rť)K<$[.ڛ(`G٦SF2 qX4rťԫlf}#ivk-+J*FqfisS75dCpw[.L)wc ʧjG H8gĵ9VԕAT$ IT%jRUIUV@$^:Y4è+.Eߙ}nVyNg[zxg#}NSӭ vZBEtRZ%Izuۓ!$$cۖgYTM}~rpJZk/3}T'/ͼDʧVdL֦j_2syM#t'IN7, l¼+*Z6%SmT+{NI#Y7*s ʥG,t׍ݭuG2~{H/)~/4,qʤ֬nM I\('%WcWyj3ओJuQ\TGu`{)?#lںm2Թ5RY96 4sy*`:^UҖ塕Nj)uMg@Q6%igX9>D˯,K0EHWJ'.ȰA3hoi$aߪֱܽwLӥ7geTDأb>Gr:kf jTƪl⶿<׼Pى_[0i[!^ht'PR)Zbm+@A^\ouJ$Rrܽ/\7]eV&"LOZM?.32&>ه c߂yfL p48,`| !p%CAe<˳f#G^l4ӥхjvSl%#ͣm29=2;ZMlݰ S5pٱ~>umNpBCP\/uWէ5`v黽C:-#'?Y.+U|&lr?EzgH"kVYxx<ʼ==AsKs__[%f[MX|zk{cTwhyUZvgsoֽ9{@<]}}mrjɫB\]IU֋+A]ߵ=F @%V.M2 c9U$ɩCd൨A4CE.L9z8|1^RY+ ؐ%e8/Ec{IJ6Ig"Ɲ YSzꓸe^x+ЊcM^}-=c&mgPmX褒ҥi9,﷍s1}oѬJW#諦IRE#^:YFԕE>u_>r6fp_EoaaԷP5r-  ,O!,6K^/LV0k&U?Ed]3p*jb_ RQx5@EEj#k`_lkTB%5[T/bKZ8,@)CjLOUQ6*[!쮻θ>|ӟYb\'zzLs{eV:x-&U)e^]R zīhpSn*Q ABbӅ񚈠UU ~Emt*֤{R^NҾ$|?Lf/aw2vģ*)5Nֺ-$nDNPoVg !O8|!y>D*'xZQ~KX]PCvKRYC3 +֯|hP2F·5]Z͡H>ק"\KQGEhF\5ZՒN7*ZcW:5@z ߎޒiM935aYQ#~Ӣ4iz!pį_uL2/ ;_oV9ZqrL .7DS`:)"(N"/hl2E2T.&(ґ\&Ƽ`&4W3ũғܳf\zQ*d6$屫R."z'_]lsKEko.,s2KWmKuQ1j"1r\υvK/ FXUv[$4ka=APZ'l4̂_tRь1ZOP= .ʚ 榪þ!ԅ]S]HWv}S-WV-'e9/2bؕVAOiș)j҂/ 4*vE$aU3Mmbj'l43rudgkvmbuї›wnwچ*X7i]L'ҚIJoѝ'q#}rS\[m20Aji`oc\ߏM7!Cj jCe|(Q:sJp~7rihF W"h#jߔwEZ}(Rg[zle1Jr-5N١Q0NUXdž,x֖NQ}~:ɫZi9~ٌ8WzS^bzϲ8ɏFVl]. NK8 vGʛ5{)ŵA -r1SVvEE/rB'\^w-[V)Grئ{sS䊒*4, d>g]L(eYoSVnz%TCnh(.f`V|J+7$r|LpB%%UIߌ9|n\A⣙k2=UZ*ej~i/5yQ8WV*EKJA 4'2Vuo$,{GiB9*ޜ^5H<>cÞΝU~!VTⲯ#- ˜U:JqEIE#രcn|QnFC !,L 2X25>tۧ)yWFIhXf=\Ydآ׆2bX=#)NvG60DR쐪8 ͦ4]?QrxX/'Ȱ-YVFqw.mzggs8<+;H]+.Dо'#cxQJ},z҆8L-2zۿ-&-[%H>߸>15K=DV&JDIʢh.o+{#C~h⤭ՍBL-L:t&uEtUIfRJΪ zkflYsU{'3PS_zԗiMY(u\VoVx-QZv4ˣ*(cǁqz'Kv0h-O%EL R 5IFV]\IOMƸI%$㔄6>min5t'AE/U&Ԭ +]ޣG4:F)W[񎪀ki0.rĘw<璙0 T;#| T"$Z8tRYT%=(8-2cP1ȧ;umX%M/Z9dk=**%{ga `g`XM/e|Bij= ҭ1jjEIKS^ԗN%z#ü6okXՕJJKPOשXdH.J1GI+1*"*dG!5M$6Qx/OHMkpcE5"\&:!n.OejPM [S5⛶;yVg8)QYCM~oupkzd1Iz ˨81*>$ELYV0iͪ^m(k{)UZk*ӖbvI~wi%Y, ??&Ao3a3Hj#Ѣ*t4a 6k}_Aa6xKAkꖜdj4,c}j4{5"ɐeUѕ %^]N^p/Gn^S"F3oqs3taNX$[|N 9fX'(VCVciA6Ѿ4hhx,<#$Dx"`Yqb? nP~Ϸ_'OQe=w"9[GDWg<>4SQꖇ$+!Z*Z~U}Gh$݄,NgO?d,*F{%:lY \miXX̒&PfoJj}Եl^֧.eQl nBKU3 \]elW1ksFPHe#wXjU(^0:{k 9_:e_aQթSUORM}*4\|†ذ™^N3(88CUէ^Τ=RImNDٛ^EqMWZhڎ?u S+3iU[3uZa˿[Vt7 bHBd8>EUx58E"="9TGp"v!%o $٫l"]ON;d,x1R"4*0t ]USU_*OSKa%_9I͹7!Z`IkuS7{[R J}R4SCgW:[0yϪMU6Q?27WQcFԅ5 :vԊ<Cv.rW>A0 fgivS<8x{M j=ڤ" %# fV ̾u)R :᢮q.*U%(uKYB[7T*֓Bd[J:ӟD P['E)B)HwЦ mk|iR.8WλL[;Bpҵj(F]Zh\k48B Dv )!;4:5%b6eyTل?0kz<'(^Sq # 8K7A<7\b `sv!#1%"h[WanHKCX뎆A>vSs&'q^Qe:ٽR@q[R*R%I;T)wme6,Zm_B%x[JMw%۔GnOp"1/sle3k7/860ỽV˗,]`W3͛y=UCdBlu.}1ڞU^-Qp^7¾\7T/ŸgRZ6%ƭ< ڜ~'ϲW=)rϕe3vo1 xmGjzCô_sqU/R!M6[SU_ hy_ZF)1[1ȔZٽl)|m3l }֪nF|0sJ2H\ So{6_a4眻dVZA ʪ׬tA7I.%M9CDBdT\MwX#y .>-"2m-СLGC$A$U:ΚSV5!ͶT&rѶ-w #(*Ԑ֐IR) a$cek[%uɪN^n+>*=BP3 %PޢtĪ9RHRA2¡4QA¨3JUT0)RU25\^KIjff\Z PRicGoj)LW*)/ U aRD)ji].U|hM(4Υj MT)RHwI<RQʸSJKXwzjdZw> iB"{V(<g/Y{kk Nu]Jd[9E)H3KbikɦIRޕSB.2 ,.%U&)(!YyKKUk+UH}M-CJhCA~ ѭNJplScbpMױ X-B#KbgOIUpd1h?`&MDDCN.(sIUKbnKdҺZ Eз\BJBV=qtK(袔 xn(a "ѐqvĴB R3R;D%຅Uhiֳa<4[99Ա_%`x"~ 2CLH:aQTeǢ_kb5z.?yI\'RWn:-Bv;*g~l{sYдBuJX(Fp;QXtJjn6L ]QQ 2݆k va/z +aNUK.)Z Jy4TЩZBĤ]1t{1uԲ13ֺ]SY}N3vofWlqlǸSq3ϓ:Cu 2>컆:ԾCxx -2۳kX6M]\Sv Q|WޏZ 靥1p:A t г#%:+TDU3owQ___vBs4]rU% F[r;6zpߟҽfVtְœ7ڷhڬ~^JU]Q630"8*x'X(>*26b`RIˊ^SlRҮmw$я ĥ&h+BYDGxn:#T%jT,>${x΋M\ Π%5 j-OM51Q$!{jPDST٧\WϵE2l7rw'{e’Ķ2^V_\VdաjڕO?TԢVY6fU)zTגkRmpJ)a[,7 O_( nIgeqd>!]W7bmWvGF]Q[|J.ϥ:~t(%GoyJ#fTα]VU[n֩,8i3G)M%=I>:5]ckI_RiDQU-{(Ys ź65kN皫J3G, ڕ)^h{U ݿ ФZД]_yYO\Wo)y t( GNUqRKH)Nx"aU* 15=i_Z#U?̯OG;ZP/# ) Bqeq(r,~$jBp(ɌMQQMaNXfڕ5Wo=$;3x{pV* ,#:7ۄ{Jε+~z[.8;E7RT٧֤sktݿ"6|9uHG[Ӝ*k'E- am+*=޷ҍk=uT&/[G-w3F"%Q~|NH5$M=f{wOc~ӚP>NJ/VuY.Q %xt1a+]U&{9AQ8K> 9x=MJmV[ѷ|u55MoOEQqEm]!8WmwOd+j`%"UHW"^1*dSN 鵬ܜ# Wv{l;4&oЭJYӄrXg迧Uq8jC2{ٻ#'ONԪvU*+RIbw.Z\cgbf] s㝛gW4mIF,HK 'ShkT *j-\J|ҩM6Q%7lBSs<I};RiX4~?fUb{w-(q2F#v$ڐh(/Q(?cf|G&[]hstX|ȡ0DK( !9 ޳/%рb+Ne5`ӷKM0v-EPk4}*X"nJوO':A=H(_[SMC|?AEKΡx-v7t&aV Fjh巺tM1 ͨVz &βvc(kTZvnF=9y4⺨ȄX^a \cf4jÕL<u3LD8>ZukdH^lC_KI[;[t>cG&^U5+մSàcEJYѺq*S}״o)ԫ_i+R'_8#,YK_P! rڥY RnڄŖ?:h>f4Pz|⭾=_T B. PоTw؟\~ -_29.5ʭ~ű]lht.us]gGh t~bes F OWbb[yr } )A\{˧IS8{O0x;ӬI17DQ'bvxɟZ߱iՙޞ[Rv$)*)㺢`~w_Z?Α6>/4{G"SiJ^= 7A4[lzXQMKG4p_ڏ+m61hҠQxIZ_P[]U$ [dQͨ %CNb&WJ#0D5 )W?SɥabNU̼e\ܛ*ZUV1_ӆͦz<%gi1AS7M=65GcSq+?N/H\w{BKZXu,5$aZ YIK&Fk!֚/zoBŨE+*DQ.3xyqqpLCAuO,Rs x*%$HRniÚ׃5jHHytS~_; e{mS:]ẺR\urkkPHi쯺)TegbFCLrE5h%+ӌN\ZȤdRԍEj/vzJЮʟ!WIDj<7_J9v;^)l@(2&qjAµZ˽i;ne0L \,85˸˹uw|{料ODQR9G"[5iz&aEaBY kpK?%2'@7/W"yws\KTS6CC'%Uʯev9דV VUƨ\moMMWyyí1(4t8uz-;)Z*]PD !PАHKj%*ZWI2_~k0_;a.n~t ȦHܞ# .I+zm5-!F} BHI4lW-.6UVQxV|ޗiM]%F%!\gڷϩ~%ԧ?%)W XFili7T"rߨBBQ7M :;m[<;+3za2sC,1?%L>G55|=HuMP^6Te{>fEr.^JDx% :l3G,Ǫ&tA_vfkl3n'MV0KAP}#9[8W<?>Mbm]K}on^;~c}Lۜ2yV\&e^^\FY67V4Y zh sv{8>&V + ҧg\ʒlU,kTFR߃vVުZIGUijj&TԻ3zmwIPοĪk~ и6/Ty"cBjļȣ?iE,쪳S[hQOOUEmjyvҵ/K`WS` v"WQ% #it?iiZ8^bUy`Rյ}9+ ԈS*21 yX5u5 wrfGUK&$Oʮ:lkRW*"^QU,G]qR}Jvt*>>=YU%uRI |IMR?{fנʽLޯ}ڼ¶*[q/p;T5qTN[0v]F`- 9hэtn!YTw#*:2mf̧C笷ED^)o*ZБj ?D.#fkbQ1QnD:rkrzS b eNt5{>fr/1\`_kCY"LOzӣ%KD{5&̪0VR^10oywG+j{Z=&H>1[Q2Ʊu~%r9jkzr;<=ךREZ֔cȦK.3<ռK#,5-iꖨB[FE]cPiOo筲1ɤ7DZVLQ\%Q_3½M<_U G*(%=zgyVy>*؆"^M"ӧ~u)UUb\(֩E%PnQ$5N8b79'HM+`efYF͋ 2qTg6HM*lAef\7t]5x4meiT(5ŝiPf ̵fZ]B^A@?9=|QD?s iH c8{V9 CJ,0Y>ygvd=3ymZkwns+qDF?cnb)cj-;vW}eftEGzo %oU{Xl=;&*'Av|#q`|\=[l8⛖|-ąDÉ .5Vm.+zN[4T^~=X~:aor>t>Q'V?QCL ikm^T]kG}$ GneW^X72v]I-9Mˌ(Jd_ӻ/2oy[o?Yהl"D=&'SamxLGu>sז_T}P'xqqDnsNfV58WV9e?z}}J=A*8¹}\K{y!O4xZث=wQO4epSzm Bxv5^犠+cͼwR tњLs$Sg[j8dvOƴK]){MTd$5AD[ώMۺFTTf؇K^ߢw)) CnPge3l^#\EYŝ ܣO3T]Z?|4/Q3Vۼwyݧh,]^a_ _]$t/HB(te"ڷ iAW[G%y@~K 9~vT.OV0H}zIU}BP}ϖ"?lYIg`jzJ Uy\3V)X]ufW#@Zڬv*K]~1.Я'MrھdEG'97} ҏ9?iSNUg\N+M_G&OCsO+p|mBYSAfSÌiwfڶ[z !^DX'ҭZ.Ϲ;s<+l%3GO/034k)kLW-aޓvIy)\fM}ld<()^R(?F2 ;X]f/YVa 6&e,gE1O,8ǿEd( "@"7Rzo<wL0MlL˙o)0\ڶ`ԲjHj\7Mgv0:LҗOYw6~io ĘO~g6'nǦagMAnyR=bl_LD WHZ+9W e!? Z&_RQP)c$ 8^5[PS25Eqź?u 3j0etb˖]tU(pͺCيU a|/i{ U>f]}V'CKSv%o| 57O'ZP6w{5qDMjBT\/[M Dc&SZN7|'^Mc٤knP,6Ejs =uW\w/E9*<{f/Y՟&;穇CRU]6]#SrByip_SY%&5?L-.IQ˱\`-U%x<6^3'G*r%)kGi [\x_󖶩Y+q| 狗n!@+ -Yĥ2.7wצ'au~O8,kYuNY /wTZQ*5rv^46U^o7E-K|wv_z;s N@O4N(L7΃QAGF0-F(QQLCC)#6:)9ul2)7Yet7ɸ?go?U\|6.үöUe0n=ٳ6UZ[uKs-iJE!#>s''MkLa[\a aPiڔ,tlz̯ͩ֡/5eV=RdmXגh-CB4%e )5XƳ1_<cکu,$,̺U$m50O˜ h-偣=B~t$̬r(ʬYTIJHlH?pk"C\pEYUd>15Je_\TSZdcY5s)g ʪ_^9ci9s`֮5}\W/m\-.HٶE.zz+">F&׼]$0qMRGM`ttQ߷9kϛgWutط.C)ScߙvqP1ʲ]5S,/Z5DiqPb6ElB[|@C)`@B\}ܾ7~r)Gu~#b4L>BȚO/9?{4ؕt+a/a:`Fe2NTPWδGHC':&?BTL6E!Һ*XwY9]BŠV^OӜaš zdEr !mdD%ԊCA>'"H/!!OIE0N ]2WBlϱj'@qƨ %T3;~p1uyM:{[߹Zs{lٮiK>z6<٘ei|r%!wZzƲ[bu>'(uv&A 㵅y1c1PBSz^@xj7Ygkt=݉;?#5 P}ELE|UF6+B^qrv;7}ZZQ\L˾]rݔ?_7V4e)ym=;5v > WTƹ=cKf`xc~U6kk\J! d䐵 ɏclk_Z|o(woLJ:׀ʦr-c27V)Hp*ō~_ BFI(1α;K2+İ O!qT Ջ ثDCӘB8}ι/K>E;$e=c\z}Y[UF %< U\*2iCk)vHҧ@3Ih.̵'c&VSC ߒ~$S2-{tɲ(1&8&ҕ%!ǠU פ%QlVU*铀/=eCdk2Xdi+-O5mam% aWRI0ړF Ҭ6T2UY_>D/#6ҪmU~Vi+BiB"E|i],QG5["ۚ-m1W %W֭)_2sO1ipqH/i&\lSfu0 R2m>d>49MmyUG^`A+WUSA,JZصS<`3,9b6,Fq OAUZLo\ܖ1,J$͕g[bib[[--qhEIe{or/':jr|Ԣa"&b%5zm>I( C GF_-Bl֙i3p:O}=4s,kqnE!bQ|e`Z(V!gUlFRW!5khhو`r<ߪw{; !K!lNke8tvVu4[ǰ &҄KhR1>'*֣9/9j*pwySym m_2|;g猡}xa^kol3N׊~^S?}ն|.{Rcg+, 'Щ* \鼚@^[]rB5N[N^.էh46UJ -]i'ҖԗaxDEM~46F2\x7l-@.4ruw*fa 'D}tWwv2Z-++KBM|=dr?1TD`WT2Wn~ܱK|vKě L:rCN7ƺî} #u16.(ȉjQўk[7Ȥ3S2- LiQ]a%ڄ$QC7rI\`T&>uTRmj[ I$ǹISvsI)sL6ZUԴI6]IJ>j/kihI&jJ!F1L \7B&L(:*+V6'WNkI,KI3hDI&HLg= jT=#Ӛ.Y+Meu݊0z'')@QmAdu^bK?U!sӜJ KIo/^Vf zi6]MtDARKc҇ QWj(osBƧ42_eA~6?uNFE9]: 갤M-Z 5 L' ELUlonFx_{;h 5MxWQZEewJr)l -{/)x \uyYnҿcZX/E$5!h>q/% *4Hn JWQ:rޚVlbc&w\%ul~JK%uɅN9K{zptn:}/iP${̟!>& @mw#TTu;5=ZƢL; æxN-mKu\-#<ڻOL)oemzr5\( џ} ?AZ%YQUH0 <l"%5]BDe6+Ш}w~sZ?yҢ^M-\VhD_,% ũ8l"~Qpk#?tҢp2k ?󰫳,a1R漊J)"֤Z?V=jxG3=%Hܝmıʃh#ѬR^cʹrd=֊W歰Zڕ B'o9psWĬJ;UgR \)쩏>梐t[f 򒜧-CE'*G7 yKo6n O`~ziכaw>d?=92=e]7;iH[n|+Tx ׆%VIM>q$fRѲ,OTko[uvִt)ThF&Kx&m?۶Y)`k_Rv\dfx~6tSEa.пȳ+5Q!i$~~O7k#~꠸yפ1eG&\P"SVF~LFv1eb*fkΕ]kIM4(րbg  ӿ׿QQEv7Pѐ (t锆\/!r4?9vҩܪ&ys$"Ǘy0j*;V7&+n}ZmJOj5ncX8Oν?Nk>Y=sH6 x88 *񲰐DATшV*-eOԻOyevmM4|' ZkqlູS:x*XgUpbfRT(,N}%y.Ng)u/|S}W :vWV]趔,|R_5be-wZQV}QEMk5C{Ki:r[Y}M.0}_޳F, 4+9nYe*f'EBd2vvE]uicKK lZoԓp+˓oad{i{ߓnkT*"k + ۵ XXȯ SX}ōVL/J;N)y̰ltIo{Xd[4[TuxDo $]I[53-+5EjbF! TުQQՕaFhruymjA<塳Q79F(59UYx&3%kT~ u4]FwTolL?\I7kӝ{QF<ՙ`'S 3#yT.T}E~* Mk%o W]Od/Yzz.\1r1s>/h $UG*h!^!ƿґYĪzںHnBfK:mz%HmH*zOtL_%>QtcZnmsP*OUM^YFZElk1iT%(qO%bd!m4պzC̤m$U!.=;Uг޳cTcd'\)kp%?%D'2QoDzNJ,Wcuu[`ֵƼjkɬiLۜowrnjM MvLQ&h)ICu׈}ʋ, 2Ihk#QIT㹿T'*=:Mfo\>E5Ib@menht*.%Aڎ7 &8]b_x)(LC h rڮm,=hb%F 25hŽCebd5iuu/SNޝ xroc3WE=fI=%9=PQ4D(I\T]JR ]XV+OB0Θ߮f:*3U\¼\{yuzcNTsnru4NIZ=bL%B9byǸf[31[&2O5)3`Z%{?yݣDNÅGő`+;?XH9/3Gw#j8ߜfuMocB F8oery#v,tyI?\_ʂįD>-\U$8U':iKI'SG; V~W%7ڲ2_Z\F;$b:#}ϵ1tOIDF'SgҒxۛ.q"p*NQx"2'OFҊ]թt3ymtK5 c5ޖUb@uVXpq5\NSj]6M".Z⧤[!!~$ѽ,d`rqE[]l&֑E"St F?֑`XԺ]W!pWWWltSU=jUUR= t@NRVٵ]ׇWYWMR3JR2 &&Fd̼5 nьY4 K|<}MB(+q)5G_DS/V/M@u s豼V)$taFM?ٽ>wQXl#ꄟȡB#9_> ^en֧v*-y.8 G=Ṉ3 b5.Qi-Ӎu9d"'9e=WT82)AhOLU.Hԫ UVc3)`!CݧI.e^a5G.Wm=T(ȾSD,r M`Ks|SoSRj!OE*ƒ _ q [L \M*_6.UFN!'6hM!LSv/b2%U9)ޝD(x(Ki`]Kmօ^p\KT",FH ZVR6O},Ӵcs03;Łȱk gM} {b: R5 Ob0ю\xXzjjO[dqubaK"sDna\?W걪%*#QYQtsVcW^)ɣIѼdidQ믦CuIFԖ$_ŠDY)GdT'ԔOvNv|s&PYVi 6N:/]F&vUE],')MdSPH;YS%-O/#F?F'HpՕYCOw*~4Ե7Y<dD[/9734rٔb=EmZ_Թ5gf+E\xȤ|N*̓D>y5ZK:4ٕPz c^>-tͨN}^I%!*̣44 {UĊFpv;;{UrEi{PZ4!Y7z,rTeXT/~iaN%.{U݊Le LL=uuT |>OV.OLn]uXG-uR)?"QTV%RA?$eM0ϱNå;'5ͼTQ4k;-S\`WHyX#`nG젿<7 ȜǢ"_جt-:~%ʲ?z/?"vH^9ˁ=kX<ĶfO"+d?Lf7M? ߓX,AyDa}:=H!g%~Ԅ7EZ7.e;gGA~0װ=nTg[eWk:AUP'y[v#{ȖGU3Uz.:J {T#'5D'U_A:n%U)s^1MYt"qi% ^6~{W}7{jƽ|s(͹>FIA,hF/m^?mDW74seI\XQdhnewo@XU={ q_P_*ļd=IjQ)+J|0n#*PZ#72ϧ"M{N2Z;SqGNTu,7QNWU^E0:>ĢO ?ɾUy7zx3)-=?Y`xͳSVБW__da%i{#Rx΢O|7TzjddWȩ%eW0SУRJS^ة^CaWF2dī3~]teS7Y=sYoR__lxymJѭ|}%yؗަ|I$|zIaTe]ExێF$D ܔc~N7(y$uSLf}j୬j]x,.{gE9*aJJ]pk.1ΛFm צ{* >}U} 3, '='SDruLQtM;T= +K$rR5KPX&"|-N xU[{^kHY5EҖ| F!Ie^⛸ zml Mig(/Ae4f{gkPkiL]m;qi Yp@5 -K\u~{oYp]m;STd>"A}A]_( e6o6[\C0m}h-SH)0-dJ2+F\#$ |EVsw͞YA )R}7#)9F=NBt49pэf3d+,vf |2Ɗ8? 9^ظzJ+buUЖd<&)Z ;n9 zUK9wkB~{Tf^&18]V'BF .wmgIoҌd5)4I kի|SL%I|2ʺ>h%XɦC&顾5PUu7¡)Vwa(O0ߢξ*npgTS\=,?bdrj4ӅQdCdӘޘfקrH*o=e9"( KܤzU1K;M$]QP)Ĥ1 C4mUw6%yZ >RUj ;Du jX'c[qmﮂtQʊBd/e͝3J+pM,SueT~ {5;~P؇$k5 X]RvDKhkLZr"i_QpkHkfQE )q~SJfmvh\F*?G3ۧ(0-ٽ-uֳr)<cꘛj):&u8 OUz'p~Źu[ϏD}u|G3Y+E֕m~w暄==c[p_sO5x;voeKVQvhw;U|<]Ez[^Vtgy_ϰ5?gnXUrRiIb*_P*oUeCyJ{m"2y§\zl쒏7 fE91"-dWB;{_v~jYiUKoPCR}7P(J?K\)lFZpʬf7\pԕR)Mծuiev,"U*mJa{!鶪 nhB%= \,!E!;D2pHBNg : bK9o zRZBW+OC5eŨ!TR >dbFb;` k-vKЖ|+M S+MW+PgnH~MJ!/$O a!B.ԬA#zX) &BybB#tBaԪU*(+%F-B\P[%(PRF9 Q KZXki)cfޞbmjė%hUJ]pJ)Pg奍 )X>jA!TS(%$ƪ./x z YQU!ɒA#p57oǹ2&WY cn9ijs#!IQII!4Ū&?dL-8O&iJM#9oM_2MS'1Z$bKSBR2*E-3l+][|$=h5~2I2b[RA9[[ q-k| Pl"| +i=~ }="-yG%|gHY<œz߹Ro*Mo[U_ʍ)EUGBz{~!0&N\Vur dѢhՄ* ,6Ue В sJ!rB\:;NطXM;_tt)gS{`3 <^ߢ9Adєm‚:_+N}7u%{GbOԞI gjlWGV7Ƴ#>d~Gtս4 UYoY ť:d׫;,4t.t aT>ƔUF7}D~?]CV\ WWO|NReNK0|tcN"#tO\7)<[E-c[W,'hݿ X\ Bx RfН] $94-r:~+q׸֔kO!dtc^EnjpL=SUs7?g]|H7[v؜49zxۄ$Z+3iwG4LbZ#.Ȫ-),)F%fLGbU(0 &iuKu鹵*kδ&Laނz)W/GT;k.*'Wvᩔy5o"}h%$^:z3o=j.Q0Y$ޢԮuBWDSLLgݿIAϴɤ68b5UR}^ze+@[G=PF:JG}칚X̷)5ey:Ja@Ћ 0rKV "Pv09"6 $Fk[l20(#ʪf*ouC.+ѤnO>]GHКFE 2ʃN42'-bBy.MG*c><+Tcg]nv?󰹶/*a{/E-赅 \%]\UQS_2/MKkc3bQОZl&9?J*VxT$;O )떾:T#-Y65S k2hd"k:,Db:GAwϑ$Wen +͞`i(4i4վA@LL) {uvU7InpKީ%V$ElUm.%Iv[;r. lVcV;Y$P9MC1++WְIo_2ҷXǯ}VFcd!b4%qWn,ˣ]U8gʲ(K^VQ'. :A};Qvp,e$Eњzy59).Y7sQzjfatuŦ:\ا#\Vڒ]/ONF=xI}WUcG􂢒pS\μuQy*)*ȮBd* *U/Ԫ+]3~eE[+ނ3fSecӇJQ;p'Y~i|]bjsS"qۂ/ B!T DTW)ڋ4֊M床H^ra=pLh5%0.v+&J8qAIۋiTU=? &aLQ|$og|w? vem+*4҇Ϋ;*JTp}j'9> i"ɾ˨?6P3qȞApUIK(/ь%[w *͡7ఓuPc"Q*G8&-Y8,.JX.{`];,>o̚)Nj>G.Y׊Ȥ]$ZӴR4嬩0w y*rYpz/`mw!T@UY6]GT/{DxRUjb=si/UȈM48ǹ "+j"`LuX-tS󂤪 TnZMI&JF%RUIy Mѭ]+BtZSgI1w6Nȥ7JաWW%j%b( 랜MQPn33\pw}Z~bfFMV1S MejIZ؅I{d5nڳl]־Evz ŝ:%!|"|'o.?O[_,z됓q4f@eqd=y]6=cFIˆشg)ݭzv=FNO**)~oJ%j+\}ZYHO OA*-QMۧYZP^HT] Ib8š ׍~G, 4ؽJYsQbekaS>V:nqe5FbDM,]`7<_i 2PYU M{DG<\Jnf2!tRDt(H eD+  0gxQ'?#F:bD,){_L8[2˚ȿU.eTF*KFYN!p~k\TUt-hNJm˜8۳b{~!ʮ(3NܺQn,W+}K~%E1n:>USEpBy}"~o!\bp8<-K#-FT'U; zrܭ)DP{[nمz-0j-Sap]tރÞoxkGSl54 OmRM\F:֨:{n-֒-lbX~ͺ];jz9N7ڇ!S֦QCJIejm[}*̙+c~ϨOYmx ˫6ykK*.{mXmBl?gQ^ۚ^0* *΂$]T-G&#,3Hi jQ{6(wܣ7QWhݤ&EyO<ҎӽeݲF9&7(=T5z Y_B~j2-jNVSTzwC^16UPoB{ < G@Chcq8:z i ޅ h2_#yW}LCOO/1|"d̝umhy& ϔC !/0f ɩ/#Ux5L2x/ pe+~*MF0rǧGQV$J^֖Ac͍r5jNߣjN VP/4I4hZ/MK(c%Y/j+H3Oi* 9hͪ&ɽ)fkdIJW[4Y,.^FżTslډIfu*|,;6RI:IG4^,SXixu/ S*tk^H Dly?l(!N/xdV61Mn}EQ>zv ڱKEPWne-|̃{ehU$ηˆ$S' 7?ܻ0\ѹ do:^}{\_ظD{ͺA2ST))YzOR篈oE1W!}kW€G">5Y4-{s>h-TX~wY]\6֞ a oʞe )!ޢ&vFXY%D7k\Λfa96*e֥ays.kBS%u^}&UGRV WsG%seHƦ0 BdEhV$$BIt]*j} "\TDj~բhch\;TlmUmdߕQ(gK#ffN=FD`EuB|SE}Cӕq/OY4\Zl/֠<%=Y9XĚKgx.Q(Xe.:gaC_;CuMᾮ)eUm{QK E}H 1wK"Ԅ|j>']O&sTRlOqL>͠!H(7@([pr{t-z׫2?.E<+ڋ3/^i#Fpӟ0%a{!4RBO蠫#t 2p?Br#S}(Bī wC~(m>6yD4+Wsz[.4 .&hE=PG77R\δtZ"* /x}~dˮQ"P6"ߜ-s^ )A qL١5r%dSVMuIeQH6} Xy8Z 7bciԭ1ǹpkR_ }jQkVm2B%1Ȩ3$VǶ1>1r9,9QU|* _mև:j] hUUMrćWZV5vXW5[j_bjz,JN3Veub6oV(hiLVӈR 7l/ 7.Z|ՈzH¬_Qia\@֤96nY辇ȥ9ʛӗyoEfmXM"9h t,sTFR^ut^I>fWԳ*2Ԯ>sL}iռsoZŸ~  Ӷ|?,2̧J fMALEaĊEMܬ h4|qrƖM*9B '[gŇLk!BO3db} +^Hf-duHHCr3"`^YBba"Eh*s<Vй.5?djZ[=۵}_B7 2(jAtWCZY]CwcՕ$F#i-yn9=GO=1<"{-4v6mY6LQH3ڎ_['K@/o~BtMa~,#jM;?^kX~U-,M)R9{Yئnpi..exwU˚r%i}@z5~w2  ];Y(0_*Iwt-HwE T^BޥLsD7aHN#uKDNEEĻ1J&II3xįrE[dn޾COB..SD?w5ER4yR,ؽ!;4 EiQU;"UzMxxL9wat#rXnTf!R_DZ]8Dbk/~9XHX?U~OO1OM)+ո])C"K'trBR ZU_HF>/[YEWf$KrXm.*YkMU8N"WݗtO>Εf^6{u^Ո̢9;5}vK{N{4e'.)5K%p2&8~R5,}O(VIOknDܚ"X-IiP߮DZ#R*#\=&U8L˾VW)Rn3Ҧ0.*,} Zl=4ʄkWWuʺ=)@I8WXHZNbЪI")Z.Me-kZ+}#鶾89 RbպUZ$ Bئ&+irWEEXN1Z`.4/ښmOBz\X4L:g3/Q/m\UWS1o5zz'hoEDRY^:,tpBZ4z8Gŭ[\u4|($ޜŵQ5D;Y<ƕfqf]tٳOѳnӦ8\IkDɬi%Gd (_9!m粮M{ LU.շ {E󀭻\0aQ*w >Xݡ`yj /%(ׯM/ -fQC[6d0v%4!*ۥhq8Ekǥ/>s 7XnK"59 XB%SQ;GPYf0x;#v;._B-Lpmf ƀ0nc/ qrK`\spQe>#/ӟi论G\n ̫ r6gVlah$D);O>>M{ \Z~x俟645ѽMmfb^|O,i;,GY}k,Ͳ0 s.m jY=uOAU')*(Nb[˽%N9l fǿYK1$$baSǤnGM.l>G. r:l7gbPdAeڑ;~CO|kՊe+<9sa3ͅ+" &Ի' GGӊzv ZJOѧR2^ &{Scoe-VnPԾW 4m|5qgb L }ELb>Ҁ>R]M!=z]%GE)5D 6[ٵ?ԋuXw<) sY5vZB-+ÉQWRzi=K݇~so8_p{볈8,*9 OI"kjص]֖=y/ KN',*<:I$Q8$FϽ urV֧!ljnBI*_Uz5NpT[]^k$ՅעYv3ñޣ$lZ?H+y۶;T5F)^.SG'j[B.F6"N–SŝG/lb&958`rEٽ!\t*ifQIc{;"<}F=$MzUĵS2 ';KN[mTS5MR9=("K%}2%Qw*~J|놟)-/,>kRwGzI!'փnQ]> :ػкpJiEV/ :7veaD!WK~̎%z;NU. A9jOКZ{&ct-yE:'G*j6Z׼?[Fw.i8ĕi+?sGXgŽC\uE] Sk}V՟]~T^Ӄj]|nBw=-!kK\CH!GUe?O%W2X:I\Cuυrۥ>#2 U|qkX$bXUq;0GI4~ $XB3]rEmJKTE?844XIFF%cM5e4 SR 吰GKWKBVުآZHV=T^m(^N%XcFΫdaj `ש3RRE+FU<=wtsOMiMt B!4!%0hNE, 1kyU.?;"HJDQOPbzB{4{}(Zk, gZk~U!;V!U;T*I_T[ S[@Ph'E4S[Ibn#fð}.Z;FU*R;!!!c 4I3d%C8wl1rk,ĚIqHJ2 dT*xBjyr<Ӿ$ MlKkBbޒUhScˣZ$&Nm#b[c =4TUBxTSzսFꓥ(>wMNoTBRBP-.JiDQ&8r9z=>4RmeG_ʓx䯓uEr$RI!dL:t"o5sRVTqP];JʊV+T$|b渼ʲjV?]ZMiz/qeg\yDME)_ed[. R:Ob+ʫ^W%޿Ȋ"1ҌjZ ն>ظ<>^VrN9AGycPrQx!cJ]8WBn$W+_NZ%"2\cL{JρkyA3Յ-_u{s?Ʒg:~M?dYޙ͹U~Gəu5y~~?wtnDזisZei+[qh(4bq!zf?b7*ZӚ*n1þ-g~q>c'-'vǚU}MewAaQsG]t%E9O_rJIkl-mR%b:T;j[Q~7^sǬ߫zw;m#X4]zYV+3gSF4X褳QfV;~˚u[eV(M.<}m隲x5Qn0Kgؖ-I>5S SW>ǎU7X5) ׶4I>c&Y<.ojc޻ӯMC黯Cz .iUry-U xZdd5QL2SQsHؗ&1i672]#ZZJwBHՉWiѨׯ-J&# |AhZyd߫>q3y&j޽θKLyX\r[4RؽINΤO]>`j]Udb\T= q63/I0/}Q]f'%4f`oUjĩkH0QTi)/N~~*NG`{;7pq`%#^x`ZjCx}B/ΕwӋ`8|Wn2JpE޳]Tt zޙ32 {!\To &"9=ܘ`AvEFN2RJW=N-N'o&ײ" 9j$(eu(udp>|oZTDͥeSΓ èmK>t!}{u CZRɣc^,IiFM,Hׅ%x`FE#-"CyOA_XofhJqVTjS MNSNq+YSnMWN̼VC ߫gxtS_qMQꞒx}쒆'OuGD ;TB#~yI}N&pD1r G jO|Ά3¸ѵka~5z}* 7O.-%ܮ`у|V$y#XjNe< kTRۂ wnK dtGm瑊Y ƌ,eL> {Il?@'7RY'˜tˤ/=%SAeUEΉ\a dU٤-Ai-X*H ޑ-d 39.(bz;ykڤ ik{++$á-Ϭg뿽f_~2ؐ~l, B)Fw')`=KAǒs}Vy^yNeҵˢ&uKo!GNh5ip,}: _9݅iץM\ NNf{fRǷtkGApF'VA!\&q+*e($TYvY#ֵE3n zU޴6/hwʌ̯酪㧩Ypj*Pu?&UgO((+Bm &QbQ/ -z]w2JvQJ;,'JE6flWΣn:],˛ﬨXVć "h:I"nDR&q*mEQPYZ_¨Wo ^]e%Qrjֆ6mébrSW踢T"ȾU#y=wڲ-|-A$K%INb5v;v'OTL&SEoiOdb&(\ۣi%QPSKĬH/hך<U!kC'ӤVIsx =6܆JQwjeFSDzkRW>6**PEڧ!PN\Ƈ:=в-YC~ 7HU^`BIu¯@EUSJ] ^R.6+ αytAu>W!F`mPN9|QWuk H Gk+?ЃTđdxuɈlQ8x6X8+d_fzpAh[u.1>Q X3!Dɂ[#?6Ę/2f%Iq^ 'O`RQ9G]H=keٲ մ;)TTwEEKq`ڧ+ڞj.j(ڧY]l/ޚ`:?i6L3HNZZLy֔UYzn=Ռ`VeL\_zuK 5Ұ}~2YQ.Mu9[X<9{n"E( AVtfQ*)wwm0ǥ6{GHjasEQ>dVQY @T=LG( h9sydiYQVnҮO]|ϵ7a_3 m'.R=$ZZ-P.+0. $cV uϵaD6ruGEc?`Jm6}6̫M]n]Eq_&>rTUZw7*vS,d-OMF+0wtLaX?G8|R[Bڭ(7}Sv辅me==Jo_i*޸vLNT_JRđMc .(oؿ3Υn" _Gj9q"A\Mj!N;Dw ÖZ۱K'JxTˠѳ}q2]G͈45QT5*5JRGͪ]:ՙ, :zػI614EmVS cYg)].xԖuzg(Nʽ&9pMXց4PYƍlU!4)Y‰|mNRMbi*ho 5Q&;pDZ/ ,Gc&|M"+; ǨfXu:CS$cm+I9xVfR羼ʣ*(Nj=BaFXq[*W]$WĎrhzM'%/ ѱ/5\Ul Db-s#ξUGSnȏѩIz~iF=AcӠge3*ˇ{΍d=t.c]OlV8(I/TF*"k:VjMSq4 ʹDI7_Tbj3 @zw4hIf9\޺Ƌ&M`Veve.}I.uM V:55eQ7k+'A,)Dž@QIȭ qrާ0pU#?EdGЛtmr5Db~j_{e׉fpnK&OlbغwY=`Q,'wtGg.nmn[:I2Ѹ`'oZuboӃ? XX9ѭLԸOM@G}sn5{SCy lmDxpy>v*r]Lnj(,6o(]'NZGcle)J[[ )W ,eAHR[6OVFVՂ!?ўntpߺg+I'n̢o5"!? Dje3EJ (IBe7[8mkU^>嚳UR&ȥ%,Zbl!g_ fAy1T*qlIfE-F ҽ"HՏVXR̵}TsK_^H3#pG ewwՙ,)M~kѱ49SGevSF`,)Mp+X kit?*6M-U۟?M2&4]fz?[h5 >lxɖ,b5\[~QjN=WiiMuDZ[{j&CV~' fBhi!ɨe]$H=ļewv-4) ^"*ܳs ,{pzp%CO'OJݰd#8+ ~84:c1ϧՖ|ocspx}Ix<QBu u=NZQ<=pmC+ۭ%P`"F:k ș`2Ffhϰ%?="4sϿzLn𘕫~4C_$j}iL|{A7sRljыjvqXnbTQikr#YzSTW4>tƮ,j.)iQ^' ||IM[@߯$ǧFH~ۍ"s/}66Jf:d%ƂL%cYcX9 ZTyNuK{姬^41VY.+HdBL"Т.벌lQ9`ܳz'Gc5 ޾zh88 ֭_GQ2yӋl/W[pD(3FѨ~Fyj$F9㴏qNO4e%YF> Li} z6㿛vk\k^*Y_(!1dᴜE%-vO^;^$E~C8w|篫3 帊Wp|{Q٠{xs[;^mmBc7mKҌ䠡R]eZ³bT'n[;f~U^sIۭN.4-|+ԓj#xŷG*VC[nӾo[6Tj6/I4C+E'A(y媻{~Ɓ_ SpW6A[T٫/cx%{ +oaMBұ-)D*089<%͢*N򨳼 kLr4"ЮGp<2)؋(4ǵDz:LB(J JP$]#֭5d `e {6%`dD0*479'R12siZ$j-)k""Dîj1-- ÍkЧ"iH֡ j#3LbȜ%^^}+o`^OUK0alpcz#΂)M^"Uehq_SW⶿G'V;lqŖt$ `pj~8x2C#h9)Pc֜p[L%?[m:4a%W7g2̹zGӭ G!;Qz@ԧܵ8Nwú0Ju'qC{ %Qjև<50L&.)^bBA~jz8LlyhR 6suUOy.k[p?5vm^%40QH? r?qm"o0(*j; [F_/.UyüfAm/vIxДD$'ak#ܤ3RUUOo'iBF5**ب]:B۞+"_"\Y]sr%j˩=tbUf5 -Js]{ w ,*wG* "41'Yn!EVb7mH~Z Lv;g+%mo+DD# :ՋQ{;.RwKO[ ~slDp$<[~UN&1UaW°!.G@XGH~r=Fb_ʒ7" 5iޙ>U+Uyo2 ƗiVBgP*F'WL[WSE\D' 8[Qtrn(wPsALFmPBOlK;TnZNI)uO:I'-= 6[*vK,306 $4T6ڞG֚Vh7 cK}jo]~򨗌ճHiʶ-=6½IUTĬXi4Nsj.%Vihٟx[ط\jlQr)Ɲ56űokk'j;*ʪ>B8{.ikw1uRE|mGU.j[Fs%8|+ Mnc4)6  $OÝ)ۈ&zKJT4{B2*],rMɠ0J9KW{ܩ= lq xާK*#G&;Nȉ(͐yPƩfQ:yAe1XᲬH07cF| ơfХI*..NvdQl Nv}o]oU4F{}$yQt,L/"(ǜu=:ySȖ aLCa!@̎bQw+d2Wy Z˜ԵTtUU[JSkPC~X 쫹M/!IL ]Vyn?}>grUl-Ee2I׮_՞5E=:h?HE]wlifAYX?9)bX/rh5|o{;{<ģ+wV9P*b¼Byд_i{ ʘyC6JyTe>+ڵؾ<P[[?IHR4c{ٿr@0lNc؝Z $Bt6bx\A7lϣ7}'?cT5nsh㕈QU$9rEhDVvA{9A |Mƽ%⧫6(1yk{{&~ڨMK+MڈجIn1)*ԏz+pش]jV+ 7l)5F3 2*thV-y9XSTʌÿU>ڜ:zy,evE!fFcZ K@")l"|iz*FJ%0jm*Sy{=]7.˲&ꢳiw =@xCzmkR7??ֈQ,-6-n+ŒRM[&Nr(Pkjު|8  8\ϡ [ĊA{G:K(8_SV,?)P1b6Ԧբ+K\B lz#]b[>B(q&vuZ>9ThғlMizQ%(AxM5d]:)u;UPګEcJq?^z_i;~^M:!ϵѥۓEQ:tITRuhQLrۦlZvĵ1믵s`l,FJ6uԚhSm V!)dЫMLJKlteP7UK_4:i*S@b_շqVZL^6EB͛44$?:)bSJNEN!/P=r=7Z1 c`vWWmEZl]&9 xQz㚊w%; *%h́5pi&,Ixm;pAjHr:|&5x`㐾H9[Zc9kˋCKm;P0U__d9)cX_c=mfS]"=5G8T tplii58@Uj+U  OMoHYȨ=YE]a+2ǟ3* '$G{ =kC$P0IU[u=z?DZ)~]j=Frs aǾF)JM>4yx (ض'ErL,jQLei-*V%*IZX8䢔R5,րYTXwԕu%&S2)zˠMPiUARd < 4j!ïd3uՖ((/ڃl *ՕUHp]#_Xsi+Wa漢Kx% Yd 'jKiX4%퉡ӑK]UVQT?2hvp(iZ2J)NYVaL qOcUe̲gZڐRD@@@nr}pԥ}y=s=ĉr|g2н)+vFQb~.<")>.h;JU=΅ ٺ ##R08 >|Ԟ"/@ p P2+ 0zB`]L*J`71t6^כi]ey9:>?φ@q-j%FX79tg # .)Y0Td@ 2ڹӬ/uʫ[ϯDpv?^tJt$Y_C (֦CnIwrэS;_4X7;TU%@\TV 8dM?QKٕS9,2Mak$`s+jg!}Q>U#.߸5 7JQsXD٢B8+8$u\[ͻ_KqMg_c>=|b`hU=FBY1p!DÉé|43SiZ SY׎MVh7>cD{QW] uGh_arlMf fYf&'LNiCBT7URĻ#߳ZV<ٛn\O] [&:j3Os?eb-XgҊu¸JP*q6_Dax8KUn`MSuNTτLJpvL]$vE=}x*zY 9 1OR^t²薵)j4+ap!{tj6P g#j?|؍h~4HxuS8hfdVaY ai1#+ψ8x?W"O<"\;)jSE$O4k G3pȃ }Xz$F ¸oaˆ~}Pv;` ҁe6ׇ ]N^璄\@ }\,BxT {'+arkfV@/ -esԦ?%{j-=yn%ʤn#QEy+I) >ڨKk'tKtK)~{~I40:JaE!XyRnĬ =JMsTæN:{G*tĨjIyWQ|; [P̮3"e:Kcr˚O~YH}4y""~iT=qvK(}T҆jԂDQR|?UhKE{CO>enI)U[G0-GdsB#T)Tl`,DEYWXྦྷl2).=%]Nc}%e jJSḪC)+$1J;3Z]M RٰNhf$8Z؈'ԕqUm?}ĝPtDl) 1.O]zUˣ?$^rM#?>Į2C3`Kn$WP}If^XYJYo}WoM |KutPdYD2FΜ$XQHDp|bz;k]YiJ_g(Ecz0e EJ܈{_~AY%T_?5jD =g{O# ˌvoGg׼+:hF)s,zobPV YNPOrJ48:OreVNr|H ]v`TQkb ͳ| ^Z OO@dt5,T^e{|sOT"mهBIjl`MAC|a3s!YS^ Ue#?u9:H^q Kߢ]kY5I$ؽڲY5%$V2CLwPy&h׬e ]P4DhLB.P8P[]ȦJC^ޣW+JRj'jk{k^[9Z4i~ҽe=XR؄`&#W#w_ ڀJť_KOU}Jj֏lfNR$J.Azup{-}Nsϝ릯a_cggmgܚ:V1a[†>|!Mkjvv զ:]z&!<0LFGf3DZ$8BxށqСd-Мa{fZ=a!Ȅ[bpa&XMOL3&Rh3ewp8XW qJZ'GԣD}ccN…E/~wC5輷mUV^FM""n{QPV碩+jA ;%261 #isޫVHaI'Om<=jH=K9^z7ΣZ^[HFWJWk+O؆TYYYv1Yf@ 9MR梿Gܧ3F 9j؆kִZWEMk)ݽzClV]8^mS*ҠXzu`Ve/E+ٶ メu樍k+5AD&͚RYWԣ)fN,wH]{.(n>˿!BPJ~G>O^G#a%Y6l 8nh д{, kdS^f|8~kKP 8 C?sçf =#HpC58N<&xv>uZ9ɖ=QB4N{uZ*"CgӰWf|8<Ԅ~S+RNVX rtH`arh #F d-1vLɴQR?5҉NHA~&nxESD֘#6C @D3V%Z,&Ao[B>99C(V縉ttyi=E2տΫYG2e ^uhdO޳6ei/gAlTj6ؙ|PUi6YwAfD>rMn71o %ŰR/6 ^SK\#6U~%dtsȹؘX9\̣q5sh HLJOG-FTS {gȞ2r&QR`[Uuē@ɼDoY 1"$:VRJ%mFw_nPg|4Kb9Яʭ~ί >QTD!}xiDOlU?몪M^ʽF,e٤.kHSyd=VUu#;yVPKsCMml"V~dm7S /CwיCm Sqr@#~o0?a".)|򕼣>Tԧn='p "Ȱ0\~uسqk1йV^92 zT]3e7<}lj5",y-| b\B; &ڑrԌg'i'J{d;wM\#=N1ovH?K~|LDQe"nۄslRwQ}5v$)1O}B~a ҏ.# Ir@:[ #?ܣV! Y: #'RĨMz:蒊 @HM8e+Și9Q~-6ٔgJtm'ؙvQC_P (l K PRLPP: 9챬i s $C0*%'׽N%)\;7aeO{1VB}2o}"jJ)=i'kSs]e_rMgLTLhQUqk#v_|Sh֭L$ s]lB3`>=`'cFݢ ,R"%$`k+ך!L!1?]K R%2F# %5l)19g݅+ع}Êg\/:WQ7L+M^NQdueElݛE+eӥ!0)ܳ$𞢷54M9h['*3yR (*pjfmUNJRQ(||+*+Zܦ<0n £~,È9)*_lc-1G6%DEL3 `Ny4 S*)~wГM!\5N\6Z AVS,i!y>,c4ł$(cєSg~(bࣺR)Ehh}wGgl5‘E))@ppj(zj1]0 NtCDZQyd!<χBTYP}M1o]t\Gq?O jhYڷ㯒HMI y}x)_K򈰇UG:-uJ2R7}WUO`SpQ~rۄ /68%>|2QqA@a /* O}_?u.J$O¸ 3,Sƫ:\Sv#B aDtjkq Ɣ&l~0³xsyimGp6(jX8#.BX䊕в*DFJP@".vZS !9)Rʼn^ DD A v# %?a< b"&D[[l .\؅"fv0N7r˼F1-+[&a\%2_Ǧܧ{jr>Uu総щlWz ztyu^ɢby˄2^AE}jp>i2 ;;̓Lܟ@'ϕNPTR5-#MWz r*9wqsQhi$52ڧ-o>;Zw^Rˮ =wՒhzؙ/uNbZj?U)_~gU5dgؙ8duٳ zz&D*$W 4 _#j,ىYasU5=a%^#$/(ՍmPT 6f'-Z[N~)jRQ-{D_)@E?#52 8V%4?uENҪ2n-Jd)_Y7}%~{ӫ;b:Qr5A7st#Gg|EBcD爉/~~v)EqgݰɑRoX]e =MykTL-KĢ(K#-gpzCCH) 2ԏC^o)-JưiY5%P>; {>N@WTeF%c0da4z5J,ٻy+ݻVNjV/1JS)YE}!) %mM{U/v(r?N{udѪgźEhcL'mͣ_Q 4s~V汻*Q{Geuc C؈a]pU zfۼ&z߸5gH, ;xɺi5IZ']P&b*ՕI[ǿG?dм$yW!f rϡo66dE3bf~ݽ*b6l$j }I* r94UVGv]5;VF";*dR9OpT>LZ#S;KMf 6o6XߩOũtVJy a z#m߲Tj4A uZ`e~jY˱Br*ԿyҳqP_fWIFaܧ3{ 釟O< aTUVfݤo?p.4=EM'g6cgTHOsn[ՔsQDkx-+ݳ&#8aAZV#tMBz4~HuM A A]8]!xa =Ã*s&ΥZo/Iy?}3.):I2_n_[!LZkkg漸;l^VfŃJW诉:a!VV^ܧ8XfT]=1jETqG,$$Ɲyįclֿރr .? OMVl%ZyL?%w_X8 ܵ+I3)4xf烠d,j pe9|+ߌa`㱽 /3Z-L ;F"O 3:Tӆ'ԏOm7<ևZXFw-QMn८T)1T[\PSBQ|{7^OcWwv[Y|Ǡxc!ExC`Gx(YnOjEd|֕@e%Pؕd{^/%tnW-GuUlZMeR獭bG^A  d}(q]Ӵ^2BZpjejqzKʙV0a=z⤦V#7IMT551"[HD0\<u1aL/Y?ӧ7ZrecTA|~yyir x8pW`w}R|s7'\z>N%p~ǯԼ؋w78v=W?]bypyz$LTkwՁNAfj) 7o^鋍 Y-wo=N}fRn+c|dnvj*aydVEߧ3}N>V;3[.~k4)2/\7 gXp3PLَOsg5V._8 a5]X+el#H%ψ(5:SP;h׃Gf~X v{԰bs~qyj˨3nfU~aD5XM`T$]r!)L_] ,l>kV쿽6kqTĭK#:RLJjCEeWr-1}]9qb3}"|K92>u_"q0ƃ'Q)3^'9@˳A\֥ VkK#'Q4{?iUļ\j'%J# 1EBL'ndءǭva5:gUReWv(UI@+kbݘL\u|jg6K zy fS ž=dKLم \f)7GDyjɫ괏Yrpk(0"FSjV`.{(ǒ?T pVvJd<"ebټ>g.Y`X')KVOZ9O]B|̴kv3ɒ6O'x$-ࣼG꟤ޮjjۭY~Z62вݴ-d;ZUM3ZǺ~xe},fԪ òmnl8Z6{lATgQ@zɛ-_} -P/88PpIjզqU 9h<%LS^% x(m(U(M7蘐 M -ìX$vR8.Ю n}[`)>nzŚ!! S<`;q|<?K%-5f LeV ?.L5$*Sd)[b';ZcSk_>7ԫ-WcS4O޳a:=JN8arGJ-p$L. qൺmŴ [gbLZ"j}4!CJ2"`@QRH8Ai1E_Q!\!l5I P"7puC7B5U1LG nw ˉٹF%& #ƙ@y>&\e.};m $APCӒϡoJxϮT%Fz.@1c~ °![&&EAN!<l f/u?Em=%fc;"sLA DDpI [&񡶏l#&uX@nd~uޫ_?sJ#3yJҥ8h Os-A3Q>Ub/i]׈^S`W&3O݀I= #GqGzO{٩T\Cqض*- ȣGFzjxV?S3`{|:+=XQjY߫Ĩ?Vjf.IdZqM]UG21 ź#tJTsnaiSmN`kXn{]_4h♧ kt°. ԇb֊ohSroi0/G.R4uUhϐ*KbƖV*鐚$J[Jl% ]RC l+K[WoKugB~:}&Y2Qw :ì7yӟ{SYº)- Y%h⡘U=0ޝs)x:Z]jnuS]B6梜tnUi)e\w5kZc|aVSfY%}k?`6,~!X%\u P} Bnւ/Qhwa4eƿ{#Y\V+Wܫ&L+'Ј Jӂ(d(5riF-R Ʊ|$D{z(]9E7>U0`]9Y4Ր{!n0-&GӐ#d}}XtL[Uh),4{™\E}Oҿ{8?G8?fuhѥp=|R%lr/g..wKx4V'TsK&ƪwkvEPp%SBb'thJU$^TePIOFHx~5԰ 78^k>Xh3ή8z -Ҭ0v.Ab5*W|+GndֳMY-ז%ܠ^`[|Z]JUg!h߻hnj~wjzЖ5)kl/+N=:7\/PPM,aمjJI-Ta2U14`Z0R_R!Zgia|L 9UMTK,R~EdYV\%҅+ (KiauEV2XTnVt\¯rSzOLd"ek6+z\?Ҕ^˒:)vp섌)zgt-{pg}F+a+}v1ŝŧlJMfyCG9h7s'Ed4Ub_TJͪC_ksj}Њu Ϯ-Z?⦆C@^ *ʴJ=RpZ]tlK٤bڕ*洛7QU' 䧶C|l>Iuf`QeBS.|z|ɣT*wZ1-5d[\D?ּm GĽ>Q*BeӰP=[Pt/Qvp_K/i=$,:X?$,CX_ЍPZBn@;VDi$ZҴaYPI=\{} J=M9mT\^ݽ;4iB%JLqUU\#XJ\UGM*8O &_&-Zi)Wb.5ׅz"Zѫ$[ ůN^!w֍?͇ڥw䢋#ڟЃFj;b%́(5(PX7FcA^H072|M_ZN JtSV!~a2I7{w_j%KAW<γх?. 3BFFCk;=vx涝ϋ1mN6ekwE࿽K?ҫm[ >ߥZ3OdNzoowǓj>:ͰzI!OFl*f߹m>[J[9_a5V2/LCs%R6iZ?F 2I>U 4Ъ+I|UW^f1$,>E<]Zତ>H9e/ֿ獛_?I=ïME|bI<*bԙ5k+H]V-Σ<D;bOlJ߯*"q5j7D[E2%Qv IPUu ,oYoeCPեZ~a }*+h)K C YTێB*O3/' d;ZDz}8˓EQ&98v L?"rپbruR#QIq2mY6UZd ;;g qwpuAlB$ B}gnL^2jo)DYnmy2v,lҀ髉DMejZUF:yjh=vnzόmZc* ℣)qOTrA{iTR=FUآv h^ҍQnK"Q']D}kreymk&k_' .~ѶٯiF"П(-@O)uviC]8auy) \gvp_S𦿌DƓ|-쫖gPUzThU5mRuUsLpw`瓖_W36#Q!?kQ9r1*;xjfjβ2XLj(AJ!0n\]r~2{ 2 ߚ䰓}'gwoŕ_(q WPΣp6# Rh ( Lx߈&@AA Qп>ЪJcJ3E&ݒȶw5wIT悙\痷0u[Eko/`ЇBtNWa'aɺ8 9+ fI-HPj , NR-TT㟲澨i}e]7 GMimsNl{.ѳKe| -P%\q #TޮӾr*A>Rlf^=)eߣ7Y&yu}}SPMmJ3K*!LE-BAuE1DpW%#OnvϷ(N05|h!f3Ky!(R=8IumN SV45FAApcw(6 9u:RE:Udlc#bz?gMms|-Qin:ܕ#}ٌOsFY=]EaBtXdkt(zcl2K(O5 YOC_ԙȞGUXݮj-tδPܟIt F#-u]ٷԪQ6Zz)6Te7-->M>g\k{e)=4*W$,.BݕKxF<&Iib&(9ED]$cMw3xAC^4>O@>L, pVVSgJT/ f 1B@}ekR8( &bT-=kA$}=3d'R`.yOxOM ׁ@ʛړXbE اo09;iFBNn*"_3˱{AO>="*9Y\tzz!,icg8PbʎYȍ_oHG(. 7ߊw7,؄v`EzDT8¨HzIV52׎ `F|{CkR`Yw*/a Z')Su Ol8"Tr5. Hh}5#tI6خ\EX*FAPɛ!cS jYKs|!40M0k]heBm_YV 7% Stܨ%sbRZ[?N'iex6Pv:e0] S]&ϤB^ A,b6Fnmk˱fCq'AΦlkk:/ 1 f. >P+^,^LR]sb;񰥠k#Pb'wo{Xe^7\LƪHQ֭Gl3⸲t~n޻.c5yPzq^SQTx45MrUju6VIrk?FxdY>J:;ȡRɻ=h_30/)M+(0Ǽ0!wTڜE 9Yo T?INjϑVu"5E/6 RUrs]#g{eXz+2#84ֵi*Ou|l[,j-M6c[{kkijdg{=* jrƾg;*o} ,.)(Jxb4:-{鲦Ұ.„YMSwEq|IM7͓?D: hlU '8k$==5ZP5ћ[ѨRTMx]h:5 RbcDP5t4Z2MmYs~2#+^= )\,{Jrr8 c&PͯC4h1UM:;])?JgcL ]gI03&<5. 䠟Dq%֬Rq(%N}=q^"|Ið"%9'm5 AXVb))I57RerZB9>}e$Y+)F DQ*nk]dTCه}e;ffȿk탷Lj&%*YMoy5@>d$Oyz/Dèv9.(43{WӨ>6=IL>%ENbQ&zhUhq Ked86 . ;5]ׯ\j׶׀y-\>U6dl=ɮx|ji/wjkγ&^k('V,t_VtRK!^FmpAR.6F'ϝ">k:ڹTD47-bxvފa<ԓ&>1BE8JQK`W)̷ת+74SIuZ_i:: kUHrl%vok I놰ڕ틲|6֧>ԤjS?#xLL^G6#\\kF!RI+<@^D&tܷ#;}o~5xiqۀ3Wy◙ ? o>it6KMo%MS\S*yO  W, HI ‹? &¼4Njg3jZ(UG8E;LdJ Zx뵶 _3'J$  CdcGffI 6ЛVɃ+_E1cVssQDPR\)F6Q+.M!U#.rc6У]OJwY\..D1@XЯ v1R*`oJ C4k_f[A")a`pZ87S}O]HG,NޛZ~-2$}WRYbc\15Uobi_8pw&f;f64v퓒g''vǴ)uͥhfp,ʽ)o촏Ud(GYXM|#^3"=FǁGz3&?mç#^c[؎"c%yRbt]&|9R"}Oϝ>%)(*j%az/d]c(!H<\ s4|D˜NYwI&8L,+6ծm6K\3ϹԇQNtM ޅ ҁr{6'1īCD\'tvF F¹(zܴ FU)V\fyBDB@U]uҖ[jYNRHhVxߗ[; \_ՑdTKcVx4݅` pJ--0ixsƱKnbXkHr"o*"hcּ"r/# )4JQYKߪM( bizfˠ'*z%Oyɒ$Sk m.rCp̸]ٿ:yS ̓myYJJǂNZv!oWRQJ&=KWxJcbBpbI?E!&DHJq4n5\F+M!#Y=TWԺvc^։SB-NVRdLšG35tFvBM3.ư-xR]rCXR&"V4:t[%lHBMO[7o|˾EkUCG]YM^KJxI4-a](k#<[Ru$1g r1ml?M xۂρKsQ}{嘆pc9jfzp֡R<,;rK1]5K/)YIyHw*淺M"nO%y!USUc*E/+$M=b'ZZ)Ma䕣li=?[kke6Dmc0_sc?c?xn`/w ˂U{,^ `\Z:x/JO܅NUGb˩{]b"NT^{Lù[[#cK=gﰞUjT+daX-!ӹUBb)+l'Ώgڊ(*3۪ʹ&db] [Uڕf%'CIfqZơ#E1E&vŻ)e^R=2OaYJMiӐ!Uˆ,(jQZW'A!)ʛFK>,ѾvП}t_dK 8,sp:vV_xJ^c~+xyw53 EnӘb-.j|澉EivT6h,/>x0<05yJ 㥓&umE`G=yt[^!㦪Ɯ< cyBzi~Jb/l]Ih|$S6ӳ(nvkYTWz,RBmL˯UpVɘHV7'.HϺ-1:Ƨ+c|a(,p*fGiΘw5epnwıaV$Qa\$ǝ!GCx~Hu6/-g@$&- By u^=,PD]șĐ CL/,?C+Hȡϩ"%J+ gi=],E ݥRvDE}+g}83L˅~[?D?#bފȆXtm}ֿ1Wu_jLvE1YCPEkWrqݙ?c[-}\tjfj|Crdz ;56my~仳q>o(QQ(1bȯtGMkbTlmf5JoWѥٕu[.[%upGuQ aV}~ :r?ڽ =6V;)mfu2Hu8aYN6c"ѓ 3LuqM+ vH=ez*2N"/{n;OLSJmxPEMV/ntEru 6UL8ҹ-VS5_7z/ۚW᫉CR7_{,JoU|dP&_Z`* Jf>4z-O~SVceC]n+zzڤ3aWnɲM"gUKtPb2elOXWqR1"swcGLUnSgG^2>J R%#*eaĿ4  ?@0dpRV(g-#)kpX+b1|/(spR%uMb 7=&^mYz }JSw%<(493foQ^CYeG},r(a쬹r7Y&I 5fNL1O':Cd:C\fB6ЛM}:B9T1IBPP)2ʳuLrWV*Zx_z{ZխX<$*+⎛I9D"Ow]8y>zQ'D~vqLqM/`>]Xm'][We {H":ƜApDadndz즋Rl -iqduTbfw^MpNhTYե$(k)^vcU7oHPr[όs0=8SԇE1M "9Yojk,~X0 N, 1; 4Gء*oFZ)rS|Ox(?9'K} S[ǼЩS>vهN"+(^5.Bޜ6ힵ>qaq]̴d"!>={OT.:.yߛ _EV~k|/2%U'##]o`7}I`Z)j}w[h=' QsW rQ۾d])cU=3`ߒ%uN |ؿBGjDwJ8֢z%dt1|{_8TVfzNStk/! dQ?vY؆)ڀ00 e[sԭD)۾9ߊtp:5 饨CD.H(3#47{.Wr^"+Hɹug4LcdVrT sG9exdaf`/W8ER7&{(dG"Agb >3j}#xF 妎 Qr@.VK&]H?D pz ښޚyX W_oOYVQ[5kbSS/n 04GC7"Aż}0C;_ҵ*ᔦԬUqCpuS,!Zjbʓ L0k> bUxUJAUu&O j(+LMDOf,"aBV3OY 4`($Ǯ^7c8# wEw(>IV: N@kus|gaXd%tnU Qc@2]j0KpzfTToWp]>-J(UԤ]'w{qH9ɆN\&zp<)ħ4rLLR2ۈcb}2:ׇUG|ɲan:eo.ϱ'GyOmނ)ш'kP.F;}ҮN{5xzo+iR&Hg8éTݲdͷ*z2$b]T~CzϽkh9M#{oBn\-zw} J蠲ɩ].y}M[Z&-9m$Qصlt̺*JNcdjα3Ȗ8 3 S6VؚM k $^RRlbQ@~sAg{5%0]殂rQR0OTiH穎Gyu[XeELӎS>b؜W[O#KfيR@3T{Qw`<^eccm %f(i? SRꢴƻ+RfIW4Îٓ{""UPJPSz*uJ4mnx ux]K63+UĔj؆tdݾ79fB>,J!t /CP, Q;q賾1eɞ@#cPY]j2zzV̻Kib5E(Yۼ7C.[Dԧg䶸\uc}ey]0W}J{?SP;wcx/;$O6_gDfWӫ@d+¦‚ *Q$q<[z րdov@ ^Dv'+ģ[#6x,Q0Ej䵔ۢ9ܯ\9eV_<̍,R#F_hʌF[4*_ɕYL!~QPϥ ⠙rp~dO!ltIݮкvys&̫i;wsoaCgtK8j̣^0ͩ*o_6ܿX(;] He7>ֵ9Ltݽ+3>֨>~RlM>vwjh%뭫dӿq\sD# m#$zmeDSqI&=>Q+~g3ɟ'V$>6g>+[>ʼ2?`XhSjZblщyБJ~7~^ʸҨ˓{>K_:dXWVi"Yq "XSU[V*KKs&h듩g* eˤ6t{}ɛzF>ʫCE%E7Ɣ`trj3A3>U™;,qQ,2$I ..i*J60%(]J&4tgvy^[51N/(٫Rcy* Rޮ+Ι2tolZHO&\ IǟA˛>3$L) [7y-T*=DT3)@b&%uqJ|.SSKEOkýUSOlugl[+Zֲ,U_2=e]ZW,HґH2?wXyWUT-0cn]%vb5{~50: ~ǔFݧ(1|%ʥV~^Ūf-z" t [S:ӊa IUTRa R TDU5E+ZLI$ &L+Pa+ZZ%Y&DGdXK7vf MfEaأH Ӥz-ۯUp+ -(+Jmﯼ * md*hB8ۏTTKiU8h~;=;vzWW[7X73k6&͟S3kbVc`SC#C=ΤYr[o/0^(8Uo+]UGYP\^dF\恵?qp"NoG7jFjK,}e3TΆTNhOQZMU:{eĢa]hyy˹l^nw֪UȎIĠ̠bn W~a:uw8)nE4(cq6WPaejFm4=* L,v- _ٍުTk%%F%J7&x3ZU܏eɁ.P[I< N.;ͧyҢ/\nIAAe59iDJ9R:jVVF0_Xs҇۝YiUNm25,4N@k6ݞ[Mf&סQQv:w{m=󳹘7qS20n8܉s!MeOjJr"yݮw eЅ*%UnisPu.!r >vV ӢN&7 Usf*k% ~G:6'u0v=x\;92!YWNJ}2˲ 4&vҳYqƕ8hpXj{e*Fw saIR䨐&ˬN' άV@쫧&_TPY] z GC-*,D%B[[t17uks- M)yv/In5fg5VJ Eu+Z׻ur}d,Mؐ.fq3te_b}=R{֤Nb]М%ݠnrD Xulw57%S|^j &SYu]YQ]urW>=[ YܪF)ηi5[i"U_`at2XyS1FWe\WMYepTu5M [O0EΚZD *K+i[{ʴ]VdDW}f0JcRuXReim?By3e3ˋ7Tw]n]*ND%ņ&s5 u+mW/2';vF2˲WHOp=yTоW*7_UsܕMU۪-eWuBV7eTz^D׉aܹ^g+yojYJ*L3FFifNvm'2!A]etn,ȚAΨjocTRI8+V)(Ny݆}i&>/0}qe8G-/ \gIF_TX֦R!UT*RUT*(LLBBK"RUXPE-PjR(ت`HJR ʼn}@٧})[M.D*ͩ֔J%;睮YIR{>$_12>dѬ=%,El&YYmCt3?5}=}t *Ưۭ/a H؎gݖ +h6]Ml6[rGE omHRMd^NzRmW'g\p5 P(-Aq2Hd-[(2]kЖabĴkL=ᛱ˰iJQU4*PUVeBU$*UBIBhj--B!biTcG]W i&UDW j5EeԎ2ٝ1[X&PSvs3J4+FcNUh_|J8X契n.Tg]BHn痯VN{ԦBuz U'mf6ll;m]m5AQ"$]Τ"Y|а.[o(P8!DXYNr -hԒBeGiU^dZ:Ȯ:[ojn'eNz9P(3k6Nr6uݤƛ*#iȢٍbJF6:P,ε#ٕKۻByWk.J®ONp4,^VvcHuꢴZIjjG EV1m6*4SZJn5UbT@I.Mki$‹]IAR_:'3z:ںk#{iy~r֞&+Hy>I `̠YVN73f;ƺ|H\ =nBDd!bsDI_QiX#Vm3\XPZM߉R{:KJ t7Sٔm76ۭe$ؘ.m nN5ҸU]9yj|Dvr fBE M +{*WY+ E""Rӝ_eMA|{{ƬYGYae۵*eďUǍm'[OPZZNq2,uY]$d{/vʊ9(RRWZZWbF$;E䪩+ve镯-M_][>۴UțbHߵMb(#Tq̍I NĞiѠFE4 R7{ˋ+VZ8_4.Z嵗CɑEb o97Ms&SC^&怩v ɴ)R5)*R!*#@ .e%$mLkT*^oD}5>5%+ԝ7Mchm JiT+6 *KVWmofKo᤿jg#3Yq|̊-dÔ޼'*;eąQ;b&AeV!c]PqmI9bW:NIWP071('\F%54 KͅCC:E)I)Nm3~vVK$<|ϥIyE_wMB=zdž魴uOeeax;5[ƃB#'I״l7T 3v3ۙ]¢jGm#Z%*ƽ3gI9xo* {j,(o]{]QƇns݅vEed<,aR'jEUydq759&'{2tgY|SąmjZN}싕 a qʺo Eܗpl/LBEBܽ9$87]Z­)/Q9m]aڶz NK*je *پlwPNF׮~?9Ŗ˛oFгʹħ%ʵms.]o,(go1ǿBȞңAr[o66\:,/ʜ%PjU4α!n^%*¡'K[+yFT*sglz4BE[I{ k+6զ6vtdk6`alHx%iIq7nd#ڴ1lk0^wrK`oV#*L,17YݗLe4 Jl%p=rǁi!87;I^PxC2R/ȧgh85_ ۸<<ǸҜʨ+Ȧ OG*zOQ]B ^'Ii"m'|TmYEW_5LuIưPS^~ M *_ƼPt3)*; Y )xV(͞= 57TDOvESCǥvj9m[ϵpFlw 7»m%`o¾kQ(;HY='-$fwĆSUW_vjUW)0 + ++!iPo)Q[B-P$A|-'"yo;KJ5Yf톇$YUޯ6 J%B_gNl'UlߍȰ챢djq8JTy4$n7`zy*+ov~+ISҫ7 %Pe]+uzFf(EF6R%$sRڥ. q^rVtAz+yby8Y\2]1Beߺξ鍍krf. uaEd.2jƞ59L3CM55k;FmU[aϚcƋq7G+qHʞ̞6Z*F$-a7QQVM˵U/3f^k3y7cbGY\MEPA>HziC3iD!f@q:_(\fڪV5=h֛$[xS7n8xϑmЩ SYUCĮYF&5#+BJk/QyzMN 1p)W[qV='ډ WJД:[EŇRZ6`XQ/Q&\ yѵEW_"P"Uഛ;ayȯc6 Uw㛙#iN"* izY\+''XC|hT'Js(*dh+ʢ5"R3+>;PPJW-Caf;LB:5MAJNu]WYkhKKЁMu+J&{(^4ٖQ\Oʫ`Q&1ZYo,<))!STR'7<2-q*'UXTk)mzlK 7U&`n+U~-Fu\i-u49|Xmc[W~k:۲ONPkHHöWNt2NOVp;yЫ^a]BZ ۳\ .DH٤dt(J̷]ey]>¢⌼RMka}J˴W&m(1tOlQ.ڂ: -ԶK_}agdl5L͙m"DKMvVE}R5K{;j ꉺY[mlvYݞvؖWV[/ΞV*(֦l-1WV٦5S]Ơé9a)ЬNC̸YQU}*ݨĬطyM zUĝe56ㅴJPeU_rºмS*55bJںViQۋYi]}ȁJg:mWe RTt,,Sb^Y[^0mE$[ ت(LrhۂZ6;aid=@S] &.)bho>[hU.q% ]RlVuv};m9$1ȿ~9;N,nl# +J;OVGy;𠩙~# U=zlj*{RNʪ4([H{QuNU8eJ|M9E,nt0)fZec9z :֗a#Eh%jTI,Hu[=7mQϛEȵU5YM>;P,_ܤJ+_Z%{~JRSM_#L6 jV j)Xı^ReV?%N-d !j\Oy"ʗsK})A|wD?W`dLw/ƺ}oIFs}b muK&D9m'5˭}3G q&U+H_+*ToQ=ǥSUgmPo7xHl f] g:fFhU'V7`Pʧ3+sYȉA=CIͩ E5VթظyU>L. Iܵo`ca̚uKV҉a~ Υ啖³9o*4%w=tMYZÍJ{9ƻ%XceFԅ\MμTfP`FHW^mCc©IƤYPOFB'#wLXͧI> =O5<lWUiGjYB6wa %[#m۪)i8U"RoVM59eҕ9G}:Я"0݇ŧfr掉X+-;ca^wBuyR5q1wY5ʍ(VuNsKiiEA"FړvYXXR]iA5A>GpIOrRUpAat#v{ vH>Ҝ3,-78D-VBr>-UIAi xQ~$îƴ"G2\OVPsO~ǻ6 jqQa BkGUC?A46{p0ؽnD3zemKZ yձr&Z᢭7Sscrz{u}e܉Sԝ}&,tު~F6F}yT13yp4^qO"%1Za«IUI]WzJO}uN~Wĝ6扩k̍9s+]pJƪQ`pJo^Rm W0H N=YT.%aUKCy&" /qVTZT1=s#gɰ۰ȿ*F^FƔe@9GZ{0NyREEЗ nA,lah+y!Ylb֔ ;|tI(Nd"pȤvbKFiK1=67Gb&E*]D#T*"YĺA (ie5k#Q L ,v~Z/Rw[;Ye2KhHF&4^\ԝKӽ.Y JuGd<4$hò`FrĢO27E1hC}u7oӱH:di=bkʉ5ןBħBcETP(<$ SSf$EF1x%Gxh<*U7NL<".zx͓D( Q=4ޫRG|HJ_6`+ ѥ0⑈*i_oVFy[#kkO4yYl\T+lzHP0Dq%<ClXR 5+wMOG2ymܠ`K_bDx#0rH}V'h}7;'6o`~nŠV,nxn {XnY4LfR;J.a. YH\0f$:Qy  Ջ3&ck'ɯcNf͏zj6]̒xaT*6`.+Jh)cNT^,%InΙ;1;[.87i~}3բԍ1 /LBlUEF + ݶĪDJȳIAe΍fKM4. \w.nM>_TYm)}H'UoI룑%椕ΪxQ˵򗥨o3߰ 3.u1UY|Z5i[Ncq(4 dEŨHwG_L`>1j`YTLUT\]fw  HOe:cKw((!~.ѶH b`aJ0c??jT2n$RuGi,.,Z?A,Ma].^iomzǩ&}ߡү_Sj*wEU '=S!9=D/}eZܞ3d?Ց*Fi'?{9]n+g E U{#kܨ'+b3b0ñBBRcbA/w>m^dL"z2JY:@%kcu$*UR,lq^gEMK i+ Gtqi5THQER.=2P 6}4C.~nՒrt"{k"![=w0!8D,$xljЙGDpJHY6xrifip&p ,O{1_X<\١NsHn(ؑyNHaJFy#tO>u@z?+vTe=Oļ*w-d;fcUaWk/)fdO5i=#}aƶWmWRwQuSZ2)Oueaag2*P ڋiYρqULbB;'6o:+E]ڒ\F.5ED9kWbD0aQBqY[w2saj⌋ͭr)gXBeWcZqQZ){jDrU*S n ]'?.QC"Gn"u-.KM&i̍AaUD_/P`X_֪NbPy҃mb;hH 1%XZJǓ+mqA>2*̌JBkJo372~&*56FL_ijsfʦVFe9e2շ`dwvt\_eWu]Qvٻ8dX\GL#"kE18i{($;N-S!֘l_Q)TwnGUu ]~k~˝Dϥ2>ڪktY֣uݞ]Rjjk53e~Ex2SXU/U}AY5aIڞu} )v jCI#Rn3' jڞmb&fea=mAn* \k(8ԅ_6VKmGe)+;)H˩~M־}qヨΨ)puoBI1jw *= FV2W.G)7{< Yr._G]t)VqD)ӴUӞg=7ufA5;鑿Iu 8VS*۹U{Z:Q*<ĪжcSco(:!AUu=R}n<{VODv_ :K-uX,l'V%4%ǩe^ѫVAJL ٛ_8:6T2vj-'ҚeA6βöbv9W*U%vuEդvPRנ젥3B¼ާe J {2O/'&ʾȟć]A 3U=˻Y[ JIк182K]mYRc*o:׸.m'YRzKN%<8HaUi}ȹ97"uuԪ"ʱiȜoa9wb$e&FI҅yٳzD5KBl*9UUXT_m>H$PgSx^FDz\"k7DhLD{5ײΑi"VUL< m&)̙EU5sWԂ Wm\rq>Ul(wVU*1ymxŭ;[uv6%alos7*g\tx蠑 &堬ɳk;%lT_آDu6B{J򇸩f)Qjk10*˴E; aR}2;Ui)kT ޚ"n *o&inH֯}M-e'mΉqeVUfo]ҝ*]wkm[.. wטAlZ?#+<+2((Nq@҉e=KKi:rqQN&FG}.KȬF_z 06]o͂eVu]Imq˼**o\WNBn<),ͷK*JQYTM}.gRWaWg̰ywֻ +*ɮme*7L eQr"E}Bmr꫶ڤdq)B?ۙFqae]cwܬ(m(@*sY9$5,Mnwxճ(UDZv뉥 ]#qӄIë¬: U7Q,#^"%սNvh5+WRjn-5*M*mSUV/MHj_ggQ]nK LH\/GU*mwaeA mVVF JzTH5׳(ZUlWC\\Nis/ 7 Oƞ`Y\GAZ w _-};GҶ]Yz.Qq8$*.gJy4Vi]iRS^%gURZ{uDF%mܙ$TQI԰eΕm{N~ʯ 2mg{waNu`Yi mˏ" Js),%{mIX mj]TcJ2(46ZfcjO~KˍR ߟߏ}쪣W]ĵNj)gaN{c@ىYXU?YY.ڲu j۷]J`O6P=:HH6cIym1Ƴūk8ʱ\\M1=9冋 Y!q.YVŪ=Y,Jo8 QiToVׇ`J1߿ gQfo"$.*WܵW[j O/ Yoդ*GUEiF4ܩ uo;V*{TmW)0Uc z4bиoijmoJĉaoF *V+:%&{KaEޟSM+qnލ)!^DNZW[]{i"Sm]eEDM,9е $opy$Ɵk2Wd̓C[ޠ3X`#Z^AzfCzУy¬]Je *P@~ôx[X^rنf:יt5(Hۊ.S"ZXgʚ:Skw&[J皉ʤcr,'TidBRw㕁YiBT]eltzS(/67UeFfR^m)6D2-VI)ԡYdٓӗ]^ ҳq e dUáv_/.SfeOe6W}abUbVea\-72BsicA}KТFق &%ٜ6ɮMI#ϕ$ס  KOخz6YX_ 'uJ+*Qp'!@ҝC/q48ND+iJKLgXWNfz:g]J$X*k쓴iAbjѢorƛ7Y *UՌn'J4 ,xk̮Mx3WI5SvKo(, SBzd:e֡F¢mDx vY:z>Ҹop5՘[N-}.DCKauOaĵֲsZQ\b[n:]ṢwٙPO_Vʜͮ)t: <P B{;g;F9> %!2 iz/,F?+Rg BBز!kJPl+56.2.6ymz4%Vzdp 3o 'M.p)< *W+:AQ{jK6}k0,rZBy"fPE5}ME#,01{]VwaKI JE; {ߥK~&+"e|'\BۥAx^'-)O*12-`HNp4懟YW*%wV_\MvVm. | 9gXundq2wv6TU5y]j պΉ 'ƽ=n<)-ug I9](LgwfeMJ;1+3ifq8u+sFeV*k^ek;N¯;Ycp*J}i+r+Nkzr2pP\zOi|i#;WaF;ӗߨ9a5RWO˙:r^ky:X[ʖP'=մ7לnV$eve$*o y"jGv;;jMWk8Xb]kYۉ#[ũE=I=AT[TXЮt)& jӜV )-iWHUk16dl-%\ucUhPAK]IZqˬNd(Ȱ|Zȗ16vXl+Ft?u,Ar OжJ^YcgXЬҜ?]F¢E-4YTm5빷P ʚSV'2k4m9l#G ,#e5`μBk"+о5v!ȫeEYP@Om*)2ua\Ym+&6W[vfL;&18EƐ!Պ1b 賮ZmFb|}\MMMnhYkJoam%fEyQaP\R_"ńU6:F)TlNOX)r`mm^%ڲGJ[m/wZvS#ҮtVWI5- S ̶,+wѵl=ߡ}]>G\UgWZ1#dQDAb-*VJU3 +om|u.Bڻn%[S/zI2U/}ϓMuW@f`Zv56i57TWy"Wfz\񰱚nr7稿G*{O 6k-wBtP@S]W6MF'Zj)`%6m핇{jҊ;z.U uzjjS"Mu2kPnU+˵ZO[U(*<(%ێ6 ݶkd_hbs3.h{MŴs;l tP7R+l"s!YXp)47Xviqrm*뚊 ,xlE a^4/13QUeꮍEĜp*"cF=Zi% WSTu5M5[PnU([]aPk* Ihɯ ĥMK9[nmӚURi@ubY}:Lnlղԉyy=_.EZ+56u\M"6J k"|>rYinVgy!x )Z̫*H!BژiVl*Ӈ]o5^"R4HWtMEum9 - ѻNeY+: F}ia|?>U]YF'qgiI6ĕAM짫jaYA,jȶS\Z`_}E4udo =*T Mr8S\s<+(zņNVfGʴM=ne}NT4L\جYFJt~cyJ~H'Yčr.#̡X}m+EJZƵx SuRfo¨Ц9_JY5sVwZUT*RJq$_Xxݵd*o#2=6V ž2+-;*DjmNFFBB} >:4Yt-4; UUtQ; n_fT;4RHm" UQ53f^^s.mxߗ.mqRv O ,khЧh ܪYDQdXneV&ږ)[55.uۊ*,K}%La^۞TRjm8Ѩj\r8\ Iֿ8R6f`_Xvl.W46S3"MA^RĶَ.mfeFftևO[[Had4&-CRhOnQhXp+^P_l>CBí޽3[iA;.ue'iάTҤWTk1\̘+>=ue7mҬq,fa6Z5[ : V)Sa{۸5XX_d]fnR]llX5I{Y,KFUtItL<݆y9Ή\PRR{}}UAIAY=oBQvQ%lPA3=au&E"kn%ұ2s ljEVLLnmzmdE.qKe%ڛJhe^0l="MX/vڙ++_ScŏeSmUeE˕IIk,Qҹyʬ 5Dk3QQkhʉe$pA34d+H P{(:d/Y<f1viޒF- 3SKb;HIʎ7bmn+l#%ǻgYRqvىך#X納ITSOzqzH@gTG3:TPf]yqjmDQwlLm+9|l|zk}3^ Ħݒ:H-GK^RjUcC]sf_Y,̱RT)2B4Vg[sTg jV%JaiXEDɳ Ym퇅ǻôQ-7*Wj]x,$UTN`UE+eQ.0NOyUUwaJu57hIȟO 1`cQHִiYd^HM"YRj,LWMwi-Sf;ض1,3}qϬbO\g^KZ6RRAM=$}D6$dgyW18hCMUQxBF&%< fIѼаcqUeEMʵ L-$Kj )[MֲvD,U^׭l6Ԥo0),,ڋu #Ȭw"s[Rzϲ򲡾l!oBn `ʲ E3JnV`EWhp3],PJVBFl.SH2{+›9>MduS"E6z5%'Vn7:<)*QcjT`T@>]i'"F̿*wiuaeKMkkV}dبB}Jv͞&(݆FWec'"SIt.2{Cڷ)ΚCuƩԲFABV'(zuTU%byWUZXp6*I Hs]d׋ FWgTJJ;t14H}IQ:\nPv+@.98I}IKw<7jѧ4+'5/WAR:ڟjpo%YmؾzJzUt _ ࢲĴ@{et6qYM }Gm#^JQ48,?-{5+7T&WQep>nbIJ("ʅES2 Z٧Oƪ=MfS"[NEEӴʲy>i&;2m/3.5#BQVtJYS ɹ/K*Km73Ju~3YiXb_U)MWkށiF%uz)m(/7v(8iI%ViҚ1M`ϳȃ #aQ9řQ.=e>7CtQ`Xaϸw]5QDbPgr683QFVeFkmґƈl:J#YQZVr9\U.XEwzW ;l֌8ܓ:Y&j5ʪb2BE )Õ r%̭.p:Q% b3շB kKj:uh͂94XQ.*.=j nlŹΆu &xQ, ol18CҪGi4Z`K t+^5}xhߊ8{Hee; FZ< j?#Vrb 6S?ЩSdm*o*l9Ӓv[,?ؽECyknHШ$O*K ymZ=c*Z̠mu)>RE]-,2KMYRw=TXm ;׾hgy(o1o[ZguCiSʑ[q%yP8YMμfeV*9VkUX,l}vپ9pXeQr0B]ais|oC2{oןrƹ_0ˬxߕ&ۼְse̵_1x bM럕 ~t ?ShZȸxV) n$(XsMI]*TZύump/ŽWi\YVD{?7}=ίI9l5 t),G:Wi}\z}/Uix8I68,;mff7=w#hf[if]c9ˮ$z+We;,hN ۉKA̪Phm>޳wc,$\Km"s=~jv$ȠzZS+#Np6ε%$S¤mj{[Q*wTTRYܯ+#M2NjΗB)O]gR&Sim"Φ5=)5ʏ RWC4)u"jUgm;O7=O*{%NOZ9Lid`aqEEAMs'•Yݫ[KL )JenXq8/KIJg}*g]׉g^PWBgAg]9ocUa[/ڨ;giiBfJao,)0ieODu*#DknZӖSi'4<{*RЭ="zk"YMk"U{*nbuNo!ʑ Y;R9t(63>ń%V^ޠQ3Xוּu&I>U##^gt!zZLjTXh»Uy{0+7]&\4Y%der¸U yȬҤ2kJ i]:-v9@EzGT+,Ou]= R5o*!yg>u$y;(XWMQk!H,l섍y-ڪU26le5nLJ{K៏iaiQkz.Bk f̦PD6VSqa]sJOk%ځq\tKqr#z㶛}rʆ '[;İкYx/&ͤP*'zdV`u#VR%TMX4(^n8T[a {uGL/˯]ZDݸJnk yBFW:y̮ʠ: h)Sem>ipWsUe@bdmWB"E'LvLU36撹Z*qVl_$>fgRzUƤ:Y}7-;np\O{[.T6`jUV޻k9Q^Ayl+תvO݅G* {Nŧa$k;3kݞ;5Wl=ҍnžs[X^FU,eGnfvzMvA,!Bӻiw|ʭH.q!qڃWcq=*wK^e$٩6裃Zו*EŅR !'PʨkǶVУvݧC]q=gҨq!\q ϙS-sϼX )nE73 ޤ9.W22.Dԑo"KOD>>o]ffe 6^{?[=Am3e;)ýRetiS_Ui##or*_x\+1V5vԖ~ڃĘR:P8=&+ֆ UژX쎥q]ONR (m.3#t$l*k˨ian1M mQ-&%:L7rkpY\nV=xgk)H̾])㼟RdaE_=%ն:\Hx(ꅯ͑FYp,0HQ^GeI=vCu R[Ҷ3E;7שuPdvne7n~w\cL 7d*ճ%"o,<+|RKzraĬZRTRRWx״WV[Iɮu=uյ}~ /}Y[l([wTZՉ3u"F6Y6\+"A֛]6wlsM]NWFUM"6o^s4+6oA"I9V̊>EdW4*Qje,t\.jkJ-_:f H32DN.$6T؝oʹM}UVל d囕Nʊju r64-I\_RԝU_ҍOSfk7.n^Шg׉QdoG9Si a^E\mզ-Lko6|)cVNwVP;lv'P0΂UDdGIוJgR\[a>N-o¯WȚƑAȱU5m=iΖdGEthQ3鑡ZbOP4'8mJTPJND@fvzfk+FaUG9K GeIIVga'gm&WUSP7)33/\x+ҶkqSvݤ4 l7W\ [H޳}YѡWN4j*eÿw})8禒㸎3J 5&ӁƉ^'k47- 5w欬f{/*j& _x6J4U%w?%UkbOӰݖiWGmi]b£ f##q$WkTPUZu)i[I29jt3a𭪪YeFa \I9MUXWMFM8zmiVfʺ;6YN¡y#{ގ G ^7OCMfڪ{^KϞ}ma5(&Ӯ= Vjz͸\Zn0hT^Bkev[; knV]ٻO>z ox-)4UdFfKٲl0-1g3JlK%U7zzJRT{5 9re8.:^ ^҃ӴΉeƪl%s]]q} ][:GBz.l PeMqabxl*5'r,X/\ \rnr'bK"%ֶmiS؁;_FvfQ3F9qVNKO5c_ҟ>lKxT(ݳ9\kZXwI"F2ഏCSK3?%d}k͋I皏*5{ʹS`J 颬:'!)Ou4C,Jl˰srm KvٸJMn4(A2 3"9OI[Z/ˡWˉԪ4,<LVH& 1'wZLś{e_]{ -d:=6;U8BMʼx85jq/' b4,%Nl5xjIKyBAEt8/9Oۦ= fV'Πa5)n{Z l̈́m*ѿ/$OzSY޴d{ Ru o.#7"}nio~nu{^g[-FĤF,6wot1>Mq۠z n#u6|o?qO}Nff$T[o{k3Woimbr;?Qd ڶԪ]?ٴO\Vn {zTIC#]zʑzsasv^d}zc_M羍*LN []M*4ik5=M6Ko U}rԬJ&_ep8UZ8ĤĕESpKhTY\oR*.]Ɖ=4UX%)Y(x~dgIJFG2$8m>m[ M.盳fY_UP˿ vk/Mċ~W26V'mڋo>_~&[㉍ OYUTK YeQ7_-6qx?ul>m{R]ܭ| ɾnh 6Bq]W_@#Z4Y!~|+n%W6g>4]Pr=?yR+Ȧʽ0;͢.DBkw;ݩe '}g*]GQҌ ~}??+ok"S1&ĝDOwY#] .8]CbDK3uʲ̽2${ 6Ly O}$eT)pT<>ߛImMnζ66_puz46~A˲{/8k~Ƌ(60?̑?܋=*KdR~X(>؏::M3?mago$T݆#EPٕU[7+kH]GoUJ%OX|-W~dkH{[u Hi9eDPm*JG7}JIWZYloaQY u{7jmw*鸒GRGg6ґ+ nFڵ['D {9 1-,tYL2{e.+.=j64\G׻7>65HT?QӲUN˻-F gs:4(20&ࢉPB4~VrySEVnoc]kQ 6tԋ~.}I;Îd\_|kh܍'*1DDnzfJ%7Y䫸Bk'yyu/-,pbM6\*g \E9WHm[4 -} OJצI5?''.02.VGEY}F]myY_i*nʦƶQntTQk8UW{(~,ZX9W޻B~f{Myn8R:v]N'mU\agC5UӫIuI+vUQ&ˑmgv}=aMIمbuRve뭴4;;-ڐ(Xo2-6ZHN"}fiA352Rӳ΂$\n2+eeԃiX;";Ms:w7^k0PLuƨPω/p;&==!CUE`x97wU5fUGw(.k)j}sȉ]Qm|BK; FUA]KC=y%Ge}ֺz7&i2SZa00Og{ !@ kjkbgy4/(haV u^D՞MVCkAznH_ޠvUPs/SfA2r*,TQěIᾹ3UcT ıqzsUI4){#[t6Ч֬-/,OgyzLgsz"'Vα ÑЪMZdQ= UJ 㬧2j}=iZ\+n)h+LFK<(l pZ7E2cJ5W?Y>~7ݔ;(AxyJh,,jBgmL״V&2!k2DOm:r7{rzׯSVo)V4۵|̶:[aU ڒlΦ8/eEI;X6yu;i[vi_4I&]4,\Ph=^U7S43|*YwhM/j;myIivC]"4Nh\6$Ԝe[T -\Om|R>f47a,[mƸѶ?t:[-u_+t;ko:z&%"rtjjPPT#Jw-Ƭ[(՞(T噢Ua+Yw%ckvh5\t,ʎ=#my!vyY[͵t/7gw9#m#+aRIrPm3H[J)֊%ɷ3ZFwXR^R[xyԜj)ʬ;˕wq).T3kaaBv}ՎVs(i ݙ뿖u?|~ʦf%fYM̻%{V#ۚ_Ml- rj^gK+E%tbWH̋aHQMi_|.T[k\sMv&ٙQj]yQ+yxޚYH]a@[X]KkL,p*0$Ur!OrNw.B)žE]=^A"De̒jT* )V5&U7uINիXm0i&uu2JVfv兺-̲BUSzi쬩_i*e= ޾^rcYZpOu=WS\n5dv5]/}Ī5> *W.ٮfUSWmbNVl9SW"gn$3YJHQ[Vv+SU,<[CR'miN}{5}˶Un; k  jVu4a}yRn:MUp8Y[l6Ԙ9xJMe%F͵$h3,6e~sB(Z"iK *?k2TҊO8Wy",P@]=AbBN8ێW4n7e~nD %5d4 [aA|^"Kⴌ+;{vTgjVTt&Zuz$UiKS}C%뵽&ԲWp%@fSҡJdoSA][ iUR6Y˴Z-I]%LXl11?{AI+Z[6P]DTKq=m9{ayԢ]qǮZWܣCu~>UWکy{KK,̭%tkmXA9,V58ZeI,84-)UVP{r.v|86fFڇyơwY9]u5p(])ﱝJSW2 yå#{SP D'Q PnڙkYE5)8o,\sұqmYΦ0ʼBk+B| w@]纶̉ʲj yGގ{*Ⱥ'W=kq0<)TZZUmTYC/wcWBˢ qiC;~Fu޺mߙTI*8?n?U#|޼QM񨳱WXH'8SւfG  O3t.,B#ޱnԚ+zLi"uu69|=^ĜHTnPDP ;jMJMٚ"Ʒidc-&ycDʼԙX|55wV UV-gݻ]qG𣙅qQU}멸8Q50B+mY. kdOzǨM[9a]{pk X}v.Q>/Nmx6J %Fв?25=ɞ(55iʋ? Fǂ78橩^eL+D 6fVVu h[3;.ڙB|V`qCyPQi}#cMcyh.;nUUeD%B?:̮E4; wK)$} [8:z Kgmʴb͕'+i%uy ޚ.y+kʹ-E 5ٝT03hzxQԮ17.4Wi}-.OgZwMl1 *$ZXЮ1j (~SaG/jB֮aK ЬQ K/U":54'Oc=Q+Ѵ]QU_!RHDH!HZnՒb1,q ewԎMvkcvY?qb7:SaA6d֗Ƭ]MʁaR;I#mģY/gtmfq%hԪTF;]kʯ;}s#;qBtM ʹj&is23N@ ն4mB{l۫vY\L+2&Q}L@+ք,fZy:X AϧIxΥqԚrj=j`bYkU)&:*'5w# Y.fk*[.£iY,?nFK*̉;TYNi*j8ظr9[k$Zvj&һƦZ\Ns  NӉJjz?WjzeMeueEA=AjsYhZP:]ma׻Eeq}CZEʴV'+#mu#Q$pjB(h(RtIAIop%hq@ٜ#OD1cl̍*F]沪rrrsFtsQ)TA5U5afۉejWSYi=ƴ)TU6Jljm-NtӓYHo+g^T֕u[:; VfͽfG]/V=_mW= E(TȫZ {qEaFڛi<*ĨPvҮɽ{$j}X&*CFxSART ě!ڡ{T<8VB} ;^y"^ i?L!q4%HVFlYjXd#DѠoQmLFbTcR+,9^)+md>sUJ6C5+Tst!##"zbSXn6kiuai^;mԴޑSzf%ՖEmE5BͥYV +ˊ NWP{#ak7.l:&ݮ iN3OMƮk.7ol-e7{]n£o!P/5ﵺӝ Pp-4.,5'+0cHԪWRbmIMm߅6KkNDH1+Oc*j:0%5ضח,mZ3䡊,YE7ޖ:̝m3Yq!3#7E=ᜍJ¤8[eHVA; }x5L{;N>}FE"$iaTSn:%dNodDĪ,;Ӂm=EZ~XHodOGU-QXRqyr wkbZӖ؜Hq5IJT즵'6efn%Y;Dח1vrՠ%+1MQJ")O"yUܳys+n5q㨠&OQl5\jQ\|כñM ՇqMY_ԕ﫷1ׅ@ס4^q*ʓSj55^ʝ2u*Qʶ|${i*uo͞mWmqu?=uش{R*ȹps^Úd:%`F]?YǝiuWFA~uJ)^w.PPyY,JG3u[m5el;NJ)dQ;Mhڑ`QfgvWXM(kJiˍy..* p¤Ĩ6=ֽ:jUCKڶ5Av:MzK fYEO2'!ڑ&D}Coᑥvn._nWѤipZkyȫeUƪA%JNlҏu>tI%Om(>;b:v_)M9SPnQO^eMefEKՈuUvm9A"EjU6ؕP1@U[w|V]*3^wh$nf4oukam>j索((+1]iMu`D]̄JN:l" ތ;[j,0*e{svl$wS(b DJ^UϿʴiY>a7/`}XPhv[uYfGa^s_2-2Rm5@:Vô`^_FJaTaWa ^@khh/kš0*UJaU!Y dвr YQ{SS~5')V(ʘp\d%Kxk~upTeQYUEWaP.u(' ATS^5(:H5 rI쵪ئ)*d񘥕Ijx؍u9gb ZoskUPSե\]\y*=tt7 ܦ8KWTEr.)j>]UF``X7LG*+B&bUF9#hw6C(ªSws _NZmgNVTLf2VVit婷Zwܛ\x|Mrb`c?v+v)Guyk ~;? gN 2<&u9PqਊoqI]Lwj'$1]T/PIu ^`IyA-O9b4K q}3NJNC{hhD NMn1n$3tТgO|p_?$wس1" ;}nHgL$Ig4Ucζ;@[e!/=yXbPګ|]ܫKo麯)`zdў1&Ze"ș_,އ#C{9'lE}PU dSEzӗnٕ꟣UVeҁ@ R;bzYD*d~..H`T߻H`g)b(/Ct7vPfaCcAtVJ־rYxO;OpJx}/ffJ*\LdTdf,7%gSk:\ukZ #O8/)θ yFgJ#NF=UN~ct0 RZo[ܓCUsr3{Ʀ:)Lz}(}W؊ξn)Sv֦٦:XE)KrëJ \RM~J%FMbժgoH%ѫ(yZ2+ Ҡ9);z)ֵNSFO96[LXr8j&,(hSTg>}9}CWԅgv2A$m0'҂M ~_.vcZǖJ ~-vrw-NՕ7gxVE=OkԻ!Hxm﫵zgztߩR->/t8dl.V2<1Fc[?ȏ/dz^~4x+65ܟš{WƦq9[~Ut3>#)YNm;#.O>NssΌ[.4z*@uҾ,oב.<*P_8)ܔ漁IQG}79ȗ[mU# qY۾ȍ: yu:qʸ-h_ۦ5LuvX85kq^7y{$ی3Jz,jL^\' _/7%7f2)5r]AyG&Q.Mu,mFõ?"A݋{Ljmo hop.ĒXWn6 l\E?9V^]%z>'8z)4Q(6s!{X2i= 5_Zze:j?Q'v1EFIȭᷝ2حϲ,̅y]\dxyQL(<- ICk-a.MxA"do_&\c-^_ 8 ԃh64g0!":ʣgEkJ#Ʀ mfj<\E=AvGP=g=qc4`} J~nMS繻kF;{}G>Ҷn-u\)n&@mgtN'+ XS*WoVLTY9\/Ԍ'1R`eKw bw`EV'g "I+AUtzPdBB͘!0bCEd9! S|=,4ۂsdJةNGm2~fi7G_4P |ͮ[xlz;TjɉnD*UJސ(̵%ľuVwslRli㼸θn((u[&C>dm<:ٹ|a;I6B)oWoLm|@!]Tiw+(G֫|6F⸔ЀyY@5`㸽pPDk)nr %n~"A\ɥ_)JʞLJ"-S1رaQGUmeWYtzʤ(4øWͽ!BaZ_Cl Kv~ZqnE;^xvQ3e֦U췆!C/TaB(R A|B[vtSEZβ5NCLpW/g3o+t-C-)LK}(UZ*FƷ… eUqX qid}1Gƺ4i#<Š3{YmOIġU$RMw}7)+*2OJY]gX"٧΅b\v%wkVUn_ "U=Jْ{LfiO}&ѕDE]VguS%mc;J֚jK#{wDGޒ*JZZ e1{jt( } >gHϒN/|GfM6-#SiDC9(oTe7VX5J6JRL^5QkԉɌXӷ-^]1骈/اڵ.!r=f"l棙U-Zu}%|s. 4`ӟњ0cPRbcWAn-{EXM4BVS}E))*|Ou7k^3t45=zuJ8SPPP{#8Vd^lgkW_%ؙ-` $BHmq&Zb7qyؔ7mƲa`(0'dُ+(p^ /7;>óU>NsG쨳iƾkE8 r{&sW8EoҷUXhtIOR4\ۻ,Zl2E:[}ӱ:Hd/&6ܭی'RaΛNeY*fE ddZ&M79hkxK N@9̻vWOt"WfY.$ȫS^7:/(첊2-_ߣEcO[< yh}/-G&k@2PTLLƣИ|eza_n/I-a;*G Q~čo)P_Tj nDJ#绲 iv1Oi|{G?Ěd޳KUcc*קbç*4OQg[뢔w(ņTzj%+ː7iהkd 6>. `9pC5 ߉4PI׎F^ |J#ˣ @ zO\fi/?ZO[ŀ DI?ePKP3~¸1o՝[ 0b ǸbևapAc6JQL̚xϭ]g rg|t5x<{U:Gf7GZ" ;E<5S|2n<.FtX3dW`+^Di;KֶL/htǡLL(c}?aRbŃ_Za::5vt|6ݦSG?#> 3lIٞ}m8"F}Ֆem#jq3F/j ňMqdwo+lA|;Ǚ| %5*XN_ntMZXY,"v@z"hܓRx|ͱPh:dduf WstᲓj^)L\Z cc6GDN.9BleCJ( ^0k\XXhXб/X{c4VS|aYY_s+iXVQszĬ ['H pj۩EU}"Kgߙ+ciǶeq(  aOhyt? ;Y5'5F9Xlڕh88H0 _dE}l< s0 xӾ߈ٿTpb.vAnWݽ|[69F=4oqfn;]g &n]h[mT ꃉpC¢suxM '[|֩H9E V}I]p|#*(̠5 "LhiﲙtSHrUk&ꆕM+iT F9o*,F魋za=jwƿvQSY1g̶L5=x~xI=EB'߾Zn_=&:5~V#F w:YjN#y}-zNIۏF6hoo֮~yILחE؅ |Ii^}oQdwgQOJ\݁/9$5N^/zvPvbJlR XةY}NT}Sx3zWWlw)XLUST~ok=s[wY[wC@z[q!cH&G U^W/yQmNeAJ'|B tϠYƖ?_W7]YpЯP0g/Fa$ \72'ݞd?2 L] "d.0̚qZ=A^]?-/ Oܕ*N,;5/)i1(r_:+M[TbĚ6ue-- ~5@?rf>10__a֕v*:A[( HUJDfRNfBؙ ^KPfdfg\:"~Y2;pWU:h93ylj+qoc Rta0x_n{ʥ]oxw݅ү E^!Me+i]F.xmBg!L2AbyLC,б*i].JyJ\v\#[Sv&-K=dXZZ(H)Et4 ."KG֔+U/|Frq_eCn@dXCЦQJ)POz}cؓJFo':nk)[@s6umFڔud"iԻ':xʅo#^'TzAAʔCxz+9c0t[F`^{ﱣ tԑGFxIf-:IaT!k;CN9&)s lz 9iNy0{tɓpb&3 7$㻣!_1wgb,}NF>V>,ȃHMR8ɯQ0o^G6[({W0V ٛc,C?_{dTFWWAD 6ѥv@w { 6<):Y&/˰~C{3 Xjh/r,m; % yEV_!^{>&2phr]I{ιq#zoPC0fTs3X'6+΢䡦LEhxQ's\QG*}H>huhq=2 +mϥ2\kM9nLuĢ\ ѮM#E6iWT#h=g%2-zE.zbjHLo^B޶##[sQNjd8HR۪nE_LJ qiH?{Ki3[n.Yםb|Yڊ.iAtdZtb^J1k?IЩkRhȌ+1s*c1y;G^?kJonT 2+s,BW/$>9`.a)i9?O8{|~gYԕR14%bM[/{NKJKFq_QSM2ԨO7TxUy3Ƈ=aZ]'7kc|̸\yV,lI#N;.x:[Wn~ ЛVEf (?ĩ+R8r{ڵ"-tKXveR&KGԟ^ QAAwA@z$rvNkRCID]BiNYmÈ0-.A=7&Zɤ<)ἆr\Al8^U*h Dm2-ֈRaix]~(iD&dm{F~w'쁌I(kkZ%.*ٶXm-I4^Wx&*^u, j[r>J_-wPUӚT{m8ǖJ7% s'GEz3u5M* i̽; 4)iUKCUUF)=ߧ+I>FEEJ6Ͷ|zfRs[{l,sWӜC@:-ZV213]GоJCTef#[Af o^VmRD,l)|m\չ{KINW5CA{j"5݄$I ܦL _Ӗ5ZI)jdO{]Y9NxY(FCd/X&H!b%wh(,ujgҪjOEɡBu SU%yJlGr71L)1[-cɫJ(k[ZEF7)SQ) Y'~&Roǫd+::=do ,5x09W~B&rq&}jG~NZquI~FҟZq'YDCE%I.C#!qvN^K{ t!O/H"ꅎ@'cnKkJf}9"W7]ZSFIl]'$PQqHRe>[xOS(]:qu1.q7(u럞\Z+@&k\4]_"ׇ VjH$q~3ꈆ/AA%I,έկǟy#(~9I~|tC4q(Pe3evHwwV!ׄ%%):J)G+U"GKWxOw<1)xVcYNG''CĖx㍏v H56A R05Kmth)SX'F7 ?ajR"nbݵ<4ɐs#;BP:[.IX?۳@O4yA706=+RԘԧ4ӱQ#EO\]ɐarN ,shϘNK,itȾxCH>pa+A:`i]R ᔔ8: TۄH(WxЫg+"!IysQxvzz}l&lFO: Lĕ~1NVmh0pExZ㏮x$a:2hP z˞kB񊬠H,n_ _k'Sk= +^mSl=/_w4Mlmɱ ڐwZF&&-^a)3|k]Nz c|~5NӅF{>iϵ-LrE E(yfJ}HX{^OmcЧ//ӆeG7sxÍsf9̥ hKvY%=۰ǽ juº= :}qRעrߢ)U豯]]v\,iuRh"/*7jjf/}$6kqSgagϖ"Ws,b~ fi˰3 IEi[kCs& 2^W4*:eCpRf(=^k !i4bYjo]!ETeѥ^E]gu-ei7N_a'~Ti;G+j7_Ty?úl{Bͳ"}OsL8eݪ{TLJyN ֿ&y =toG]Tl&5( uUĢrv7I+ hҤקʩJiF*BTt6q.>o"z=d~y"NSObƾQb?)joc q+0j+OR.T ZG<,Z(wC[I9]rƇYťXag]*QSum8e/纋wދYvx1Q]_2%b!0 *-e>m/OF9!S5j+z%ib{gHi vVRk5^hQzʬsy"'2ttͪDPTƵ 2Oe⺤/vF&,Ͼ5=,6r֛NBn&#U=7m!EDoUC|a!oڸm9Bc\&2y 9@@Uڮs4͌fp_E]3KraAHa`1@Y<j,*d7> yj|6ALqM:Ş~'vXuUZ9|w!s=5[<|GW#E qFt9Te\fGhE?Ðv?"ݰk>+FSNՊ&H+ƈl vX5Ǽ*rD%|`H7#/?ܥr`υvn͡O B`뙠>t=fuH:`IR e9,X)b%[՝ #mn'!V`_ =T_Lt%SpぁQ1ovSqU%v.]WYx;ܥN]8ȼЇ!L:NR,4ʁlJ/Z, >^Xl;I]QfcPoP*4F&*Xb`%.Dm7~M-;fDm1#79 -!\-Ť `D Xwc]DGV#RzhNjY;na\Jh}7hdrlqYn4#3Ǹۀ8BS$E^qpXHGZ@Q + C*5/%ㅘCڵ+9tPmU{*]K;W$#m-;A {/C;lCn('FpK4B_2yGƲw?[+)%9;M@j_h8v|iˆJJq.3{zR0X|8`Zo62ޤpm$|pL/i&'̉{鍫ТهFNXKp ' 7~}4l^ w޲=I,qVO3r'9]#gefEH(;Β`X´lРV]QUqOseGUkNl G~N7ζ,[xkd]QtJY!%o=8<̿JT@06j裻YdJ68>5Z|N°DXfVOK.,IL#X#V^{̴ۖ1noUQONCS.1M_թB@1Q fzbQ:kTҩ#_՟%uXe-&]ٳ?%+dޚ~ģUGhc@j(;Z|R&?Qm,omT*E-nk9+ۨ&j$,ޛRHdM(-t`*J:*hnn3Qޒ{ ;ɛOU~^y]j?rAךS6lW *Jtb*Iė. ?i's*2/5 g̗0B*;+0H3UxN)7T .NqN/l0^ؽI\x(6RIH1߹A3j }]zѽ*<ܟEj_w":3oƌA QxxfQ5xRzc/61W66fcYñ1 EK&>ݭcvJSTVy{ PΝϚb6],rK_\ y0#Em~vp"9LVF|95ɦ~fb}ٛv 9"g'p ^@FQ %XeLFQ>IWx^R]O213N&Ǯ̼50ոX&:TuʨfcODĥi4\Mc3LӋKgDեuf=c3xObhkбݨ~:|Uh^^P#p\oS*_t=`o:)顟5"%6 ^۪ߖ^t68=$LGVoj5|sҭVskL_gZN\Vm6S8Kv+›kiLxpm88aK.t`^՘?XSe?K"|_L;# @/KauZ S~v3U, ! @s~9?CWX8m92]XKdֳN#xM(bL3lG")z9%|M[>{$v-a=J|xHQ4 Ē`TD)_y:& %I):i"Qtz7Ħ/FClKw /d7R b3gGh^{_4[Lx6,ͻlظ#XD(t5.EM=}48V-UdW.⤱/TSr&҆*N:UօS5ۏ/*d\ڥmZ_jj@=s*iV['d|N$ߢk$ǰR5-j֨c7tIt$[sWTeu{nUk%ٛK4 9`'(6=#U} ZQפ-jOY.d`2Ͱڤsc ;"Wo$urOmGeo{<чeA[JXMGCXēnYv'HHWl--JYMI=R}<7]'Yk,"8<#N: "z3 I~a.5mOlD :McQlO}Eb)0#a~Nps5詴eSĊ-be^6m_=}a&@H.NcPtWRa p5HoN*rԗl֊cD$쫨?uŭkjn' ڛHjo;}T\?lFMėFX7.?Ryt - 706eRX3WvBkh}름Qiv hʯXo Y өgoW=~gC I85{^O.6'q6ל2&q-+kBp-zXijjd).l!߭6רD 9S}l9"֨KKfe=4!b5>+U CA6kZ'IyDUXjhdԗ4kH~t4b<0SPMA.)^8N7c[JT"pZop<iSNaʣ:@3ŬzĝkUtpQj՘ZfP~vIxٕfŵHtKb/jrJt/ߥ]92NG§uV:uqcy]؀`  8n"l#G)w>3 ]7] w/v ŗ #LsW*8F:$,:+fnR-xg05jf#+/thR -3xXC;5cvn rKg3( 2OiPt-MrLu.K~e@< %>ynW+ItJf( ~7q91h BΘg;ޅ)V'flKp1j 7$Mb"]1]a_ )?qKr֔4|z:1Qe.߽@R#+0f>hS7Z| np.ρȓ}Pvʙl&^JWN2+~) q"٢1E9%] E {?L3,ֳbO02f ɀʃ&1- G{ԯ㭅8itTKC.=N o-}IO8{F>q‚+rwZ1h=QJtPƝȒT 4ZE/=䆝SjczNby`u6o PDӝhoahk.&^/Q0Etjܯ]Mdq "뾄Lg3\gZ <dv8 "mtU0TαSM֚sƩ~ +~ZZ<4kź./ֵ0FIhU©_H*WS!tj9x1zW6͕Uǘ` P撰>B4/\ RTǵh[Iܚ!&M\cVKm !Hc^b_彥uC*!jL,^A64#i.seRꡯ3o(AT(KM9^Y*(M-Q_\8XRdC-z7wng\\cvhϱ=Etrhk<%Id#B21 QW}hl\׾̫C:{yKVK*ڜ+PT2)r+ɺҲM&j[ZIQ6F"X84Hz- _NYW"PoX664ҴԜ jOM D%:U''?ߛ=9sg/I2^Q.iYz bgab7:T$h/BV&~ }73/>G{xŖVS2\qT,%Co5CoGZVmJT0*hS -ŮT"&JB!Чs|\tr![7r[ZnҭظmE?)] QuCbt6Wtbi~KKXYf{mBZJYvJ8s*0BS@ҧ~Ň|1V=VƬ)!<6[X4)1GγMA[NUEbbzfs(`P`)nAk^q[Ս8; pYD7('WH794.Rd#Z/PBݗ5rˊKzkLWĊ\pK2U?=lZQHYAyF%,K}C.ڽ9VasV/=eh}J>5hhxM3_QPZd[J-<-7m*$%y;5v5sxuh.m8a] =h?trMR ޮjB^vuRf ݉i5p˻s.+('ޠ>Vu@Qݺ7i<ð,W'>=yX=M^k A?ױ"̷a8pBdug6N? VtodpK50R$=*ѝ\w4 3-WטNO$OdP`c< Y  |X釗UndY7$S4'4۰UV+ǕfIu1wM:DrZ=:K|ŵz2FAK5]F~\w2%ֱc[%hbhAIQMS/݀%I>oqPڪN wLz:9i/ghO%Lwpf}Kzdo !k!L)>&8糫J^#ڑ7%]Od'Pu ͂( 7.$JFI4,-"H:>Op=4wcHTfr:29xgEa9kNrHOZ1+A9mJ4ӑ~}¬Ƕo?T c$x^+DvE N=GLͩT֕ﴺoIϤgmk/;r3Oa3gКZ=NML4zw9t]D9#_ʿwS8X^mU3[Mű:sNf5=k'ihv8 6*Vnv-xVf\)>E[|]6#IFnɏeȝ#@fܚ>ќO9*qypSYMJ]< HWmLsJwtcڟjۨ"p/G|IǓSE93 >Cdbٖ?B_`h_T;H[8lR쓗2ͮE]7^[g>g`-{ZM32t(wiN1<$*Va6u{-SoD #%&DhQ]#OCnv0tx>Tɸ,X#T*XoHm^~F) NW$Q_f L;TkCzd_{/܋H/80'ͼ(wbtrzIk6י7[{GB ~@2_y8Ds'FzqF2b7,M~q 8{SL~$ⵆVZ bWB'/QCg{1(= -d]"i&<4iq^qo(e:.i#:t~kڹcu"#Z@`&xkɐG?ic<^kJdNTR8,f{eI O 4v*H\/!pkigʢ`v Ƞq"EOe yE b(9tڼViZ#;h8FВ e3~=ݚlMfZW˘pp']_(㦫'm^l'ܶ|~bf/*|ihk5lqo,XoSkg`/Vn\Wr#rCoC3=W횝ש#]l毗]zޓ J Uog ~NVp~S?yF?LBv$Fuh3 ?!)#[6Uٚ5<c;0ƙֆ8T*u.n^X=ۜ4&NNi3d_&+Fu[#̍0q4wt.w?ٽ!&PTSbv :5%Lt=hHj>4"zKډ3zr Wڍp9VȩêEzԂnIy! zU792w5a?#RG\ ?oƲel72cݣ3,S N~jYx@m!~Gq )E;lBPGnS.Š|\Q<^5M3]Ɯ=b?.P9(n/,doB>ik+>Ͽ{BC=91ûVt~X~Y KV7о XF ً)[Ƀԗq餚^{YخX- 2YmQq[=w?Ekk!A.pM 솑+yl>= ZBQYEɃ o2&K3~_#jj-;lom'egFP$Kl~MqՊA+G8,Et7 >~j7]Uu_Swð1g jkrw+ WE'SiX)f2猹/꼶6Ϋ*J  Df)w$Hvm[|Ow[cǧb5|i$SNb+ ~9yLsSIpdt5hhIN+*/Bl\79K jV# }馺ڥ.b- {Q.1S`Z0]wDOӔj.."3.=*2:*Ga4Z #.Jv.rQb-ZIc&">œ.>{dҎO. }B z%z,j Jj෤:DlV8YyC_VWA]PU-VUE~iܥ+56ZjY:kژ5-LylXab}LH|A&4wT4cq>c X VN_HO5b q#4+Q1>:ãXnB7}-P}ۂLM3hCl%Q!>UܮaI`b-/kg 7\emO"HNH'*9\,'P`'bOV '$Ҷ) v嗱R(-i(;(ZD27q2‚9)w?ybFG+e\mbo%:v7  ђk۹̈́=씫U_+0jve?T +=tϤZZzLðB_7hEp#NR-Kr83gf};fVפ>+jI7,e,|r5Z(҇oaT6nDjtXWep򔖑-:Įyrڨe-%"vt85L{n7 #h|Bˢ~-rCog&˫K>-tR?F%\6a{/kR)\mQM _ J]pZbI>V-p- ׸g q?h55HI㜵P*(MSh)6B} yPU5ftR19cw?}]Eh 5ojU./)utʛW),Sx7duKFIѩgy{źnUg|={(>f6?mdl\IѴґQ\D%>L}Gi@_gW8Izt'T eXu F {t^YiRwl97 ʛk|y9~IµQt5oM|YϥQ>LsH `u3 (7?C_&7q?yZ6IM?7W@=B>ܾ.)x*ɔH33MA,=iǬep%ؘM |krYMlSHZujDsG'b ;^4"Y0ޣ>V>9[$//Ӳٔ=P>]#)z?z(*6Y(Q!wPf;;s/2/{hd;-{][4BɇcrS֬~tY Z%-4t\Ak*pf¦pu+f*dKT3ôWnIqMX*+S:G A{uHvQ&x$lBo,j3gitĺ aF1:TyV g'4w!7 P]Sط+w8TW=1ay5}fo\[ڗHA&7lojm!)ڐɳEmגKK1xAVe/P*O4n>|3kƞZ!]olzGQE-ac}LQ!7ZyU#2tyqcV!6N]R-5YܟC+gO_`LxI76j9(7rh:/vi( uo? ZK3jh>_hHɉ@S'ShXIE0LfCt$N[('fi?;..<AĈ*5V^b ҜG͈U{q/Wb'c\E*A*h bFL?D57p`PإS>|Xvsαf]eI\';mN>ҖckzATv!'cz;`Osr/9ѧm%c?DdQXl+Չ;Zr73oٮ^Uj1H]mҪ1N&&MUNJ)zB7R-eXuI9=Y})JK¦IyW?չ#:VA]|S`GڜqLXf"*kj϶"0j3iol!R=wZNr4-hW| 7s>(]UE$O]H/(uMZ=w⥣ү|2Z~ƙLUrd: c.WͪɬsK8JJwkcFin&ŧjQZÕ>5M.Im3zv_CTWpQ= Ʈ-4M//&5#{-N Lࡰjd¼,oifqI,5UM2k;T?*V yDѦs">>ǃbu ">$PQH*pi,IZM8ނXfɆ$Wf=V[_JQ'T[1h;`cѾ. ]_ֵiUYsi^qp{j8*f/_:%} Z5}IQGdBQsh\I3XSjYsUTnݥY "suлEsNjx,o3*jl&^5G7Jx{= ̊'L UTmJ5ĸ_&ʖo>&oWZBtڲcIOy#' A;lvEbT9hZ5L*M34;,_~=>pJ+"&d+ BE`\TիY\L^+Eu$!d]5YBa=7$)$mLVGۦf]]M_F6u0?8ՙ$'E3GWܗhdS_ۯ-+붌F--B|#c>&ZZᬏRSZ6>7| h=D6{MrتIg}N]G)dB[ գEVNn%5_ڄCf[f*lGzX~F+&fQHn5i")k[~l7_|^|")FHE|+WJlrY |7dƒ$iN{Ox/T"߷uK*q+'.3YDM9Kq{Y$:c.o)dpeYcC=%i.*m'ߞQrYTL @6 MJ2ͳkͻ<ނ-J mL}H^l X w f=UK*5qK I\?y>I 3JlՃ)-XE|O tf kz-xj// AFiU]:[W}V;^ԣ~%{ ?vӴ9޾$pҍ8v9a8Qo3xx վXy'˯ONc2oL| B*Y節G/%/ڥ[x'MIO/IsR X*r.;6(ͩQ60:%+nʺ4m %#Z^uI#[ﺪ>x*Ml(ՎqqI>o꓄+JLX%ЍMRzڠtkQ,wTcRWbhw塯:`ֻ+A`Rr~]GC1~vőehU(\)4JD>f ̽J떧WV=ȡ(1 J E:s},%k-MNmgXm@pQb⤝zItU!gPMS1x'=zѣɛJ-2k*Rmsz9HnIlgƷ"PS4:5%A"<$gv^8zjRk^$ՒK *ANk>=-6+1dr$*DTxt|2؈^8 t¾#i%B}/]ym.J';dӾ1'oIWceABumzih)b} -t*#\_LzOmDӺr5빟u8ʳGd6C_jlBR^ݫNq%#a_JUCF<_Jӭ|^GY֩,%N:־8*:V(2OlzN)4R~U f}j#b5 1 %+It +mT4sG!fFh#uM$,hǺ}hClcEmq} ;¡n.|^*a_;~մC~.+x%6[BJGW !A[e!`86=+ANඃ| MCpvr7%ꃱyw{fRFT^OM{.{ӑ3U1/d7( +V 2lF=VPTWQi S=tI:^t%3^d4tኸVmj]n};N5^IjKPTMF`O=j_ r_ڔN$&v[ٯ$E q#^g_Ӏ~Rj]S˯jʎԱ̹-p0Xªt1$+MFra{=bs>l$ٝ9 򏒻hC7BK:U(LMA31HUNk?h΍LЈ χzn OŤۙ5]ѷyŋ=*Q83*8^nM&WHU^T UP}Yt+Lܒ#MHő2*CɖbaǸ mYrf\3dpuHMzI0*]MLP&=%JB<<~ MҴh !ě[`uʖv ^G24w/^!(mIU^3LJV~[4CR_t#zu؎+t;j#cRU(m5uGTpqQk@7jw,LCОpz%gtX~yއ&:=xT+v_ZH7d 'U7n<`6S[@>e.&C(w( HeM )z88V;m [˭>zat¥HpeS'bUk 64U"7J1м9"bN{RwkWl{ͧ=A楳#8k+7ͳ]9\9ha|lx 6cxr>5&_GP,?ٕEI5z{c%ҝ`[hpEko_Fm= F}Q& zg:pA\uqX)yxNUYR4j<^'qE{(@+4nyqL83-oƚGO[>NB`؈\GTƙM<5DyrUUj~ڪn5o/33; OMD}:}M8S|g=]Er3bǟXKv$r%k Gk=k랡׭Ou9+B oO6Cp"&,'5 UE4=Vfh^q GNyL7psG[OeWmNr7`n]YS׵kJw.=wgt?*UMϭE3a^̨RP}fG( p̸\s;v{l'P-=ϙ ֱTC#hvg__I}G_f+zag**z5RfS2vSwѤ2wR]uVT]{NY%z/;q9MbQƙL^,h+,y=CA._Q-ߏs#Uה{~v)]1 d-*ݸJu.o&Iyw(tck*'ϵ.;Z1yٟM$CxIF}# J`QyK*la|B2J6bgq&aQD)W7@~KQ*5kt-Z 3-ȥc)屎TZlpՙ(7L{xX6#WTȡ83@gMc ~WZCCiUEeJEw^ѤylcJ["o/5Һnnf XEKf_3 ŲEP*h0-ktt)Hw;L:ZŷY Wd75lМ=X繏NpٷIL W6e{TX$VVve-}d;럋X%eSrFP{N#c_+~;a-|4j51JoSIe*6rP5v)EB{4k|k6R _©/Tac}}ڸf~N5NG?vIm10#WF mO7w=umY,B]?󠮽!}}W~5JTc1k>b E~SsM.d8TܽRѽ^geQ/aDN)EaZVFٚIF7sE{YC6&ӼF:`woq7׾Gq/9zHkPahN2'L܅R& E2Jk__|%Sm >~om|J|.ܸnS\[% _RN__d gmA|jkоfy&=^&kN6'έ.ӚߋoiAi5!w:-!9u %>z_wh~ S#Ǻr5j_\[z/ixa{z^ԓOz]+I+otPҿՔzrl7'KB?cE~B&2,Rtbr[^ gLJpӛ'b!*_\kN-4Cd*B53CRj8u GzoE">d}*04QDhCTjA-OoE;fҡʮ3K'koSIC~F]Xl <\mkjt3=@N]Cb[ʜ?#N3G B"B~I5/2'fŃ{`ɇVX| j]LNkj*=zs)΂j-缤vTpUMeM_Z+BjiI궚=Nž*|,hr_11oE7Mv[&K}J mitQ/] Ue4kZޕ+ui*VgyAdgiD/јULHNLjW7=q+H?P_g/I>n?Co}cǸޭt c s6: }Ӳ$2g}[w,LWK(f8wod` ket8:Boba ~t  Dr˯g \T v<%o.R1ӕi3hǯ9IрO'/\tLI-#P_rGp(J!ŷ _մ ɲɣkbku4鸗DK>x5-fǰXB-HZdRYބn^G+Od9 qY[CPnWGЯRwxsc3Mǿ?5=0X@ަY^ᩮE!P6Nx|<%59Z#ԙx:=6`Q9>V 5m.9SnFgcR$'m6dH rC9؁aO瓗:乘}q U#U]ʬ65=A ؟>ˬL՟hoOrz(/L DwhE~c-Aڥ>㤻o#niZί e9Ar%s[#c;&թJ}Ό`Fp*%K8nt4}ޜ/Ed>DKLe/Z/lؘ. Mӧ\0gM1ɲ nWqV,uol[;lpȃ^Qs M9(KZm(b3֓E CbM .vM植~{V%7ZyX;sn:ғTTצ_pЙ] Au eh{:`k6n ?v饲R +::;^?k4s(jtW7`I30K_hضY4V#KGF ͞tP5gQ<`@Q_Z'9R>4}̲5) \tA΄GbEg#˦.bk&Ɖ YsۥyYQK&zˠV|Q05A+"DDP5b)`2Gs/Y"YwgѲ"X`یg31*𴖁we6 {I;N>4^ƲI-j%O/ S5(M[tjaT Imh˸%G$cB^R7yu@_sNT&H;(0Au,cp9z58W/;=j'qMU0Mq4QZ6^*k߱,&,[ ?oȳ*|Di ?+8\YH5Z9aY&TVG"RC@hFy 9=TFŌN(2鷔c~G:'qPvlΡbZb <7cf:!/K-xbdmߪ`'M2X#r&sArKfl_+6UC2TWjv~"p^lX6)=U_q'>iUzʒ1Ȝqb`~k|Ùu]" H_sO}OzɅU'I<}sX.j`Pk}О,Tu]=Um$ ;ms1/2]ZMoٸ=l[1VkĪ'BxnS_䟹lO|6i}~V7Q;(qlxYwqpvg3Z 1.簢;ɓ оu||zRN9Yjr8BI:Ρx"0I$/YLQhm-);SNy ¤B}ܔ3x=]>Yi[,V5FRr2OɢUVR.X GZհ!3FvDXcfJ⪮8h*ݬݟf40;Z #t=t : S3.gq&Ndy{ϝ~HT&q?ۢݢdk gD5YO7t(R (Bgֲtv9y 5OCfؐyjZ`Uup6?Eyj^%[R7)Xy0) wCA%*)rN8`hTߟl#cT_UV&s{{j%}%m+K6Us?"2RX?n2݃Ik9Aͻ2/DOA0DT7r }xI`) $C9omvҝꅇA棾EVMn[x H^顉&8٪JL\?E+.cbwM/W[x1sJC(N;.JqAO5cgq4i9ZXh ^nT cH9d3A;yrrAښ/tof>X75@Z8r;."Ucl@тϨNűj&#gVt=;aVbeF&2,sЖQ?%U+Btԣ6*)h/X~SR2h^_[m2o+QZBN2.}iޓlm>7{k%jDzgTx!:{KQ찶\ۭ39"EG]__n>ncvX?h#&_>翕zM suxQPYP]&T0"#wSv(+Ld&\~ ,d}GiT]9ih_FӸOo\'mRϫ!Y6Q=갏,O=[?G9ap|홙hUtsRx=+L{JJ?w6bpվOzL!S'~o?rŝ^Է+I%c>fUaSLcQ0>8gڷ[\ܸ+_f)9LWl/*B뼦wWׁUދY|e{nV;,҆.XנWԗWZD_ˤ%Y5Jh%+jzE_yO{;J3c\arhu-UB#6m\PDzµ9{Qv xضvKQ]k;#?xc }3DnWw.q|@i$p8Q?{Ae='ۧ6tfw|{1|uٕtTˌPD6m˞Ot+iJ@U9yZe5[HO1ɧmr&%rkIl?ڱ.Iv[ "ifȖIc(ѼR* QŭhL^M]e`_byYy.$t]9g(J9(%-* =3A9Ltc`ǽB% OC]RjAüt7Խ6%Se.Qj ||"u@r[ǖ) ̸_j?w6 yUG^C%Uy_^* ǿOuJp>'9QϠ*sH^ L\D睶]vUۖM3ZXq`lr9x'K;+T!w!󞼳 *"1ሁtTvufK4S&D~(D#&>;c ܀VVX?Y_*iuO y<GϙC.lTz-!U-P81%_`K6뗤Poj]ه0i`7/1ۂýsTڢ6VN^nݴe?G|*k\8#>wإʏ;>0~_U=][ rR4X_D[sSp>0('mq0`ZVΘLɮxmݻORl?!*e܄%͎ lf1u'韢=v-i&eyzx\\6Ǡ|=3zY!Suv8^ .9aQՇ2˯~>B[t]ʚ{ǹ0@)48!\Icp:R]ojF#^wI a6)#o>}֮(##0B1e *8+͛i Ōmܳ=ނi:al{V֦F^)6Hg{xYŇ4]+%Gfb7I"3L!u`̉~ax✘n1WD"ffy\;7yT;PLDaa @FE7ihWĬ8jv/?{4>&c:&(f 6GFDb]|OE5EKP,cלzS2-^|Jr7۫Fw"q0- ~c#\ؿ!MR02J 1QoKe7* o\VM+$^2e2&LČM?]~ +@i=bPEنT%bDP|w;?G|fQO^'FteC+8i>u)3sLԜ{ j#ZXuHO߲o4,8Ϳ3:1i.CBl k,0]r(a&y&i5hANI7麉)fL>H_-߮ū:MHZK416[R֩z9X=~O>#BwD#Rُ5,{Mj0?9jahK[ԫj@pB a4$Ȳ |tNaC6Oo:dō8ǔb^sNe2mҡ$xVN(`8K A(OHmM58#qFS0y tWx*=0d>l-NjE`U^ϧ]u/7]u3hۜEYc)pI0Pt.̵Ia:Jݵ3QQ^C ޳༅1G~k{H]t2/N~γiF-;^gTǨloZ rȴJᆬ/hM"޺lmZަP${c)i=`#%T1-\#cD7GA2o~\=*ގ[y m~CNg8-0:}涻s56hiS9t <2N4y[:i4n,ܤtm@^!ׯ\2gOf`Krq]?s?c3֛O Ҫa(m#ͨm//vG15+HuVAO6Ҏ ttYzo+Y;{ܵ& h\`mE&d2"ʴw֖ L\o:gQ#aֺBN%̓*%ۆTdaQm6X%?zf}_C?k?~MaT -񕛥oT}Psէo*1:22%J@O2e%$(+Wy?S-er ; 6:zd{iЭ&yEPq673&8%p$B)\$3$-,զ >$Il e#Wjr#r Ç1csqVOSЛ든L!#ӖѫJdDmY[G'(?l i^ժɰ͒%s`9!*~s>cxQ`tn72 F՛_(>Uz&]X#_ /i޾!o=;zcN'6ǫZm XAxmuh`\@:U-W׷PCk1B*g/;P;~s9 q[\[矣<2iYrhV-蠲y .*rβLH*:~K+|@H]=CNe ˤA?f7رkD[߹$@y2ase _즫AͅٺZQkPresΔ$;2.|,,@d=DqtHʮ^Fe͵Wq3܋LFlyY_”!=8/4NaiD[>7py9cArGMMܡ s_sJXN'q73*.,>%!Ps#;\^O+ci4 ׷nӞj,j)xShĮ]!U5v/s%ezӡlt63g:'t;>uUEfݶDZc8~yh?s&SiynI;Wr2Pmͪ"nΗq"D0 VQ=>T F~ 5dx b]eU^:Vx󵆂kwLg}VCkZpS͓3u9`VMUSMAW;Ɏ\;gcѳ& }/p."* 13]xvݶܲљcrg1mm,4jlhlNjCMMWPE-E`*pշ+HpKp}~_L;zԽomK. >9iZi&r8vz.GRIt,a3%rӷ ?ߺ^:w~;PK='Miˋ}e9 u,rKFȊ|豜}Za2kŋP-2VRet+i (xćpʅ)QFj)IфG~1ȔR )fȤ^mEP圕Ji7-dR?hCA=\ӗuM4ȥ4!",lL mT8^ )TRZM*-åXLf8bb'6%=Bxw1P0Cc/8HL9 c7"=^%@ kiRMTcS"*wǾ\x_t~~(-)Ҕņ-"/%Z̅ªSFaBzT6+N TJtu1;]?Z|d qЮW]&x*jzFT!:T! ݘo:t/2:mZk[V5K>la5oNkܺ=~bEp(8페Ax YKѫ?IG:Ar9\VݕBp>#StZ(H~d&EsZ+ >:L!xɹ4yZg9kÖ\J|;uL9 E뎂hVG Q&1"g~ZTK qRmG%!Wo"YRh}F6{%tpxq?)j Ъ.uUʒN8c0>rA!GJߦ/갲u"VNZӐGDsG0Z F7^Cs=wwƜm&ETЎO5:˰+-3MH4 Ҷ`re]Fzsepz00An'P/08Tʴ|ANFbˊ%#x\{Df+ 6ŸX}56{GR*!CLS֞0o(_^jz,2MsЂ/yǔ-l#,8y4:B 8X(OݢEf|q8Db0DExO21 $H׺}ju?b8hz򅟚v݅r՗թ۠878QDM)oM |nyߧZc!W[KQf2GԍD*qΈ_x+pgu8pj "є3X~)S[N\&%DYOyJd/@7ȗ [s]jH9 aw̓m o]Mw\L(F_2P+7:K)4^-mEd[[0` .(.LmUqzSR'(iWO@Ji|X_oD1Nt`:`t bG^SA "T .(l};$_8Cd#3G21bM![53>!j挐qn76AJ%b#AXC \5- SEt4[:(B1ԏӡel8/?zr_}&-(*|w~o\ UEi=L h3fP-ˬNHOHV96Yל}S/_\L#8+YB ^ɐڨA6 Y̞lzUp2EW{.5EI':[%dzh|kcd4ۍi2+!S'1SG'ZUЛLTҚ]ۘ1EQ%M%uRSx 8%A #1,E"<$% R[ 2h1K]&(VuYf[O)9T5iqK#BTбʭ1*TR?zG6XPfE7Mvh9$JQ)~*0eJZUB#sJQBlɤ¶BkNIGL]i4HDkH脺RM. АD(%T.!)Uq/% f)֮&LM:4*rk7@t3VxV']}f@p>|Ky ' x'RaX;(fʍ.4*ɼ.~Ĝ'L`䄅BlOm`/="/H0"{C+a<*ԏXr::px=AΌ2@ uR{x\$#ŧBdA\CtV q yq]X-RaFlf^#wiGko8igTڵ#* rL,Ʃf}&:FI_qpuN!9K._8{Q(54+<4KMNMK( CD& p85G[D x AU3 O$&GB6^`#bzUzX3Rn(>H7DŽ@=4yz4]"CͯB@C08YLFK% E_xA^EvP#cY6aϾkPp&F+G-߰lXXCT>oS\PuՎn=G(x{ڜ`zUV_g 3B-rQZqOgM02[_ŘԼFE7NCo}Kx}o%_0N [}@ַIcp7"/b|M7F!s ÞG(uE\ >6?+QG)Qs!c$g]:2 6Mȿ>ȦviIYJvSK Vև޶@``ouV{v[v&~L^zy} D|#]qfB_pv$@xy߂x1nK[e߽{Q4eM1*y2 uq\}GIѯlY'jw!8̋;\渳k^+HǒHM T ~A{=+j}!IJUCrWg}V[Dי7CH4W1͘auddt. G%Y)7Or9IIlɅ#6G]~{8(.zDIU؍`z`r1F"e)>{hq v\p[ CI-2q.f6/A|HGNJsTj]ÍGS[@v5]cV.!wEFqF988ILhMݖzV)ð)@·E59NU񑱑?mz[^ZoA)iVap _̉Z:2>+*\qy9 >pF+21s9|D sį4a@? O ~8Fr&_k=#Zʒ_39[AΥN7a\'yD;0.$a  )^="ww]B;}ܦ dGJ:y9x>擳-,kEoFu+B F(; ' 8;vER,K4Xɝh\kį0FB7 b wɪoY& :U|tErLq8iS./?c;n]x|"˵-ڟCg0W[*U_ry `ح2MU}x뜍WK}IH?۾ՍSD5 eQ㨫c]G>-*zO=6SY0J1~Wɬz3Z&m52pKC0xDE~y*;rZADPVpf)VUP}Ɣ۰ ~uȴ)1+"=)^ťtګʻ])߲aHKh>hkH3L(K5TBhJI3?#cfN"ge?}W?Y(7WVmaE(G"Y%**--ZK,VQ=h4.ow%#3-nX]++1ڕcV05OԸ{*8 x^ L}.B?78k 2g>ZJi[d6ʛ'@ҏy%2o qZ|E!BKS804<^iTpf ᬴:;G`yH{ gٍ*]@S: ]zڳ 9 \+j] Ʃyr<-]淩zն%$ ?S23ofti v1E%"a$R f(͑ZN/vʮC՗g]XwS諅㧈D!F,Q(P.F)CֶP9|v~4erμwrJ-Ɋ(R' _)z)k0,V,^F81hN>- *GYQv|,\xY!&w)peJI}'IS wwGNi|/*Au$n踿'VbU糘i[[CozG@2D{O +ŏa#`EkV1?Ú =aV_3)"L * (G^)ޝKmsi+A°!*Ie;oo~;k|Q4>Ƌz/C,qÝ( tJ나|)@ ^E#eහ|Gy"fG(4e3C@gHPU}t3 ^ a [wi{a_va*Lx|e!  MRh40Ce@^Ko?SBGaX>H;/´[ X_"[ts:*FWEE~ <؂4Ů(9 `522QvdU WvX~G>\Z&bdDugeC q= "hGH%YP.KHx ]ԡ'P9'8np떺^3Ҧ@w}y 8 8RwhÓI<{CqBA<ځL\~-A[Rd#~P'  %'Cԕ*R}\fBëJ;=#)$uJ[-O֗ Q&*U4]kuo||BcB(2P;-d5*Y^)G'uTqPCqBJCsr~ Btq=JjT{G>^B?t[GƎ?95Ն;$6}j}RþTAaSHfPFr:tqB,;]9FT^?UiBJ yɲ ptp?=sOYJ M'7e ȱ9T^kZ𠡢n_;FU2{܌¨n cr#1͏⾻`Qb!Su]Bs7àu;VdWbjU;EǢςȆ9Kv!,WJ\+ ݛ0k{IL~ Kڣ]Ώ70k$xYD^U">&rڣiC:(OC*ކ%@v`8N…F=LV,I cͻ18l `-HbD|K ]2R yP"tG,~ߴ~ňY+'i.绱P=t!h⋦5 Sgq0*zO)C_jI :cuͷKuQݞoŒ: I=]ZBc~ڟSpYG޾R@v/t+C|O(=_L?\(r*@F]QƄ onV#o?1M =7="گfgA hh!)*`Z_43.:X +m+,Zuonߣmk{IpƝOB z,P 4*J`9iĐƅ6!m(ckOk:jϪQkښ=e"iH'osjқ C8~{}F[vvIVTX=!2xZdKSROYMIdS5Bl4bd6*r3(AP~Cw*ִ Y&D68i>/)>F2ItSgp1Gi 5C>a[)vEQ8O6wQInkXՂ*Q/{HJ%qTus^M6:"r1]Ka~ j&}8ďp 䆌(g>-%g'NrB&âS,I6O4̛Q4CZٗʿT*;aӥ}uFes}ȵv[6=O-y5SҸ!DpwYj!斕tɆ0]@ (݄I7MUvEM!EU䏦]wkԼ]fvL(x%w4ն\ngT@n9"ppr0C'[\HD HLp]3w[{˹} F4=d{S8ެ?:ֆX"nxsV60G7<2 1s<)HbUr;,C2/5!QzN_Ht_!~Ƿ 1,90Iد%| 2=p^SJkEܴ {(:b9;O{ʧ| $.U_ь9_9v# ]u[!zJK!XB0F lV&@$*>0bL`b`aSB/{tO"M> h-3E0*ar.D/hڤTUuHRS4{w{\8JJgp|a\m AŬG_QrrD~@PzR $ď L7~>82w5>MݟÞ* 1T2+ƴYeش-NS"^ֹ_ܦ:b6:ΛJYۤ$L<)0 j++vhl/>Z:p@GL=4Q(˶`#ݞ'g O&ե3i=dn\]$_3}g,!_[j4;ϹخǾ1^Li<}p+xs}DԔbdDaB61x)Q25Wcp|V|z!FԉVĶ412clFl anSD'q0~[_;3%!9O9k(O@~z/=JV]&1K )F>1*A؜; a F 񤱷@v>"H=u%l1aDrXOC5&S@yGWygCJ8 mT}@jG!`KLsB)vŒ+9u[W ûc,yw[էmeҷ4jSH1+.9a)mW!+ M[Cqҋ+I$Uw/Ҭt+;]y2G˪ejI=7ςK*YV__ŴLMG9H;/t8_  PȩYw͛A۽EϻWR_Gg>ˀo ͫG:Ojl#t k4bvV4s8n`U*(D@,xIyYyOp)qޱ#P^%DhCrѣAQJ~YZAs"Գmbڼ&^\tQSUvמ~I;lrV 1H\}M uOx3T/r^twٗkQ]&m,;k3t6__zV}m:ʚZpZqP1^bm<5!MP%Ov~O ]ԲNM*{HCUXDܤx?Y\F%OF B-3,LD%F{v’^ oW s FJE)}iWtrQS%(ܫ#E4,{[~Zfvis\i+I9,F{z-0OPq?|ESVOVS)(,/1e#Umr6I1Ey.rSf:MLm/]Xi٢V;Fp^{gw}"9Ae15\x|Xz;yfkUKtÿ(ZFne9akT5dx*sd4;MxH1 ,+"C!Ol뼃Ѷ?çFOj vł'm p̼#@͟+u[lQHcBc7Kr!$`P$w G&V):v_ʈlIqAs~+7ۗ"[4;n%VWEV hά6BOOm,$Fut jٕ#=a9]hbpb[bAX.F]P%`' 6)j"x3W2.J%#,{Ej2ٹ`j.bH `Pf6|C!5`RAD9q7x!/kf7urf|nN),Q~% 9"dz8>Ħ<&)ΕvGD׶V/ S (Yz9-pC4k ؕ ;0rv<ٛ7 ͑O.0FAY)a"BCP[=M`e< ?kTN MixuG95YwG 0܁]л_-RoR6{i+: ȑ M[؏ 9|4Fmi2 Ǥ*G{TUQ9\zt;f[6>b.bW8~u0̐!`kL˲b D8).k8`r?VG1U߱9b 6.| .{yIaXYtG|bZmt#67PAt:_!^zÿcW8 2BTGWɃã^*jQ4jEYm0 l84tA\So;mܵ=ց_c&cOQTtܟj; t< ] |#o6M݃B]$rHߢLFjS eEkY+Փ~f뤍 xB2Y܈^+FK"p =KҶ-0 o%4)ŞFO(YL5DByz<~"]03(A^$Bu~f./CNKsdr2\ 8uĵMfA WٶæW=B'hya eM/9?A)Gv"D P[pb| hq 1A0V)o8EL$Nas/uI߳B`ȍ Dd/NljԄW~՚72TҺS@Sk<),n/ _+ ctzӠaFh j&pviWtH/H&"̙/wEZ;i\Fz%VZȗU宯]H~v}| n֟*USW@{C.%($ߊǧ*Ax6ƻ!l±{K$UDW%h/IX[ߵ ;Z1`'vf,4w_`lz~=Ԟg#SzU+Bӧ8Ϋj>Gʫ Rp$7mu!^WU1Ro|22!oǿ?ID$ʞ4.)zU"ĻjʫF)c:It$M[EW{( x⼝_ GZt7I>*X- DTYibrLYJAFg*cjy苊'ZW^^G/$kS]b%U5G2vٕ__m]%9gj+Ca=r R.4z黶#_3O)9ʗp*Ӛ0[w0هa<يO~.}x4oa3ͼSk 4f`Nti>VP?y %VW;w=Yq[·F]MKdUf9Vֵx)jJvhpgS*6c@ghT #_q?(cYPրSCC +F4er$+]w7 ՎdBƥ4+QXQ ޞ>bھtL]]߯ i)t|a}^~R6词RL׼%)x]çqvy v>̨&7xD3uІ1*"3H*4h B4qn-9hچ! #}#umX]x_-yX+Цb^5P=Jyw1]0ot U,{lȠ=W pi7d@&e{aat"!tHㅯh^7Gۊ50k*(|~E6;A9A0-9ƌ.q'v|嘣P 9BLh6s' Эes\ Ci4Ӯ]^Q v qc(5CӚ_x񾆧 RPV A`R77WY)uxڄ \qㅸQ2)U|WZ2sb}FϽWyOӶ)S0?R%<}WǍ`e.K@S%:lC` :kO:-E.nƶIYQ*ሞ߬IQyu>OZU{; V~7%ɱȯU# ![4Q)8^ɤ iR>_~,ϭUtZdCju)YO  ElfVe LkW:,3X6cNw/3?bݭ.;fпOJ_&W(F(9R^cWcY5]]gds"0NWXкfNJs: >@V]\]2N1?~]b{J^"Z.k!Ÿk>e"h㮨_}_ZmMR…lԧMdh3]Nl qdSO/M ~Qҿf- Q4!F AD{}/1=!Gs_eߣįZx5XQR)}OW;>McM?+zxc,W8O)ϖm~כV:cOj֥f PˮMH<}9T*2~Fb&iiqg[z:EN.I6Xh,Saq){-23dPp,n:tL Qbڟ`}yyAYJ;Oluv6>z3{G5nSXt1) $~B>ݷv1 ֳţgNQTGմKRDdQ+`m3-H^V!"doӉ /qk$ǫB+y+CgGWf,O8նޫR$8y6]Xm[ZuS<ɍC"|T^IN_c>nP{.n"su]BfK05/Q KnKМN;Ȼd&[ hB15t[Lc\~D %U24ݝ8e 538Wmq/^>vqۋf)w K=/Ue!Zs6^Kê;3f[zK^S=t.MԷ}[TZ)$6W.7")IFE)Ҏ76F& &uY:,SVGeXuwъ5Nڀ,/!>|+1H+Nzhݕp&{"Xb1v篫M#4_)Oe&E ? bţ코F?6UM_ SN ?u}n/L{ov/k2~o^t[ip>TXhþKyueYձLxūusAH z6KO_ePx5lN+VnvOUG߿Šʢn-<=|;bQsRʾS?1yd>]GJӮ Jr^)!߲bKFAhZ خ;,^dO=?u5ZE~} þC[wT7꾗^S2-^lrn*jS1_ݗ}OsY9/sߟ6蝣 Z2NȿF'[A0ϑr}jj<"14M84]/__sP"d0/ ؙ'uY)9]z߶N2[D [SfO$Ȯ+n#ͻҼ\la s9oKqgϋV/*{rH+D/Hq4I Ixju^| TPD|Y-h`63DZ(,>ȿIvfm.չ-+ӄ׭8ZEu[RU3 wn 6qlLD7Hhׂ!>'*=ccwWY^|B14ykʛT-$ƝTjҬDt&0EhVܟ4N|bVmL7Sn?msC]\^YhTWG&Xwl'e5M=ZM@)5W53cwMߛ㱋s}]m%}Y&=|E$ eKBj1es2Vg֧-n\fŦb)9W JU<` U\Rdyj3s[ A.*RzRށe|fIJ@?EjV *?v MQHyTfi4i%e9TX։cU뷋j*/MR\ CLT1kKRF?QT+"PfoSMlΆnNޕQ5LMIFtS:-cK2ɫ 5+<]~SiťCW}mKh\ڕp%]Pd]gI,lW=a'B|1c*Ϲj;0 bﱅjǎq?飰2l0oy,E!Jq(ް⭌_T'¬aPD"vz9;0,QJiF^`ZUiè"fѡJKn일F:+zo0 dSzTTF4}{aʹkgڗKGd5S/eX(Y,r˧)H-3/ZUYwgՑl%A8W;CQTq5>}y5D7rWuI͒5FRU 79p`tZjj)]堂ʪm[G^;Ciq3O>Y=p_62mM_٦~+?eZݜ3_ $| 9)ڂ,&mLfP 8!vQB->e݅%#cj~/yBq+{6~Uki:S/31g; GY}nJیnRPz+`&55u|SYXtZ*VG-n] c^TbUֲ.[|kzYlֆ inWݾ ]'K{GS+~ z:z7]7Qu)Jkn9|Kv2Vuo!ٜ<"W!9s)Kr?k8촭3j«=f=MqWSf- $ռQm"B̔Fyab|Xhū2]tծeWys"&(twR.4%Q1 ]J1!hfв黿Ѯsl#y`EMҜVt<"ZyFQj:65*!#ٷllӖB=G4C~E2MrSg^^zϕ0 䊤TU~%`c$nIORs-pFʺmO\'KVI?y,+DW zf cdlSfs") 3fԖ9>ZWQT~Ѫ:lP5DX5Sg)IPֵ J|l*ԦOz ]!k-RNp,G¦IXG5>J%|Jc>]GMVqn{}KXQDc>߾mc^*Y5T47jTFp"Koˆ8Rt'N#X(}OeUa$R^<|J+NF!'kCLeR7CK|ǯD>u5_lUL 95CRǣNwQKD(w}o!|oQN; ݽK|]m=m j6 I5j%kj+}v=99{G0Ѫ)>"' zUʥQ~=ggԾS*:el=a:qNezM+9ުvoWv1_XC0,J,Czyj3 e'-m2?{J u|ikϜF5寎Cz%iU5slWצҽ&UcE)VF(עشkN(|/M("pL*͏YvtjZoͲ+Խ t0.LOUV5Hkı/?_^:]V"q+LgmRh;&ͺƿݍ>yZt/`g ʺ=׿iނ4ko!qtO+D7t]t7N^+i 1/)Yc* }W^| >E4tܕ៱[k{(TZ]&T{5&.HtWنa;mg5i 5f&cY}:l~|An\xj U^jx-Imc_08&);e9>'u k34Lc`+sG{=ulgQ!¨ g6 Ew9ìr{/2zEvi5 6IuB)K=gT* «z% _mfqgR VV)SK nFR6,S*d4Š£ѯOT\U Y=F3M~нbg8׷]>O;k\pm-zkD͏V-p5A{RWe1_^7*63=Y$sG+sޣqCCSCҺ]r{=W 6WKCYt)͜[lX嘕Ex(i|yɺse)Ua1̻i˔f6XPGwuY: 6]tCliZ}1JS:xjMصԺ֞3U3Z3^tzfb'M:Sޓe??jd,BٕYHP"ƅ'&˾ "{ 7"^ = Y XRWw]\%U3#<~cq;MkICbgJ0s9rr͟[t+uTk (p9i a;YlJ,(趹6X#t dM| ;J&]av^z{?-i7ӷSU2;h0+kR- gLTvOTi8+IXXKrhSץbJUyF~]+/q30W'>B\S}-oQq(ԆڏxUXfFz=ijl_6UNUV15X//}o),`[ϑwט-)kli(QH7_[&9hd9)3XU\=2 GO|2gjvٷnd5y{,F#ʛg&axXSWg}V~۳u*U,͉lﱮʺj'?fo竟ӌ}{N<( ]MoGMg[.`Gӵ`MUvȿhq'}?rrzq5w^Z|<{rt=B-0캖5wA tL^E.R `RPۡ ^)My~)|hYj!Ž:V3fYMI%?c@a+a5_c CaBiA$bwlʷN 絏^i+zh=.k3 d3\[^;;ZO-g/1mᄚ2UM].kUCBUb]x9ZUG% sNŭ4iOSUl(,Bi9; C0 hĥ}%D4TKf͏VP)Y~2+U8UѮ7Һ-{UK횅~b\prg}&Ers.SCshk(!/,0|Kז.4%%Ū"¨)Je\f~:XkΙkE:JhZsKK~%2`5Bsݒ\C21gMmVngM3orWG,wjX%GĀ/MԊ>uĠ8,t{ j˲,;4~EL%n,!;b\:FYR/"#/y)o"4J> f>XKSKmMܔ*2ֵ RKos9YE4 ÌX[H~lhU%L:+Q\^1,Q]{%t,j/Oָl4f!u¡4;朞%& j#Y^/U!Ӭ!?+XC{ E9?cBVkwJ:JD:-Y !(CTK$"A&'lқޟ- jm1|V:lKBf[V*iK\JV \g!)mkX^9{4AD Ъ go&YbPЧNB=SO+k- )67Hω48nQ1SbC\m?g|Zs..76Nv$kYrk2m)OͥZ)E`p^]:VGջUURNm"=^Zf_5IUʛ1^PVP F E@(^ 0ϧhߢyM@RY|3ͷȜbZwSj[DWغ rg" O!{h٘% Pi_E=GX1[锟\Ύi&MFtgyaf4kdIg]9%WUR cΚ=LٻUNu],uJk~W.QfY,{}Er]˂G퉧:PDl ,*M`|j{~mn†!׺m5q})_ardőpEHx:NBnԷ Ԗu|_t9MO{ךa1–.گW'N1_K]u|/lYy&awX7΍P ÎG0 3QU wx,%-]U7^[}7gmIX~ l6UjZ}8 NDѲq$mTf [H3PSXkvD#~U5pSNڤ: F}S=)L6f BU{p'kNFK$%ԦՉʶ=3Qi1 _ [օb,ÖDj*w;Ϲ{N [aSTu7MܙŹr\<%C=xKw_RuXiUhuM̦UdTu[ycNs\VU^ê0jSNQ2x>ISV'"%ϼ  |#^{[ GZ"9֊u+ qz<~h?f9~ӴeTW%4i?-OPР&Fq(egn6S~l{ fú\[R0V,MykofcB}#s"tHқe}յ0="O؄?&hw5q>߿"IDئa7/ckULN,ZQ7Me QyD|N 1*IksT]K۝uqj:R6Qjz`lyV%MiW-MӚ,?I _lӨK؅{rTYݿ'SWh/-;*i ɣep|Idܧau|v皆%)~ ㊧\ɱjKzSVڦܮ>n[yEmhY#v$7l{(٫o/J2 "pRϝkf?eI\^+i"Uf_J(D4nӮ:"G3N%mT.h4_WNr-zF+ RȯTc_bu_hlF=&uX_VGKJ& lT~ ;0ceݷWOm~p˥Xԇ VfY3d? ²/r蟳Y l'!3t+;MʮƢzǐVwn)q)&#.OE{8&:6,9Ae|5ZdQ)㲟>-Q@shŝzuQ#5u P^TWni+*)]l^Nu [} Q"Sn1c\GyU{瘞b*cKO?'<˭QJK/ˡ5J 5Iܚ B0$I̼˄K6Z佔F[|h:l^o9+N[7yyy*JBN:~EN>'֢?4"4$_>-mΚ>cGbel UiY kjPOQv].B_+;+t7*Bӗ#jgSMTw8ݞJ[ c kE,ؖSZԊ+\/ު> M0Ȼ AԘR|)DYJƧT+?@a~[(0-:̪ݹo8+n{ پRɒ-˺vȴAm1eG2Tվ:F,B4Jc#(-"5 &})xv1^DJm(+7 iVֻ&>W:PE]eBTm_O7*TRI3zWp_Y6x^c_%m{x!.k .9+pqmؙ>i塩 i{o4oel&)0 %=j*i)+x N: ; 娳fduYEzXMeJ!6ʷ1%,-MuUV!XmkqP2;vΫS{kDQVͥJ{ bN/2zO.W$S:n jXvGj2r6 \r;{'x[f-"&_Ŏ#W=BMVOcWv>g3=}h+s\ !´<9䶞4uɴ:~Ș6:3*ݰ53N<&>>urpfݣqܷ ]skp^:>53^]d"s:ܥ3ܠ1)i_Lzo`."f1F|o=T$Ye {XmoYf1Ӊ '+i 8iʋpd++ZPQ>Pg,ߓ}jROP]vźgcz;k.-b=cOR*4}QO1$mCPb(g+}&glG(urXMn^|i5pKYoYRy"liVIncr> "h*SqhxNkZpW +TnNOݕM}U3ou| ?DN|=}6QS\\uh 9jEn-:bVGƫ-N1|͛F-rǮ+;0*ťVߋQKB#調 =ະ)uJME?d)2]e5fK`#rEU| SWVI5m!+iz 7~L}~0>u\L4f'J [^2]I+ie/4JÐ r6ϧдNb1t_-/\Uoh|M*i(jvz)5pEJ\R·4l9>NKc @tS~DZfaTצ^\ y|2o=QW{-Ӕ֖avaRU+dl0>EYXM|ɄuVؕg|G0n I| nƾWk@ȽEr69 |oMݕY~WX&Ίp0"dN=Qcy׵B>YU!>ANgq_ 6k]F=ZW˪OCyj @*),iQ+]M[%QO׎ EF=6įo-@ sŦAZteNNS& Tx{bSAQ`݌j%R~lgӨ zަW馬%`ZV-GC}+C"fZgTy du,OMէҢ"Gٔ(b+VR?2 S/zqHgQ?{XUNJks.;pW>NVSVI-qTM8.&>J|`o6K]IpUSnzImq_筏c_"êiv]_{;a++h-龗cu7n69mKRGݵ cYWZym61kYT%{l.g/1_}bfSifZ1BM]]ƪ.=S-:^le85e H.({-#\7^DFq0t]UTL& `_Wvlcrf#HwKa!(8;T6aDIj%iuy-N .][@_^0F0'BbKyQa rMe/ -eUV7߹9G䩾+J&ϡ$^uS}]†'N6䠲ofe 8$ɰhhmOK~5H-ˠl3dtj+⎢%mi1E>(ؓG"*/Fj/л? w8ުgڵIE9}KB1hi;jiKim&ὔ_Fz[)4b0SXfZ hJu) xi_jb*”۔c#+2pƩDRJ;,McUMV'V6UCIf,Kn[N2~mMjBA@¿g,RgҺ3v͐d^IV}/-WW L;VKQRخJ-$*''&8 K6z<)ORPCN(/zllJ^"R'/4E9UNϕcQB̘TՖuu݅lІeg~ |LJ?&%&sTL~+H/ ds&xqj#+Օ&QQzjI`N+ΪYD݂F1[l,z\^;VS4Gз&Twsi,#z^IEf۞ihM-8p1E~bKPMm/cze^9tǼL+?Ͱ `bQ̸F4ܒ]&yӬdۿb27!U!6vNʳOA!ij(X |(REUMS9h:zPKuƸ=Tؼ{ 9e>u~U.l,n/Ͻ x}b)~ʛjh5Hj!\ ^|3#<Қ3ca]z3ŔLNEׄҗn'~_!]jVJh_+2c VbZߓ%(|V5hYm4蔋^h~3z/=ܫ,.eqmߓ* Fm)*I{=H U۴ݽ%_6n0{ CpWyMk8ytRˣu7Ac]+qUUM)u !<tخVke7̓HbbE#fʂGпg9Mh,M:g(h-p1XeЫqgTlL) ,%oV´ "k+lLũP<={Io%G]xβT.B)@&c~FKJO #/B#dj29c]TmSo|Ϥ+zKdl5, 'O 2)d+MSW%6!05/?٩{j_X^ORq96W2˴}e!/3\+~IZ⾔M=Y0NY5by=vq0=;tZ4;2.Krpо&^򰰧O l".m]d,~4y"$ZD%)Čd$"G@n9}uo{_2{NCۦfN6ݟLlK|:Խ *3lY}ppsa‹v22(YQ-~ 4q+#H-`{q0CIQQj+eFMȬ?=dNi8rƬFE)9cu)ij%Yd͡Kn>RnTP?TMjԧبRGRU9Nt(Z**j)zk8,3#dнwrbj>{N\oiG2l@ΥB9yR5zWGq qNZ-Ah!v+_Y1 i[yt,y |L;TU46i=)~UUm(yS hTWeC@ETRj1F=uƤHi,*"vS_ugMhh,#m0Km7fҗ 5#PyNkD(հ9QXnsT8\P*hyzhtD~cXtIʮ_-}e[Gd"MUFNZS,2A,$m12ie#>)ϕ# j@/\kp aWC>,e9ay=@1 C5f"D7L!}oV_QJ/H(5WZ\^St}g|`\z<{ihM$ 98xW~|R5i%EֹGoBpdj^3J.N<IqċȤi$&2i^yT\')E/PcT. 6RrbQ:lgUu%j('YbSObu?se)DKO5MHX^BmLV'r&,&q'%U߿alH(Hk1NՕx4HBW6}G`?:u{7 ܙڭ=aebh}e QiU[F@v!o*Niy/!|,ѣRrY]]Uf֔ɖP7C(r=AgK2)֕+]ƥ5&/Uo񖷗{M,_;D]~\֔LO6n*(6,|Ү긏nQBCB-SQQM72bVRpפ_KXO1Otኇ@Фhkx1F^Iˣ:yhh4! Mz]L5 jjmGJ[k|+3ҔK"=]J>?zԇse.O [4}ifHŽQ).Æ=hǞѷC8z#V :=SS+*xQ 倗e&XI: ċe^RhTVYLYL [d^@,Kr橬ӖŰ ֭= ;gԴz-@U߿C<'8fxx!,LL9aAUMTJ!?.+\0o>]=NsV-G8j]i7 f]5YRdk {⢬&)UwaGc*yrUvkuW%'>{/-~>#x=`믧q {!͐",.Rhʹ(;y waiXg sl]9 f_-47kWƙBkN;` ONNŴuAj2)K<?Y޲fWֱmj۳)U_t4(hGArWKЛkD}TVǪ&[6O/Hwʛ( 0ö',3d4J:+ +n-Ob"3 DVؗ-f&҄(v˺(L6sK#-I4:f3;U1JSXV1rzyjyGܵ1tu EI+*׮l}]\[&Rf레5rIb{=#GɫIG?^ 4H@2jՕE ҄LYܷר_b~տ7 :_娈jR-QNba')riG/IE?o]kq[Aw6hEQ6I'4lp.˛*<ŁW$YݗZr5u|oTe9@_U=v[N1SyJpR2,h'$%p)E;cXjY.ŹdkI.:hܽs^}H8CjDo)L3B]a[;-% do>)F<&1 NV(÷kUGoX5$vN3}9sy M4@yJ1.n#{-ԱV)^d-hXE/KS`I-ozLG̹04¨ҤV#x|ɫ0Wյ6/A0bSzFy9I=-X$L[!㹪䯱 ZŦ=OeVTda|jTѪ~*cB.jSVe=weP1cgMٸrZ+*afla_, jh}ܥeKb.KC.NvmIAJ鷌CƵ3>07lq6aueuz;8S5U\+(T_zKz}3Au U%vJ4yQLN(Ilfuwoio]ӗԶjkn+Ok hU-B=KuUoKCWYSa6ܳo-$umъB2p. JgۻΝGαjUXˤ`޺ 8iܻŁoPA礓X IyrB rm~?гYdL;ۢ)?pwJ3v&tL_4}RʯJ!TPkyqad=&lT9hOd&Q).&oԵ,j*I_ô:-QlntkESW6iy6qHl`TYfm}Ztf̖?5MGitnUhz}mR**?&IR]Oca]W$WiIjiH&DG1Cv ֐哕W/1,} SķG3rƧ']yOE:uUf~Ks3k* bĸ r>(Q҈)I"rI}|׶*QZU7UBB$Wd9}5#X娯eUsV6)\@[oLI3 ݨT uT5BI ^y*ĻM:fU\K.*)@m^ų/S[6Ϳ7qM}M8BRG,K7--/ebfDh# '5eaU5]TE]XHƣ)LKyM–zX |{Id^cU ܭYaJEpg>6k) ;YUNQ%s_Js3"TexW(-MWy%[ sF"'"U5Q-œjdZ1rcTw[xl^"xTT]6z 9G\*"lg'Se葬򡳩ʄCTjϡExVQ=i T^~rP Ka&5} kI.'D2w^Ѩ&5¡5R}G^S+j-)Ql݆}_Pi?~_P'AoW}9{`ۂ\|[4bzTh1s(y]E%V_EXuS&_F쾿NӀvf9OY*ѾgQg٧yMcbYV̚df^4*ll?n7ҵ6Pl]k$J0)LY)EFQVݞVPE??N)oǐ,(.+~6ycbQWJ<9CŲC<4ˈmL>U8IID_S4 Dm FOQ# $Y{Tq\'%p*=ˆz/eTJ|8kӪ*Y7֕&5/^asݙU&Α['NJ. k6BȤ._uy0<. Iqr.IHm1ԃo:bX<ţ^V9mv}JFy5N-STSFXZxͨ[ Gex_zؗ6yVln]0oYYVly .MaK.1C{S?Q_aꚾ/5mEZfȴ{amJ5 Ľ)Ncb|RA3gZn;ȷzjZwߤ jWN>*(#aV|IjQ=o:LL#B7;dɪ7AeTT 7TLh<27Wb>X3ZbA>`<-H%4Z Sw R2IJdS߽Veyۙ7el\vU|l^_J*lnWua. $Ǣi_bCѨj#٦?-Xr(jJdk'R:h3ȍl}Bϱ/IO%T_ g鄢K>US jCzߵ/JChXY٥-CmXt*.*2+"Ke3ʭ9oDhk=FTtݹe9?7%̦&G]@;-w`y >9#@/zU\sh6S}MB%Jl e=ufy+rvIQߓ'Ч6![ byV1: UwfA@6.ƴ+MOt7#70m$&4bx4 K#6mҌܥֶDy=oT#M4SNi)i 7zkINJ6%Mg#] R{By/eTo-t nj~_n4+RU綿S0֡/%JK ]V50YRhMƭ|jrƭn[JBe:ݷgؕ\VMK;kӲ\3*}Ua&UpG&ߜԵfd֚cb!rbu4g79)R =K[&kVtmVgF y֍QKTۮ5PKU4&X9V'#fR!vKAu '10)l,n_o͌nj9>FJS$5K .-r~(XUAw~%/PfJJ2+kЫ}6zNF{O";n¹&&ϽlvxӞjP\D&y?6V꽵=Ob5I#Ta`|ZU/ LA^12鯉L$[^]dͧa܇)Ea[YTU/xY^Ya;IFa;VGAP_HޥuWWE|IgLz۬mY'Lrs}}/*:圶UY樟u]ȲH1~YĻ9BJͧ赵=HSV 6"S[ӧUR*cW{G/{*UC[^Ԍx>]!AcYmYt<"[,xagʰMKeK/L+°?%#'h]oVlRe";E*Izut͊Iy*+uSu[w-8XFI{N#<](̒kMDGWS9Y]%~^59mxQ?U+uzuL>6v*VZѴJK:OG"]-EyaW[b9$եFNTV?s)hl Mӕ'22탊~s7|wq"JAά®c{z۽TIκ7sl.D5m%TEZkK3gПbZV3ߦRU3{>s\^ө~du-сnȣN; Q ɭ^ d[;n՘~˗ ?(PG5G" \pH[/_i[G£՝J#n0-2_[}/Fg&R&_уmnc=_EҐʥCM%硚m1q=F^I&jVuUi=X3ݺRfX6]uk}h)ij5xK7TqyMb<(k\¬TCaEG"Z(6DRrH*E<DPcQR&U$" !$ GJ2qHΞ\"%>u/C !KB QC8$kQ߲ ԮBLUZ8:(zsBOZ8\Z%8h;Ma6#`gtq\{*Ҵ)yE)p] ^#@UV>\ C89\|%4pu8愺\BR%X)I,w(JBRiqj(z,?3|ƻu4G\f4C[nU=$P0*RImhBC2fX+ll]0[u$Siz?Op_MС3n=FII.5,=w?DUS]9kG=&E"_\'UPF9GW'Im|OqUU7F:)#K:nDX//wXYGn3ϽlOT,jKsfٰKx2(iv!e}9+9=&%ut5a~O^[^,k2"Y9`_pZdnlbxROm PPދRoyf/8y*j`Vw>4S[{|V R4u}E^"۴JȺ֦ZqWiXUՉBNêj6zSKgMy2OAT;K,Om.>"4&~SYeɕ@sh?nQ*̿0Op$T FwǠI:WչhXuLB]sj~),~OmCg[gmsS^wܛ3jllڣ#upT* f٧ns~=zlULJ?fI-sƱOR-<,FxӣTu}( ڔ!0oQ0,O9X@<"{DYpФ |0U٤i! Z_ :9I [.*~ T`P\ZP)GӓB^JKX% KGRJBUT0<*y- -B%B^ԥ=f5bTB ^Ig"RB!إM U mZ4VD4D%V^wU#C0ˮ*!N}UjCJI!֡Et/aJYxJ sIBRPB`ҥ?JZsKzo+|Vw !*%F9^%(! H)RjN׈\;ExUߤ:ST!M6ܕ-CjBYRE]Sm\e͠M [J>Vyͭ]ER!CH!MOomѯ+iN/ߨ\zTa@cohO( DmيBED(٥ۏ hV"!ԕBCBΊ)tfۼmҾܐZ_QϦhVic/+K"a;`@y+ǕuO5{qy`٫ULBI-QV괐-{m}}OKZi_~J36b&}9ɗUwk #Rt=DηjRCvFy9fԵi*JXk??{߭wvW~JNj]/.n`J_ݭ\}5.ӯ ꤑ>z-rP4r5U DdfZV]]G)J,}VSw&7oQL|*ڻێ8,l"1OK:F^hd-&E>Qt-b(oյ|.lFPV m݇ީ+hUaiő[`6SMߛeJAAZ)+sS[W 7[%U+Ul״jr1*̱/%.=gГ}BQ ۭBIf+m2]TuMoXޫ[{*X4I{IdG,9 QUrmZ4]MSY}N9ܭl;iu5 9Է仨*M;L9g>Qv?ɵj(kO\fa?BMè᦯5]yzh[dVT~-* Hl> <SRضNm Գ#T(}W]G"d(MrI; z3<`Mq zmYAvjW s6O{"P'mG< dwB=5mKYYU)*;huVu15j8[ǽV-Qg؄؃Imji|2MI.ks[Ue5dQ{{^pNݵϢ?c|gh4uR|n8 Zw¬nZ(zYeQO b*<d6Jd{w35x,øE6ܺ{4-"2N[xyh&۳gdߙ'7e+k,)|սòNkt%p=?92Z)W%)OavxVf?%m@#z*mTz4vXWeLȌ&iDή hѨ*O$=!zo<~+(y)'6ε'Ncn6u3+E5 pV<+HkHat?L;SkMdd/UcՓ%Mk5r*,5͡!t|C`p7G-J%r"R5$O$x2_W[&l_)6UIN5BԮ'c[0Fil[UJ!T>*ҕ! BU֯ k,l#f)oٲnj)1ʯIkȻѶȧդ7Yu!@;^k築^k:<ޅ)?_i*MRMUcRu)mEXuu HCc[T7bXmtivŢ/ɫ!wN4)DyPXy meN㬟S&EenV:"rF[N݄r5ARLeNZ=|M6Zkq3NՅ6dSMzJމ`%11{ b]9JG<)VEIU .GZGy\#cpg oJ1i比_u|n=Q4['QBhEm M}MCoRu&yTVx6ʼnM*-ZmZIF"ăU+r.+xe_|LR敪48eozutҳKl^%9R>eq}aw`7itjR%D pa*5N.OVij2{Nb`"щ%tR_IGQhd$ܫ*aXTgO&EOx٪qfƔECa_YaRE~O\Ӕ(x%ۦ NMENM뾱#/{PW:#OԄ zRҫ[S ]SZS_S_z/ʖgS7I/hRKRDފ ghV{ReRϩ:,J*蕉.`XW$ekF&mU\}(ڽLyvpT' 2 J]nE-UJ0G…J"uPd*+( pG{ C@|l\ WgEo|2leIkXGSQI"tCݳLޣOUn4-)PRXĊ|5$UZVWuŭ|YeT`NzUDz1hW%]5oŁR-=8J'u4VX~oé )XMjBQ Ĵ(r|{+ZIs06j1OCI[ =t8W_!B+RkSy5h;P@=纫?PoO%Gj 8!xA.o;d-ޤAG Z1rXfeJA_d˃DUX&iRt8K`yB0@`$hC? kc4 2h[ʻΨ"'Z0d_!J,n#8չ%g]tnjt й&nfYv]ʪ*-KU.Bj c=.eQezn'{^[Uh?՝Tυ5=aZR$w1utM] _iJEL-򄄔@SBpG-*cɓHVZ*M"OVWHSWOLeMrWȤUYbώ9eb'U))LRqdQnDŠKK[Th]/[psGOl7^,'%-H [HlAP˜z<͍/ֽ((,e1P@Lȃz;YWSܯȽhuy>*FNS &utqJnWnCNRK5 5{4Ey:^Wk ܕRK#NH0;&VWʬrNUj޶o5d)/W*d'mCboie^-y"]-ZLřbjqac6[ {7-.ʉ=iKON%)߱U ԺAtݧ#^.뻌X)~N͵'fϽS`{>D݊LOqI}c %9ڪ/]˖sXtSQ\.4VHצ}-K(.sl~dIQZ죵|I\Dԗ aDT-$il(tՇ"Q7Ǝ5l[mcPYY0?rҢ؇S%I鶭:&#IDuUAz+E} e_*splSF,HUoP5 AK>NwIy_{B"bybek•{4=("dҩ-B`Jﻔ־{,2+}V>6pйn͞2XPC3K{*EKЩ?,'Q:iuy<ռf.ꑝtw kyK4ɍE˴D [$ml8:;Ep[ ~zSZް ' m;Iob^zǀbuWXōHgl3sL(3hߋ>T*vk#߲y&gPQ[RgY_KORMdW޿+Tg)j9PY(릏e-ˎ2* ҺS{y[6Iد:U iYkVQo#~̫s7,ZԨ̋DԚ p];jSWiCEz #N9jLM"C9l M%:Ws򓵢lkO֤1Iâ{6)ԅCd'b^_ThxJ6jBb7 [t R~z*^Hkz&'cVf'Y+54nڢҹx(6Nݥk5zmM9LJen:¹k ^G \̧Dnzc +'\1mYSyJ7 /=iEmUrlOzuEv` IWJ6A,*wdT *BY.3)ETN]#xtAePJ&$+ J3)%Yy2  >z59M6FsD>PXWF,oI~jےTXemQ_"his"Z7.y:."(QQ/ah\ԫB XT㰕M"6n+vgLNS qm(978 [TNQ64FU,%YZ^"IKG,wmW^4XKB+Ҽ /hfO}-d 7i'3ŹV9.9d9fKiz+li 6I-z ?:;KByq.>(eUd?^kmau."zF 7u;.xpt2D\V)HV뫜ġ,#,"}ԾG 3jCT #Vܹe=I|#bZGά,m0KZs\;ive9Ri벶lʧ# _BYFEz#c~{/]R jGg۔c1‹~AoxcbN*sO ꭹvYiWZ^VQꮩ'i4wVfd5Ճf'9}-lܾQ]zw  2EQDddDOM&5R)M:/ˆ7J= q!dL>\e:d& HY3XV$}5y_E׆K)M#[ح:x7mKC' jq?5pO!O&+g7g|o؟8Cpp>6:j:ₙ(?Z~Bմo]MWEk)1Z 8+-UMDV񅦤hێ}l.9[_~`fKVG Rz=}Iva]Ej΢NUj^tPPT{@)έ4uM&PŠ9aKw "AUgRCmP˺=[QjdcجCm:f 6#!ViE*k>"K@αMsz ^$3ZDC:vLQgl֮7LծAM4fWJ˝UjMB;vy ʡ+P^}(d {i "UΛ--wWS91)E$)7t5XUE5 3g @J>5I'.J5HNcX")PD=%}W$EYzg;]URS`[%0W9\ZEAS vr1Nh~53KJߟƹip*γiJS֤rSbm׻ K}J'OG[(=Tt/?~Ib+ rqɂeM_ώ'O]-o-#i ] t\¦O%I[:M=Q$k[b88)>mGMereA .9oHS$k=8Yw*=dbWv=A 6Jc/JYPLrcQXű=8K)ok4NebrQ-JM#"Qf'Xg֕xtU3Mcgb% Po}SY'J -\XGBy)ǡx*D}bh;JfQQRj)7=BÉtWǜJ5-O렍iޒuB" ,d+!I"buڀ_  3s{жB1q|~Y(~Svա">ie}NN<}{{7Qq\Ӆe_Lyʞ5kΩF]׭Q>uy% ztltc|o3NXWJػzN{o,BUݗT&=VeE⭭ս#Ql(ir:k]3)ζ)&KmqX L$k/x:9X&MNKzy,Lg3,עߏ}uMchzߕchkja,|Qd[ܤI=$&^T2]eCj/36V3nU2+)TpbgHFjn<$ +'N;-L/{N^qPksYj;ʥ$RpG9\^f=-¸0,rU,oɒ%I(.r';VJMgPE:!H}nsz~UtW'G'̳2m~,TS6:[Tv-孀@ȶI7-f'Bi Xr_n^?UUi^%FM*XvXY iƦj:j]odk̳GXm_!<~ RoBN_ {}[;Ši \fh_:=6Ri;s4u*$1O:E]{+ Н$ ɓnVK~ ٹEE8Ș&SD*Id]2ME@?e#$ϿI#lYU Qx•eՄ{&$4 E3M Z+ 1zIuLK)WYm@7ֽ]V05yLY|U1HyK &tweBvӬL(1myʤ͌UՅ=,("%i;Y[nUӑz/5uJ4IU绋o,nsU4/WUVcr)Ճr"QXr`P,6(r4)ZeT-}Jʮ-*f* '۟L&^m,sx O+ӂ N/aFM2%cp)ٝP&i~zF >ѿrRfm6{I>/%΅g<+2JZ'Zn޳iq7T8AYy뿚/Qѵ>g'rt3g 95;]v;Ec F* 83lݠ9 Ѭ4t8&5 <0^}Cԟ~Wh]kAg#կw&M/TY>6@XNnvٰ)ˤdzLA]NںAJے'KlZZgק/} ۝zq<摰L=F ;bT?w)S4|i+pfwii~t4RFbڄr6D /)srLffDbۧG+=f *10/~Jdf|%^LVX9g7vA&󘜿,b. m%~"vۤ7*n'M`Y4\\B]8\[PYwKʩ8ʬ]WQ )!k.SYy7K'H K&SNV>YhT?^Ӎ]qy_T((ߘ?%lkz )#u|.ar\14=Dȓx+|*! ,\s͊`=,dR̰4+c1lѫO E*ci_hyKOʽ?bn۱ř Y8]X-1Ctj ~:}/}wW}jBvݞEq]b5FITtӄ$ՄY$-Zns-) M# y/yJT*5QO_`щ4T3Ft5j靖D3J)E 3²}]P7*)^QR^{pѫsQX/FsGM0Ę֪k4jԷDįt"?fMF6>W 6/m=[爷ܛN.O8}O_jdFݞAzE i&dfkVgc)^^CbYV^g*Ze*=%7WT8u+K`umfU4b5j2{1mNΣeoyMUn㫒jdY0˱,N_z[f2L3&d1huHV)oG.4^y#W&Ma⾸λ #ѭ3ZZ(pDkf9@B0^]WGMvJoY(p,K@y]bRTz5YuTw+i]{-^ٱszki.>>Ω{E.mǞ ќ]O=\|V][Sqh5[ Ezqek:L|ox]G{￵-ɰ5ƾ֝;g ]¥+`zMɵo1</;rk~hNޱbx3=y{~I`U '\J[Pk7ȫw1Ӧ5HzW;!zɲ,kVt*i^;9*x2iWZ5LG]ya$Gqm?-7'fŢSsV{뽸R]~*ɌrWzjR᧯./!XU7ª:JE#뿥,۱j,\{qMj/ᵊokWGA~qV.zi[8QAo_x+mgy75f94LKh[˴_} ~hF0+{ݧ-fbj)hlDE%^;9akSE&- R?x<9jo-)Pn3Ɲ.MlɧL=u%KZq[mcл;*+b[f%crި 렮/Mr1gjٵ,paxUy=]11 ߬T[W\)?N`d5|uYcPLydJnާN)UZ]D{_+)Sozd䩻 iǦ?fypU aY"ndUEydHqO<yE^,"^GX]s"( 5/k-sZ)j|.6OXxk]cJNM.KHg(XC~vsW6rkM jkFkUޭP`~5wȿbo+ !*=xn*oT"ڇX/ty)#Cx7a\&]'4!:Nq/]L48^#n`Rԥr/.7i*Rh[bu6WζӚnU 5_׵.UJ>)XzIeq >nO̺-5̦iSY\MpCʓ6TVR͗ql0H%a"cilo̕Vi8hT"|lOˊ}ˬsĮi:.OAɲ1r+ΒV3Ym~F)oITS;etsk? !(ug Lt({m!6 FU-}$`¸Q.1,9x]qRL)EZʩB\siL!zUnVC1M4=~G_ҍM)pO?Uq*QJ~hj1L y}m+겯Q a]{X)=UFA*oح>i-E$3nu6v["a;}(ţ2;:]&;f*U!Kbk Mb ކЃ;E:horM nU5e4 Cjd[Sc[NdyWZP/UD#`["Y+&~voW)ՍŢ-Qj]ZRڲ%-C_8pLkULsvlJ0 ,U(\B]n9JB)G  q%*=\ԥ qc,,CpV,ږ(p+䞪)µnRv-yV%F۪!(}[UK!*LR.HOV&wiR!6]CddهxYveتKw9;{Oic|19[?LRinI9kB3SUUޠg]ni*!45Y At-´JL۽V>_QTrH+cCU^RgQ碲:*M-0/*\TbdVR+AYeaQR3,^tO|J| قqufd5لǧzv紉tpޒ(O-W]2 ӼG E?6j\5vz&m"r}[uH]Pf׈mws`+<ٳ'ؽ&Ңg@?"KLߡ[ma\s'J_o°}^fs?7[,r{y-raDWnqp=u!C' {'C~yXeeϻQܵfa phPz6ILeA8 Fw9MB7 TEQWL}ȕ_",J<ivo/vԇ]I2[* ?)6u>։||5M`[V%9*Dl>蒺*i%fX_}Ef9 CyЬ*ql!:O4Pg!:m+gRѼu[ZM_ W&˅/묾 ;)\Ko埈|?M`h*  hGX( ;CU F~-*jzE Sg0?󬠧KLEU *MsyQIWi{TUX#Ԍs-Qo|,U.I1ﰭ%bI;(lr9=իeysU ⾓*O\ű/d ƦD\V)J!nGuCyZ8s4~‹{*湽KBA)2Y#c.t}c|&"|^)ȦqiUYF٤͓ZGg-:yiJJț81=O%FYR藔]Uȯ(&dU[X-nUZ#$z#mNikC{ks?]°ܷsi~-=UW}|3h8˚L='޻)N_VNZͻUߛ+zN¼\"^S}y;J []eΪ+]dNaVaMzBEw`ݵlVV"vڮIFi3C4{%٥ME?^[tǏ4czO󖄴{{vU뽶-Iն=`[YK~+jh^kޥFʼb<ZixDOʵj=Ɏq#_rfC"RUha(S4>f.JVnEPDvgu+9AO^: ͧ 5\Uvwο)"z帨~ZŻ,Zt fQES  "͆zߓtv#lI j:g:=`#7+ҫZ^eW2* 3l='-Mfm`HIICH(J:n(s2UInf,rϟ;_Fqkn-pK< vQoʬ-{NnKRmFx.窧)OqoaF4+6I[k~F`W5zcO_wYz_ R˦ivvתZǬ¼/t(VمvZAGy"0Dž@cqaYgV?jp{JˀbWJwM|GE'kGT7^I5$쭰oاi`Z`:%PJr* ffSEmO™#eX٦Z+F/ ̹_$*I,KsWiN=DdCFYsYdOԹ=Zd^G|{yywOCj$ًSOlc) h%%VEOYo:IOuB%evȴ9F*Ruuۉ $֣=0$DTuW/ef y%$5coZzı?hjMz[W͋PTݒZSuibǐfXKs 6 lP&I4LrUuW+p4O&$z&Asr/ZGN2~[ V~͊1b{%E;iwڝꅽ /Կj®nZK3n{Y՗f.UÌlmzTxm+xitV ˒@\g..U^ j|Zh쓄 r7*sS&is`B:vr扽)򐶢XVEH-stW\Lˣ>\g";rwy ޲,EL1$?jE)Hd‚rhZhڵKĜs_ Mfӆ{ ާL5Qb(|y+ R jle =Β)m-_-3:R?^zJ2%;EBne(Ui,}SMksk#(YUiNcS>,l>ޣHxS_}S*:ѭϩL}OOk(b- =Pb9?[ReQ`GGeX&&) 퉛df-ITi kxK}"5uhENOI}N+Ը Irv}F&-e{hOjUW*qSbwhzODȞ'ʰ'c\p*Pi1/}LS-Z$7UFC1 I|om{M*?6 *# 6FL>?75GZXFKUS ku  *A_ ֶmA$W^:W&5PƄd5}aN-2h]La ߾A,DjKi&n' 9ޘ߮Q-W֊5Dw 8I%%;E'U~L[(/Jh. O{sȷ' '=aWzV=ޚ;e_,{&YvcH?D>?Ϩ֙,KhInx;ouX4UWyC{-uys3X?nƤj*gan2AWydEZu*$ {[|l7x]l$&@_[ΌiTܥ.-pR+^;Hbfb$4$L&[?#\|_o녴NV0de!BWɗ1 ӏq)K4XG CPD^+wT[BK@s f޸egFِ_u8} b²2M.ͭ/,N_"?{kYF9p!!]2Y2qsu .cNG3޾bW!|sB2(zzɲ6؇B)ޥgR-( MbMV@UP`2'r5E IJJ$[xJ=~ۭ!6IbQYK~XMq?&*.b#^h̡cSNַҟ8ޚ.us8EMzrVSۆ̵Us8+KiFm^~ٖ4DܚBJb3_ EϞf'B^bW"M-P؝5JCU?l4Q? tTPvd*2-[Nk'DkϕYm;>;m:ԷrWG$Mտ#L}ohygU] ֛/?Q^Ӌͺ7$+i?y"Mܲ.J"3h6#gy HMYg'ϲb*\;-geR}9rroQ}cif)cZ[Եgu$YzaN+{aUe|͒e0mj5wt0q'̣z޼ji'y/9&O7.lT쯎C^Exgj؂f^Il% ׄJxzXf#ݹ˲۠&Q oUšpCt῵Jzӯ_B?{L[*wX7#m>XAuGW 47gh&?ew_#~Fwh7~ş8;}l,&u*Ƶ*;qj5-G}16Cb/_l,y4aD6HzK2&aAhȸ]+;位C)l\Vu~ YtXOd`>}(g_JRm\G汈a5!4}44JP>fr`oM6}LZf^F-)-SLZvﵨުLzt*ރ9n+dU=obާn]Y"==ɣ-Iy-K:nFL7˛ecBΤL{~h= j}ݦxrcu%U]F5_ٖɫh1DP;m6sYu陾>lFQk;[y[ޓoDi{ jɷkM`ٹ{-Rl5D6]IDYMTYf)z2/}v܆vZۇ*J績^ٮIYДUe(L ȉJeUD2To?V)&߮q4V%mq +/$h{ʠ`l҈Uc&CՊʘ&1wsՅU:aVkV)xF%UZ|آ9| ž$b6\2,M%ؖmFRVALQӘ6\WƷ/y͊}D`J̲fܦYmohW+} 6Ɍy&DR/z shT&SlZbXne1ヲMOV ~dYtMv"968ɧ)i&6Z=瘰, HGRR;4*)?P縨<)Qܥ\>Ba')f{^44d4VEPyɓuǤ }r+sB ]k})ޮ6iħ3˺ݚ(o[2l 7Ks/**%@ztSI(ߊ=dEePSDV}ow̵'AIgdfުsaV;gJ=0Ncɘ[RY/E9{QTrv!1gҮ1RӰhoEl1<۲wrVބ3Yoġ+J^[Ga~│աo] < ֡}G^ƱDzl3lc+>VQc~45Io"%10<VGw z:Pple^a[g-e±Kkt˃닉uJ/%1NC(6 +%"byj"ЫPV^>G@Ƭ+xv9`Pk䗨Pm,hٮ6aD{[o1vOvg݇km3֯kjYx5m:wA >#ykvM ER>s^5oAEgil[]I 3WR UopZږs,{z-u*n;Pީ B-kv錼zě-Rɭ V^")k5|~*o~&BE%IDH=%ՕqIO\3g% X e}%u[y|Ψ'j\$2=~3^6T9W~fӜ9NdtT5D12$_5@Ӷ-0t>Qrl U̸ɢ'4@Qk_&i\>EiSjٽ}STYgN޼ܐ~׷j&*E7*uTTj)),iS ?E2)3kR&UџKiPj?ƭ5Yi^bx̢'i'Me{4"ԳLt't C|,ZU:ϙ gL(Rr̾[XcXA,lؙh^R%:aU'KE$engYt0honF 7F#f:h#MB16hm[F#tsHܤIDTB$_G5GVrlҪ*kM?)KK)]+b&K.dɢ6ep.5(y\Kvi]iKZ $J(6ƸPQJD1STU B:9Vڂ ~5T)C*pKBPa-lǿUtkz73PDKM^)ibưT¤Iܲ? s jFYLZ)ՎV!U.!bJU$%kj+ޭwibϴr0!A4,"YTPYvK!!z'AYMiajʻӌOh,4[0cʋy&.% v6;5OK@U^uUEalK.[erdtTлH4uͦOߥ\J3Ɠ*S#>]P"B%w)lMwX|zu#Ѧ+(g4lY1s[T1Gh 6^4dX  W)!έ>P.eo1K$c`eYMVMqCvmjTQ*.8%3t45悫2uy*sͽyxWѸ^̚ZThZ[MǶ~)kKwjChHZc)TZ,"&F__9NxrƲˍ-i%D!K6CVXS*+Jx(A0/⬱ijeTݒV5lA]X>qkB3} c ^rkV1M Q m}4-F1L~{³?Ē->"z7wZȠuuWC[!E Pɥ?"Ҋm-`RUO)CJªKDKW 6Ć"劐,XɗZ隂mR0;Iq$j)M CL~%iX^!* hh~0H*IQRY"zE&b!t:S]Q1XZ2! ũB?2$갪TϑKWT qVRK4U4!vQ4`aJiE(QJx?1pR" 3^+)" |Ry44o(T!CE)8Da7}صC^'*hNZJ[Iq*!Z,?'8o}Wk-4Im4T+JuSyJhh=/:JT"|nҜ]SzDgXY͗WH-hS☈QE{T͐(cʚg*iK<%R ϥ,: Ò{r!4Gg@V$)Gn)jmkR%jKK GY(B64%*ݝ⴦-,bů*"Ъhsm1qZB74 J`B)UT)(BƬYJBa+0nZtR^3.)ppcx 5 o #;{J!ӿ2L)MTJQJCqLƴ:QK "(%$Ba p!HB:zsALPmKK3;,;}KÛۻVb:SJ|:UEwdB2meK*7rY×˱rgHsްȧRtV=&"; 0j*ͯrɣZ0sګkJ" sz84UR|nBI:v_[pQ;n|jIM()ɒ]Ļz_#0ꤚlq+(oz*@}5]\yU)Hl_ AQ5{ "1i|OuM5fEu\WQM(*EE :mUl4sDE{f<šJTv1zj*ZpT[|:$95F[Zrw΍B>zpϓy^Y?Bp)1ν|Ayok1Q 508cnʘl<>l Nn㪫Sbةov<6/&;]gOoW [n]g ڵ7Oe>}.An]UeZw?:?kUQ\Fdܷzx./a|! Jz.I{c/RtX U"-fPʞ%q,Ns3yTügY 4wT(u-qnTk[ǁImS:s>^Zݺ /켯|P#[hQB\i+N2Q_顨01۪Ƚ%&͊͐LzWF+{:Rx`- 0+H@t0HՍ UNY%mAo?T[YWUe_e?z zN$k/0,1l|:ް'OQ$PY৾gi\qƍ ;.-)i&+QRn%rVw} v_R)DQV2Y|ٱ\w*N]EUZ>8vMy84jj0%}MdifO_ĨKJrG4Vl }]g=W[1 M]`K9,M *Qڞ9tI ͧU]hQsEacF)O#X<=Rf'*n=M8(l,d\ՈKrmF/ȷy'77OU쪫Zg"s.mf!%U[I6bUXVi`^r|OR%sYCЙ'Hz <%OE-ϙ<61uIF XGM> %k73Jbz/'iWZ2TO,Bܾ0{'+3,(tv;X)R*܇MPlڰ^b_#M$ŁRj 5IʶkuDZOQg|* :'+i)jӖ&U{,[KVei0ζ)6h ת @BpX̀?_䞘!a`ZT8N\;VP[^^X]yQd W?maUeu\xPk)V5?=B&rYYeaeᩝ<S0Ű.;9ߙ7A E'5jijR~*_k*z T=Xv^ VzeFS%EæԔٽI0T;+UΨ/K_׸>?_Y(ii)O%L ;wUTQ/mDsoOwmsq ڼE:JH?yk7l58R=fJLj=2dˣI14[j0>6RAoɱ_VG{iyfz,IXYnhݣVqNe XF 5QkZx8m}XٚQV>lRry,ZF]CO,XE򥓤N1wӧ%h"E)nhTiL1?%ǿ' *IJ1 𼨍F({\:5o+0e\K 'Q%Q^FMD65hЋEN$,-Z5$ 71OL5 ] Fvn_XWѭ e^i7~$')Ć(/fd:YǩgH6,[r y)ۊ;O=L5jO"/ρPR- =G&E=g\5})p<ѓ{hDM$Kk]=jYL;IJE㩌9FzWuuM&E`Ѵ[]^$ytzj)m[: )K@SH Vn (!\TKSf/ƞ'?]I^b〰Ɲes}>dBg\lèW(G7Im8 >*X7FQ,co#[Vt#u"ZȢ>#[wv=DZE\aih_Թg٧ҙ?fMMOzk[UpN)W!Wǟ V\['9@Nu>WR]߮'9u[+o|.U Ê'-*EX"a$pK#^uj#90\];myhMSoaa%ug$\NUw꿵#.b(P]NpUvV%ͼvs>5UTӤzR]__^\צl3ƙHP_Jx3\I(fHtξT ,D7(GE^ՉPsUp)xbũtӗL[VڢW87jnȵA6NY7DXB),BUVř?M3^oqXDyHDbB)S\R횮'3_  Cɺ]Tt+BԻZ5o@g+oQ)1hsU 04yO="/KU cq)iO\Qה>juߑCpUiN4F*myI y(V,*Q|ԧE,kMf^sS8UVwьϘ`SF aF} g42={hРl_zO݄lU#7T>KF)If]|fȡR9ZRpUUiQ}G,ʊK٫H."YX`^/JXҫز Nk 3IRq1/~.2|{,+lta5/EB<=Z=V%^m{wb\EzVcLD)r^b1Ptvi}KБ7X?t2ƻUdݣ]$ ,Q>SYYn1F)E_OG14Fa"DHDk͢7}WqAVQu_]O=(O:&!T޾1J+ Y,默ʊ-NR^VE6~~D?d EP^ԛνE#\wE'̲4#~-[֜_1n(b&xrְMmG\*4ڰ[^^rY. l66h3a! =.x/yFF}F}K>l>@QDzz~|OY~3XNq_Ӵڿs\&6xX.4&j'%Uz/®:hn3~. ثSԮĪQ%VCB][w^8&7xnukʿn/Z_.3ǼeSq}+%ܴFiŻhVjaFJI7NuaHh]7XT\Ir+\~Ghif,iLl=iDstqNid#Vg "%"ף C* #J/yvcQ-Fml o%f'Ӵ̛lTߖle03R~寡En6>gfq*UQaUi*4R!Rq6)^Srв$PDj pS|V%),CW;(Q)E6E+f(P? XRJI&"ǡQ-HXk_hr/ C7] &"ՠJ ZUa])0IP) :nMJIQ1?Ըw`\kȣA/eџ;11aY2ꇐ̠d,&Ts>"WE%tke!Hރi뭑?5ı,;\4)MhDaRNbjCDp%toJ1 gf ))iM@\e]FNf6eO'o2S+J:H|2$S?fI0:Ni ~5`=t s^3格m[S 4bW7z$ UDNcDqd{y2 :g*1M3OYnUd&ƨ n&KJ(R&IOWEN;GEr5 ;s9J+jfcj:FY"#Ԥ.ا,-j门R{ WsY[9(J\m NQJei!:ۺ(99ӟg;JC~Jׇ%Fސ&]uEuCOTm0ESS瞊K<XNUϭ ш9őeUjA|AN (m* ncWt< K5ٻ*/tE]wekyL}q]V'}mBa婂ZV҂ 3j* N,#$:+k&,?ujx\ c֠e/ʂ}G\%'SƑxq^MxPJ=y0ۤѯvWw^=sz*mzlN'Z5*.ȖE`sa+GE4ayZƅ7P]]&f&ko&1b6NԅVqk0/݅lsT`\ũ&vz["&(҂wK<$\ r'APЁWv >E-$̓={k'sĻn70Mg+:0_r?$Q֫7qi7".#=* iX(PAyWҬ5?Wڭ#݇Y.hR򎚸ȥi >xh- -/^/~IT_򍒴aС¢Mc[וuj5b k(oPϪx I׼AGk n]./iюNaBjw$WēRĢ^l@&I56^=Ȃ/,,䠊_a*|w Ta.fW]K=7'[q9ץa[^눙f%y x;uV=5Ao(2+|4v6t]{nQ65+L>/šWJdQK F=z[GMrַ6;N)NQD˳Sdm9}qZ} v!MRh-CWu?xVX]H3h0+B1t$~sڹql&',[P5+zڡN4N%;hퟠ?Ed{}ℤIuCU7 [/q+S#|su flNV I× 8_Q֕Al+|6x9hۻVdbs?cG=-CS}IJkRː"vljXZCzpkæCFr_B8蝙qҮqm]>VXC ߘ"EC@ΌPFhؿizf%w E縗ҙ`Z+ҵ5Fd46ayI7wz.UK {)M+.+Jgni%090huB\QUiIJUPqpB]^fqZ,bYZp6XB ٤6UP"%!+Oùh&UWkOaܠ<׬՞͟ơ٬:| aJVUhtqaX:sVE4TJKeWG׊#[Mm J^iKӐ]^ YKJkm4UBā}r_E,($kNӮ-$?1ƦcCfҊBPCEB+fak֗1IKriiU/U^s*Do۬Mv"0JuWÔ@kZ*x׿de̲njqt֕+̓m߫hemPNw\j5,!H`c6o"ԕESve0< [UV%u4òiYչ*&hPZ$e SqRA\5}d ڊmwM#Jiկt#Qe_U_#I$VXXl\?bT{yyUhVox <;MǟH:=Pa*+¿:nZQ*Su(4JZ,h~UTI5"QowS}G`[*?)Ue}/OEJ‚QRZ8h+}T&h$A;ut5iv^dkE?-S+ q>AzK$#y_Ul݃KWLXܰL+..<av[ꭞB -ǫoC(K+toT:`O'N=lBb[ ݺ~jkiƷ# ٻjre_XVūV+MB]|VuOΩʿX:-W]oI6-7EpZ)uu" H:ͳDJ)ۤ=gXWiiԋ\>ͰVCqɆek[yɜ|MΗy%&dggG\w޺Fe@m٣إOwʧSfY91FK:Kۢ: 0S/O!Tg1}RjNJD[mOad8nXۄ[wvQu^="<(1[vܕQOZ$vqH4-q|:J9zm#`]MHRI$h jQs [ZY&AKE*oAx^>$Һ.yD% xCMZAJЍgK"NԕMhO9ϔzgW?W(LK\h* K :9VKJ 5vľyyeE9 "^DŽ=4~cMz#2lʩn`Q!!)i]#aaQ=߾&!<[2 '1JkĪ'ktRԇ.25!Kl-d8 |"%^iT5qosDŽ?ytTgre ˥k5&EaT)zSRO!vQ'y?bZݷSV爸?k,G^Y$DRtrV9.ضyw #T6Re]b_]; gh9=[tVc_!^?1?se1tUyʼX4kI3+´M˔d\zZw~U"4K:PV;|WLšfZa&mrį cp&1eSFuMz-RʒRK"=2lN10DgQN7qA3WurW!D&麒J6LpTձVVI z@ϹZE=O]o}YC|.JVvʹN~YEY21ZK+vLY+AxztXbJ%n/$+F7|k$侗ZEQfLos*#L<©:pF:v.Nq$ׅ,c1E9>a ZR$-gtN4.$ʢʰsgFZeechw-*o7~|k~ @<֙Wy8;3=DKi+Aܗ JP\|HizoiZxq=Yˏ`AKHѐwZ;u7jd^sܜa4ME9\dܙg:Mi_;Io~d[]u1 xs~i՝Ub\%e~TtM)_Z.on_ C^1@/d_XgbmSīs~'9;% ~^?E_اMҼoVWEy o\sYWmI-3Oy$akJy]B䲏|ou<|(Yy UzL*v:.ɛbUJקύY*"QTTT~/IbwbQ܄s$њFfS'ژ~CDN[CI+Ke mEy"f6pJ6 sMU}I昽QOf\iWcOC,SٳʢZ,95Svhe*6RhP^%b'oԤR1:R66Kqh,˾Їʿ7Q|ʲ9}kل^^b2# m}ga[OaʢhuMAUBPfuJmvwY^kp bvSwPkY/[>%Y9X˂(}P2$KbBCdЗUѶ]\lR VBk:E zj }j%Yb|jAp[<>MYfr- YRr¯7ɪ1&GA#F:΋±"c8!l(NF~r߹ ;nJ1Γ( Y_#YMYt|WijBleJdW2n Kn[b4uEi}E=oT/YfXĞìDo6:c$5^)L =&ir{;z`_r++*vN^Z9+2/+y;!^L١  }Y WE7%A0jk7I9(PVf԰nhq5}1jZ;1:Ptg4 ؅*Lƥ^]_n+_Wi q<ׇ;$psD`“~cNtoW~>zj&*KB; v: ) BXÞR_iKlؿ1tZmg$?*RB!4 [k~S}\f4)wQ;lv>@k:&rkW'$L5|lb) \o饦%I iy }:Ϻ >]vRubDo/_:j*z/OK=R5DH8'L&e"*yn/ݾ4K4B+s72^U*{쪚e4UbZ5+n| EebIզ)c.֣Į۞/AwͶMST=%P#*SXT)@yKz=ڴ=iϲsmveV:U={rbEM/]Pkfz֕cj˪ |Vi0́NPMQHT~Uڋe1ɓ{pItו3B<֡zkֈ%e5M3DvFKJbQw.(*IuU2nzˣGCCl$hPO1*M%[ZΒȝٔf\x#aqu56DU;ypuU>MgzVJ5lsPb='mOfw::o)Kjel,"]3X^ȤωSG/kS+O mӝ_HXՇI,T}3}Ndu٘*0 O""GRV5dy0qO5x-͚aoyDZ *MfS'U~E uJM&Լ̧R, %~Y8_*څ)U A'L#&9mU=>Yuu^dR%5AmXdŗ' x桥+@,J*|x} Ta<L:(g0lݤ{>EK|# G[KcdUP{YM{aaݧ;n *#n:$]pHqLw)0&8IǚSzq)3PBtE逤?c%I<j7 "\,NjBAOgVq[Z__Lk7x1fQ}d hkyar>U*|xYmA{, [űVt[*1ata zsER~~+` F⯫_Y,n㜚|]r5TWɣBV:>x&L2IQZ%|YY7U"݇μ;C}u'>ԔZ5qìω.K#SG:Gy GPIgr$ |1fmۇئ:L~ WBH.0S"%T6)P$irhzQN#}Hz.a S%Ci}e|\%1XؔT3CGJ /)5cX} KP|SV:*K3 vx-U,9[5I2 o%XUy5Z^_ڲUOIj 1ꪬT:WgoKecn_u\w_OEMgf\껺J%gZ/E\4ɪ1mLʍ}ȩe]I{WG;N[^jqN3>{^Ψ:JmPZforO ThO!|XUZx -*R-IB'OzX{gfI&j#@- Vs&H򘩺kC=UF}*G=wѻjKV5)c{ 8_fUHJcd^«l˦IՎOmJt*kj>W`RǾͶ+l$ZДeI#)[ۼ.%-ch#U5k\zM!6΢s^s)YX֕p+}:qȊLRzj˭) o ڸɩrYE8Z^ιȟvv.xޢv{~ cl#FgBnT2 > "-یUVr<ﰩM5|kRs5qs/cAdf^p=KmEM3\&S&w9 G%WeٗAB{(6L}n㘯zr~s)JxŬdSzj{5&N3Nq{twqJ&kw9>ڍUwV߹9wQE+{{Nե:8F eUh>E̼A9dڅuJߪ,٪Du |E]_Fqb`&LEH)G[eAEEkSl| =WE"dH0.%]kAg惨@"R;$&")?j.yzʫmDhTj|wG~+s``X: {Lz9 ېѸJ8N1>GuI_=X[8Y~ȪJfQXĬ|ܽ5C@My\hyWRuNR!rVypi<] oUIQuV[u>L[=u0M, "}?I\\)uݓQ\7@7eoۧ٠)Hh<*I=D~%WL$blpNTOL1oe}s'}rW5[ضE drN2ʅٴs^5gC5jn*I':Ҫ)dh޼E&U%c$+SG%Mk W;Z偮-̓>*)Տ:Jӎ*ZRѿђfvIMEg.0hLseT8vB~ZmPVW_iFN嵒iVu尬:7lsKꊊ5zͬȷ.)>P^Z-^?߭g'MfzL =z˚U{Go$!]WD.xY^k]z컨)$[f4jM; ݼ $̺fN4jv⬲ϿWӵ9z~RJǮO?)BPk栳/zO=eGxܣH7|{9(!F M,`Po* )1(gUƭJ76ubR _ko-CJO1 XCBQKJJJث7lbemb_,"Hc]U(Lᆚ0qJJĴ$W8Z]Dj ?җDU Ob[C%.eɣ_++SPCAB` AVU0HR\M4@-%SYAT)OR7ň*BP0P2%$sXiSWh؊!6R;U ^.Y_ eWJ>)V-",S 2KR[ S:tD!eUB4)`3jUR^vb!!B!ۚTRi( 4Қ  ) a WVhiZB E 6|ՑFkM3:RLGލm).%%-- ;vRE2(RQ-y 4RU|#>IBXU? U!Nghn XmKU6=_:*`X44/Ц3V!6TS# Uʦ48A6D!o)Utb5a!*LtXQRbJHDR)ϱXƴ m!rɤ }_=娆:ӑp!NR2e>Ht6"<޼&/ЅZy#?l/CpkH- XQb;7?7WT*"#u-/9%LT0ͽU[KsՈu˜ bZR)ж]b44~y.PVUEb")B"X:^kmΓECZF jA(|t|mHG^ŮƉc, ZZRf,/49MW.Q^5|d35T|`\e%tgMJJ?*<5RE=v:6M-$2))UX!BYi$!T}\];TRS۹EV Qֹͫ St9Z^01,) ՐZtW4xOrZ:~qJfBk*ݏ]u xXu? d3\Uxʰ L3ԟtA۠xkj#VBWSVBD!6t9鿪^"F% AfQYYԵƒek4pI%7!BP1iB BM*/V:R^%M zqyu`eoіg Th&+P IXcXD" WЖ e*B!DPq*ShCdR[ *)Ua dgKxN]+ZJ-eq. \BD_JWrL[D)!5֢0 JQ4IQZB8~'am0`ސ$!5)zJDzd"$+n\U-YJ嚺eк)K()C]g(M)hR! ZS1d$Pؚ8UY|^*hjI*DC!c*)*XJYt~(Ԑ3RIVHE!MeL:W!/P|!#g+)T(0ʕ=/IgzbҐA͐XS>C.$R]kIU&USpBvU\BBeU|4b*YjdK@J*B)) Ұ!J!]ޢla "LEaǢZKWNmV$LDgRȥ,Ane- m+ *fRU*JȤV bSEJI(c٬rz NU$wIl&&oTXE!v[J)XLtaBS!M HB,_:J/P3}[SHm\a!L{pM3v-+,*d)0Ȣ! M\YTRGTEL0!Ъ֣֗.R!zI!- "E.ᕦM50QB+Nח3ZUA75,$mK)ID8Ԅ,Cн\n4t)grMYG&.EcUJL"i4~ *jTRa!دSBRQ [mRB)&ZY*CUE(L hȕ!VNT*EiF !ϐkrܳx$(PYEQ5k*4t lUU'%\PR!JzpLX`/ڂh>1G FS*?7y eŷ~C&^syϑ;$h-(=ōbۗמ|(_0E+d٭ۑe0#tW]eJعi1\$\T9>.bͺT&_ꂺ;1.Rlqۉ]qY5.+ڶ#\d|oEg坦M8MˊU~O)CQLeF1N|X+bɸJCDK` n/nE-T]';&4Wo/ۧ,ʜNӽPrek6gP2QU,B49O(*,jYE)/Kf2oi]Uw,Ƿ akw ZSvq{M3|m@ʶ3oa' 6jk 5*2ȧ_VSq3gԍ~"I~\7 xWi5_gILmxQ@s|:/(ϡscSU){|kI@|n:6uKvWIi5F-m5IQ")>])hrV)֑]e_Qt%].+MV9ߧšnX4-iС9.O=nL3}@[W 6J.+JȞe6H7Bҽḳ(gIS3Ye-[Xn+4ܫҨ*UZTٙIFV$hOehN^o;-ʝdTR󲢬thxx5WQTMVG5.|}Kr3+Vteǁ .k Ya%Qa&Iêg K*9OJ{ ⴩MzZP/od7ܻ/cbNrլ)lGMnShU}¢t$3&M]s$5 ;!pJ)YK{X־]B{jY)&+ْ4ʚ')ysL55.~AWX&WqsQn#<ڦKYL#eYVڻ Y1]% L*YE]ߊ%9awL8ڒ8N:A!C#)(Ƒg(QBr(0n؜U9~y 4-H.IƷ{Zjy.{jؽ CP%%$UjW17Dct'3HN)gj){/Fkԍ/KT6W k{yT+~z ~N9-PQ&謉Fy&U״ĬI✱X"xjQ@Ѱ-Z]f}&Uu.#M[SUG*6^JUɄϧ"}LZ?J?D?3;57&٘y7dY6b6#Ysm %ɼӵ_}Wl7zͿ{Pܓ_9WN&]bwVr\+mn]w_ zB!\]60k2*pv.>8}xyWA-;~w5vR | ]v/aSuazGIw.!lH览QkyyiI'yLo>h^I9]Wf$˦'Oqe&徔J$X&E滗Z[D3t*iﮭU>i&h\dJ9ρ|ëѣ}&1]lIgheٻ~zWu_2o'|7wSmϐˊ'r7g@qU“l.}̼Ҳj0d`Z[@Ơ־Hi-MQ4Iˆd9ikz= uNJyT4uMD?=9hl%2cȴq5eȫVpٔ΁3¥٦ `Y1Denǿ9,`]ζ9 &7=q9WT5'vE-oE^SL4_%#6+3.Z# Zi=&:[]If-WJ1funۤ]ɴi]{I2[rܜ*HF&QW#QW$a]]kb[]VBYj_NvZ-rB]˅g|ޙ5J\P#RVVaRn-=C0p r)%F|~."#oz>["*£" UVT囫YVW0wJrP;kr5Vg!x\}OM9jXWdDPXjF&H| M;^[& _iGR+5YxEE]إ;m>r5I)*QC"׮*B1kiiR UKؼfYCTSO6ϭk^ނuj]<5j>|FY=Uu=piSIJW՛~}UшbR/uja``\/x%785K1=G;I~6jr;O&=kcuOX69ϧ1 j1u]$`gΙY2^*un,o%^߂YxZM^V Υ6 [rz4QsX͂jةյahe);jTBէVeHCPkf0~M[Q@UѬt۸?&^kzhů.SCg &sS\V>57%t"d))$$bVj"ZzJ{6)JhNEV-,dk{WKf;jitM:ņΓ[WjH),0˞UQA"{,@/ω;O*DcUנ'/9hR]6q<ʳ&6J"¿8qmJŸ<%`zJχRLYhLr pPWvu줗fL%Ql7K2?4 %ŭ(Zg~H+ ܶ5U+Eݓ3s=MmM5*5(Vb _겊 dWMb>]謌݉C:z-(ǒ:oC5\դ&]Xe^Q{*[G\W螨Q0i3fbVtwY}j'΍pCXN[|֣6#hSUV^ 'EHq4YO~9Y[_갡Q")hqWOIlSu: :M2]oCs߂TNSE-L'e,Mahm *v(A?8__*(w}Vegj!d!Ap9wPA~ 'mK\bo#M 6AN;?1nHא s=L.uUt#[{YոК}Mػc|k󢵭ifԗJ *̦^K5CaV^xS竍Þ> Y򊉺SdqNv*nzSյ7xiZ]'!5fUE2 rMYan҈h+I²Y`Z)D7GHfƽ4RNQIjZұ,2EOSŃL.b~){]e;T5R:5R\ˌK$=N}[fМuU\q3guRӴ[Xs:Hj5^JοD{LƤZ! nNq"yIu2"p B]zmo-_UXձTZciGE`&]Rޚ݆?2܁ k\4Yt6P]{^^c&~)j- AkJ;V,fmENC˼bST6 [7Lv$KSf|j 5ıR1 q=G͓`T]zɶ%;3,?MdWQ9l^S+ SkxP}˖v+Zn4C¨a'Lܬu9P{ihA98Z+Ha+{f( רoVhuw.k'v9PO5mPU%jSWKMMMJu4IJ~Uv+;_=qo * I7_<087v[aA<^^*ib 2qG5αMEei#~v-_a7 |8&s]D42%1S^kЬ?o@~➶i3JOfjoG $o&=*fYrUA #5\f%j[\55I:sPTyOHNd{bA]_*xLZXTl]5/vUJ}{a k -ZI=DG?Pvԇe4G"a{)}DLݧ5OxKǪqE@\Zǰ]uBz6\jWCֿu[Z:.Hf9EeLGIJ2jr'iD1vQ7_:MQbe g훪'p@'\(hݍ gUI;M;Wwm[ԥaqQ/jv9h_9,y6)F( FPYWY$!R-M!BbP8Zzw e~ ֶl,3gj:) ABuёYJg֟%K℡϶Ή6͛joh/].vW %#?C hM-{"m/Zְ^obTsYZ{سv>*]ؽ&U=\VZtpYʪDnhJL8pw)*B"IF 69f%3tҨZe$ DRt-UBjSLGK0!% 4'_*^#:YEL0)a^IM"тmIVY6e?ꮺFQ&t*Cn)0z^ Np- NJ;3Y:HCpCALT|IϓA^l *g!m*:i̡zxִ='x:&W)๯) j{J2KW[k9yN qNB5FԮYaBDuνby;qܾYk;l׭[ i:f=WH rQض8/g3"DI84#r'r=@M(e6m(6L1MM9P۾(5pN2i5vS1c3 Ku#uy'Vbn5'x7Q=3/(2)SGq 6/|w)۵k=."Q.%[SW1-&ʥ嶼5u볶/ZHE&?Vr+X"*L-~ԁ\Rdj$J"ͻSRZ"F&/e#iqN FMa ۷i\6/v~ |컦Qm?C~T]DOy7 bŘBgݕ5pE+y[FAG$T4wY8/Q!(>T;U^G`~ 3XڪSYvU!I˽.{5lR*pn9bV2)>t0HgSoRѷDgm ļw2UR|[KU ?/QеTn)I(~ܘ> S=gl_¸Uw25yVm˧6SbgHb!C aT HD\ _@N o ƒ35$5~+CG֦VԑTTE֮ s !gk jQG_ܪ3viMEkujJDPuXGd،Saáb.TyŰ ŋ)U !,+cnR|JZiKؓ(.*RWtSBCY.0 [+k9Ú ]bNUunMe~$Wd%c"!!T!:UeEงӕ,0|ta..)8d!u D7"ġae tmϞN<*n[u]>W x;=oUr]-ޒ۠M?) %ʩysLU[RV*@Y)-sM=Z:^.2z4eR2׮4馠8d/Jx=vkeCM%eZ j)x60+:Tٳ8ʱLU{cWgoW pkAϱ_:Ɖy&lݛ+y1P{~mgBRT~gaծ/{6n:J,B(SJi u׎X:wFoթi*F#ȴ 5K1 VKQ؛.A%`s$7?2 c]gG.Q}HrR #|{OۓppXoB"|*n#iuN٩~[I;r-_֟E͝wmJq7ELPea) +ƒhɿi쟪ӊ,M۝FGO, 1 &[9G&Lڎ&KII( 4 V5@pqa7TsK'iՏ;oA6BhĿG滾"6 i9|NR%2ͣ|pYAl[>̱lӞڹ%g)fhחerؔj^j-%oΏ\պkͫ`rJ~zUϟ0.dѿT5QS^r-E.BwO'>zZڷj 05VO %9cjnm\BvJc˴-W'Atu[Tݪ w^Ì,''nWR/ů}~,ʼ=S%_SUF1>KER%Y|x^*=FM~:x?R0+M)J1*[zdd?ErIе˻eKlFEb3/7_==t[Ԓ3vG)pF\ )w\m*ˊT1V%W%yOVmo' WG`){͋zHGS4UvM(l@UUz\ص,>&ר56yKpDC}vUoYFx'7ԋz 2&Lڬ+YO3V 3s:*l>Z°BokRӘ& Xg/ J_VY/kf|-jOМ۰Һ̻DLn.+nt\UѯNsiPk3Q7a~JTa8R-}M.jQ7c1Knd4ZMZj `[󟚄 UL.E+Ӭ8=I*h!ޟ"SfP&6JClƩȾ5(/ʢ฼ݚNQ;cK:>ټ\K* "5aY`jȏr_KV&lۤ7(`/$aYq1\4=SuKoM0n=}IQ%3SemWkuTZn?NgY[u9qc4Hl'J8VL%ycd`ZX5^ZoKVΚBZl}:?éx~Ŷ[np;q~tDkk$UjW*(u'kSkOVZ]e{)6AOP5m#n,M5HO'%^oӿMQ?gUxaw Tw}N% QۨceX˺PQ뮇A6A{rk{M_k-}1I%Z yɒ5긏i|sV\ v4h2S5OI խpuNQr?KmW&JXkfۏpLL (ǥ[M%}oޛФUb jȥ7IrE,iwRm붻aJ9+N! a$veciPaC+oKlN#~Ezo |CjQ^3"R3F~3Lyͽn.Eil}V位BYzwhlc|)KVf_հM!RTzpKaEjʒW})/_~WY3ξN^2=w!%{OŚK.EiŖdߏ23zi9wJE6|EQE=eTvkHH703Nf} m]r ѽ'8UB֬dMYr/H7 cLXyEKTY`u cS~ҩ5wF)曭)]{(PzvBoZLZI\hPBMIQBH)ZI&TRYhy-,SlJ'U/]Q\l-#E?{uOҫ>f%Tuǿw-^ޭݓRMۈ.2CB|R:let?bVT)hkkfSOa 3;)}΅Q{IYۊ J8I(c.vsx$ٕ-pUTh#猣0rTP\].ɝӥDbv-E[Hu-|OhTSGEQi^J.-3 %o-4ܼHrI%n2h5pOGkQF"RS 0NB.nN2l໱4;VLVc.ACW`>8/A8!',0+ )]&D_ԥ^ՈIVdEFAiT(Qr D*&D>>#M6MZG5L9_v ߷nSOBxl˧ڂl l= wҊdz.F1世>D5ۢ={/e#rz1X'Mbn+ֵ ?4,{Tj/Z&2)a8WTNDj#;OF"23oX&΍7ϰ F&#@%躊"TgyH!H(ͮ"!yh>5UM?$װE'h'|岽ttTBz8'H٢)I6E6/:W7-CdG]ejLͬUo[%c@иc!:Yo:_R)G\j#2x342M`W^ۄ Q>S^G5lU"=Gn;tDK5wkxI`5;jABrz%Oy ] 9FQ+#"/]A&꭬yR 641iyoQU3mkҙM%_8AK|K9i 4!Rf=YRJJ⺦k+''jb6-[߲7l]qM[Oiް[p_b\{ .c|xaiBU]ѫPI#"#$LZ5G9W9+ң.="ݼViE0-;Lt6I%Dy f:2#zƽF că05q+KQ4s@D $A$G2Ni߲0k3B_TkQ-E'iϜ#b>ͯfEysעþ']S|߃YORj8f*t/Z_1,n!?zWӖ;pr&M<(ϚB1~/}C`߳s&U* Eq,ni+EJT4Zܟ154O"VWٶmLByϼƚ7;/\ك%z,V0FW|vG,"p"kbvlk"ڙȖx [STHDqdZI_&ɏߝ5'G^_iͥ9ųE37)()h_2d>U1Oae)WƖ>p>E)G]5tGm|2^wkpAqU'+TKʛ(zxդvIefM4c蠪mY|")&ΕUJk(-}.L_Vǩ Z}8S4,ad_&X)?ws TMk~b ϖN"-gMC-aJ_gɽEɿ] ;n@,{ҠҬ,PjXZ5 FӵA9 ȈwC%cq~6?;muV5#*3,ZtkLWߣcS^5dtes%4XX r,2ُAnQUuNwx[+XT^*9%,i0( OǀzǨ&0˶r̋K=ܫnY?UnKYM o]Q6mDП+zJ_='/<]=M;`I(޼ خg-ScӷvEmc%ʒ߰Ž(nU2MJvOWնL1+L}L%ȣ['}zs)yA;58\'낰8hhYAN6"^9L~Ԧb1fF*jʿ XilYoZ,S#9@hۦa1"A8Du<`ԦMV!MTv-S5Q ֠8jC)kY62N?- Q[|X2赧m)zt3 D͟ian{[ڽW^N8A+g7) ;N&^bW6&;B!1tm0/;Ե۬l=&`aB}NG%yKA;mnp$|(Xd<lS fJ)B-4t}śemHuTZ4 - #U}}1(;hd9k¿<*Kvz κfɤ^m ڴl pRi;uEn̆8K+}mPqzyCruXۭԣoJ) ^i@lo)]hǼ6>yv?Y~W[ԲF$8M@,iNqhy SҰLnk(* )|n$kX775%5iF:|FújQ8OQԦ$RT*wӍ~Vp Zo-eM 踋DS NIf.2:$ǽpZ5$ bmҦ+PsϑoqJAI&8Qv1*t_'BrX{Y6NB{*>uYϵi&i_K̕s8ɲENokkMUu(6J\Py ]9 Y1;}O u>[E|F%&*d.fIi: l 9i) ORZB)¡w5Wu^KIv gC%dZ򒿺|w6EP-v4p5i{v!B׻_Q:h~1zl, 2i3tpC0 Eebz+{3I+QX55+ 9tv9'Ap%b%PBS_ȫpAL6oJ7oC1HV`i,̗jjw|miw1^m}MVŴjr}DΠf;wUpChH?N-Sn*{WUcv|Ti[)0g8& -C\1 #Kwt'}1F)PMJMh6 Q[" BV(J] T^ro8Rt͋3ɬʻBЧaY7(6Mc`tV9uBT :HȊ#Y9yO^ƹ ;PДu 4m#4m2t2ҫ%Y`)z%0,r$/%筩Q#R*Υ8͌rnM)_g#"3HuIv A*.)Ta#BnsƊʧ]J=oP_xT bԙYLwϭ̫f|+%v֯E!$--6ϴ Z ͔|B>ZkS9 t, H 4%1mB\SnWU} HQaN7jBeQ7mĻ 珞?s:uPT2@ײp%;b:Rڳ(+I 7_"¡5HA[Ӆ2s"?|ӄKYUXu(ZtR)Y JND|lcdϻA91Fۙ)l9kC<} :>i<.d/FCZKu^ӮA}򲒊s"X\v!+mɥd\Е[GJ)Jt0:5:{+|"R<2O~SHgh6FµiGbɸM ŸE;MT/͜= !$RhJ/xd<SgB\RxU3ckk#.wbPP\z;zՋL[7sYW_!!-)3.q{V~RŽҖ!eRJ !JCM?!*CbUG#X_^{KB*~5iZIr&s14I͒(aPruKykJa"3gK5 9+Hu(tJPdQT5&=ֱLM.9*Hg-YԠ Vxܚk˫ZkjWSr{LjYM1*vۢK\B"&lgl z׌o -dL+ 6%pV;b3"?7석,~>Ca?U RžǗ"Ί)f|BPe5$f[oڤTm~t֠&Ov*6܉[ZE`% ua;_oٲg[7+s6':v 66Wtlk;w7iW;+[oLKbs[l$F])s޸[^aEE"Jt_y*ehv5Q DVf]BguM׺ӤL]]ePky n&8 a4sFPӊ҄>Rp_2^#2|]8S4XU6)5m;8͓7ɚK)e X7Poձ0+k6[$8YԧIdS Nu`YѯW\ռ[zJ[v4ړ,U,*J"О2p%5BzGYUg Jt#@*ݬʼn2Rg c.GН8ص$SuAXRD[N459BS0E ED͒tF?%r(:,:VUvcFګܕJ=mE$[aR ȹ1ю3fAChS6'ѱ5s4\7 c26fUU!Nb:UFU]dSږ!rd;/=RX>b==~65IKJz{X`?)6|[]Z1! %UAi;,☞6’Mj?dj%mdblu]G{͂5z2)mVGn jrIೊ EX8Vgw_n*JqYV,|ПBTMK͊.Q>#Z}O+41Y0 #+jފj֪1t6UT߽uFF'lodua*Y ױjr-a |`XdI~46'zQ'MPߢ;vOIs] n1lJJ) 9h]&%TsڻHqf"vn彿u_09]SؤŁKhd"&^9Z6uO>knWZ tYj /c_LP)=RR+nԔhdvj]ܛ n5&ן>#z?1,?K٢4JNCq6H:/9U^M -ϕÇ"b|W`5[ҵ*q>ך"|.6տSNԸֆG+t&6qn,Y[J<&Ͼִ=(v-Qjdݭ% X%ά~ۯ~kBmZ")5~UuAs^ *j#3T:>95@PW7*fAHvQ0oODvɔ{^6of1~^ 2kHҰOQ-xÞNC[E:quU{\\~̿u] JuU5kp`__Uw)?UҚkoI;4dУR'̺0ehVwQ")s6Uj,If@=4/eU^T*ʆ,%_Ilk.H0P^V9~*>UU Oy2)"ץ&R_o],wjnj)%}Ucd'­++][%gXU=:K]Ye!oN3N.)41JRjSoċ\ʺ/M66-Kr~]2p1 a+ZA []WݡuseAGSKVS<I,or;sJ3<7AE=,+#l5!2/1cr}օ=)Rڦ1]qlg2"QirGh|,%!MKu6B`*c`|H ®Nx?TwNظPJB3Ld \ +@baMa [uƒŶg%&tSg~/#<}ٶ7'lpuۙz{Yμ3I0'\Bl&Oq\*˭GʼG^Ci`K)Mͫ[w@أmݵ$V:M9fg(- ٠PYP,߽sԗHrW9/=J}m;(Y7m+z\=`F`6`^w&GXy'½8xMKN,,relP5d|j&ϙO5};~T5iI~-˦Ŵ f(˚iI+k + Zѵx9r}~5^ܿHw- l ݕsaj:¡gDtS>&_qTԛ}?ZMNKXʫXJCOr&TQX]ȑ=wW[mMb)JZ^3V]yL:c*GKŸ|-sM"0T`w{xRZ؟F!LR++(4zCNЗoyjfkZu`M^2Bzj*]_TcV'&6L ec֖YfML5xFuf~*v> .R #UHĮI l0Ƒy-zjO{SN*z$:߽[%NT4~^kY_]B]*KI"'CF"pͽq˭Smj4v7 %ҭ8OMιU~AbX%0%^kZV}ED'iV,QQ1 ֆ++zE%V^Cf;=EdGZ=OI'3&)=i!)%. B}.Z/oNeU 1Lq"l2i'Kvڐ' -}]'qMTe-/(0)1ʴqm0bŠۮ5%~^),?b E0jvW]e_'.AWJ-I[^D^pCVd/ 8꿢&cØHuMJQA 6"%^5sxrvqH"gNVF xշyk#,8jeu=~ɬڼ'G?J_P-O "Тk +F0vrY|O ;v} ~柽g/<c1_=8.#n~?F.Sw 1[?Gma*q?a>&lV]4=:gE{=|F#N_FSH8c̣`,u lnk-^:G˲hf&acyί8]U6ô:ү1K7z9IWةIU[v{lBE1S2YEel`ֿ%D{Ҭ"#XMhn#eU[A3bdА+KZ)AP6EVdYQm|RkԈ6t,dZZi4ʯ>r[+d{l} >ZDj7x-n(4MA=lTLִKr%{_ţZS f淬{\xJSn fK6+䜓G[*pj n*t y9?KĆA_WO[͒q0YYK֛OI(EoVN˫Q%Fқ)w璌ڠ'2 B\xv21L4^E92'aZ`wz0)looki4<#oDŽWr)zK`Wf=*0tr]'6A&zSKC? ~"_t?u]_^)8V3?Wcmih+lY*iro޿p[2Jm>WyuR/zB]ˬ^29DoMZ{ǧM_Mi"CkK+.0ǪjT_2 i^=%#jHr%3xPõ.a6(v\z *-oجϭʪ1P]28BҶW9GVYGVM~ CE-"uusP+8bJPDI˸"|,-C`ZdAcUO;nJ11z7_5^zdڳnNGea4gpm{j1v :gC[6˗?vmV?Ǣ\mW9}kkd}MI>d )FyOǹ>+3Nf@=oݏ!.e[97 3.,sy)8Li^Vn9io^72/vzV%HY݅^.2%}2ŋp+iTT% Ztr%iSk֦jҳ)MKU3vHk98jVw~*Vm"&* iJ%EKڂ=ŁO}'΄>@qXJ<Cjr/]9}l{_(1_0HWfov2֨*+[%-t9Wkx-an/Bge$5JJJժJUU)URի !iRa׬s_ecM۴ro-E~ y1qPReO$&L?X7M%z\ϗ}IO^hkM? iz.1 "9]RTSPVDFn(5;`UEL) BΦ: F"s4y{ {n ӺǷϩyYY%ycI#(sy![tN Sk#!nS1m `߷MߒԮ"ɇ'w4rZEJ7rΫ첊+GA~h?%eZ|*&k{AJ75T':5*aBԠ^]zq #6j<gqc3zT8%+I} ͰROv\<.)YXU=^_?ݥ?epu=pyU]==TNg XSyiqWVO)3^WRè5*Jj 2A6 {K|ږm _WY6{(;h>gg)S|*⯣hT\[5UYTzl/KT`3֐ŧKjL}1OH3jc{%uff i_\ UUTF2)DH6qI[ Bgo!aH2 r?ur{9U)_[UEJ%(&SCk)`/#"9ԆA4'mmWEG,#02*iYTZ`PN5IFKlu" jsJY+Ҋ_[X|pW[K"xI7=?6WnuT7Ⱚ:~Fiz$FQy8?xY$#!dZZQ^<UWs* iOT7;H,L#"-ᤕUKPQ˲=v3t5&BhTQ,xN~/P99 ̺*{.̉\wWE_3hI}kE$JlHVSfjJ|xѤZ!3NVfVxʜpT."9 ZKh/<пX7O_)ūjmW[W򖶍ro7:#O}Y{ix޻Kho݂VUUvbb*SK=|vYY[]uq&.r~Da #|>B{n#|mKf^jahv1]~J~"6?fUv86 3ͫ} ⾨=JZGܭYdk,TtN+E] S's?gkی~tG[RWIyh55mjQW;v_[/;MT3M)@]S=X |5-'cڸ')Kb+iu ߹(:F==VӲD:#W(lH-RH=aޞUNӷig*Z]3L݆hrEaNٚh*}2Cw=wWwY7GFG&+ZSH~gU7XԲoSL%*LFIQ[zԤ[9!RƥfU|(k;:\*N?Y/ mMUS5:eV&UҬag"'Jj0dK]Z$ L qV"l8!{TdiR`Do|4YɑT"[Ĉ{$њ!0UTCsiB *96}6f"FiZ.%pwf1YYZԒZ֋;_|qDѥZm,4{1H};aq,B ȖуK-ZUMǣ[:XϒZSv!jI6X 䡿*o-?bXORf}oam5a`Y|"=ҿѩ.WȇPZ1lXrN=$^eRJ_ܖiC6HѤׇ[IQeqY!kyF/-|I#^pg-JbUl0*ITWKGb?}Xl'=F0ωUBh? OatvkW=+))\"TG",nު?# xJ?&NΓG?YvlS;r/=粯#`Ϡf&3͡c'?y}`~>󈙯 dGw1NFKGX#=O5y]KO^-eV5ճg(K6N+BJzʙKx>2vD[l?`[qEm5 ui88E,fr^B/JYxeRE-+&٬pv[q>&##5mn+Ey\D;]7)i7N_?PN]Ut#nmTUlܖI?I1DM޻]1%?Oh,MHMl)),^$E6DˆݽwsA{V+ߑP[FRq4}5IIQ5Hֽ|BmM&zZCCE441Y&p캪Sx6)CJԹUYBirLՍ,هSHUrƠ(YSD8첂XaZ1j%-y RoY1Dה' {1f4KtRc>G]Q2]|ߵ9ZDsxX?tɡL-(u04EV$ MuU4ߧE~*p]կqLDYTfe>W=&۴X,խi{I|U\ȿ NRt؜l)9R'`K FKOq]zeJb)YL-FIYTxҟU˂I)vdZ8oܤ4ҋq$*MYS5IQHP- OQ^ñ\Da>9΃"VdCVXbh4aY(K/L xϲhmF){:w(LDio[^(ޡ,K+=IɽzN:I{omeёy5{m걟gDN[?1Ś\3W^uۇUI:F+y uɾ5"Nz&ay˅mky'ﴟ9^]ڵ /'߈?:gf %NFU0bĒ?*6.7xOyWW-*NtݾW{nF1}*k=<8cXȽUThZ=>w=֕aigZGUJaT)KQF ?c&\@KdJ=&,/*#L^O4A/!;mG{GrGZ%9_ނ/@H87l?Ԃk>uN{Oiua)ܛUiS{ԣz #Qe*I^_9mc5I:[O\@N_]Lj]zYg -qo{u<%lmT,JM^ 9P^GqaUǬz#TxKsUU5\ѯm n/Z}EgEKfuQ{ʪp3(YGCjUu6~ J8_DEsUt8Τk ¢+x/ d8"Y8_\K7N2lr\޵{CK'm3.߲vΰu%)Km؈l$Dslơe6ZLDS(Ӎ7eVUU ~)hRF m|rļ0'HIL$TYVMnD,°a"^Z$Üed )BܤՆ|1#u u5YL |R4Ȩsl&[B3D55Wּ6,K@Q^{mu^QFvFUiT N#JYv^ѮC#Ս #inW^{$~b܅.3pt B^,7(L*+,sm|-"6):j3kЊ>hӭaXyY7I]v7;ZQWrz lJ\V zʹoC(SHx&QNinb/ˇlld\w]H|AQ69d\%)um".k(Q)w >zh_3"YlXg,hnB5@"5l# b(*!I$DZ3~/#I>VL4n+> aT-{hNcl*o1QVĖLF}UBiPwm 2[Jʸ\z_ n1pHN0bnʨ-093z45=:̦^P_vd pnjSqȊ&zXoS_* [|rն WȦ"=pE3Hl\g!vi ZQ/JB=IGGvMV[ VM"kѪC@3Ki_.&'Q56z29Ug6WGFajeA[/UUS'm>YCOQ}5*HcW3]+z|Al~epqɷG7*4e |WVW(k)k\Kӆ7wTzTQ%*&5WXu3mY%[?FyF*^3t=o*[)qׄE\( \$( 1Xg jK4 kXo[JX՚ԩJ)4h z!(琨&Ijh')&.C"lYHF{*ki4m7Yp|kPBI{bM pV}._LQ"٥ٶY8il ~,i"]qIC6=j;m#z-^JLIۧDbѮ)C")i{X>'nënZNT RRWUr[yӘ' b_^Uɲ ǦKY/Rhn36\{ .ȓgy.~_s1s8|~Bzlze>lk]4O vRLteDm^K,-Q2Ox\mX$Z[|)s~izO۵%Wҏz?1xy?`ܘְ<*v2u)N}eCR&(.{m6$ܮjAP;b^])O*)E=_UU)l#5 !h{HͪEVY߿բPK3*}7 yTz#D' C?V%wRqh IN3"iFu0'M"\ݔEL$$ԓS, 8*䲢6zd2<-=JRJ⊰T>8^YYٕ=w`S[SRU&^ݙ[WEMK~2hܤ2=FUp#2\qP{ ʲJ)^iZdխCj8QDb}vBI-+NQFnhOQHn[™ IQڅwR cMT4Xoq@_d'}@{>2t?-FiA^|'7{G$WR[l)NR% _Mlo*s۸O=U<[ϣzF]V*ϡc6f^1|uvܾgKZ!E=6:s}DX 7TIbarA3uA@F[_"ږyvW5jKM$7QZۼ8l1_LL Jm*ڮýz]RLw]^#oP{oPtqYB=)+xZzn[F YU_Jj~#"˸[S4k-[|wpvm[9@p3EҼ(3 j"D͈Ho̍w_\| { ȫӦuEb>?-^'O|L3 +UW-(/U[FTKTa9GH?TZa=b:kᠳYIsKUW8O)I*t͢w:%Q?D\e6jzSkM[亽,ղ뺺%z{ݠG2 . ^((A|I,ښ;z,aBd]&QzWEI|ж۽4,uwD'47&"IORZLF%o'⌡1J)BZ%IEJgkϸk>^U6e]Tr::Vo>reTEX &Ky ;ʴAqE)v#^^^r{𴴤ĤP;՜ufOՈS$[_SD[˝[(&h N&HK15G3Tjݢ4dQԛc=WH XiJ?*Ukr7ebQEKtK.0UWHSMURB)MmsyL^RԺy>1oJCTRZ(Q ꥕(ҟU*e28G+z?JrJA {'4k_~Gnx[z6^-\ @l[oz%w\t<BU})f]ZHbACCBrİO*KַWLcXkYH5# !fXmKCB3+Ŷˬ+n2SY}̂1񵙕G|f[7Ǝu`KGm~`;n.  :L$M9O~E[2׿6kvj,r <l]v˸"]nmc^&Gr2ӑk v%x>i}3j:FɸGͱ5/ޣ P"'th i,"?L:ɭc~do>k HBJ| m9vh{~î-RߢK8&]_T_)w]Yҭ9ۣ0}/'{7,0[_ J{O|n7AV"Z⧐ZKJPp%d~7)>QuxdM- rtT= #9rؤw_zI ꯅjHh\j)Z,D!!A*r,-_ւ` )b(vM4jSvu]BM8 VȊx:U]ڧܡs.<䕔`_VѬhECy0Ҷ2_VBtI\\qbd,B I*ĭAtUdU\:AIKRh)i3BXZDrwt) jŨ]cUN%צE"ҖI*LH Ct)SJJ * s*ķJ$%cQ*_&T+,$BB436Z aF ,fȢ30vuLQik(`rW)%GVԖ0e.6UѪ *u.EWQ9xSΡhK$gH8U[* &)avIN.M,!K*ASTCTd-8HJxxʰ)b!x?Xj?ȐqVխ{ P6JCz ]5A*߳MU襻nIjg p*vVa `BNn&,B1}ijmT>LV4bk[bJZ[Y=%%$Ivj4URhsV$yj6g Tv9.sko*?]xxy]ĹԦC.) |H>tE"bًGAAv.koȷId[tRv<5(@43|d7VF-X*hϖ7 vzSB'*|h*F(N&]EWS 3 g$$nOZh5: <,T ?\?|:^AluTgIrDߌ ]f׳W%LV,;mﵰ=%0ןgrʹ{X{r RۗҪ&'%xAfj~ksQ'c: t?Av}Eml]W&5qc٥Nc }BYi]8KR^:MkQC~HiӚ#s)ryߺ/)OWlFy)Æ'pK>YwC%?E鯭 ΝM4 UaFer *# Vw>"p,?sE% "1/4v(ʚ)؊m=m]NU7N"׶SFzzdu' vSU|cRK%xڳBU?4]% CL4]{ vu7YڴbA;3 aG>E6?ITJy:$?ޓ󴴽g3Tm z{lZ?SkVm} lDz)3=HtF&A0)X4 Dan4n+hA`u=e{Oa%$/I z,VԆ]OӴɘyұ/o*(N*ͭulS@TgZ ;UWƏRts u]T2* ZUVjv#ӕ!j NJ(#)J#&kPX_b- VbMìg*=۷ivEREXa*i-s<ꮝ,YEcAy\#E6 ~˅wՖwߣK鿜y(܂M[$R((NܑXY5KtsZԗbZV&7f*_k3jdRlzˣy۷ A֭f9|#`ZB)o[JK|֩4[վCVyBa1jON~~nvre]FAza.,H|gӘ徦qKqZԟ,Kʣ1fcZ6!,!x,/cZx_tPl<5yhɷH2L7Dzo8|O*IRiMTa(XQN}:MpOtM"*琟,"T,f|@l rt1,U([_6}zh^[JtʪD[ .¬Dc<{ !7)iK=AHVٟd}o.Y\T_߳)ޜ9M}j/UeΚN2JBQb^Jٔs*1}xzTiQ&N %MS^N=|vwy9haoq]3O-J:0IJbQl*5vWQ훤&Jb-;S8+[?8"uy~]'1k<1"W[{4$Y/5v$g]]ljJ]L5SnY$ 򠲷-3]Rb YF."(dK^&k^%|MvErSFm$c$/y:IeX=>F+QjSۺV[RU+s/1}.Q~6Fl.#M Y./HMfzjbIlet2)("$83 J'k Pw0%mtSFP2OX_25V#-$HkNQpm($u7e{Z-^ȻmI={K{92%(z$(6L;C{'W_{LPk͉X]olP0P9$~+vG0Ӱ QNy|莥V^yl]M6`C 2-Ke]ƋyH0I xZИG&"G.G)'NQ ?sZVR<5Lj R=*Aƪk;/U5rDGUY=-dݪjTikS=zAE*#, ?o1TlNExYDlyJZ\|ՂNcT'}A١npa=jy )wKWښPu'yq{>bpNlj6Tg|9kWhǒCGLJFU!J"pټOkb53 PaזkuV7;sݶ!QQP=qQRZ1Sj7#v~w[tWSi{++\". 9mܴ%lf>WwfQr R~p\,;00SgZgs\OmRu%z d"]+/C$)!r{ue{j~=P5nJI0[9.7);~I~Oq2-s+4by(hi[X ;%)S_-jTۈMj132=FO]urIfoC<5̔`RhԯEgT@xjٱI5gyeAxw3j5R3udSz^PRͳyE31DNUE\Әjt4by4'ߵ */%9]a%`bTIU<؊%xcj0/(Dgq'KKad+jɨ6 LSm ._n)ɶ!+_wԩGEꙷk}#U&9F|Ko]]Py_* REd鱬s֬fSRn)}˲tvؕ2F+7*&9 \&LYxJ)I8J5gĚ&tYV{zK&iUO6)j[+}(4z;Tf0迟5=kX;|$ ~6e*[a|J鷟F+OY@ziRthTeѩi\3!y/y;Ts:÷Nb/+9e=-+`ۧ]$-k4lYy+z5mFy}-5iX{ǻ1rXU3>KUTCپdvN2s$a>fzmVk[Vܟq2:4׬])eFhH%LW*p#oDwU*QZǠ#㺴?X|h^Qp _pViJqW=eq7ujW̳MjgEK<:J5YK ;:í-{ӑoh\tkNԷ˽ZUԲIQ\ Kp%ӎT%>յeуSޢQbP"OG @=<~jé/Wrf̹lC|/.%N^ύLeRds[ǜ'-М%2/Mڹ?AKma58 2[N0|i%ijYת/1=Dc@,÷!'q]={6OYJv- _]^=qe*(ЧÝ:D=oԣDM*˦ V;hz3ΧfeܖzQgk|M p2WFE( s*̚znzWMa^_;̼ \k^vXRuڈj>9.\F*XŠ} =9ǎzԳ귥.J6E, ӬmGlFMܟ)ңlɓ̬Iߚӕ.JsrT'R%k^[ծKaZ[B>=V^S ("o(iۜϯ3a}*JL)Dm_vζlz۰*27k-@=G/)5ITk2=~.BW_9T7Q:d߃J>WNl;0æbht2u+mxL>V R1Yl l#S:kv*!lS&ϸ6\_YczkOAڱ'9( f2x1.B& ei'«I-250^*ەe)sжLU7bС'I?_B'HoQ!zU$ Bx 0 #qع;{=_th|NdLzzה{ z~޳|Z6`z"?=&oِ|sf. nF0D~^bvEB>DK^%nVrY4cO e1~ʪfL)m'+Ɔ./(KjzVijjVu}vb=5cIS?Zu|ki[DrEX C?TU8"x̓xn@sqS(ת*Q%5ESZTϙ<"XnιZ61(SI&xiSXVPkgy(׉3:U4mh?* zNq+aN j׊;N. \%-X+sqm.?|!=UˎtYguU<)#l2 Uʣ0 ?gpFOj3.9U?c<Ӹ%p0/شʽs>+GFXսWRf5,<'Kؼ ">_874 G#p`ZDX[u@/L1MYٝmkaq׺(E}0?3ge퉛. ~n}0^}k:#;pKKKUE>y.-d|jK+mxק<7p_&p+0r-#ξd[;+NEՓY(zDsU1F&HIlrG?*sU1jOE"3hkWd{L} zxl E$R(mDSK(!=&rqu)ԢޅΪV˒ҿg.)qpPrO7/^jo rAOaoɣvN][n%@ױk]#ewʮ${YK afϗz)^-UlUqi|=9NV]=W:>-ClBrc7XUr$:4rߺYIE3,%=*")`PӠ SN5j[n۞-9UMA Ym\?v3gM/bmcEvSkCZ]T 5rJΗ:j^-E9u:BeKhkv)]"3 jƮ˥Z9ShpInHl(5 &jUK񬤜s5 aVթ|UKGA"w\Rja>AOY=3P)-s<,Y$ME콅UMG-Ƭ>ze dV%Js,eD2*#0L`Tŝ9i7U{O`"PWeeu/i#j_A]Xۄ<"gBpbھ+=M' "UŁ'oQ]a$Co.)x=kպSbp,c&/<_ȺsS)"fFi&bUUSZTYUֽEͭK@Xle}6Pr~(㐝#jaBuk˛Nt)+KJV_,5g1FTUr6gPeɷ9*%u?:/{5y>{6T|)ĚY~L>dl =R'Ħ*Ҕ~(4 릥u}8WvQb^_ƪHJ2--: q rMt:bוCt?PC?B6C 5 )KrQdp )5)nI UbOYt Sߡʱۻ[v-|q~fk؞z9x!B­Czʱk2Sm+qH8m}fÀY?+pjJZ/N C3J3iGێA[!B#5q,kbӆE{"}-sST']J BĸO@ģٸg BP7az!fQBnRqzNZ XVUykkpljjİ}imWYVU,/Y&-G yӦƾF%CY^gL* |x* b7)p-Vi7ZYv-*CqQ<-HSz.+o.;0kB̓M FH{C$ܵiF&NJY,\EU`{ˌ,3!Cz/)(GH~ cT_l Ygl[&ڃ?}pzA3Hv=5SuAZ4J1׼)^S-vF2iz'iSuj*Vֵad~͂vFўtYU&%}0jⶮ wj/DNؚ~˷K h7wIJ$2ѧP%`};Cs՗aTT]hxZE?Tۇ$jA=i6NQW4Tme`ISضet4eu_XP*j޸6Q}G#6 R]݄&:otMN'V60.F[RW=lJĿAXNRDv;.,jh3]&-3o゛8)Nj(wкriGYEJvmS7A'GP'mb6MImKY⭎)yaK "Y0짌 <1 SHKgqJ0n;ukme՜PUF&oh<"]ӎc U =6jh2d]Ж@$FBpROuBFvm:D+*c^UY=fS&d0rs@ӨU3Oa"/ښpgx'KʡPa `Mrݫhu6!IҐ }#v:Ef +ˣXAqSZ;#%X59W*;߅|eo^~k.q.maM^[|^lŴӞpoKk1u{Gey_S8\#,sIx_7Hypw9GnacDb7e.#QU=З8Z䋜8lkzɰH)E1U}A_WʵUld+koMP;8M#Z=OGuNU Ra7Y7߶(ŽgZI5 E~o ^,VӤW7kOSdFF&*$:w3~ְuѹc]^/2ٷUqt~k|O)2#xHt.ȕIgZz!.jjӸDFud6 IlssA81mϢܫ&̮a۩tMqv6851Ddkȵ LZ2zcx)2O4 K'L^o¸ :,=T>4"5vF("Yb=BWpTW}Cҵ,*=WEF [4~ŵ)Yu]B]jIx0 oUdЩ(oM!u̻Un!*)D6}Paz|#Hl$~d4(DNȯ578/1/%9:S[J񵧡v3,O,s˞Tů?ˀGo/mcƤf 6AbR>JL#W9C$ګk"HFդىAb_YWvIT{V[Kp 6^jJ/JRr]R/^vB3߰lBޕLT=5XwOUƦ;ZX{1 hRpnoꋬ yZ8U9顤 D0̚-EBտ[:"5,Jw,阦Im">8Y~y>V3qZ/͒\ '`˽pgOɢb?[)ESS![PTSS\~2-~[SS_КeN86:TijC;0LR~;!p)(Mrž4t[| #c`,֜ŰMvI*܉mgmn{\QKҔ"Yv]`񯼴 /Դ15A6Hx˺%dZm5PR4<>E0y.U /5$GgԪk2Ʊ%զP/h4m,UhqcGqM WyB}j~ '΂v Ӣଯy䢰-hKf=&ӄY=m?PP 6W$O]b(L t |;*ٸKU~I&iߡC;څzgꖦy֓ft7&j e搙]V=%{?q+vs6]=97z[UWM{BW)|8&k"| TohTp4PlCk _H=b PQ_Jh h`,SbϡDtA2LSv{O캨$u}?ěQ7ݫtu>=*NȦ/N6.C&>6a{m"V%QP1-T `֥ Cu>kwܯw< 5ƺjΰlz*z3h'tb@%e"F%,>Uc\5C;TZQzаu=Lo4q\kmf) =) ֕ѺNP.ݩMgW;cQ5ńt9IY|xvl=vVQ>:-DkWfM'aP'%%)mv1S䉉tG42u~wħ#P7Wc 8oŕ^W{DG"r $Uī 3)x \ͦLSP;UiD;Q(+ LEw^j;V )oseXd,Pӷ—BK1|O^⽫!V24;WugiUr]e۪bظ"6Dh25sh! ^t'6nUJlSrHua~)hgQ0Dz"늂-}R^E^^Ш,j+ {Iw2eabEK^#VWP*;r7gYxQ( G0}L^b<ŅyS_E] MRi.#O1g&U+s%=[[_&OmˍaM[a{Z2F͊x.o#qȺ1R0 #ͪf#`6/Pdэ{g[*{:ua})4ʸ'(^ Ys2įϪi*jn nS[vN}Rt ׋kk-OS ~'j?SyzBt}!L {JUit[Չ{2Ac7蝿SJ5F tу&9Bmcɴ.Q6A_TmR)ʬfbu(Q145~j_`ҚUXéJ< O;TG9ہӤT4GJ+2o"Ph/L˹2T!lRw~ᑨAq ;(ï+3z4G5!\6 p+^冄/-AO~ͣ\P1lږ(߾z2Z깮rj ˆrVdgCl#[U[5'cVݩjj2XW]S^kV3RH~w/y1&ǹIv7WnYs5Hr@FaF~l7\UC{ |=$RgվUm]9j[n:HF?vխ%:BU.+wu[FzW۴6=qVt BV c紼 cxdߛ7oVlgwk;Y[I0l+Te T("h.j+E1)1oB'{V\.%nqP޺6\ˮukH)p]؅y*XjYax5-NXSݥ=(OhZZCyȵAOёJTeqK}+{obpʿdU%l$VwX=Ef*~>wYk<׾К4U\k*O^Le|jzƯ>'?5&ܫז*lCMX $">s~_TC+ 2KTτ.++R~ D)LQ"uJa[)A (VE>aa;kÀͿG- A*ϵ̳gr ]>~ +DymƻgqGzːa8='a-_;h 85lzNB;}&s~o7Vrhƻ/w#~ dܼS>}\=HO=Z~u *x0tG(M<~;>zG6gQ# o}7>4]\1Dj6Mi]F =fǁ^[Y کiT)iƩ I~Q4jFНך&6TRM} B =QQBy^DHp ײI4sPyERW?SeN:zti_1މU=MnzN 5Pg}DG2 #+ UVEc$ZLolr:kIt~akk%2nŽNgVSgK'@qz@gߓtg[M''/yDm02髏(89U/odz(zm i1SyA@]"vD̪ѭlCTu5P|"'^{jxV)D5NT,3zbŠhUU4͐inu7H{08et YKP0jUfʪ" )TSn誦 UscqZC]_Ҕ!BZ*{m+&mX)~c,R )^R-5Q!T:gVM)Gڅsdm?EܢQN%cfrTS;j6֓ B R,JiK6Cnp\cL{h;gs<^xٗB^yw?J۸l:0&46e.kq5"64V,kj{6nh8O۰Um7^-Ίc;l#N۸f~WU?~nuz-E>7s1й~ *# )lA,[ϛY͏hez0p[^P=fuu @?.OE _z#Be #U%Uڳ]*Wd)=)]e_ɹnpME8w>9,[f$B7^z4!Nk-kNJZD_i؅?*5 )~grUBna-d%YRj(nZC@[q/=C]nM):cRPta,Ishr)u6+F!n N-䢑Oփlh^-Aֻ5UE;`!^U uxvTšK)nI&?Е4Ĕ6CjB)HE Zӡ[RPJ%)?)~NKBeޒ¡zevUe):VUҕR  U g(>Ƥ .BHR񡿊ym򠴆ƇV*oP_m%0M`U&(mq.j^;6;a[^e#hnj.!BQF 9-鹢D!jkh0% QdʰNj~!9,3燓o m:/!N6VXזaͫEdc5`|O&Nz=fT0ilkXDSUV*zphE)b)JCJi2z)RIǥ,wvai6])1ISKKW!KhU\S B;X2ʥ?ccZ PUvP.RdpsZ *&I`Z+ mxled"Wti1PyV$lpNaqj~7;+ FU*_'ĔPbbnJ)vKCXB☥LR)e7Dʐmnh]lД!AEPQCmA ?QZ c޲_}1#1H^!J_Tpm JjZҙ<"$*ϒ׬k]siu RR4յ"F%KdIϪL!ȥ/ƙ{HRT0*Ci,KBUL,Yaq4ƘCX]ÆZ7;BXxKVI1~&MII|R)*2*z}v? E#_Ҧ֛)[֑8 zAi ~Lb8nZ|,f3˃, ?J EUUL5s^f7,:rq<,[|;s7[r+S"}a=En~S_Cf;j~yOTYT= bB0oB үJyVD3. VaUsq:yH X |cYnJ4/Mz/v3sb1}ak[Ҭ^evJ8Fqʱ)ҴVfWԪKX]Y(/K7og'ک-^S{7tW LTIpSaEu^yIÚ7 $qQ҄ECZ4]ܺ_ȥjrVV%"cfZ'izNQ__nEkRĴ5fZDi͹ǶWߚ ~ŷzFY QD($ձGN|} jv%J%5Y4 ªI:j5E'yhbZZW8=߄stzv+=$&yx\Yp; c1O"y O( ѻʺW螾UBTŞURERWl:+ V6=*Y%zKNzhQ\y5am>9L#{*Χ\Z}$5H*VIJ1U1[G˂16S rFn髸7 *煮{fAc7*.;},t(DK;ЈXN`ri.*?R/СׁW^{OciWr<"^Ki;LƯ05 8gWcǻo#N?&i>#֖a(,pΐo : G\c,#4JBEVĚ- &Od}rE'+/"4EQJ(I ENBo@Z&{ Ibz5)ȢV*߼wRVyV\O/72sg>[R_\ {q6 ZU-X$.[ɲ-L43cQAUZ}쮫;Yl!u'鶭UmPYU__It m^#f>eIl(ZQjIr%U*IZ,r,Ab[>T2P%62?%Zܵ&-"j؂ 6׺ɸ8{Go3 {\V|xYM…YzH:,r1ZZF7 䃘;tXsA/eﮫ(V %ܶAܳoObɧfռ3Ơ' jc9z4w!k)xp$HIsB,FaGq{ٗ0lSlR;.ڪ9j^;)XRpUy~ zEm{ەRzTnHJ߿ X[̚m ԲRA< ]uH:skQ% +ng 6tT1UQ+BuFͅ^HIU n"O(e^b6.bS$aWGo5E-KC62'ƣ.kF1PR4O*Y:*.70Nź'eq5}BI!Ȋp$)Hא{UKi{xip3h"%uCů}QiP!dk=Jy+Vt}(^?jjU=4]nE좎ڜԪ*t9H9ߩoOe"S9M]v5OQЗBD-FWr}S+tחώVu(܇"`&h8!#ÁN?juR8R* C"tEQ_n+Vh|6[<:JX}/iE%ԖşMSʾ+J?VBA Den4R`gOe^fSt,ɺ| *ƻAd&ͣ0EttZyc[ * @wTʔqQi;R΄н̞)DA} ܦZ[cRP6hֳ8*\tͫ:˓ZϩHWD%%NWմZ)_yHIWE]Tԧ(ie:?"6*vQ=B ucN25Yl>a`|/qa!lM7?:bۘð&*W:!Gƹ1s}{uek)ſ_3OhO[2a ߳f͕&e$Ϥ*'uxvޔÆlRPX7oe${ͮY+au̧^ۦ+BG,/F;G,Z feu|[յY^ˊ^G̍I8uW=YݔUV2ʤȴSiLekeZeqZfi';PQ0'< $¶M%c:/( ׆iZUYVTWa7MZɽkca]5œ+~7R;?*%$ҩ-&MTVnbgqsnRlB*o L+U%kr{*G̿6٧hZ5*HRVwk˲v, PhlG6ڕs>5M99NKB_݅>b-*͟ Kr`ŅԬfD-waZz#`y,-d(FFĹM_ s2v;0He rQ[;"eY[d+v oL:j8-j#U [ked>U}"iI3UPdVma* 2Ǣ\ZʋUsqHi}l%usJJ,g\؊$BԜxKN 0 ʵšCs{69)'Zr3oxq:`=FeЌK"̡9I鬸~?gQc햃3>IqP/-{E²t=ſtyC6hRE#@ Dѫhgc #~јk}DJF Jh^SuSF‚pRBtD~2Yk3'Niy/t0A=[n 2Ȓ0=zN7QM$V .˼'h>[5tr&Of2Qً^Rt1d_(f0'K׍`DS _.oNto_KSVm-(EY;p3u=]e4<\KQYNz>yj*%fwPk:bބ(ʲfܨ ;uёi ZuWs9,SX6ax/e7լVTфgԊOץ?Jۏy[_J‹H2-xT947ۏF,ۚ&" F?ʬi TU#9%iDPĻQ"gJ<ـ~hۺg)֒X[?nJ8NlڋOl" ʄ73i-3U4èoɬD).@oj3E (꾲XO%)FeE4O4WeUpm w Euk1ô-^y&ܣsN%M4~)sg"#E2)?soE/0MWxOueRJMc^-¿M$LlD=@of{-#^w5,m}p:;*jtSј<=11)۫JÄdBD)Q[UnOc6-nJ;jڰ^a^zrOMOK+ʃЛ5!lh"4 "@Xמٟ% (s!N[n5 ڎi~Yև*㭗 YSb֍BMTZkVP^&{\:WRtXA)AMT'{/rz-fîC~.Ac 8nȶ=\M,ɫkZIoSD|5a.)wm@Z<QLV?J3yK1l0l? j:]K e>*|8;kحsFq`8ȸqGݙ:Y_CӅ-+H 5 :7zȸ)nRF1Ef :9QU׭$4D[%Ne, ªUƙZ jA5* V?Uq>A|hWOv;Uz;BEA)'^AtY]"F^͘zLmee>mAG(+Od^2.cJ_MEy7]jz[w8FHgSW+bDžߗ58t_#hٝϼ/>e%{^z ww\jVE 쿖}]Blz̦&%w~*~Lҡ 3LkP밺CT'L[bԷ=z|7\'-C I$;[SD^}'©G;ҕhkEC2hsn')h1_$J23?W4d kZTc1ZQ˥AT8*顫kN[wăy+Y]o +W0X|xdL8 E̟z(}ո^U5o|Qhu?zԠ/ˣ߽?K2MΜdd^aNTOUln-Ò5Qa=F*s֗ ˆ.ߴ7o_S\4}5TVݟy^_]IY'FL1?f9뭪^Qr%7X OU&oC*Wg,mvɹpء./MԢx,l|[EQU?M^rP6]n$NʁNF!0Ԓ]Ro쪗V)CCNJJʃEwNBwզ"X!>_n1L+Yj4PO>K#’F(dq;(;^xȱmө[M>1zH_vW|eq%DWuVy CYE]XXMVlC X=l 74ou]QfQ|Cm^) 7=Oyp76Ie??>~;f2a0( ZYA{ j^G,+# RCnml FEP|WǦω`h>"ߦjyy'X)/YDr 6s~T_|eXexnƠU ;Q=1[ԕx'βvPЮHHwВA,R%Fu)>qs^id\[c?j8 %@YY?1NJ|־Q]6T7ҟI>oTvQ`nwmМ("TRfZ ~i®jTvSN,*+ Wu\g_$)~;-81Nn/!l֕$ xpUs.l!JiE#i(/C`I!Tk?VAPMwlAaY1Ge#b\ s5,)eI_&rx ~PCC+F?ibe:j?U]̚5*pй6|i[O n⠷,)}AA9l)ރdѐw^nN=g3{5Kqm;Cw?ziE[צOS5(P.dM4$u rhEQ0] | TUGցh+:i3zN;_m>uu_4՜s7=q?vnx݋b)&1`igڽJG9hJ_XYzB/Hg3UixүTUM(r d7)i,u:mmgA.v57`v1CT\%@zZY8awKQFf`-Uh&$;,OV>R11(ԘFDمSݶ4E9Biѻ% iHhe$JMBV׬On[=e(v)7ܾ]/骐,UlRUIUGx4> kuа"GN/oI$AR"?W= 8AK,#_JQpvq| 7+iك^/=L ii-׮mS Oz5,%aVkwʴ7xkBX8?LX:td~RE,!CXU- rĚ\C?i^]W5-fi&ށ8Fe^̿[.thL4/R3Uu$nkN1GzװxjX}"TC;uyKSϥM!gؒaZs;tR)/e:kfW q#V㊽JUsNY lKCwKhBAS+TR3"5'.:C_cb[5>5Ɗ^_3w}]f|Ƨs/-zd\6cmZlTW4Cv?;ʺnc%E+*R3}L=ڸqZh[L-~VCK2I#_X/uEYùַ9+_j3o,Snio喺HXRjl9+SGUs/ӗĻD)|_իV`Pkhu)*_ UQƵ$P:RorM S@/’S3ZkM}-% |f7(:>v+4\q`X-zB^_uiXe⊡#sZ?Jx Uk B8ƶ5t=%Ե'I -^U,/T=\RġgizPe̴iaBムӮz5 46J~jLh W=UԬrTPB)XiS7W"xnUCkWvT%rGΫt [Hx(lOƗCPCs]w5~ū 0)XLٻ=Mx9k+?GnޓK~4FYUR)r s 9i ]^2;]]|ZiBnJ;ZjEa7kGC{x )l;,7~t0?upʸϝ_MWlm؟F"ORmb>o .[' 'N{ G5k lM/Ex5-Z=v{]R=e*ey3,TwƬmE՛b+K<(WC;Oa_KJ ؞% SDfhTd_VoKo<ט>赫K C16[;+cS5`,R}8|YՑG|5vAjA $O齶mOUIxxUwQ~sWgDL%^ΣsI(2bV[td}yףܺӦh)>kYV5MϽkSǺ*KPd%]c{h9leEN}͹CWt&ƝoqȏUսNsWՉvϊƞQ_R,Wsl3>'=tVs0o8o_ByRx<[l{Ӷjw)jPӅU^٧Ӿ0EIn*͓4]쬊gBʪsBϸ/Cj슎~&*jKPQE*S꫾fm0ͩ#nWupT6ϑ|Ns{M>7b%ARV nFmM4f't^Xۨfʤ6K;)(b܆(%-CJ5>bS"ݯkzgE3cR9zO`O:pS5^UM]=xQrMTz-+O\7M$Gzj/ /oiM&oA<ѼjymT=u]%2&k6h&QG\HjnDGUSS6h Kao-I&àu)xd3<=@uV0ˊKsޫ_d]]uM8=gYԛZ!J|ȹy9yˋLGu|Jq=e:]^AȚoBhoAP{+E<+dEŞ?Wᔵ[uVOY5Ii*F=t`,\')Fe1mt'ZM]B(Pq*UXcT {'VфRncwU(Giv>]568ZS^>x?-cOt7|Phog3Yٔf[\˳﹢_r%Ix{__T%éoni IW2cPҲ, r4QE>fޮ!Jvil(~=GqL5}r(h j9Rz U,ꦺ)>J>6tJ e7'Idnۃ]QKPԋRꟶOPz/%nҨ rUaCgPwiRKŁ~ӎ=T%N@üaV>Y85 ~*nQ;+z ~&h]]gf)2I[NSY44:k2+;T훨0 :fl#/J#M.)@s2֣'wRtL)KS4]ӌζTQװزaoi|VjU-j:Ox'ecQJcn&=^kfe=(YWa$Ӆ<)kF) M`5uC^iX=?)PF.XE)L~f5׌]#Hut|sQk^,/ x/I6eGuHbA[@nV[UGR[vyI#АUESx"54NZtg=o ˦kO_Xv6u~E\HgϨѸ ĚIvUUrE*B$j+ `8Nb1qrZG8+C31#9lL~2C6snD5z!>ƘI6 eu,K8R6Ϧk]ՉRYWQӥe< CI&R(i>>drT~ⶻp׹TRfܷ̒?o-SQs!$]Cf:U_FޫeM&uؚ?1 δi}UxR a}kEn)`T%I_Pd-ҠFhs7o( R"ݴSdbdR M>E^x_}jgu^*xGqyws򴙦IR4r9kY9?LTr7&QV$Mզbt7U) r9 g[ӯ$=MgT acgB,j=|Mt(%ܶnH/ 2XUGN 1CeK/)$&UALҨԃ7#W1=Ǣ;jfD/5y?p+^rC|kuCv*6RڐTsA;mӢD4jpST4G7bBS_@Xbi@,B?%E_hʺN5O)jL[LDq!$y[5T-Di7uZnVkZmaV$S&-0Pۙil =LaqMEU޹]eWyVךca~7h#^*;^ڭ'k[L42GWរu7&+[3=|:ЅJ.1/5/׿#O} $"dr䂒!#6M" &Ӵ̓3e^_ZS{ zm^2VN0Cwmd^]f618SW}oAT|JDٖq^[x"8vR)NMV,#.ϟgQ2n*%Z@$,+ XՎ LEnM[MYEcݠtsW'DXWN ɯYӞP'Ec; $ M|6 }Ri~nNn!9~fn]#ivFAƙ,}Z~%UtYH"Tō̵+~k>72h|kSpA| <=wFI>zOQѠOj+_qQ펂/$bzW/616\䒔(uPu~M)bQSPq*B\*ŬuktivK,VKv-nUHQ[oMd3Tm&iBhJzMVD #rWr$qhط%Lڪ=]`H8o%{l6ugKn.f"q3>UnQҝ;~_uȰ#BƏ=Sw0%<K z ;ũ콥*+RpǮ#1dP¹dmSEk.fl:+lz'N:T$K;fZVq; $I\"pT%*mjUUuP=i?m/$|m۴doAgX㶮nZ&Ug8[dpR4kCEOh)> –䡹)d}ԕ^mS.3LT]bZ{j/vޕ;vrnY*'l:Eޯv Lei)f^O]pxl\2T =6joX]%ue (V ۶%V5 2BG'OLnoDшf[R,w\٤!YL3J75Nc&lkD彖YEM&k[FH:lxzpD;tCI)^? ¦YDmS=]Q\7K^2)6FuC`O9IGJ76!~/XKƮSuvnG܋.׬'zB5N'aZc]QWU$4E}Kƫ v.hw_ϗpQQB$%kŁYqvVAT\6o+ndTaw <Ž8Xu_V2%];TxC)Ȼj؍2*"x5k^~z#IuDMfnGWWE= ̵KM SGYkpA;Q=ޤ'R#5w8k^kIPM95IeܖAO.r bҲ*V#g‚Ŀy7cŒcvmiN;T= k'|A?F]3@w(=){=rȵ)TήLfɥDS/,[UgĶJvZlBxyOjk#VX,ܘ3aCC3(+-A.#EAXU6\ At",}I#V2p1ҍ)x?? .mfkf]!ͽVmDh޹0/AP'mSr=èQ{^}tT\8r{6qLJ|_/+;|C;8wAvBo4J)m1{*)+#JOS>Zg{-Dt'!EoރfV;NU=VWƩZZ*TҾyg5pP_z}u9jYyJaQs|h4(k&E'q<ԪU 2.c~L{En JFͰSk:waa]K&gwY\f$ȶl%&J=Y~ *TOY')_6M.E2dQG;R"&o%j*kDǝ-. j:ϰj֒WJ8kaE9{ZZF?5/WR~o&F-zCvpHl:QLRMY1wfTϬآ3,nrb5J ʖLڄ neCT$%^h%a¾b\}o%]TѯM{VdjIzU4bzX)IzLKNTDr=^Azmc_puZ eIԦ9XJ"aϟw5LW]>B^iyUttp-J}~Ls&YUVl"¹-nr|K,5pV]KYV==xPMl tM:e鮖*J13D욒LۈG!T5܆Kz\t{)^R)[yI'܄pTEkRL 8MR3z®<*"h!=FJ̒Si}YMO6f:sd#DV.%j{JFE¤ݶPj52x("MҨCcX3VqhI6LJMZ\-y 1AiYIG{CH{*֕s4d}' &HNt5qZd"}kmCHL/k[z|**Ҝ;*l.I6ZBzn0E0N%{z1=J!ZoȼŖd GBfT^_CFlڊ{-.3+ЫF0fӹJt¢=O 5z̓ɢկrJtL z2*JFwN'q^$0isq/-VJ Jk$*jL)A%Z%WL;I#͝;,)6z;nIC0@p'Kw-_"qV:`GrQ{f=t`"=}PpRJԥX*]CpZ犚u93z?b]Cm*zxJMYf~?E%uIpY!6_T]E7QRWxpFbTp{̧pRg*8MBСFtIXæ>Nъt@.i>JAI3 r|U7]9yL_U Wyү@}+ZHZ2bQZ }M VOUfQ]%NJ49£VئEYUh;nRDɨC[ޠ\_վ_ˆ|*rmzϫSv@`* D@7MMx47 Dith՚v\%|T GƝzT@Gi0PdğYʙ !$&#f]7ьoir.m;?}iy+?",77u ]N"&+_ؿ#|Eg_ž7pt-O-2JH* ux#G?B%xd6*z. ErOL'j"'?|3^M̯fRy "5bM\; Eqn p\,{}O{6jŵB'ټTv w\kfG::죴} )c+ZIWIjLYg* > 4mR䯽}dнd v79%B` v6eגּ=v!yZ\1ah:bqu'[lkBLTUXYNzS@&ɧt,Lh*r޺%Mi92^7%D(8;m_;ϫ~sFjq^gK$ѐUS恫^֧fpԪҩbSTSӤuU2oYLo)\J]6N}6JSFjyFXߜ |%5FO[-J;yh!BQī1z)QcvYjz)EHXFa+,'Z\#9MHjr\N#*g&UJ45RzƜ(+[)KmW!,(pDǵyX'5&բW*qȩVX.[GoݽߙbߥRjN>IeyO%\W09O&$\!|%LU1xR$tFS mؖD=@$pªnCXR }~)1-熟a|"6(OӽV:_w{P.w:i.TT`ӱ+Xjв:LY%mO_YmR3U޺ڸ k% :{ |*mtڙ૊&:;pJcgjR-եwBdVc':&MUn'SX(.PQs GT܊l8rh妗*gx2*T$/Ւ9 #_-4i@2o&t6M3T7LZ須Lt9KTٺU2 דez5K*&VNG'^,d:`SugOuA=TbـSQ:ݦjK?A)γ.&Z溈 OJrwmR=R[NkP?A۔™%jgw~ Xh:k?%6[y4&]Y^ӛǽ`C?J8^`|ZZUqjVki[ *m]Uc,^Qjέdߡ_kLR~wbkY*TUĈ~+#N=vE_µ,`?ku;  BT(X$R@I9^BH Q'Q^E^b&4nERm' ]SFb6t([I# %db@9+;uIP( ȅV\vz5PCH«euE kȪ95Y*$I5O"qA}x/H #CTXJTˡXZ ک^U1luU!~ƥT3 )+F:Ʉ2D$ش[ȑAEi?i[Aa֭_%jԠˣ 2[PdMiDّ@iUB#ޯ +P )}*SR)!T@'ȜJʏm J3@@IL$$3U6!92$WN9 9^SKEW*s#&7~bjBªjjd/UdhIdgG7m9CBHSґƪl)-"#@E ])cD̊jI 9 G}B2Ed#VKB)/څJcxRԼik?&wEBgTk5&LʚH.C&,2jиd|]^J Q#\RnԤNNr"D9ԸeP2t hn+oDIhQ#?b$5Q-3,D![4p3,\IxۚˊՓsaށ:)`HXFUajrzQYBm55Y u JKPm=4H@܅ i ic-ʋZU %*8t4u¶TJ~g$S;_b ܮVdJGN?:WIȴNq9]iw_RuЅԁUzX kI2?IF[q4nyB:R.Xđ"DBΉQ5 ܬ$"t&~tѐnѬO}wb[_ɭ=ƞb @߳=yx7-(UPQv^;&ۭ=A}U$Im7Vs> J~즣U^yfR+.*"DW~T۔xU(<˩Vs?݉OIi=# o"M|i01p!NFq)l:V{ҋVHY9H.sCH[P>ھ.ʄQ@]qQLR}pm&Hޛ1[44UZBܣQi֎ɯ=TMU|Af'RvMmJAEuҤpƠF]':+:ompYk^PcWǂ'·1)$s=d40:&m> LzS_CmIȄ΅2'xʲgąhQ>SE$_~d)۲#NIz߉voQAOuřNW@C32/r?bݺ ,t T6W{ݹ6^5WD .~=u5U$J1PUYUD\Yټ^5 Qwлͼki[55K0@prӝa0)&uwq!5ԉ>4hT,ukwP9W* WSCD j_XDFÅ}FStv֯\Gq e2C\Qzۓ1&Q*ez ^6JT, RM57m^ҍm~gEA:J+\Z/O_WNyM#9sCMR JdpҒ &|m 篕;#w=7IqźMUNwqʉzs#zn5&w,/$eWvޖw>bTTYTPTTcضmK-wn {JlE&RA?3T%hMɟ̔! @_}^W3] .FlMu‡e֎ۑ7'ךּ1WUNVcB5nOʉ%'3:52|+ϸ07j.i."}A]mXZM<(P}I Kv6Y/ƴwȰԅ]B]uE*ѣ]AA9q-%+Uufڢ5I99иMۏYM5]~n*=f39`)x7ֆC`v Wԁ23/3c[pM>ns9W~lNVo"md|U= k9s*+s*UgO`q<>\bB :!q)~D*oR^E kZ n+FIef10KȎUzJS#FԱ2B]Ey#-B~'\oFzC>VON@XZ>f$Ss g*2sJu,zTٜ9s$`]Tk]PB2l#gR@[ԍ5M߁"E) [W 2T\amMM6`%G̾: x|UQ ?9ZMzw kUX?uUyk6iwP}h؂d^>SW/5$d}ikZ[Ogt}AJT ވK%XV8h ZMZ 5A396їbisب>k?[TOc}a5 ́eHlBar#,D)mx; #R &u(FM(&uu&RF+Jq¹kDT8\'ʱUEYB9aW`j8݅$u6{[ZE+)љBm OfiIYu2q5$jNH\\Mv*uŅo6W iIfJH!кXK]^j޴]pŦjOSU.U i7ѭC3ȁ BkmļxOM9sjB5!eݮ'D[6fvetH:=ƧLxͻ-ǦKƦEW6eT-w(֨yP.!l\DʊgRo ])8Nu g+$}iF]VMsy="E庬t!|;7]q"'B$q$Di#M;^Nc))!PyB1̥fJ#bkGߒ(SΕȑiܞzKNy_ugQZHzƶPRnkէ}l;nô5Q(56VyTN3=s?.;bfڲ }QKqe>E7#OMySw2K@}S-ETqyTjak~F2r}j GDw6'+"Zƅk5,\ض:`ɖ=V.ν-c[j Z|C0\ڔmnڑԢ!МSm/!f/,i3"#o>mief: JMu%/vW]64kl_bo+v܏˙Fjjk.4ivaF^CjFDDS><4,__Y59]qЈ&RTжE &DĴ YMP@U9S J{Gf(rK8HKAuX =&ңy*b"%O_}TO+}7)An9ٕ+l;JMgmA2{] &w=ԁ')#sUH)H3D)!@טּ)/Z~UdTiYM5AYcm=M=W'= lA7a¨ԅ~DȨQ%QkLU#"fH Գ1 weeHH@#CKrrmE 45CK{a Sx%y[HS咚|mHX+&ScSBbdIEMYIMW~$TXK*7bª'6W_-Ħ#?F/BMvTuXMMQ촿>We=6٦ 7U~l A9%bv8S^ܲ_S@B;栋|݆6+/q?@EuVx77ב+7*kU Ⰽ?Ou'kLy9 YUWk$o Cv&U#a+"MIvR,J漌q7R$;UTX}S[?)(U@щ XFV{3^smueEߥ2vq+iԅR2qi!t4 χǭ%Xd4i+D@H{ 5fطnaHhDRǏ QJt#Sm:^'#yHп~;vTVPF#Tzs=ee4T9-= "w^D+ ~*#Ο{қԱrW{R0PZ(q0tm$?G"dogҍFgБ/UkB8ә#^5ߙg&>U"]$ziTI~mП}A *oW}%w_a&iB$3}Ek1${s^fvv4خ&ή{(P?I?Jm54jH~)·®#YGڻСao2mDΧKޚ+3#tdMÉZ=qs{s=ĉUO2V5& ;Nu~8`CSOhHTV*uSnou}ѓ>ܝ+bj[ J,&7Tߋ`֗Kcfg}ze4j xJhw<QR~JyRQbYO{[DRD_12V(sѼOkF簛阞7+z}e\ZJ qD8) -,>b;|gfeuoߐa>' ѐLfU]A.!Dgw~AjUEERQ[V 5 y\X%NH%ѲI$+h~-bKGN.p_%gQ2z#\NJDZ-s JE(Ik\{~J-r`P3K w{[MJi'Ci{B=̤/?7'*aU9lZMMHBާ5'ߡ﷞Dl,ƍU$)L%J tM& Jy½3ST^8֚]ۮ*yJ]=RrX̊Iet,0 M\I]z A3D":86'Ŷи˓b +RrVܴ)⩵ /T`wVTA={{/EpRÒmggOn/X-4#BJ"V''49SRMo Hm&7qJT zO}Uy5]B=JMGʕ4wf}YMҫ3+2Y~(<`jzjC[jTԮZ7WȐDڈ]eB9]^p9~gB'z]y"n+[oƅUSWV`TrXy06}^E +Th 7D܎UmqnfZ wIUq+ƣa(2?䥲 YF|mܮgRmVz#fr&uU/B+ 5D('yu=YeThlXWi^bMJB fr6З 6]55T R:D@x0#&Xt-ْqsVWLT[[j]S|iTS T KG^jk,*TaBV=QpQqX$6Y|0 "X^lг#N3U4^BӍZӶ6UI|kY"3ޥuY_˴5;ֻ|MN ILk@)LnyOҪ__m2y+gŊeGYyA=f_ UZVv?B}&%$Ds!Uo̴ԉ"2s+q(v43&@RPUT(:dB5S*"Pp(|+!Hl^NMeHXUJB-RFPw[i+-T :Тs ZwмwEF]XxR{~rZz( J ]=&9L68U@!HM5G]M_*q䲥ӴŴS_m'2!ev_p)SM`fwCAهgZhUVH-D;ԱncRh7U_Ƨl@natSm)w_9YU;1_i[2DhJKiAOqwZEs ٲ)j/j7,BX(U9_czi>W}9_i_B۰-Յ$I_Q[[>=Sqazf%n5W¤UuuAa'2(7>i6g  )#diY*}GZ]Iδ0Tw^+GyMKF kqe73_X~ ])}ΕĠOkץ|M59F5;q&f@5&2C𦯵h@pcS->26SKjsY3 NNɨ;UV0(cK""D8\BqϦEf"]^5V iIE{ZURaMYX̞ksvҺsSY{ȡ_ ˭eU>U)DQ&=1h|vN -q"M%%KU#9AĮ?;r^#FRgkGmk_#{N4jWBۥ%P jU\H;iѦy'&tDyvQW|i[sѪgFQК+d+hO:jӋʊ8'^M)Q+K r%7~j4 X"Qښ5THJ#*;ɩʔΉ"*&k_op%bM/4,(-%@%ք(̚=G$@eNSXĞ~4*؅!BW։:0TSD;im^}IeQoTy;Xu-BƤr$ԉ] +컴ԓnq*kΩaG̍X@%5-m$3JL(Ȟ$S!h"{a5y56d-ʺ4\䌊KY*M[h{P8VUUHРk 4O=U9A5E5 [7cvR&W_{T}6;ȑ VPL;y<; + {ﴠmZIgPY`Mk΍9glbVڋnRUzyYqtln0ʽ߭aq^me{ݍԩPLAaA5S#T@5K(ȅB*o"TJejUu+t+D۞QYXjL=Xuh(k; CG.Yb(4[U 1L"xNFn4)w%'YUglROk, q_^fXHwdFĥ; nlpaƔjBE;WJ5+=#N~H1|X: .+"Nxi135}/Sya9=fk=N{Nb&^mIkzlym{ȘиzJn(]++lx G_uQ= }3=F4SmdJY*kjJ|0{ *6zv5$iQL,lU9*'{->V$d&$I'̲q.`k9*֤ \po_ؾNofmffFtHiI)\ѧ g$xy/2E)ʏoGe'6uo w my'ne:V7QkэEYEoUMZl&kG5va(!8ֺJb\e#iR'czmQ#Q3]+NM^ҡ "Dk t+YUY#D#MCR5$HPU^T,y[}Zw+.҃e )3H. DX0W>U'o,?Q4?~l fWSvk[G4^5ft"^7p/Sͷy}SDى5UƑD'ɬ)SPƍu~vk2;Kv7sړڅOʛȨIcp @EA|L,o<L(!dT}Kݽ"EUqǕКΜ*vT|&dJz.#guMQ|^4oJ]ͲoȦx)6\!gJJ[XD$MG2M뎇[Qyw'uqUA )?iAy`q; fe"R7D -q5ʪM*cy9}X L~ 5VX΂Hf_jqm65 Xiʲ) x5. ji9fHiֲOMeMqqE -/"TLm6KákϳAUy)l qUYLF^V(Br猦qc_yW٩3YBg Ԩ^WؤPxI{CY~YW!|KkkՅNYQ%{yӏ2maIYiQ"DM5wiW"MST:e=\@;̿%E J(&۰ھIi;+Si[V~I; w5yH)%m5uwC7hw]VL|U5$/7mκٍeI={&ԫ-f7^ʋylu"fȤ-FfK]$jzhWR6'!D?6wawVݡEҔ22H3f~n|Қ&z:.g烝}(-*=Nek u1wd79qmDf΅9MdV_RDdMU L=]ʈ._tP=tH_u9̪Jۛ݇kB/#m~'MadSrrX []3j/Ho('N@Ց^rtsvMro?0jY1lkb&K,B)q4)=mx\Qt>2kr> zkGX]мn|~:Ew_苿ma⦍my}*s}.MZ+F#36hR!H{>("vH+20hAeGȐLzvW\)"*RfjJA@FNDA=d׺\)(zէ*??+h}]^ \USRJۼ&DHw^+ƳA]QqY e$eNNgҞiJԁ5[_|pAI]aBL;//O,iҷP[~٬@t $ժhqm}|/ƅUF3ʸ9 H4΄*diP_KU*mX@p;{#u.=N{)^n1/rXcgHƪUbYm -/"$0̠W'"M2D5Dہ@־S @p_f^]Dȅ" \Ԋ'v|zas+0;M5tPOw7i騴5gr!HҖ; '!=i7'JU T(ܪQ6TS!R@\dF˿QRGvse} ˴5| kؙe\imS*v1Ƥ{D {(&Wl{0n27PRaX5𧸞STPm;SbQ&ʃ1;oH|WVe7=|Ny^g;04a^&$Q-^Z}4KXl}j.hRgYKn)y ~~4.E2SR~Ues=(fo9^sOw!{G c[r*OXNTF(9qzy=[Q̅Q#R+φGUREJf6Ы,2"vko~(:E)rYS\J +&W*U2>[蓞ðGPWnRSRz>l6&Gy"aXF*5OJ=t/"}AuBeR~&ÉEwĞ]*D~ʲүMY]M[f52ˍ}Ez'wf5KRwkC6kkiiMt[R,XR0En'/_:*kj(mB 2 anRW]jbgt$x_6̉^6ҷP#Nq,$ZnFw}q#;'*8e}{͇}32IgUE@Mڻm,>i+"ϳaU?mJϸkAzdf[ՃZӛ:K)u9Ƹ'5U#'l?C4+=ͬhWtYIe+a6͚S_<®咥.3>D,4H.%|VdH|Љa5USp&̑rDUW#4 IPB|ӏA3EU54[\sңNfl5dƁԺʚ?e^ȤUk FawTTSX9іTDHHcQJJ)M&\7)-k椸-Xʺ諭gHڕ=NfF4henss"~4Zoe‚bPNY޺{yP駼,ʹ~H }g51U^a ՉSV+b% \w)RRHP_~tOߧ>4'qiJ}Ѕ\wiK*=,9̅5v*jj*eMy"'fBg#U['~Nm5yjޢhW=TSӔ'z,?&T=%;sXϝUڹZIy,/+TOD-&j66Ejo .EGPnqiIZle<ͷkqJ XSg+-Ηd" )4yM͛55O7"yu"#B-⁥jfD zEރu55#C5EOlk!N,s@oޗo]e3N:[ϭ#UOZJ.kaN{d]D=uGؽWmPk6 KϩYt]O 'q/]Aa̡6Rvw3$5Ч(<))U?Si?=U:ض5T|gWgcn<.dVvrF:$ oz־KEMgi wO"#Vș lꝮu,8.A R~nq 0]Dyۖ: P{P2)u\m2]@lMuQ _'_y(TU:[q_k:+?zb(āY *V%[n(YYĵj\Z6E+vg$b5y ;&|*#)| BvWgև!\K_|Y{3g`+ ޷CV;RJоz V7u1r&斂?U9crdٶ>{kQ'vڷH1-~ :G\f>h֠;T%nOoSV,L>w% X{OܤJգQt""ZW_d?v-AMݞn95,Ӆxo&'y{Ly^2 yGЛ& WWZ&3|?k0bi*UE5 E B,b4"Ϛ/O*p`°3^ 'Ŏ>IZ9͙R\ aX,O#k7GLzN؏gwFKܧz1s\P\<򠰡|`햸!TP"&|6ly}M'$kJd:.o\ ֦3=Wa=̿?N1WI4BK,J%ET3'=G~Ixf}y%9dWGgzԦg=!\nVZ_@ѥ$JJAPo|JE("+yJӸ F𼷛[W|8rW]殽h1Q4Z5;S!}k!|a> n,O$ T7!禄NOqH1Zav^tpi.vf2Ź,TwLubZ>)pW;D$ E"%Wگ;ZU%m4Ǻw9fua2Vx>¤4I$h|N^bН?Wg^2ACzGD-%#u"0>l4dU&~~s+P?}?m.$(oE~έ1M%cdE5tR]tU6Ns2>ٯvxΫ"QTڏn +I*ln*Jd* Y1hb,;vtk{ +,R1MjY7հ-sbg*RYvjאdTV~8 Kb~rY+b۔wN7I u|TϺ*"A%U)!0 J-pmԺp{DA2-:K*-rh;xn=y-#{,R_fkF-z)ڔL9Q;"jvA!hL3 M18GD Gl"IfzH2C^d9IwL][.|ڈTMjzШnXOPī+:[+ÂfIdI,^iU~ks sVCȠަ!~IJ Ū?Ch^KtYo]Q47%8ڠSCCXnR=+)$R4`&47GyI.XK!7Fc"oϦ~f6z%hq[_fˆoa@ec'E n r&z%SK2ʪރ96w};0sbt&On:BSfx/mʶ(gctOʻtꪌrf3ό޹DxJj-l54I(kCyC%+nG8[4-i]dؗtߗS6kIe0Q`煵w.m]}o]_ߣ. *~|>BlpTiڢ9uQ隚VYץ6MEd}FՁxw]tK]zO[ g}-OU`X/)M'诩 o]*(*1/|Ӥ3ΪIq^_Eihǁ3iz}zJ[~[2ѯ"V(Pcr2Dl#Wt3u)-Bu(Q91.ڥr3 R-?AD%DA>>D@pYRvS"(.-5"]Z0M3bia{F7>ەyZSU.[m׆=ֳ_Ybqo\Y4LA`#ד ߏ#_X21Aly~gX }94u :bξicuG0^T=9MvtSѳlM ֯[a/4 ;b}I'15X6EǑGOi4\9ҳU_c1̣bK)3*= 4։Ldv1y=>Gʕn?Ds/-bh6.+2͋հIalPE :h||k3\r3+gDn*Vu,ԇ]Iy)݀pǁ{=i8uk٧-oʝ!bM*Ur.j2ԓU4ѺoQzS{KLcWTC[$*YDJRkynSűt'~Ԩ'u|ꩪ:عzoUb`, E\.&*|ëMb&̒hViiUVe4 oܭO:EkkhIHW3o B?eQ]Q.Xg䰨;ԛħ^ku62103e1ͼo(,/!V|*ɶIW5)7sթf2q-i=OZ-#>d-IY0++m"5[i^W蛳6&ji x5íbvEj ||b+/WѱD7qo,#P5|ڣH+7UqVQhLc~MHyHa)K{ S@H{*{H`ɚwYj(Qq'j0) 6bw6F yA&QCdV / LFj&+{K^zw~ªzCG qYz& ^ V^ՒbeCFܓ?>7\ ԄIHYmLc^;_|Gnwsզᘶa_8ڣǺu-?muo/>rA%}fyaY68V`ϲUEuг(*ܧ L N!/,M[emXQkKuL8yXx ǯ*<;Em[t5n-ŻhܦY885Z HEji Cs#m]Ā1}&KdYjMkֆyHTW=GYQA~+fSE}UӴ5M*+{Snl Z]d4`}-:sE(Yrv!u4Fyk%_vXJ+~7ſ{j抎E 2ë`6+d@o'E~#W ZHQ`[٦}VVUFUe`7"To (y۠.HsUiCrڔgdQNEx*Ŵ:Νn*XkvtSy^ju+BǀqmKX/4i8w-\{C]g vQz'꫈JU͙wflq+g \*xYl\h+Z#H2e߂ (}֠]SaKgKc9*ϐazHu"#Qwev1-cD4D 5{83/ 1⭌N \T{+dŴj vl%xAj8*tڼ⤊`KP}v2p*z֣MXJ5i=7%4c1!F* ~ /JͨT}No6d-rlzK$ {پWQm&ɫ1Gp_S{)و8֣2jR_V| #7xGH-`El -OAM_q : WѠEJd݇S (q]GƗ]QW P̫% ]Cm]zI}pSe2xJn#?-KKB[WxS69MwhY[Y.-a7g4p/5T%ǴK(6ѿf';wY-ei弖4x `+ЯK]Ixr<襮!'zə[{_j4(ُaNFvI꒘{8~͍Y \J~( {uV"?c[8呔q.CCErZkGb!5 <-1>2O>5#y3 oަ#V57M lu=>f:rݡxw#pD̖(J~+o㧺h +Q gzkMBZ'<[-XmR'P*HYOmPXag"5̻]PWv`5y@Gh=ʹOX˜VU66jyʯJ*]RH–Ϸ/&E'(z9 %><?5SX^:Ь*jmj#*zo܏w9O7uJf>M;vX_[ɭ%Qjhx}0[(7AR/ۦ'Aٷn H6 vuMRmv_$=6SH&Qjܞfi}L+jtweȍV계`p2x=,{iڤ[IlRZG Ikח`~d"i(Qw{3W--2d˧:DJĿ2%"ue_j٦T? i7 2 oϥ[5#BP%]`~oYJ7n\ި, {(oi[&*_ ZS<}˺54xy1zTu7k"eN|r9:ōYvT5zj2 3Ö́}է#Xׅq^ʒV(),^j/iDb9zvcѩ &Gѳ6]S5$OT W&]})v#X :mܚ 뵏bb:7=ŻeuPVPC z-Zr]ēuUGݮ:%dvP\Uo4uk$=O{8dהcR5ٖ촦 w; D STiL#Zk),;O{p1]?b.rօJY_4xaeo!wSGh4jDb}oX%5VVŵZ!F~J:+#P4;M0Yw5=}kզ,^RRCLvZd!`Hl 9C y"`0WI |m `G xs.ᖶ^,C0<-h1v6[`4GEMZEz&&+S }Wm_ٮ`9M=l  wA^Cl ^~k{B3NBPK^o 2Q|'-^Օ9.wg֚؄Zt*-s5!YwUԽ޸ѳo(Ӣ}5>Bt4+ BE+5/|ne~fRJ]Tf LS| z( ymy2O%0=UcW%y0JX )3ҵ# +b| 3P+ڬLeb\eAfN=L.ĴXݿvE1LoQMn.Wi `֧'+^2ܝ2֧Vp97,2R? ,Se[5%7b >UK.dʤ"򴈷&U=gX4rhM#1 T7íTk*J 5aPSU٢Bվ'uSRJlBkh麎C.wOMYQJʂx߮jwU-MZl*Mxa%E0*:t)dP*9NY' ۂrR&jylzNݔL{RywʏzҖa9XuR{:C_$ٍ""Sد:61 ~+eC}QO ճfȍ- %CNer\*U`ZTYHsi5tWj0<68*ƢZ)N'Hn/cAįW_r7ڵe _Pz%xLb<4ADd'YS&藔E*iE`Wk:PauրA~fХWʷ$5]'$!I"rf-vą֟jq*J)Gޓ 6\įЛ5[mVC,]WR'k.eUB"JE59߿`ZqoګjZa.MV7eKQ|+V-?sz1p/ee?k_뫯wJJ5ފ#~7F$sI&,2EU9"oD˜K߾ !0(N b3pTچSmBĆ*EkbƑ%8@]#AyWqh ۯ?c^f%=bA~~ȫ]Q^`sKRjnUx\ -M.UōZ!6fq&Ze">οzl.K~' K艮oB?cH]xwgqd담hiT'L\c{k3v闫ӄO`K̫f@]tLu1H.h@i*`Q%D`3A%Nhzd*CG#R]iWcQ┌h5e۔(4,eXGd؛n)00(vH6g-5EK5%Rv)M:Y[VFX6qk^0S.(["mcоFuMU;~w/%ڵgK4\B⚴9$~7[T2-QV _!*MSu{FХX7fU5F U@ϐJ0I][`^رv>BaCŴR:,=#M)-u,Sf-c-+H6,Xpnzp]Z;HU# ?fg$5ՔQLFk<[a3Ѹ3ikuU9Fp2޷eS]쬓oVzߗG%NX`[ \BJiORZB)4Q!EU{ COC,S=ӗk|3u)ͬU%4kUbq)E*40h" -h+ejRWLb眭;Hp4_~e VS6mykJe}kXգW-:WeQ ÷rU(b­WkJSItU҇^شVem?ۂuBȦֆ-]16ڴҗ/򋪺wOPU{Zk%_,mJh"%oh2m7NZVTO!blhLZ$Q*TzuXFA[,v`P̮7GE#CyA!B%1nEYi4Im9Vll!j]N_?KonSqaq"]) Q(- QJQ3\SOji U&oV]rY0DѪz>BU:x´˓U-k%ANE HlvȺjs;z&o3}+晶'\4ˤ_Q\$dXWrYS3~F;Nԫ/-ܞ)4?g%]WWung,w¸{|kbNjs81[" rGugi*"?q[D]F16E"2*۬++J ؄%>&G\Gi Χ6 Zw*٫C)jFwzgcT4W%;*/JNXd5{MZwSRE|2!?יNC;=zXV٨l+'y}NGa@?Ie@7]\Kwc1L-Rtͦ[yc?[anTJnvߒ~>}lJ:-֗=0LM!+e2*UHhoSbǦĩ jzuU-kU4?`\ՇO)ʎէIy쥨O+)[C^ҖuFSցSOvSW^&DwxbLT{hEBl}XK2sqX_ ~ht77ұP szl=y)f\YSuCͅ9򎪸΋^>p~h}=qii ľ]Lb*hc[sk+f<=>8ٽ7+lL|[r-iTQT(PR Cbkk* ޺!]ˏo[M ~QSf z4F *|`IÐ%cYXfb3z.Zp3 3kOxjEg}NLZy^%8,i-|Ge ORެД0|HonlcJI9|yշwIFDR яU/1iW%͂F첖7/EjQOװ&j!'U}2} -K.s%P_ ԢP-6Ŋ\ƕjW*aP CC@[=YInWfQ" m^bKsduDΞ7^%ţwRI&UKCQS4L1[lq:xT'*ZT`,չD6ð\Y2hgP~L+,|Me"!SW gMhA_}OmsyBh^WnF &NRܲ_:[nݺ^Vw7bPT 'dM]Nufޜ}&ѱ,-\DEvڗeoF- 4PV~l]71\&uITjݞWTrnI>+MHF? $4kzw!9CiF(I;ALTl>OmY(S ?O(VIWԔHKrj5'ֹF, OF{;_J]1a .frBa&i[NT=˴%z`fx%WE]G&i3DE:j1'nSYQjBfNayymUuK^=EByhcطެ;!NT%3_>RgO=7Yƫ5ueMM+Yl7ͽ3﷡[3JӕU\"4ZSTUKڊ+J> 枛}UwPES 17QU46M73 àrU7-uׅ`.,C38z!I=XRj*=5,bl [Z]ef&Sԯ "\ bҩA7cUTNDޖ6!OW8k"j >wt$J@J-1nBf׵ oWL Α/ޅ8'joSB  [vj؆u ~?ڹ*z;Zq>m}QS%_͏}˿?JC]!cJaךBIW3fVڰmUL Ƽ)(!D|lzɚ1>deǯuVûo[_ISd|kz%ID)4R5TuW)HB:LCc[DOq:nRȊعωtQT'ʷ*Au~=#=DCC()UCDD 1]r)ۥ1aV>֘&HtILz+2UAU=RB"iO:E5?s˷Nx:Y1yqZGL'ʒdhTm4vӿ*q=E,g 6#yPʴ?Rp<v+&ԥ  M+4+RѪK I[^34*ǵMxn˃ jb,cWȿ{Wͣ0'wvlέ/E '?&Vѳ]\RPָJa tV[^m{e}qN_H5QZщ_$ f'ahJ֡4u$m͍`ImT U|bRR닖_q?ˁ^*`ޫd`{ISXҽԷƊg*PBk3^#?ז=Ij-xt[v=&ei!H$Z'Ajts&I!H((!v(Q%l5I=9CzB̥"^7 wZMz)8%y{.K>iPBơna2-qkQ5>ii p6}+bz- -*9,fȦ\Cu6ߍeJ]Ckvw4ùPltmrez\w{h.5d9]Veu4uC/WZ@ZYķv4?[QYE4u*gDcEu|*ŽNP,Ҕ&5*KN%?#$V7t/tUVf~ޒU'~GZ>%-n u }H`pH)P%v!֯LMEq~D{XNko0;jܾ W.3Z۪DdQ1uA\V\ en) PMZzȮu|6RIESQO1hbV袮^X 7{(ENʾgU$~V">T|kX~G&λrJ'>im?HšJ՞֒տK^#R^)JFr?pFFmh֙pm&ZaJfs~Uc*hܢ<̓Y?7$+^mk[&mCzI(26o[kӜT/\['R^f6Tٍ$<֟*h)se&R-(3|AX\pBoCGn^AùWMeJ.H!S')"- 4lҨģZU:H'Lb)ښ)(.R^y";,Z=KELEYJγ:SQGi}2f]Kca1 䙪LeZ*&yaW7%8N4; ܍Mdz4j j'6)ܫWUSՔi|WeNYV5Z&ƣ?jq7*'﵃o_ۗ?ph[rѝKhUy;& kT]ڋ:lQY¢ᓇkhG3#]R򒈧Ir.h! 9D59rVo$P,4!*bݶinIzf23x˥XMG'79R[dJ0j}y^[5 ;JʹcRvۧMT$Z(d̬(Z;].j Q99/ R#rTt0KUfiK޿$ЩTNjMF*/d?^+ ߴ8V5t2fRҌ;e bD釵tnܶ4juIP6ٚSbTW7m9&j_HȊKҔyՔ-4EaקjEIdQ OL3^7˄w.NZ fy( G<.ذ&ϞA3e~ 5rtD?5e![1"z6l&AŊofA]'(qV XKa$/mK6wYiU[ZӝT]XǭY߯sX;F*/P\P:{ѻ\$u.Bʲ-]M"]dKΨR?%]z6]K@2|c6照S`P_U19Ii'KӸBkpwȁ$pgEi;we'Yy[`i]}j3*'hЛLD-EGЎ1KVYP'ҧOם zN-XeXG7ER]ַK,}O+oQKȦb5U ?HH@e(lڣ6MSj>Qe?J%%:&< ;-E{WE)BU Q0WEjQzJM{|{'r7 n]pi[R۹VUo!;_#xQeߥl֚K܎_ b5ʦw_ #gMB+}o֝54zckc\iyO.n]cLmev(JEbWa,% ^?>16ˡl<nS GoehӶCN໶ȯG궯~em}L͢sT*T#SB'mR"_*KBҌl{щwESM[""wѣ.ı0(;uUFEMW] ?~̹_ԻX֡Hb9}>UVק3{ɥՕz[^m}NQ¹ĴzT7,~˾)¶Y ѭɳ-qjm)Wً]ږi5Gj`CS]_R٥MCXUej)YնeF7k۪W%nK˳H6iVWFL^;RV%q>{)UF7Iz{֊i&[[miTW4dXsX튺ŦMڌ))zOXQw4Ea^҈,)%|e/^IgZ,ZU!kpe4KBQH8|"q=iu b(5$ib:!IE:hL^{yiV|<-46䦚3S'bPs~SiIvVĬ-4,B),В֙yt%.=(wo kj)ZE0UJڽE/,a G:HnD{I6{/E%f)QeL(֥>GUMkXp3\SE.({i`m˯[M* 6\R\mL uM,66/ʭ49.E6WC)T)yiuH_.QTQ!IfM!bQgMm.!jhKjs]=WݺЖoh:t0Ɨv{ܷ5ɲ}ٴn"6^?CjړfOV)RO%f*V }?܈D )pӧj˧gxسw*mtHaPk 4*Rcܬ} u^%l(,Ri[7T3ޫwom3Rj絕v\C&Qt Pu8Bmu%(]Tƈ iHChA`(!OVAKXS pPդR-* d%9WbǬ]n>I(k%ړ7cߓ\kN+`X7@[UKFs .}KҢB`1,ipMMڱM4t{KMUaC:j*roA7][JA*ʂS޴z (Н4x7vwaedTj)-rL ` ]^+_:#^zϾ%R9)=v; n4OiJCWL3QoxCH{2ľ7"ʥ7++= |s{5U*&Z*URbP&!Jɷ!]a]2$: ׁkKm`5n͇-h7?wr,nF+Bf4Oqq6]Y6%KpL}~pi[E5Ƽy5z 3JU>oXPCϳ꬯+ UD:tyj MT('hŋTѺ+&>SPkpDh"ah Q2PAdZa[nFOETyy~+ fcz uXƬNּ5,k,8j6qHϩ`g߯I׽\ܞֻPaƺoE%X\a!Wgc| Q1hgU q?}z*ž9ns-,b-OM+45QoMAZ@lNj=Gs'X~A\$wt T^Y'K:"fUATbլ)nzY)lMd⺨/lVxSJ 묌˵swmV4y`tV{JR]Y. %q&ĤMj6Vβj'qkUU +.6j"DaԎ F~/F8hӥUk-d9klE ,5~i4dh3 ߨJ'PP>fpl8xT5QrCu$RnM?M_֧1?MJI=jT좑kMzx>aS І:_[Ox8fXīž3l2vϲ|`5TՇ2 ЭĒ\٧˲OWӯ(l1LBв rx7ʳ+k2Y>2M=h̴xSJx5gԥAνM{dE ?ʞ4Y#<5jpniJQZ?>a.O%VqM讀FwSƑڶ9*|e}i{+:-O(^p'/ՏM$5ٜWtV_轛a6ϭcR=U5fT4dԥԘhR!< Bo+oØ {[r]g/cxUyrxEM+d2(+AVfs F!!;B=tu-g^mp=%4c`gp"7߿df5;FMfU$[̷jjWԻvxƱ:Fb;}IDvCn+\C_n~Bjn+GjQߡ~y>6ZDY?={uXfM,o>" sB "F@z o=8lku >[ǯdC^ykKED>#va*GƪO SX.ÚK:XGO_[KOla6w`'ޜ{JagsqdYYgMf-,Z8[ ){ H]AS~By(J򑽮 .;~]#0-4_X5aSg6ƜWj5^1偯L rt",_RA?5Q0%.ųT૯]~OK[JZ7ڵ0^BKܪIP6_h} U%fZ6ύ3_cti)휂NҦ7Z Zzy^tNn+6_cȝ4N=U2 8"a(e*ׄAԥ!rNEw/Mr/Q)A#JǹK vѰQfSQ,m>VetOgL7HuR4/CMauS P('G~P3pIIFqڕ(O5},+--iT+SuPJ*~;2w[Poz`~ټQ϶٩j$m?2[|o{TRڿ& 29bu5SlŲ5KSӭO߫IZ?y˫"!K(S@Wݔ՚^]S/` P؜etT$a#(E OG22'^>M ~J҂jZ?p%{H_iɷ +j>+צe|{ɜ0-l:-sC^*=&r[>M\\$֯0/a$M;օT5!Fcb++ί&AR;8! 2_Ӆ=&~L8i6#4z,S:KύVkZ4ŁfB[z"yPT)J Y(4]_*xN PLC@ +ZJ%y'#odKmޓ11'g-b`MMQLUT}";aln3U hjBR3)J}O )m^±k&ɬ4ӯU+FngXvvzĻ!cC\fCOӚ_NULطmokýE9習Z~5甚]5zՖc5E.˪]Don4(%-NOѾ2ҫI}˛XGzx!G}wUשìU,5y\齕d&9wCMtUCMmr0YaAX{>en:Mv'T+>cuq(Rn^Atn:]'̉I3^RUYL630S/۝?KA'o1)t'N$3yI2hWRPL%Qe3j]5‚#k"ƉOձ&O۩ j}J౵|R=[.3ZN/7<ƺ)G%(nNP>ׁ(>%޴75L/=賈I *LBZ7Ej=hҋie&XU~ۘ֨<=ڱ/QzRiUb|#ݤcZd6m;#Rj Ţb"'*)b7qAgv%ݲ/$"-DPv)V] ~D_V>RfQƠfba^iwҔ!^Gz/Lj<˨Jcg^B­雨ɻv&J]aJJ+OvQ̹UiQh>5/-+R/WPlr%cXieent'X>eP0rcDN Qp83b+_K'UU,UaSk0?$8pWX==4կj!A,۶}*bYGSZݣiW6:/3oձD0Ɲ1saׄ$n(3Ԡu]])Hܦ۞K+v-u8Hz*CYx!jW .*Cz~݋\,ۿD3*'(,ͻJ֓F(GzLkM| S\7:$ccܽ3iC-q;q85yOe; (.VEO'?UE=2M> /:n bm5Qr"O탕S,n F;[dm& FdZ#z .1zJ*atSYlQ+"j_cNnBMGf4Sdt֛5}[()ʸMU]ʂ? Sq/p1]xT"%*%tbG+u_ܦ5WeN&$[¨%[N==6sP~Mrz[NRUⴚdCC_яaVsHZxרYhtVxkaEdG䩵q^VZ6iLOˠ"A4G[ͻ[ս(=td⫒R E"˨̶ _ʒW|i%EF#HxLБP)&Zn%R)mեQOv'Hn_mu/ӖoJ%r̊ƾU}k[lsODu? ޒjpמQӧUzƮv=4IV/(?INVLW)iIN7̤S|Ͷ=}Ch6vS"+UTK_K6hp=%X_j)-#Mw>"8DwQ)dymM~EIz˪iz|}]Pkɍ;t_~PMRMiLcNU C{য়Oq/SuzXPMR-_3R$պA t)O~zQE&!X>KCO)L*pwT&.V0hkSxKP\V_m;jYPe?-Qr]eRWIJ'*ȳ5aSUrVr D51.颓[XR(hƬRK[x*)+}.9]"|$S:/9_T賊4Mb/hcUgdէ SKKd~חiMŞ$9',qA$lSl9XVRq=Kh7F[۷ԳuQL0ZςiNeeh~(OЇlUhݫ[g>Ȟ=M>gжt">]-]VPHjuHv], z<9oƾ qxprҼK'=V}R߿wT̝1V+4h ]{vҭ%)KtfY+ۙdʳ08xp"n#`\ϗ+ҕiW/ĴFa-pH301:Mhv&[1I;j3"hiu-ֿ^J@z?k?wkNCw3+-2m`_/rc6G=k@wG5jM?EO9YKQxղیguzR|9;jOqo[M{vM U(}cfG1Z59k,lO>Go霷k{տT$= kDҽ,^IMH cb&zlKӰ(a RȥQHQCS Z}:w>"Z#sb{6bNa/;W"ն)Y}Ou%cLT/Ѩ&ONW zhnv4g>X`d:D*u)LB]K{cD|ogx;XOk>ҘOCEpCtl.)KgԲ[x)ߡ3Vja'Ji ߓ x % *uS>' J![~'HԩqCUKzAZ6 BR W|x eq B+!#J[!Aμ0-!7(I2l DXķ䵭oYB9ܳE.0iI$\BUӘ E]:RqOi-s߲|g=]^朸E,DjRӍy5jhe+>TJ5=рf .;fV;V7T5$*hʥ>%u5-EE)ØXH'gAV_0-^)|/ #֒iKbC{CorZ2͖.D o E;QƝnqՍF%}m"wHMKz@!(^+KjH%4KP!jJ("uRXFYMЭ0m(-D SMxUWY՝2Z61(J2Z5-ni҈ew]4k|U}I84}ݱ_P}$:ބVq@~,xi$)KZOz|iٵu W>{:oOKZfb$ώ@RL/VW$q ٧hW\$S51F{IEi0kƱƥ\P=W̋j[o jڈ¨oQOF§]#߉tw8VQtkzڵ'Kok$x\5)?aQ=א[Sb(QYNJpn Cm?Wp` qݪPƚXc4VGlz"X7'Dި"[xrQIљE{}Z"_ZGF$QQ z%&'ȧ-u%zb߃d<5ѓG)MX7m4{YG&r,jhm&ߨ[TY(ι1jr)W*UځOeyU}#Z'yQiGz=76*"`/J=nRɎ5jܼ++r .j|zzvmI*G%1$(5ǫS8ΒkJ2kg3ٗ'ja>aw~SEH?ŌdY5Joۄb$Yz梾65@f؆,ӆ9"O1JPlH5H.zpO4)զuyuSDJhL&&K7]lSǤܫz)t\:LM]a%ץM[R>ǰOL\G*Yue[YyI("oƍԗ:kt w=}K#SR; ꡥ]Xmn)IO{$G/[5@;ϣs32wn"i;"Rv i ]}mښxne-GL+.~+3ljϩ7=yxw(:<ݖdwi'g306=vv.{K+RҍP7KM&)K3/)FP%9F9-=5^) Aw1FO;Wh4N-&!0ǚӿ'&{B=pyÜXsn*M1)j+r{rG 0YS5OTҙ< u?XѬ¦.HA^X)|!WT()̡&L_TV=Te^|g˹Sr iJl8Pe~ R}âpcs+D9l) 3-!BpS,$#̙h]gsAjr7=|D<>[#.E8ϯ>xs-A_?^J]>WWj*v+YoeAuWkJwL="a}*uY/h!`*Σ'ާ(5;u5e.=xs?F[ޛd[e;k*9*Q}.&Q +nnROf3ACGIS;5Auߋ}i5=KR_3vW,)qp[ >üMU\({zowoQy-UK'êKSTeRx$ѩjLZJ]qo4Uނ)Ж2% _7/I,r?_g&5f/V},-^>KQ~J X5H4 U5=:޺ h?{T$rMwb Q]nJi c>WvzxSD8[Jy=wH|/مMR 'rUp谭T̛/99VU䊁s Z Zh%_}L K=]SHL?%Uc-ESE+/yAV$zh-Dw?K$W4B 7ɁC]{\=UQWj5tޫJêھY_z#X3%Ibmkz٢'%A}_xTJ1NoW'uli_+y*bYZ|}xs0??RSb-I۰6K]%Ɩ D]]F:;L44T!ԭrE2+s=kN^[F2sQfakm-%!ݵiO]8E~X7qigo!v֙Ō%3/)fƲ;Rn/TWE1l#Kfښ-='6RmѾF-)Ed_Iej_c5-x~KlfBe_>heD*3dF%^Ӗ5iW]R V#`_uq]Wo:Y쑧,YN=s/U FU sUQ;PluQ"gW`+Eabӥ9g\4vwM3 HsA rski~ mj&_OKkq; |oi_2DU1xS6Y4l#A;}!>]|]6ΩWmٵUHE zlV7WW 8b$WX׸r2!DAyL bw+w=2Н Bլ b_-A<p}KkE8+]~}=YUWXɖ: Zm&*Bu8cd'< C,@E, ŒJ'5IzAޣךlH-5z:F̵Șڬ⑕v/%c>KU,<{%wX֘+̻4ɇcݤ']ŹPiUŇ01.@xM-cB36p:ݿr11èIE- 7YLM4F"5,€de8O0͑4f6&SCB׊ٕVt.KD`y) 4naW|DI>IK,Ҁן"gwrmMZ1,:\KA=αᝓ% !ȿ*]u^-@~B=Y4l})k`&GEyh3;ߢQ%TU]Hh*d []:ܡƨ =}O|jw8֡fO936lNsՏ|G9׫}raryfIٲ+Fڟ毱LQ0>y, tv#ROIxʰ[T)/ O(1ɕT(AUdJYH)Iod7l[I iP\oj|('e&7C^6Gx]1 7 (Q%Q-SVuRTFhi[u8FN h"b3Y8q~b-`k^Y )"Wlh,6;ZiOq/"׆tZmOڸ۽3q ۤGmzz5iꮟ*Z(oU}@yhOq*E?ThxR N*\s"y6vOY}L. >4e9S{v2r-}nj}NÔl}QJpXE5MP'lS=}s5)}n=ϵN֦UEI¢.e-YӞF:IQ]Sy?+Cm?Q>5oaׇ貈N,NrmYt"p,:}2/vW'U<ė7k @tSWۧ,#c$T*!ɲU:]GU_4/!]?UaMq卶@?wO3 ڜ:DBAZHlkUy.kEtf[W$귎z)UM'kHxPkb'ҥOV8yKf^k,Ԛ(~K#-ݙ́tdw==U@QۗaAaTD8_Mz*V?҂ZkywnC {6ROpBO'+/ ~ 3c!⧼fGQ=dOK4]rclKx񫻃dA7;W {P7Ԯ޻ii(ZYM[=gi:{se[r[oS$UNFysԽ)_yncsOQ4qSydg].7U)~ZhyATukoq?xL+ VDza(s5II- D(-~YӺɺd-xU&]~mCOX$66dr`Ɲ:I,O%VTdj}+B5z+=։:D`OgqY* {vUKuƁF-k]E x̛4XoRHX QQG3MZY59UJ %Q2R%Nz 9:% 2(ި-)N w Ӑ2ת୑;TRZ lܢ7yPT w1oBfS}jV([% 3Z[=2mzڛnܙ(3# #Xe0YN2^u=MrӴhY%eu[^:T:^U5%FZzMU~6 =_,]BAXfk00dSK/):T S*j^WVE^)MCPw},efJDzYp"+kϪ|FS8Χ>ʙ*SpԱ~j'q[>z: _FѯJ>f1jLh̲AQi]~Xǚ?F;wڈZekxI>oz/- RMVk(.S&ɡ2fDXvS߻~ tAeiפݼ|!3l4/XY`G' YN_NT ?SXj+J=VpL|t`-Kg_4j0/p7KHPBi,Yd9!Qӱ=\e˒f`F-*eD[}_dZ+z&&~krA 2EҝD26Mf¾K z bxRht;'mA2~sBvs6EEYۧ/TN mGڴJ3>Ƣޤ:}8}'wʼ."xNczULۘyoM^~Q3YՁHHkXNƉrPf2\gôN??v o;O}i'mixpYmUQB.ԹPXJK7>UaOu:֪j4k:.PSϕj^ă_(_YY>' GWMR ":}sO$acp^U?nmXk{t~.+lUg-)ΨTO@PRae\܇EKr0&M7^˓n.jըRpL?kK#ص_ٞ}E2:3z%a^X/Cfy)e'9A2t7[r)d~ 3N5HFh4 !u K*%vY6!5҃FbQ7c@uS7N{SZ\(jdP\CvSLTYJcIݭ,ZhbZ M29tUi/)CtErJ'Uvy+SM!* ᭫͝IuV/RhTB-ZRP;t/¢$"LZ[UyNRoƱ ɥ4QS\l \.~_!,-1R fNU' B@B\C%j(} VPs\SBKu^C\JC~hLԥK,V]r0)D1mV L_$բPBhKf%x…a wM R!.6j"z)&t1шZ-MAJ?J.YU/D ﳄnJFq"CPZJCXD˅VEZ=|[bf -$)<]I/%q!|U4G1jHR ճD5tkFZ'r gq+ԩvi֔^z_)jzQb?_[ԖRQ:6 ^(E#MҴkxȢ1)*a_B49ŅǬ;r;׭G0H~Rģ~7lb=% AyJ/XZ[.ڽ)XKӧYm T꺪1V _<u*m<ĩ.&>W TQLjQ]m(jK)IR)! ^ªx}\b/Ennl NE)C*U{ϓ]\RlGn5jZƐ_X!FUZ2JS!U?UX6vܶ)vRoAv1步7'lpg2,d*ҙ_=|+M8\֋Uh%ɪjKW< h.(E3,5ԥ _s|qKH$(UYKK8RXBRzSRCXUEBBhjݪ 梋."O,3"Z6%%!KԴA' ÅIBQKfT¿EP*业DSu,U"S[dқcWLq4bWڪ\BiqB3GNi%Ws,X!aK" :*$ ,!?U&8;4N B4p fMaJ(!4Q!4CoMV T e SRi+fJXg )B!baR !#b5%hJJ(0FJZ( ,1H%V!j|E,CuXXVD?TfUHmrvE)u0(&eVaU;]KKY8o% s@#pzV]V) cЫdKTȮ$M)Azk=kg?Ƃ4DnHc&(7bn ~=WU{-lkeuX7S?% B33hVW,Jj/Օ/D!"pH,u8g6`q #ߒhrڍj=0ն3:QZkύ%TdSB5K//\Pn6oA9.Q0U~K/mg+4}^^Jɷ YuARZY=MfzPryZh.p1-$f=I\ZLK(3Ij}UwZ%[F!ޞ EJaX&IXom[_.b֩! -BIlc,^˅35R|i^O4:inB:XW}-Ӛ٠ZW)+3?1ۓQvk[+ |4 6&Ծ4 TS*wM|OO>W6f.:wlgvNvl{:gl5]p p~IOܿoecvh5?ùexUnfλ{("8O2o=<~26 ЬSa&%RӘioSՙecoōUΌM*©6Vc+G==͇ξ}&4Bj?0[my= )+[^+J\]!Us .O CV*E)J_մζ6Z6*:*b>䭖:nZ4œ.WάeBp)}UNMtGS,(9NEu>':+5_zɟCI7JxԦ$KTpjL`i涣2XolgV4-y*Bv%gAD]-ni׎O#w/MWeib'%wij]Βd^-zSRh!+B1 &T,2O3{S}.b'K^HQQ* ȭ* sNU j3*Շ争$ݺ RܔlY藢)kҢ4=-i+CI4lc**auCFOy;o O_wܘY[p͋䯋24KC;GuWC6$4|/`礪JBTQ}"ɲ%>E]PLJ]T1 U.,ٮzf/MaJ+5{&YP} S@'?]eHN;K*ߠM|ʂq\U9Cge/[/ ѽ8/EavM7LCLżr>5yȴ5`y]&,ﶍ<]7Yt>]^YI"[1 šĵ-þ:CEB"R*~<`,Zy\Xð/wWxeԦm-ZpPWY%cN{Qg:ȾGZ BxةsLޒ|f?Ι( ›Iyaџc:O LZm^/Ƨa]N[aX6\=$Uߵ&.vsꬉzަΌ9Pq\$z E-w~~W;`g*Y˿㽊ggޘǿ~Cq:՝)kՕ}+>FYʮm.n[m9́WO>$uDL)QG ʺVu1(~}J E&ZMy6mSŢ;$QwHioL/2c'cyq^?֭b淜Pg\>oIc{So)nܽ!?uM맊y, W5Nʙ6Bݵ:nM_3 ȹ?My5*&01u^(C(h lh.@_i;NHϷג'LxWK`>e[<K]-nβ౰.ä&釫QT\FF4g*AMUX?TO]&' jwU=TRqO1O}qD%s_P` ;|-D%)\FsZUgNQS 3xr"fL3KnKfMYmb55IT4km(;ND%6G>dSfJ4R衼EO{~#ߕ{8 ҝV]s5XanN+#/H'>iڶʆVׁVY)~Mצ+$SQ۴9OKR+m"}GoH<}Un͕ME*Ma ey+Mإ}xI-W$Do洰%Q+J,wϰ5fOKI5LBM'Hy͋kfTTvpշk+JdKtƛ2–(Y[/ r;T'ȋJMMXgTU|i5nu}[_K@ɚExZauj- OosEwZJGm[TGJ٬6إ;)n&KҺTeZRUQ@U Zm~֫m'Lo7ڼ7hn")yT[nxWH9 {.JBmtst4ͨWU5h 0˳2÷ `-h&$݈^'kEP{&2~Ox%\-0qR;g; ax/4cfgQRV*WZ\8l[cj7>ڶ|%e":l[}'*Ou , <&1yr{N27rKc=N٠X[=;~4u/\NMso۳`Qջ*+wUJYT-dp* ❠-Ha. 0&ҘѰcHi:;´޲mH%5SK(i,KW^vEr~W;U*fOe&AiԖmeB?G]2| Z١Z۴[ַmwth֡4WW3H!kʻC椔%:dJ)G]F /ڠPG:*L^?5K[%b=eg*k릥 +&EZ~;?uJSTdʦH3nT{ b҆U٦%oS ܼiPz~֥^K{K (Q_#4-LhCQTjr\_!X(V2OꄖPLRY_S[=vSe'].q͒.d *EdFΩqtzԽi^8QY!JRʴmIMU_ޢjTE&u)eҚ/ ԌK7V_PL*2 K&d)Nk2DɬUVR?a?Lī$ &zl*ǃ6>3v-mX+?GyKU9$iLEeWU6nݒbV׈|O-j:/3{$޺~,LhT\HXYj$j#@G0kN(0<CdG{xx*;לE}Z҈N[] ;,‰Sd4!{U]C,3`뻻6sRN I!=Iq;fU7VX]4kh&z5LgZ Y`bɸb=hT Җ^х0WZ"oL+VAzeew^S$\)Z))= .Q]Swڙ.ުE TP!,'T- {a~E\ѥQ%wxFY )j})%E]JYf2(k|x)ɱ^YG8r qvRxѢH6_e4-q2Nu /`RQwܶm9E h]cY[-p~a]ӎXdӐt Krm)]1~è}^=`nIۢaS A| n$4%'Gy$8|ZGDO3vkCVJ'>k:)xJ%Ԥ,7gA uyK^XJ~NCs_눝^`,Q~5zQ}LCVl7wźf[Kp pY3bSwxf1fAE=|3ƫ2v=k7eŲ\1;sfڧt7:el¾`=C:R.RkA˩Au,>; -wB&Nl2oo? ]g\=udmS+rOvfϹeK񣈑?:4?݈P"eؔeVN]Յp~wiLd$͚S"wXNEk?G9YDJCcbF TUM@{Ret%;w=ecTW_ϿGܜ%Ey+GN"ھMS s=eV[R|wUx,#Ǯؔݙ]T&#4d=x0aBMn&ajzy)o"5khkpᦉ-Cxwj"nʚ}rYS]Wګ>ByȭFy禍(ʬtd H'WZE.WmkYdIcDm% WYõVgd͟cUKn5!{ V5+ݧab=WLa>\5XsUNwٔp1b%}aQ:vF bʡsw]7!5Wbx4/7{vX窹;l# 1E!ۈKRs>DAP+eI"Qim-S2'8-T cTYO@%~OmV4SV9%yTДayQ.{P'5>̼$5DbyzOCZtK9nK]h̔iZ|>$|֟Vm)J?BQRI@\5ܠ6E~E{l-pԖEr7Nho9$ڬKIvybIv-O_erXҴ '؞!'8 ՟\:Us,)5aRlN$:OP[U]"y^DJQ"(,kf\0!"6ZeUg|36}ZjlCDXs ]J!n[d++\$#*L΅E(J;>-NĜ9)E3uNs7YӺN v2jJF#p]!6Ј"2m[٪Ȧ'IOִkz*9Y֕HU]æ,=ͣ*+*;e{Ҿ$ѻ:3eU=XϨѮZ$׎5cf//*lWz K{#52eX6=GH>lTgJ#ڧޯ=U SWMB|kj־<DNr[ 2zK 3_۟;x]QTen{NRyfw*ŘCȳx #Oΰ|mX$M,,Qve"EvP jXGF é1$AUD¹J[%HA)qSmS[wmX5I9-#u:F۷tx~lx2K(z&^\+ L=Wg׻V#g^?AD쭕x.::Yڛ/_hNOQ'8務p@B[Hy.Cr˫('awv9+i:$wمU-mmmV&r uܰ<;u69]ё~3+gUr|.ѲD5TU_gZ7DG@Nl[VȦId,[׻5#yOץDd^K ^NAo/arX՗!hӯR3l}f'm[66tInN5Bɞ^(Z*ZhJod:W  P`/ #@M {z('IQPi8$BZ>Bu7u4FV7q *UQЪi 堈TE^4mQ/,y~e)1+Wi5ܜB5`ԙÊv Me5ÀxnY kv6PS\c1){z%/Röy5plZ{mvMF | W۵I/G\{)XRRi(BGUSxd[+GhoA5i50*(KKn yW.V:Q #3""yTn<]`b_uy2`vwlΆ5es;rE)(v-m\^JVZrrWHhiӑJ9t BGrhN= v[bs\3psewGhX:"p ] r+uUmvPRZՔtm .ǭaͶJN/qιEM1ߕ,U$z {Xqc^KY79 iiK澜;>.YCBKLS7pJћ.*!gՍnk wVWjU^$&Ԧ("ŽUF*eZi.'Ds~M|̵?@٠o qǨ·:!CHd&ZiOΗosZU+rH}QU`gHcPs^IWY ! 3TĦj:m,~6VM B+pa{LZ6fBZ~_*.U7UA P󚪴/Dλ=|9=iOȶ[C0PPϕIk4U wj*"ԡ$A2g+dx7b]v.xubaO%cZ^˱h(a8VЭ;r2:f/Y?aެ]#0%mjv%_y$j|ٲHzx_'z#jڿ> ~;VupeQ55f ڰe=@BTyŖ8~-ΊYXµ%GnKuzYZ[85ck-|k:TR̚eJ+orԙڶ:ImR‚/qERSQ1(Liy"J2y."}' ;0%]>z =zvZ!4oF* dKrS&}6Mt\Mb)ވ'#,j&^I6n0 {T<2x`U\zv54δ{J,G7R=fJ%-i=E)4I)i?5,R䖥i05Ijc>ծj0%J\dzDK-mJֹ#fl;VhhcɄ5R,{;zDhu|Xos⢴t~jSJ[B~\{QPJr)i$* g=팝[Fͺq:hV٧m\XևIr9hTr$4*&6θ -iJ a69d|˓hr'NlGAsd&sW ^Ufg޾6VxXoѬsKBJωV%Yf샱CEH3i|)nli/kGv].yW'mQoYa6NKO4rD]J\L"=:X"<{R&/}.G컦ySv?ۉĨt>'Pox/׉fѷoe_[tD٫:UVh)4EŹ(i7yB;cIDE0*{H*Zh7Jlo^H*}lAR5mK\Wf9*"ʮp>B袓,Hi7-a}S1{0AyHh lX( ҲyzikSXf[}QHs^8Dv,[^FnZ뛇qOTlLT*rTuùuA=.H(Hd]Ż_ *h2o|nfrlnnp&hrW{^^ߕ[ySq >:j+EtguYR]]=/dms&̴M9d-љp bBwϡ"XD"s2ˢ5㤝}ڗXt): Nu)SEq{.tsjW.&-Խvbg#%ǜ!ǖJ贾z覯F:Æ"SJ/)d"9oy}M@3SɑWS0r%bONԉYpmkDkOa'/MV{B^5w_Ig"E^J-?\\jghG[6 +qFPnʾڲ] l][x>쏞ޭ [U#tȕf5Tm;FǨzI@u'e"ţB%{mOL%U[cc*6]>pWWVK<\TP- |~niGT59+PDkP[eAHcX_B: l>PV&\r#52ϳ?OQ`Aiqg+{ytC;>4UçD SV2jν),&޷ Y{Wԇ')?ͥ1RWT'ޢ 9sT}XPƧcUX%~bm+TZFg;Udɰj_m8 WT6A>T5ifUʹ48m)bAk-P{4P*G} E}mVѯ[B|Lr ,(!G#9AnC¼Ĵ>5wrAV,Juİ*Ԃ Ҡ,~轼GA Wk]+XtߗډQ0i9$ܷX`&4*7If1~O?ç~";|uᨽ=n!iw7Ґ~-6鲦ig g?iq]3_*+ОHO hVEta8)ň_N'%P8xs^7,u]3˥g B|00:]z$s/3~ *c2)Cׯ)Mgz~]U@T:WCNY&ͥdЪM?X|EG`Rs,*;#6A`UV]uه=ﱀn,ϙS b)M5}aAr^N_2{^2*NK؜1c]+KЈ7a,C`NN|/'E3.ʴ|m$+LrgtԻd!rq R(9O}oXe)giI<Ź5֦uiQS&QN֗^F*-UfEh7,+;mT]X7'JYrmXM6r"">Uo91;ue4U<%iZûR$7M>nV}jaQm*WJ+m*nȵ$ּőeb\])K(cս|gn$?(mAwAE)"m7Y&߳Ww+5 s:IEqa)nYlU'")mwYi_÷XԡIU#3RC/hXG(c D7q E\Lvq91lOEEo]"ٷ#ъ`ե_1>h'.xXEZ4ZʬfK[jxKKWd5nؼ1B¡ZCg);Iq W جJZ3QY  rl7LWċp9) fwJuڳ(O}k{:H)ƶ1=`7.PXOObYqCu6;lDYw99t)o}g"Nb(4Lc๫z* ="oN>Zrk)T뜓r}k>QrbVFXĨONs2.Z-~jm%N^eNo)Iqaf[UֳE}vWsiG5{3]^7>.E_uTvʨI#y!e,{m".jܿ.?Sʴ:Z)&$ڄ!$<^Y-1zHo1 4c-[`Mz)['sG;Sz,.=Jn׮{/1Nb"nFj7 yq*4{Hzo4IBnunZd=X٧˅ÓU&*ۆ|f \-%AKL^zI?4j!4].j"dV0M*~>^X+U{|5VrB-P<+i=q~eFU!^Kd&})ӭa\_~OQiJ7޽"Pu6uXW I1ROiuk7 C¢Xȅ,UiQ^:ǡe{.KtY#jPLa(~Zdp}䩜eYY3)(ߪ-zQ|y;wå\l[5#W[MG,mWQsՇµN"aXQjQ' %^n׭{$s)NIi{F{ngidv[fi/ rkS s.*–'nF9/c؄s)SfaPYIWГQTY'r'y]F5zJ*@:r;=^j؆ϩdf{$XSvdTwE;lcu<*'()Ʃ ~NHmz}{ ۲m<ʦa4OV+ćº*hQqmUe }׏{qYf>E_9n9 "byʾƚ?Fte;jgw\_nL=o#]}9fZǥn؁ M-{.4AxRXz(]URK+B~MU, I>_]!:4YO3:/^.rro%Wf=ReG'@|uu5PxlW $Slr$Eog"B~^"Mn@W'cGq?r%~zh. -_dQ_2Vshé Ҳ[yּ-L+ Q'=SޣFT\nJbׯ5@@ȾOm[_[y"(iӶ8Ƚ:{Xoozno}`6,ŕۃfTs^ B1GZf/FM۷&?V^ f_6lQP!|3_<سKdkiEݼue[uEd%?l^3EO1&ws_mzkԆ -* Ct+{B\I(/N[?Gm nؕBa\&{==]SG觫"2[5ܿLڣqhtGʲøʒǍ(=AQ>x^ #UsIw DxZkH=963MjթTO-%㽤J8:yo}XxL’%u]vp펊mUr f{~Ě|8Pbn\GVq(9M[׃k-NnXB$K$;>=v$+LiiXLD,YVLzO5">s.?ʡhKZ7eF;2A^ꎝ s2{Y!WצU(>͈Qdq)otwM-CV:_}N< ļÏP$*}701*a>dݛI$LyϣշMH& JkY_1UrYBn={^Pg@? <+ėk^miS}nM%E-*|Og>h׵u[7*O]rtj^":Coߤ\ј!e)ghc6R-BIDhzlHWiW)>j-l"~OUUYO7 rCz]kԽBwza`umJd&˩'&9QY5!ZE@2mTRx'{dhE=Ŀ..-./o.J1HtX=V(h'ivY$'j65f#8OMJl1(-F"^&9%8}qR>6) 0ID5]tWsSeѫ㓤o("P2 a$OgrQb!C~ XZn顸PеHCaM)Īa=Eho/e*T&lg]⑱9u_%Z! jOU?"hPCv74ӭ^#rvꪅ8$>SAhklɥ,[aovaZKFfE sNpVry+^ZjNy1W?uxKED[E!@?Em|j{N;n2c.bŜ]]4T5*}S}IPZM{ihM8~=skV4HuL}=z2hM S:ɓE$i~MY}鱴ܵ.>gD%?B_ˆCLGw5Oܗւ8)Bk~nASi| W?۝;ƒ0=ء)qJY*W)(BBTV i6b!K;p/qu:R5+hu_xѵGfj~]oh_0inu~~wGbE\WGq-E! CW|4^9|ܿ&"~k0A0itN-i^f_Si 6gQUj):;'/Vd?Bv/q>Uk75컪nnlӺC85caőCZQ2ΙlnQsZS2.mD[C.1(I3?g p*Tٳ(}?K8aX[چ[[\0}gK^U"Vi/mL=N E^,fP]EbL])iZCrC9~ޯ)x:TTPC^{ՖqʯZ3Ԫ&XBH1mZQ0àf+n?hcoN֯!C,ptJ5^!L4,5xM!4R} #ZnK^+!ܡc*) bGYM]5-pC|Ŭ%DK1զbMŴ5 Sxh˯ڰ0PJ~)Ie9F)ҴgWW]RmhRU,r XJ]M*)#fJhP\Ćej8(J~uWRR(E!R(^R<5Uh-CgJm+Rg-mUriL~ˬRԾV*—ҖҔNT v=dZWGM:RB N% jI%(tW`Dmϝ/ 3:Z+3vvKQE`QMxeҸ1sO4SJT0T h"CG܆zʤ8^Dv9WW{H▞Q$ސm  ҒB+,KgA_Q 0Xy h ֛\0~.B[5Ř+m+7joГWxVt*b^Mi"lAj 4ğiQMJړc\jKk(1k֐Ч|R0Pme*QaqbPjcUPX))JY5ljufȧ>:G4\)b;T?K7iҚ1/NK[Oš% I sM<;`:[YߩWG?`SJ~fU`J)SuEc,V.9V/%SPhj(2=iR%"YT:ʰcC=RRѢuW W9խQxݕKɣM;(!c:~ n)q1 >82XFϖe}f͜RK,Ztب9Ywh BuR'J)T2xjSs f^:3udU׼`꒪+LQ_U.+VeMU_k j'1?jCihϥ+:6e[N^ZU~n%k`b^+`ӖAunIj+Ve]_cJ吐\9 :)i5=6 WPY,u2g?T-ah-bˤ\R֚Уw$H9R곹w( I 9Mdw>y^shVf5v b{ܓ{߷f Q&Й'{?"|>V]p/~."+:~W>.qpȮtFeV~ ~?8&FhS%﵈h-H]EsUd8V/>^2P0TMt} ιֶ5KRx1VJlߩm;o=]z^^1j0N;ui* \&o';}Gгw!c5k}OycV%sQ/M\椚T~9ҤGeP[>^UyVJKs}z q+XPa]?wޚ?'_T+B(nQPHm2A ԒNp!SfgR1")R%N1ǛlI*z%j$[[ IiSUU㩛)/щl ;qSǸ1XhO=Q|V6Q(U,٥Ylŭ-QZ _MfQ),̬*rRILߢ%cћq{5C{+Nv_2 `Zu-=G.KġLVLOĕe\Cd=2tIVΰ8mUUU) ~Զ5SzsyL&Ѫ/J*H(ɲ{YAMdRE3Mg.KcAQ3Ic2.%NNs4g j`ODXRCdT-*ƝIt~M&E īJ*U? z(>EttQV19xH6% bo (´=a4KK\H2i7-,Ge>OYd[\V*VǴ,x5ɫnMFZOe1fK{2Tv󂈸>0O EG\1/y QleӦM!2_K`d6EZA%˱vV+rh&Nķ +voRm;({gUYSi|[rDp7oNmh%t.,l$- zϵk˒5.)Rz'ԝSXYa\׶Z9Pkk6YلpZ3J UaZaN>ڙ۶ѣHM90Kktz|̜4dTUX4ajV5^9wr_:!*[k8 l{KҨl2yQm k~bOH0X)76m,yW)oN"+IdfRYѩ}PY~Z2nI$9qx퓴+BL*0+ Jj~ʣUhngw].`Sbu%x!a_(.qM;v7;ψl BaSGxWnDTg%橋- (_}r;46OSoGr0%OŜo> RiѤX_WegVۺj^A.IUQZmyJXd=UUF*zbJ]zUVuuN.ejRTMOG},OKPG)9";+O WeQ#ה$]3%,8N=*5yZyMrTgeUI:* &.N=Oq!(%j^SܤڢUfծ]W)}RI'm%jR`[Sya9a֯JaybDeX%4;а~lbH1B%_ڷJth4/E=wet}%'MVAu.)'/j_ h$/Ww/gЙ6a M*XwE9| :RLvdz,#: ꆹm|+T˘yd+eT#*BnTteEKD铗ݺ-1{l Y&C_D^{Y'K6Ff]trEPI AfH颸M34k pP_co=8=gWJ\mJorPֹ-4Oׁ5ϩAzOHiZѯĥSFrY2yqѩ%9~o"kZ|~G֩BcWu3bjyK C V-)+r4W_OBPn c<- K]~ShGj=>QQ{jĽB8nCZr;z8VzZ栻 WcIj^Q?&qu]|)ӛNz'ȥYjx*,k\*)w(ʼBIbi4$z SJ(hlo>7y#V4ݜ>I%(.|,*R&E8Hx~u! a_}2YXfeY?"_SzIg7ᤷl2L⚠[tf8h_!J=-Jf%|o%6^%czU=[L+)0 ["[wܔ]yk-'G,Ej&qL'"=ġ>#bXv,w8#w}$STdZB%nd:H]h- NVWȉS Q ^b$Eڑ~aLuxW$K;i8e֛Xw|:AiRUQUEkMgDLuT-购Oz5`s[D$GŒOf{{ <ŽjĽ%[OPA"RM51 DIzJ<笽m*!PT(ס_5M[<{Ya} }(/x =1yۡfM(IUޛ\?iXd9ELʋ]mu0j\ h# YyVWOkJc,E0hUap_8pS`jUĠݰL-nY\\{׀?Z\4wYI<փMZDZ)]@7yΙMwoExF+͋@J?ș?ϡm_# R,ݐǿ[\}ĸg9jq~gXU1$lRIZjm՛5%05ӎys~>󃧺BDc'2 550+^rWrbюE\렮V0=W^r?[c>.stmo aP]`_޳Cg 垚 C'e֩E֒!DT+}m\skг (iZYьjq6ŏy-W`m+{a7ѕo涄ʥQl+X;̥4hllBJMV%y.IƄQ.ܧF,LZSQʲ)$ir C˒2 M/2fׄL3BYY2M$lW":*dyR$q7_3D3QyU>ERZ{Q`zjj!JD}ZĶ:"6%gI% ch4k$&;$(KCjZ~;ʤf0H3j!o=z+H:W~n:KeS]u%Yv-},)/\=GUf_C__K0kɻ~h4N(EzlBr_[$WŪc3P^.I`%/e>%⤯8" E43-;B7_#BE7=$?Aukjc֡B 5OE?|rZJ 9ɇᦫk`!»b"}VW&j&3>Vi}K:?mʢj*7޿x*C7WbZ%c>}U[T 3+>~q <)\YћM)usk޻¡9ouՙ/eQf*?vެSTݨ]êۻD}^ILno\k]'=qoqCE/V؞y|[Ȧz [X:ϕy7%p* j;`1Qن}MXuc- y}R/qO|NㆮA_- Ɔ-suf}}ΏnUW"ƺbMX=Bai{ *.)qEJ6*%iUHn{k)"ѧoAxTZfHL+]~껪`XZ?n@@ha%DY0li=xf!ZP[[wȋ\^O6Jr_R1^;l?I6JX݁9iKJl"Ӣ,AyweCYcatVG]E!+RB@ЧH彎a'iKZI;QG0ja;*j\_^FdeJ/P%ުf- k~t/$Pq";˾5JѢ 2`( ), k~.u/!C7wՕݳu ETl˳Lkg弯7ơB:ZABsR'SW(iECt%Ԭ8pLזyH{mO-d^&Ձ 7 f5&OkL՟˅nP^雪sasՌ25(̃a+RESUX^\YPI4W^{j:jĵ zɾl|7ֆ#j=weMJ+I;OKTvқEɒ$>+ >T:}3rulvTVIPUF]O=d_޶\upRܫLS迧L>>m$ a 9sVj"ؗW&=k*qw晤YZVOk֦J O[IJk]MꍭXVcZ>k佄1I`R%^b 0d)5E+jOIgm#WU>22/HLǮLk)MSSHzڰn)g(=fM'O=l\I%!UaҺN\'=N^T$Spܲa~-eU6@5e>eX8S2˼ֲKv-/Ҳ@FZ^Wn4+ "]ukrUɊwv 0/uWuRI,ڄ>5m>% NHH"'+jE)Q#P$yE\a-tXʩʳ@i˒%U~jݤl*f vGr2KSc1(ϱae{:w;-#B!o!c^ \lIWSJb-_ģ=TUB^g' Wn_ȿG}'V*K鵸-=%3yXVJIg^l}:ēWPՅM-&1,s@f)F [FVA|?ʇ MTl8Zgh2Ըq17AHij}K`Zv\+‰TY(>w]]JlPlNM%%6TIgά*jη ݬQXR(_fsV]ł Icp/fhq=q9u01bkQ6Fa[Q; :…bb?UnZM4ܜI I]xUfۇ~iphnƪ0폾]/f5qg:E"?FSp6 DKo3okE~X, _K}^ gY5pQl3jsVjO}0"D}55N6;tcSqkPבQQ3 3ΐO^-%Y51A\@J*T,GP9"~kV=fRU[. E U# Bz 2]:ڥ*³""<If|OGDnAZg"R޸T%RH(i | )|U8H(˚U~DPf&J"ˊi稉RjUˮfɝdڂoTݘ|*"/QvZY~Bέ$)F FSӊ.+  KrcS%W5b+i;|˛D=(R= 6Hn J3o!v6ؽdmUǁ ɯϲ%MHB./}-ԧt72׬۷HF6)F! *LK*4NdRڷlNk߲Qai78l*#j_j;{>['cӖEW#ړ6_EASa_DӘ3 `'+1 _4mwG#9RTU}Hl m3Mp%YHB!DUC7ۭ q流eKܙE|Tν4^PSD?b]nqZԟ[<[}¤>:~)@IuBPtvjq {wi`kX׆CBs."V2zZҨbjIZYQMdBe^45ܳ.4Gy9Zz&Z ) 1 (h۞ҝ[^.iJ\媴&טɯ#3תJ[IO5ϪV 9{54fUƷKKb1 Қˁ|*рErEU\Zbƫy'w=tY,\ Ÿ: n"}0;&P\vB33Z 4$$}큝W9 mo)x#Qch ^.lKiV3ҾUϹI#kp g*ڝ=j1jx%kB'>,uV|P\b޽;s8!o8(lR,ɴ)՝ I=xCFd Le絹 {||4N%V yYv'3+_ekX:﷮CR*d>'JȥdM2|T_YXJ(k~W>8dzhkMsTߛ }4v"VoJ lȽe^:VxKJCӇ`! ð5jR6WB*.ԺiT'j%zsz*_0.bGYg 355/ZmYM >baٯUYj E>*=&%z^3⸫`GeV?X$,X_-XE)nRJ][b/_^4"htLiO=i6!bK_iѪXY!$Q*#QnjoQ{H2_EtZUuoʎh2l:Mm:4uz[XZ CxCBWbXdEA9U/3 _庮F"iH~4(I#JiФե% )_-?T4إɡu~&=ZB7Tv,CEnö 1LhS}P'BoW~uPSF`XU)"F^9y+WΗҼ@YWXK^UנIР#b\9}уqӜϹ,uhį߉Q(FA)i:x*3G۟3eύf[iŭy-NWf?RW)^Ce- *j?NIv*(*ȹugãgmm,}(\uZYUKR !2mA*҄C"p ˏYm*П%CZZXr YG4ڮS?%KJ{^*,U l 4a?B\0Ryoq,_bR.a?2uΣ b%AHjkNa4liS'r=Dcv=k1kҪqZ˰ϲQzⲙ%7CZ&v猟T=Uykʒ= =fų@[ܭ*2X׫Yr\=5eAxE AOR&Z 2>wIrŒZY픆L"mb]"ؿc"ԉy+Lh)o5p!Hd$=(4)$]]hABS)d xӠhYLۥgL6Dlu iy[+sv `P/ =fKdɣI79?4%.TXh"e $[\^9yk 0saAzlQ"I;_M H}`b"">qbDq1?ّUC @ԜTYa "fs2i8M~V("աE8 2/ҧNSr{Gk;pWx4S',ũ=uiDrǬVU[ҽ-L8kGYVqsgU%qHOy3*GܧϽZ\2x:FT#b:Kvk}č~LqSpoc.0̪j5A+)}|kN6n:S۲¦Kڔ(곌TR%,eӧj)|G94Ȋ}%?H cem,%5͝Ī(K,w$yϩ:,Itnr9 ?TSN^w\DjZGO w5d'D6w|pcfY[hUcjDM~o[뺄jײsjul=9\2JOBQnB%$*Io(U==qTu)]V QUv[C`g3c=_3|Y;Qf*.juV%+6ϝ`S֑\W!>qݖen?Dv]Ӥm9˖9vFmQ &X<-Hj\DRQN㺘R51 ?etzw1sXVLu.%XE{cԷTy/ZݰҐjsQF_(>^| "6ͣ?|5bWEO܅=1ЦrExD٧N Tb/*y׮0L"i4$54nRh O\  RR</S8E=̥q).lяB#)]E,Ȧ|uqMY%:DTkg_b%)a|%P?!oe G'N3mAmkPUS_pVԢ̌mHa_yӨ;B2#%-J^Y tx zPmPNE`ނiVOo\GҞyp0˵uAFK]KJ5ol.wO1?cL(jQOQIJwϣFS_bNjۿM|>@,AJT}+U?/^c|7G5.Pb [5t_˷gSwgƵNS:!>{z ]S<)D7*/jrRUV1(Pnin>wqsn5Ծ`U0/{v݋Rh _syM}4_Ώ$)8E.3z+C)$ຎk$?|SMF~;i :T;>l%1FӞ[6Sr7q,:m,y|E 0ͦGsR7IVooO`x듽oY mrǪ gԯ3-A6I5?AQj_բ!";JR":L=Q9]=te\:5~zDwƏE{ Zu9eyA4}OcO#T[}SyڨDDQ:uqV| R԰OaN%bPs@ijL-[苭Q"3ƻ7iO_߫=G=TrˬK&dIĩ4D\1KKzoڐi{-)i qޒUrm"rFG^r%`+ ꦶ8aU%" WMylnR)BQ9zִ~p*:r3ybɭhvT(nbj֥YVVTD$|No_fNŤ%K?漲6It6hjjb"|OJe美_sxA5w.[D򒟛vܙV >4N[r!PTy {ՌxǼgbqIj7_P$ -}NHcTi˒=.NTYC#~9ẹ/v^&eYHte1j.IQEGXYE 5G5ӫ2['=Ʉ-OP3X 6m7)ve]cOoR$4RwGy/U*WfQk@Gql5gP`w٧*kUUG8KJ;c_**ܑ I|/ҏCRipJm^ARъ} raySjڛOwuʛ1=ge4ꦞUnxn{C]W4VX*:\fQo%zbʋQuM㨵'I\5>A_'i IOwh,G΋ЧpM}hbg~.4 /lD!D*q*OKf%_"{ً*Xu=Uxp_8Ulc7]DzLdr-,LNE@krMucWS 66*l(Er3֌3ƮEvo:0Yuno^񃽼ZZUY N7lF餟!̲QZⲭQId$J:GY5ePɡ,Ӄk l*t$7]Ej,a#tC,W\ wau`@ⱇpvD2BM{KnRZ9>uquhJC)U\G#R EXARUwֶiI,ZCrUۻTbϥED}I,NҤ_i ^^[F]97VSK(cS3DB2n t9j{<5tOѮLl Cv)󤐘()yQ 0ۤTGܭ(mdVU&+[XQMz_QIct Rx&נ*K>/,bA_"rJBa5tYtքs2nUOl:*wg֊C\7yg-OϺX ~*j2Y5}\ϥZ(j{NFPKYᡶﰾ.v12 SDW^U-k NwhU9~jD;N/5+ugysK39(i13pGvژs-T_zmu?E]B]ahfdV:aEcz%,IEEȏ>}h* ɂKy#3GLhUDϡ"5FH*LjB¦+ BVЭGY/KF86eoOn&S[qH[U+HB]uaVh Շ*TWjUuQNseZ}Ƞ,SXw¬j_z1hDKsX2*Kܡ`Vq\V\ԾwS&E)NMjifҭ|jӧF߮wJBEd[9WdѶYߓvb?>%zK>9N7qmm׹C^M2;$5z:U8mX7${(cӈ&GMItiVݧi-rH~uVYSz+6MTJ9jOâ<Dz^m082~5oaz{_L{:l,G9zr)+HBU& g9®+eyz/r/ɓPNi^ky+1qi6FL9@w5~wHD_4#=jZjS+oFoYVL4,Ʊ]òR)Er^bV.&'+Aj;syQ[ɟ0 .-xB=*JRJрmx-oy`H[О%#<݀G:zƯLmfF%%W'TTt_=wsjV֔sN*Rd/:eRBJ~ۣ>]6-'HOI76L$'OMM{%ֆ_E=`'m՛Z۾F!CLU=Q]e92Jrj=rC}a[Wơo`z zW㶫; >#^&$J{j+~&?}lʟ$gꯋr,r% YM^?1SR[%M e!=AZKyuUaWְ,)*"(޵\Fcװ_|}{%Yxu")6e{fZ(~(QwreXEqlx #(6n(T9%ycۘιy9K.w1]g>`R 1ߦf?"g]2ëϲ>rr+i[r }-w PgR('׮"fqQC`^Te Xx%nu(v, 3AF5{כs<^M"-{\Wb4=xԓ9:hG3ݜN]K"9)Dl\ӇZ :3mxu5',m$D79 @+K3hы3-ysp3C"GrORťrf_bxGq6OiֿELenCUQNys+:U2HS -\Cg.xRf5$ym# ͷ|bIFaRd7%E<&ECZQ`./\;U[f7]SdI8o]G\]XVn5B"?}:ۢrx5!Js)Nd OM]T4No}HNF<&O\C7ݎs`l>}=J^B+ʩ2O$)$i&E mjU~23Iz- mkNGT}VW/ҵ2Z2NB6.JOq*ŴMUɟ9o꠭Ĉ>h/L'ύ\`T5UJq6V)52y죝Gs#z1K/ܨ0SfˮmᓆɊi@Bq0ki ӯ%'yKM.0NTg!{t̚7OBR?lrķ)avۮ6*9ѥR/YPvtjн Tn.4)L gbJh 7ٵw SW>TU|颙KQdSB(/Eyy֘iқDfNS&',kb~_@pAfmZf6}`{' ̾eJK+BWdS>v{ ĸExoMv)Q1S%Ҍ3b=Ogoܦ?:xk\uVwbk\;ڭ_k"?}]Ƥ"{S0.JsL?U]bXe>N0.)N¢ KZL2EHSd,plx nsFH ɢ.G C1|:A bvh0>-Ƞݴ )5+V}c!+-~Xlj*WPb(߲Ə.|(X]GmLFTƃn4W[%wf+xk޳1/'şY'l vuWaK9/-SdΉpfM} =kr+ r;JKSǞ-l<5T=qK>Of}O?T'KBТ>6 PQڙzj  1SMB/m}s*žG_7gV"aHrNؔSXK;5v˱86vˮ]sE#qk{(Gz}D%±G7:E|18/ʊ`nڅg0^Y9H+n۩b*bUk/:nϑ]'=w9[E{9dl۽CPLFE2IU*lG*GfSmZMvi>Y&X|)GH(Lb=R)P ҡ[ӧkLzp]Dj Et`4~ӊ(DdTT|jr2 ;\jz(/.9CG57#y!V9-"TQ3-yQYG :Qԅ]ҭrawҘPTNG>tLu@g*t3s4Zr/DՍjO'u":_S 6P2P)iμ*;(\4<7Vn:.UO~ 6#UQ( RWyd+N*)CtxSEckOVH;RQMuuh&esAe^Ue"5M4g+ DE&) 4 `WH 1s2/data/s2_data_tbl_cities.rda0000644000176200001440000001564213774601521015733 0ustar liggesusersBZh91AY&SY<:>+z{ޭo;&j]y6ªxA2a41M44LOSiLS 3j4y4=O C$Mi=24e3CSiAzO)@cTb < <&"OaLښhj)3e<56=Lei$jz6di#Si4Й)f zO 53M4mFțI6Pzhړɣjzdz OLMMyFzh4h64 @4&@hOi@L&MzOSM=&=4bf&@zCh4ƈC@hh" 2Ч64Ʌ2l4=ɵ4L C z@i4444L 2FADО(zS 0OMO Sڍ=DC)0F4Ih eOPM3 h4hFѢcI6hmHi"WRe*66$[%)Bsl I8i16C@f*!6@Hi؛BH 4 m 9B!6jQ PMBAImlIlHm&ЛE6 lKhZP6llfRǤ՞niTB?[B #BBX_aBK[#fñ%_ ?0X#mb9tm X/^Oh @q,hj< 1JI4)Yy4n,JE>;lm*ti)qf;S%@^XƶM4ƱHdN6 =>&JУ bIb5_N6HäT<BR&,FD8C/28`%VO< aFP %!Q@y!c5NTqՕgRƭY lp{{_4K" B +{6B}]>d,K3: jR@xw?ʕ߭r^AcZ܍>VE&=L?%6YŻʴx[gC^7r[u@{,oβUϋ攰Z!!8ՓŲ( D+l0|#yJS(Q&W ԑE b0n:G<߈ s ۬t:y'YJ~\# YCۄ0`ڵjs&Iu-v0Gڄ2E=6N׉_ Z}-tEHÅ}]oXe"̇7xշН|!A^'0dy=orB;4Y,h5ai- \_dyu +]jI.T9mVa'{or.XD ^ZS; vmk>Eųb$bAxT:]iA-(`;Ҟ[qx3q'7zO}nsP!OHn6"E*"XlX; wz-νUMwk`'I: * mڼ̲`A^wc!| Duɦ*%i{³ WCm=o^(nLtfmK> 3erl, f $`lecv$S1lqS0nt$Wy83w<jvH3'۬ gTn#&yhyN>چi> B B(Eri_d2m.OO6Ft`-T:ATxI¥ϙ:lV;Oai..lkBP0r~JαF腩< ʕ|/ J4PAkU/ˌ{T{F{-]Gm:Wu]faKRLvbZ74Bb,-'85Pk7A9Aw֣Z3fŗ=P{{- ݘ6;#yg@-6ms\)9J|IOSK;r20ɑ -{礪ڣPUYׇTBH2j/yaJ|m> 'q !^6fgkXԤm+FZl/:2Rpn dŢ-nIk Dk÷ Y_o0 •oU>+g ѿtb?N!Xl4 ׶偽GgQwid f;a`t>je4zȪ|g6b-& H@.cBLeeuA=iZXbHN h+nUOgF=$HWnwCiΤ΀"iTaTV)$#MuIudHty򅃰 u/ᐒ$%*vZ\؇rWz8=m(GT"U;4{#|Lj'hKOφnKUx}ojx7P`B@B"@vk1UO]-u6{~Iƭi2Ϡ{,uRfPaT8`uˏ[uumJ,bP*K\!HIE GRjݙ|m>"U}Q;"LCd ENzAiX$ &Ǹ&Ji0=$#Z),*blD@'7, pTF؈*NM &s,By% D3!jN'FʗKPXZV`Kxbq(h".snUT89Q!ʆ:? "Pc&FvVp\6z\i! ;ɣLd\0-=Wed&nud\!`ajְD,:AM N3$-F! ֆඹ\_֭emע`:vkj :R,J9P3 ^$0PYˀIEԹep(ud fDQ?SI<ݤ/0cItXW-jP5Ŋ9,:>%y*TQu y~Cn;'2V3dEKt+ڃqv lg C ;ѲLʁv:QhE9Q[{@(V۠Qհys\ PeFmߩEQA*t.jgtB4heHӌ#|2ul@dM߫zq>j {]ob"TN */6xdOu+fC)x&<__}m` ъqiپLܐb&0`E]0(ԁX&F1JA3oC갏]̩re$ȔEeֆg.kkT<4N FM̠㈸6IyN mӮuVZD3dC;D))*!$q 6a6*H$"-rE2Β]mE}~(&l|NNaoɔ̮ngxAdYr&,ػ>)޻U3!+Ӭ)zBC hn}@4Ntt2DΰcUE Nr*}h?> uĊEƤ] JLӉwȮs^4VsS.~" !{o# ]ocuUGWk [B@Fq3+,Q1p9S(Xazf}c+3'-{6ӖejtLވ;yx[Z|ʳDX`:K%us(ڮ iDT]n`r!kY3f=>Q% yb틂3}~LTz_ZgAO67Y]WΊX2^M9*7Ji8OCRC'^]Eؒ9+#hUYTLA˟G5vyZe%akBҿ}h0HA>Jq]rAWET;fo07T%G \ҫKj8Ũ㢃Th/&xYA&Q'U%];P8d X$ <>>y{c-)gRK,/$h;S%|}W:Rr{sœ1=Ͳ@sfkI[kA<Ⱥ( ekZl&n܀ B]<+j vhͅb[ud"Ldg$zYU1ɪBœp={]kDV͖Y?BDw+bNzSy^/#jߞH}*XjtYzDVs}V£tfMhAH7 [P!"4!ú[j4g6(3T[ӊO#1zᨩṷRzȍS  O?r %_F0g] YtŒR8/ L:ZGy<:m'|BɴOl[J3 늎O}/8^jSˈoL X;q/2|rZ&u4^ 9e8e BW;vNEy2WRi1wkDwX L: ÂufH׋I>_Ox r Gt84ųd6 Д:^,[,?y Ϝ1|E j`& 2m"}j}~B*ZCB)_1rPyfs,-w5t>U+mP)OXM QzE5(tw{$AHz65n"8X /# Tmd'li|>=u?nԞ{o3A(҇8pwi &FS&/+7{^XUAreFը\X+qIS_5u,s/2^ݸ .HЦ>ƣPtx X fԛ~F*F. و]+ K bP\f dAކ0#N*qv Fj[4ĩFb,, A2r.ƵN;)"aS2WI8v/Z qDi5vjWs!t; ҄;P_A/(kyNxiC[%b'h`bunFIyOI@>{ &ŖGÛ"yMcQVrH*h(a"/;%jI.=Ϣ:h#e:- :­%@TT *&(~T >~FF""eݻ]&'.7ݤ|LG 6vL#65j>cn՗aߓǎ5Ӹ!^ s84Ur*C#o{g*8Hׇr2YYw_s`<]BBs2/man/0000755000176200001440000000000014052002014011335 5ustar liggesuserss2/man/s2_interpolate.Rd0000644000176200001440000000332114050664625014601 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-accessors.R, R/s2-transformers.R \name{s2_project} \alias{s2_project} \alias{s2_project_normalized} \alias{s2_interpolate} \alias{s2_interpolate_normalized} \title{Linear referencing} \usage{ s2_project(x, y, radius = s2_earth_radius_meters()) s2_project_normalized(x, y) s2_interpolate(x, distance, radius = s2_earth_radius_meters()) s2_interpolate_normalized(x, distance_normalized) } \arguments{ \item{x}{A simple polyline geography vector} \item{y}{A simple point geography vector. The point will be snapped to the nearest point on \code{x} for the purposes of interpolation.} \item{radius}{Radius of the earth. Defaults to the average radius of the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.} \item{distance}{A distance along \code{x} in \code{radius} units.} \item{distance_normalized}{A \code{distance} normalized to \code{\link[=s2_length]{s2_length()}} of \code{x}.} } \value{ \itemize{ \item \code{s2_interpolate()} returns the point on \code{x}, \code{distance} meters along the line. \item \code{s2_interpolate_normalized()} returns the point on \code{x} interpolated to a fraction along the line. \item \code{s2_project()} returns the \code{distance} that \code{point} occurs along \code{x}. \item \code{s2_project_normalized()} returns the \code{distance_normalized} along \code{x} where \code{point} occurs. } } \description{ Linear referencing } \examples{ s2_project_normalized("LINESTRING (0 0, 0 90)", "POINT (0 22.5)") s2_project("LINESTRING (0 0, 0 90)", "POINT (0 22.5)") s2_interpolate_normalized("LINESTRING (0 0, 0 90)", 0.25) s2_interpolate("LINESTRING (0 0, 0 90)", 2501890) } s2/man/s2_earth_radius_meters.Rd0000644000176200001440000000142313774632166016314 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-earth.R \name{s2_earth_radius_meters} \alias{s2_earth_radius_meters} \title{Earth Constants} \usage{ s2_earth_radius_meters() } \description{ According to Yoder (1995), the radius of the earth is 6371.01 km. These functions are used to set the default radis for functions that return a distance or accept a distance as input (e.g., \code{\link[=s2_distance]{s2_distance()}} and \code{\link[=s2_dwithin]{s2_dwithin()}}). } \examples{ s2_earth_radius_meters() } \references{ Yoder, C.F. 1995. "Astrometric and Geodetic Properties of Earth and the Solar System" in Global Earth Physics, A Handbook of Physical Constants, AGU Reference Shelf 1, American Geophysical Union, Table 2. \doi{10.1029/RF001p0001} } s2/man/s2_lnglat.Rd0000644000176200001440000000250313774601521013533 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-lnglat.R \name{s2_lnglat} \alias{s2_lnglat} \alias{as_s2_lnglat} \alias{as_s2_lnglat.s2_lnglat} \alias{as_s2_lnglat.s2_point} \alias{as_s2_lnglat.s2_geography} \alias{as_s2_lnglat.matrix} \alias{as.data.frame.s2_lnglat} \alias{as.matrix.s2_lnglat} \alias{as_wkb.s2_lnglat} \alias{as_wkt.s2_lnglat} \title{Create an S2 LngLat Vector} \usage{ s2_lnglat(lng, lat) as_s2_lnglat(x, ...) \method{as_s2_lnglat}{s2_lnglat}(x, ...) \method{as_s2_lnglat}{s2_point}(x, ...) \method{as_s2_lnglat}{s2_geography}(x, ...) \method{as_s2_lnglat}{matrix}(x, ...) \method{as.data.frame}{s2_lnglat}(x, ...) \method{as.matrix}{s2_lnglat}(x, ...) \method{as_wkb}{s2_lnglat}(x, ...) \method{as_wkt}{s2_lnglat}(x, ...) } \arguments{ \item{lat, lng}{Vectors of latitude and longitude values in degrees.} \item{x}{A \code{\link[=s2_lnglat]{s2_lnglat()}} vector or an object that can be coerced to one.} \item{...}{Unused} } \value{ An object with class s2_lnglat } \description{ This class represents a latitude and longitude on the Earth's surface. Most calculations in S2 convert this to a \code{\link[=as_s2_point]{as_s2_point()}}, which is a unit vector representation of this value. } \examples{ s2_lnglat(45, -64) # Halifax, Nova Scotia! as.data.frame(s2_lnglat(45, -64)) } s2/man/s2_bounds_cap.Rd0000644000176200001440000000310313774601521014364 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-bounds.R \name{s2_bounds_cap} \alias{s2_bounds_cap} \alias{s2_bounds_rect} \title{Compute feature-wise and aggregate bounds} \usage{ s2_bounds_cap(x) s2_bounds_rect(x) } \arguments{ \item{x}{\link[=as_s2_geography]{geography vectors}. These inputs are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects (e.g., character vectors of well-known text) directly.} } \value{ Both functions return a \code{data.frame}: \itemize{ \item \code{\link[=s2_bounds_rect]{s2_bounds_rect()}}: Columns \code{minlng}, \code{minlat}, \code{maxlng}, \code{maxlat} (degrees) \item \code{\link[=s2_bounds_cap]{s2_bounds_cap()}}: Columns \code{lng}, \code{lat}, \code{angle} (degrees) } } \description{ \code{\link[=s2_bounds_rect]{s2_bounds_rect()}} returns a bounding latitude-longitude rectangle that contains the region; \code{\link[=s2_bounds_cap]{s2_bounds_cap()}} returns a bounding circle represented by a centre point (lat, lng) and an angle. The bound may not be tight for points, polylines and geometry collections. The rectangle returned may depend on the order of points or polylines. \code{lng_lo} values larger than \code{lng_hi} indicate regions that span the antimeridian, see the Fiji example. } \examples{ s2_bounds_cap(s2_data_countries("Antarctica")) s2_bounds_cap(s2_data_countries("Netherlands")) s2_bounds_cap(s2_data_countries("Fiji")) s2_bounds_rect(s2_data_countries("Antarctica")) s2_bounds_rect(s2_data_countries("Netherlands")) s2_bounds_rect(s2_data_countries("Fiji")) } s2/man/s2_is_collection.Rd0000644000176200001440000000742514044456532015111 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-accessors.R \name{s2_is_collection} \alias{s2_is_collection} \alias{s2_is_valid} \alias{s2_is_valid_detail} \alias{s2_dimension} \alias{s2_num_points} \alias{s2_is_empty} \alias{s2_area} \alias{s2_length} \alias{s2_perimeter} \alias{s2_x} \alias{s2_y} \alias{s2_distance} \alias{s2_max_distance} \title{S2 Geography Accessors} \usage{ s2_is_collection(x) s2_is_valid(x) s2_is_valid_detail(x) s2_dimension(x) s2_num_points(x) s2_is_empty(x) s2_area(x, radius = s2_earth_radius_meters()) s2_length(x, radius = s2_earth_radius_meters()) s2_perimeter(x, radius = s2_earth_radius_meters()) s2_x(x) s2_y(x) s2_distance(x, y, radius = s2_earth_radius_meters()) s2_max_distance(x, y, radius = s2_earth_radius_meters()) } \arguments{ \item{x, y}{\link[=as_s2_geography]{geography vectors}. These inputs are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects (e.g., character vectors of well-known text) directly.} \item{radius}{Radius of the earth. Defaults to the average radius of the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.} } \description{ Accessors extract information about \link[=as_s2_geography]{geography vectors}. } \examples{ # s2_is_collection() tests for multiple geometries in one feature s2_is_collection(c("POINT (-64 45)", "MULTIPOINT ((-64 45), (8 72))")) # s2_dimension() returns 0 for point, 1 for line, 2 for polygon s2_dimension( c( "GEOMETRYCOLLECTION EMPTY", "POINT (-64 45)", "LINESTRING (-64 45, 8 72)", "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))", "GEOMETRYCOLLECTION (POINT (-64 45), LINESTRING (-64 45, 8 72))" ) ) # s2_num_points() counts points s2_num_points(c("POINT (-64 45)", "LINESTRING (-64 45, 8 72)")) # s2_is_empty tests for emptiness s2_is_empty(c("POINT (-64 45)", "POINT EMPTY")) # calculate area, length, and perimeter s2_area("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))") s2_perimeter("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))") s2_length(s2_boundary("POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))")) # extract x and y coordinates from points s2_x(c("POINT (-64 45)", "POINT EMPTY")) s2_y(c("POINT (-64 45)", "POINT EMPTY")) # calculate minimum and maximum distance between two geometries s2_distance( "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))", "POINT (-64 45)" ) s2_max_distance( "POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0))", "POINT (-64 45)" ) } \seealso{ BigQuery's geography function reference: \itemize{ \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_iscollection}{ST_ISCOLLECTION} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_dimension}{ST_DIMENSION} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_numpoints}{ST_NUMPOINTS} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_isempty}{ST_ISEMPTY} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_area}{ST_AREA} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_length}{ST_LENGTH} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_perimeter}{ST_PERIMETER} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_x}{ST_X} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_y}{ST_Y} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_distance}{ST_DISTANCE} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_maxdistance}{ST_MAXDISTANCE} } } s2/man/s2_cell_is_valid.Rd0000644000176200001440000000274314052002014015027 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-cell.R \name{s2_cell_is_valid} \alias{s2_cell_is_valid} \alias{s2_cell_debug_string} \alias{s2_cell_to_lnglat} \alias{s2_cell_center} \alias{s2_cell_boundary} \alias{s2_cell_polygon} \alias{s2_cell_vertex} \alias{s2_cell_level} \alias{s2_cell_is_leaf} \alias{s2_cell_is_face} \alias{s2_cell_area} \alias{s2_cell_area_approx} \alias{s2_cell_parent} \alias{s2_cell_child} \alias{s2_cell_edge_neighbour} \alias{s2_cell_contains} \alias{s2_cell_distance} \alias{s2_cell_max_distance} \alias{s2_cell_may_intersect} \title{S2 cell operators} \usage{ s2_cell_is_valid(x) s2_cell_debug_string(x) s2_cell_to_lnglat(x) s2_cell_center(x) s2_cell_boundary(x) s2_cell_polygon(x) s2_cell_vertex(x, k) s2_cell_level(x) s2_cell_is_leaf(x) s2_cell_is_face(x) s2_cell_area(x, radius = s2_earth_radius_meters()) s2_cell_area_approx(x, radius = s2_earth_radius_meters()) s2_cell_parent(x, level = -1L) s2_cell_child(x, k) s2_cell_edge_neighbour(x, k) s2_cell_contains(x, y) s2_cell_distance(x, y, radius = s2_earth_radius_meters()) s2_cell_max_distance(x, y, radius = s2_earth_radius_meters()) s2_cell_may_intersect(x, y) } \arguments{ \item{x, y}{An \code{\link[=s2_cell]{s2_cell()}} vector} \item{k}{An integer between 1 and 4} \item{radius}{The radius to use (e.g., \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}})} \item{level}{An integer between 0 and 30, inclusive.} } \description{ S2 cell operators } s2/man/s2_point.Rd0000644000176200001440000000213313774601521013402 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-point.R \name{s2_point} \alias{s2_point} \alias{as_s2_point} \alias{as_s2_point.s2_point} \alias{as_s2_point.s2_lnglat} \alias{as_s2_point.s2_geography} \alias{as_s2_point.matrix} \alias{as.data.frame.s2_point} \alias{as.matrix.s2_point} \title{Create an S2 Point Vector} \usage{ s2_point(x, y, z) as_s2_point(x, ...) \method{as_s2_point}{s2_point}(x, ...) \method{as_s2_point}{s2_lnglat}(x, ...) \method{as_s2_point}{s2_geography}(x, ...) \method{as_s2_point}{matrix}(x, ...) \method{as.data.frame}{s2_point}(x, ...) \method{as.matrix}{s2_point}(x, ...) } \arguments{ \item{x, y, z}{Vectors of latitude and longitude values in degrees.} \item{...}{Unused} } \value{ An object with class s2_point } \description{ In S2 terminology, a "point" is a 3-dimensional unit vector representation of an \code{\link[=s2_lnglat]{s2_lnglat()}}. Internally, all s2 objects are stored as 3-dimensional unit vectors. } \examples{ lnglat <- s2_lnglat(-64, 45) # Halifax, Nova Scotia! as_s2_point(lnglat) as.data.frame(as_s2_point(lnglat)) } s2/man/figures/0000755000176200001440000000000013774601521013023 5ustar liggesuserss2/man/figures/rc300.png0000644000176200001440000003652613774601521014374 0ustar liggesusersPNG  IHDR,D;sRGB pHYs.#.#x?viTXtXML:com.adobe.xmp Adobe ImageReady 1 ).=;$IDATx E;k& d!B!NC yB5O}$, "Ld\6$3dL2۽]wzٲ`fvuթSN:}jm{YΊى.~_ys\'Z Usy9PoߐA RVg,}[R7Y*eEiJV 3^g0Z1Oʯ~U.rȁ{W'뻮^$-u+XR9N1!m;n3o9ZJ͑r8Nj`0W /rȁ{/3RV˗?XNܗŃBAǯ 2]JR\+pQ٣P:&>.㤙;*hmsEW ʺ|ٳ nx7]3uܭt~Z;}A{Mdd 2Wrҝ'79Ac5E#(0S0ʾ6R9sPV9PAā= Z]%}æ' G8E7T^x/rȁ4hT )X}Rẫ\,{5u]Y^@JTۇ1Qȁ"0e~O ee碵e9Q9pppU"`MIΗX?p/dMwYel4>Hn2P!]o9|WZ_J)A"j%sǾԭ"eU=c[|7WZUI | jdRvTpX\u^MpêFhok)Nζm/|^pSڰ+rݶfVqY8Ӟ4 e#m&Na?:jTYZ{12ܯTU-Ͱ'irWE-˙Z__ߐ6*wpr*Dmm6F2ӦM+4iұGuFŅS7kh'u,:.F.e^MFte:ڻ&X:p7 vT%FTRC@AWeQSWW%\h]imIx vرUr؇Iill֑dNᅜZ%@sPsTC/suJz֕R RScOL +,VЀ) @!fNxXu5'IKX:-uE52EyEئNKp]tE K}ܸqDbd*;!^ٰaßB+,[SS3J1?%.8Nu`% KkeK$;#*˱>%A(/c,6?cٿb;TWѧe 4jngZ?GR).sMR::v"(WTT޽ctAk]{Ɓ:MA|7^6.;2UO'|K|OvhH-Jw|XHpIsGnݺ-ՓgɳByʉkljr]E>$n!JJlxqyN><3i'M=(a2ac~Iϫ7σY/{kӺN7@XzVc O`,e$7egm6^9qHUfRXm#ל/_qGYr.:L~^Њ9ݕ-el2qij@ہI+ayLCo(U]mĚ~ @`Fz'; AXo|/P'N|wBT~ ?h!OՈ+ƊG"LˉSDq?$?盔-?ޜxPk %Û^HKkj E (*x3Z<$ĿCÝFECOd<1 K*F.+?sYplyVHt& s9 RIy_3E8J%_# aUNxsĈ mjM$eUEk CnϦ6zM<9ưKC|RFxm8ʃпQgB_Fnl~Ws 3)gҹ\x}`<\P,yCs< GPQ//.ux՗(W}T|ΪoƣF,4 q6[6P"kjj.@(a`ueEEe34jZ*!MF %␐ʢ|>>)+[Y𥠜#UAc !tq܀ͭtRR]\ì̕ÖAّ(O/t_YeS=M&h%џL^Y_kϪ,ZE3qwҸϲJ |/^@k@|{.~Y.#:UN V=۹ūػΟT)N.NJKe:ΎxGpU^C'wei.QmQ:ɂҵ뙺wmzQ-zQpFQV)g)5ʺ}+ 3{Zp Τ!̤YPI}:1""HjjT+(4xؾwן9w=틡w6'*8ϯm%Mc)Tm]4V2h1N߼_'a2~ zD}%h.Ѥ |/O}CЬW-لY{E;aQ஧RdA%Lae]xltoAB<:RI]^B#!nS|I IƆX , &O/ey#Dr7cÐH$C< xC،<*彑gݥ3ׯ *VUv|5 %püy'4@Z;,3bFyif; 6 wylì%e 'eU*IGIEG^R۔K#Wb *9'ȃ>TJy@iifNN7\pzjWz#%:Hce3>$ ꣺3p>8 jh6<}ןΘ#1bi y g<' RN:+ai%\Y^ȉ7$5 1_5FKݲb]0DeG)3ϔ9ބN}EH6ԽSȩ\;S%3Ŵ>3NDaFcE}c„ zp_y\[V>l^Uȯg趽֎MR]C<]\MLsS)+uvf$JG_&JOf4É3܍ BhrNBZ;N3}ٙ\~BQIzیcԴn$#@9UptȾs}MuP' \r;Q~S0| Z6FN Rd@Q^,5PX)htN~A\ \عmlllrB%?m;ֲRy34?}x˵*%%>R|^a+ U׷[6]" $ߦzXM]f $,E\7WZ"$“/p_D*~)UZ#14o-~Ag5AEEsh .a%w1(x8Xq s~u4~͚Irj,RVHzIǜ[Zqhԍ*nntBƙB>gr]ዔdㆍK#^rF虅:F>IY%@F^ qXSSCB GR*N~/n ^-|F,5=;{>Li _TWmݛYnB ;0o~O) 3xx^%2׋d,Mes竊¹IU=ٳO?t%콣4ɘ{Oz9K&x_vГgN[gDѝF05l9ALxiw4#@ΈڡQ$VYQai6uΡUmW7mlTErbܚef-L~(4NklEEQ(X- РwcP7IiY(MC9t2ZaIv7FH,K@?IJĚLQ8ZQBpn&BQСEY`alnV+70$LlfD7qJh2NuV]M@[ދ9["eOJhr*րW<(mLWrघ  f5cmݺ5Ki'~ |~-H 2}~\*++1cnx*/&K޶c䊹bn97kb{uuRJp,|)CMMsXU`}3*c1ػw H8#eBc:8MG’ש0Qi"+0RF2V@􌔕cv7| >=ڔ2TXac {hxK"ˣbKU8QRmn|ZPZds*)_wRGKZ͈ ܬ"vrY!i(kko{`w6@xr: CCV㧸zѳ\į4Y!M+}Rc0ao3cR խ,lawǢhTR:4pRBª\4|ڀ{ule!L~H:H/-Țf۸_[T>ofZRRdΗ}?0˚5WS?awynv]{_xa8YaJMxEk؃nKt "_ҏR,)^$VDcf29#yeQ G016UTqTT&xY qee pzɯ ß^EHr#c aJ*Jw&JP\8I5\ WtZ)C]G& WKcv'Z5tNDpEܫ&kPߡ5~5ȸQ0I 4*EeXGlh }LςY os)t0^,!ڂf 1*}`< 7 ̱%+cQb.`֖W9缲2QY1t]g>Wm*T8x+Ӌϴ3w'۵+ñi`iAF>*03Ac12 xyXJp%}eއsQ\$tpvHPކ(>ec{qa-Je444`MJkqW/A NY%,T\FeƁ˅ r;%NJh-<+c%xNio볶B1DtzծsԱ;wnNGGݻw\#+-k?kH IY$q@cԭd8Wg~ᎋ7N9nUt\qQs{렰 -1akjIƭb0nӌ%߽伆gµ<V(,tPFط-O=3gvG H*Au)am:YT{Xv -4V_\H~vCD/17Kpt7ˬb|ŪJطV7jDJd-jOfx~ſcJk{ ]AB9$k L"FG-l᱉Ԡͺ%?f#o1@q ږUCn$g_;kb`XMWϠj7iS a+PDFfԜJ>œd62Ic 󜱺gy8 D/w(P' }0IL=ccϠW8}񛅣QjFoԨ緟hDJH!O%fA1NlM*$i2fPAJu&a̪~0NdMbV[ ӘnSN5- uI|jw xrgu5+f"ik Ѡ&5wCUa@F4M LZ!bVCwu@O횝#j:H,My<& Crʚ(p'M(cLK( #j˃x>C"5VCz-_HGxUsfN~ex#Qb.VFF(u^{gΠ-u\4QC؛j$`/)&}iӼ?oM":R`f6 pKHq8Uz ֭d4`~tZk}5N7s;+56iR|RV⩔%ea(E(뼓Hf)Яn8/F<OEn%kY|1Z9_F)<ה2Pm-^ (ecC*Zrjjj+{в[Iz;< !d-y?ZEx+/ªµH3 /O f>+Rox6X)gd4, GHmFy8 0!vO% 2݈ơ %ũ1N kY9 s(+}9ę߅%tqķFpItCt'M u&pAd? MP>Du;葆3Ն X4:(s9jWQ "%j&(ʡgX: oI)po{ ObwYtzڀ e~eįʗ+R7#?GidCQ޶AHȝ_͹fF4|G*'FuX;ke RrxJqK>BXkeb3M"A Clx _$=,m:=FX[0nc,}+n=c^呜Z"1J(<6;X0*[WƅMܦs>χWƙU6ሮO3o9t oGz574>|gwevYoƅ8.sѢEGki`Fk ⳌUqx7hFho#|SGù/Il*x`=Ğ B5xiow UFTiRz|!'"$8BD*x4!teA%X1rDw}*JZH%bM~S(g#D">)<dz e>`P*z hx*mSX'|v]8_ Xީ^ <@>Zc䃛4Z#NK-Ӊɋ2ކB^: w-p mq_cڴŗz3 y :+J@J[+DK*,wنv ƪǼ; ?9xTVGAtNsݔ .8]WS c}Q߹P٤6/g^т6ܕ\ĜjsKB>C J'۾&uӌaqYD[0ƈNNEIuJQ^3x|(1hV"\JZ]8z d{16,e ngYh4l"ϣPHKFPn\? ĝ8n֦/%op2PQ#p6G>@0)Zxw!҄}7`&!C9Eצ M'T](0n{eidpk Ƅ^g= oєG<!{f*8l)/m6.FRhLe67[(*)y.fYI9Gў:n'\.N+y7ϓ8'8$!mCNT1"z R[Y7/_M !::㛉VYՕ)$ݍ6],K]9g TB..f@k fxJUP8~P|jTd,l wi{>1QF@_ɥ {+-_Ќ࢟?U|DՐ)#R[ ^Ga/=N}92ZT<*DDX\Ƚ]J)5g׽ npW|DO~`(+՗+=ϻ|JCv;keUDk'EyK[ڝe ?a_)@m(PaVxp9P@}Z4XKl)E9pqpґ ~nߗvTB46jYkd|V1UOt$M+z%FtŷbxEB)d di,- ˇ)>9PAˁ֠ThLИ@֦ut&֐0THtGμijIw.i";ph)3]&Op &?i^m'w<&X[򴛪!*Sl~-F9PߝTJ -"Q6Ǭ8aOd W)kmn9pK }8mV;(a-9'Q2NSjhñ4tp B0鎹V /9ps`,,,;|m;_J{42qĦ/cZjs=EʪceƎG/P_ PVfay|3 PtE9@A/y WF)c3ԘPE2?we'z w;،=і( }N5 F+jjߖȁ":€z9|YPv+aU+Zf kjϳrcyY{7b 7<|\xBc_£ :_-r w=/]E9g +b8~ pI*(ͱV}̌#}EyIݢ/>갔Ύ6:tiNP,^XVφ:cV%l͉ґ.q)NKynҊE*r{@ĉ(kgɶm2_aYFę˷q(Ϲofjk='{wkgE#ۻsf\CV^JO=۱Vh¢{6۾TN OEl1,)M+f?ӻ%h>rl:O]w=$@RSEb&?QVW ,rȁ{faٵVX=VY+s,kfP~ڒRs9TGVaeLT3u*hrW{YnNEY$ʪNJaE.8G ܬ)dDvP3Ja# sm{՞"HKN++rȁ{ga(Y:Ұg_vYSfv}BھmK?sfUW]է#P o4ŧ"89QhdIENDB`s2/man/s2-package.Rd0000644000176200001440000000261013774601521013562 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-package.R \docType{package} \name{s2-package} \alias{s2} \alias{s2-package} \title{s2: Spherical Geometry Operators Using the S2 Geometry Library} \description{ Provides R bindings for Google's s2 library for geometric calculations on the sphere. High-performance constructors and exporters provide high compatibility with existing spatial packages, transformers construct new geometries from existing geometries, predicates provide a means to select geometries based on spatial relationships, and accessors extract information about geometries. } \seealso{ Useful links: \itemize{ \item \url{https://r-spatial.github.io/s2/} \item \url{https://github.com/r-spatial/s2} \item \url{https://s2geometry.io/} \item Report bugs at \url{https://github.com/r-spatial/s2/issues} } } \author{ \strong{Maintainer}: Edzer Pebesma \email{edzer.pebesma@uni-muenster.de} (\href{https://orcid.org/0000-0001-8049-7069}{ORCID}) Authors: \itemize{ \item Dewey Dunnington \email{dewey@fishandwhistle.net} (\href{https://orcid.org/0000-0002-9415-4582}{ORCID}) \item Ege Rubak \email{rubak@math.aau.dk} } Other contributors: \itemize{ \item Jeroen Ooms \email{jeroen.ooms@stat.ucla.edu} (configure script) [contributor] \item Google, Inc. (Original s2geometry.io source code) [copyright holder] } } \keyword{internal} s2/man/s2_cell.Rd0000644000176200001440000000455514052002014013160 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-cell.R \name{s2_cell} \alias{s2_cell} \alias{s2_cell_sentinel} \alias{s2_cell_invalid} \alias{as_s2_cell} \alias{as_s2_cell.s2_cell} \alias{as_s2_cell.character} \alias{as_s2_cell.s2_geography} \alias{as_s2_cell.s2_lnglat} \alias{as_s2_cell.s2_point} \alias{new_s2_cell} \title{Create S2 Cell vectors} \usage{ s2_cell(x = character()) s2_cell_sentinel() s2_cell_invalid() s2_cell_sentinel() as_s2_cell(x, ...) \method{as_s2_cell}{s2_cell}(x, ...) \method{as_s2_cell}{character}(x, ...) \method{as_s2_cell}{s2_geography}(x, ...) \method{as_s2_cell}{s2_lnglat}(x, ...) \method{as_s2_cell}{s2_point}(x, ...) new_s2_cell(x) } \arguments{ \item{x}{The canonical S2 cell identifier as a character vector.} \item{...}{Passed to methods} } \value{ An object of class s2_cell } \description{ The S2 cell indexing system forms the basis for spatial indexing in the S2 library. On their own, S2 cells can represent points or areas. As a union, a vector of S2 cells can approximate a line or polygon. These functions allow direct access to the S2 cell indexing system and are designed to have minimal overhead such that looping and recursion have acceptable performance when used within R code. } \details{ Under the hood, S2 cell vectors are represented in R as vectors of type \code{\link[=double]{double()}}. This works because S2 cell identifiers are 64 bits wide, as are \code{double}s on all systems where R runs (The same trick is used by the bit64 package to represent signed 64-bit integers). As a happy accident, \code{NA_real_} is not a valid or meaningful cell identifier, so missing value support in the way R users might expect is preserved. It is worth noting that the underlying value of \code{s2_cell_sentinel()} would normally be considered \code{NA}; however, as it is meaningful and useful when programming with S2 cells, custom \code{is.na()} and comparison methods are implemented such that \code{s2_cell_sentinel()} is greater than all valid S2 cells and not considered missing. Users can and should implement compiled code that uses the underlying bytes of the vector, ensuring that the class of any returned object that should be interpreted in this way is constructed with \code{new_s2_cell()}. } \examples{ s2_cell("4b59a0cd83b5de49") as_s2_cell(s2_lnglat(-64, 45)) as_s2_cell(s2_data_cities("Ottawa")) } s2/man/s2_unprojection_filter.Rd0000644000176200001440000000436014052002014016317 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/wk-utils.R \name{s2_unprojection_filter} \alias{s2_unprojection_filter} \alias{s2_projection_filter} \alias{s2_projection_plate_carree} \alias{s2_projection_mercator} \title{Low-level wk filters and handlers} \usage{ s2_unprojection_filter( handler, projection = s2_projection_plate_carree(), tessellate_tol = Inf ) s2_projection_filter( handler, projection = s2_projection_plate_carree(), tessellate_tol = Inf ) s2_projection_plate_carree() s2_projection_mercator() } \arguments{ \item{handler}{A \link[wk:wk_handle]{wk_handler} object.} \item{projection}{One of \code{\link[=s2_projection_plate_carree]{s2_projection_plate_carree()}} or \code{\link[=s2_projection_mercator]{s2_projection_mercator()}}} \item{tessellate_tol}{An angle in radians. Points will not be added if a line segment is within this distance of a point.} } \value{ \itemize{ \item \code{s2_unprojection_filter()}, \code{s2_projection_filter()}: A \code{new_wk_handler()} \item \code{s2_projection_plate_carree()}, \code{s2_projection_mercator()}: An external pointer to an S2 projection. } } \description{ Low-level wk filters and handlers } \examples{ library(wk) # simple conversion of individual coordinates *to* unit sphere # space wk_handle( wkt("LINESTRING (0 0, 0 45, -60 45)"), s2_unprojection_filter(wkt_format_handler(5)) ) # simple conversion of individual coordinates *from* unit sphere # space wk_handle( wkt("LINESTRING Z (1 0 0, 0.7071 0 0.7071, 0.3536 -0.6124 0.7071)"), s2_projection_filter(wkt_format_handler(5)) ) # use tessellate_tol to force points to be added to an edge # unprojection will ensure an edge maintains its cartesian # assumption when transformed to the unit sphere # (i.e., what you probably want when importing a geography) wk_handle( wkt("LINESTRING (0 0, 0 45, -60 45)"), s2_unprojection_filter(wkt_format_handler(5), tessellate_tol = 0.001) ) # projection will ensure an edge maintains its geodesic # assumption when transformed to projected space # (i.e., what you probably want when exporting a geography) wk_handle( wkt("LINESTRING Z (1 0 0, 0.7071 0 0.7071, 0.3536 -0.6124 0.7071)"), s2_projection_filter(wkt_format_handler(5), tessellate_tol = 0.001) ) } s2/man/as_s2_geography.Rd0000644000176200001440000000512514044456532014726 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-geography.R \name{as_s2_geography} \alias{as_s2_geography} \alias{s2_geography} \alias{as_s2_geography.s2_geography} \alias{as_s2_geography.s2_lnglat} \alias{as_s2_geography.s2_point} \alias{as_s2_geography.wk_wkb} \alias{as_s2_geography.WKB} \alias{as_s2_geography.blob} \alias{as_s2_geography.wk_wkt} \alias{as_s2_geography.character} \alias{as_s2_geography.logical} \alias{as_wkb.s2_geography} \alias{as_wkt.s2_geography} \title{Create an S2 Geography Vector} \usage{ as_s2_geography(x, ...) s2_geography() \method{as_s2_geography}{s2_geography}(x, ...) \method{as_s2_geography}{s2_lnglat}(x, ...) \method{as_s2_geography}{s2_point}(x, ...) \method{as_s2_geography}{wk_wkb}(x, ..., oriented = FALSE, check = TRUE) \method{as_s2_geography}{WKB}(x, ..., oriented = FALSE, check = TRUE) \method{as_s2_geography}{blob}(x, ..., oriented = FALSE, check = TRUE) \method{as_s2_geography}{wk_wkt}(x, ..., oriented = FALSE, check = TRUE) \method{as_s2_geography}{character}(x, ..., oriented = FALSE, check = TRUE) \method{as_s2_geography}{logical}(x, ...) \method{as_wkb}{s2_geography}(x, ...) \method{as_wkt}{s2_geography}(x, ...) } \arguments{ \item{x}{An object that can be converted to an s2_geography vector} \item{...}{Unused} \item{oriented}{TRUE if polygon ring directions are known to be correct (i.e., exterior rings are defined counter clockwise and interior rings are defined clockwise).} \item{check}{Use \code{check = FALSE} to skip error on invalid geometries} } \value{ An object with class s2_geography } \description{ Geography vectors are arrays of points, lines, polygons, and/or collections of these. Geography vectors assume coordinates are longitude and latitude on a perfect sphere. } \details{ The coercion function \code{\link[=as_s2_geography]{as_s2_geography()}} is used to wrap the input of most functions in the s2 package so that you can use other objects with an unambiguious interpretation as a geography vector. Geography vectors have a minimal \link[vctrs:vctrs-package]{vctrs} implementation, so you can use these objects in tibble, dplyr, and other packages that use the vctrs framework. } \seealso{ \code{\link[=s2_geog_from_wkb]{s2_geog_from_wkb()}}, \code{\link[=s2_geog_from_text]{s2_geog_from_text()}}, \code{\link[=s2_geog_point]{s2_geog_point()}}, \code{\link[=s2_make_line]{s2_make_line()}}, \code{\link[=s2_make_polygon]{s2_make_polygon()}} for other ways to create geography vectors, and \code{\link[=s2_as_binary]{s2_as_binary()}} and \code{\link[=s2_as_text]{s2_as_text()}} for other ways to export them. } s2/man/s2_options.Rd0000644000176200001440000000776114050664625013762 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-options.R \name{s2_options} \alias{s2_options} \alias{s2_snap_identity} \alias{s2_snap_level} \alias{s2_snap_precision} \alias{s2_snap_distance} \title{Geography Operation Options} \usage{ s2_options( model = NULL, snap = s2_snap_identity(), snap_radius = -1, duplicate_edges = FALSE, edge_type = "directed", validate = FALSE, polyline_type = "path", polyline_sibling_pairs = "keep", simplify_edge_chains = FALSE, split_crossing_edges = FALSE, idempotent = FALSE, dimensions = c("point", "polyline", "polygon") ) s2_snap_identity() s2_snap_level(level) s2_snap_precision(precision) s2_snap_distance(distance) } \arguments{ \item{model}{One of 'open', 'semi-open' (default for polygons), or 'closed' (default for polylines). See section 'Model'} \item{snap}{Use \code{s2_snap_identity()}, \code{s2_snap_distance()}, \code{s2_snap_level()}, or \code{s2_snap_precision()} to specify how or if coordinate rounding should occur.} \item{snap_radius}{As opposed to the snap function, which specifies the maximum distance a vertex should move, the snap radius (in radians) sets the minimum distance between vertices of the output that don't cause vertices to move more than the distance specified by the snap function. This can be used to simplify the result of a boolean operation. Use -1 to specify that any minimum distance is acceptable.} \item{duplicate_edges}{Use \code{TRUE} to keep duplicate edges (e.g., duplicate points).} \item{edge_type}{One of 'directed' (default) or 'undirected'.} \item{validate}{Use \code{TRUE} to validate the result from the builder.} \item{polyline_type}{One of 'path' (default) or 'walk'. If 'walk', polylines that backtrack are preserved.} \item{polyline_sibling_pairs}{One of 'discard' (default) or 'keep'.} \item{simplify_edge_chains}{Use \code{TRUE} to remove vertices that are within \code{snap_radius} of the original vertex.} \item{split_crossing_edges}{Use \code{TRUE} to split crossing polyline edges when creating geometries.} \item{idempotent}{Use \code{FALSE} to apply snap even if snapping is not necessary to satisfy vertex constraints.} \item{dimensions}{A combination of 'point', 'polyline', and/or 'polygon' that can used to constrain the output of \code{\link[=s2_rebuild]{s2_rebuild()}} or a boolean operation.} \item{level}{A value from 0 to 30 corresponding to the cell level at which snapping should occur.} \item{precision}{A number by which coordinates should be multiplied before being rounded. Rounded to the nearest exponent of 10.} \item{distance}{A distance (in radians) denoting the maximum distance a vertex should move in the snapping process.} } \description{ These functions specify defaults for options used to perform operations and construct geometries. These are used in predicates (e.g., \code{\link[=s2_intersects]{s2_intersects()}}), and boolean operations (e.g., \code{\link[=s2_intersection]{s2_intersection()}}) to specify the model for containment and how new geometries should be constructed. } \section{Model}{ The geometry model indicates whether or not a geometry includes its boundaries. Boundaries of line geometries are its end points. OPEN geometries do not contain their boundary (\code{model = "open"}); CLOSED geometries (\code{model = "closed"}) contain their boundary; SEMI-OPEN geometries (\code{model = "semi-open"}) contain half of their boundaries, such that when two polygons do not overlap or two lines do not cross, no point exist that belong to more than one of the geometries. (This latter form, half-closed, is not present in the OpenGIS "simple feature access" (SFA) standard nor DE9-IM on which that is based). The default values for \code{\link[=s2_contains]{s2_contains()}} (open) and covers/covered_by (closed) correspond to the SFA standard specification of these operators. } \examples{ # use s2_options() to specify containment models, snap level # layer creation options, and builder options s2_options(model = "closed", snap = s2_snap_level(30)) } s2/man/s2_data_tbl_countries.Rd0000644000176200001440000000261313774601521016121 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/data.R \docType{data} \name{s2_data_tbl_countries} \alias{s2_data_tbl_countries} \alias{s2_data_tbl_timezones} \alias{s2_data_tbl_cities} \alias{s2_data_countries} \alias{s2_data_timezones} \alias{s2_data_cities} \title{Low-resolution world boundaries, timezones, and cities} \format{ A data.frame with columns \code{name} (character), and \code{geometry} (wk_wkb) An object of class \code{data.frame} with 120 rows and 2 columns. An object of class \code{data.frame} with 243 rows and 3 columns. } \source{ \href{https://www.naturalearthdata.com/}{Natural Earth Data} } \usage{ s2_data_tbl_countries s2_data_tbl_timezones s2_data_tbl_cities s2_data_countries(name = NULL) s2_data_timezones(utc_offset_min = NULL, utc_offset_max = utc_offset_min) s2_data_cities(name = NULL) } \arguments{ \item{name}{The name of a country, continent, city, or \code{NULL} for all features.} \item{utc_offset_min, utc_offset_max}{Minimum and/or maximum timezone offsets.} } \description{ Well-known binary versions of the \href{https://www.naturalearthdata.com/}{Natural Earth} low-resolution world boundaries and timezone boundaries. } \examples{ head(s2_data_countries()) s2_data_countries("Germany") s2_data_countries("Europe") head(s2_data_timezones()) s2_data_timezones(-4) head(s2_data_cities()) s2_data_cities("Cairo") } \keyword{datasets} s2/man/s2_closest_feature.Rd0000644000176200001440000001043414050664625015445 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-matrix.R \name{s2_closest_feature} \alias{s2_closest_feature} \alias{s2_closest_edges} \alias{s2_farthest_feature} \alias{s2_distance_matrix} \alias{s2_max_distance_matrix} \alias{s2_contains_matrix} \alias{s2_within_matrix} \alias{s2_covers_matrix} \alias{s2_covered_by_matrix} \alias{s2_intersects_matrix} \alias{s2_disjoint_matrix} \alias{s2_equals_matrix} \alias{s2_touches_matrix} \alias{s2_dwithin_matrix} \alias{s2_may_intersect_matrix} \title{Matrix Functions} \usage{ s2_closest_feature(x, y) s2_closest_edges(x, y, k, min_distance = -1, radius = s2_earth_radius_meters()) s2_farthest_feature(x, y) s2_distance_matrix(x, y, radius = s2_earth_radius_meters()) s2_max_distance_matrix(x, y, radius = s2_earth_radius_meters()) s2_contains_matrix(x, y, options = s2_options(model = "open")) s2_within_matrix(x, y, options = s2_options(model = "open")) s2_covers_matrix(x, y, options = s2_options(model = "closed")) s2_covered_by_matrix(x, y, options = s2_options(model = "closed")) s2_intersects_matrix(x, y, options = s2_options()) s2_disjoint_matrix(x, y, options = s2_options()) s2_equals_matrix(x, y, options = s2_options()) s2_touches_matrix(x, y, options = s2_options()) s2_dwithin_matrix(x, y, distance, radius = s2_earth_radius_meters()) s2_may_intersect_matrix(x, y, max_edges_per_cell = 50, max_feature_cells = 4) } \arguments{ \item{x, y}{Geography vectors, coerced using \code{\link[=as_s2_geography]{as_s2_geography()}}. \code{x} is considered the source, where as \code{y} is considered the target.} \item{k}{The number of closest edges to consider when searching. Note that in S2 a point is also considered an edge.} \item{min_distance}{The minimum distance to consider when searching for edges. This filter is applied after the search is complete (i.e., may cause fewer than \code{k} values to be returned).} \item{radius}{Radius of the earth. Defaults to the average radius of the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.} \item{options}{An \code{\link[=s2_options]{s2_options()}} object describing the polygon/polyline model to use and the snap level.} \item{distance}{A distance on the surface of the earth in the same units as \code{radius}.} \item{max_edges_per_cell}{For \code{\link[=s2_may_intersect_matrix]{s2_may_intersect_matrix()}}, this values controls the nature of the index on \code{y}, with higher values leading to coarser index. Values should be between 10 and 50; the default of 50 is adequate for most use cases, but for specialized operations users may wish to use a lower value to increase performance.} \item{max_feature_cells}{For \code{\link[=s2_may_intersect_matrix]{s2_may_intersect_matrix()}}, this value controls the approximation of \code{x} used to identify potential intersections on \code{y}. The default value of 4 gives the best performance for most operations, but for specialized operations users may wish to use a higher value to increase performance.} } \value{ A vector of length \code{x}. } \description{ These functions are similar to accessors and predicates, but instead of recycling \code{x} and \code{y} to a common length and returning a vector of that length, these functions return a vector of length \code{x} with each element \code{i} containing information about how the entire vector \code{y} relates to the feature at \code{x[i]}. } \examples{ city_names <- c("Vatican City", "San Marino", "Luxembourg") cities <- s2_data_cities(city_names) country_names <- s2_data_tbl_countries$name countries <- s2_data_countries() # closest feature returns y indices of the closest feature # for each feature in x country_names[s2_closest_feature(cities, countries)] # farthest feature returns y indices of the farthest feature # for each feature in x country_names[s2_farthest_feature(cities, countries)] # use s2_closest_edges() to find the k-nearest neighbours nearest <- s2_closest_edges(cities, cities, k = 2, min_distance = 0) city_names city_names[unlist(nearest)] # predicate matrices country_names[s2_intersects_matrix(cities, countries)[[1]]] # distance matrices s2_distance_matrix(cities, cities) s2_max_distance_matrix(cities, countries[1:4]) } \seealso{ See pairwise predicate functions (e.g., \code{\link[=s2_intersects]{s2_intersects()}}). } s2/man/s2_boundary.Rd0000644000176200001440000001545314044456532014106 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-transformers.R \name{s2_boundary} \alias{s2_boundary} \alias{s2_centroid} \alias{s2_closest_point} \alias{s2_minimum_clearance_line_between} \alias{s2_difference} \alias{s2_sym_difference} \alias{s2_intersection} \alias{s2_union} \alias{s2_snap_to_grid} \alias{s2_simplify} \alias{s2_rebuild} \alias{s2_buffer_cells} \alias{s2_centroid_agg} \alias{s2_coverage_union_agg} \alias{s2_rebuild_agg} \alias{s2_union_agg} \title{S2 Geography Transformations} \usage{ s2_boundary(x) s2_centroid(x) s2_closest_point(x, y) s2_minimum_clearance_line_between(x, y) s2_difference(x, y, options = s2_options()) s2_sym_difference(x, y, options = s2_options()) s2_intersection(x, y, options = s2_options()) s2_union(x, y = NULL, options = s2_options()) s2_snap_to_grid(x, grid_size) s2_simplify(x, tolerance, radius = s2_earth_radius_meters()) s2_rebuild(x, options = s2_options()) s2_buffer_cells( x, distance, max_cells = 1000, min_level = -1, radius = s2_earth_radius_meters() ) s2_centroid_agg(x, na.rm = FALSE) s2_coverage_union_agg(x, options = s2_options(), na.rm = FALSE) s2_rebuild_agg(x, options = s2_options(), na.rm = FALSE) s2_union_agg(x, options = s2_options(), na.rm = FALSE) } \arguments{ \item{x}{\link[=as_s2_geography]{geography vectors}. These inputs are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects (e.g., character vectors of well-known text) directly.} \item{y}{\link[=as_s2_geography]{geography vectors}. These inputs are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects (e.g., character vectors of well-known text) directly.} \item{options}{An \code{\link[=s2_options]{s2_options()}} object describing the polygon/polyline model to use and the snap level.} \item{grid_size}{The grid size to which coordinates should be snapped; will be rounded to the nearest power of 10.} \item{tolerance}{The minimum distance between vertexes to use when simplifying a geography.} \item{radius}{Radius of the earth. Defaults to the average radius of the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.} \item{distance}{The distance to buffer, in units of \code{radius}.} \item{max_cells}{The maximum number of cells to approximate a buffer.} \item{min_level}{The minimum cell level used to approximate a buffer (1 - 30). Setting this value too high will result in unnecessarily large geographies, but may help improve buffers along long, narrow regions.} \item{na.rm}{For aggregate calculations use \code{na.rm = TRUE} to drop missing values.} } \description{ These functions operate on one or more geography vectors and return a geography vector. } \section{Model}{ The geometry model indicates whether or not a geometry includes its boundaries. Boundaries of line geometries are its end points. OPEN geometries do not contain their boundary (\code{model = "open"}); CLOSED geometries (\code{model = "closed"}) contain their boundary; SEMI-OPEN geometries (\code{model = "semi-open"}) contain half of their boundaries, such that when two polygons do not overlap or two lines do not cross, no point exist that belong to more than one of the geometries. (This latter form, half-closed, is not present in the OpenGIS "simple feature access" (SFA) standard nor DE9-IM on which that is based). The default values for \code{\link[=s2_contains]{s2_contains()}} (open) and covers/covered_by (closed) correspond to the SFA standard specification of these operators. } \examples{ # returns the boundary: # empty for point, endpoints of a linestring, # perimeter of a polygon s2_boundary("POINT (-64 45)") s2_boundary("LINESTRING (0 0, 10 0)") s2_boundary("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))") # returns the area-weighted centroid, element-wise s2_centroid("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))") s2_centroid("LINESTRING (0 0, 10 0)") # returns the unweighted centroid of the entire input s2_centroid_agg(c("POINT (0 0)", "POINT (10 0)")) # returns the closest point on x to y s2_closest_point( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT (0 90)" # north pole! ) # returns the shortest possible line between x and y s2_minimum_clearance_line_between( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT (0 90)" # north pole! ) # binary operations: difference, symmetric difference, intersection and union s2_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", # 32 bit platforms may need to set snap rounding s2_options(snap = s2_snap_level(30)) ) s2_sym_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", # 32 bit platforms may need to set snap rounding s2_options(snap = s2_snap_level(30)) ) s2_intersection( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", # 32 bit platforms may need to set snap rounding s2_options(snap = s2_snap_level(30)) ) s2_union( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", # 32 bit platforms may need to set snap rounding s2_options(snap = s2_snap_level(30)) ) # use s2_union_agg() to aggregate geographies in a vector s2_coverage_union_agg( c( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" ), # 32 bit platforms may need to set snap rounding s2_options(snap = s2_snap_level(30)) ) # snap to grid rounds coordinates to a specified grid size s2_snap_to_grid("POINT (0.333333333333 0.666666666666)", 1e-2) } \seealso{ BigQuery's geography function reference: \itemize{ \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_boundary}{ST_BOUNDARY} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_centroid}{ST_CENTROID} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_closestpoint}{ST_CLOSESTPOINT} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_difference}{ST_DIFFERENCE} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersection}{ST_INTERSECTION} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_union}{ST_UNION} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_snaptogrid}{ST_SNAPTOGRID} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_simplify}{ST_SIMPLIFY} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_union_agg}{ST_UNION_AGG} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#s2_centroid_agg}{ST_CENTROID_AGG} } } s2/man/s2_contains.Rd0000644000176200001440000001261513774601521014075 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-predicates.R \name{s2_contains} \alias{s2_contains} \alias{s2_within} \alias{s2_covered_by} \alias{s2_covers} \alias{s2_disjoint} \alias{s2_intersects} \alias{s2_equals} \alias{s2_intersects_box} \alias{s2_touches} \alias{s2_dwithin} \title{S2 Geography Predicates} \usage{ s2_contains(x, y, options = s2_options(model = "open")) s2_within(x, y, options = s2_options(model = "open")) s2_covered_by(x, y, options = s2_options(model = "closed")) s2_covers(x, y, options = s2_options(model = "closed")) s2_disjoint(x, y, options = s2_options()) s2_intersects(x, y, options = s2_options()) s2_equals(x, y, options = s2_options()) s2_intersects_box( x, lng1, lat1, lng2, lat2, detail = 1000, options = s2_options() ) s2_touches(x, y, options = s2_options()) s2_dwithin(x, y, distance, radius = s2_earth_radius_meters()) } \arguments{ \item{x}{\link[=as_s2_geography]{geography vectors}. These inputs are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects (e.g., character vectors of well-known text) directly.} \item{y}{\link[=as_s2_geography]{geography vectors}. These inputs are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects (e.g., character vectors of well-known text) directly.} \item{options}{An \code{\link[=s2_options]{s2_options()}} object describing the polygon/polyline model to use and the snap level.} \item{lng1, lat1, lng2, lat2}{A latitude/longitude range} \item{detail}{The number of points with which to approximate non-geodesic edges.} \item{distance}{A distance on the surface of the earth in the same units as \code{radius}.} \item{radius}{Radius of the earth. Defaults to the average radius of the earth in meters as defined by \code{\link[=s2_earth_radius_meters]{s2_earth_radius_meters()}}.} } \description{ These functions operate two geography vectors (pairwise), and return a logical vector. } \section{Model}{ The geometry model indicates whether or not a geometry includes its boundaries. Boundaries of line geometries are its end points. OPEN geometries do not contain their boundary (\code{model = "open"}); CLOSED geometries (\code{model = "closed"}) contain their boundary; SEMI-OPEN geometries (\code{model = "semi-open"}) contain half of their boundaries, such that when two polygons do not overlap or two lines do not cross, no point exist that belong to more than one of the geometries. (This latter form, half-closed, is not present in the OpenGIS "simple feature access" (SFA) standard nor DE9-IM on which that is based). The default values for \code{\link[=s2_contains]{s2_contains()}} (open) and covers/covered_by (closed) correspond to the SFA standard specification of these operators. } \examples{ s2_contains( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c("POINT (5 5)", "POINT (-1 1)") ) s2_within( c("POINT (5 5)", "POINT (-1 1)"), "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))" ) s2_covered_by( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c("POINT (5 5)", "POINT (-1 1)") ) s2_covers( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c("POINT (5 5)", "POINT (-1 1)") ) s2_disjoint( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c("POINT (5 5)", "POINT (-1 1)") ) s2_intersects( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c("POINT (5 5)", "POINT (-1 1)") ) s2_equals( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((10 0, 10 10, 0 10, 0 0, 10 0))", "POLYGON ((-1 -1, 10 0, 10 10, 0 10, -1 -1))" ) ) s2_intersects( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c("POINT (5 5)", "POINT (-1 1)") ) s2_intersects_box( c("POINT (5 5)", "POINT (-1 1)"), 0, 0, 10, 10 ) s2_touches( "POLYGON ((0 0, 0 1, 1 1, 0 0))", c("POINT (0 0)", "POINT (0.5 0.75)", "POINT (0 0.5)") ) s2_dwithin( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c("POINT (5 5)", "POINT (-1 1)"), 0 # distance in meters ) s2_dwithin( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", c("POINT (5 5)", "POINT (-1 1)"), 1e6 # distance in meters ) } \seealso{ Matrix versions of these predicates (e.g., \code{\link[=s2_intersects_matrix]{s2_intersects_matrix()}}). BigQuery's geography function reference: \itemize{ \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_contains}{ST_CONTAINS} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_coveredby}{ST_COVEREDBY} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_covers}{ST_COVERS} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_disjoint}{ST_DISJOINT} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_equals}{ST_EQUALS} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersects}{ST_INTERSECTS} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_intersectsbox}{ST_INTERSECTSBOX} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_touches}{ST_TOUCHES} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_within}{ST_WITHIN} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_dwithin}{ST_DWITHIN} } } s2/man/s2_geog_point.Rd0000644000176200001440000001023114044456532014402 0ustar liggesusers% Generated by roxygen2: do not edit by hand % Please edit documentation in R/s2-constructors-formatters.R \name{s2_geog_point} \alias{s2_geog_point} \alias{s2_make_line} \alias{s2_make_polygon} \alias{s2_geog_from_text} \alias{s2_geog_from_wkb} \alias{s2_as_text} \alias{s2_as_binary} \title{Create and Format Geography Vectors} \usage{ s2_geog_point(longitude, latitude) s2_make_line(longitude, latitude, feature_id = 1L) s2_make_polygon( longitude, latitude, feature_id = 1L, ring_id = 1L, oriented = FALSE, check = TRUE ) s2_geog_from_text(wkt_string, oriented = FALSE, check = TRUE) s2_geog_from_wkb(wkb_bytes, oriented = FALSE, check = TRUE) s2_as_text(x, precision = 16, trim = TRUE) s2_as_binary(x, endian = wk::wk_platform_endian()) } \arguments{ \item{longitude, latitude}{Vectors of latitude and longitude} \item{feature_id, ring_id}{Vectors for which a change in sequential values indicates a new feature or ring. Use \code{\link[=factor]{factor()}} to convert from a character vector.} \item{oriented}{TRUE if polygon ring directions are known to be correct (i.e., exterior rings are defined counter clockwise and interior rings are defined clockwise).} \item{check}{Use \code{check = FALSE} to skip error on invalid geometries} \item{wkt_string}{Well-known text} \item{wkb_bytes}{A \code{list()} of \code{raw()}} \item{x}{\link[=as_s2_geography]{geography vectors}. These inputs are passed to \code{\link[=as_s2_geography]{as_s2_geography()}}, so you can pass other objects (e.g., character vectors of well-known text) directly.} \item{precision}{The number of significant digits to export when writing well-known text. If \code{trim = FALSE}, the number of digits after the decimal place.} \item{trim}{Should trailing zeroes be included after the decimal place?} \item{endian}{The endian-ness of the well-known binary. See \code{\link[wk:deprecated]{wk::wkb_translate_wkb()}}.} } \description{ These functions create and export \link[=as_s2_geography]{geography vectors}. Unlike the BigQuery geography constructors, these functions do not sanitize invalid or redundant input using \code{\link[=s2_union]{s2_union()}}. Note that when creating polygons using \code{\link[=s2_make_polygon]{s2_make_polygon()}}, rings can be open or closed. } \examples{ # create point geographies using coordinate values: s2_geog_point(-64, 45) # create line geographies using coordinate values: s2_make_line(c(-64, 8), c(45, 71)) # optionally, separate features using feature_id: s2_make_line( c(-64, 8, -27, -27), c(45, 71, 0, 45), feature_id = c(1, 1, 2, 2) ) # create polygon geographies using coordinate values: # (rings can be open or closed) s2_make_polygon(c(-45, 8, 0), c(64, 71, 90)) # optionally, separate rings and/or features using # ring_id and/or feature_id s2_make_polygon( c(20, 10, 10, 30, 45, 30, 20, 20, 40, 20, 45), c(35, 30, 10, 5, 20, 20, 15, 25, 40, 45, 30), feature_id = c(rep(1, 8), rep(2, 3)), ring_id = c(1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1) ) # import and export well-known text (geog <- s2_geog_from_text("POINT (-64 45)")) s2_as_text(geog) # import and export well-known binary (geog <- s2_geog_from_wkb(wk::as_wkb("POINT (-64 45)"))) s2_as_binary(geog) } \seealso{ See \code{\link[=as_s2_geography]{as_s2_geography()}} for other ways to construct geography vectors. BigQuery's geography function reference: \itemize{ \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogpoint}{ST_GEOGPOINT} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_makeline}{ST_MAKELINE} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_makepolygon}{ST_MAKEPOLYGON} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogfromtext}{ST_GEOGFROMTEXT} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_geogfromwkb}{ST_GEOGFROMWKB} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_astext}{ST_ASTEXT} \item \href{https://cloud.google.com/bigquery/docs/reference/standard-sql/geography_functions#st_asbinary}{ST_ASBINARY} } } s2/DESCRIPTION0000644000176200001440000000374114124561362012315 0ustar liggesusersPackage: s2 Title: Spherical Geometry Operators Using the S2 Geometry Library Version: 1.0.7 Authors@R: c( person(given = "Dewey", family = "Dunnington", role = c("aut"), email = "dewey@fishandwhistle.net", comment = c(ORCID = "0000-0002-9415-4582")), person(given = "Edzer", family = "Pebesma", role = c("aut", "cre"), email = "edzer.pebesma@uni-muenster.de", comment = c(ORCID = "0000-0001-8049-7069")), person("Ege", "Rubak", email="rubak@math.aau.dk", role = c("aut")), person("Jeroen", "Ooms", , "jeroen.ooms@stat.ucla.edu", role = "ctb", comment = "configure script"), person(family = "Google, Inc.", role = "cph", comment = "Original s2geometry.io source code") ) Description: Provides R bindings for Google's s2 library for geometric calculations on the sphere. High-performance constructors and exporters provide high compatibility with existing spatial packages, transformers construct new geometries from existing geometries, predicates provide a means to select geometries based on spatial relationships, and accessors extract information about geometries. License: Apache License (== 2.0) Encoding: UTF-8 LazyData: true RoxygenNote: 7.1.2 SystemRequirements: OpenSSL >= 1.0.1 LinkingTo: Rcpp, wk Imports: Rcpp, wk (>= 0.5.0) Suggests: testthat, vctrs URL: https://r-spatial.github.io/s2/, https://github.com/r-spatial/s2, https://s2geometry.io/ BugReports: https://github.com/r-spatial/s2/issues Depends: R (>= 3.0.0) NeedsCompilation: yes Packaged: 2021-09-22 15:42:08 UTC; edzer Author: Dewey Dunnington [aut] (), Edzer Pebesma [aut, cre] (), Ege Rubak [aut], Jeroen Ooms [ctb] (configure script), Google, Inc. [cph] (Original s2geometry.io source code) Maintainer: Edzer Pebesma Repository: CRAN Date/Publication: 2021-09-28 09:40:02 UTC s2/build/0000755000176200001440000000000014122647320011676 5ustar liggesuserss2/build/partial.rdb0000644000176200001440000000556114122647320014032 0ustar liggesusers\ScKrIɋ&7!~!.aq@:!4]KkC\I ޿Dѹϝ#ZRBeF<ѣ>ZrT440 e){AJ/C6r&Wez{|ͅ%Ydre.QG&IC኶±-V֟ŽڸF`LO>5FCOS59uS d_=cS4<, Κz?#NpQIM+n?-r3;ۻ/U7tƠ8hkE,VȯWίjK7bmA]=\\DEg@&!n}FrŶSi\/_v׋;>3Ɖ%h(䱁Qww\c(\Tn 򎁑1YjMeʺ)W)y.TbJsRz1l ΀Ο a@ "8]|/H\uQkVǛ7=DP>)ܝzL%uXA~?$rCJES?etc}. WR􍫻hd^ C7nd+k<•,aAaVۮ5]CX~|~kkȖw7fSê"ԏ]u]Cg"zU+o$Vrװjd\&3 X^ybz}jzM*tmmX/{/\~8q-pH1N5K-9.KriQT*7KW\Jy^ZYl ] iZ?dH-޸ QJUC.~ {QaKh_Ɩ^T66bH,XN_N\!y!o+U1:tH'qb^Nܫbn`M3p^W}RW9b?`>obVuOi޴sQK<+vS6F/P5:\(+?XL{_i3CK%_,*M])rLUh?%}m9O \`>g8 SQHԯޅBoMDyt~=UaxXնࠢڌؤ@dHi@wj e9qo}c 7&𰪅s*-$SjAƌe1PĊx[ 0-S #\e>;Et)F5 =8T7v |T)NYKڻfFAdH12+~,RyO(αi5 k(TEFo6XxN(M2(-d) -V۰-S"Yljz7U5@nn`ێI!D7n28ATji hs'B wQ'qX'.;+.P揖DFOOFbDh튶 %ѨK⽛0A5cnen m+(+[' 9 *AfHiv1A^14zHYc+Ltʓ09諛:wz} ^EWq|_;[ű,ld 0Ss`kwKn5weEl\T+kƢ㽳m{k,y}gKܩ:/HD_Tfp`_: ^&/Em3'{>9{8=== K/YHYL?j*eS;-SMoA5J_WMnX,*؂|)I 1`='( ~COUAwJ_**liX-ۈ#? tlGy`؀RAyӽ=Bv ;8iMo k?STRz'8ոZR fb_ t,LW{y ! AsJ53%4`2G>?{7m$+ jIopx1 TDs2/tests/0000755000176200001440000000000013774601521011746 5ustar liggesuserss2/tests/testthat/0000755000176200001440000000000014124561362013604 5ustar liggesuserss2/tests/testthat/test-s2-bounds.R0000644000176200001440000000336213774601521016526 0ustar liggesusers test_that("s2_bounds_cap works", { cap_ant <- s2_data_countries("Antarctica") expect_is(s2_bounds_cap(cap_ant), "data.frame") expect_identical(nrow(s2_bounds_cap(cap_ant)), 1L) expect_true(s2_bounds_cap(cap_ant)$lat < -80) expect_true(s2_bounds_cap(cap_ant)$angle > 20) expect_identical(nrow(s2_bounds_cap(s2_data_countries(c("Antarctica", "Netherlands")))), 2L) expect_true(s2_bounds_cap(s2_data_countries("Netherlands"))$angle < 2) expect_true(s2_bounds_cap(s2_data_countries("Fiji"))$angle < 2) }) test_that("s2_bounds_rect works", { rect_ant <- s2_bounds_rect(s2_data_countries("Antarctica")) expect_is(rect_ant, "data.frame") expect_named(rect_ant, c("lng_lo", "lat_lo", "lng_hi", "lat_hi")) expect_identical(nrow(s2_bounds_rect(s2_data_countries(c("Antarctica", "Netherlands")))), 2L) expect_equal(rect_ant$lng_lo, -180) expect_equal(rect_ant$lng_hi, 180) expect_equal(rect_ant$lat_lo, -90) expect_true(rect_ant$lat_hi < -60) expect_identical(nrow(s2_bounds_rect(s2_data_countries(c("Antarctica", "Netherlands")))), 2L) rect_nl <- s2_bounds_rect(s2_data_countries("Netherlands")) expect_true((rect_nl$lng_hi - rect_nl$lng_lo) < 4) rect_fiji <- s2_bounds_rect(s2_data_countries("Fiji")) expect_true(rect_fiji$lng_hi < rect_fiji$lng_lo) rect_multipoint <- s2_bounds_rect("MULTIPOINT(-179 0,179 1,-180 10)") expect_equal(rect_multipoint$lat_lo, 0) expect_equal(rect_multipoint$lat_hi, 10) expect_equal(rect_multipoint$lng_lo, 179) expect_equal(rect_multipoint$lng_hi, -179) rect_linestring <- s2_bounds_rect("LINESTRING(-179 0,179 1)") expect_equal(rect_linestring$lat_lo, 0) expect_equal(rect_linestring$lat_hi, 1) expect_equal(rect_linestring$lng_lo, 179) expect_equal(rect_linestring$lng_hi, -179) }) s2/tests/testthat/test-vctrs.R0000644000176200001440000000212014052002014016020 0ustar liggesusers test_that("s2_geography is a vctr", { x <- new_s2_xptr(list(NULL), "s2_geography") expect_true(vctrs::vec_is(x)) expect_identical(vctrs::vec_data(x), list(NULL)) expect_identical(vctrs::vec_restore(list(NULL), x), x) expect_identical(vctrs::vec_ptype_abbr(x), class(x)[1]) }) test_that("s2_lng latis a vctr", { x <- new_s2_xptr(list(NULL), "s2_lnglat") expect_true(vctrs::vec_is(x)) expect_identical(vctrs::vec_data(x), list(NULL)) expect_identical(vctrs::vec_restore(list(NULL), x), x) expect_identical(vctrs::vec_ptype_abbr(x), class(x)[1]) }) test_that("s2_point is a vctr", { x <- new_s2_xptr(list(NULL), "s2_point") expect_true(vctrs::vec_is(x)) expect_identical(vctrs::vec_data(x), list(NULL)) expect_identical(vctrs::vec_restore(list(NULL), x), x) expect_identical(vctrs::vec_ptype_abbr(x), class(x)[1]) }) test_that("s2_cell is a vctr", { x <- new_s2_cell(NA_real_) expect_true(vctrs::vec_is(x)) expect_identical(vctrs::vec_data(x), NA_real_) expect_identical(vctrs::vec_restore(NA_real_, x), x) expect_identical(vctrs::vec_ptype_abbr(x), "s2cell") }) s2/tests/testthat/test-s2-accessors.R0000644000176200001440000001657414050664625017234 0ustar liggesusers test_that("s2_is_collection works", { expect_identical(s2_is_collection(NA_character_), NA) expect_false(s2_is_collection("POINT (-64 45)")) expect_false(s2_is_collection("POINT EMPTY")) expect_true(s2_is_collection("MULTIPOINT ((0 0), (1 1))")) expect_false(s2_is_collection("LINESTRING (0 0, 1 1)")) expect_true(s2_is_collection("MULTILINESTRING ((0 0, 1 1), (2 2, 3 3))")) expect_false(s2_is_collection("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))")) expect_true( s2_is_collection("MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)) )") ) }) test_that("s2_is_valid() works", { expect_identical( s2_is_valid( c( # valid "POINT (0 1)", "LINESTRING (0 0, 1 1)", "POLYGON ((0 0, 0 1, 1 0, 0 0))", "GEOMETRYCOLLECTION (POINT (0 1))", # (for the purposes of S2, linestrings that cross aren't considered invalid # in the sense that they won't cause errors when you try to pass them to # a boolean operation or the builder) # invalid "LINESTRING (0 0, 0 0, 1 1)", "POLYGON ((0 0, 0 1, 1 0, 0 0, 0 0))", "GEOMETRYCOLLECTION (POLYGON ((0 0, 0 1, 1 0, 0 0, 0 0)))", NA ) ), c(TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, NA) ) }) test_that("s2_is_valid_detail() works", { expect_identical( s2_is_valid_detail( c( # valid "POINT (0 1)", "LINESTRING (0 0, 1 1)", "POLYGON ((0 0, 0 1, 1 0, 0 0))", "GEOMETRYCOLLECTION (POINT (0 1))", # (for the purposes of S2, linestrings that cross aren't considered invalid # in the sense that they won't cause errors when you try to pass them to # a boolean operation or the builder) # invalid "LINESTRING (0 0, 0 0, 1 1)", "POLYGON ((0 0, 0 1, 1 0, 0 0, 0 0))", "GEOMETRYCOLLECTION (POLYGON ((0 0, 0 1, 1 0, 0 0, 0 0)))", NA ) ), data.frame( is_valid = c(TRUE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, NA), reason = c( NA, NA, NA, NA, "Vertices 0 and 1 are identical", "Loop 0: Edge 3 is degenerate (duplicate vertex)", "Loop 0: Edge 3 is degenerate (duplicate vertex)", NA ), stringsAsFactors = FALSE ) ) }) test_that("s2_dimension works", { expect_identical(s2_dimension(NA_character_), NA_integer_) expect_identical(s2_dimension("POINT EMPTY"), 0L) expect_identical(s2_dimension("LINESTRING EMPTY"), 1L) expect_identical(s2_dimension("POLYGON EMPTY"), 2L) }) test_that("s2_num_points works", { expect_identical(s2_num_points(NA_character_), NA_integer_) expect_identical(s2_num_points("POINT (-64 45)"), 1L) expect_identical(s2_num_points("POINT EMPTY"), 0L) expect_identical(s2_num_points("LINESTRING (0 0, 1 1)"), 2L) expect_identical(s2_num_points("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))"), 4L) }) test_that("s2_is_empty works", { expect_identical(s2_is_empty(NA_character_), NA) expect_false(s2_is_empty("POINT (-64 45)")) expect_true(s2_is_empty("POINT EMPTY")) expect_false(s2_is_empty("LINESTRING (0 0, 1 1)")) expect_true(s2_is_empty("LINESTRING EMPTY")) expect_false(s2_is_empty("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))")) expect_true(s2_is_empty("POLYGON EMPTY")) }) test_that("s2_area works", { expect_identical(s2_area(NA_character_), NA_real_) expect_identical(s2_area("POINT (-64 45)"), 0) expect_identical(s2_area("POINT EMPTY"), 0) expect_identical(s2_area("LINESTRING (0 0, 1 1)"), 0) expect_identical(s2_area("POLYGON EMPTY"), 0) expect_identical(s2_area("POLYGON ((0 0, 90 0, 0 90, 0 0))", radius = 1), 4 * pi / 8) # make sure the radius is squared! expect_true( abs(s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 180 / pi) - 100) < 0.27 ) }) test_that("s2_length works", { expect_identical(s2_length(NA_character_), NA_real_) expect_identical(s2_length("POINT (-64 45)"), 0) expect_identical(s2_length("POINT EMPTY"), 0) expect_identical(s2_length("LINESTRING EMPTY"), 0) expect_identical(s2_length("LINESTRING (0 0, 0 1)", radius = 180 / pi), 1) expect_identical(s2_length("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))"), 0) }) test_that("s2_perimeter works", { expect_identical(s2_perimeter(NA_character_), NA_real_) expect_identical(s2_perimeter("POINT (-64 45)"), 0) expect_identical(s2_perimeter("POINT EMPTY"), 0) expect_identical(s2_perimeter("LINESTRING EMPTY"), 0) expect_identical(s2_perimeter("LINESTRING (0 0, 1 1)"), 0) # there is some error here because of the way this is calculated involves # some round-tripping through lat/lon expect_true( abs(s2_perimeter("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 180 / pi) - 40) < 0.155 ) }) test_that("s2_x and s2_y works", { expect_identical(s2_x(NA_character_), NA_real_) expect_identical(s2_y(NA_character_), NA_real_) expect_equal(s2_x("POINT (-64 45)"), -64) expect_equal(s2_y("POINT (-64 45)"), 45) expect_identical(s2_x("POINT EMPTY"), NA_real_) expect_identical(s2_y("POINT EMPTY"), NA_real_) expect_error(s2_x("LINESTRING EMPTY"), "Can't compute") expect_error(s2_y("LINESTRING EMPTY"), "Can't compute") expect_error(s2_x("POLYGON EMPTY"), "Can't compute") expect_error(s2_y("POLYGON EMPTY"), "Can't compute") }) test_that("s2_project() and s2_project_normalized() work", { expect_equal( s2_project( "LINESTRING (0 0, 0 90)", c("POINT (0 0)", "POINT (0 22.5)", "POINT (0 67.5)", "POINT (0 90)", NA), radius = 1 ), c(0, 0.25, 0.75, 1, NA_real_) * pi / 2 ) expect_equal( s2_project_normalized( "LINESTRING (0 0, 0 90)", c("POINT (0 0)", "POINT (0 22.5)", "POINT (0 67.5)", "POINT (0 90)", "POINT EMPTY", NA) ), c(0, 0.25, 0.75, 1, NA_real_, NA_real_) ) expect_error( s2_project_normalized("POINT (0 1)", "POINT (0 1)"), "must be a polyline" ) expect_error( s2_project_normalized("LINESTRING (0 1, 1 1)", "LINESTRING (0 1, 1 1)"), "must be a point" ) expect_error( s2_project_normalized("LINESTRING (0 1, 1 1)", "MULTIPOINT (0 1, 1 1)"), "must both be simple geographies" ) }) test_that("s2_distance works", { expect_equal( s2_distance("POINT (0 0)", "POINT (90 0)", radius = 180 / pi), 90 ) expect_equal( s2_distance("POINT (0 0)", "LINESTRING (90 0, 91 0)", radius = 180 / pi), 90 ) expect_equal( s2_distance("POINT (0 0)", "POLYGON ((90 0, 91 0, 92 1, 90 0))", radius = 180 / pi), 90 ) expect_identical(s2_distance("POINT (0 0)", NA_character_), NA_real_) expect_identical(s2_distance(NA_character_, "POINT (0 0)"), NA_real_) expect_identical(s2_distance("POINT (0 0)", "POINT EMPTY"), NA_real_) expect_identical(s2_distance("POINT EMPTY", "POINT (0 0)"), NA_real_) }) test_that("s2_max_distance works", { expect_equal( s2_max_distance("POINT (0 0)", "POINT (90 0)", radius = 180 / pi), 90 ) expect_equal( s2_max_distance("POINT (0 0)", "LINESTRING (90 0, 91 0)", radius = 180 / pi), 91 ) expect_equal( s2_max_distance("POINT (0 0)", "POLYGON ((90 0, 91 0, 89 1, 90 0))", radius = 180 / pi), 91 ) expect_identical(s2_max_distance("POINT (0 0)", NA_character_), NA_real_) expect_identical(s2_max_distance(NA_character_, "POINT (0 0)"), NA_real_) expect_identical(s2_max_distance("POINT (0 0)", "POINT EMPTY"), NA_real_) expect_identical(s2_max_distance("POINT EMPTY", "POINT (0 0)"), NA_real_) }) s2/tests/testthat/test-s2-point.R0000644000176200001440000000367713774601521016376 0ustar liggesusers test_that("s2_point objects can be created from and converted back to R objects", { # in expect_is(s2_point(1, 2, 3), "s2_point") expect_length(s2_point(1, 2, 3), 1) expect_is(as_s2_point(matrix(1, 2, 3, ncol = 3)), "s2_point") point <- s2_point(1, 2, 3) expect_identical(as_s2_point(point), point) expect_identical( as.data.frame(as_s2_point(s2_lnglat(0, 0))), as.data.frame(s2_point(1, 0, 0)) ) # subset assignment point2 <- point point2[1] <- point expect_identical(point2, point) point2 <- point point2[[1]] <- point expect_identical(point2, point) # out expect_identical(as.data.frame(s2_point(1, 2, 3)), data.frame(x = 1, y = 2, z = 3)) expect_identical(as.matrix(s2_point(1, 2, 3)), as.matrix(data.frame(x = 1, y = 2, z = 3))) # zero-length expect_length(s2_point(double(), double(), double()), 0) expect_identical( as.data.frame(s2_point(double(), double(), double())), data.frame(x = double(), y = double(), z = double()) ) expect_identical( as.data.frame(s2_point(double(), double(), double())[NA]), data.frame(x = NA_real_, y = NA_real_, z = NA_real_) ) }) test_that("s2_point vectors can't have other types of objects concatenated or asssigned", { point <- new_s2_xptr(list(NULL), class = "s2_point") expect_is(c(point, point), "s2_point") expect_error(c(point, new_s2_xptr(list(), class = "some_other_class")), "All items must inherit") expect_error(point[1] <- new_s2_xptr(list(NULL), class = "some_other_class"), "no applicable method") expect_error(point[[1]] <- new_s2_xptr(list(NULL), class = "some_other_class"), "no applicable method") }) test_that("s2_point can be imported from s2_geography", { expect_equal( as.data.frame(as_s2_point(as_s2_geography("POINT (-64 45)"))), as.data.frame(as_s2_point(as_s2_lnglat(as_s2_geography("POINT (-64 45)")))), ) }) test_that("s2_point objects can be printed", { expect_output(print(s2_point(1, 2, 3)), "s2_point") }) s2/tests/testthat/test-s2-geography.R0000644000176200001440000001602114050664625017217 0ustar liggesusers test_that("s2_geography class works", { expect_s3_class(s2_geography(), "s2_geography") geog <- new_s2_xptr(list(NULL), class = "s2_geography") expect_output(print(geog), "s2_geography") expect_output(str(geog), "s2_geography") expect_identical(as_s2_geography(geog), geog) # subset assignment geog2 <- geog geog2[1] <- geog expect_identical(geog2, geog) geog2 <- geog geog2[[1]] <- geog expect_identical(geog2, geog) }) test_that("s2_geography vectors can be put in a data frame", { expect_identical( data.frame(geog = s2_geography()), new_data_frame(list(geog = s2_geography())) ) }) test_that("s2_geography vectors can't have other types of objects concatenated or asssigned", { geog <- new_s2_xptr(list(NULL), class = "s2_geography") expect_is(c(geog, geog), "s2_geography") expect_error(c(geog, new_s2_xptr(list(), class = "some_other_class")), "All items must inherit") expect_error(geog[1] <- new_s2_xptr(list(NULL), class = "some_other_class"), "no applicable method") expect_error(geog[[1]] <- new_s2_xptr(list(NULL), class = "some_other_class"), "no applicable method") }) test_that("s2_geography vectors can be created from s2_lnglat and s2_point", { expect_wkt_equal(as_s2_geography(s2_lnglat(-64, 45)), "POINT (-64 45)") expect_wkt_equal(as_s2_geography(as_s2_point(s2_lnglat(-64, 45))), "POINT (-64 45)") }) test_that("s2_geography vectors can be created from WKB and WKT", { wkb_point <- wk::as_wkb("POINT (-64 45)") expect_output(print(as_s2_geography(wkb_point)), "") wkt_point <- wk::as_wkt("POINT (-64 45)") expect_output(print(as_s2_geography(wkt_point)), "") # also test other classes commonly used to signify WKB or WKT expect_output(print(as_s2_geography(structure(wkb_point, class = "WKB")), "")) expect_output(print(as_s2_geography(structure(wkb_point, class = "blob")), "")) }) test_that("s2_geography can be exported to WKB/WKT", { expect_wkt_equal( wk::as_wkb(as_s2_geography("POINT (-64 45)")), wk::as_wkb("POINT (-64 45)"), precision = 10 ) expect_wkt_equal( wk::as_wkt(as_s2_geography("POINT (-64 45)")), wk::as_wkt("POINT (-64 45)"), precision = 10 ) }) test_that("s2_geography vectors can be created from wkt", { expect_output(print(as_s2_geography("POINT (-64 45)")), "") expect_output(print(as_s2_geography("POINT EMPTY")), "") expect_output( print(as_s2_geography("MULTIPOINT ((-64 45), (30 10))")), "" ) expect_output( print(as_s2_geography("LINESTRING (-64 45, 0 0)")), "" ) expect_output( print(as_s2_geography("LINESTRING EMPTY")), "" ) expect_output( print(as_s2_geography("MULTILINESTRING ((-64 45, 0 0), (0 1, 2 3))")), "" ) expect_output(print(as_s2_geography("POLYGON EMPTY"), "")) expect_output( print(as_s2_geography("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))")), "" ) expect_output( print( as_s2_geography( "GEOMETRYCOLLECTION ( POINT (30 10), MULTIPOINT (11 12, 12 13), LINESTRING (40 40, 40 41), MULTILINESTRING ((-10 -12, -12 -13), (-15 -15, -16 -16)), POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0)), MULTIPOLYGON (((0 0, -10 0, -10 -10, 0 -10, 0 0))), GEOMETRYCOLLECTION (POINT (-100 0), MULTIPOINT(-101 0, -102 0)) )" ), max_coords = 100 ), paste0( "GEOMETRYCOLLECTION.*?POINT.*?MULTIPOINT.*?LINESTRING.*?MULTILINESTRING.*?", "POLYGON.*?POLYGON.*?GEOMETRYCOLLECTION.*?POINT.*?MULTIPOINT" ) ) expect_output(print(as_s2_geography("GEOMETRYCOLLECTION EMPTY")), "") }) test_that("empty points are empty when imported from WKB", { wkb_empty <- wk::as_wkb("POINT EMPTY") expect_true(s2_is_empty(s2_geog_from_wkb(wkb_empty))) }) test_that("nested ring depths are correctly exported", { # polygon with hole expect_output( print( as_s2_geography("MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ( (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20) ) )"), max_coords = 100 ), "\\(20 35, 10 30, 10 10, 30 5, 45 20, 20 35\\), \\(30 20, 20 15, 20 25, 30 20" ) # polygon with a hole in a hole! expect_output( print( as_s2_geography("MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ( (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20), (27 21, 21 21, 21 16, 27 21) ) )"), max_coords = 100 ), "30 20, 20 15, 20 25, 30 20\\), \\(27 21, 21 21, 21 16, 27 21" ) }) test_that("polygons with holes are interpreted as such by S2", { expect_true( s2_intersects( "MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ( (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20), (27 21, 21 21, 21 16, 27 21) ) )", "POINT (23 19.5)" ) ) expect_false( s2_intersects( "MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ( (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20) ) )", "POINT (23 19.5)" ) ) }) test_that("polygon construction works with oriented = TRUE and oriented = FALSE", { polygon_with_bad_hole_nested <- as_s2_geography("MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ( (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 25, 20 15, 30 20) ) )", oriented = FALSE) expect_false(s2_intersects(polygon_with_bad_hole_nested, "POINT (23 19.5)")) expect_error( as_s2_geography("MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ( (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 25, 20 15, 30 20) ) )", oriented = TRUE, check = TRUE), "Inconsistent loop orientations" ) expect_silent( as_s2_geography("MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ( (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 25, 20 15, 30 20) ) )", oriented = TRUE, check = FALSE) ) }) test_that("Full polygons work", { expect_true(s2_intersects(as_s2_geography(TRUE), "POINT(0 1)")) expect_wkt_equal(s2_difference(as_s2_geography(TRUE), "POINT(0 1)"), "POLYGON ((0 -90, 0 -90))") }) s2/tests/testthat/test-s2-lnglat.R0000644000176200001440000000600213774601521016507 0ustar liggesusers test_that("s2_lnglat objects can be created from and converted back to R objects", { # in expect_is(s2_lnglat(45, 64), "s2_lnglat") expect_length(s2_lnglat(45, 64), 1) expect_is(as_s2_lnglat(matrix(45, 64, ncol = 2)), "s2_lnglat") lnglat <- s2_lnglat(45, 64) expect_identical(as_s2_lnglat(lnglat), lnglat) expect_identical( as.data.frame(as_s2_lnglat(s2_point(1, 0, 0))), as.data.frame(s2_lnglat(0, 0)) ) # subset assignment lnglat2 <- lnglat lnglat2[1] <- lnglat expect_identical(lnglat2, lnglat) lnglat2 <- lnglat lnglat2[[1]] <- lnglat expect_identical(lnglat2, lnglat) # out expect_identical(as.data.frame(s2_lnglat(-64, 45)), data.frame(lng = -64, lat = 45)) expect_identical(as.matrix(s2_lnglat(-64, 45)), as.matrix(data.frame(lng = -64, lat = 45))) # zero-length in and out expect_length(s2_lnglat(double(), double()), 0) expect_identical( as.data.frame(s2_lnglat(double(), double())), data.frame(lng = double(), lat = double()) ) # NAs and NULLs expect_identical( as.data.frame(s2_lnglat(double(), double())[NA]), data.frame(lng = NA_real_, lat = NA_real_) ) }) test_that("s2_lnglat can be imported/exported to/from wkb and wkt", { wkb_point <- wk::as_wkb("POINT (-64 45)") expect_wkt_equal(wk::as_wkb(s2_lnglat(-64, 45)), wkb_point) expect_wkt_equal(wk::as_wkt(s2_lnglat(-64, 45)), wk::wkt("POINT (-64 45)")) expect_identical(wk::as_wkt(s2_lnglat(NA, NA)), wk::wkt("POINT EMPTY")) expect_equal( as.data.frame(as_s2_lnglat(wk::as_wkb(c("POINT EMPTY", "POINT (-64 45)")))), data.frame(lng = c(NA, -64), lat = c(NA, 45)) ) expect_equal( as.data.frame(as_s2_lnglat(c("POINT EMPTY", "POINT (-64 45)"))), data.frame(lng = c(NA, -64), lat = c(NA, 45)) ) expect_equal( as.data.frame(as_s2_lnglat(wk::wkt(c("POINT EMPTY", "POINT (-64 45)")))), data.frame(lng = c(NA, -64), lat = c(NA, 45)) ) expect_error(as_s2_lnglat(wk::wkt("LINESTRING EMPTY")), "non-point") expect_error(as_s2_lnglat(wk::as_wkb("LINESTRING EMPTY")), "non-point") }) test_that("s2_lnglat vectors can't have other types of objects concatenated or asssigned", { lnglat <- new_s2_xptr(list(NULL), class = "s2_lnglat") expect_is(c(lnglat, lnglat), "s2_lnglat") expect_error(c(lnglat, new_s2_xptr(list(), class = "some_other_class")), "All items must inherit") expect_error(lnglat[1] <- new_s2_xptr(list(NULL), class = "some_other_class"), "no applicable method") expect_error(lnglat[[1]] <- new_s2_xptr(list(NULL), class = "some_other_class"), "no applicable method") }) test_that("s2_lnglat can be imported from s2_geography", { expect_equal( as.data.frame(as_s2_lnglat(as_s2_geography("POINT (-64 45)"))), data.frame(lng = -64, lat = 45) ) }) test_that("s2_lnglat can be imported from wkb", { wkb_point <- wk::as_wkb("POINT (-64 45)") expect_equal( as.data.frame(as_s2_lnglat(wkb_point)), data.frame(lng = -64, lat = 45) ) }) test_that("s2_lnglat objects can be printed", { expect_output(print(s2_lnglat(-64, 45)), "s2_lnglat") }) s2/tests/testthat/test-s2-xptr.R0000644000176200001440000000275113774601521016232 0ustar liggesusers test_that("s2_xptr class works", { expect_s3_class(new_s2_xptr(list()), "s2_xptr") expect_s3_class(new_s2_xptr(list(), class = "custom"), "custom") expect_s3_class(new_s2_xptr(list(), class = "custom"), "s2_xptr") expect_error(new_s2_xptr(NULL), "must be a bare list") }) test_that("objects pointed to by an s2_xptr are destroyed by the garbage collector", { xptr <- expect_output(s2_xptr_test(1), "Allocating") expect_output(s2_xptr_test_op(xptr), "test\\(\\) on XPtrTest") expect_identical(validate_s2_xptr(xptr), xptr) expect_output({rm(xptr); gc()}, "Destroying") }) test_that("s2_xptr validation works", { expect_identical(validate_s2_xptr(new_s2_xptr(list())), new_s2_xptr(list())) expect_identical(validate_s2_xptr(new_s2_xptr(list(NULL))), new_s2_xptr(list(NULL))) expect_error(validate_s2_xptr(list("wrong type")), "must be externalptr") }) test_that("s2_xptr subsetting and concatenation work", { expect_length(rep(new_s2_xptr(list()), 10), 0) xptr <- new_s2_xptr(list(NULL, NULL)) expect_identical(xptr[1], new_s2_xptr(list(NULL))) expect_identical(xptr[[1]], xptr[1]) expect_identical(c(xptr, xptr), new_s2_xptr(list(NULL, NULL, NULL, NULL))) expect_identical(rep(xptr, 2), new_s2_xptr(list(NULL, NULL, NULL, NULL))) expect_identical(rep_len(xptr, 4), new_s2_xptr(list(NULL, NULL, NULL, NULL))) }) test_that("s2_xptr default print method works", { expect_output(print(new_s2_xptr()), "s2_xptr") expect_output(print(new_s2_xptr(list(NULL))), "s2_xptr") }) s2/tests/testthat/test-wk-utils.R0000644000176200001440000001243114052002014016444 0ustar liggesusers test_that("the projection/unprojection filter errors for invalid input", { expect_error(s2_unprojection_filter(NULL), "must be a ") expect_error(s2_unprojection_filter(wk::wk_void_handler(), projection = NULL), "must be an") expect_error(s2_unprojection_filter(wk::wk_void_handler(), tessellate_tol = -1), "must be") expect_error(s2_unprojection_filter(wk::wk_void_handler(), tessellate_tol = -1), "must be") }) test_that("unprojection using a wk filter works", { expect_equal( wk::wk_handle(wk::xy(0, 0), s2_unprojection_filter(wk::xy_writer())), wk::xyz(1, 0, 0) ) expect_identical( wk::wk_handle( wk::wkt("LINESTRING (0 0, 0 45, -60 45)"), s2_unprojection_filter(wk::wkt_format_handler(precision = 4)) ), # 0.7071 ~ sqrt(2) / 2 "LINESTRING Z (1 0 0, 0.7071 0 0.7071, 0.3536 -0.6124 0.7071)" ) expect_identical( wk::wk_handle( wk::wkt("POLYGON ((0 0, 0 45, -60 45, 0 0))"), s2_unprojection_filter(wk::wkt_format_handler(precision = 4)) ), # 0.7071 ~ sqrt(2) / 2 "POLYGON Z ((1 0 0, 0.7071 0 0.7071, 0.3536 -0.6124 0.7071, 1 0 0))" ) expect_identical( wk::wk_handle(wk::wkt(NA_character_), s2_unprojection_filter(wk::wkt_writer())), wk::wkt(NA_character_) ) }) test_that("tessellating + unprojection using a wk filter works", { # using big examples here, so use a tolerance of 100 km (forces # adding at least one point) tol <- 100000 / s2_earth_radius_meters() expect_equal( wk::wk_handle( wk::xy(0, 0), s2_unprojection_filter(wk::xy_writer(), tessellate_tol = tol) ), wk::xyz(1, 0, 0) ) expect_identical( wk::wk_handle( wk::wkt("LINESTRING (0 0, 0 45, -60 45)"), s2_unprojection_filter(wk::wkb_writer(), tessellate_tol = tol) ) %>% s2_num_points(), 6L ) expect_identical( wk::wk_handle( wk::wkt("POLYGON ((0 0, 0 45, -60 45, 0 0))"), s2_unprojection_filter(wk::wkb_writer(), tessellate_tol = tol) ) %>% s2_num_points(), 8L ) expect_identical( wk::wk_handle(wk::wkt(NA_character_), s2_unprojection_filter(wk::wkt_writer(), tessellate_tol = tol)), wk::wkt(NA_character_) ) }) test_that("projection using a wk filter works", { expect_equal( wk::wk_handle(wk::xyz(1, 0, 0), s2_projection_filter(wk::xy_writer())), wk::xy(0, 0) ) expect_identical( wk::wk_handle( wk::wkt("LINESTRING Z (1 0 0, 0.7071 0 0.7071, 0.3536 -0.6124 0.7071)"), s2_projection_filter(wk::wkt_format_handler(precision = 4)) ), "LINESTRING (0 0, 0 45, -60 45)" ) expect_identical( wk::wk_handle( wk::wkt("POLYGON Z ((1 0 0, 0.7071 0 0.7071, 0.3536 -0.6124 0.7071, 1 0 0))"), s2_projection_filter(wk::wkt_format_handler(precision = 4)) ), "POLYGON ((0 0, 0 45, -60 45, 0 0))" ) expect_identical( wk::wk_handle(wk::wkt(NA_character_), s2_projection_filter(wk::wkt_writer())), wk::wkt(NA_character_) ) }) test_that("projection + tessellating using a wk filter works", { tol <- 100000 / s2_earth_radius_meters() expect_equal( wk::wk_handle(wk::xyz(1, 0, 0), s2_projection_filter(wk::xy_writer(), tessellate_tol = tol)), wk::xy(0, 0) ) expect_identical( wk::wk_handle( wk::wkt("LINESTRING Z (1 0 0, 0.7071 0 0.7071, 0.3536 -0.6124 0.7071)"), s2_projection_filter(wk::wkb_writer(), tessellate_tol = tol) ) %>% s2_num_points(), 6L ) expect_identical( wk::wk_handle( wk::wkt("POLYGON Z ((1 0 0, 0.7071 0 0.7071, 0.3536 -0.6124 0.7071, 1 0 0))"), s2_projection_filter(wk::wkb_writer(), tessellate_tol = tol) ) %>% s2_num_points(), 8L ) expect_identical( wk::wk_handle(wk::wkt(NA_character_), s2_projection_filter(wk::wkt_writer(), tessellate_tol = tol)), wk::wkt(NA_character_) ) }) test_that("errors are propagated through the coordinate filter", { expect_error( wk::wk_handle(wk::new_wk_wkt("POINT ENTPY"), s2_projection_filter(wk::wk_void_handler())), "ENTPY" ) }) test_that("early returns are respected by s2_projection_filter()", { big_line <- as_wkt(s2::s2_make_line(1:10, 1:10)) unprojected <- wk::wk_handle(big_line, s2_unprojection_filter(wk::wkt_writer())) expect_identical( wk::wk_handle(unprojected, s2_projection_filter(wk::wkt_format_handler(max_coords = 2))), "LINESTRING (1 1, 2 2...", ) expect_identical( wk::wk_handle( unprojected, s2_projection_filter(wk::wkt_format_handler(max_coords = 2), tessellate_tol = 1e-8) ), "LINESTRING (1 1, 1.062476 1.062512...", ) }) test_that("early returns are respected by s2_unprojection_filter()", { big_line <- as_wkt(s2::s2_make_line(1:10, 1:10)) expect_identical( wk::wk_handle(big_line, s2_unprojection_filter(wk::wkt_format_handler(max_coords = 2))), "LINESTRING Z (0.9996954 0.01744975 0.01745241, 0.998782 0.03487824 0.0348995...", ) expect_identical( wk::wk_handle( big_line, s2_unprojection_filter(wk::wkt_format_handler(max_coords = 2), tessellate_tol = 1e-8) ), "LINESTRING Z (0.9996954 0.01744975 0.01745241, 0.9996562 0.01853987 0.01854306...", ) }) test_that("mercator projection works", { expect_equal( wk::wk_handle( wk::xy(c(0, 180), 0), s2_unprojection_filter(s2_projection_filter(wk::xy_writer(), s2_projection_mercator())) ), wk::xy(c(0, 20037508), 0) ) }) s2/tests/testthat/test-s2-transformers.R0000644000176200001440000004515614060402272017757 0ustar liggesusers test_that("s2_centroid() works", { expect_wkt_equal(s2_centroid("POINT (30 10)"), "POINT (30 10)") expect_true(s2_is_empty(s2_centroid("POINT EMPTY"))) expect_wkt_equal(s2_centroid("MULTIPOINT ((0 0), (0 10))"), "POINT (0 5)") expect_wkt_equal(s2_centroid("LINESTRING (0 0, 0 10)"), "POINT (0 5)", precision = 15) expect_near( s2_distance( s2_centroid("POLYGON ((-5 -5, 5 -5, 5 5, -5 5, -5 -5))"), "POINT (0 0)" ), 0, epsilon = 1e-6 ) }) test_that("s2_centroid() and s2_centroid_agg() normalize points", { expect_equal( s2_distance( s2_centroid("MULTIPOINT (1 1, 1 1)"), "POINT (1 1)" ), 0 ) expect_equal( s2_distance( s2_centroid_agg(c("POINT (1 1)", "POINT (1 1)")), "POINT (1 1)" ), 0 ) }) test_that("s2_boundary() works", { expect_true(s2_is_empty(s2_boundary("POINT (30 10)"))) expect_true(s2_is_empty(s2_boundary("POINT EMPTY"))) expect_true(s2_is_empty(s2_boundary("POLYGON EMPTY"))) expect_wkt_equal(s2_boundary("LINESTRING (0 0, 0 10)"), "MULTIPOINT ((0 0), (0 10))") expect_wkt_equal( s2_boundary("MULTIPOLYGON ( ((40 40, 20 45, 45 30, 40 40)), ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20)) )"), "MULTILINESTRING ( (40 40, 20 45, 45 30, 40 40), (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20) )", precision = 15 ) }) test_that("s2_closest_point() works", { expect_wkt_equal(s2_closest_point("POINT (0 1)", "POINT (30 10)"), "POINT (0 1)") expect_wkt_equal(s2_closest_point("LINESTRING (0 1, -12 -12)", "POINT (30 10)"), "POINT (0 1)") }) test_that("s2_minimum_clearance_line_between() works", { expect_wkt_equal( s2_minimum_clearance_line_between("POINT (0 1)", "POINT (30 10)"), "LINESTRING (0 1, 30 10)" ) expect_true(s2_is_empty(s2_minimum_clearance_line_between("POINT (30 10)", "POINT EMPTY"))) expect_wkt_equal( s2_minimum_clearance_line_between("LINESTRING (0 1, -12 -12)", "POINT (30 10)"), "LINESTRING (0 1, 30 10)" ) expect_wkt_equal( s2_minimum_clearance_line_between("LINESTRING (0 0, 1 1)", "LINESTRING (1 0, 0 1)"), "MULTIPOINT ((0.5 0.500057), (0.5 0.500057))", precision = 6 ) }) test_that("s2_difference() works", { expect_wkt_equal(s2_difference("POINT (30 10)", "POINT EMPTY"), "POINT (30 10)") expect_true(s2_is_empty(s2_difference("POINT (30 10)", "POINT (30 10)"))) expect_true(s2_is_empty(s2_difference("LINESTRING (0 0, 45 0)", "LINESTRING (0 0, 45 0)"))) }) test_that("s2_difference() works for polygons", { # on Windows i386, these fail without snap rounding df <- s2_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", s2_options(snap = s2_snap_level(30)) ) expect_near( s2_area(df, radius = 1), s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) - s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1), epsilon = 0.004 ) df0 <- s2_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "open", snap = s2_snap_level(30)) ) df1 <- s2_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "semi-open", snap = s2_snap_level(30)) ) df2 <- s2_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "closed", snap = s2_snap_level(30)) ) expect_equal(s2_area(df0) - s2_area(df2), 0.0) expect_equal(s2_area(df0) - s2_area(df1), 0.0) }) test_that("s2_sym_difference() works", { expect_wkt_equal(s2_sym_difference("POINT (30 10)", "POINT EMPTY"), "POINT (30 10)") expect_true(s2_is_empty(s2_sym_difference("POINT (30 10)", "POINT (30 10)"))) expect_wkt_equal(s2_sym_difference("POINT (30 10)", "POINT (30 20)"), "MULTIPOINT ((30 20), (30 10))") expect_true(s2_is_empty(s2_sym_difference("LINESTRING (0 0, 45 0)", "LINESTRING (0 0, 45 0)"))) }) test_that("s2_sym_difference() works for polygons", { # on Windows i386, these fail without snap rounding df <- s2_sym_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", s2_options(snap = s2_snap_level(30)) ) expect_near( s2_area(df, radius = 1), 2 * s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) - s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1), epsilon = 0.0042 ) df0 <- s2_sym_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "open", snap = s2_snap_level(30)) ) df1 <- s2_sym_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "semi-open", snap = s2_snap_level(30)) ) df2 = s2_sym_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", s2_options(model = "closed", snap = s2_snap_level(30)) ) expect_equal(s2_area(df0) - s2_area(df2), 0.0) expect_equal(s2_area(df0) - s2_area(df1), 0.0) }) test_that("s2_intersection() works", { expect_wkt_equal(s2_intersection("POINT (30 10)", "POINT (30 10)"), "POINT (30 10)") expect_true(s2_is_empty(s2_intersection("POINT (30 10)", "POINT (30 11)"))) expect_wkt_equal( s2_intersection( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "LINESTRING (0 5, 10 5)" ), "LINESTRING (0 5, 10 5)" ) expect_equal( s2_distance( s2_intersection("LINESTRING (-45 0, 45 0)", "LINESTRING (0 -10, 0 10)"), "POINT (0 0)" ), 0 ) }) test_that("s2_intersction() works for polygons", { expect_wkt_equal( s2_intersection( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", s2_options(snap = s2_snap_level(30)) ), "POLYGON ((5 5, 10 5, 10 10, 5 10, 5 5))", precision = 2 ) expect_true(s2_is_empty(s2_intersection("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT(0 0)"))) expect_true( s2_is_empty( s2_intersection( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT(0 0)", s2_options(model = "open") ) ) ) expect_true( s2_is_empty( s2_intersection("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT(0 0)", s2_options(model = "semi-open")) ) ) expect_wkt_equal( s2_intersection("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POINT(0 0)", s2_options(model = "closed")), "POINT(0 0)" ) df0 <- s2_intersection( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "open") ) df1 <- s2_intersection( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "semi-open") ) df2 <- s2_intersection( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "closed") ) expect_equal(s2_area(df0) - s2_area(df2), 0.0) expect_equal(s2_area(df0) - s2_area(df1), 0.0) }) test_that("s2_union(x) works", { expect_wkt_equal(s2_union("POINT (30 10)"), "POINT (30 10)") expect_wkt_equal(s2_union("POINT EMPTY"), "GEOMETRYCOLLECTION EMPTY") expect_wkt_equal( s2_union("MULTILINESTRING ((-45 0, 0 0), (0 0, 0 10))"), "LINESTRING (-45 0, 0 0, 0 10)" ) expect_wkt_equal(s2_union("GEOMETRYCOLLECTION (POINT (30 10))"), "POINT (30 10)") expect_wkt_equal( s2_union("GEOMETRYCOLLECTION (POINT (30 10), LINESTRING (0 0, 0 1))"), "GEOMETRYCOLLECTION (POINT (30 10), LINESTRING (0 0, 0 1))" ) }) test_that("s2_union(x) works with polygons that have overlapping input regions", { # two outer loops txt <- "MULTIPOLYGON (((0 0, 0 1, 1 1, 1 0, 0 0)), ((0.1 0.9, 0.1 1.9, 1.1 1.9, 1.1 0.9, 0.1 0.9)))" # geos::geos_unary_union(txt) %>% as_wkb() %>% s2_area(radius = 1) unioned <- s2_union(as_s2_geography(txt, check = F)) expect_equal(s2_area(unioned, radius = 1), 0.0005817275) # two outer loops, one valid inner loop # geos::geos_unary_union(txt2) %>% as_wkb() %>% s2_area(radius = 1) txt2 <- "MULTIPOLYGON ( ((0 0, 0 1, 1 1, 1 0, 0 0), (0.1 0.1, 0.5 0.1, 0.5 0.5, 0.1 0.5, 0.1 0.1)), ((0.1 0.9, 0.1 1.9, 1.1 1.9, 1.1 0.9, 0.1 0.9)) )" unioned <- s2_union(as_s2_geography(txt2, check = F)) expect_equal(s2_area(unioned, radius = 1), 0.0005329892) }) test_that("s2_union(x) errors for the case of mixed dimension collections", { expect_error( s2_union( c("GEOMETRYCOLLECTION(POLYGON ((-10 -10, -10 10, 10 10, 10 -10, -10 -10)), LINESTRING (0 -20, 0 20))") ), "Unary union for collections is not implemented" ) }) test_that("s2_union(x, y) works", { expect_wkt_equal(s2_union("POINT (30 10)", "POINT EMPTY"), "POINT (30 10)") expect_wkt_equal(s2_union("POINT EMPTY", "POINT EMPTY"), "GEOMETRYCOLLECTION EMPTY") # LINESTRING (-45 0, 0 0, 0 10) expect_wkt_equal( s2_union("LINESTRING (-45 0, 0 0)", "LINESTRING (0 0, 0 10)"), "LINESTRING (-45 0, 0 0, 0 10)" ) }) test_that("s2_union() works for polygons", { # on Windows i386, these fail without snap rounding u <- s2_union( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", s2_options(snap = s2_snap_level(30)) ) u0 <- s2_union( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "open", snap = s2_snap_level(30)) ) u1 <- s2_union( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "semi-open", snap = s2_snap_level(30)) ) u2 <- s2_union( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" , s2_options(model = "closed", snap = s2_snap_level(30)) ) expect_equal(s2_area(u0) - s2_area(u2), 0.0) expect_equal(s2_area(u0) - s2_area(u1), 0.0) expect_near( s2_area(u, radius = 1), s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) + s2_area("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", radius = 1) - s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1), epsilon = 0.004 ) }) test_that("binary operations use layer creation options", { expect_wkt_equal( s2_union( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", options = s2_options(polyline_type = "path", polyline_sibling_pairs = "discard") ), "LINESTRING (0 0, 0 1, 0 2, 0 3)" ) expect_true( s2_is_collection( s2_union( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", options = s2_options(polyline_type = "walk") ) ) ) expect_wkt_equal( s2_coverage_union_agg( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", options = s2_options(polyline_type = "path", polyline_sibling_pairs = "discard") ), "LINESTRING (0 0, 0 1, 0 2, 0 3)" ) expect_true( s2_is_collection( s2_coverage_union_agg( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", options = s2_options(polyline_type = "walk") ) ) ) }) test_that("s2_coverage_union_agg() works", { expect_wkt_equal(s2_coverage_union_agg(c("POINT (30 10)", "POINT EMPTY")), "POINT (30 10)") expect_wkt_equal(s2_coverage_union_agg(c("POINT EMPTY", "POINT EMPTY")), "GEOMETRYCOLLECTION EMPTY") # NULL handling expect_identical( s2_coverage_union_agg(c("POINT (30 10)", NA), na.rm = FALSE), as_s2_geography(NA_character_) ) expect_wkt_equal( s2_coverage_union_agg(character()), as_s2_geography("GEOMETRYCOLLECTION EMPTY") ) expect_wkt_equal( s2_coverage_union_agg(c("POINT (30 10)", NA), na.rm = TRUE), "POINT (30 10)" ) }) test_that("s2_union_agg() works", { expect_wkt_equal(s2_union_agg(c("POINT (30 10)", "POINT EMPTY")), "POINT (30 10)") expect_wkt_equal(s2_union_agg(c("POINT EMPTY", "POINT EMPTY")), "GEOMETRYCOLLECTION EMPTY") # NULL handling expect_identical( s2_coverage_union_agg(c("POINT (30 10)", NA), na.rm = FALSE), as_s2_geography(NA_character_) ) expect_wkt_equal( s2_coverage_union_agg(character()), as_s2_geography("GEOMETRYCOLLECTION EMPTY") ) expect_wkt_equal( s2_coverage_union_agg(c("POINT (30 10)", NA), na.rm = TRUE), "POINT (30 10)" ) }) test_that("s2_rebuild_agg() works", { expect_wkt_equal(s2_rebuild_agg(c("POINT (30 10)", "POINT EMPTY")), "POINT (30 10)") expect_wkt_equal(s2_rebuild_agg(c("POINT EMPTY", "POINT EMPTY")), "GEOMETRYCOLLECTION EMPTY") # NULL handling expect_identical( s2_coverage_union_agg(c("POINT (30 10)", NA), na.rm = FALSE), as_s2_geography(NA_character_) ) expect_wkt_equal( s2_rebuild_agg(character()), as_s2_geography("GEOMETRYCOLLECTION EMPTY") ) expect_wkt_equal( s2_rebuild_agg(c("POINT (30 10)", NA), na.rm = TRUE), "POINT (30 10)" ) }) test_that("s2_centroid_agg() works", { expect_wkt_equal(s2_centroid_agg(c("POINT (30 10)", "POINT EMPTY")), "POINT (30 10)") expect_wkt_equal(s2_centroid_agg(c("POINT EMPTY", "POINT EMPTY")), "POINT EMPTY") expect_wkt_equal(s2_centroid_agg(c("POINT (0 0)", "POINT (0 10)")), "POINT (0 5)", precision = 15) # NULL handling expect_identical( s2_centroid_agg(c("POINT (30 10)", NA), na.rm = FALSE), as_s2_geography(NA_character_) ) expect_wkt_equal( s2_centroid_agg(c("POINT (30 10)", NA), na.rm = TRUE), "POINT (30 10)" ) }) test_that("s2_snap_to_grid() works", { expect_wkt_equal( s2_as_text(s2_snap_to_grid("POINT (0.333333333333 0.666666666666)", 1e-2)), "POINT (0.33 0.67)", precision = 6 ) }) test_that("s2_buffer() works", { # create a hemisphere! ply <- s2_buffer_cells("POINT (0 0)", distance = pi / 2, radius = 1) expect_near(s2_area(ply, radius = 1), 4 * pi / 2, epsilon = 0.1) }) test_that("s2_simplify() works", { expect_wkt_equal( s2_simplify("LINESTRING (0 0, 0.001 1, -0.001 2, 0 3)", tolerance = 100), "LINESTRING (0 0, 0.001 1, -0.001 2, 0 3)" ) expect_wkt_equal( s2_simplify("LINESTRING (0 0, 0.001 1, -0.001 2, 0 3)", tolerance = 1000), "LINESTRING (0 0, 0 3)" ) }) test_that("s2_rebuild() works", { s2_rebuild("POINT (-64 45)") s2_rebuild("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))") s2_rebuild("GEOMETRYCOLLECTION (POINT (-64 45), LINESTRING (-64 45, 0 0))") # duplicated edges expect_wkt_equal( s2_rebuild("MULTIPOINT (-64 45, -64 45)", options = s2_options(duplicate_edges = FALSE)), "POINT (-64 45)" ) expect_wkt_equal( s2_rebuild("MULTIPOINT (-64 45, -64 45)", options = s2_options(duplicate_edges = TRUE)), "MULTIPOINT (-64 45, -64 45)" ) # crossing edges expect_true( s2_is_collection( s2_rebuild( "LINESTRING (0 -5, 0 5, -5 0, 5 0)", options = s2_options(split_crossing_edges = TRUE) ) ) ) # snap expect_wkt_equal( s2_rebuild( "MULTIPOINT (0.01 0.01, -0.01 -0.01)", options = s2_options( snap = s2_snap_precision(1e1), duplicate_edges = TRUE ) ), "MULTIPOINT ((0 0), (0 0))" ) # snap radius expect_wkt_equal( s2_rebuild( "LINESTRING (0 0, 0 1, 0 2, 0 3)", options = s2_options( snap_radius = 1.5 * pi / 180 ) ), "LINESTRING (0 0, 0 2)" ) # simplify edge chains expect_wkt_equal( s2_rebuild( "LINESTRING (0 0, 0 1, 0 2, 0 3)", options = s2_options( snap_radius = 0.01, simplify_edge_chains = TRUE ) ), "LINESTRING (0 0, 0 3)" ) # validate bad_poly <- s2_geog_from_text( "POLYGON ((0 0, 1.0 0, 1.0 1.0, -0.1 1.0, 1.1 0, 0 0))", check = FALSE ) expect_wkt_equal( s2_rebuild(bad_poly, options = s2_options(validate = FALSE)), bad_poly ) expect_error( s2_rebuild(bad_poly, options = s2_options(validate = TRUE)), "Edge 1 crosses edge 3" ) # polyline type expect_wkt_equal( s2_rebuild( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", s2_options(polyline_type = "walk") ), "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)" ) expect_true( s2_is_collection( s2_rebuild( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", s2_options(polyline_type = "path") ) ) ) # sibling edge pairs expect_true( s2_is_collection( s2_rebuild( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", s2_options(polyline_type = "path", polyline_sibling_pairs = "keep") ) ) ) expect_false( s2_is_collection( s2_rebuild( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", s2_options(polyline_type = "path", polyline_sibling_pairs = "discard") ) ) ) # dimension expect_true( s2_is_empty( s2_rebuild( "LINESTRING (0 0, 0 1, 0 2, 0 1, 0 3)", s2_options(dimensions = c("point", "polygon")) ) ) ) }) test_that("real data survives the S2BooleanOperation", { # the 32-bit Solaris build results in some of the roundtripped # edges becoming degenerate. Rather than pass check = FALSE to # as_s2_geography(), just skip this on Solaris skip_on_os("solaris") for (continent in unique(s2::s2_data_tbl_countries$continent)) { # this is primarily a test of the S2BooleanOperation -> Geography constructor unioned <- expect_is(s2_coverage_union_agg(s2_data_countries(continent)), "s2_geography") # this is a test of Geography::Export() on potentially complex polygons exported <- expect_length(s2_as_binary(unioned), 1) # the output WKB should load as a polygon with oriented = TRUE and result in the # same number of points and similar area reloaded <- as_s2_geography(structure(exported, class = "wk_wkb"), oriented = TRUE) expect_equal(s2_num_points(reloaded), s2_num_points(unioned)) expect_equal(s2_area(reloaded, radius = 1), s2_area(unioned, radius = 1)) # also check with oriented = FALSE (may catch quirky nesting) reloaded <- as_s2_geography(structure(exported, class = "wk_wkb"), oriented = FALSE) expect_equal(s2_num_points(reloaded), s2_num_points(unioned)) expect_equal(s2_area(reloaded, radius = 1), s2_area(unioned, radius = 1)) } }) test_that("s2_interpolate() and s2_interpolate_normalized() work", { expect_identical( s2_as_text( s2_interpolate_normalized("LINESTRING (0 0, 0 60)", c(0, 0.25, 0.75, 1, NA)), precision = 5 ), c("POINT (0 0)", "POINT (0 15)", "POINT (0 45)", "POINT (0 60)", NA) ) expect_identical( s2_as_text( s2_interpolate("LINESTRING (0 0, 0 60)", c(0, 0.25, 0.75, 1, NA) * pi / 3, radius = 1), precision = 5 ), c("POINT (0 0)", "POINT (0 15)", "POINT (0 45)", "POINT (0 60)", NA) ) expect_error( s2_interpolate_normalized("POINT (0 1)", 1), "must be a polyline" ) expect_error( s2_interpolate_normalized("MULTILINESTRING ((0 1, 1 1), (1 1, 1 2))", 1), "must be a simple geography" ) }) s2/tests/testthat/test-s2-constructors-formatters.R0000644000176200001440000001033313774601521022164 0ustar liggesusers test_that("s2_geog_point() works", { expect_wkt_equal(s2_geog_point(-64, 45), "POINT (-64 45)") }) test_that("s2_make_line() works", { expect_wkt_equal( s2_make_line(c(-64, 8), c(45, 71)), "LINESTRING (-64 45, 8 71)" ) # check separation using feature_id expect_wkt_equal( s2_make_line( c(-64, 8, -27, -27), c(45, 71, 0, 45), feature_id = c(1, 1, 2, 2) ), c("LINESTRING (-64 45, 8 71)", "LINESTRING (-27 0, -27 45)") ) }) test_that("s2_make_polygon() works", { expect_wkt_equal( s2_make_polygon(c(-45, 8, 0), c(64, 71, 90)), "POLYGON ((-45 64, 8 71, 0 90, -45 64))" ) # check that loops can be open or closed expect_wkt_equal( s2_make_polygon(c(-45, 8, 0, -45), c(64, 71, 90, 64)), "POLYGON ((-45 64, 8 71, 0 90, -45 64))" ) # check feature/ring separation expect_wkt_equal( s2_make_polygon( c(20, 10, 10, 30, 45, 30, 20, 20, 40, 20, 45), c(35, 30, 10, 5, 20, 20, 15, 25, 40, 45, 30), feature_id = c(rep(1, 8), rep(2, 3)), ring_id = c(1, 1, 1, 1, 1, 2, 2, 2, 1, 1, 1) ), c( "POLYGON ((20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 15, 20 25, 30 20))", "POLYGON ((40 40, 20 45, 45 30, 40 40))" ) ) }) test_that("s2_geog_from_wkt() works", { expect_wkt_equal(s2_geog_from_text("POINT (-64 45)"), "POINT (-64 45)") }) test_that("s2_geog_from_wkb() works", { expect_wkt_equal(s2_geog_from_wkb(as_wkb("POINT (-64 45)")), "POINT (-64 45)") }) test_that("s2_as_text() works", { expect_identical( s2_as_text("POINT (0.1234567890123456 0.1234567890123456)"), "POINT (0.1234567890123456 0.1234567890123456)" ) }) test_that("s2_as_binary() works", { expect_identical( s2_as_binary("POINT (0 0)", endian = 1), structure( list( as.raw( c(0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ) ) ), class = "blob" ) ) }) test_that("s2_as_binary works on (multi)polygons", { geog <- s2_data_countries() wkb <- s2_as_binary(geog) expect_identical( sum(vapply(wkb, length, integer(1))), 173318L ) expect_identical(length(wkb), length(geog)) }) test_that("polygon constructors respect oriented and check arguments", { polygon_with_bad_hole_wkt <- "POLYGON ( (20 35, 10 30, 10 10, 30 5, 45 20, 20 35), (30 20, 20 25, 20 15, 30 20) )" polygon_with_bad_hole_wkb <- wk::as_wkb(polygon_with_bad_hole_wkt) polygon_with_bad_hole_df <- data.frame( x = c(20, 10, 10, 30, 45, 30, 20, 20), y = c(35, 30, 10, 5, 20, 20, 25, 15), ring_id = c(1, 1, 1, 1, 1, 2, 2, 2) ) expect_false( s2_intersects( s2_geog_from_text(polygon_with_bad_hole_wkt, oriented = FALSE), "POINT (23 19.5)" ) ) expect_false( s2_intersects( s2_geog_from_wkb(polygon_with_bad_hole_wkb, oriented = FALSE), "POINT (23 19.5)" ) ) expect_false( s2_intersects( with( polygon_with_bad_hole_df, s2_make_polygon(x, y, ring_id = ring_id, oriented = FALSE) ), "POINT (23 19.5)" ) ) expect_error( s2_intersects( s2_geog_from_text(polygon_with_bad_hole_wkt, oriented = TRUE, check = TRUE), "POINT (23 19.5)" ), "Inconsistent loop orientations" ) expect_error( s2_intersects( s2_geog_from_wkb(polygon_with_bad_hole_wkb, oriented = TRUE, check = TRUE), "POINT (23 19.5)" ), "Inconsistent loop orientations" ) expect_error( s2_intersects( with( polygon_with_bad_hole_df, s2_make_polygon(x, y, ring_id = ring_id, oriented = TRUE, check = TRUE) ), "POINT (23 19.5)" ), "Inconsistent loop orientations" ) expect_silent( s2_intersects( s2_geog_from_text(polygon_with_bad_hole_wkt, oriented = TRUE, check = FALSE), "POINT (23 19.5)" ) ) expect_silent( s2_intersects( s2_geog_from_wkb(polygon_with_bad_hole_wkb, oriented = TRUE, check = FALSE), "POINT (23 19.5)" ) ) expect_silent( s2_intersects( with( polygon_with_bad_hole_df, s2_make_polygon(x, y, ring_id = ring_id, oriented = TRUE, check = FALSE) ), "POINT (23 19.5)" ) ) }) s2/tests/testthat/test-s2-predicates.R0000644000176200001440000002015313774601521017354 0ustar liggesusers test_that("s2_contains() works", { expect_identical(s2_contains("POINT (0 0)", NA_character_), NA) expect_true(s2_contains("POINT (0 0)", "POINT (0 0)")) expect_false(s2_contains("POINT (0 0)", "POINT (1 1)")) expect_false(s2_contains("POINT (0 0)", "POINT EMPTY")) # make sure model is passed on to at least one binary predicate # in the open model, lines do not contain endpoints (but do contain other points) expect_false(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0)", s2_options(model = "open"))) expect_true(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 1)", s2_options(model = "open"))) expect_false(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0.5)", s2_options(model = "open"))) # semi-open and closed: endpoints are contained expect_true(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0)", s2_options(model = "semi-open"))) expect_true(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 1)", s2_options(model = "semi-open"))) expect_false(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0.5)", s2_options(model = "semi-open"))) expect_true(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0)", s2_options(model = "closed"))) expect_true(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 1)", s2_options(model = "closed"))) expect_false(s2_contains("LINESTRING (0 0, 0 1, 1 1)", "POINT (0 0.5)", s2_options(model = "closed"))) }) test_that("s2_covers() and s2_covered_by() work", { expect_true(s2_covers("POINT (0 0)", "POINT (0 0)")) expect_false(s2_covers("POINT (0 0)", "POINT (1 1)")) expect_false(s2_covers("POINT (0 0)", "POINT EMPTY")) expect_true(s2_covers("LINESTRING (0 0, 1 1)", "POINT (0 0)")) expect_false(s2_covers("LINESTRING (0 0, 1 1)", "POINT (-1 -1)")) # make sure line is along a geodesic edge # if the line doesn't contain the endpoints, floating # point math won't always keep it strictly inside or outside # the polygon (hard to predict which way it will go) # (when the model is set such that the boundary of the polygon # is considered 'contained' by it) polygon <- "POLYGON ((0 0, 1 1, 0 1, 0 0))" line <- "LINESTRING (0 0, 1 1)" point <- "POINT (0.5 0.7)" expect_true(s2_covers(polygon, polygon)) expect_false(s2_covers(polygon, line, s2_options(model = "open"))) expect_false(s2_covers(polygon, line, s2_options(model = "semi-open"))) expect_true(s2_covers(polygon, line, s2_options(model = "closed"))) expect_true(s2_covers(polygon, point, s2_options(model = "open"))) expect_true(s2_covers(polygon, point, s2_options(model = "semi-open"))) expect_true(s2_covers(polygon, point, s2_options(model = "closed"))) expect_false(s2_covered_by(polygon, line, s2_options(model = "open"))) expect_false(s2_covered_by(polygon, line, s2_options(model = "semi-open"))) expect_false(s2_covered_by(polygon, line, s2_options(model = "closed"))) expect_true(s2_covered_by(point, polygon, s2_options(model = "open"))) expect_true(s2_covered_by(point, polygon, s2_options(model = "semi-open"))) expect_true(s2_covered_by(point, polygon, s2_options(model = "closed"))) }) test_that("s2_disjoint() works", { expect_identical(s2_disjoint("POINT (0 0)", NA_character_), NA) expect_false(s2_disjoint("POINT (0 0)", "POINT (0 0)")) expect_true(s2_disjoint("POINT (0 0)", "POINT (1 1)")) expect_true(s2_disjoint("POINT (0 0)", "POINT EMPTY")) expect_false(s2_disjoint("LINESTRING (0 0, 1 1)", "POINT (0 0)")) expect_true(s2_disjoint("LINESTRING (0 0, 1 1)", "POINT (-1 -1)")) }) test_that("s2_equals() works", { expect_identical(s2_equals("POINT (0 0)", NA_character_), NA) expect_true(s2_equals("POINT (0 0)", "POINT (0 0)")) expect_true(s2_equals("POINT (0 0)", "POINT (0 0)", s2_options(model = "open"))) expect_true(s2_equals("POINT (0 0)", "POINT (0 0)", s2_options(model = "semi-open"))) expect_true(s2_equals("POINT (0 0)", "POINT (0 0)", s2_options(model = "closed"))) expect_false(s2_equals("POINT (0 0)", "POINT (1 1)")) expect_false(s2_equals("POINT (0 0)", "POINT EMPTY")) expect_true(s2_equals("POINT EMPTY", "POINT EMPTY")) expect_true(s2_equals("LINESTRING (0 0, 1 1)", "LINESTRING (1 1, 0 0)")) expect_false(s2_equals("LINESTRING (0 1, 1 1)", "LINESTRING (1 1, 0 0)")) }) test_that("s2_intersects() works", { expect_identical(s2_intersects("POINT (0 0)", NA_character_), NA) expect_true(s2_intersects("POINT (0 0)", "POINT (0 0)")) expect_false(s2_intersects("POINT (0 0)", "POINT (1 1)")) expect_false(s2_intersects("POINT (0 0)", "POINT EMPTY")) expect_true(s2_intersects("LINESTRING (0 0, 1 1)", "LINESTRING (1 0, 0 1)")) expect_false(s2_intersects("LINESTRING (0 0, 1 1)", "LINESTRING (-2 -2, -1 -1)")) expect_true(s2_intersects("LINESTRING (0 0, 1 1)", "POINT (0 0)")) expect_false(s2_intersects("LINESTRING (0 0, 1 1)", "POINT (0 0)", s2_options(model = "open"))) expect_true(s2_intersects("LINESTRING (0 0, 1 1)", "POINT (0 0)", s2_options(model = "semi-open"))) expect_true(s2_intersects("LINESTRING (0 0, 1 1)", "POINT (0 0)", s2_options(model = "closed"))) polygon = "POLYGON((0 0,1 0,1 1,0 1,0 0))" expect_false(s2_intersects(polygon, "POINT (0 0)")) expect_false(s2_intersects(polygon, "POINT (0 0)", s2_options(model = "open"))) expect_false(s2_intersects(polygon, "POINT (0 0)", s2_options(model = "semi-open"))) expect_true(s2_intersects(polygon, "POINT (0 0)", s2_options(model = "closed"))) }) test_that("s2_intersects_box() works", { expect_error( s2_intersects_box("POINT (-1 -1)", -2, -2, 2, 2, detail = 0), "Can't create polygon" ) expect_true(s2_intersects_box("POINT (0.1 0.1)", 0, 0, 1, 1)) expect_true(s2_intersects_box("POINT (0.1 0.1)", 0, 0, 1, 1)) expect_true(s2_intersects_box("POINT (0.1 0.1)", 0, 0, 1, 1, options = s2_options(model = "open"))) expect_true(s2_intersects_box("POINT (0.1 0.1)", 0, 0, 1, 1, options = s2_options(model = "semi-open"))) expect_true(s2_intersects_box("POINT (0.1 0.1)", 0, 0, 1, 1, options = s2_options(model = "closed"))) expect_false(s2_intersects_box("POINT (1 1)", 0, 0, 1, 1)) expect_false(s2_intersects_box("POINT (0 0)", 0, 0, 1, 1)) expect_false(s2_intersects_box("POINT (0 0)", 0, 0, 1, 1)) expect_false(s2_intersects_box("POINT (0 0)", 0, 0, 1, 1, options = s2_options(model = "open"))) expect_false(s2_intersects_box("POINT (0 0)", 0, 0, 1, 1, options = s2_options(model = "semi-open"))) expect_true(s2_intersects_box("POINT (0 0)", 0, 0, 1, 1, options = s2_options(model = "closed"))) expect_true(s2_intersects_box("POINT (-1 -1)", -2, -2, 2, 2)) expect_false(s2_intersects_box("POINT (-1 -1)", 0, 0, 2, 2)) expect_false(s2_intersects_box("POINT (0 0)", 1, 1, 2, 2)) }) test_that("s2_within() works", { expect_identical(s2_within("POINT (0 0)", NA_character_), NA) expect_true(s2_within("POINT (0 0)", "POINT (0 0)")) expect_false(s2_within("POINT (0 0)", "POINT (1 1)")) expect_false(s2_within("POINT (0 0)", "POINT EMPTY")) expect_false(s2_within("POINT EMPTY", "POINT (0 0)")) expect_true(s2_within("POINT (0 0)", "LINESTRING (0 0, 1 1)", s2_options(model = "semi-open"))) expect_false(s2_within("POINT (0 0)", "LINESTRING (0 0, 1 1)", s2_options(model = "open"))) expect_false(s2_within("POINT (0 0)", "LINESTRING (1 1, 2 2)")) }) test_that("s2_touches() works", { # is inside expect_false(s2_touches("POLYGON ((0 0, 0 1, 1 1, 0 0))", "POINT (0.5 0.75)")) # is outside expect_false(s2_touches("POLYGON ((0 0, 0 1, 1 1, 0 0))", "POINT (-0.5 0.75)")) # is vertex expect_true(s2_touches("POLYGON ((0 0, 0 1, 1 1, 0 0))", "POINT (0 0)")) }) test_that("s2_dwithin() works", { expect_identical(s2_dwithin("POINT (0 0)", NA_character_, 0), NA) expect_true(s2_dwithin("POINT (0 0)", "POINT (90 0)", pi / 2 + 0.01, radius = 1)) expect_false(s2_dwithin("POINT (0 0)", "POINT (90 0)", pi / 2 - 0.01, radius = 1)) expect_false(s2_dwithin("POINT (0 0)", "POINT EMPTY", 0)) expect_true(s2_dwithin("LINESTRING (-45 0, 45 0)", "POINT (0 20)", 21, radius = 180 / pi)) expect_false(s2_dwithin("LINESTRING (-45 0, 45 0)", "POINT (0 20)", 19, radius = 180 / pi)) # check vectorization expect_identical( s2_dwithin("POINT (0 0)", "POINT (90 0)", pi / 2 + c(0.01, -0.01), radius = 1), c(TRUE, FALSE) ) }) s2/tests/testthat/test-utils.R0000644000176200001440000000252314044456532016051 0ustar liggesusers test_that("recycle_common works", { expect_identical(recycle_common(1, 2), list(1, 2)) expect_identical(recycle_common(1, b = 2), list(1, b = 2)) expect_identical(recycle_common(1, 2:4), list(c(1, 1, 1), c(2L, 3L, 4L))) expect_identical(recycle_common(numeric(0), 2), list(numeric(0), numeric(0))) expect_error(recycle_common(numeric(0), 2:4), "Incompatible lengths") }) test_that("problems stopper formats problems correctly", { expect_error( stop_problems_create(1, "Houston! We have a correctly formatted problem!"), "Found 1 feature with invalid" ) expect_error( stop_problems_create(1:11, paste0("problem ", 1:11)), "\\.\\.\\.and 1 more" ) }) test_that("processing problems stopper formats problems correctly", { expect_error( stop_problems_process(1, "Houston! We have a correctly formatted problem!"), "Encountered 1 processing error" ) expect_error( stop_problems_process(1:11, paste0("problem ", 1:11)), "\\.\\.\\.and 1 more" ) }) test_that("wkt tester works", { expect_wkt_equal("POINT (0.123456 0)", "POINT (0.1234561 0)", precision = 6) expect_failure(expect_wkt_equal("POINT (0.123456 0)", "POINT (0.1234561 0)", precision = 7)) }) test_that("almost equal expectation works", { expect_near(0.001, 0, epsilon = 0.0011) expect_failure(expect_near(0.001, 0, epsilon = 0.0009)) }) s2/tests/testthat/test-s2-matrix.R0000644000176200001440000001501214050664625016535 0ustar liggesusers test_that("s2_closest|farthest_feature() works", { cities <- s2_data_cities("London") countries <- s2_data_countries() # with zero length y, results will all be empty expect_identical(s2_closest_feature(cities, character(0)), rep_len(NA_integer_, length(cities))) # should correctly identify that London is closest to United Kingdom country_match <- s2_closest_feature(cities, countries) expect_identical(s2_data_tbl_countries$name[country_match], "United Kingdom") country_match_farthest <- s2_farthest_feature(cities, countries) expect_identical(s2_data_tbl_countries$name[country_match_farthest], "New Zealand") }) test_that("s2_closest_edges() works", { expect_identical( s2_closest_edges( "POINT (0 0)", c("POINT (0 0)", "POINT (0 1)", "POINT (0 2)", "POINT (0 3)"), k = 1 ), list(1L) ) expect_identical( s2_closest_edges( "POINT (0 0)", c("POINT (0 0)", "POINT (0 1)", "POINT (0 2)", "POINT (0 3)"), k = 2, min_distance = 0 ), list(2L) ) expect_identical( s2_closest_edges( "POINT (0 0)", c("POINT (0 0)", "POINT (0 1)", "POINT (0 2)", "POINT (0 3)"), k = 5 ) %>% lapply(sort), list(1:4) ) }) test_that("matrix predicates work", { expect_identical( s2_contains_matrix( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"), ), list(2L) ) expect_identical( s2_within_matrix( c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"), "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))" ), list(integer(0), 1L, integer(0)) ) expect_identical( s2_covers_matrix( "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"), ), list(2L) ) expect_identical( s2_covered_by_matrix( c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"), "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))" ), list(integer(0), 1L, integer(0)) ) expect_identical( s2_intersects_matrix( c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"), "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))" ), list(integer(0), 1L, integer(0)) ) expect_identical( s2_disjoint_matrix( c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"), "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))" ), list(1L, integer(0), 1L) ) expect_identical( s2_equals_matrix( c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)", "POLYGON ((1 0, 1 1, 0 1, 0 0, 1 0))"), "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))" ), list(integer(0), integer(0), integer(0), 1L) ) expect_identical( s2_touches_matrix( c("POINT (0.5 0.5)", "POINT (0 0)", "POINT (-0.5 -0.5)"), "POLYGON ((0 0, 0 1, 1 1, 0 0))" ), list(integer(0), 1L, integer(0)) ) expect_identical( s2_dwithin_matrix( c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"), "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", 0 ), list(integer(0), 1L, integer(0)) ) expect_identical( s2_dwithin_matrix( c("POINT (-1 0.5)", "POINT (0.5 0.5)", "POINT (2 0.5)"), "POLYGON ((0 0, 1 0, 1 1, 0 1, 0 0))", s2_earth_radius_meters() ), list(1L, 1L, 1L) ) }) test_that("s2_(max_)?distance_matrix() works", { x <- c("POINT (0 0)", "POINT (0 90)") y <- c("POINT (180 0)", "POINT (0 -90)", "POINT (0 0)") expect_equal( s2_distance_matrix(x, x, radius = 180 / pi), matrix(c(0, 90, 90, 0), ncol = 2) ) expect_equal( s2_distance_matrix(x, y, radius = 180 / pi), matrix(c(180, 90, 90, 180, 0, 90), ncol = 3) ) # max distance is the same for points expect_equal( s2_max_distance_matrix(x, x, radius = 180 / pi), matrix(c(0, 90, 90, 0), ncol = 2) ) expect_equal( s2_max_distance_matrix(x, y, radius = 180 / pi), matrix(c(180, 90, 90, 180, 0, 90), ncol = 3) ) # NA handling for both rows and cols y[2] <- NA expect_true(all(is.na(s2_distance_matrix(x, y)[, 2]))) expect_true(all(is.na(s2_max_distance_matrix(x, y)[, 2]))) x[2] <- NA expect_true(all(is.na(s2_distance_matrix(x, y)[2, ]))) expect_true(all(is.na(s2_max_distance_matrix(x, y)[2, ]))) }) test_that("s2_may_intersect_matrix() works", { countries <- s2_data_countries() timezones <- s2_data_timezones() maybe_intersects <- s2_may_intersect_matrix(countries, timezones) intersects <- s2_intersects_matrix_brute_force(countries, timezones) for (i in seq_along(countries)) { expect_identical(setdiff(intersects[[!!i]], maybe_intersects[[!!i]]), integer(0)) } }) test_that("indexed matrix predicates return the same thing as brute-force comparisons", { countries <- s2_data_countries() timezones <- s2_data_timezones() # contains expect_identical( s2_contains_matrix(countries, countries), s2_contains_matrix_brute_force(countries, countries) ) expect_identical( s2_contains_matrix(timezones, countries), s2_contains_matrix_brute_force(timezones, countries) ) # within expect_identical( s2_within_matrix(countries, countries), s2_within_matrix_brute_force(countries, countries) ) expect_identical( s2_within_matrix(timezones, countries), s2_within_matrix_brute_force(timezones, countries) ) # covers expect_identical( s2_covers_matrix(countries, countries), s2_covers_matrix_brute_force(countries, countries) ) expect_identical( s2_covers_matrix(timezones, countries), s2_covers_matrix_brute_force(timezones, countries) ) # covered by expect_identical( s2_covered_by_matrix(countries, countries), s2_covered_by_matrix_brute_force(countries, countries) ) expect_identical( s2_covered_by_matrix(timezones, countries), s2_covered_by_matrix_brute_force(timezones, countries) ) # intersects expect_identical( s2_intersects_matrix(countries, countries), s2_intersects_matrix_brute_force(countries, countries) ) expect_identical( s2_intersects_matrix(timezones, countries), s2_intersects_matrix_brute_force(timezones, countries) ) # disjoint expect_identical( s2_disjoint_matrix(countries, countries), s2_disjoint_matrix_brute_force(countries, countries) ) expect_identical( s2_disjoint_matrix(timezones, countries), s2_disjoint_matrix_brute_force(timezones, countries) ) # equals expect_identical( s2_equals_matrix(countries, countries), s2_equals_matrix_brute_force(countries, countries) ) expect_identical( s2_equals_matrix(timezones, countries), s2_equals_matrix_brute_force(timezones, countries) ) }) s2/tests/testthat/test-s2-cell.R0000644000176200001440000002451214052002014016131 0ustar liggesusers test_that("s2_cell class works", { expect_s3_class(new_s2_cell(double()), "s2_cell") expect_s3_class(new_s2_cell(NA_real_), "s2_cell") expect_true(is.na(new_s2_cell(NA_real_))) expect_identical(as_s2_cell(s2_cell()), s2_cell()) }) test_that("invalid and sentinel values work as expected", { expect_false(s2_cell_is_valid(s2_cell_sentinel())) expect_false(s2_cell_is_valid(s2_cell_invalid())) expect_false(is.na(s2_cell_sentinel())) expect_false(is.na(s2_cell_invalid())) expect_true(s2_cell_sentinel() > s2_cell_invalid()) }) test_that("sort() and unique() work", { # temporary workaround for broken c() in wk expect_identical( unique(new_s2_cell(c(unclass(s2_cell_sentinel()), NA, 0, 0, unclass(s2_cell("5"))))), new_s2_cell(c(0, unclass(s2_cell("5")), NA, unclass(s2_cell_sentinel()))) ) expect_identical( sort(new_s2_cell(c(unclass(s2_cell_sentinel()), NA, 0, 0, unclass(s2_cell("5"))))), new_s2_cell(c(0, 0, unclass(s2_cell("5")), NA, unclass(s2_cell_sentinel()))) ) expect_identical( sort( new_s2_cell(c(unclass(s2_cell_sentinel()), NA, 0, 0, unclass(s2_cell("5")))), decreasing = TRUE ), rev(new_s2_cell(c(0, 0, unclass(s2_cell("5")), NA, unclass(s2_cell_sentinel())))) ) }) test_that("geography exporters work", { expect_identical( s2_as_text(s2_cell_center(as_s2_cell(s2_lnglat(c(-64, NA), c(45, NA)))), precision = 5), c("POINT (-64 45)", NA) ) expect_identical( s2_as_text(s2_cell_polygon(as_s2_cell(s2_lnglat(c(-64, NA), c(45, NA)))), precision = 5), c("POLYGON ((-64 45, -64 45, -64 45, -64 45, -64 45))", NA) ) expect_identical( s2_as_text(s2_cell_boundary(as_s2_cell(s2_lnglat(c(-64, NA), c(45, NA)))), precision = 5), c("LINESTRING (-64 45, -64 45, -64 45, -64 45, -64 45)", NA) ) expect_equal( s2_x(s2_cell_vertex(s2_cell(c("5", "5", "5", NA)), k = c(0, 1, NA, 1))), c(45, 135, NA, NA) ) }) test_that("s2_cell_is_valid() works", { expect_identical( s2_cell_is_valid(new_s2_cell(double())), logical() ) expect_identical( s2_cell_is_valid(new_s2_cell(NA_real_)), FALSE ) }) test_that("Ops, Math, and Summary errors for non-meaningful s2_cell() ops", { expect_error(s2_cell("X") + 1, "not meaningful") expect_error(abs(s2_cell("X")), "not meaningful") expect_error(all(s2_cell("X")), "not meaningful") }) test_that("Ops, Math, and Summary ops work", { expect_identical( s2_cell(c("5", "5", "x", "x", "x", NA, NA)) == s2_cell(c("5", "x", "5", "x", NA, "x", NA)), c(TRUE, FALSE, FALSE, TRUE, NA, NA, NA) ) expect_identical( s2_cell(c("5", "5", "x", "x", "x", NA, NA)) != s2_cell(c("5", "x", "5", "x", NA, "x", NA)), c(FALSE, TRUE, TRUE, FALSE, NA, NA, NA) ) # 'invalid' is 0 expect_identical( s2_cell(c("5", "5", "x", "x", "x", NA, NA)) > s2_cell(c("5", "x", "5", "x", NA, "x", NA)), c(FALSE, TRUE, FALSE, FALSE, NA, NA, NA) ) expect_identical( s2_cell(c("5", "5", "x", "x", "x", NA, NA)) >= s2_cell(c("5", "x", "5", "x", NA, "x", NA)), c(TRUE, TRUE, FALSE, TRUE, NA, NA, NA) ) expect_identical( s2_cell(c("5", "5", "x", "x", "x", NA, NA)) < s2_cell(c("5", "x", "5", "x", NA, "x", NA)), c(FALSE, FALSE, TRUE, FALSE, NA, NA, NA) ) expect_identical( s2_cell(c("5", "5", "x", "x", "x", NA, NA)) <= s2_cell(c("5", "x", "5", "x", NA, "x", NA)), c(TRUE, FALSE, TRUE, TRUE, NA, NA, NA) ) expect_identical( cummax(s2_cell(c("5", "x", "5", "x", NA, "x", NA))), s2_cell(c("5", "5", "5", "5", NA, NA, NA)) ) expect_identical( cummin(s2_cell(c("5", "x", "5", "x", NA, "x", NA))), s2_cell(c("5", "x", "x", "x", NA, NA, NA)) ) expect_identical( range(s2_cell(c("5", "x", "5", "x", NA, "x", NA)), na.rm = TRUE), s2_cell(c("X", "5")) ) expect_identical( max(s2_cell(c("5", "x", "5", "x", NA, "x", NA)), na.rm = TRUE), s2_cell("5") ) expect_identical( min(s2_cell(c("5", "x", "5", "x", NA, "x", NA)), na.rm = TRUE), s2_cell("X") ) expect_identical( range(s2_cell(c("5", "x", "5", "x", NA, "x", NA))), s2_cell(c(NA_character_, NA_character_)) ) expect_identical(range(s2_cell()), s2_cell(c(NA_character_, NA_character_))) expect_identical( range(s2_cell(NA_character_), na.rm = TRUE), s2_cell(c(NA_character_, NA_character_)) ) }) test_that("Binary ops are recycled at the C++ level", { expect_identical(s2_cell(rep("5", 2)) == s2_cell(rep("5", 2)), c(TRUE, TRUE)) expect_identical(s2_cell(rep("5", 1)) == s2_cell(rep("5", 2)), c(TRUE, TRUE)) expect_identical(s2_cell(rep("5", 2)) == s2_cell(rep("5", 1)), c(TRUE, TRUE)) expect_error(s2_cell(rep("5", 2)) == s2_cell(rep("5", 0)), "Can't recycle") }) test_that("s2_cell is not numeric", { expect_false(is.numeric(s2_cell())) }) test_that("s2_cell subsetting and concatenation work", { cells <- new_s2_cell(c(NA_real_, NA_real_)) expect_identical(cells[1], new_s2_cell(NA_real_)) expect_identical(cells[[1]], cells[1]) expect_identical( c(cells, cells), new_s2_cell(c(NA_real_, NA_real_, NA_real_, NA_real_)) ) expect_identical( rep(cells, 2), new_s2_cell(c(NA_real_, NA_real_, NA_real_, NA_real_)) ) expect_identical( rep_len(cells, 4), new_s2_cell(c(NA_real_, NA_real_, NA_real_, NA_real_)) ) expect_error( c(new_s2_cell(c(NA_real_, NA_real_)), 1:5), "Can't combine" ) }) test_that("s2_cell() can be created from s2_geography()", { expect_identical( as_s2_cell(as_s2_geography("POINT (-64 45)")), s2_cell("4b59a0cd83b5de49") ) }) test_that("s2_cell() can be created from s2_lnglat()", { expect_identical( as_s2_cell(s2_lnglat(-64, 45)), s2_cell("4b59a0cd83b5de49") ) }) test_that("s2_cell() can be created from s2_point()", { expect_identical( as_s2_cell(as_s2_point(s2_lnglat(-64, 45))), s2_cell("4b59a0cd83b5de49") ) }) test_that("s2_cell() can be created from character", { expect_identical( as_s2_cell("4b59a0cd83b5de49"), as_s2_cell(s2_lnglat(-64, 45)) ) }) test_that("subset-assignment works", { x <- as_s2_cell(c(NA, "4b59a0cd83b5de49", "4b5f6a7856889a33")) expect_identical(as.character(x[3]), "4b5f6a7856889a33") x[3] <- "4b59a0cd83b5de49" expect_identical(as.character(x[3]), "4b59a0cd83b5de49") x[[3]] <- "4b5f6a7856889a33" expect_identical(as.character(x[3]), "4b5f6a7856889a33") }) test_that("s2_cell can be put into a data.frame", { expect_identical( data.frame(geom = new_s2_cell(NA_real_)), new_data_frame(list(geom = new_s2_cell(NA_real_))) ) expect_error(as.data.frame(new_s2_cell(NA_real_)), "cannot coerce class") }) test_that("s2_cell default format/print/str methods work", { expect_identical( format(s2_cell()), as.character(s2_cell()) ) expect_output(print(new_s2_cell(double())), "s2_cell") expect_output(print(new_s2_cell(NA_real_)), "s2_cell") expect_output(str(as_s2_cell(character())), "s2_cell\\[0\\]") expect_output(str(as_s2_cell(NA_character_)), "NA") }) test_that("s2 cell exporters work", { expect_equal( as.data.frame(s2_cell_to_lnglat(s2_cell(c("4b59a0cd83b5de49", "x", NA)))), as.data.frame(c(s2_lnglat(-64, 45), s2_lnglat(NA, NA), s2_lnglat(NA, NA))) ) expect_identical( s2_cell_debug_string(s2_cell(c("4b5f6a7856889a33", NA))), c("2/112233231103300223101010310121", NA) ) expect_match(s2_cell_debug_string(s2_cell("X")), "Invalid") }) test_that("s2_cell() accessors work", { expect_identical( s2_cell_is_valid(s2_cell(c("4b5f6a7856889a33", "5", "x", NA))), c(TRUE, TRUE, FALSE, FALSE) ) expect_identical( s2_cell_level(s2_cell(c("4b5f6a7856889a33", "5", "x", NA))), c(30L, 0L, NA, NA) ) expect_identical( s2_cell_is_leaf(s2_cell(c("4b5f6a7856889a33", "5", "x", NA))), c(TRUE, FALSE, NA, NA) ) expect_identical( s2_cell_is_face(s2_cell(c("4b5f6a7856889a33", "5", "x", NA))), c(FALSE, TRUE, NA, NA) ) expect_equal( s2_cell_area(s2_cell(c("5", "x", NA)), radius = 1), c(2 * pi / 3, NA, NA) ) expect_equal( s2_cell_area_approx(s2_cell(c("5", "x", NA)), radius = 1), c(2 * pi / 3, NA, NA) ) }) test_that("s2_cell() transversers work", { expect_identical( s2_cell_parent(s2_cell(c("5", "4b5f6a7856889a33", "x", NA)), 0), s2_cell(c("5", "5", NA, NA)) ) leaf_manual <- function(x, children) { if (length(children) == 0) { x } else { leaf_manual( s2_cell_child(x, children[1]), children[-1] ) } } # see output of s2_cell_debug_string() expect_identical( leaf_manual( s2_cell("5"), as.numeric(strsplit("112233231103300223101010310121", "")[[1]]) ), s2_cell("4b5f6a7856889a33") ) expect_identical( s2_cell_child(s2_cell(c("5", "5", "5", "x", NA)), c(-1, 4, NA, 0, 0)), s2_cell(as.character(c(NA, NA, NA, NA, NA))) ) expect_identical( s2_cell_debug_string(s2_cell_edge_neighbour(s2_cell("5"), 0:3)), c("1/", "3/", "4/", "0/") ) expect_identical( s2_cell_edge_neighbour(s2_cell(c("5", "5", "5", "x", NA)), c(-1, 4, NA, 0, 0)), s2_cell(as.character(c(NA, NA, NA, NA, NA))) ) }) test_that("s2_cell() binary operators work", { cell <- s2_cell("4b5f6a7856889a33") expect_true(s2_cell_contains(s2_cell_parent(cell), cell)) expect_false(s2_cell_contains(cell, s2_cell_parent(cell))) expect_identical(s2_cell_contains(s2_cell_sentinel(), s2_cell_sentinel()), NA) expect_equal( s2_cell_distance( as_s2_cell(s2_lnglat(0, 90)), as_s2_cell(s2_lnglat(0, 0)), radius = 1 ), pi / 2 ) expect_equal( s2_cell_max_distance( as_s2_cell(s2_lnglat(0, 90)), as_s2_cell(s2_lnglat(0, 0)), radius = 1 ), pi / 2 ) f1 <- s2_cell_parent(as_s2_cell(s2_lnglat(0, 0)), 0) f2 <- s2_cell_parent(as_s2_cell(s2_lnglat(0, 90)), 0) expect_equal(s2_cell_distance(f1, f2), 0) expect_equal(s2_cell_max_distance(f1, f2, radius = 1), pi) expect_identical(s2_cell_max_distance(s2_cell_sentinel(), s2_cell_sentinel()), NA_real_) expect_identical(s2_cell_distance(s2_cell_sentinel(), s2_cell_sentinel()), NA_real_) expect_false( s2_cell_may_intersect( as_s2_cell(s2_lnglat(0, 90)), as_s2_cell(s2_lnglat(0, 0)) ) ) expect_true(s2_cell_may_intersect(s2_cell_parent(cell), cell)) expect_true(s2_cell_may_intersect(cell, s2_cell_parent(cell))) expect_identical(s2_cell_may_intersect(s2_cell_sentinel(), s2_cell_sentinel()), NA) }) s2/tests/testthat/test-s2-options.R0000644000176200001440000000064613774601521016731 0ustar liggesusers test_that("s2_options() works", { expect_is(s2_options(), "s2_options") }) test_that("s2_options() errors are readable", { expect_error(s2_intersects("POINT EMPTY", "POINT EMPTY", options = NULL), "must be created using") expect_error(s2_options(model = "not a model"), "must be one of") expect_error(s2_options(snap_radius = 100), "radius is too large") expect_error(s2_snap_level(31), "between 1 and 30") }) s2/tests/testthat/test-s2-earth.R0000644000176200001440000000021613774601521016332 0ustar liggesusers test_that("s2_earth_radius_meters works", { expect_is(s2_earth_radius_meters(), "numeric") expect_length(s2_earth_radius_meters(), 1) }) s2/tests/testthat/test-data.R0000644000176200001440000000161513774601521015622 0ustar liggesusers test_that("s2_data_country() works", { expect_is(s2_data_countries("Germany"), "s2_geography") expect_length(s2_data_countries("Germany"), 1) expect_is(s2_data_countries("Europe"), "s2_geography") expect_length(s2_data_countries("Europe"), 39) expect_is(s2_data_countries(), "s2_geography") expect_length(s2_data_countries(), 177) }) test_that("s2_data_timezone() works", { expect_is(s2_data_timezones(), "s2_geography") expect_length(s2_data_timezones(), 120) expect_is(s2_data_timezones(-4), "s2_geography") expect_length(s2_data_timezones(-4), 3) expect_is(s2_data_timezones(-15, 15), "s2_geography") expect_length(s2_data_timezones(-15, 15), 120) }) test_that("s2_data_cities() works", { expect_is(s2_data_cities(), "s2_geography") expect_length(s2_data_cities(), 243) expect_is(s2_data_cities("Cairo"), "s2_geography") expect_length(s2_data_cities("Cairo"), 1) }) s2/tests/testthat.R0000644000176200001440000000006013774601521013725 0ustar liggesuserslibrary(testthat) library(s2) test_check("s2") s2/tests/area.Rout0000644000176200001440000000173613774601521013540 0ustar liggesusers> library(s2) > s2_set_snaplevel(30) # for windows i386 [1] -1 > u = s2_union( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" + ) > s2_area(u, radius = 1) [1] 0.05284581 > s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) + + s2_area("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", radius = 1) - + s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1) [1] 0.04910511 > s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) [1] 0.03038216 > s2_area("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", radius = 1) [1] 0.03002974 > s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1) [1] 0.01130679 > > df = s2_difference( + "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", + "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))" + ) > s2_area(df, radius = 1) - + (s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) - + s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1)) [1] 0.003740703 > s2/tests/area.R0000644000176200001440000000156313774601521013006 0ustar liggesuserslibrary(s2) u = s2_union( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", s2_options(snap = s2_snap_level(30)) ) s2_area(u, radius = 1) s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) + s2_area("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", radius = 1) - s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1) s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) s2_area("POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", radius = 1) s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1) df = s2_difference( "POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", "POLYGON ((5 5, 15 5, 15 15, 5 15, 5 5))", s2_options(snap = s2_snap_level(30)) ) s2_area(df, radius = 1) - (s2_area("POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0))", radius = 1) - s2_area("POLYGON ((5 5, 10 5, 10 15, 5 10, 5 5))", radius = 1)) s2/src/0000755000176200001440000000000014122647320011366 5ustar liggesuserss2/src/s2-bounds.cpp0000644000176200001440000000334113774601521013714 0ustar liggesusers #include "s2/s2latlng_rect.h" #include "s2/s2cap.h" #include "s2-options.h" #include "geography-operator.h" #include "point-geography.h" #include "polyline-geography.h" #include "polygon-geography.h" #include "geography-collection.h" #include using namespace Rcpp; // [[Rcpp::export]] DataFrame cpp_s2_bounds_cap(List geog) { SEXP item; NumericVector lng(geog.size()), lat(geog.size()), angle(geog.size()); for (R_xlen_t i = 0; i < geog.size(); i++) { Rcpp::checkUserInterrupt(); item = geog[i]; if (item == R_NilValue) { lat[i] = lng[i] = angle[i] = NA_REAL; } else { Rcpp::XPtr feature(item); S2Cap cap = feature->GetCapBound(); S2LatLng center(cap.center()); lng[i] = center.lng().degrees(); lat[i] = center.lat().degrees(); angle[i] = cap.GetRadius().degrees(); } } return DataFrame::create( _["lng"] = lng, _["lat"] = lat, _["angle"] = angle ); } // [[Rcpp::export]] DataFrame cpp_s2_bounds_rect(List geog) { SEXP item; NumericVector lng_lo(geog.size()), lat_lo(geog.size()), lng_hi(geog.size()), lat_hi(geog.size()); for (R_xlen_t i = 0; i < geog.size(); i++) { Rcpp::checkUserInterrupt(); item = geog[i]; if (item == R_NilValue) { lng_lo[i] = lat_lo[i] = lng_hi[i] = lat_hi[i] = NA_REAL; } else { Rcpp::XPtr feature(item); S2LatLngRect rect = feature->GetRectBound(); lng_lo[i] = rect.lng_lo().degrees(); lat_lo[i] = rect.lat_lo().degrees(); lng_hi[i] = rect.lng_hi().degrees(); lat_hi[i] = rect.lat_hi().degrees(); } } return DataFrame::create( _["lng_lo"] = lng_lo, _["lat_lo"] = lat_lo, _["lng_hi"] = lng_hi, _["lat_hi"] = lat_hi ); } s2/src/s2-lnglat.cpp0000644000176200001440000000251413774601521013704 0ustar liggesusers #include "s2/s2latlng.h" #include "s2/s2point.h" #include "wk/rcpp-io.hpp" #include "wk/wkb-reader.hpp" #include "wk/wkb-writer.hpp" #include using namespace Rcpp; // [[Rcpp::export]] List s2_lnglat_from_numeric(NumericVector lng, NumericVector lat) { List output(lat.size()); S2LatLng item; for (R_xlen_t i = 0; i < lat.size(); i++) { item = S2LatLng::FromDegrees(lat[i], lng[i]); output[i] = XPtr(new S2LatLng(item)); } return output; } // [[Rcpp::export]] List s2_lnglat_from_s2_point(List s2_point) { List output(s2_point.size()); SEXP item; S2LatLng newItem; for (R_xlen_t i = 0; i < s2_point.size(); i++) { item = s2_point[i]; if (item == R_NilValue) { output[i] = R_NilValue; } else { XPtr ptr(item); output[i] = XPtr(new S2LatLng(*ptr)); } } return output; } // [[Rcpp::export]] List data_frame_from_s2_lnglat(List xptr) { NumericVector lng(xptr.size()); NumericVector lat(xptr.size()); SEXP item; for (R_xlen_t i = 0; i < xptr.size(); i++) { item = xptr[i]; if (item == R_NilValue) { lng[i] = NA_REAL; lat[i] = NA_REAL; } else { XPtr ptr(item); lng[i] = ptr->lng().degrees(); lat[i] = ptr->lat().degrees(); } } return List::create(_["lng"] = lng, _["lat"] = lat); } s2/src/s2-matrix.cpp0000644000176200001440000005271714050664625013743 0ustar liggesusers #include #include #include #include "s2/s2boolean_operation.h" #include "s2/s2closest_edge_query.h" #include "s2/s2furthest_edge_query.h" #include "s2/s2shape_index_region.h" #include "geography-operator.h" #include "s2-options.h" #include using namespace Rcpp; std::unordered_map buildSourcedIndex(List geog, MutableS2ShapeIndex* index) { std::unordered_map indexSource; std::vector shapeIds; for (R_xlen_t j = 0; j < geog.size(); j++) { checkUserInterrupt(); SEXP item2 = geog[j]; // build index and store index IDs so that shapeIds can be // mapped back to the geog index if (item2 == R_NilValue) { Rcpp::stop("Missing `y` not allowed in binary indexed operators()"); } else { Rcpp::XPtr feature2(item2); shapeIds = feature2->BuildShapeIndex(index); for (size_t k = 0; k < shapeIds.size(); k ++) { indexSource[shapeIds[k]] = j; } } } return indexSource; } std::unordered_set findPossibleIntersections(const S2Region& region, const MutableS2ShapeIndex* index, std::unordered_map& source, int maxRegionCells) { std::unordered_set mightIntersectIndices; MutableS2ShapeIndex::Iterator indexIterator(index); // generate a small covering of the region S2RegionCoverer coverer; coverer.mutable_options()->set_max_cells(maxRegionCells); S2CellUnion covering = coverer.GetCovering(region); // iterate over cells in the featureIndex for (S2CellId featureCellId: covering) { S2ShapeIndex::CellRelation relation = indexIterator.Locate(featureCellId); if (relation == S2ShapeIndex::CellRelation::INDEXED) { // we're in luck! these indexes have this cell in common // add all the features it contains as possible intersectors for featureIndex const S2ShapeIndexCell& cell = indexIterator.cell(); for (int k = 0; k < cell.num_clipped(); k++) { int shapeId = cell.clipped(k).shape_id(); mightIntersectIndices.insert(source[shapeId]); } } else if(relation == S2ShapeIndex::CellRelation::SUBDIVIDED) { // promising! the geog2 index has a child cell of it.id() // (at which indexIterator is now positioned) // keep iterating until the iterator is done OR we're no longer at a child cell of // it.id(). The ordering of the iterator isn't guaranteed anywhere in the documentation; // however, this ordering would be consistent with that of a Normalized // S2CellUnion. while (!indexIterator.done() && featureCellId.contains(indexIterator.id())) { // potentially many cells in the indexIterator, so let the user cancel if this is // running too long checkUserInterrupt(); // add all the features the child cell contains as possible intersectors for featureIndex const S2ShapeIndexCell& cell = indexIterator.cell(); for (int k = 0; k < cell.num_clipped(); k++) { int shapeId = cell.clipped(k).shape_id(); mightIntersectIndices.insert(source[shapeId]); } // go to the next cell in the index indexIterator.Next(); } } // else: relation == S2ShapeIndex::CellRelation::DISJOINT (do nothing) } return mightIntersectIndices; } template class IndexedBinaryGeographyOperator: public UnaryGeographyOperator { public: std::unique_ptr geog2Index; std::unordered_map geog2IndexSource; IndexedBinaryGeographyOperator() { this->geog2Index = absl::make_unique(); } // maxEdgesPerCell should be between 10 and 50, with lower numbers // leading to more memory usage (but potentially faster query times). Benchmarking // with binary prediates seems to indicate that values on the high end // of the spectrum do a reasonable job of efficient preselection, and that // decreasing this value does little to increase performance. virtual void buildIndex(List geog2, int maxEdgesPerCell = 50) { MutableS2ShapeIndex::Options indexOptions; indexOptions.set_max_edges_per_cell(maxEdgesPerCell); this->geog2Index = absl::make_unique(indexOptions); this->geog2IndexSource = buildSourcedIndex(geog2, this->geog2Index.get()); } }; // -------- closest/farthest feature ---------- // [[Rcpp::export]] IntegerVector cpp_s2_closest_feature(List geog1, List geog2) { class Op: public IndexedBinaryGeographyOperator { public: int processFeature(Rcpp::XPtr feature, R_xlen_t i) { S2ClosestEdgeQuery query(this->geog2Index.get()); S2ClosestEdgeQuery::ShapeIndexTarget target(feature->ShapeIndex()); const auto& result = query.FindClosestEdge(&target); if (result.is_empty()) { return NA_INTEGER; } else { // convert to R index (+1) return this->geog2IndexSource[result.shape_id()] + 1; } } }; Op op; op.buildIndex(geog2); return op.processVector(geog1); } // [[Rcpp::export]] IntegerVector cpp_s2_farthest_feature(List geog1, List geog2) { class Op: public IndexedBinaryGeographyOperator { public: int processFeature(Rcpp::XPtr feature, R_xlen_t i) { S2FurthestEdgeQuery query(this->geog2Index.get()); S2FurthestEdgeQuery::ShapeIndexTarget target(feature->ShapeIndex()); const auto& result = query.FindFurthestEdge(&target); if (result.is_empty()) { return NA_INTEGER; } else { // convert to R index (+1) return this->geog2IndexSource[result.shape_id()] + 1; } } }; Op op; op.buildIndex(geog2); return op.processVector(geog1); } // [[Rcpp::export]] List cpp_s2_closest_edges(List geog1, List geog2, int n, double min_distance) { class Op: public IndexedBinaryGeographyOperator { public: IntegerVector processFeature(Rcpp::XPtr feature, R_xlen_t i) { S2ClosestEdgeQuery query(this->geog2Index.get()); query.mutable_options()->set_max_results(n); S2ClosestEdgeQuery::ShapeIndexTarget target(feature->ShapeIndex()); const auto& result = query.FindClosestEdges(&target); // this code searches edges, which may come from the same feature std::unordered_set features; for (S2ClosestEdgeQuery::Result res : result) { if (res.distance().radians() > this->min_distance) { features.insert(this->geog2IndexSource[res.shape_id()] + 1); } } return IntegerVector(features.begin(), features.end()); } int n; double min_distance; }; Op op; op.n = n; op.min_distance = min_distance; op.buildIndex(geog2); return op.processVector(geog1); } // ----------- indexed binary predicate operators ----------- class IndexedMatrixPredicateOperator: public IndexedBinaryGeographyOperator { public: // a max_cells value of 8 was suggested in the S2RegionCoverer docs as a // reasonable approximation of a geometry, although benchmarking seems to indicate that // increasing this number above 4 actually decreasses performance (using a value // of 1 dramatically decreases performance) IndexedMatrixPredicateOperator(List s2options, int maxFeatureCells = 4): maxFeatureCells(maxFeatureCells) { GeographyOperationOptions options(s2options); this->options = options.booleanOperationOptions(); } // See IndexedBinaryGeographyOperator::buildIndex() for why 50 is the default value // for maxEdgesPerCell void buildIndex(List geog2, int maxEdgesPerCell = 50) { this->geog2 = geog2; IndexedBinaryGeographyOperator::buildIndex(geog2, maxEdgesPerCell); } IntegerVector processFeature(Rcpp::XPtr feature, R_xlen_t i) { S2ShapeIndex* index1 = feature->ShapeIndex(); S2ShapeIndexRegion region = MakeS2ShapeIndexRegion(index1); // build a list of candidate feature indices std::unordered_set mightIntersectIndices = findPossibleIntersections( region, this->geog2Index.get(), this->geog2IndexSource, this->maxFeatureCells ); // loop through features from geog2 that might intersect feature // and build a list of indices that actually intersect (based on // this->actuallyIntersects(), which might perform alternative // comparisons) std::vector actuallyIntersectIndices; for (R_xlen_t j: mightIntersectIndices) { SEXP item = this->geog2[j]; XPtr feature2(item); if (this->actuallyIntersects(index1, feature2->ShapeIndex(), i, j)) { // convert to R index here + 1 actuallyIntersectIndices.push_back(j + 1); } } // return sorted integer vector std::sort(actuallyIntersectIndices.begin(), actuallyIntersectIndices.end()); return Rcpp::IntegerVector(actuallyIntersectIndices.begin(), actuallyIntersectIndices.end()); }; virtual bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) = 0; protected: List geog2; S2BooleanOperation::Options options; int maxFeatureCells; }; // [[Rcpp::export]] List cpp_s2_may_intersect_matrix(List geog1, List geog2, int maxEdgesPerCell, int maxFeatureCells, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options, int maxFeatureCells): IndexedMatrixPredicateOperator(s2options, maxFeatureCells) {} bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) { return true; }; }; Op op(s2options, maxFeatureCells); op.buildIndex(geog2, maxEdgesPerCell); return op.processVector(geog1); } // [[Rcpp::export]] List cpp_s2_contains_matrix(List geog1, List geog2, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options): IndexedMatrixPredicateOperator(s2options) {} bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) { return S2BooleanOperation::Contains(*index1, *index2, this->options); }; }; Op op(s2options); op.buildIndex(geog2); return op.processVector(geog1); } // [[Rcpp::export]] List cpp_s2_within_matrix(List geog1, List geog2, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options): IndexedMatrixPredicateOperator(s2options) {} bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) { // note reversed index2, index1 return S2BooleanOperation::Contains(*index2, *index1, this->options); }; }; Op op(s2options); op.buildIndex(geog2); return op.processVector(geog1); } // [[Rcpp::export]] List cpp_s2_intersects_matrix(List geog1, List geog2, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options): IndexedMatrixPredicateOperator(s2options) {} bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) { return S2BooleanOperation::Intersects(*index1, *index2, this->options); }; }; Op op(s2options); op.buildIndex(geog2); return op.processVector(geog1); } // [[Rcpp::export]] List cpp_s2_equals_matrix(List geog1, List geog2, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options): IndexedMatrixPredicateOperator(s2options) {} bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) { return S2BooleanOperation::Equals(*index1, *index2, this->options); }; }; Op op(s2options); op.buildIndex(geog2); return op.processVector(geog1); } // [[Rcpp::export]] List cpp_s2_touches_matrix(List geog1, List geog2, List s2options) { class Op: public IndexedMatrixPredicateOperator { public: Op(List s2options): IndexedMatrixPredicateOperator(s2options) { this->closedOptions = this->options; this->closedOptions.set_polygon_model(S2BooleanOperation::PolygonModel::CLOSED); this->closedOptions.set_polyline_model(S2BooleanOperation::PolylineModel::CLOSED); this->openOptions = this->options; this->openOptions.set_polygon_model(S2BooleanOperation::PolygonModel::OPEN); this->openOptions.set_polyline_model(S2BooleanOperation::PolylineModel::OPEN); } bool actuallyIntersects(S2ShapeIndex* index1, S2ShapeIndex* index2, R_xlen_t i, R_xlen_t j) { // efficiently re-uses the index on geog2 and takes advantage of short-circuiting && return S2BooleanOperation::Intersects(*index1, *index2, this->closedOptions) && !S2BooleanOperation::Intersects(*index1, *index2, this->openOptions); }; private: S2BooleanOperation::Options closedOptions; S2BooleanOperation::Options openOptions; }; Op op(s2options); op.buildIndex(geog2); return op.processVector(geog1); } // ----------- brute force binary predicate operators ------------------ class BruteForceMatrixPredicateOperator { public: std::vector geog2Indices; S2BooleanOperation::Options options; BruteForceMatrixPredicateOperator() {} BruteForceMatrixPredicateOperator(Rcpp::List s2options) { GeographyOperationOptions options(s2options); this->options = options.booleanOperationOptions(); } List processVector(Rcpp::List geog1, Rcpp::List geog2) { List output(geog1.size()); // using instead of IntegerVector because // std::vector is much faster with repeated calls to .push_back() std::vector trueIndices; for (R_xlen_t i = 0; i < geog1.size(); i++) { trueIndices.clear(); SEXP item1 = geog1[i]; if (item1 == R_NilValue) { output[i] = R_NilValue; } else { Rcpp::XPtr feature1(item1); for (size_t j = 0; j < geog2.size(); j++) { checkUserInterrupt(); SEXP item2 = geog2[j]; if (item2 == R_NilValue) { stop("Missing `y` not allowed in binary index operations"); } XPtr feature2(item2); bool result = this->processFeature(feature1, feature2, i, j); if (result) { // convert to R index here (+1) trueIndices.push_back(j + 1); } } IntegerVector itemOut(trueIndices.size()); for (size_t k = 0; k < trueIndices.size(); k++) { itemOut[k] = trueIndices[k]; } output[i] = itemOut; } } return output; } virtual bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) = 0; }; // [[Rcpp::export]] List cpp_s2_dwithin_matrix(List geog1, List geog2, double distance) { class Op: public BruteForceMatrixPredicateOperator { public: double distance; Op(double distance): distance(distance) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { S2ClosestEdgeQuery query(feature2->ShapeIndex()); S2ClosestEdgeQuery::ShapeIndexTarget target(feature1->ShapeIndex()); return query.IsDistanceLessOrEqual(&target, S1ChordAngle::Radians(this->distance)); }; }; Op op(distance); return op.processVector(geog1, geog2); } // ----------- distance matrix operators ------------------- template class MatrixGeographyOperator { public: MatrixType processVector(Rcpp::List geog1, Rcpp::List geog2) { MatrixType output(geog1.size(), geog2.size()); SEXP item1; SEXP item2; for (R_xlen_t i = 0; i < geog1.size(); i++) { item1 = geog1[i]; if (item1 == R_NilValue) { for (R_xlen_t j = 0; j < geog2.size(); j++) { output(i, j) = MatrixType::get_na(); } } else { Rcpp::XPtr feature1(item1); for (R_xlen_t j = 0; j < geog2.size(); j++) { checkUserInterrupt(); item2 = geog2[j]; if (item2 == R_NilValue) { output(i, j) = MatrixType::get_na(); } else { Rcpp::XPtr feature2(item2); output(i, j) = this->processFeature(feature1, feature2, i, j); } } } } return output; } virtual ScalarType processFeature(Rcpp::XPtr feature1, Rcpp::XPtr feature2, R_xlen_t i, R_xlen_t j) = 0; }; // [[Rcpp::export]] NumericMatrix cpp_s2_distance_matrix(List geog1, List geog2) { class Op: public MatrixGeographyOperator { double processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { S2ClosestEdgeQuery query(feature1->ShapeIndex()); S2ClosestEdgeQuery::ShapeIndexTarget target(feature2->ShapeIndex()); const auto& result = query.FindClosestEdge(&target); S1ChordAngle angle = result.distance(); double distance = angle.ToAngle().radians(); if (distance == R_PosInf) { return NA_REAL; } else { return distance; } } }; Op op; return op.processVector(geog1, geog2); } // [[Rcpp::export]] NumericMatrix cpp_s2_max_distance_matrix(List geog1, List geog2) { class Op: public MatrixGeographyOperator { double processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { S2FurthestEdgeQuery query(feature1->ShapeIndex()); S2FurthestEdgeQuery::ShapeIndexTarget target(feature2->ShapeIndex()); const auto& result = query.FindFurthestEdge(&target); S1ChordAngle angle = result.distance(); double distance = angle.ToAngle().radians(); // returns -1 if one of the indexes is empty // NA is more consistent with the BigQuery // function, and makes way more sense if (distance < 0) { return NA_REAL; } else { return distance; } } }; Op op; return op.processVector(geog1, geog2); } // ----------- brute force binary predicate operators (for testing) ------------------ // [[Rcpp::export]] List cpp_s2_contains_matrix_brute_force(List geog1, List geog2, List s2options) { class Op: public BruteForceMatrixPredicateOperator { public: Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { // by default Contains() will return true for Contains(x, EMPTY), which is // not true in BigQuery or GEOS if (feature2->IsEmpty()) { return false; } else { return S2BooleanOperation::Contains( *feature1->ShapeIndex(), *feature2->ShapeIndex(), this->options ); } }; }; Op op(s2options); return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_within_matrix_brute_force(List geog1, List geog2, List s2options) { class Op: public BruteForceMatrixPredicateOperator { public: Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { // note reversed index2, index1 // by default Contains() will return true for Contains(x, EMPTY), which is // not true in BigQuery or GEOS if (feature1->IsEmpty()) { return false; } else { return S2BooleanOperation::Contains( *feature2->ShapeIndex(), *feature1->ShapeIndex(), this->options ); } }; }; Op op(s2options); return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_intersects_matrix_brute_force(List geog1, List geog2, List s2options) { class Op: public BruteForceMatrixPredicateOperator { public: Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { return S2BooleanOperation::Intersects( *feature1->ShapeIndex(), *feature2->ShapeIndex(), this->options ); } }; Op op(s2options); return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_disjoint_matrix_brute_force(List geog1, List geog2, List s2options) { class Op: public BruteForceMatrixPredicateOperator { public: Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { return !S2BooleanOperation::Intersects( *feature1->ShapeIndex(), *feature2->ShapeIndex(), this->options ); } }; Op op(s2options); return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_equals_matrix_brute_force(List geog1, List geog2, List s2options) { class Op: public BruteForceMatrixPredicateOperator { public: Op(List s2options): BruteForceMatrixPredicateOperator(s2options) {} bool processFeature(XPtr feature1, XPtr feature2, R_xlen_t i, R_xlen_t j) { return S2BooleanOperation::Equals( *feature1->ShapeIndex(), *feature2->ShapeIndex(), this->options ); } }; Op op(s2options); return op.processVector(geog1, geog2); } s2/src/s2-c-api.cpp0000644000176200001440000001255014052002014013373 0ustar liggesusers #include #include "s2/s2projections.h" #include "s2/s2edge_tessellator.h" typedef struct s2_projection_t s2_projection_t; typedef struct s2_tessellator_t s2_tessellator_t; #ifdef __cplusplus extern "C" { #endif s2_projection_t* s2_projection_create_plate_carree(double scale); s2_projection_t* s2_projection_create_mercator(double max_x); void s2_projection_destroy(s2_projection_t* projection); int s2_projection_project(s2_projection_t* projection, const double* coord_in, double* coord_out); int s2_projection_unproject(s2_projection_t* projection, const double* coord_in, double* coord_out); s2_tessellator_t* s2_tessellator_create(s2_projection_t* projection, double tolerance_radians); void s2_tessellator_destroy(s2_tessellator_t* tessellator); int s2_tessellator_reset(s2_tessellator_t* tessellator); int s2_tessellator_add_r2_point(s2_tessellator_t* tessellator, const double* coord); int s2_tessellator_add_s2_point(s2_tessellator_t* tessellator, const double* coord); int s2_tessellator_r2_points_size(s2_tessellator_t* tessellator); int s2_tessellator_s2_points_size(s2_tessellator_t* tessellator); int s2_tessellator_r2_point(s2_tessellator_t* tessellator, int i, double* coord); int s2_tessellator_s2_point(s2_tessellator_t* tessellator, int i, double* coord); #ifdef __cplusplus } #endif s2_projection_t* s2_projection_create_plate_carree(double scale) { return (s2_projection_t*) new S2::PlateCarreeProjection(scale); } s2_projection_t* s2_projection_create_mercator(double max_x) { return (s2_projection_t*) new S2::MercatorProjection(max_x); } void s2_projection_destroy(s2_projection_t* projection) { if (projection != nullptr) { delete ((S2::Projection*) projection); } } int s2_projection_project(s2_projection_t* projection, const double* coord_in, double* coord_out) { S2Point p(coord_in[0], coord_in[1], coord_in[2]); R2Point result = ((S2::Projection*) projection)->Project(p); coord_out[0] = result.x(); coord_out[1] = result.y(); return true; } int s2_projection_unproject(s2_projection_t* projection, const double* coord_in, double* coord_out) { R2Point p(coord_in[0], coord_in[1]); S2Point result = ((S2::Projection*) projection)->Unproject(p); coord_out[0] = result.x(); coord_out[1] = result.y(); coord_out[2] = result.z(); return true; } // Wrapper class around a tessellator that also keeps coordinate buffers // and methods to iterate through them. class TessellatorWrapper { public: TessellatorWrapper(s2_projection_t* projection, double tolerance_radians): tessellator((S2::Projection*) projection, S1Angle::Radians(tolerance_radians)), has_r2_last(false), has_s2_last(false) {} void reset() { s2points.clear(); r2points.clear(); has_r2_last = false; has_s2_last = false; } void add_r2_point(const double* coord) { R2Point pt(coord[0], coord[1]); if (has_r2_last) { this->tessellator.AppendUnprojected(r2last, pt, &(this->s2points)); } this->r2last = pt; this->has_r2_last = true; } void add_s2_point(const double* coord) { S2Point pt(coord[0], coord[1], coord[2]); if (has_s2_last) { this->tessellator.AppendProjected(s2last, pt, &(this->r2points)); } this->s2last = pt; this->has_s2_last = true; } int r2_points_size() { return this->r2points.size(); } void r2_point(int i, double* coord) { const R2Point& pt = r2points[i]; coord[0] = pt.x(); coord[1] = pt.y(); } int s2_points_size() { return this->s2points.size(); } void s2_point(int i, double* coord) { const S2Point& pt = s2points[i]; coord[0] = pt.x(); coord[1] = pt.y(); coord[2] = pt.z(); } private: S2EdgeTessellator tessellator; std::vector s2points; std::vector r2points; R2Point r2last; S2Point s2last; bool has_r2_last; bool has_s2_last; }; s2_tessellator_t* s2_tessellator_create(s2_projection_t* projection, double tolerance_radians) { return (s2_tessellator_t*) new TessellatorWrapper(projection, tolerance_radians); } void s2_tessellator_destroy(s2_tessellator_t* tessellator) { if (tessellator != nullptr) { delete ((TessellatorWrapper*) tessellator); } } int s2_tessellator_reset(s2_tessellator_t* tessellator) { ((TessellatorWrapper*) tessellator)->reset(); return true; } int s2_tessellator_add_r2_point(s2_tessellator_t* tessellator, const double* coord) { ((TessellatorWrapper*) tessellator)->add_r2_point(coord); return true; } int s2_tessellator_add_s2_point(s2_tessellator_t* tessellator, const double* coord) { ((TessellatorWrapper*) tessellator)->add_s2_point(coord); return true; } int s2_tessellator_r2_points_size(s2_tessellator_t* tessellator) { return ((TessellatorWrapper*) tessellator)->r2_points_size(); } int s2_tessellator_s2_points_size(s2_tessellator_t* tessellator) { return ((TessellatorWrapper*) tessellator)->s2_points_size(); } int s2_tessellator_r2_point(s2_tessellator_t* tessellator, int i, double* coord) { ((TessellatorWrapper*) tessellator)->r2_point(i, coord); return true; } int s2_tessellator_s2_point(s2_tessellator_t* tessellator, int i, double* coord) { ((TessellatorWrapper*) tessellator)->s2_point(i, coord); return true; } s2/src/wk-impl.c0000644000176200001440000000003114052002014013067 0ustar liggesusers #include "wk-v1-impl.c" s2/src/polygon-geography.h0000644000176200001440000002373414044456532015230 0ustar liggesusers #ifndef POLYGON_GEOGRAPHY_H #define POLYGON_GEOGRAPHY_H #include "wk/reader.hpp" #include "geography.h" #include "point-geography.h" #include "polyline-geography.h" // This class handles polygons (POLYGON and MULTIPOLYGON) // This is similar to an S2PolygonLayer class PolygonGeography: public Geography { public: PolygonGeography() {} PolygonGeography(std::unique_ptr polygon): polygon(std::move(polygon)) {} Geography::Type GeographyType() { return Geography::Type::GEOGRAPHY_POLYGON; } bool FindValidationError(S2Error* error) { return this->polygon->FindValidationError(error); } const S2Polygon* Polygon() { return this->polygon.get(); } bool IsCollection() { return this->outerLoopIndices().size() > 1; } int Dimension() { return 2; } int NumPoints() { return this->polygon->num_vertices(); } bool IsEmpty() { return this->polygon->is_empty(); } double Area() { return this->polygon->GetArea(); } double Length() { return 0; } double Perimeter() { std::unique_ptr boundary = this->Boundary(); return boundary->Length(); } double X() { Rcpp::stop("Can't compute X value of a non-point geography"); } double Y() { Rcpp::stop("Can't compute Y value of a non-point geography"); } S2Point Centroid() { return this->polygon->GetCentroid(); } S2Cap GetCapBound() { return this->polygon->GetCapBound(); } S2LatLngRect GetRectBound() { return this->polygon->GetRectBound(); } std::unique_ptr Boundary() { PolylineGeography::Builder builder; std::vector> flatIndices = this->flatLoopIndices(); // export multilinestring WKGeometryMeta meta(WKGeometryType::MultiLineString, false, false, false); meta.hasSize = true; meta.size = this->polygon->num_loops(); builder.nextGeometryStart(meta, WKReader::PART_ID_NONE); int loopId = 0; for (size_t i = 0; i < flatIndices.size(); i++) { this->exportLoops(&builder, meta, flatIndices[i], loopId); loopId += flatIndices[i].size(); } builder.nextGeometryEnd(meta, WKReader::PART_ID_NONE); return builder.build(); } std::vector BuildShapeIndex(MutableS2ShapeIndex* index) { std::vector shapeIds(1); std::unique_ptr shape = absl::make_unique(); shape->Init(this->polygon.get()); shapeIds[0] = index->Add(std::move(shape)); return shapeIds; } void Export(WKGeometryHandler* handler, uint32_t partId) { std::vector> flatIndices = this->flatLoopIndices(); if (flatIndices.size() > 1) { // export multipolygon WKGeometryMeta meta(WKGeometryType::MultiPolygon, false, false, false); meta.hasSize = true; meta.size = flatIndices.size(); WKGeometryMeta childMeta(WKGeometryType::Polygon, false, false, false); childMeta.hasSize = true; handler->nextGeometryStart(meta, partId); for (size_t i = 0; i < flatIndices.size(); i++) { childMeta.size = flatIndices[i].size(); handler->nextGeometryStart(childMeta, i); this->exportLoops(handler, childMeta, flatIndices[i]); handler->nextGeometryEnd(childMeta, i); } handler->nextGeometryEnd(meta, partId); } else if (flatIndices.size() > 0) { // export polygon WKGeometryMeta meta(WKGeometryType::Polygon, false, false, false); meta.hasSize = true; meta.size = flatIndices[0].size(); handler->nextGeometryStart(meta, partId); this->exportLoops(handler, meta, flatIndices[0]); handler->nextGeometryEnd(meta, partId); } else { // export empty polygon WKGeometryMeta meta(WKGeometryType::Polygon, false, false, false); meta.hasSize = true; meta.size = 0; handler->nextGeometryStart(meta, partId); handler->nextGeometryEnd(meta, partId); } } class Builder: public GeographyBuilder { public: Builder(bool oriented, bool check): oriented(oriented), check(check) {} void nextLinearRingStart(const WKGeometryMeta& meta, uint32_t size, uint32_t ringId) { // skip the last vertex (WKB rings are theoretically closed) if (size > 0) { this->vertices = std::vector(size - 1); } else { this->vertices = std::vector(); } } void nextCoordinate(const WKGeometryMeta& meta, const WKCoord& coord, uint32_t coordId) { if (coordId < this->vertices.size()) { vertices[coordId] = S2LatLng::FromDegrees(coord.y, coord.x).Normalized().ToPoint(); } } void nextLinearRingEnd(const WKGeometryMeta& meta, uint32_t size, uint32_t ringId) { std::unique_ptr loop = absl::make_unique(); loop->set_s2debug_override(S2Debug::DISABLE); loop->Init(vertices); if (!oriented) { loop->Normalize(); } if (this->check && !loop->IsValid()) { std::stringstream err; err << "Loop " << (this->loops.size()) << " is not valid: "; S2Error error; loop->FindValidationError(&error); err << error.text(); throw WKParseException(err.str()); } this->loops.push_back(std::move(loop)); } std::unique_ptr build() { std::unique_ptr polygon = absl::make_unique(); polygon->set_s2debug_override(S2Debug::DISABLE); if (this->loops.size() > 0 && oriented) { polygon->InitOriented(std::move(this->loops)); } else if (this->loops.size() > 0) { polygon->InitNested(std::move(this->loops)); } // make sure polygon is valid if (this->check && !polygon->IsValid()) { S2Error error; polygon->FindValidationError(&error); throw WKParseException(error.text()); } return absl::make_unique(std::move(polygon)); } private: bool oriented; bool check; std::vector vertices; std::vector> loops; }; private: std::unique_ptr polygon; // Calculate which loops in the polygon are outer loops (loop->depth() == 0) std::vector outerLoopIndices() { std::vector indices; for (int i = 0; i < this->polygon->num_loops(); i++) { if (this->polygon->loop(i)->depth() == 0) { indices.push_back(i); } } return indices; } // Calculate the arrangement of loops in the form of a multipolygon // (list(list(shell, !!! holes))) std::vector> flatLoopIndices() { std::vector outerLoops = this->outerLoopIndices(); std::vector> flatIndices(outerLoops.size()); for (size_t i = 0; i < outerLoops.size(); i++) { int k = outerLoops[i]; flatIndices[i] = std::vector(); // the first loop here is the shell (depth == 0) flatIndices[i].push_back(k); // loops in the S2Polygon are arranged such that child loops are // directly after the outer loop, so add all loop indices before // the next parent loop (or end of polygon). This is similar to // S2Polygon::GetLastDescendant() but is slightly easier to understand. while (++k < this->polygon->num_loops() && this->polygon->loop(k)->depth() > 0) { flatIndices[i].push_back(k); } } return flatIndices; } void exportLoops(WKGeometryHandler* handler, WKGeometryMeta meta, const std::vector& loopIndices, int loopIdOffset = 0) { S2LatLng point; for (size_t i = 0; i < loopIndices.size(); i++) { int loopId = loopIndices[i]; S2Loop* loop = this->polygon->loop(loopId); if (loop->num_vertices() == 0) { continue; } // this is a slightly ugly way to make it possible to export either the // boundaries or the loops using the same code WKGeometryMeta childMeta(WKGeometryType::LineString, false, false, false); childMeta.hasSize = true; childMeta.size = loop->num_vertices() + 1; WKGeometryMeta coordMeta; if (meta.geometryType == WKGeometryType::Polygon) { handler->nextLinearRingStart(meta, loop->num_vertices() + 1, i + loopIdOffset); coordMeta = meta; } else if (meta.geometryType == WKGeometryType::MultiLineString) { handler->nextGeometryStart(childMeta, i + loopIdOffset); coordMeta = childMeta; } else { std::stringstream err; err << "Can't export S2Loop with parent geometry type " << meta.geometryType; Rcpp::stop(err.str()); } if ((loop->depth() % 2) == 0) { // if this is the first ring, use the internal vertex order for (int j = 0; j < loop->num_vertices(); j++) { point = S2LatLng(loop->vertex(j)); handler->nextCoordinate( coordMeta, WKCoord::xy(point.lng().degrees(), point.lat().degrees()), j ); } // close the loop! point = S2LatLng(loop->vertex(0)); handler->nextCoordinate( coordMeta, WKCoord::xy(point.lng().degrees(), point.lat().degrees()), loop->num_vertices() ); } else { // if an interior ring, reverse the vertex order for (int j = 0; j < loop->num_vertices(); j++) { point = S2LatLng(loop->vertex(loop->num_vertices() - 1 - j)); handler->nextCoordinate( coordMeta, WKCoord::xy(point.lng().degrees(), point.lat().degrees()), j ); } // close the loop! point = S2LatLng(loop->vertex(loop->num_vertices() - 1)); handler->nextCoordinate( coordMeta, WKCoord::xy(point.lng().degrees(), point.lat().degrees()), loop->num_vertices() ); } if (meta.geometryType == WKGeometryType::Polygon) { handler->nextLinearRingEnd(meta, loop->num_vertices() + 1, i + loopIdOffset); } else if (meta.geometryType == WKGeometryType::MultiLineString) { handler->nextGeometryEnd(childMeta, i + loopIdOffset); } } } }; #endif s2/src/init.cpp0000644000176200001440000000101713774601521013041 0ustar liggesusers #include "s2/s2debug.h" #include using namespace Rcpp; // [[Rcpp::export]] void cpp_s2_init() { // It's important to set this flag, as users might have "debug" flags // for their build environment, and there are some checks that will terminate // R instead of throw an exception if this value is set to true. // When possible, we also disable debug checks on a per-operation basis // if there is another way to do so (e.g., constructing S2Loop and S2Polygon objects). FLAGS_s2debug = false; // # nocov } s2/src/s2-constructors-formatters.cpp0000644000176200001440000000374414044456532017366 0ustar liggesusers #include #include "wk/rcpp-translate.hpp" #include "wk/rcpp-coord-reader.hpp" #include "wk-geography.h" using namespace Rcpp; // [[Rcpp::export]] List cpp_s2_geog_point(NumericVector x, NumericVector y) { NumericVector z(x.size()); z.fill(NA_REAL); NumericVector m(x.size()); m.fill(NA_REAL); WKRcppPointCoordProvider provider(x, y, z, m); WKRcppPointCoordReader reader(provider); WKGeographyWriter writer(provider.nFeatures()); reader.setHandler(&writer); while (reader.hasNextFeature()) { checkUserInterrupt(); reader.iterateFeature(); } return writer.output; } // [[Rcpp::export]] List cpp_s2_make_line(NumericVector x, NumericVector y, IntegerVector featureId) { NumericVector z(x.size()); z.fill(NA_REAL); NumericVector m(x.size()); m.fill(NA_REAL); WKRcppLinestringCoordProvider provider(x, y, z, m, featureId); WKRcppLinestringCoordReader reader(provider); WKGeographyWriter writer(provider.nFeatures()); reader.setHandler(&writer); while (reader.hasNextFeature()) { checkUserInterrupt(); reader.iterateFeature(); } return writer.output; } // [[Rcpp::export]] List cpp_s2_make_polygon(NumericVector x, NumericVector y, IntegerVector featureId, IntegerVector ringId, bool oriented, bool check) { NumericVector z(x.size()); z.fill(NA_REAL); NumericVector m(x.size()); m.fill(NA_REAL); WKRcppPolygonCoordProvider provider(x, y, z, m, featureId, ringId); WKRcppPolygonCoordReader reader(provider); WKGeographyWriter writer(provider.nFeatures()); writer.setOriented(oriented); writer.setCheck(check); reader.setHandler(&writer); while (reader.hasNextFeature()) { checkUserInterrupt(); reader.iterateFeature(); } if (writer.problemId.size() > 0) { Environment s2NS = Environment::namespace_env("s2"); Function stopProblems = s2NS["stop_problems_create"]; stopProblems(writer.problemId, writer.problems); } return writer.output; } s2/src/s2-transformers.cpp0000644000176200001440000005421414060402272015143 0ustar liggesusers #include "s2/s2boolean_operation.h" #include "s2/s2closest_edge_query.h" #include "s2/s2polygon.h" #include "s2/s2polyline.h" #include "s2/s2point.h" #include "s2/s2error.h" #include "s2/s2boolean_operation.h" #include "s2/s2builder.h" #include "s2/s2builderutil_s2polygon_layer.h" #include "s2/s2builderutil_s2polyline_vector_layer.h" #include "s2/s2builderutil_s2point_vector_layer.h" #include "s2/s2builderutil_closed_set_normalizer.h" #include "s2/s2builderutil_snap_functions.h" #include "s2/s2shape_index_buffered_region.h" #include "s2/s2region_coverer.h" #include "s2-options.h" #include "geography-operator.h" #include "point-geography.h" #include "polyline-geography.h" #include "polygon-geography.h" #include "geography-collection.h" #include using namespace Rcpp; std::unique_ptr geographyFromLayers(std::vector points, std::vector> polylines, std::unique_ptr polygon, int dimensions) { // count non-empty dimensions bool has_polygon = (dimensions & GeographyOperationOptions::Dimension::POLYGON) && !polygon->is_empty(); bool has_polyline = (dimensions & GeographyOperationOptions::Dimension::POLYLINE) && (polylines.size() > 0); bool has_points = (dimensions & GeographyOperationOptions::Dimension::POINT) && (points.size() > 0); int nonEmptyDimensions = has_polygon + has_polyline + has_points; // return empty output if (nonEmptyDimensions == 0) { return absl::make_unique(); } // return mixed dimension output if (nonEmptyDimensions > 1) { std::vector> features; if (has_points) { features.push_back(absl::make_unique(std::move(points))); } if (has_polyline) { features.push_back(absl::make_unique(std::move(polylines))); } if (has_polygon) { features.push_back(absl::make_unique(std::move(polygon))); } return absl::make_unique(std::move(features)); } // return single dimension output if (has_polygon) { return absl::make_unique(std::move(polygon)); } else if (has_polyline) { return absl::make_unique(std::move(polylines)); } else { return absl::make_unique(std::move(points)); } } std::unique_ptr doBooleanOperation(S2ShapeIndex* index1, S2ShapeIndex* index2, S2BooleanOperation::OpType opType, S2BooleanOperation::Options options, GeographyOperationOptions::LayerOptions layerOptions) { // create the data structures that will contain the output std::vector points; std::vector> polylines; std::unique_ptr polygon = absl::make_unique(); s2builderutil::LayerVector layers(3); layers[0] = absl::make_unique(&points, layerOptions.pointLayerOptions); layers[1] = absl::make_unique(&polylines, layerOptions.polylineLayerOptions); layers[2] = absl::make_unique(polygon.get(), layerOptions.polygonLayerOptions); // do the boolean operation S2BooleanOperation booleanOp( opType, // normalizing the closed set here is required for line intersections // to work as expected s2builderutil::NormalizeClosedSet(std::move(layers)), options ); // build and check for errors S2Error error; if (!booleanOp.Build(*index1, *index2, &error)) { stop(error.text()); } // construct output return geographyFromLayers( std::move(points), std::move(polylines), std::move(polygon), layerOptions.dimensions ); } std::unique_ptr rebuildGeography(S2ShapeIndex* index, S2Builder::Options options, GeographyOperationOptions::LayerOptions layerOptions) { // create the builder S2Builder builder(options); // create the data structures that will contain the output std::vector points; std::vector> polylines; std::unique_ptr polygon = absl::make_unique(); // add shapes to the layer with the appropriate dimension builder.StartLayer( absl::make_unique(&points, layerOptions.pointLayerOptions) ); for (S2Shape* shape : *index) { if (shape->dimension() == 0) { builder.AddShape(*shape); } } builder.StartLayer( absl::make_unique(&polylines, layerOptions.polylineLayerOptions) ); for (S2Shape* shape : *index) { if (shape->dimension() == 1) { builder.AddShape(*shape); } } builder.StartLayer( absl::make_unique(polygon.get(), layerOptions.polygonLayerOptions) ); for (S2Shape* shape : *index) { if (shape->dimension() == 2) { builder.AddShape(*shape); } } // build the output S2Error error; if (!builder.Build(&error)) { throw GeographyOperatorException(error.text()); } // construct output return geographyFromLayers( std::move(points), std::move(polylines), std::move(polygon), layerOptions.dimensions ); } class BooleanOperationOp: public BinaryGeographyOperator { public: BooleanOperationOp(S2BooleanOperation::OpType opType, List s2options): opType(opType) { GeographyOperationOptions options(s2options); this->options = options.booleanOperationOptions(); this->layerOptions = options.layerOptions(); } SEXP processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { std::unique_ptr geography = doBooleanOperation( feature1->ShapeIndex(), feature2->ShapeIndex(), this->opType, this->options, this->layerOptions ); return Rcpp::XPtr(geography.release()); } private: S2BooleanOperation::OpType opType; S2BooleanOperation::Options options; GeographyOperationOptions::LayerOptions layerOptions; }; // [[Rcpp::export]] List cpp_s2_intersection(List geog1, List geog2, List s2options) { BooleanOperationOp op(S2BooleanOperation::OpType::INTERSECTION, s2options); return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_union(List geog1, List geog2, List s2options) { BooleanOperationOp op(S2BooleanOperation::OpType::UNION, s2options); return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_difference(List geog1, List geog2, List s2options) { BooleanOperationOp op(S2BooleanOperation::OpType::DIFFERENCE, s2options); return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_sym_difference(List geog1, List geog2, List s2options) { BooleanOperationOp op(S2BooleanOperation::OpType::SYMMETRIC_DIFFERENCE, s2options); return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_coverage_union_agg(List geog, List s2options, bool naRm) { GeographyOperationOptions options(s2options); MutableS2ShapeIndex index; SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { item = geog[i]; if (item == R_NilValue && !naRm) { return List::create(R_NilValue); } if (item != R_NilValue) { Rcpp::XPtr feature(item); feature->BuildShapeIndex(&index); } } MutableS2ShapeIndex emptyIndex; std::unique_ptr geography = doBooleanOperation( &index, &emptyIndex, S2BooleanOperation::OpType::UNION, options.booleanOperationOptions(), options.layerOptions() ); return List::create(Rcpp::XPtr(geography.release())); } // This approach to aggregation is slow but accurate. There is probably a more efficient way // to accumulate geometries and/or re-use the layers vector but thus far I haven't figured // out a way to make that work. // [[Rcpp::export]] List cpp_s2_union_agg(List geog, List s2options, bool naRm) { GeographyOperationOptions options(s2options); GeographyOperationOptions::LayerOptions layerOptions = options.layerOptions(); S2BooleanOperation::Options unionOptions = options.booleanOperationOptions(); S2Builder::Options buillderOptions = options.builderOptions(); // using smart pointers here so that we can use swap() to // use replace accumulatedIndex with index after each union std::unique_ptr index = absl::make_unique(); std::unique_ptr accumulatedIndex = absl::make_unique(); SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { item = geog[i]; if (item == R_NilValue && !naRm) { return List::create(R_NilValue); } if (item != R_NilValue) { Rcpp::XPtr feature(item); index->Clear(); s2builderutil::LayerVector layers(3); layers[0] = absl::make_unique(index.get(), layerOptions.pointLayerOptions); layers[1] = absl::make_unique(index.get(), layerOptions.polylineLayerOptions); layers[2] = absl::make_unique(index.get(), layerOptions.polygonLayerOptions); S2BooleanOperation booleanOp( S2BooleanOperation::OpType::UNION, s2builderutil::NormalizeClosedSet(std::move(layers)), unionOptions ); S2Error error; if (!booleanOp.Build(*accumulatedIndex, *(feature->ShapeIndex()), &error)) { stop(error.text()); } accumulatedIndex.swap(index); } } std::unique_ptr geography = rebuildGeography( accumulatedIndex.get(), options.builderOptions(), options.layerOptions() ); return List::create(Rcpp::XPtr(geography.release())); } // [[Rcpp::export]] List cpp_s2_centroid_agg(List geog, bool naRm) { S2Point cumCentroid; SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { item = geog[i]; if (item == R_NilValue && !naRm) { return List::create(R_NilValue); } if (item != R_NilValue) { Rcpp::XPtr feature(item); S2Point centroid = feature->Centroid(); if (centroid.Norm2() > 0) { cumCentroid += centroid.Normalize(); } } } List output(1); if (cumCentroid.Norm2() == 0) { output[0] = Rcpp::XPtr(new PointGeography()); } else { output[0] = Rcpp::XPtr(new PointGeography(cumCentroid.Normalize())); } return output; } // [[Rcpp::export]] List cpp_s2_rebuild_agg(List geog, List s2options, bool naRm) { GeographyOperationOptions options(s2options); MutableS2ShapeIndex index; SEXP item; for (R_xlen_t i = 0; i < geog.size(); i++) { item = geog[i]; if (item == R_NilValue && !naRm) { return List::create(R_NilValue); } if (item != R_NilValue) { Rcpp::XPtr feature(item); feature->BuildShapeIndex(&index); } } std::unique_ptr geography = rebuildGeography( &index, options.builderOptions(), options.layerOptions() ); return List::create(Rcpp::XPtr(geography.release())); } std::vector findClosestPoints(S2ShapeIndex* index1, S2ShapeIndex* index2) { // see http://s2geometry.io/devguide/s2closestedgequery.html section on Modeling Accuracy: // Find the edge from index2 that is closest to index1 S2ClosestEdgeQuery query1(index1); query1.mutable_options()->set_include_interiors(false); S2ClosestEdgeQuery::ShapeIndexTarget target1(index2); auto result1 = query1.FindClosestEdge(&target1); if (result1.edge_id() == -1) { return std::vector(); } // Get the edge from index1 (edge1) that is closest to index2. S2Shape::Edge edge1 = query1.GetEdge(result1); // Now find the edge from index2 (edge2) that is closest to edge1. S2ClosestEdgeQuery query2(index2); query2.mutable_options()->set_include_interiors(false); S2ClosestEdgeQuery::EdgeTarget target2(edge1.v0, edge1.v1); auto result2 = query2.FindClosestEdge(&target2); // what if result2 has no edges? if (result2.is_interior()) { stop("S2ClosestEdgeQuery result is interior!"); } S2Shape::Edge edge2 = query2.GetEdge(result2); // Find the closest point pair on edge1 and edge2. std::pair closest = S2::GetEdgePairClosestPoints( edge1.v0, edge1.v1, edge2.v0, edge2.v1 ); std::vector pts(2); pts[0] = closest.first; pts[1] = closest.second; return pts; } // [[Rcpp::export]] List cpp_s2_closest_point(List geog1, List geog2) { class Op: public BinaryGeographyOperator { SEXP processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { std::vector pts = findClosestPoints(feature1->ShapeIndex(), feature2->ShapeIndex()); if (pts.size() == 0) { return XPtr(new PointGeography()); } else { return XPtr(new PointGeography(pts[0])); } } }; Op op; return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_minimum_clearance_line_between(List geog1, List geog2) { class Op: public BinaryGeographyOperator { SEXP processFeature(XPtr feature1, XPtr feature2, R_xlen_t i) { std::vector pts = findClosestPoints(feature1->ShapeIndex(), feature2->ShapeIndex()); if (pts.size() == 0) { return XPtr(new PolylineGeography()); } else if (pts[0] == pts[1]) { return XPtr(new PointGeography(pts)); } else { std::unique_ptr polyline = absl::make_unique(); polyline->Init(pts); std::vector> polylines(1); polylines[0] = std::move(polyline); return XPtr(new PolylineGeography(std::move(polylines))); } } }; Op op; return op.processVector(geog1, geog2); } // [[Rcpp::export]] List cpp_s2_centroid(List geog) { class Op: public UnaryGeographyOperator { SEXP processFeature(XPtr feature, R_xlen_t i) { S2Point centroid = feature->Centroid(); if (centroid.Norm2() == 0) { return XPtr(new PointGeography()); } else { return XPtr(new PointGeography(centroid.Normalize())); } } }; Op op; return op.processVector(geog); } // [[Rcpp::export]] List cpp_s2_boundary(List geog) { class Op: public UnaryGeographyOperator { SEXP processFeature(XPtr feature, R_xlen_t i) { std::unique_ptr ptr = feature->Boundary(); return XPtr(ptr.release()); } }; Op op; return op.processVector(geog); } // [[Rcpp::export]] List cpp_s2_rebuild(List geog, List s2options) { class Op: public UnaryGeographyOperator { public: Op(List s2options) { GeographyOperationOptions options(s2options); this->options = options.builderOptions(); this->layerOptions = options.layerOptions(); } SEXP processFeature(XPtr feature, R_xlen_t i) { std::unique_ptr ptr = rebuildGeography( feature->ShapeIndex(), this->options, this->layerOptions ); return XPtr(ptr.release()); } private: S2Builder::Options options; GeographyOperationOptions::LayerOptions layerOptions; }; Op op(s2options); return op.processVector(geog); } // [[Rcpp::export]] List cpp_s2_unary_union(List geog, List s2options) { class Op: public UnaryGeographyOperator { public: Op(List s2options) { GeographyOperationOptions options(s2options); this->options = options.booleanOperationOptions(); this->layerOptions = options.layerOptions(); } SEXP processFeature(XPtr feature, R_xlen_t i) { // complex union only needed when a polygon is involved bool simpleUnionOK = feature->IsEmpty() || (feature->Dimension() < 2); // valid polygons that are not part of a collection can also use a // simple union (common) if (feature->GeographyType() == Geography::Type::GEOGRAPHY_POLYGON) { S2Error validationError; if(!(feature->Polygon()->FindValidationError(&validationError))) { simpleUnionOK = true; } } if (simpleUnionOK) { MutableS2ShapeIndex emptyIndex; std::unique_ptr ptr = doBooleanOperation( feature->ShapeIndex(), &emptyIndex, S2BooleanOperation::OpType::UNION, this->options, this->layerOptions ); return XPtr(ptr.release()); } else if (feature->GeographyType() == Geography::Type::GEOGRAPHY_POLYGON) { // If we've made it here we have an invalid polygon on our hands. A geography with // invalid loops won't work with the S2BooleanOperation we will use to accumulate // (i.e., union) valid polygons, so we need to rebuild each loop as its own polygon, // splitting crossed edges along the way. const S2Polygon* originalPoly = feature->Polygon(); // Not exposing these options as an argument (except snap function) // because a particular combiation of them is required for this to work S2Builder::Options builderOptions; builderOptions.set_split_crossing_edges(true); builderOptions.set_snap_function(this->options.snap_function()); s2builderutil::S2PolygonLayer::Options layerOptions; layerOptions.set_edge_type(S2Builder::EdgeType::UNDIRECTED); layerOptions.set_validate(false); // Rebuild all loops as polygons using the S2Builder() std::vector> loops; for (int i = 0; i < originalPoly->num_loops(); i++) { std::unique_ptr loop = absl::make_unique(); S2Builder builder(builderOptions); builder.StartLayer(absl::make_unique(loop.get())); builder.AddShape(S2Loop::Shape(originalPoly->loop(i))); S2Error error; if (!builder.Build(&error)) { throw GeographyOperatorException(error.text()); } // Check if the builder created a polygon whose boundary contained more than // half the earth (and invert it if so) if (loop->GetArea() > (2 * M_PI)) { loop->Invert(); } loops.push_back(std::move(loop)); } // Accumulate the union of outer loops (but difference of inner loops) std::unique_ptr accumulatedPolygon = absl::make_unique(); for (int i = 0; i < originalPoly->num_loops(); i++) { std::unique_ptr polygonResult = absl::make_unique(); // Use original nesting to suggest if this loop should be unioned or diffed. // For valid polygons loops are arranged such that the biggest loop is on the outside // followed by holes such that the below strategy should work (since we are // just iterating along the original loop structure) if ((originalPoly->loop(i)->depth() % 2) == 0) { polygonResult->InitToUnion(accumulatedPolygon.get(), loops[i].get()); } else { polygonResult->InitToDifference(accumulatedPolygon.get(), loops[i].get()); } accumulatedPolygon.swap(polygonResult); } return XPtr(new PolygonGeography(std::move(accumulatedPolygon))); } else { // This is a less common case (mixed dimension output that includes a polygon). // In the absence of a clean solution, saving this battle for another day. throw GeographyOperatorException("Unary union for collections is not implemented"); } } private: S2BooleanOperation::Options options; GeographyOperationOptions::LayerOptions layerOptions; }; Op op(s2options); return op.processVector(geog); } // [[Rcpp::export]] List cpp_s2_interpolate_normalized(List geog, NumericVector distanceNormalized) { class Op: public UnaryGeographyOperator { public: NumericVector distanceNormalized; Op(NumericVector distanceNormalized): distanceNormalized(distanceNormalized) {} SEXP processFeature(XPtr feature, R_xlen_t i) { if (NumericVector::is_na(this->distanceNormalized[i])) { return R_NilValue; } if (feature->IsCollection()) { throw GeographyOperatorException("`x` must be a simple geography"); } if (feature->IsEmpty()) { return R_NilValue; } if (feature->GeographyType() == Geography::Type::GEOGRAPHY_POLYLINE) { S2Point point = feature->Polyline()->at(0)->Interpolate(this->distanceNormalized[i]); return XPtr(new PointGeography(point)); } else { throw GeographyOperatorException("`x` must be a polyline geography"); } } }; Op op(distanceNormalized); return op.processVector(geog); } // [[Rcpp::export]] List cpp_s2_buffer_cells(List geog, NumericVector distance, int maxCells, int minLevel) { class Op: public UnaryGeographyOperator { public: NumericVector distance; S2RegionCoverer coverer; Op(NumericVector distance, int maxCells, int minLevel): distance(distance) { this->coverer.mutable_options()->set_max_cells(maxCells); if (minLevel > 0) { this->coverer.mutable_options()->set_min_level(minLevel); } } SEXP processFeature(XPtr feature, R_xlen_t i) { S2ShapeIndexBufferedRegion region; region.Init(feature->ShapeIndex(), S1ChordAngle::Radians(this->distance[i])); S2CellUnion cellUnion; cellUnion = coverer.GetCovering(region); std::unique_ptr polygon = absl::make_unique(); polygon->InitToCellUnionBorder(cellUnion); return XPtr(new PolygonGeography(std::move(polygon))); } }; Op op(distance, maxCells, minLevel); return op.processVector(geog); } s2/src/wk-c-utils.c0000644000176200001440000003430114052002014013515 0ustar liggesusers #define R_NO_REMAP #include #include #include "wk-v1.h" typedef struct s2_projection_t s2_projection_t; typedef struct s2_tessellator_t s2_tessellator_t; #ifdef __cplusplus extern "C" { #endif s2_projection_t* s2_projection_create_plate_carree(double scale); s2_projection_t* s2_projection_create_mercator(double max_x); void s2_projection_destroy(s2_projection_t* projection); int s2_projection_project(s2_projection_t* projection, const double* coord_in, double* coord_out); int s2_projection_unproject(s2_projection_t* projection, const double* coord_in, double* coord_out); s2_tessellator_t* s2_tessellator_create(s2_projection_t* projection, double tolerance_radians); void s2_tessellator_destroy(s2_tessellator_t* tessellator); int s2_tessellator_reset(s2_tessellator_t* tessellator); int s2_tessellator_add_r2_point(s2_tessellator_t* tessellator, const double* coord); int s2_tessellator_add_s2_point(s2_tessellator_t* tessellator, const double* coord); int s2_tessellator_r2_points_size(s2_tessellator_t* tessellator); int s2_tessellator_s2_points_size(s2_tessellator_t* tessellator); int s2_tessellator_r2_point(s2_tessellator_t* tessellator, int i, double* coord); int s2_tessellator_s2_point(s2_tessellator_t* tessellator, int i, double* coord); #ifdef __cplusplus } #endif // Expose projections as external pointers so that they can theoretically be generated // by other packages. void s2_projection_xptr_destroy(SEXP projection_xptr) { s2_projection_destroy((s2_projection_t*) R_ExternalPtrAddr(projection_xptr)); } SEXP c_s2_projection_plate_carree() { SEXP projection_xptr = PROTECT(R_MakeExternalPtr(s2_projection_create_plate_carree(180), R_NilValue, R_NilValue)); R_RegisterCFinalizer(projection_xptr, &s2_projection_xptr_destroy); UNPROTECT(1); return projection_xptr; } SEXP c_s2_projection_mercator() { SEXP projection_xptr = PROTECT(R_MakeExternalPtr(s2_projection_create_mercator(20037508), R_NilValue, R_NilValue)); R_RegisterCFinalizer(projection_xptr, &s2_projection_xptr_destroy); UNPROTECT(1); return projection_xptr; } // The s2_coord_filter at the C level is used both for projecting (i.e., // simple mapping of points from x, y to an x, y, z on the unit sphere) // and tessellating (i.e., projecting AND adding additional points to // ensure that "straight" lines in the current projection refer to the same // line on the surface of the earth when unprojected on to the unit // sphere. Going the other direction, points are added to ensure that // great circle edges (S2's assumption) refer to the same line on the // surface of the earth when exported to the specified projection. // This is similar to D3's "adaptive resampling" which also handles // segmentizing and projecting as a single operation. typedef struct { s2_projection_t* projection; s2_tessellator_t* tessellator; wk_handler_t* next; wk_meta_t meta_copy; wk_vector_meta_t vector_meta_copy; int unproject; uint32_t coord_id; } coord_filter_t; static inline void modify_meta_for_filter(coord_filter_t* coord_filter, const wk_meta_t* meta) { memcpy(&(coord_filter->meta_copy), meta, sizeof(wk_meta_t)); // if we're projecting we're going to end up with an XY point // if we're unprojecting, we're going to get an XYZ point if (coord_filter->unproject) { coord_filter->meta_copy.flags |= WK_FLAG_HAS_Z; } else { coord_filter->meta_copy.flags &= ~WK_FLAG_HAS_Z; } // if we're tesselating a polyline, the 'size' should be set to unknown if (meta->geometry_type == WK_LINESTRING) { coord_filter->meta_copy.size = WK_SIZE_UNKNOWN; } // any srid that initially existed is definitely no longer valid coord_filter->meta_copy.srid = WK_SRID_NONE; } void s2_coord_filter_initialize(int* dirty, void* handler_data) { coord_filter_t* coord_filter = (coord_filter_t*) handler_data; *dirty = 1; coord_filter->next->initialize(&coord_filter->next->dirty, coord_filter->next->handler_data); } int s2_coord_filter_vector_start(const wk_vector_meta_t* meta, void* handler_data) { coord_filter_t* coord_filter = (coord_filter_t*) handler_data; // if we're projecting we're going to end up with an XY point // if we're unprojecting, we're going to get an XYZ point memcpy(&coord_filter->vector_meta_copy, meta, sizeof(wk_vector_meta_t)); if (coord_filter->unproject) { coord_filter->vector_meta_copy.flags |= WK_FLAG_HAS_Z; } else { coord_filter->vector_meta_copy.flags &= ~WK_FLAG_HAS_Z; } return coord_filter->next->vector_start(&(coord_filter->vector_meta_copy), coord_filter->next->handler_data); } SEXP s2_coord_filter_vector_end(const wk_vector_meta_t* meta, void* handler_data) { coord_filter_t* coord_filter = (coord_filter_t*) handler_data; // use the modified vector meta from vector_start return coord_filter->next->vector_end(&(coord_filter->vector_meta_copy), coord_filter->next->handler_data); } int s2_coord_filter_feature_start(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { coord_filter_t* coord_filter = (coord_filter_t*) handler_data; return coord_filter->next->feature_start(&(coord_filter->vector_meta_copy), feat_id, coord_filter->next->handler_data); } int s2_coord_filter_feature_null(void* handler_data) { coord_filter_t* coord_filter = (coord_filter_t*) handler_data; return coord_filter->next->null_feature(coord_filter->next->handler_data); } int s2_coord_filter_feature_end(const wk_vector_meta_t* meta, R_xlen_t feat_id, void* handler_data) { coord_filter_t* coord_filter = (coord_filter_t*) handler_data; return coord_filter->next->feature_end(&(coord_filter->vector_meta_copy), feat_id, coord_filter->next->handler_data); } int s2_coord_filter_geometry_start(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { coord_filter_t* coord_filter = (coord_filter_t*) handler_data; modify_meta_for_filter(coord_filter, meta); if (coord_filter->tessellator) { s2_tessellator_reset(coord_filter->tessellator); coord_filter->coord_id = 0; } return coord_filter->next->geometry_start(&(coord_filter->meta_copy), part_id, coord_filter->next->handler_data); } int s2_coord_filter_geometry_end(const wk_meta_t* meta, uint32_t part_id, void* handler_data) { coord_filter_t* coord_filter = (coord_filter_t*) handler_data; modify_meta_for_filter(coord_filter, meta); return coord_filter->next->geometry_end(&(coord_filter->meta_copy), part_id, coord_filter->next->handler_data); } int s2_coord_filter_ring_start(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { coord_filter_t* coord_filter = (coord_filter_t*) handler_data; modify_meta_for_filter(coord_filter, meta); if (coord_filter->tessellator) { s2_tessellator_reset(coord_filter->tessellator); coord_filter->coord_id = 0; } return coord_filter->next->ring_start(&(coord_filter->meta_copy), WK_SIZE_UNKNOWN, ring_id, coord_filter->next->handler_data); } int s2_coord_filter_ring_end(const wk_meta_t* meta, uint32_t size, uint32_t ring_id, void* handler_data) { coord_filter_t* coord_filter = (coord_filter_t*) handler_data; modify_meta_for_filter(coord_filter, meta); return coord_filter->next->ring_end(&(coord_filter->meta_copy), WK_SIZE_UNKNOWN, ring_id, coord_filter->next->handler_data); } int s2_coord_filter_error(const char* message, void* handler_data) { coord_filter_t* coord_filter = (coord_filter_t*) handler_data; return coord_filter->next->error(message, coord_filter->next->handler_data); } void s2_coord_filter_deinitialize(void* handler_data) { coord_filter_t* coord_filter = (coord_filter_t*) handler_data; coord_filter->next->deinitialize(coord_filter->next->handler_data); } void s2_coord_filter_finalize(void* handler_data) { coord_filter_t* coord_filter = (coord_filter_t*) handler_data; if (coord_filter != NULL) { s2_tessellator_destroy(coord_filter->tessellator); // coord_filter->projection is managed using an external pointer // coord_filter->next is managed using an external pointer free(coord_filter); } } // The main show for the coord filter! We define separate functions for the // projecting and unprojecting case as it's difficult to keep the code // clean otherwise. int s2_coord_filter_coord_unproject(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { coord_filter_t* coord_filter = (coord_filter_t*) handler_data; modify_meta_for_filter(coord_filter, meta); double coord_out[4]; // if we're not tessellating, skip adding the edge to the tessellator if ((coord_filter->tessellator == NULL) || (meta->geometry_type == WK_POINT)) { s2_projection_unproject(coord_filter->projection, coord, coord_out); return coord_filter->next->coord( &(coord_filter->meta_copy), coord_out, coord_id, coord_filter->next->handler_data ); } // Add this point to the tessellator s2_tessellator_add_r2_point(coord_filter->tessellator, coord); // If we have an edge in the tessellator, reguritate all the points // except the first one to the next handler. If we have zero points, // we send the it to the handler right away since we'll skip it when // the next point arrives. int size = s2_tessellator_s2_points_size(coord_filter->tessellator); int result; if (size < 2) { s2_projection_unproject(coord_filter->projection, coord, coord_out); result = coord_filter->next->coord( &(coord_filter->meta_copy), coord_out, coord_id, coord_filter->next->handler_data ); coord_filter->coord_id++; if (result != WK_CONTINUE) { return result; } } else { for (int i = 1; i < size; i++) { s2_tessellator_s2_point(coord_filter->tessellator, i, coord_out); result = coord_filter->next->coord( &(coord_filter->meta_copy), coord_out, coord_filter->coord_id, coord_filter->next->handler_data ); coord_filter->coord_id++; if (result != WK_CONTINUE) { return result; } } // Clear the tessellator and re-add this point to be ready for the next // point that forms an edge s2_tessellator_reset(coord_filter->tessellator); s2_tessellator_add_r2_point(coord_filter->tessellator, coord); } return WK_CONTINUE; } int s2_coord_filter_coord_project(const wk_meta_t* meta, const double* coord, uint32_t coord_id, void* handler_data) { coord_filter_t* coord_filter = (coord_filter_t*) handler_data; modify_meta_for_filter(coord_filter, meta); double coord_out[4]; if ((coord_filter->tessellator == NULL) || (meta->geometry_type == WK_POINT)) { s2_projection_project(coord_filter->projection, coord, coord_out); return coord_filter->next->coord( &(coord_filter->meta_copy), coord_out, coord_id, coord_filter->next->handler_data ); } s2_tessellator_add_s2_point(coord_filter->tessellator, coord); int size = s2_tessellator_r2_points_size(coord_filter->tessellator); int result; if (size < 2) { s2_projection_project(coord_filter->projection, coord, coord_out); result = coord_filter->next->coord( &(coord_filter->meta_copy), coord_out, coord_id, coord_filter->next->handler_data ); coord_filter->coord_id++; if (result != WK_CONTINUE) { return result; } } else { for (int i = 1; i < size; i++) { s2_tessellator_r2_point(coord_filter->tessellator, i, coord_out); result = coord_filter->next->coord( &(coord_filter->meta_copy), coord_out, coord_filter->coord_id, coord_filter->next->handler_data ); coord_filter->coord_id++; if (result != WK_CONTINUE) { return result; } } // Clear the tessellator and re-add this point to be ready for the next // point that forms an edge s2_tessellator_reset(coord_filter->tessellator); s2_tessellator_add_s2_point(coord_filter->tessellator, coord); } return WK_CONTINUE; } SEXP c_s2_coord_filter_new(SEXP handler_xptr, SEXP projection_xptr, SEXP unproject, SEXP tessellate_tol) { if (TYPEOF(handler_xptr) != EXTPTRSXP) { Rf_error("`handler` must be a wk_handler pointer"); } if (TYPEOF(projection_xptr) != EXTPTRSXP) { Rf_error("`projection` must be an external pointer"); } if (!IS_SIMPLE_SCALAR(unproject, LGLSXP)) { Rf_error("`unproject` must be TRUE or FALSE"); // # nocov } if (!IS_SIMPLE_SCALAR(tessellate_tol, REALSXP) || (REAL(tessellate_tol)[0] < 1e-9)) { Rf_error("`tessellate` must be a double() greter than 1e-9"); } wk_handler_t* handler = wk_handler_create(); handler->initialize = &s2_coord_filter_initialize; handler->vector_start = &s2_coord_filter_vector_start; handler->vector_end = &s2_coord_filter_vector_end; handler->feature_start = &s2_coord_filter_feature_start; handler->null_feature = &s2_coord_filter_feature_null; handler->feature_end = &s2_coord_filter_feature_end; handler->geometry_start = &s2_coord_filter_geometry_start; handler->geometry_end = &s2_coord_filter_geometry_end; handler->ring_start = &s2_coord_filter_ring_start; handler->ring_end = &s2_coord_filter_ring_end; handler->error = &s2_coord_filter_error; handler->deinitialize = &s2_coord_filter_deinitialize; handler->finalizer = &s2_coord_filter_finalize; coord_filter_t* coord_filter = (coord_filter_t*) malloc(sizeof(coord_filter_t)); if (coord_filter == NULL) { wk_handler_destroy(handler); // # nocov Rf_error("Failed to alloc handler data"); // # nocov } coord_filter->projection = (s2_projection_t*) R_ExternalPtrAddr(projection_xptr); if (REAL(tessellate_tol)[0] < R_PosInf) { coord_filter->tessellator = s2_tessellator_create(coord_filter->projection, REAL(tessellate_tol)[0]); } else { coord_filter->tessellator = NULL; } coord_filter->unproject = LOGICAL(unproject)[0]; if (coord_filter->unproject) { handler->coord = &s2_coord_filter_coord_unproject; } else { handler->coord = &s2_coord_filter_coord_project; } coord_filter->next = R_ExternalPtrAddr(handler_xptr); if (coord_filter->next->api_version != 1) { Rf_error("Can't run a wk_handler with api_version '%d'", coord_filter->next->api_version); // # nocov } handler->handler_data = coord_filter; // include the external pointers as a tag for this external pointer // which guarnatees that they will not be garbage collected until // this object is garbage collected return wk_handler_create_xptr(handler, handler_xptr, projection_xptr); } s2/src/tests/0000755000176200001440000000000013774601521012535 5ustar liggesuserss2/src/tests/soname.h0000644000176200001440000000026213774601521014170 0ustar liggesusers#include "openssl/opensslv.h" #define XSTR(x) STR(x) #define STR(x) #x #ifdef SHLIB_VERSION_NUMBER echo XSTR(SHLIB_VERSION_NUMBER) #else echo XSTR(OPENSSL_SHLIB_VERSION) #endif s2/src/s2-geography.cpp0000644000176200001440000000675314044456532014422 0ustar liggesusers #include "s2/s2latlng.h" #include "s2/s2polyline.h" #include "s2/s2polygon.h" #include "wk/wkb-reader.hpp" #include "wk/wkt-reader.hpp" #include "wk/wkb-writer.hpp" #include "wk/wkt-writer.hpp" #include "wk/geometry-formatter.hpp" #include "geography.h" #include "wk-geography.h" #include "point-geography.h" #include "polyline-geography.h" #include "polygon-geography.h" #include "geography-collection.h" #include using namespace Rcpp; // [[Rcpp::export]] List s2_geography_from_wkb(List wkb, bool oriented, bool check) { WKRawVectorListProvider provider(wkb); WKGeographyWriter writer(wkb.size()); writer.setOriented(oriented); writer.setCheck(check); WKBReader reader(provider); reader.setHandler(&writer); while (reader.hasNextFeature()) { checkUserInterrupt(); reader.iterateFeature(); } if (writer.problemId.size() > 0) { Environment s2NS = Environment::namespace_env("s2"); Function stopProblems = s2NS["stop_problems_create"]; stopProblems(writer.problemId, writer.problems); } return writer.output; } // [[Rcpp::export]] List s2_geography_from_wkt(CharacterVector wkt, bool oriented, bool check) { WKCharacterVectorProvider provider(wkt); WKGeographyWriter writer(wkt.size()); writer.setOriented(oriented); writer.setCheck(check); WKTReader reader(provider); reader.setHandler(&writer); while (reader.hasNextFeature()) { checkUserInterrupt(); reader.iterateFeature(); } if (writer.problemId.size() > 0) { Environment s2NS = Environment::namespace_env("s2"); Function stopProblems = s2NS["stop_problems_create"]; stopProblems(writer.problemId, writer.problems); } return writer.output; } // [[Rcpp::export]] List s2_geography_full(LogicalVector x) { // create single geography with full polygon std::unique_ptr l = absl::make_unique(S2Loop::kFull()); std::unique_ptr p = absl::make_unique(std::move(l)); Geography *pg = new PolygonGeography(std::move(p)); List ret(1); ret(0) = Rcpp::XPtr(pg); return ret; } // [[Rcpp::export]] CharacterVector s2_geography_to_wkt(List s2_geography, int precision, bool trim) { WKRcppSEXPProvider provider(s2_geography); WKGeographyReader reader(provider); WKCharacterVectorExporter exporter(reader.nFeatures()); exporter.setRoundingPrecision(precision); exporter.setTrim(trim); WKTWriter writer(exporter); reader.setHandler(&writer); while (reader.hasNextFeature()) { checkUserInterrupt(); reader.iterateFeature(); } return exporter.output; } // [[Rcpp::export]] List s2_geography_to_wkb(List s2_geography, int endian) { WKRcppSEXPProvider provider(s2_geography); WKGeographyReader reader(provider); WKRawVectorListExporter exporter(reader.nFeatures()); WKBWriter writer(exporter); writer.setEndian(endian); reader.setHandler(&writer); while (reader.hasNextFeature()) { checkUserInterrupt(); reader.iterateFeature(); } return exporter.output; } // [[Rcpp::export]] CharacterVector s2_geography_format(List s2_geography, int maxCoords, int precision, bool trim) { WKRcppSEXPProvider provider(s2_geography); WKGeographyReader reader(provider); WKCharacterVectorExporter exporter(s2_geography.size()); exporter.setRoundingPrecision(precision); exporter.setTrim(trim); WKGeometryFormatter formatter(exporter, maxCoords); reader.setHandler(&formatter); while (reader.hasNextFeature()) { checkUserInterrupt(); reader.iterateFeature(); } return exporter.output; } s2/src/cpp-compat.cpp0000644000176200001440000000176213774601521014150 0ustar liggesusers #include "cpp-compat.h" #include #include using namespace Rcpp; #include // va_ stuff void cpp_compat_printf(const char* fmt, ...) { va_list args; va_start(args, fmt); Rprintf(fmt, args); va_end(args); } void cpp_compat_abort() { throw std::runtime_error("abort() called"); } void cpp_compat_exit(int code) { throw std::runtime_error("exit() called"); } int cpp_compat_random() { // trying to match what random() would return // www.gnu.org/software/libc/manual/html_node/BSD-Random.html#BSD-Random // the RNG state is correctly managed for functions that use // Rcpp::export...other functions will require management of the RNGScope return unif_rand() * INT_MAX; } void cpp_compat_srandom(int seed) { // pretty sure this should not have any effect // it gets called on load here with the initiation // of the Random class in s2testing, so it can't // error out } std::ostream& cpp_compat_cerr = Rcerr; std::ostream& cpp_compat_cout = Rcout; s2/src/s2/0000755000176200001440000000000014124561362011715 5ustar liggesuserss2/src/s2/s2coords_internal.h0000644000176200001440000000521414122355625015523 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef S2_S2COORDS_INTERNAL_H_ #define S2_S2COORDS_INTERNAL_H_ // Author: ericv@google.com (Eric Veach) namespace S2 { namespace internal { // The canonical Hilbert traversal order looks like an inverted 'U': // the subcells are visited in the order (0,0), (0,1), (1,1), (1,0). // The following tables encode the traversal order for various // orientations of the Hilbert curve (axes swapped and/or directions // of the axes reversed). // Together these flags define a cell orientation. If 'kSwapMask' // is true, then canonical traversal order is flipped around the // diagonal (i.e. i and j are swapped with each other). If // 'kInvertMask' is true, then the traversal order is rotated by 180 // degrees (i.e. the bits of i and j are inverted, or equivalently, // the axis directions are reversed). int constexpr kSwapMask = 0x01; int constexpr kInvertMask = 0x02; // kIJtoPos[orientation][ij] -> pos // // Given a cell orientation and the (i,j)-index of a subcell (0=(0,0), // 1=(0,1), 2=(1,0), 3=(1,1)), return the order in which this subcell is // visited by the Hilbert curve (a position in the range [0..3]). extern const int kIJtoPos[4][4]; // kPosToIJ[orientation][pos] -> ij // // Return the (i,j) index of the subcell at the given position 'pos' in the // Hilbert curve traversal order with the given orientation. This is the // inverse of the previous table: // // kPosToIJ[r][kIJtoPos[r][ij]] == ij extern const int kPosToIJ[4][4]; // kPosToOrientation[pos] -> orientation_modifier // // Return a modifier indicating how the orientation of the child subcell // with the given traversal position [0..3] is related to the orientation // of the parent cell. The modifier should be XOR-ed with the parent // orientation to obtain the curve orientation in the child. extern const int kPosToOrientation[4]; // The U,V,W axes for each face. extern const double kFaceUVWAxes[6][3][3]; // The precomputed neighbors of each face (see GetUVWFace). extern const int kFaceUVWFaces[6][3][2]; } // namespace internal } // namespace S2 #endif // S2_S2COORDS_INTERNAL_H_ s2/src/s2/s2shapeutil_shape_edge_id.h0000644000176200001440000000570514122355625017161 0ustar liggesusers// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_S2SHAPEUTIL_SHAPE_EDGE_ID_H_ #define S2_S2SHAPEUTIL_SHAPE_EDGE_ID_H_ #include #include "s2/base/integral_types.h" namespace s2shapeutil { // ShapeEdgeId is a unique identifier for an edge within an S2ShapeIndex, // consisting of a (shape_id, edge_id) pair. It is similar to // std::pair except that it has named fields. // It should be passed and returned by value. struct ShapeEdgeId { public: int32 shape_id, edge_id; ShapeEdgeId() : shape_id(-1), edge_id(-1) {} ShapeEdgeId(int32 _shape_id, int32 _edge_id); bool operator==(ShapeEdgeId other) const; bool operator!=(ShapeEdgeId other) const; bool operator<(ShapeEdgeId other) const; bool operator>(ShapeEdgeId other) const; bool operator<=(ShapeEdgeId other) const; bool operator>=(ShapeEdgeId other) const; }; std::ostream& operator<<(std::ostream& os, ShapeEdgeId id); // Hasher for ShapeEdgeId. // Example use: std::unordered_set. struct ShapeEdgeIdHash; ////////////////// Implementation details follow //////////////////// inline ShapeEdgeId::ShapeEdgeId(int32 _shape_id, int32 _edge_id) : shape_id(_shape_id), edge_id(_edge_id) { } inline bool ShapeEdgeId::operator==(ShapeEdgeId other) const { return shape_id == other.shape_id && edge_id == other.edge_id; } inline bool ShapeEdgeId::operator!=(ShapeEdgeId other) const { return !(*this == other); } inline bool ShapeEdgeId::operator<(ShapeEdgeId other) const { if (shape_id < other.shape_id) return true; if (other.shape_id < shape_id) return false; return edge_id < other.edge_id; } inline bool ShapeEdgeId::operator>(ShapeEdgeId other) const { return other < *this; } inline bool ShapeEdgeId::operator<=(ShapeEdgeId other) const { return !(other < *this); } inline bool ShapeEdgeId::operator>=(ShapeEdgeId other) const { return !(*this < other); } inline std::ostream& operator<<(std::ostream& os, ShapeEdgeId id) { return os << id.shape_id << ":" << id.edge_id; } struct ShapeEdgeIdHash { size_t operator()(ShapeEdgeId id) const { // The following preserves all bits even when edge_id < 0. return std::hash()((static_cast(id.shape_id) << 32) | static_cast(id.edge_id)); } }; } // namespace s2shapeutil #endif // S2_S2SHAPEUTIL_SHAPE_EDGE_ID_H_ s2/src/s2/s2loop_measures.h0000644000176200001440000003122314122355625015212 0ustar liggesusers// Copyright 2018 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) // // Defines various angle and area measures for loops on the sphere. These are // low-level methods that work directly with arrays of S2Points. They are // used to implement the methods in s2shapeindex_measures.h, // s2shape_measures.h, s2loop.h, and s2polygon.h. // // See s2polyline_measures.h, s2edge_distances.h, and s2measures.h for // additional low-level methods. #ifndef S2_S2LOOP_MEASURES_H_ #define S2_S2LOOP_MEASURES_H_ #include #include #include #include "s2/s1angle.h" #include "s2/s2point.h" #include "s2/s2point_span.h" #include "s2/s2pointutil.h" namespace S2 { // Returns the perimeter of the loop. S1Angle GetPerimeter(S2PointLoopSpan loop); // Returns the area of the loop interior, i.e. the region on the left side of // the loop. The result is between 0 and 4*Pi steradians. The implementation // ensures that nearly-degenerate clockwise loops have areas close to zero, // while nearly-degenerate counter-clockwise loops have areas close to 4*Pi. double GetArea(S2PointLoopSpan loop); // Like GetArea(), except that this method is faster and has more error. The // result is between 0 and 4*Pi steradians. The maximum error is 2.22e-15 // steradians per loop vertex, which works out to about 0.09 square meters per // vertex on the Earth's surface. For example, a loop with 100 vertices has a // maximum error of about 9 square meters. (The actual error is typically // much smaller than this.) The error bound can be computed using // GetCurvatureMaxError(), which returns the maximum error in steradians. double GetApproxArea(S2PointLoopSpan loop); // Returns either the positive area of the region on the left side of the // loop, or the negative area of the region on the right side of the loop, // whichever is smaller in magnitude. The result is between -2*Pi and 2*Pi // steradians. This method is used to accurately compute the area of polygons // consisting of multiple loops. // // The following cases are handled specially: // // - Counter-clockwise loops are guaranteed to have positive area, and // clockwise loops are guaranteed to have negative area. // // - Degenerate loops (consisting of an isolated vertex or composed entirely // of sibling edge pairs) have an area of exactly zero. // // - The full loop (containing all points, and represented as a loop with no // vertices) has a negative area with the minimum possible magnitude. // (This is the "signed equivalent" of having an area of 4*Pi.) double GetSignedArea(S2PointLoopSpan loop); // Returns the geodesic curvature of the loop, defined as the sum of the turn // angles at each vertex (see S2::TurnAngle). The result is positive if the // loop is counter-clockwise, negative if the loop is clockwise, and zero if // the loop is a great circle. The geodesic curvature is equal to 2*Pi minus // the area of the loop. // // The following cases are handled specially: // // - Degenerate loops (consisting of an isolated vertex or composed entirely // of sibling edge pairs) have a curvature of 2*Pi exactly. // // - The full loop (containing all points, and represented as a loop with no // vertices) has a curvature of -2*Pi exactly. // // - All other loops have a non-zero curvature in the range (-2*Pi, 2*Pi). // For any such loop, reversing the order of the vertices is guaranteed to // negate the curvature. This property can be used to define a unique // normalized orientation for every loop. double GetCurvature(S2PointLoopSpan loop); // Returns the maximum error in GetCurvature() for the given loop. This value // is also an upper bound on the error in GetArea(), GetSignedArea(), and // GetApproxArea(). double GetCurvatureMaxError(S2PointLoopSpan loop); // Returns the true centroid of the loop multiplied by the area of the loop // (see s2centroids.h for details on centroids). The result is not unit // length, so you may want to normalize it. Also note that in general, the // centroid may not be contained by the loop. // // The result is scaled by the loop area for two reasons: (1) it is cheaper to // compute this way, and (2) it makes it easier to compute the centroid of // more complicated shapes (by splitting them into disjoint regions and adding // their centroids). S2Point GetCentroid(S2PointLoopSpan loop); // Returns true if the loop area is at most 2*Pi. (A small amount of error is // allowed in order to ensure that loops representing an entire hemisphere are // always considered normalized.) // // Degenerate loops are handled consistently with s2pred::Sign(), i.e., if a // loop can be expressed as the union of degenerate or nearly-degenerate // counter-clockwise triangles then this method will return true. bool IsNormalized(S2PointLoopSpan loop); // LoopOrder represents a cyclic ordering of the loop vertices, starting at // the index "first" and proceeding in direction "dir" (either +1 or -1). // "first" and "dir" must be chosen such that (first, ..., first + n * dir) // are all in the range [0, 2*n-1] as required by S2PointLoopSpan::operator[]. struct LoopOrder { LoopOrder(int _first, int _dir) : first(_first), dir(_dir) {} int first; int dir; }; bool operator==(LoopOrder x, LoopOrder y); std::ostream& operator<<(std::ostream& os, LoopOrder order); // Returns an index "first" and a direction "dir" such that the vertex // sequence (first, first + dir, ..., first + (n - 1) * dir) does not change // when the loop vertex order is rotated or reversed. This allows the loop // vertices to be traversed in a canonical order. LoopOrder GetCanonicalLoopOrder(S2PointLoopSpan loop); // Returns the oriented surface integral of some quantity f(x) over the loop // interior, given a function f_tri(A,B,C) that returns the corresponding // integral over the spherical triangle ABC. Here "oriented surface integral" // means: // // (1) f_tri(A,B,C) must be the integral of f if ABC is counterclockwise, // and the integral of -f if ABC is clockwise. // // (2) The result of this function is *either* the integral of f over the // loop interior, or the integral of (-f) over the loop exterior. // // Note that there are at least two common situations where property (2) above // is not a limitation: // // - If the integral of f over the entire sphere is zero, then it doesn't // matter which case is returned because they are always equal. // // - If f is non-negative, then it is easy to detect when the integral over // the loop exterior has been returned, and the integral over the loop // interior can be obtained by adding the integral of f over the entire // unit sphere (a constant) to the result. // // REQUIRES: The default constructor for T must initialize the value to zero. // (This is true for built-in types such as "double".) template T GetSurfaceIntegral(S2PointLoopSpan loop, T f_tri(const S2Point&, const S2Point&, const S2Point&)); // Returns a new loop obtained by removing all degeneracies from "loop". In // particular, the result will not contain any adjacent duplicate vertices or // sibling edge pairs, i.e. vertex sequences of the form (A, A) or (A, B, A). // // "new_vertices" represents storage where new loop vertices may be written. // Note that the S2PointLoopSpan result may be a subsequence of either "loop" // or "new_vertices", and therefore "new_vertices" must persist until the // result of this method is no longer needed. S2PointLoopSpan PruneDegeneracies(S2PointLoopSpan loop, std::vector* new_vertices); //////////////////// Implementation details follow //////////////////////// inline bool operator==(LoopOrder x, LoopOrder y) { return x.first == y.first && x.dir == y.dir; } template T GetSurfaceIntegral(S2PointLoopSpan loop, T f_tri(const S2Point&, const S2Point&, const S2Point&)) { // We sum "f_tri" over a collection T of oriented triangles, possibly // overlapping. Let the sign of a triangle be +1 if it is CCW and -1 // otherwise, and let the sign of a point "x" be the sum of the signs of the // triangles containing "x". Then the collection of triangles T is chosen // such that either: // // (1) Each point in the loop interior has sign +1, and sign 0 otherwise; or // (2) Each point in the loop exterior has sign -1, and sign 0 otherwise. // // The triangles basically consist of a "fan" from vertex 0 to every loop // edge that does not include vertex 0. These triangles will always satisfy // either (1) or (2). However, what makes this a bit tricky is that // spherical edges become numerically unstable as their length approaches // 180 degrees. Of course there is not much we can do if the loop itself // contains such edges, but we would like to make sure that all the triangle // edges under our control (i.e., the non-loop edges) are stable. For // example, consider a loop around the equator consisting of four equally // spaced points. This is a well-defined loop, but we cannot just split it // into two triangles by connecting vertex 0 to vertex 2. // // We handle this type of situation by moving the origin of the triangle fan // whenever we are about to create an unstable edge. We choose a new // location for the origin such that all relevant edges are stable. We also // create extra triangles with the appropriate orientation so that the sum // of the triangle signs is still correct at every point. // The maximum length of an edge for it to be considered numerically stable. // The exact value is fairly arbitrary since it depends on the stability of // the "f_tri" function. The value below is quite conservative but could be // reduced further if desired. static const double kMaxLength = M_PI - 1e-5; // The default constructor for T must initialize the value to zero. // (This is true for built-in types such as "double".) T sum = T(); if (loop.size() < 3) return sum; S2Point origin = loop[0]; for (int i = 1; i + 1 < loop.size(); ++i) { // Let V_i be loop[i], let O be the current origin, and let length(A,B) // be the length of edge (A,B). At the start of each loop iteration, the // "leading edge" of the triangle fan is (O,V_i), and we want to extend // the triangle fan so that the leading edge is (O,V_i+1). // // Invariants: // 1. length(O,V_i) < kMaxLength for all (i > 1). // 2. Either O == V_0, or O is approximately perpendicular to V_0. // 3. "sum" is the oriented integral of f over the area defined by // (O, V_0, V_1, ..., V_i). S2_DCHECK(i == 1 || origin.Angle(loop[i]) < kMaxLength); S2_DCHECK(origin == loop[0] || std::fabs(origin.DotProd(loop[0])) < 1e-15); if (loop[i + 1].Angle(origin) > kMaxLength) { // We are about to create an unstable edge, so choose a new origin O' // for the triangle fan. S2Point old_origin = origin; if (origin == loop[0]) { // The following point is well-separated from V_i and V_0 (and // therefore V_i+1 as well). origin = S2::RobustCrossProd(loop[0], loop[i]).Normalize(); } else if (loop[i].Angle(loop[0]) < kMaxLength) { // All edges of the triangle (O, V_0, V_i) are stable, so we can // revert to using V_0 as the origin. origin = loop[0]; } else { // (O, V_i+1) and (V_0, V_i) are antipodal pairs, and O and V_0 are // perpendicular. Therefore V_0.CrossProd(O) is approximately // perpendicular to all of {O, V_0, V_i, V_i+1}, and we can choose // this point O' as the new origin. origin = loop[0].CrossProd(old_origin); // Advance the edge (V_0,O) to (V_0,O'). sum += f_tri(loop[0], old_origin, origin); } // Advance the edge (O,V_i) to (O',V_i). sum += f_tri(old_origin, loop[i], origin); } // Advance the edge (O,V_i) to (O,V_i+1). sum += f_tri(origin, loop[i], loop[i+1]); } // If the origin is not V_0, we need to sum one more triangle. if (origin != loop[0]) { // Advance the edge (O,V_n-1) to (O,V_0). sum += f_tri(origin, loop[loop.size() - 1], loop[0]); } return sum; } } // namespace S2 #endif // S2_S2LOOP_MEASURES_H_ s2/src/s2/s1interval.cc0000644000176200001440000002400513774601521014317 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s1interval.h" #include #include #include #include "s2/base/logging.h" using std::fabs; using std::max; S1Interval S1Interval::FromPoint(double p) { if (p == -M_PI) p = M_PI; return S1Interval(p, p, ARGS_CHECKED); } double S1Interval::GetCenter() const { double center = 0.5 * (lo() + hi()); if (!is_inverted()) return center; // Return the center in the range (-Pi, Pi]. return (center <= 0) ? (center + M_PI) : (center - M_PI); } double S1Interval::GetLength() const { double length = hi() - lo(); if (length >= 0) return length; length += 2 * M_PI; // Empty intervals have a negative length. return (length > 0) ? length : -1; } S1Interval S1Interval::Complement() const { if (lo() == hi()) return Full(); // Singleton. return S1Interval(hi(), lo(), ARGS_CHECKED); // Handles empty and full. } double S1Interval::GetComplementCenter() const { if (lo() != hi()) { return Complement().GetCenter(); } else { // Singleton. return (hi() <= 0) ? (hi() + M_PI) : (hi() - M_PI); } } bool S1Interval::FastContains(double p) const { if (is_inverted()) { return (p >= lo() || p <= hi()) && !is_empty(); } else { return p >= lo() && p <= hi(); } } bool S1Interval::Contains(double p) const { // Works for empty, full, and singleton intervals. S2_DCHECK_LE(fabs(p), M_PI); if (p == -M_PI) p = M_PI; return FastContains(p); } bool S1Interval::InteriorContains(double p) const { // Works for empty, full, and singleton intervals. S2_DCHECK_LE(fabs(p), M_PI); if (p == -M_PI) p = M_PI; if (is_inverted()) { return p > lo() || p < hi(); } else { return (p > lo() && p < hi()) || is_full(); } } bool S1Interval::Contains(const S1Interval& y) const { // It might be helpful to compare the structure of these tests to // the simpler Contains(double) method above. if (is_inverted()) { if (y.is_inverted()) return y.lo() >= lo() && y.hi() <= hi(); return (y.lo() >= lo() || y.hi() <= hi()) && !is_empty(); } else { if (y.is_inverted()) return is_full() || y.is_empty(); return y.lo() >= lo() && y.hi() <= hi(); } } bool S1Interval::InteriorContains(const S1Interval& y) const { if (is_inverted()) { if (!y.is_inverted()) return y.lo() > lo() || y.hi() < hi(); return (y.lo() > lo() && y.hi() < hi()) || y.is_empty(); } else { if (y.is_inverted()) return is_full() || y.is_empty(); return (y.lo() > lo() && y.hi() < hi()) || is_full(); } } bool S1Interval::Intersects(const S1Interval& y) const { if (is_empty() || y.is_empty()) return false; if (is_inverted()) { // Every non-empty inverted interval contains Pi. return y.is_inverted() || y.lo() <= hi() || y.hi() >= lo(); } else { if (y.is_inverted()) return y.lo() <= hi() || y.hi() >= lo(); return y.lo() <= hi() && y.hi() >= lo(); } } bool S1Interval::InteriorIntersects(const S1Interval& y) const { if (is_empty() || y.is_empty() || lo() == hi()) return false; if (is_inverted()) { return y.is_inverted() || y.lo() < hi() || y.hi() > lo(); } else { if (y.is_inverted()) return y.lo() < hi() || y.hi() > lo(); return (y.lo() < hi() && y.hi() > lo()) || is_full(); } } inline static double PositiveDistance(double a, double b) { // Compute the distance from "a" to "b" in the range [0, 2*Pi). // This is equivalent to (remainder(b - a - M_PI, 2 * M_PI) + M_PI), // except that it is more numerically stable (it does not lose // precision for very small positive distances). double d = b - a; if (d >= 0) return d; // We want to ensure that if b == Pi and a == (-Pi + eps), // the return result is approximately 2*Pi and not zero. return (b + M_PI) - (a - M_PI); } double S1Interval::GetDirectedHausdorffDistance(const S1Interval& y) const { if (y.Contains(*this)) return 0.0; // this includes the case *this is empty if (y.is_empty()) return M_PI; // maximum possible distance on S1 double y_complement_center = y.GetComplementCenter(); if (Contains(y_complement_center)) { return PositiveDistance(y.hi(), y_complement_center); } else { // The Hausdorff distance is realized by either two hi() endpoints or two // lo() endpoints, whichever is farther apart. double hi_hi = S1Interval(y.hi(), y_complement_center).Contains(hi()) ? PositiveDistance(y.hi(), hi()) : 0; double lo_lo = S1Interval(y_complement_center, y.lo()).Contains(lo()) ? PositiveDistance(lo(), y.lo()) : 0; S2_DCHECK(hi_hi > 0 || lo_lo > 0); return max(hi_hi, lo_lo); } } void S1Interval::AddPoint(double p) { S2_DCHECK_LE(fabs(p), M_PI); if (p == -M_PI) p = M_PI; if (FastContains(p)) return; if (is_empty()) { set_hi(p); set_lo(p); } else { // Compute distance from p to each endpoint. double dlo = PositiveDistance(p, lo()); double dhi = PositiveDistance(hi(), p); if (dlo < dhi) { set_lo(p); } else { set_hi(p); } // Adding a point can never turn a non-full interval into a full one. } } double S1Interval::Project(double p) const { S2_DCHECK(!is_empty()); S2_DCHECK_LE(fabs(p), M_PI); if (p == -M_PI) p = M_PI; if (FastContains(p)) return p; // Compute distance from p to each endpoint. double dlo = PositiveDistance(p, lo()); double dhi = PositiveDistance(hi(), p); return (dlo < dhi) ? lo() : hi(); } S1Interval S1Interval::FromPointPair(double p1, double p2) { S2_DCHECK_LE(fabs(p1), M_PI); S2_DCHECK_LE(fabs(p2), M_PI); if (p1 == -M_PI) p1 = M_PI; if (p2 == -M_PI) p2 = M_PI; if (PositiveDistance(p1, p2) <= M_PI) { return S1Interval(p1, p2, ARGS_CHECKED); } else { return S1Interval(p2, p1, ARGS_CHECKED); } } S1Interval S1Interval::Expanded(double margin) const { if (margin >= 0) { if (is_empty()) return *this; // Check whether this interval will be full after expansion, allowing // for a 1-bit rounding error when computing each endpoint. if (GetLength() + 2 * margin + 2 * DBL_EPSILON >= 2 * M_PI) return Full(); } else { if (is_full()) return *this; // Check whether this interval will be empty after expansion, allowing // for a 1-bit rounding error when computing each endpoint. if (GetLength() + 2 * margin - 2 * DBL_EPSILON <= 0) return Empty(); } S1Interval result(remainder(lo() - margin, 2*M_PI), remainder(hi() + margin, 2*M_PI)); if (result.lo() <= -M_PI) result.set_lo(M_PI); return result; } S1Interval S1Interval::Union(const S1Interval& y) const { // The y.is_full() case is handled correctly in all cases by the code // below, but can follow three separate code paths depending on whether // this interval is inverted, is non-inverted but contains Pi, or neither. if (y.is_empty()) return *this; if (FastContains(y.lo())) { if (FastContains(y.hi())) { // Either this interval contains y, or the union of the two // intervals is the Full() interval. if (Contains(y)) return *this; // is_full() code path return Full(); } return S1Interval(lo(), y.hi(), ARGS_CHECKED); } if (FastContains(y.hi())) return S1Interval(y.lo(), hi(), ARGS_CHECKED); // This interval contains neither endpoint of y. This means that either y // contains all of this interval, or the two intervals are disjoint. if (is_empty() || y.FastContains(lo())) return y; // Check which pair of endpoints are closer together. double dlo = PositiveDistance(y.hi(), lo()); double dhi = PositiveDistance(hi(), y.lo()); if (dlo < dhi) { return S1Interval(y.lo(), hi(), ARGS_CHECKED); } else { return S1Interval(lo(), y.hi(), ARGS_CHECKED); } } S1Interval S1Interval::Intersection(const S1Interval& y) const { // The y.is_full() case is handled correctly in all cases by the code // below, but can follow three separate code paths depending on whether // this interval is inverted, is non-inverted but contains Pi, or neither. if (y.is_empty()) return Empty(); if (FastContains(y.lo())) { if (FastContains(y.hi())) { // Either this interval contains y, or the region of intersection // consists of two disjoint subintervals. In either case, we want // to return the shorter of the two original intervals. if (y.GetLength() < GetLength()) return y; // is_full() code path return *this; } return S1Interval(y.lo(), hi(), ARGS_CHECKED); } if (FastContains(y.hi())) return S1Interval(lo(), y.hi(), ARGS_CHECKED); // This interval contains neither endpoint of y. This means that either y // contains all of this interval, or the two intervals are disjoint. if (y.FastContains(lo())) return *this; // is_empty() okay here S2_DCHECK(!Intersects(y)); return Empty(); } bool S1Interval::ApproxEquals(const S1Interval& y, double max_error) const { // Full and empty intervals require special cases because the "endpoints" // are considered to be positioned arbitrarily. if (is_empty()) return y.GetLength() <= 2 * max_error; if (y.is_empty()) return GetLength() <= 2 * max_error; if (is_full()) return y.GetLength() >= 2 * (M_PI - max_error); if (y.is_full()) return GetLength() >= 2 * (M_PI - max_error); // The purpose of the last test below is to verify that moving the endpoints // does not invert the interval, e.g. [-1e20, 1e20] vs. [1e20, -1e20]. return (fabs(remainder(y.lo() - lo(), 2 * M_PI)) <= max_error && fabs(remainder(y.hi() - hi(), 2 * M_PI)) <= max_error && fabs(GetLength() - y.GetLength()) <= 2 * max_error); } s2/src/s2/s2metrics.h0000644000176200001440000002101214122355625013776 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) // // The following are various constants that describe the shapes and sizes of // S2Cells (see s2coords.h and s2cell_id.h). They are useful for deciding // which cell level to use in order to satisfy a given condition (e.g. that // cell vertices must be no further than "x" apart). All of the raw constants // are differential quantities; you can use the GetValue(level) method to // compute the corresponding length or area on the unit sphere for cells at a // given level. The minimum and maximum bounds are valid for cells at all // levels, but they may be somewhat conservative for very large cells // (e.g. face cells). #ifndef S2_S2METRICS_H_ #define S2_S2METRICS_H_ #include #include #include "s2/s2coords.h" #include "s2/util/math/mathutil.h" namespace S2 { // Defines a cell metric of the given dimension (1 == length, 2 == area). template class Metric { public: explicit constexpr Metric(double deriv) : deriv_(deriv) {} // The "deriv" value of a metric is a derivative, and must be multiplied by // a length or area in (s,t)-space to get a useful value. double deriv() const { return deriv_; } // Return the value of a metric for cells at the given level. The value is // either a length or an area on the unit sphere, depending on the // particular metric. double GetValue(int level) const { return ldexp(deriv_, - dim * level); } // Return the level at which the metric has approximately the given // value. For example, S2::kAvgEdge.GetClosestLevel(0.1) returns the // level at which the average cell edge length is approximately 0.1. // The return value is always a valid level. int GetClosestLevel(double value) const; // Return the minimum level such that the metric is at most the given // value, or S2CellId::kMaxLevel if there is no such level. For example, // S2::kMaxDiag.GetLevelForMaxValue(0.1) returns the minimum level such // that all cell diagonal lengths are 0.1 or smaller. The return value // is always a valid level. int GetLevelForMaxValue(double value) const; // Return the maximum level such that the metric is at least the given // value, or zero if there is no such level. For example, // S2::kMinWidth.GetLevelForMinValue(0.1) returns the maximum level such // that all cells have a minimum width of 0.1 or larger. The return value // is always a valid level. int GetLevelForMinValue(double value) const; private: const double deriv_; Metric(const Metric&) = delete; void operator=(const Metric&) = delete; }; using LengthMetric = Metric<1>; using AreaMetric = Metric<2>; // Each cell is bounded by four planes passing through its four edges and // the center of the sphere. These metrics relate to the angle between each // pair of opposite bounding planes, or equivalently, between the planes // corresponding to two different s-values or two different t-values. For // example, the maximum angle between opposite bounding planes for a cell at // level k is kMaxAngleSpan.GetValue(k), and the average angle span for all // cells at level k is approximately kAvgAngleSpan.GetValue(k). extern const LengthMetric kMinAngleSpan; extern const LengthMetric kMaxAngleSpan; extern const LengthMetric kAvgAngleSpan; // The width of geometric figure is defined as the distance between two // parallel bounding lines in a given direction. For cells, the minimum // width is always attained between two opposite edges, and the maximum // width is attained between two opposite vertices. However, for our // purposes we redefine the width of a cell as the perpendicular distance // between a pair of opposite edges. A cell therefore has two widths, one // in each direction. The minimum width according to this definition agrees // with the classic geometric one, but the maximum width is different. (The // maximum geometric width corresponds to kMaxDiag defined below.) // // For a cell at level k, the distance between opposite edges is at least // kMinWidth.GetValue(k) and at most kMaxWidth.GetValue(k). The average // width in both directions for all cells at level k is approximately // kAvgWidth.GetValue(k). // // The width is useful for bounding the minimum or maximum distance from a // point on one edge of a cell to the closest point on the opposite edge. // For example, this is useful when "growing" regions by a fixed distance. // // Note that because S2Cells are not usually rectangles, the minimum width of // a cell is generally smaller than its minimum edge length. (The interior // angles of an S2Cell range from 60 to 120 degrees.) extern const LengthMetric kMinWidth; extern const LengthMetric kMaxWidth; extern const LengthMetric kAvgWidth; // The minimum edge length of any cell at level k is at least // kMinEdge.GetValue(k), and the maximum is at most kMaxEdge.GetValue(k). // The average edge length is approximately kAvgEdge.GetValue(k). // // The edge length metrics can also be used to bound the minimum, maximum, // or average distance from the center of one cell to the center of one of // its edge neighbors. In particular, it can be used to bound the distance // between adjacent cell centers along the space-filling Hilbert curve for // cells at any given level. extern const LengthMetric kMinEdge; extern const LengthMetric kMaxEdge; extern const LengthMetric kAvgEdge; // The minimum diagonal length of any cell at level k is at least // kMinDiag.GetValue(k), and the maximum is at most kMaxDiag.GetValue(k). // The average diagonal length is approximately kAvgDiag.GetValue(k). // // The maximum diagonal also happens to be the maximum diameter of any cell, // and also the maximum geometric width (see the discussion above). So for // example, the distance from an arbitrary point to the closest cell center // at a given level is at most half the maximum diagonal length. extern const LengthMetric kMinDiag; extern const LengthMetric kMaxDiag; extern const LengthMetric kAvgDiag; // The minimum area of any cell at level k is at least kMinArea.GetValue(k), // and the maximum is at most kMaxArea.GetValue(k). The average area of all // cells at level k is exactly kAvgArea.GetValue(k). extern const AreaMetric kMinArea; extern const AreaMetric kMaxArea; extern const AreaMetric kAvgArea; // This is the maximum edge aspect ratio over all cells at any level, where // the edge aspect ratio of a cell is defined as the ratio of its longest // edge length to its shortest edge length. extern const double kMaxEdgeAspect; // This is the maximum diagonal aspect ratio over all cells at any level, // where the diagonal aspect ratio of a cell is defined as the ratio of its // longest diagonal length to its shortest diagonal length. extern const double kMaxDiagAspect; ////////////////// Implementation details follow //////////////////// template int S2::Metric::GetLevelForMaxValue(double value) const { if (value <= 0) return S2::kMaxCellLevel; // This code is equivalent to computing a floating-point "level" // value and rounding up. ilogb() returns the exponent corresponding to a // fraction in the range [1,2). int level = ilogb(value / deriv_); level = std::max(0, std::min(S2::kMaxCellLevel, -(level >> (dim - 1)))); S2_DCHECK(level == S2::kMaxCellLevel || GetValue(level) <= value); S2_DCHECK(level == 0 || GetValue(level - 1) > value); return level; } template int S2::Metric::GetLevelForMinValue(double value) const { if (value <= 0) return S2::kMaxCellLevel; // This code is equivalent to computing a floating-point "level" // value and rounding down. int level = ilogb(deriv_ / value); level = std::max(0, std::min(S2::kMaxCellLevel, level >> (dim - 1))); S2_DCHECK(level == 0 || GetValue(level) >= value); S2_DCHECK(level == kMaxCellLevel || GetValue(level + 1) < value); return level; } template int Metric::GetClosestLevel(double value) const { return GetLevelForMaxValue((dim == 1 ? M_SQRT2 : 2) * value); } } // namespace S2 #endif // S2_S2METRICS_H_ s2/src/s2/s2region_coverer.cc0000644000176200001440000004506614122355625015515 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2region_coverer.h" #include #include #include #include #include #include #include #include #include "s2/base/logging.h" #include "s2/s1angle.h" #include "s2/s2cap.h" #include "s2/s2cell_union.h" #include "s2/s2metrics.h" #include "s2/s2region.h" #include "absl/base/casts.h" using std::is_sorted; using std::max; using std::min; using std::unordered_set; using std::vector; // Define storage for header file constants (the values are not needed here). constexpr int S2RegionCoverer::Options::kDefaultMaxCells; S2RegionCoverer::S2RegionCoverer(const S2RegionCoverer::Options& options) : options_(options) { S2_DCHECK_LE(options.min_level(), options.max_level()); } // Defaulted in the implementation to prevent inline bloat. S2RegionCoverer::S2RegionCoverer() = default; S2RegionCoverer::~S2RegionCoverer() = default; S2RegionCoverer::S2RegionCoverer(S2RegionCoverer&&) = default; S2RegionCoverer& S2RegionCoverer::operator=(S2RegionCoverer&&) = default; void S2RegionCoverer::Options::set_max_cells(int max_cells) { max_cells_ = max_cells; } void S2RegionCoverer::Options::set_min_level(int min_level) { S2_DCHECK_GE(min_level, 0); S2_DCHECK_LE(min_level, S2CellId::kMaxLevel); // min_level() <= max_level() is checked by S2RegionCoverer. min_level_ = max(0, min(S2CellId::kMaxLevel, min_level)); } void S2RegionCoverer::Options::set_max_level(int max_level) { S2_DCHECK_GE(max_level, 0); S2_DCHECK_LE(max_level, S2CellId::kMaxLevel); // min_level() <= max_level() is checked by S2RegionCoverer. max_level_ = max(0, min(S2CellId::kMaxLevel, max_level)); } void S2RegionCoverer::Options::set_fixed_level(int level) { set_min_level(level); set_max_level(level); } void S2RegionCoverer::Options::set_level_mod(int level_mod) { S2_DCHECK_GE(level_mod, 1); S2_DCHECK_LE(level_mod, 3); level_mod_ = max(1, min(3, level_mod)); } int S2RegionCoverer::Options::true_max_level() const { if (level_mod_ == 1) return max_level_; return max_level_ - (max_level_ - min_level_) % level_mod_; } S2RegionCoverer::Candidate* S2RegionCoverer::NewCandidate(const S2Cell& cell) { if (!region_->MayIntersect(cell)) return nullptr; bool is_terminal = false; if (cell.level() >= options_.min_level()) { if (interior_covering_) { if (region_->Contains(cell)) { is_terminal = true; } else if (cell.level() + options_.level_mod() > options_.max_level()) { return nullptr; } } else { if (cell.level() + options_.level_mod() > options_.max_level() || region_->Contains(cell)) { is_terminal = true; } } } ++candidates_created_counter_; const std::size_t max_children = is_terminal ? 0 : 1 << max_children_shift(); return new (max_children) Candidate(cell, max_children); } void S2RegionCoverer::DeleteCandidate(Candidate* candidate, bool delete_children) { if (delete_children) { for (int i = 0; i < candidate->num_children; ++i) DeleteCandidate(candidate->children[i], true); } delete candidate; } int S2RegionCoverer::ExpandChildren(Candidate* candidate, const S2Cell& cell, int num_levels) { num_levels--; S2Cell child_cells[4]; cell.Subdivide(child_cells); int num_terminals = 0; for (int i = 0; i < 4; ++i) { if (num_levels > 0) { if (region_->MayIntersect(child_cells[i])) { num_terminals += ExpandChildren(candidate, child_cells[i], num_levels); } continue; } Candidate* child = NewCandidate(child_cells[i]); if (child) { candidate->children[candidate->num_children++] = child; if (child->is_terminal) ++num_terminals; } } return num_terminals; } void S2RegionCoverer::AddCandidate(Candidate* candidate) { if (candidate == nullptr) return; if (candidate->is_terminal) { result_.push_back(candidate->cell.id()); DeleteCandidate(candidate, true); return; } S2_DCHECK_EQ(0, candidate->num_children); // Expand one level at a time until we hit min_level() to ensure that we // don't skip over it. int num_levels = ((candidate->cell.level() < options_.min_level()) ? 1 : options_.level_mod()); int num_terminals = ExpandChildren(candidate, candidate->cell, num_levels); if (candidate->num_children == 0) { DeleteCandidate(candidate, false); } else if (!interior_covering_ && num_terminals == 1 << max_children_shift() && candidate->cell.level() >= options_.min_level()) { // Optimization: add the parent cell rather than all of its children. // We can't do this for interior coverings, since the children just // intersect the region, but may not be contained by it - we need to // subdivide them further. candidate->is_terminal = true; AddCandidate(candidate); } else { // We negate the priority so that smaller absolute priorities are returned // first. The heuristic is designed to refine the largest cells first, // since those are where we have the largest potential gain. Among cells // of the same size, we prefer the cells with the fewest children. // Finally, among cells with equal numbers of children we prefer those // with the smallest number of children that cannot be refined further. int priority = -((((candidate->cell.level() << max_children_shift()) + candidate->num_children) << max_children_shift()) + num_terminals); pq_.push(std::make_pair(priority, candidate)); S2_VLOG(2) << "Push: " << candidate->cell.id() << " (" << priority << ") "; } } inline int S2RegionCoverer::AdjustLevel(int level) const { if (options_.level_mod() > 1 && level > options_.min_level()) { level -= (level - options_.min_level()) % options_.level_mod(); } return level; } void S2RegionCoverer::AdjustCellLevels(vector* cells) const { S2_DCHECK(is_sorted(cells->begin(), cells->end())); if (options_.level_mod() == 1) return; int out = 0; for (S2CellId id : *cells) { int level = id.level(); int new_level = AdjustLevel(level); if (new_level != level) id = id.parent(new_level); if (out > 0 && (*cells)[out-1].contains(id)) continue; while (out > 0 && id.contains((*cells)[out-1])) --out; (*cells)[out++] = id; } cells->resize(out); } void S2RegionCoverer::GetInitialCandidates() { // Optimization: start with a small (usually 4 cell) covering of the // region's bounding cap. S2RegionCoverer tmp_coverer; tmp_coverer.mutable_options()->set_max_cells(min(4, options_.max_cells())); tmp_coverer.mutable_options()->set_max_level(options_.max_level()); vector cells; tmp_coverer.GetFastCovering(*region_, &cells); AdjustCellLevels(&cells); for (S2CellId cell_id : cells) { AddCandidate(NewCandidate(S2Cell(cell_id))); } } void S2RegionCoverer::GetCoveringInternal(const S2Region& region) { // We check this on each call because of mutable_options(). S2_DCHECK_LE(options_.min_level(), options_.max_level()); // Strategy: Start with the 6 faces of the cube. Discard any // that do not intersect the shape. Then repeatedly choose the // largest cell that intersects the shape and subdivide it. // // result_ contains the cells that will be part of the output, while pq_ // contains cells that we may still subdivide further. Cells that are // entirely contained within the region are immediately added to the output, // while cells that do not intersect the region are immediately discarded. // Therefore pq_ only contains cells that partially intersect the region. // Candidates are prioritized first according to cell size (larger cells // first), then by the number of intersecting children they have (fewest // children first), and then by the number of fully contained children // (fewest children first). S2_DCHECK(pq_.empty()); S2_DCHECK(result_.empty()); region_ = ®ion; candidates_created_counter_ = 0; GetInitialCandidates(); while (!pq_.empty() && (!interior_covering_ || result_.size() < options_.max_cells())) { Candidate* candidate = pq_.top().second; pq_.pop(); S2_VLOG(2) << "Pop: " << candidate->cell.id(); // For interior coverings we keep subdividing no matter how many children // the candidate has. If we reach max_cells() before expanding all // children, we will just use some of them. For exterior coverings we // cannot do this, because the result has to cover the whole region, so // all children have to be used. The (candidate->num_children == 1) case // takes care of the situation when we already have more than max_cells() // in results (min_level is too high). Subdividing the candidate with one // child does no harm in this case. if (interior_covering_ || candidate->cell.level() < options_.min_level() || candidate->num_children == 1 || (result_.size() + pq_.size() + candidate->num_children <= options_.max_cells())) { // Expand this candidate into its children. for (int i = 0; i < candidate->num_children; ++i) { if (interior_covering_ && result_.size() >= options_.max_cells()) { DeleteCandidate(candidate->children[i], true); } else { AddCandidate(candidate->children[i]); } } DeleteCandidate(candidate, false); } else { candidate->is_terminal = true; AddCandidate(candidate); } } S2_VLOG(2) << "Created " << result_.size() << " cells, " << candidates_created_counter_ << " candidates created, " << pq_.size() << " left"; while (!pq_.empty()) { DeleteCandidate(pq_.top().second, true); pq_.pop(); } region_ = nullptr; // Rather than just returning the raw list of cell ids, we construct a cell // union and then denormalize it. This has the effect of replacing four // child cells with their parent whenever this does not violate the covering // parameters specified (min_level, level_mod, etc). This significantly // reduces the number of cells returned in many cases, and it is cheap // compared to computing the covering in the first place. S2CellUnion::Normalize(&result_); if (options_.min_level() > 0 || options_.level_mod() > 1) { auto result_copy = result_; S2CellUnion::Denormalize(result_copy, options_.min_level(), options_.level_mod(), &result_); } S2_DCHECK(IsCanonical(result_)); } void S2RegionCoverer::GetCovering(const S2Region& region, vector* covering) { interior_covering_ = false; GetCoveringInternal(region); *covering = std::move(result_); } void S2RegionCoverer::GetInteriorCovering(const S2Region& region, vector* interior) { interior_covering_ = true; GetCoveringInternal(region); *interior = std::move(result_); } S2CellUnion S2RegionCoverer::GetCovering(const S2Region& region) { interior_covering_ = false; GetCoveringInternal(region); return S2CellUnion::FromVerbatim(std::move(result_)); } S2CellUnion S2RegionCoverer::GetInteriorCovering(const S2Region& region) { interior_covering_ = true; GetCoveringInternal(region); return S2CellUnion::FromVerbatim(std::move(result_)); } void S2RegionCoverer::GetFastCovering(const S2Region& region, vector* covering) { region.GetCellUnionBound(covering); CanonicalizeCovering(covering); } bool S2RegionCoverer::IsCanonical(const S2CellUnion& covering) const { return IsCanonical(covering.cell_ids()); } bool S2RegionCoverer::IsCanonical(const vector& covering) const { // We check this on each call because of mutable_options(). S2_DCHECK_LE(options_.min_level(), options_.max_level()); const int min_level = options_.min_level(); const int max_level = options_.true_max_level(); const int level_mod = options_.level_mod(); const bool too_many_cells = covering.size() > options_.max_cells(); int same_parent_count = 1; S2CellId prev_id = S2CellId::None(); for (const S2CellId id : covering) { if (!id.is_valid()) return false; // Check that the S2CellId level is acceptable. const int level = id.level(); if (level < min_level || level > max_level) return false; if (level_mod > 1 && (level - min_level) % level_mod != 0) return false; if (prev_id != S2CellId::None()) { // Check that cells are sorted and non-overlapping. if (prev_id.range_max() >= id.range_min()) return false; // If there are too many cells, check that no pair of adjacent cells // could be replaced by an ancestor. if (too_many_cells && id.GetCommonAncestorLevel(prev_id) >= min_level) { return false; } // Check that there are no sequences of (4 ** level_mod) cells that all // have the same parent (considering only multiples of "level_mod"). int plevel = level - level_mod; if (plevel < min_level || level != prev_id.level() || id.parent(plevel) != prev_id.parent(plevel)) { same_parent_count = 1; } else if (++same_parent_count == (1 << (2 * level_mod))) { return false; } } prev_id = id; } return true; } bool S2RegionCoverer::ContainsAllChildren(const vector& covering, S2CellId id) const { auto it = std::lower_bound(covering.begin(), covering.end(), id.range_min()); int level = id.level() + options_.level_mod(); for (S2CellId child = id.child_begin(level); child != id.child_end(level); ++it, child = child.next()) { if (it == covering.end() || *it != child) return false; } return true; } // Replaces all descendants of "id" in "covering" with "id". // REQUIRES: "covering" contains at least one descendant of "id". void S2RegionCoverer::ReplaceCellsWithAncestor(vector* covering, S2CellId id) const { auto begin = std::lower_bound(covering->begin(), covering->end(), id.range_min()); auto end = std::upper_bound(covering->begin(), covering->end(), id.range_max()); S2_DCHECK(begin != end); covering->erase(begin + 1, end); *begin = id; } S2CellUnion S2RegionCoverer::CanonicalizeCovering(const S2CellUnion& covering) { vector ids = covering.cell_ids(); CanonicalizeCovering(&ids); return S2CellUnion(std::move(ids)); } void S2RegionCoverer::CanonicalizeCovering(vector* covering) { // We check this on each call because of mutable_options(). S2_DCHECK_LE(options_.min_level(), options_.max_level()); // Note that when the covering parameters have their default values, almost // all of the code in this function is skipped. // If any cells are too small, or don't satisfy level_mod(), then replace // them with ancestors. if (options_.max_level() < S2CellId::kMaxLevel || options_.level_mod() > 1) { for (S2CellId& id : *covering) { int level = id.level(); int new_level = AdjustLevel(min(level, options_.max_level())); if (new_level != level) { id = id.parent(new_level); } } } // Sort the cells and simplify them. S2CellUnion::Normalize(covering); // Make sure that the covering satisfies min_level() and level_mod(), // possibly at the expense of satisfying max_cells(). if (options_.min_level() > 0 || options_.level_mod() > 1) { S2CellUnion::Denormalize(*covering, options_.min_level(), options_.level_mod(), &result_); *covering = std::move(result_); } // If there are too many cells and the covering is very large, use the // S2RegionCoverer to compute a new covering. (This avoids possible O(n^2) // behavior of the simpler algorithm below.) int64 excess = covering->size() - options_.max_cells(); if (excess <= 0 || IsCanonical(*covering)) { return; } if (excess * covering->size() > 10000) { GetCovering(S2CellUnion(std::move(*covering)), covering); } else { // Repeatedly replace two adjacent cells in S2CellId order by their lowest // common ancestor until the number of cells is acceptable. while (covering->size() > options_.max_cells()) { int best_index = -1, best_level = -1; for (int i = 0; i + 1 < covering->size(); ++i) { int level = (*covering)[i].GetCommonAncestorLevel((*covering)[i+1]); level = AdjustLevel(level); if (level > best_level) { best_level = level; best_index = i; } } if (best_level < options_.min_level()) break; // Replace all cells contained by the new ancestor cell. S2CellId id = (*covering)[best_index].parent(best_level); ReplaceCellsWithAncestor(covering, id); // Now repeatedly check whether all children of the parent cell are // present, in which case we can replace those cells with their parent. while (best_level > options_.min_level()) { best_level -= options_.level_mod(); id = id.parent(best_level); if (!ContainsAllChildren(*covering, id)) break; ReplaceCellsWithAncestor(covering, id); } } } S2_DCHECK(IsCanonical(*covering)); } void S2RegionCoverer::FloodFill(const S2Region& region, S2CellId start, vector* output) { unordered_set all; vector frontier; output->clear(); all.insert(start); frontier.push_back(start); while (!frontier.empty()) { S2CellId id = frontier.back(); frontier.pop_back(); if (!region.MayIntersect(S2Cell(id))) continue; output->push_back(id); S2CellId neighbors[4]; id.GetEdgeNeighbors(neighbors); for (int edge = 0; edge < 4; ++edge) { S2CellId nbr = neighbors[edge]; if (all.insert(nbr).second) { frontier.push_back(nbr); } } } } void S2RegionCoverer::GetSimpleCovering( const S2Region& region, const S2Point& start, int level, vector* output) { return FloodFill(region, S2CellId(start).parent(level), output); } s2/src/s2/s1angle.h0000644000176200001440000002424714122355625013432 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_S1ANGLE_H_ #define S2_S1ANGLE_H_ #include #include #include #include #include "s2/base/integral_types.h" #include "s2/_fp_contract_off.h" #include "s2/s2point.h" #include "s2/util/math/mathutil.h" #include "s2/util/math/vector.h" class S2LatLng; #ifndef SWIG #define IFNDEF_SWIG(x) x #else #define IFNDEF_SWIG(x) #endif // This class represents a one-dimensional angle (as opposed to a // two-dimensional solid angle). It has methods for converting angles to // or from radians, degrees, and the E5/E6/E7 representations (i.e. degrees // multiplied by 1e5/1e6/1e7 and rounded to the nearest integer). // // The internal representation is a double-precision value in radians, so // conversion to and from radians is exact. Conversions between E5, E6, E7, // and Degrees are not always exact; for example, Degrees(3.1) is different // from E6(3100000) or E7(310000000). However, the following properties are // guaranteed for any integer "n", provided that "n" is in the input range of // both functions: // // Degrees(n) == E6(1000000 * n) // Degrees(n) == E7(10000000 * n) // E6(n) == E7(10 * n) // // The corresponding properties are *not* true for E5, so if you use E5 then // don't test for exact equality when comparing to other formats such as // Degrees or E7. // // The following conversions between degrees and radians are exact: // // Degrees(180) == Radians(M_PI) // Degrees(45 * k) == Radians(k * M_PI / 4) for k == 0..8 // // These identities also hold when the arguments are scaled up or down by any // power of 2. Some similar identities are also true, for example, // Degrees(60) == Radians(M_PI / 3), but be aware that this type of identity // does not hold in general. For example, Degrees(3) != Radians(M_PI / 60). // // Similarly, the conversion to radians means that Angle::Degrees(x).degrees() // does not always equal "x". For example, // // S1Angle::Degrees(45 * k).degrees() == 45 * k for k == 0..8 // but S1Angle::Degrees(60).degrees() != 60. // // This means that when testing for equality, you should allow for numerical // errors (EXPECT_DOUBLE_EQ) or convert to discrete E5/E6/E7 values first. // // CAVEAT: All of the above properties depend on "double" being the usual // 64-bit IEEE 754 type (which is true on almost all modern platforms). // // This class is intended to be copied by value as desired. It uses // the default copy constructor and assignment operator. class S1Angle { public: // These methods construct S1Angle objects from their measure in radians // or degrees. static constexpr S1Angle Radians(double radians); static constexpr S1Angle Degrees(double degrees); static constexpr S1Angle E5(int32 e5); static constexpr S1Angle E6(int32 e6); static constexpr S1Angle E7(int32 e7); // Convenience functions -- to use when args have been fixed32s in protos. // // The arguments are static_cast into int32, so very large unsigned values // are treated as negative numbers. static constexpr S1Angle UnsignedE6(uint32 e6); static constexpr S1Angle UnsignedE7(uint32 e7); // The default constructor yields a zero angle. This is useful for STL // containers and class methods with output arguments. IFNDEF_SWIG(constexpr) S1Angle() : radians_(0) {} // Return an angle larger than any finite angle. static constexpr S1Angle Infinity(); // A explicit shorthand for the default constructor. static constexpr S1Angle Zero(); // Return the angle between two points, which is also equal to the distance // between these points on the unit sphere. The points do not need to be // normalized. This function has a maximum error of 3.25 * DBL_EPSILON (or // 2.5 * DBL_EPSILON for angles up to 1 radian). If either point is // zero-length (e.g. an uninitialized S2Point), or almost zero-length, the // resulting angle will be zero. S1Angle(const S2Point& x, const S2Point& y); // Like the constructor above, but return the angle (i.e., distance) between // two S2LatLng points. This function has about 15 digits of accuracy for // small distances but only about 8 digits of accuracy as the distance // approaches 180 degrees (i.e., nearly-antipodal points). S1Angle(const S2LatLng& x, const S2LatLng& y); constexpr double radians() const; constexpr double degrees() const; int32 e5() const; int32 e6() const; int32 e7() const; // Return the absolute value of an angle. S1Angle abs() const; // Comparison operators. friend bool operator==(S1Angle x, S1Angle y); friend bool operator!=(S1Angle x, S1Angle y); friend bool operator<(S1Angle x, S1Angle y); friend bool operator>(S1Angle x, S1Angle y); friend bool operator<=(S1Angle x, S1Angle y); friend bool operator>=(S1Angle x, S1Angle y); // Simple arithmetic operators for manipulating S1Angles. friend S1Angle operator-(S1Angle a); friend S1Angle operator+(S1Angle a, S1Angle b); friend S1Angle operator-(S1Angle a, S1Angle b); friend S1Angle operator*(double m, S1Angle a); friend S1Angle operator*(S1Angle a, double m); friend S1Angle operator/(S1Angle a, double m); friend double operator/(S1Angle a, S1Angle b); S1Angle& operator+=(S1Angle a); S1Angle& operator-=(S1Angle a); S1Angle& operator*=(double m); S1Angle& operator/=(double m); // Trigonmetric functions (not necessary but slightly more convenient). friend double sin(S1Angle a); friend double cos(S1Angle a); friend double tan(S1Angle a); // Return the angle normalized to the range (-180, 180] degrees. S1Angle Normalized() const; // Normalize this angle to the range (-180, 180] degrees. void Normalize(); // When S1Angle is used as a key in one of the btree container types // (util/btree), indicate that linear rather than binary search should be // used. This is much faster when the comparison function is cheap. typedef std::true_type absl_btree_prefer_linear_node_search; private: explicit IFNDEF_SWIG(constexpr) S1Angle(double radians) : radians_(radians) {} double radians_; }; ////////////////// Implementation details follow //////////////////// inline constexpr S1Angle S1Angle::Infinity() { return S1Angle(std::numeric_limits::infinity()); } inline constexpr S1Angle S1Angle::Zero() { return S1Angle(0); } inline constexpr double S1Angle::radians() const { return radians_; } inline constexpr double S1Angle::degrees() const { return (180 / M_PI) * radians_; } // Note that the E5, E6, and E7 conversion involve two multiplications rather // than one. This is mainly for backwards compatibility (changing this would // break many tests), but it does have the nice side effect that conversions // between Degrees, E6, and E7 are exact when the arguments are integers. inline int32 S1Angle::e5() const { return MathUtil::FastIntRound(1e5 * degrees()); } inline int32 S1Angle::e6() const { return MathUtil::FastIntRound(1e6 * degrees()); } inline int32 S1Angle::e7() const { return MathUtil::FastIntRound(1e7 * degrees()); } inline S1Angle S1Angle::abs() const { return S1Angle(std::fabs(radians_)); } inline bool operator==(S1Angle x, S1Angle y) { return x.radians() == y.radians(); } inline bool operator!=(S1Angle x, S1Angle y) { return x.radians() != y.radians(); } inline bool operator<(S1Angle x, S1Angle y) { return x.radians() < y.radians(); } inline bool operator>(S1Angle x, S1Angle y) { return x.radians() > y.radians(); } inline bool operator<=(S1Angle x, S1Angle y) { return x.radians() <= y.radians(); } inline bool operator>=(S1Angle x, S1Angle y) { return x.radians() >= y.radians(); } inline S1Angle operator-(S1Angle a) { return S1Angle::Radians(-a.radians()); } inline S1Angle operator+(S1Angle a, S1Angle b) { return S1Angle::Radians(a.radians() + b.radians()); } inline S1Angle operator-(S1Angle a, S1Angle b) { return S1Angle::Radians(a.radians() - b.radians()); } inline S1Angle operator*(double m, S1Angle a) { return S1Angle::Radians(m * a.radians()); } inline S1Angle operator*(S1Angle a, double m) { return S1Angle::Radians(m * a.radians()); } inline S1Angle operator/(S1Angle a, double m) { return S1Angle::Radians(a.radians() / m); } inline double operator/(S1Angle a, S1Angle b) { return a.radians() / b.radians(); } inline S1Angle& S1Angle::operator+=(S1Angle a) { radians_ += a.radians(); return *this; } inline S1Angle& S1Angle::operator-=(S1Angle a) { radians_ -= a.radians(); return *this; } inline S1Angle& S1Angle::operator*=(double m) { radians_ *= m; return *this; } inline S1Angle& S1Angle::operator/=(double m) { radians_ /= m; return *this; } inline double sin(S1Angle a) { return sin(a.radians()); } inline double cos(S1Angle a) { return cos(a.radians()); } inline double tan(S1Angle a) { return tan(a.radians()); } inline constexpr S1Angle S1Angle::Radians(double radians) { return S1Angle(radians); } inline constexpr S1Angle S1Angle::Degrees(double degrees) { return S1Angle((M_PI / 180) * degrees); } inline constexpr S1Angle S1Angle::E5(int32 e5) { return Degrees(1e-5 * e5); } inline constexpr S1Angle S1Angle::E6(int32 e6) { return Degrees(1e-6 * e6); } inline constexpr S1Angle S1Angle::E7(int32 e7) { return Degrees(1e-7 * e7); } inline constexpr S1Angle S1Angle::UnsignedE6(uint32 e6) { return E6(static_cast(e6)); } inline constexpr S1Angle S1Angle::UnsignedE7(uint32 e7) { return E7(static_cast(e7)); } // Writes the angle in degrees with 7 digits of precision after the // decimal point, e.g. "17.3745904". std::ostream& operator<<(std::ostream& os, S1Angle a); #undef IFNDEF_SWIG #endif // S2_S1ANGLE_H_ s2/src/s2/s2edge_tessellator.cc0000644000176200001440000003250413774601521016024 0ustar liggesusers// Copyright 2017 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2edge_tessellator.h" #include #include "s2/s2edge_distances.h" #include "s2/s2latlng.h" #include "s2/s2pointutil.h" using std::vector; // Tessellation is implemented by subdividing the edge until the estimated // maximum error is below the given tolerance. Estimating error is a hard // problem, especially when the only methods available are point evaluation of // the projection and its inverse. (These are the only methods that // S2::Projection provides, which makes it easier and less error-prone to // implement new projections.) // // One technique that significantly increases robustness is to treat the // geodesic and projected edges as parametric curves rather than geometric ones. // Given a spherical edge AB and a projection p:S2->R2, let f(t) be the // normalized arc length parametrization of AB and let g(t) be the normalized // arc length parameterization of the projected edge p(A)p(B). (In other words, // f(0)=A, f(1)=B, g(0)=p(A), g(1)=p(B).) We now define the geometric error as // the maximum distance from the point p^-1(g(t)) to the geodesic edge AB for // any t in [0,1], where p^-1 denotes the inverse projection. In other words, // the geometric error is the maximum distance from any point on the projected // edge (mapped back onto the sphere) to the geodesic edge AB. On the other // hand we define the parametric error as the maximum distance between the // points f(t) and p^-1(g(t)) for any t in [0,1], i.e. the maximum distance // (measured on the sphere) between the geodesic and projected points at the // same interpolation fraction t. // // The easiest way to estimate the parametric error is to simply evaluate both // edges at their midpoints and measure the distance between them (the "midpoint // method"). This is very fast and works quite well for most edges, however it // has one major drawback: it doesn't handle points of inflection (i.e., points // where the curvature changes sign). For example, edges in the Mercator and // Plate Carree projections always curve towards the equator relative to the // corresponding geodesic edge, so in these projections there is a point of // inflection whenever the projected edge crosses the equator. The worst case // occurs when the edge endpoints have different longitudes but the same // absolute latitude, since in that case the error is non-zero but the edges // have exactly the same midpoint (on the equator). // // One solution to this problem is to split the input edges at all inflection // points (i.e., along the equator in the case of the Mercator and Plate Carree // projections). However for general projections these inflection points can // occur anywhere on the sphere (e.g., consider the Transverse Mercator // projection). This could be addressed by adding methods to the S2Projection // interface to split edges at inflection points but this would make it harder // and more error-prone to implement new projections. // // Another problem with this approach is that the midpoint method sometimes // underestimates the true error even when edges do not cross the equator. For // the Plate Carree and Mercator projections, the midpoint method can // underestimate the error by up to 3%. // // Both of these problems can be solved as follows. We assume that the error // can be modeled as a convex combination of two worst-case functions, one // where the error is maximized at the edge midpoint and another where the // error is *minimized* (i.e., zero) at the edge midpoint. For example, we // could choose these functions as: // // E1(x) = 1 - x^2 // E2(x) = x * (1 - x^2) // // where for convenience we use an interpolation parameter "x" in the range // [-1, 1] rather than the original "t" in the range [0, 1]. Note that both // error functions must have roots at x = {-1, 1} since the error must be zero // at the edge endpoints. E1 is simply a parabola whose maximum value is 1 // attained at x = 0, while E2 is a cubic with an additional root at x = 0, // and whose maximum value is 2 * sqrt(3) / 9 attained at x = 1 / sqrt(3). // // Next, it is convenient to scale these functions so that the both have a // maximum value of 1. E1 already satisfies this requirement, and we simply // redefine E2 as // // E2(x) = x * (1 - x^2) / (2 * sqrt(3) / 9) // // Now define x0 to be the point where these two functions intersect, i.e. the // point in the range (-1, 1) where E1(x0) = E2(x0). This value has the very // convenient property that if we evaluate the actual error E(x0), then the // maximum error on the entire interval [-1, 1] is bounded by // // E(x) <= E(x0) / E1(x0) // // since whether the error is modeled using E1 or E2, the resulting function // has the same maximum value (namely E(x0) / E1(x0)). If it is modeled as // some other convex combination of E1 and E2, the maximum value can only // decrease. // // Finally, since E2 is not symmetric about the y-axis, we must also allow for // the possibility that the error is a convex combination of E1 and -E2. This // can be handled by evaluating the error at E(-x0) as well, and then // computing the final error bound as // // E(x) <= max(E(x0), E(-x0)) / E1(x0) . // // Effectively, this method is simply evaluating the error at two points about // 1/3 and 2/3 of the way along the edges, and then scaling the maximum of // these two errors by a constant factor. Intuitively, the reason this works // is that if the two edges cross somewhere in the interior, then at least one // of these points will be far from the crossing. // // The actual algorithm implemented below has some additional refinements. // First, edges longer than 90 degrees are always subdivided; this avoids // various unusual situations that can happen with very long edges, and there // is really no reason to avoid adding vertices to edges that are so long. // // Second, the error function E1 above needs to be modified to take into // account spherical distortions. (It turns out that spherical distortions are // beneficial in the case of E2, i.e. they only make its error estimates // slightly more conservative.) To do this, we model E1 as the maximum error // in a Plate Carree edge of length 90 degrees or less. This turns out to be // an edge from 45:-90 to 45:90 (in lat:lng format). The corresponding error // as a function of "x" in the range [-1, 1] can be computed as the distance // between the the the Plate Caree edge point (45, 90 * x) and the geodesic // edge point (90 - 45 * abs(x), 90 * sgn(x)). Using the Haversine formula, // the corresponding function E1 (normalized to have a maximum value of 1) is: // // E1(x) = // asin(sqrt(sin(Pi / 8 * (1 - x)) ^ 2 + // sin(Pi / 4 * (1 - x)) ^ 2 * cos(Pi / 4) * sin(Pi / 4 * x))) / // asin(sqrt((1 - 1 / sqrt(2)) / 2)) // // Note that this function does not need to be evaluated at runtime, it // simply affects the calculation of the value x0 where E1(x0) = E2(x0) // and the corresponding scaling factor C = 1 / E1(x0). // // ------------------------------------------------------------------ // // In the case of the Mercator and Plate Carree projections this strategy // produces a conservative upper bound (verified using 10 million random // edges). Furthermore the bound is nearly tight; the scaling constant is // C = 1.19289, whereas the maximum observed value was 1.19254. // // Compared to the simpler midpoint evaluation method, this strategy requires // more function evaluations (currently twice as many, but with a smarter // tessellation algorithm it will only be 50% more). It also results in a // small amount of additional tessellation (about 1.5%) compared to the // midpoint method, but this is due almost entirely to the fact that the // midpoint method does not yield conservative error estimates. // // For random edges with a tolerance of 1 meter, the expected amount of // overtessellation is as follows: // // Midpoint Method Cubic Method // Plate Carree 1.8% 3.0% // Mercator 15.8% 17.4% // The interpolation fraction at which the two edges are evaluated in order to // measure the error between them. (Edges are evaluated at two points measured // this fraction from either end.) With respect to the algorithm description // above, this value is t0 = (1 - x0) / 2 in the range [0, 1] that corresponds // to x0 in the range [-1, 1] chosen such that E1(x0) == E2(x0). static constexpr double kInterpolationFraction = 0.31215691082248312; // The following is the value of E1(x0) == E2(x0). static constexpr double kScaleFactor = 0.83829992569888509; S1Angle S2EdgeTessellator::kMinTolerance() { // This distance is less than 1 micrometer on the Earth's surface, but is // still much larger than the expected projection and interpolation errors. return S1Angle::Radians(1e-13); } S2EdgeTessellator::S2EdgeTessellator(const S2::Projection* projection, S1Angle tolerance) : proj_(*projection) { if (tolerance < kMinTolerance()) S2_LOG(DFATAL) << "Tolerance too small"; // Rather than scaling the error estimate as described above, instead we scale // the tolerance. See algorithm description at the top of this file. scaled_tolerance_ = S1ChordAngle(kScaleFactor * std::max(tolerance, kMinTolerance())); } S1ChordAngle S2EdgeTessellator::EstimateMaxError( const R2Point& pa, const S2Point& a, const R2Point& pb, const S2Point& b) const { // See the algorithm description at the top of this file. // We always tessellate edges longer than 90 degrees on the sphere, since the // approximation below is not robust enough to handle such edges. if (a.DotProd(b) < -1e-14) return S1ChordAngle::Infinity(); constexpr double t1 = kInterpolationFraction; constexpr double t2 = 1 - kInterpolationFraction; S2Point mid1 = S2::Interpolate(t1, a, b); S2Point mid2 = S2::Interpolate(t2, a, b); S2Point pmid1 = proj_.Unproject(proj_.Interpolate(t1, pa, pb)); S2Point pmid2 = proj_.Unproject(proj_.Interpolate(t2, pa, pb)); return std::max(S1ChordAngle(mid1, pmid1), S1ChordAngle(mid2, pmid2)); } void S2EdgeTessellator::AppendProjected( const S2Point& a, const S2Point& b, vector* vertices) const { R2Point pa = proj_.Project(a); if (vertices->empty()) { vertices->push_back(pa); } else { pa = proj_.WrapDestination(vertices->back(), pa); S2_DCHECK_EQ(vertices->back(), pa) << "Appended edges must form a chain"; } R2Point pb = proj_.Project(b); AppendProjected(pa, a, pb, b, vertices); } // Given a geodesic edge AB, split the edge as necessary and append all // projected vertices except the first to "vertices". // // The maximum recursion depth is (M_PI / kMinTolerance()) < 45, and the // frame size is small so stack overflow should not be an issue. void S2EdgeTessellator::AppendProjected(const R2Point& pa, const S2Point& a, const R2Point& pb_in, const S2Point& b, vector* vertices) const { R2Point pb = proj_.WrapDestination(pa, pb_in); if (EstimateMaxError(pa, a, pb, b) <= scaled_tolerance_) { vertices->push_back(pb); } else { S2Point mid = (a + b).Normalize(); R2Point pmid = proj_.WrapDestination(pa, proj_.Project(mid)); AppendProjected(pa, a, pmid, mid, vertices); AppendProjected(pmid, mid, pb, b, vertices); } } void S2EdgeTessellator::AppendUnprojected( const R2Point& pa, const R2Point& pb, vector* vertices) const { S2Point a = proj_.Unproject(pa); S2Point b = proj_.Unproject(pb); if (vertices->empty()) { vertices->push_back(a); } else { // Note that coordinate wrapping can create a small amount of error. For // example in the edge chain "0:-175, 0:179, 0:-177", the first edge is // transformed into "0:-175, 0:-181" while the second is transformed into // "0:179, 0:183". The two coordinate pairs for the middle vertex // ("0:-181" and "0:179") may not yield exactly the same S2Point. S2_DCHECK(S2::ApproxEquals(vertices->back(), a)) << "Appended edges must form a chain"; } AppendUnprojected(pa, a, pb, b, vertices); } // Like AppendProjected, but interpolates a projected edge and appends the // corresponding points on the sphere. void S2EdgeTessellator::AppendUnprojected( const R2Point& pa, const S2Point& a, const R2Point& pb_in, const S2Point& b, vector* vertices) const { // See notes above regarding measuring the interpolation error. R2Point pb = proj_.WrapDestination(pa, pb_in); if (EstimateMaxError(pa, a, pb, b) <= scaled_tolerance_) { vertices->push_back(b); } else { R2Point pmid = proj_.Interpolate(0.5, pa, pb); S2Point mid = proj_.Unproject(pmid); AppendUnprojected(pa, a, pmid, mid, vertices); AppendUnprojected(pmid, mid, pb, b, vertices); } } s2/src/s2/s2latlng_rect_bounder.cc0000644000176200001440000004161613774601521016517 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2latlng_rect_bounder.h" #include #include #include "s2/base/logging.h" #include "s2/r1interval.h" #include "s2/s1chord_angle.h" #include "s2/s1interval.h" #include "s2/s2pointutil.h" using std::fabs; using std::max; using std::min; void S2LatLngRectBounder::AddPoint(const S2Point& b) { S2_DCHECK(S2::IsUnitLength(b)); AddInternal(b, S2LatLng(b)); } void S2LatLngRectBounder::AddLatLng(const S2LatLng& b_latlng) { AddInternal(b_latlng.ToPoint(), b_latlng); } void S2LatLngRectBounder::AddInternal(const S2Point& b, const S2LatLng& b_latlng) { // Simple consistency check to verify that b and b_latlng are alternate // representations of the same vertex. S2_DCHECK(S2::ApproxEquals(b, b_latlng.ToPoint())); if (bound_.is_empty()) { bound_.AddPoint(b_latlng); } else { // First compute the cross product N = A x B robustly. This is the normal // to the great circle through A and B. We don't use S2::RobustCrossProd() // since that method returns an arbitrary vector orthogonal to A if the two // vectors are proportional, and we want the zero vector in that case. Vector3_d n = (a_ - b).CrossProd(a_ + b); // N = 2 * (A x B) // The relative error in N gets large as its norm gets very small (i.e., // when the two points are nearly identical or antipodal). We handle this // by choosing a maximum allowable error, and if the error is greater than // this we fall back to a different technique. Since it turns out that // the other sources of error in converting the normal to a maximum // latitude add up to at most 1.16 * DBL_EPSILON (see below), and it is // desirable to have the total error be a multiple of DBL_EPSILON, we have // chosen to limit the maximum error in the normal to 3.84 * DBL_EPSILON. // It is possible to show that the error is less than this when // // n.Norm() >= 8 * sqrt(3) / (3.84 - 0.5 - sqrt(3)) * DBL_EPSILON // = 1.91346e-15 (about 8.618 * DBL_EPSILON) double n_norm = n.Norm(); if (n_norm < 1.91346e-15) { // A and B are either nearly identical or nearly antipodal (to within // 4.309 * DBL_EPSILON, or about 6 nanometers on the earth's surface). if (a_.DotProd(b) < 0) { // The two points are nearly antipodal. The easiest solution is to // assume that the edge between A and B could go in any direction // around the sphere. bound_ = S2LatLngRect::Full(); } else { // The two points are nearly identical (to within 4.309 * DBL_EPSILON). // In this case we can just use the bounding rectangle of the points, // since after the expansion done by GetBound() this rectangle is // guaranteed to include the (lat,lng) values of all points along AB. bound_ = bound_.Union(S2LatLngRect::FromPointPair(a_latlng_, b_latlng)); } } else { // Compute the longitude range spanned by AB. S1Interval lng_ab = S1Interval::FromPointPair(a_latlng_.lng().radians(), b_latlng.lng().radians()); if (lng_ab.GetLength() >= M_PI - 2 * DBL_EPSILON) { // The points lie on nearly opposite lines of longitude to within the // maximum error of the calculation. (Note that this test relies on // the fact that M_PI is slightly less than the true value of Pi, and // that representable values near M_PI are 2 * DBL_EPSILON apart.) // The easiest solution is to assume that AB could go on either side // of the pole. lng_ab = S1Interval::Full(); } // Next we compute the latitude range spanned by the edge AB. We start // with the range spanning the two endpoints of the edge: R1Interval lat_ab = R1Interval::FromPointPair(a_latlng_.lat().radians(), b_latlng.lat().radians()); // This is the desired range unless the edge AB crosses the plane // through N and the Z-axis (which is where the great circle through A // and B attains its minimum and maximum latitudes). To test whether AB // crosses this plane, we compute a vector M perpendicular to this // plane and then project A and B onto it. Vector3_d m = n.CrossProd(S2Point(0, 0, 1)); double m_a = m.DotProd(a_); double m_b = m.DotProd(b); // We want to test the signs of "m_a" and "m_b", so we need to bound // the error in these calculations. It is possible to show that the // total error is bounded by // // (1 + sqrt(3)) * DBL_EPSILON * n_norm + 8 * sqrt(3) * (DBL_EPSILON**2) // = 6.06638e-16 * n_norm + 6.83174e-31 double m_error = 6.06638e-16 * n_norm + 6.83174e-31; if (m_a * m_b < 0 || fabs(m_a) <= m_error || fabs(m_b) <= m_error) { // Minimum/maximum latitude *may* occur in the edge interior. // // The maximum latitude is 90 degrees minus the latitude of N. We // compute this directly using atan2 in order to get maximum accuracy // near the poles. // // Our goal is compute a bound that contains the computed latitudes of // all S2Points P that pass the point-in-polygon containment test. // There are three sources of error we need to consider: // - the directional error in N (at most 3.84 * DBL_EPSILON) // - converting N to a maximum latitude // - computing the latitude of the test point P // The latter two sources of error are at most 0.955 * DBL_EPSILON // individually, but it is possible to show by a more complex analysis // that together they can add up to at most 1.16 * DBL_EPSILON, for a // total error of 5 * DBL_EPSILON. // // We add 3 * DBL_EPSILON to the bound here, and GetBound() will pad // the bound by another 2 * DBL_EPSILON. double max_lat = min( atan2(sqrt(n[0]*n[0] + n[1]*n[1]), fabs(n[2])) + 3 * DBL_EPSILON, M_PI_2); // In order to get tight bounds when the two points are close together, // we also bound the min/max latitude relative to the latitudes of the // endpoints A and B. First we compute the distance between A and B, // and then we compute the maximum change in latitude between any two // points along the great circle that are separated by this distance. // This gives us a latitude change "budget". Some of this budget must // be spent getting from A to B; the remainder bounds the round-trip // distance (in latitude) from A or B to the min or max latitude // attained along the edge AB. double lat_budget = 2 * asin(0.5 * (a_ - b).Norm() * sin(max_lat)); double max_delta = 0.5*(lat_budget - lat_ab.GetLength()) + DBL_EPSILON; // Test whether AB passes through the point of maximum latitude or // minimum latitude. If the dot product(s) are small enough then the // result may be ambiguous. if (m_a <= m_error && m_b >= -m_error) { lat_ab.set_hi(min(max_lat, lat_ab.hi() + max_delta)); } if (m_b <= m_error && m_a >= -m_error) { lat_ab.set_lo(max(-max_lat, lat_ab.lo() - max_delta)); } } bound_ = bound_.Union(S2LatLngRect(lat_ab, lng_ab)); } } a_ = b; a_latlng_ = b_latlng; } S2LatLngRect S2LatLngRectBounder::GetBound() const { // To save time, we ignore numerical errors in the computed S2LatLngs while // accumulating the bounds and then account for them here. // // S2LatLng(S2Point) has a maximum error of 0.955 * DBL_EPSILON in latitude. // In the worst case, we might have rounded "inwards" when computing the // bound and "outwards" when computing the latitude of a contained point P, // therefore we expand the latitude bounds by 2 * DBL_EPSILON in each // direction. (A more complex analysis shows that 1.5 * DBL_EPSILON is // enough, but the expansion amount should be a multiple of DBL_EPSILON in // order to avoid rounding errors during the expansion itself.) // // S2LatLng(S2Point) has a maximum error of DBL_EPSILON in longitude, which // is simply the maximum rounding error for results in the range [-Pi, Pi]. // This is true because the Gnu implementation of atan2() comes from the IBM // Accurate Mathematical Library, which implements correct rounding for this // instrinsic (i.e., it returns the infinite precision result rounded to the // nearest representable value, with ties rounded to even values). This // implies that we don't need to expand the longitude bounds at all, since // we only guarantee that the bound contains the *rounded* latitudes of // contained points. The *true* latitudes of contained points may lie up to // DBL_EPSILON outside of the returned bound. const S2LatLng kExpansion = S2LatLng::FromRadians(2 * DBL_EPSILON, 0); return bound_.Expanded(kExpansion).PolarClosure(); } S2LatLngRect S2LatLngRectBounder::ExpandForSubregions( const S2LatLngRect& bound) { // Empty bounds don't need expansion. if (bound.is_empty()) return bound; // First we need to check whether the bound B contains any nearly-antipodal // points (to within 4.309 * DBL_EPSILON). If so then we need to return // S2LatLngRect::Full(), since the subregion might have an edge between two // such points, and AddPoint() returns Full() for such edges. Note that // this can happen even if B is not Full(); for example, consider a loop // that defines a 10km strip straddling the equator extending from // longitudes -100 to +100 degrees. // // It is easy to check whether B contains any antipodal points, but checking // for nearly-antipodal points is trickier. Essentially we consider the // original bound B and its reflection through the origin B', and then test // whether the minimum distance between B and B' is less than 4.309 * // DBL_EPSILON. // "lng_gap" is a lower bound on the longitudinal distance between B and its // reflection B'. (2.5 * DBL_EPSILON is the maximum combined error of the // endpoint longitude calculations and the GetLength() call.) double lng_gap = max(0.0, M_PI - bound.lng().GetLength() - 2.5 * DBL_EPSILON); // "min_abs_lat" is the minimum distance from B to the equator (if zero or // negative, then B straddles the equator). double min_abs_lat = max(bound.lat().lo(), -bound.lat().hi()); // "lat_gap1" and "lat_gap2" measure the minimum distance from B to the // south and north poles respectively. double lat_gap1 = M_PI_2 + bound.lat().lo(); double lat_gap2 = M_PI_2 - bound.lat().hi(); if (min_abs_lat >= 0) { // The bound B does not straddle the equator. In this case the minimum // distance is between one endpoint of the latitude edge in B closest to // the equator and the other endpoint of that edge in B'. The latitude // distance between these two points is 2*min_abs_lat, and the longitude // distance is lng_gap. We could compute the distance exactly using the // Haversine formula, but then we would need to bound the errors in that // calculation. Since we only need accuracy when the distance is very // small (close to 4.309 * DBL_EPSILON), we substitute the Euclidean // distance instead. This gives us a right triangle XYZ with two edges of // length x = 2*min_abs_lat and y ~= lng_gap. The desired distance is the // length of the third edge "z", and we have // // z ~= sqrt(x^2 + y^2) >= (x + y) / sqrt(2) // // Therefore the region may contain nearly antipodal points only if // // 2*min_abs_lat + lng_gap < sqrt(2) * 4.309 * DBL_EPSILON // ~= 1.354e-15 // // Note that because the given bound B is conservative, "min_abs_lat" and // "lng_gap" are both lower bounds on their true values so we do not need // to make any adjustments for their errors. if (2 * min_abs_lat + lng_gap < 1.354e-15) { return S2LatLngRect::Full(); } } else if (lng_gap >= M_PI_2) { // B spans at most Pi/2 in longitude. The minimum distance is always // between one corner of B and the diagonally opposite corner of B'. We // use the same distance approximation that we used above; in this case // we have an obtuse triangle XYZ with two edges of length x = lat_gap1 // and y = lat_gap2, and angle Z >= Pi/2 between them. We then have // // z >= sqrt(x^2 + y^2) >= (x + y) / sqrt(2) // // Unlike the case above, "lat_gap1" and "lat_gap2" are not lower bounds // (because of the extra addition operation, and because M_PI_2 is not // exactly equal to Pi/2); they can exceed their true values by up to // 0.75 * DBL_EPSILON. Putting this all together, the region may // contain nearly antipodal points only if // // lat_gap1 + lat_gap2 < (sqrt(2) * 4.309 + 1.5) * DBL_EPSILON // ~= 1.687e-15 if (lat_gap1 + lat_gap2 < 1.687e-15) { return S2LatLngRect::Full(); } } else { // Otherwise we know that (1) the bound straddles the equator and (2) its // width in longitude is at least Pi/2. In this case the minimum // distance can occur either between a corner of B and the diagonally // opposite corner of B' (as in the case above), or between a corner of B // and the opposite longitudinal edge reflected in B'. It is sufficient // to only consider the corner-edge case, since this distance is also a // lower bound on the corner-corner distance when that case applies. // Consider the spherical triangle XYZ where X is a corner of B with // minimum absolute latitude, Y is the closest pole to X, and Z is the // point closest to X on the opposite longitudinal edge of B'. This is a // right triangle (Z = Pi/2), and from the spherical law of sines we have // // sin(z) / sin(Z) = sin(y) / sin(Y) // sin(max_lat_gap) / 1 = sin(d_min) / sin(lng_gap) // sin(d_min) = sin(max_lat_gap) * sin(lng_gap) // // where "max_lat_gap" = max(lat_gap1, lat_gap2) and "d_min" is the // desired minimum distance. Now using the facts that sin(t) >= (2/Pi)*t // for 0 <= t <= Pi/2, that we only need an accurate approximation when // at least one of "max_lat_gap" or "lng_gap" is extremely small (in // which case sin(t) ~= t), and recalling that "max_lat_gap" has an error // of up to 0.75 * DBL_EPSILON, we want to test whether // // max_lat_gap * lng_gap < (4.309 + 0.75) * (Pi/2) * DBL_EPSILON // ~= 1.765e-15 if (max(lat_gap1, lat_gap2) * lng_gap < 1.765e-15) { return S2LatLngRect::Full(); } } // Next we need to check whether the subregion might contain any edges that // span (M_PI - 2 * DBL_EPSILON) radians or more in longitude, since AddPoint // sets the longitude bound to Full() in that case. This corresponds to // testing whether (lng_gap <= 0) in "lng_expansion" below. // Otherwise, the maximum latitude error in AddPoint is 4.8 * DBL_EPSILON. // In the worst case, the errors when computing the latitude bound for a // subregion could go in the opposite direction as the errors when computing // the bound for the original region, so we need to double this value. // (More analysis shows that it's okay to round down to a multiple of // DBL_EPSILON.) // // For longitude, we rely on the fact that atan2 is correctly rounded and // therefore no additional bounds expansion is necessary. double lat_expansion = 9 * DBL_EPSILON; double lng_expansion = (lng_gap <= 0) ? M_PI : 0; return bound.Expanded(S2LatLng::FromRadians(lat_expansion, lng_expansion)).PolarClosure(); } S2LatLng S2LatLngRectBounder::MaxErrorForTests() { // The maximum error in the latitude calculation is // 3.84 * DBL_EPSILON for the RobustCrossProd calculation // 0.96 * DBL_EPSILON for the Latitude() calculation // 5 * DBL_EPSILON added by AddPoint/GetBound to compensate for error // ------------------ // 9.80 * DBL_EPSILON maximum error in result // // The maximum error in the longitude calculation is DBL_EPSILON. GetBound // does not do any expansion because this isn't necessary in order to // bound the *rounded* longitudes of contained points. return S2LatLng::FromRadians(10 * DBL_EPSILON, 1 * DBL_EPSILON); } s2/src/s2/s2shape_measures.cc0000644000176200001440000001105313774601521015477 0ustar liggesusers// Copyright 2018 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2shape_measures.h" #include #include #include "s2/s2loop_measures.h" #include "s2/s2polyline_measures.h" using std::fabs; using std::vector; namespace S2 { S1Angle GetLength(const S2Shape& shape) { if (shape.dimension() != 1) return S1Angle::Zero(); S1Angle length; vector vertices; int num_chains = shape.num_chains(); for (int chain_id = 0; chain_id < num_chains; ++chain_id) { GetChainVertices(shape, chain_id, &vertices); length += S2::GetLength(vertices); } return length; } S1Angle GetPerimeter(const S2Shape& shape) { if (shape.dimension() != 2) return S1Angle::Zero(); S1Angle perimeter; vector vertices; int num_chains = shape.num_chains(); for (int chain_id = 0; chain_id < num_chains; ++chain_id) { GetChainVertices(shape, chain_id, &vertices); perimeter += S2::GetPerimeter(S2PointLoopSpan(vertices)); } return perimeter; } double GetArea(const S2Shape& shape) { if (shape.dimension() != 2) return 0.0; // Since S2Shape uses the convention that the interior of the shape is to // the left of all edges, in theory we could compute the area of the polygon // by simply adding up all the loop areas modulo 4*Pi. The problem with // this approach is that polygons holes typically have areas near 4*Pi, // which can create large cancellation errors when computing the area of // small polygons with holes. For example, a shell with an area of 4 square // meters (1e-13 steradians) surrounding a hole with an area of 3 square // meters (7.5e-14 sterians) would lose almost all of its accuracy if the // area of the hole was computed as 12.566370614359098. // // So instead we use S2::GetSignedArea() to ensure that all loops have areas // in the range [-2*Pi, 2*Pi]. double area = 0; vector vertices; int num_chains = shape.num_chains(); for (int chain_id = 0; chain_id < num_chains; ++chain_id) { GetChainVertices(shape, chain_id, &vertices); area += S2::GetSignedArea(S2PointLoopSpan(vertices)); } // Note that S2::GetSignedArea() guarantees that the full loop (containing // all points on the sphere) has a very small negative area. S2_DCHECK_LE(fabs(area), 4 * M_PI); if (area < 0.0) area += 4 * M_PI; return area; } double GetApproxArea(const S2Shape& shape) { if (shape.dimension() != 2) return 0.0; double area = 0; vector vertices; int num_chains = shape.num_chains(); for (int chain_id = 0; chain_id < num_chains; ++chain_id) { GetChainVertices(shape, chain_id, &vertices); area += S2::GetApproxArea(S2PointLoopSpan(vertices)); } // Special case to ensure that full polygons are handled correctly. if (area <= 4 * M_PI) return area; return fmod(area, 4 * M_PI); } S2Point GetCentroid(const S2Shape& shape) { S2Point centroid; vector vertices; int dimension = shape.dimension(); int num_chains = shape.num_chains(); for (int chain_id = 0; chain_id < num_chains; ++chain_id) { switch (dimension) { case 0: centroid += shape.edge(chain_id).v0; break; case 1: GetChainVertices(shape, chain_id, &vertices); centroid += S2::GetCentroid(S2PointSpan(vertices)); break; default: GetChainVertices(shape, chain_id, &vertices); centroid += S2::GetCentroid(S2PointLoopSpan(vertices)); break; } } return centroid; } void GetChainVertices(const S2Shape& shape, int chain_id, std::vector* vertices) { S2Shape::Chain chain = shape.chain(chain_id); int num_vertices = chain.length + (shape.dimension() == 1); vertices->clear(); vertices->reserve(num_vertices); int e = 0; if (num_vertices & 1) { vertices->push_back(shape.chain_edge(chain_id, e++).v0); } for (; e < num_vertices; e += 2) { auto edge = shape.chain_edge(chain_id, e); vertices->push_back(edge.v0); vertices->push_back(edge.v1); } } } // namespace S2 s2/src/s2/s2error.h0000644000176200001440000001244014122355625013466 0ustar liggesusers// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) // // S2Error is a simple class consisting of an error code and a human-readable // error message. #ifndef S2_S2ERROR_H_ #define S2_S2ERROR_H_ #include #include #include #include "s2/base/port.h" // This class is intended to be copied by value as desired. It uses // the default copy constructor and assignment operator. class S2Error { public: enum Code { OK = 0, // No error. //////////////////////////////////////////////////////////////////// // Generic errors, not specific to geometric objects: UNKNOWN = 1000, // Unknown error. UNIMPLEMENTED = 1001, // Operation is not implemented. OUT_OF_RANGE = 1002, // Argument is out of range. INVALID_ARGUMENT = 1003, // Invalid argument (other than a range error). FAILED_PRECONDITION = 1004, // Object is not in the required state. INTERNAL = 1005, // An internal invariant has failed. DATA_LOSS = 1006, // Data loss or corruption. RESOURCE_EXHAUSTED = 1007, // A resource has been exhausted. //////////////////////////////////////////////////////////////////// // Error codes in the following range can be defined by clients: USER_DEFINED_START = 1000000, USER_DEFINED_END = 9999999, //////////////////////////////////////////////////////////////////// // Errors that apply to more than one type of geometry: NOT_UNIT_LENGTH = 1, // Vertex is not unit length. DUPLICATE_VERTICES = 2, // There are two identical vertices. ANTIPODAL_VERTICES = 3, // There are two antipodal vertices. //////////////////////////////////////////////////////////////////// // S2Loop errors: LOOP_NOT_ENOUGH_VERTICES = 100, // Loop with fewer than 3 vertices. LOOP_SELF_INTERSECTION = 101, // Loop has a self-intersection. //////////////////////////////////////////////////////////////////// // S2Polygon errors: POLYGON_LOOPS_SHARE_EDGE = 200, // Two polygon loops share an edge. POLYGON_LOOPS_CROSS = 201, // Two polygon loops cross. POLYGON_EMPTY_LOOP = 202, // Polygon has an empty loop. POLYGON_EXCESS_FULL_LOOP = 203, // Non-full polygon has a full loop. // InitOriented() was called and detected inconsistent loop orientations. POLYGON_INCONSISTENT_LOOP_ORIENTATIONS = 204, // Loop depths don't correspond to any valid nesting hierarchy. POLYGON_INVALID_LOOP_DEPTH = 205, // Actual polygon nesting does not correspond to the nesting hierarchy // encoded by the loop depths. POLYGON_INVALID_LOOP_NESTING = 206, //////////////////////////////////////////////////////////////////// // S2Builder errors: // The S2Builder snap function moved a vertex by more than the specified // snap radius. BUILDER_SNAP_RADIUS_TOO_SMALL = 300, // S2Builder expected all edges to have siblings (as specified by // S2Builder::GraphOptions::SiblingPairs::REQUIRE), but some were missing. BUILDER_MISSING_EXPECTED_SIBLING_EDGES = 301, // S2Builder found an unexpected degenerate edge. For example, // Graph::GetLeftTurnMap() does not support degenerate edges. BUILDER_UNEXPECTED_DEGENERATE_EDGE = 302, // S2Builder found a vertex with (indegree != outdegree), which means // that the given edges cannot be assembled into loops. BUILDER_EDGES_DO_NOT_FORM_LOOPS = 303, // The edges provided to S2Builder cannot be assembled into a polyline. BUILDER_EDGES_DO_NOT_FORM_POLYLINE = 304, // There was an attempt to assemble a polygon from degenerate geometry // without having specified a predicate to decide whether the output is // the empty polygon (containing no points) or the full polygon // (containing all points). BUILDER_IS_FULL_PREDICATE_NOT_SPECIFIED = 305, }; S2Error() : code_(OK), text_() {} // Set the error to the given code and printf-style message. Note that you // can prepend text to an existing error by calling Init() more than once: // // error->Init(error->code(), "Loop %d: %s", j, error->text().c_str()); void Init(Code code, const char* format, ...) ABSL_PRINTF_ATTRIBUTE(3, 4); bool ok() const { return code_ == OK; } Code code() const { return code_; } std::string text() const { return text_; } // Clear the error to contain the OK code and no error message. void Clear(); private: Code code_; std::string text_; }; ////////////////// Implementation details follow //////////////////// inline std::ostream& operator<<(std::ostream& os, const S2Error& error) { return os << error.text(); } inline void S2Error::Clear() { code_ = OK; text_.clear(); } #endif // S2_S2ERROR_H_ s2/src/s2/s2shapeutil_get_reference_point.h0000644000176200001440000000407114122355625020422 0ustar liggesusers// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_S2SHAPEUTIL_GET_REFERENCE_POINT_H_ #define S2_S2SHAPEUTIL_GET_REFERENCE_POINT_H_ #include "s2/s2shape_index.h" namespace s2shapeutil { // This is a helper function for implementing S2Shape::GetReferencePoint(). // // Given a shape consisting of closed polygonal loops, the interior of the // shape is defined as the region to the left of all edges (which must be // oriented consistently). This function then chooses an arbitrary point and // returns true if that point is contained by the shape. // // Unlike S2Loop and S2Polygon, this method allows duplicate vertices and // edges, which requires some extra care with definitions. The rule that we // apply is that an edge and its reverse edge "cancel" each other: the result // is the same as if that edge pair were not present. Therefore shapes that // consist only of degenerate loop(s) are either empty or full; by convention, // the shape is considered full if and only if it contains an empty loop (see // S2LaxPolygonShape for details). // // Determining whether a loop on the sphere contains a point is harder than // the corresponding problem in 2D plane geometry. It cannot be implemented // just by counting edge crossings because there is no such thing as a "point // at infinity" that is guaranteed to be outside the loop. S2Shape::ReferencePoint GetReferencePoint(const S2Shape& shape); } // namespace s2shapeutil #endif // S2_S2SHAPEUTIL_GET_REFERENCE_POINT_H_ s2/src/s2/s2edge_crosser.cc0000644000176200001440000000716113774601521015144 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2edge_crosser.h" #include "s2/base/logging.h" #include "s2/s2pointutil.h" #include "s2/s2predicates.h" int S2EdgeCrosser::CrossingSignInternal(const S2Point* d) { // Compute the actual result, and then save the current vertex D as the next // vertex C, and save the orientation of the next triangle ACB (which is // opposite to the current triangle BDA). int result = CrossingSignInternal2(*d); c_ = d; acb_ = -bda_; return result; } inline int S2EdgeCrosser::CrossingSignInternal2(const S2Point& d) { // At this point, a very common situation is that A,B,C,D are four points on // a line such that AB does not overlap CD. (For example, this happens when // a line or curve is sampled finely, or when geometry is constructed by // computing the union of S2CellIds.) Most of the time, we can determine // that AB and CD do not intersect by computing the two outward-facing // tangents at A and B (parallel to AB) and testing whether AB and CD are on // opposite sides of the plane perpendicular to one of these tangents. This // is moderately expensive but still much cheaper than s2pred::ExpensiveSign. if (!have_tangents_) { S2Point norm = S2::RobustCrossProd(*a_, *b_).Normalize(); a_tangent_ = a_->CrossProd(norm); b_tangent_ = norm.CrossProd(*b_); have_tangents_ = true; } // The error in RobustCrossProd() is insignificant. The maximum error in // the call to CrossProd() (i.e., the maximum norm of the error vector) is // (0.5 + 1/sqrt(3)) * DBL_EPSILON. The maximum error in each call to // DotProd() below is DBL_EPSILON. (There is also a small relative error // term that is insignificant because we are comparing the result against a // constant that is very close to zero.) static const double kError = (1.5 + 1/sqrt(3.0)) * DBL_EPSILON; if ((c_->DotProd(a_tangent_) > kError && d.DotProd(a_tangent_) > kError) || (c_->DotProd(b_tangent_) > kError && d.DotProd(b_tangent_) > kError)) { return -1; } // Otherwise, eliminate the cases where two vertices from different edges // are equal. (These cases could be handled in the code below, but we would // rather avoid calling ExpensiveSign whenever possible.) if (*a_ == *c_ || *a_ == d || *b_ == *c_ || *b_ == d) return 0; // Eliminate cases where an input edge is degenerate. (Note that in most // cases, if CD is degenerate then this method is not even called because // acb_ and bda have different signs.) if (*a_ == *b_ || *c_ == d) return -1; // Otherwise it's time to break out the big guns. if (acb_ == 0) acb_ = -s2pred::ExpensiveSign(*a_, *b_, *c_); S2_DCHECK_NE(acb_, 0); if (bda_ == 0) bda_ = s2pred::ExpensiveSign(*a_, *b_, d); S2_DCHECK_NE(bda_, 0); if (bda_ != acb_) return -1; Vector3_d c_cross_d = c_->CrossProd(d); int cbd = -s2pred::Sign(*c_, d, *b_, c_cross_d); S2_DCHECK_NE(cbd, 0); if (cbd != acb_) return -1; int dac = s2pred::Sign(*c_, d, *a_, c_cross_d); S2_DCHECK_NE(dac, 0); return (dac != acb_) ? -1 : 1; } s2/src/s2/s2polyline_alignment.h0000644000176200001440000002735114122355625016235 0ustar liggesusers// Copyright 2017 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef S2_S2POLYLINE_ALIGNMENT_H_ #define S2_S2POLYLINE_ALIGNMENT_H_ #include #include #include "s2/s2polyline.h" // This library provides code to compute vertex alignments between S2Polylines. // // A vertex "alignment" or "warp" between two polylines is a matching between // pairs of their vertices. Users can imagine pairing each vertex from // S2Polyline `a` with at least one other vertex in S2Polyline `b`. The "cost" // of an arbitrary alignment is defined as the summed value of the squared // chordal distance between each pair of points in the warp path. An "optimal // alignment" for a pair of polylines is defined as the alignment with least // cost. Note: optimal alignments are not necessarily unique. The standard way // of computing an optimal alignment between two sequences is the use of the // `Dynamic Timewarp` algorithm. // // We provide three methods for computing (via Dynamic Timewarp) the optimal // alignment between two S2Polylines. These methods are performance-sensitive, // and have been reasonably optimized for space- and time- usage. On modern // hardware, it is possible to compute exact alignments between 4096x4096 // element polylines in ~70ms, and approximate alignments much more quickly. // // The results of an alignment operation are captured in a VertexAlignment // object. In particular, a VertexAlignment keeps track of the total cost of // alignment, as well as the warp path (a sequence of pairs of indices into each // polyline whose vertices are linked together in the optimal alignment) // // For a worked example, consider the polylines // // a = [(1, 0), (5, 0), (6, 0), (9, 0)] and // b = [(2, 0), (7, 0), (8, 0)]. // // The "cost matrix" between these two polylines (using squared chordal // distance, .Norm2(), as our distance function) looks like this: // // (2, 0) (7, 0) (8, 0) // (1, 0) 1 36 49 // (5, 0) 9 4 9 // (6, 0) 16 1 4 // (9, 0) 49 4 1 // // The Dynamic Timewarp DP table for this cost matrix has cells defined by // // table[i][j] = cost(i,j) + min(table[i-1][j-1], table[i][j-1], table[i-1, j]) // // (2, 0) (7, 0) (8, 0) // (1, 0) 1 37 86 // (5, 0) 10 5 14 // (6, 0) 26 6 9 // (9, 0) 75 10 7 // // Starting at the bottom right corner of the DP table, we can work our way // backwards to the upper left corner to recover the reverse of the warp path: // (3, 2) -> (2, 1) -> (1, 1) -> (0, 0). The VertexAlignment produced containing // this has alignment_cost = 7 and warp_path = {(0, 0), (1, 1), (2, 1), (3, 2)}. // // We also provide methods for performing alignment of multiple sequences. These // methods return a single, representative polyline from a non-empty collection // of polylines, for various definitions of "representative." // // GetMedoidPolyline() returns a new polyline (point-for-point-equal to some // existing polyline from the collection) that minimizes the summed vertex // alignment cost to all other polylines in the collection. // // GetConsensusPolyline() returns a new polyline (unlikely to be present in the // input collection) that represents a "weighted consensus" polyline. This // polyline is constructed iteratively using the Dynamic Timewarp Barycenter // Averaging algorithm of F. Petitjean, A. Ketterlin, and P. Gancarski, which // can be found here: // https://pdfs.semanticscholar.org/a596/8ca9488199291ffe5473643142862293d69d.pdf namespace s2polyline_alignment { typedef std::vector> WarpPath; struct VertexAlignment { // `alignment_cost` represents the sum of the squared chordal distances // between each pair of vertices in the warp path. Specifically, // cost = sum_{(i, j) \in path} (a.vertex(i) - b.vertex(j)).Norm2(); // This means that the units of alignment_cost are "squared distance". This is // an optimization to avoid the (expensive) atan computation of the true // spherical angular distance between the points, as well as an unnecessary // square root. All we need to compute vertex alignment is a metric that // satisifies the triangle inequality, and squared chordal distance works as // well as spherical S1Angle distance for this purpose. double alignment_cost; // Each entry (i, j) of `warp_path` represents a pairing between vertex // a.vertex(i) and vertex b.vertex(j) in the optimal alignment. // The warp_path is defined in forward order, such that the result of // aligning polylines `a` and `b` is always a warp_path with warp_path.front() // = {0,0} and warp_path.back() = {a.num_vertices() - 1, b.num_vertices() - 1} // Note that this DOES NOT define an alignment from a point sequence to an // edge sequence. That functionality may come at a later date. WarpPath warp_path; VertexAlignment(const double cost, const WarpPath& path) : alignment_cost(cost), warp_path(path) {} }; // GetExactVertexAlignment takes two non-empty polylines as input, and returns // the VertexAlignment corresponding to the optimal alignment between them. This // method is quadratic O(A*B) in both space and time complexity. VertexAlignment GetExactVertexAlignment(const S2Polyline& a, const S2Polyline& b); // GetExactVertexAlignmentCost takes two non-empty polylines as input, and // returns the *cost* of their optimal alignment. A standard, traditional // dynamic timewarp algorithm can output both a warp path and a cost, but // requires quadratic space to reconstruct the path by walking back through the // Dynamic Programming cost table. If all you really need is the warp cost (i.e. // you're inducing a similarity metric between S2Polylines, or something // equivalent), you can overwrite the DP table and use constant space - // O(max(A,B)). This method provides that space-efficiency optimization. double GetExactVertexAlignmentCost(const S2Polyline& a, const S2Polyline& b); // GetApproxVertexAlignment takes two non-empty polylines `a` and `b` as input, // and a `radius` paramater GetApproxVertexAlignment (quickly) computes an // approximately optimal vertex alignment of points between polylines `a` and // `b` by implementing the algorithm described in `FastDTW: Toward Accurate // Dynamic Time Warping in Linear Time and Space` by Stan Salvador and Philip // Chan. Details can be found below: // // https://pdfs.semanticscholar.org/05a2/0cde15e172fc82f32774dd0cf4fe5827cad2.pdf // // The `radius` parameter controls the distance we search outside of the // projected warp path during the refining step. Smaller values of `radius` // correspond to a smaller search window, and therefore distance computation on // fewer cells, which leads to a faster (but worse) approximation. // This method is O(max(A, B)) in both space and time complexity. VertexAlignment GetApproxVertexAlignment(const S2Polyline& a, const S2Polyline& b, const int radius); // A convience overload for GetApproxVertexAlignment which computes and uses // suggested default parameter of radius = max(a.size(), b.size())^0.25 VertexAlignment GetApproxVertexAlignment(const S2Polyline& a, const S2Polyline& b); // GetMedoidPolyline returns the index `p` of a "medoid" polyline from a // non-empty collection of `polylines` such that // // sum_{all j in `polylines`} VertexAlignmentCost(p, j) is minimized. // // In the case of a tie for minimal summed alignment cost, we return the lowest // index - this tie is guaranteed to happen in the two-polyline-input case. // // ASYMPTOTIC BEHAVIOR: // Computation may require up to (N^2 - N) / 2 alignment cost function // evaluations, for N input polylines. For polylines of length U, V, the // alignment cost function evaluation is O(U+V) if options.approx = true and // O(U*V) if options.approx = false. class MedoidOptions { public: // If options.approx = false, we compute vertex alignment costs exactly. // If options.approx = true, we use approximate vertex alignment // computation, called with the default radius parameter. bool approx() const { return approx_; } void set_approx(bool approx) { approx_ = approx; } private: bool approx_ = true; }; int GetMedoidPolyline(const std::vector>& polylines, const MedoidOptions options); // GetConsensusPolyline allocates and returns a new "consensus" polyline from a // non-empty collection of polylines. We iteratively apply Dynamic Timewarp // Barycenter Averaging to an initial `seed` polyline, which improves the // consensus alignment quality each iteration. For implementation details, see // // https://pdfs.semanticscholar.org/a596/8ca9488199291ffe5473643142862293d69d.pdf // // The returned polyline from this method is unlikely to be point-for-point // equal to an input polyline, whereas a polyline returned from // GetMedoidPolyline() is guaranteed to match an input polyline point-for-point. // NOTE: the number of points in our returned consensus polyline is always equal // to the number of points in the initial seed, which is implementation-defined. // If the collection of polylines has a large resolution distribution, it might // be a good idea to reinterpolate them to have about the same number of points. // In practice, this doesn't seem to matter, but is probably worth noting. // // ASYMPTOTIC BEHAVIOR: // Seeding this algorithm requires O(1) vertex alignments if seed_medoid = // false, and O(N^2) vertex alignments if seed_medoid = true. Once the seed // polyline is chosen, computing the consensus polyline requires at most // (iteration_cap)*N vertex alignments. For polylines of length U, V, the // alignment cost function evaluation is O(U+V) if options.approx = true, and // O(U*V) if options.approx = false. class ConsensusOptions { public: // If options.approx = false, vertex alignments are computed with // GetExactVertexAlignment. If options.approx = true, vertex alignments are // computed with GetApproxVertexAlignment, called with default radius // parameter. bool approx() const { return approx_; } void set_approx(bool approx) { approx_ = approx; } // If options.seed_medoid = true, we seed the consensus polyline with the // medoid of the collection. This is a more expensive approach, but may result // in higher quality consensus sequences by avoiding bad arbitrary initial // seeds. Seeding with the medoid will incur up to (N^2 - N) / 2 evaluations // of the vertex alignment function. If options.seed_medoid = false, we seed // the consensus polyline by taking an arbitrary element from the collection. bool seed_medoid() const { return seed_medoid_; } void set_seed_medoid(bool seed_medoid) { seed_medoid_ = seed_medoid; } // options.iteration_cap controls the maximum number of DBA refining steps we // apply to the initial seed. int iteration_cap() const { return iteration_cap_; } void set_iteration_cap(int iteration_cap) { iteration_cap_ = iteration_cap; } private: bool approx_ = true; bool seed_medoid_ = false; int iteration_cap_ = 5; }; std::unique_ptr GetConsensusPolyline( const std::vector>& polylines, const ConsensusOptions options); } // namespace s2polyline_alignment #endif // S2_S2POLYLINE_ALIGNMENT_H_ s2/src/s2/encoded_s2point_vector.h0000644000176200001440000001204314122355625016530 0ustar liggesusers// Copyright 2018 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_ENCODED_S2POINT_VECTOR_H_ #define S2_ENCODED_S2POINT_VECTOR_H_ #include #include "absl/types/span.h" #include "s2/encoded_string_vector.h" #include "s2/encoded_uint_vector.h" #include "s2/s2point.h" namespace s2coding { // Controls whether to optimize for speed or size when encoding points. (Note // that encoding is always lossless, and that currently compact encodings are // only possible when points have been snapped to S2CellId centers.) enum class CodingHint : uint8 { FAST, COMPACT }; // Encodes a vector of S2Points in a format that can later be decoded as an // EncodedS2PointVector. // // REQUIRES: "encoder" uses the default constructor, so that its buffer // can be enlarged as necessary by calling Ensure(int). void EncodeS2PointVector(absl::Span points, CodingHint hint, Encoder* encoder); // This class represents an encoded vector of S2Points. Values are decoded // only when they are accessed. This allows for very fast initialization and // no additional memory use beyond the encoded data. The encoded data is not // owned by this class; typically it points into a large contiguous buffer // that contains other encoded data as well. // // This is one of several helper classes that allow complex data structures to // be initialized from an encoded format in constant time and then decoded on // demand. This can be a big performance advantage when only a small part of // the data structure is actually used. class EncodedS2PointVector { public: // Constructs an uninitialized object; requires Init() to be called. EncodedS2PointVector() {} // Initializes the EncodedS2PointVector. // // REQUIRES: The Decoder data buffer must outlive this object. bool Init(Decoder* decoder); // Returns the size of the original vector. size_t size() const; // Returns the element at the given index. S2Point operator[](int i) const; // Decodes and returns the entire original vector. std::vector Decode() const; // TODO(ericv): Consider adding a method that returns an adjacent pair of // points. This would save some decoding overhead. private: friend void EncodeS2PointVector(absl::Span, CodingHint, Encoder*); friend void EncodeS2PointVectorFast(absl::Span, Encoder*); friend void EncodeS2PointVectorCompact(absl::Span, Encoder*); bool InitUncompressedFormat(Decoder* decoder); bool InitCellIdsFormat(Decoder* decoder); S2Point DecodeCellIdsFormat(int i) const; // We use a tagged union to represent multiple formats, as opposed to an // abstract base class or templating. This represents the best compromise // between performance, space, and convenience. Note that the overhead of // checking the tag is trivial and will typically be branch-predicted // perfectly. // // TODO(ericv): Once additional formats have been implemented, consider // using std::variant<> instead. It's unclear whether this would have // better or worse performance than the current approach. // dd: These structs are anonymous in the upstream S2 code; however, // this generates CMD-check failure due to the [-Wnested-anon-types] // (anonymous types declared in an anonymous union are an extension) // The approach here just names the types. struct CellIDStruct { EncodedStringVector blocks; uint64 base; uint8 level; bool have_exceptions; // TODO(ericv): Use std::atomic_flag to cache the last point decoded in // a thread-safe way. This reduces benchmark times for actual polygon // operations (e.g. S2ClosestEdgeQuery) by about 15%. }; struct UncompressedStruct { const S2Point* points; }; enum Format : uint8 { UNCOMPRESSED = 0, CELL_IDS = 1, }; Format format_; uint32 size_; union { struct UncompressedStruct uncompressed_; struct CellIDStruct cell_ids_; }; }; ////////////////// Implementation details follow //////////////////// inline size_t EncodedS2PointVector::size() const { return size_; } inline S2Point EncodedS2PointVector::operator[](int i) const { switch (format_) { case Format::UNCOMPRESSED: return uncompressed_.points[i]; case Format::CELL_IDS: return DecodeCellIdsFormat(i); default: S2_LOG(DFATAL) << "Unrecognized format"; return S2Point(); } } } // namespace s2coding #endif // S2_ENCODED_S2POINT_VECTOR_H_ s2/src/s2/s2polyline_simplifier.h0000644000176200001440000001110114122355625016404 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) // // This is a helper class for simplifying polylines. It allows you to compute // a maximal edge that intersects a sequence of discs, and that optionally // avoids a different sequence of discs. The results are conservative in that // the edge is guaranteed to intersect or avoid the specified discs using // exact arithmetic (see s2predicates.h). // // Note that S2Builder can also simplify polylines and supports more features // (e.g., snapping to S2CellId centers), so it is only recommended to use this // class if S2Builder does not meet your needs. // // Here is a simple example showing how to simplify a polyline into a sequence // of edges that stay within "max_error" of the original edges: // // vector v = { ... }; // S2PolylineSimplifier simplifier; // simplifier.Init(v[0]); // for (int i = 1; i < v.size(); ++i) { // if (!simplifier.Extend(v[i])) { // OutputEdge(simplifier.src(), v[i-1]); // simplifier.Init(v[i-1]); // } // simplifier.TargetDisc(v[i], max_error); // } // OutputEdge(simplifer.src(), v.back()); // // Note that the points targeted by TargetDisc do not need to be the same as // the candidate endpoints passed to Extend. So for example, you could target // the original vertices of a polyline, but only consider endpoints that are // snapped to E7 coordinates or S2CellId centers. // // Please be aware that this class works by maintaining a range of acceptable // angles (bearings) from the start vertex to the hypothetical destination // vertex. It does not keep track of distances to any of the discs to be // targeted or avoided. Therefore to use this class correctly, constraints // should be added in increasing order of distance. (The actual requirement // is slightly weaker than this, which is why it is not enforced, but // basically you should only call TargetDisc() and AvoidDisc() with arguments // that you want to constrain the immediately following call to Extend().) #ifndef S2_S2POLYLINE_SIMPLIFIER_H_ #define S2_S2POLYLINE_SIMPLIFIER_H_ #include "s2/_fp_contract_off.h" #include "s2/s1chord_angle.h" #include "s2/s1interval.h" class S2PolylineSimplifier { public: S2PolylineSimplifier() {} // Starts a new simplified edge at "src". void Init(const S2Point& src); // Returns the source vertex of the output edge. S2Point src() const; // Returns true if the edge (src, dst) satisfies all of the targeting // requirements so far. Returns false if the edge would be longer than // 90 degrees (such edges are not supported). bool Extend(const S2Point& dst) const; // Requires that the output edge must pass through the given disc. bool TargetDisc(const S2Point& point, S1ChordAngle radius); // Requires that the output edge must avoid the given disc. "disc_on_left" // specifies whether the disc must be to the left or right of the edge. // (This feature allows the simplified edge to preserve the topology of the // original polyline with respect to other nearby points.) // // If your input is a polyline, you can compute "disc_on_left" as follows. // Let the polyline be ABCDE and assume that it already avoids a set of // points X_i. Suppose that you have aleady added ABC to the simplifer, and // now want to extend the edge chain to D. First find the X_i that are near // the edge CD, then discard the ones such that AX_i <= AC or AX_i >= AD // (since these points have either already been considered or aren't // relevant yet). Now X_i is to the left of the polyline if and only if // s2pred::OrderedCCW(A, D, X, C) (in other words, if X_i is to the left of // the angle wedge ACD). bool AvoidDisc(const S2Point& point, S1ChordAngle radius, bool disc_on_left); private: double GetAngle(const S2Point& p) const; double GetSemiwidth(const S2Point& p, S1ChordAngle r, int round_direction) const; S2Point src_; S2Point x_dir_, y_dir_; S1Interval window_; }; #endif // S2_S2POLYLINE_SIMPLIFIER_H_ s2/src/s2/s2max_distance_targets.h0000644000176200001440000002062314122355625016527 0ustar liggesusers// Copyright Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // This file defines a collection of classes that are useful for computing // maximum distances on the sphere. Their purpose is to allow code to be // shared among the various query classes that find remote geometry, such as // S2FurthestPointQuery and S2FurthestEdgeQuery. #ifndef S2_S2MAX_DISTANCE_TARGETS_H_ #define S2_S2MAX_DISTANCE_TARGETS_H_ #include #include "s2/_fp_contract_off.h" #include "s2/s1angle.h" #include "s2/s1chord_angle.h" #include "s2/s2cell.h" #include "s2/s2distance_target.h" #include "s2/s2edge_distances.h" #include "s2/s2shape_index.h" class S2FurthestEdgeQuery; // S2MaxDistance is a class that allows maximum distances to be computed using // a minimum distance algorithm. Specifically, S2MaxDistance(x) represents the // supplementary distance (Pi - x). This has the effect of inverting the sort // order, i.e. // // (S2MaxDistance(x) < S2MaxDistance(y)) <=> (Pi - x < Pi - y) <=> (x > y) // // All other operations are implemented similarly (using the supplementary // distance Pi - x). For example, S2MaxDistance(x) - S2MaxDistance(y) == // S2MaxDistance(x + y). class S2MaxDistance { public: using Delta = S1ChordAngle; S2MaxDistance() : distance_() {} explicit S2MaxDistance(S1ChordAngle x) : distance_(x) {} explicit operator S1ChordAngle() const { return distance_; } static S2MaxDistance Zero(); static S2MaxDistance Infinity(); static S2MaxDistance Negative(); friend bool operator==(S2MaxDistance x, S2MaxDistance y); friend bool operator<(S2MaxDistance x, S2MaxDistance y); friend S2MaxDistance operator-(S2MaxDistance x, S1ChordAngle delta); S1ChordAngle GetChordAngleBound() const; // If (dist < *this), updates *this and returns true (used internally). bool UpdateMin(const S2MaxDistance& dist); private: S1ChordAngle distance_; }; // S2MaxDistanceTarget represents a geometric object to which maximum distances // on the sphere are measured. // // Subtypes are defined below for measuring the distance to a point, an edge, // an S2Cell, or an S2ShapeIndex (an arbitrary collection of geometry). using S2MaxDistanceTarget = S2DistanceTarget; // An S2DistanceTarget subtype for computing the maximum distance to a point. class S2MaxDistancePointTarget : public S2MaxDistanceTarget { public: explicit S2MaxDistancePointTarget(const S2Point& point); S2Cap GetCapBound() final; bool UpdateMinDistance(const S2Point& p, S2MaxDistance* min_dist) final; bool UpdateMinDistance(const S2Point& v0, const S2Point& v1, S2MaxDistance* min_dist) final; bool UpdateMinDistance(const S2Cell& cell, S2MaxDistance* min_dist) final; bool VisitContainingShapes(const S2ShapeIndex& index, const ShapeVisitor& visitor) final; private: S2Point point_; }; // An S2DistanceTarget subtype for computing the maximum distance to an edge. class S2MaxDistanceEdgeTarget : public S2MaxDistanceTarget { public: explicit S2MaxDistanceEdgeTarget(const S2Point& a, const S2Point& b); S2Cap GetCapBound() final; bool UpdateMinDistance(const S2Point& p, S2MaxDistance* min_dist) final; bool UpdateMinDistance(const S2Point& v0, const S2Point& v1, S2MaxDistance* min_dist) final; bool UpdateMinDistance(const S2Cell& cell, S2MaxDistance* min_dist) final; bool VisitContainingShapes(const S2ShapeIndex& index, const ShapeVisitor& visitor) final; private: S2Point a_, b_; }; // An S2DistanceTarget subtype for computing the maximum distance to an S2Cell // (including the interior of the cell). class S2MaxDistanceCellTarget : public S2MaxDistanceTarget { public: explicit S2MaxDistanceCellTarget(const S2Cell& cell); S2Cap GetCapBound() final; bool UpdateMinDistance(const S2Point& p, S2MaxDistance* min_dist) final; bool UpdateMinDistance(const S2Point& v0, const S2Point& v1, S2MaxDistance* min_dist) final; bool UpdateMinDistance(const S2Cell& cell, S2MaxDistance* min_dist) final; bool VisitContainingShapes(const S2ShapeIndex& index, const ShapeVisitor& visitor) final; private: S2Cell cell_; }; // An S2DistanceTarget subtype for computing the maximum distance to an // S2ShapeIndex (a collection of points, polylines, and/or polygons). // // Note that ShapeIndexTarget has its own options: // // include_interiors() // - specifies that distances are measured to the boundary and interior // of polygons in the S2ShapeIndex. (If set to false, distance is // measured to the polygon boundary only.) // DEFAULT: true. // // brute_force() // - specifies that the distances should be computed by examining every // edge in the S2ShapeIndex (for testing and debugging purposes). // DEFAULT: false. // // These options are specified independently of the corresponding // S2FurthestEdgeQuery options. For example, if include_interiors is true for // a ShapeIndexTarget but false for the S2FurthestEdgeQuery where the target // is used, then distances will be measured from the boundary of one // S2ShapeIndex to the boundary and interior of the other. // class S2MaxDistanceShapeIndexTarget : public S2MaxDistanceTarget { public: explicit S2MaxDistanceShapeIndexTarget(const S2ShapeIndex* index); ~S2MaxDistanceShapeIndexTarget() override; // Specifies that distance will be measured to the boundary and interior // of polygons in the S2ShapeIndex rather than to polygon boundaries only. // // DEFAULT: true bool include_interiors() const; void set_include_interiors(bool include_interiors); // Specifies that the distances should be computed by examining every edge // in the S2ShapeIndex (for testing and debugging purposes). // // DEFAULT: false bool use_brute_force() const; void set_use_brute_force(bool use_brute_force); bool set_max_error(const S1ChordAngle& max_error) override; S2Cap GetCapBound() final; bool UpdateMinDistance(const S2Point& p, S2MaxDistance* min_dist) final; bool UpdateMinDistance(const S2Point& v0, const S2Point& v1, S2MaxDistance* min_dist) final; bool UpdateMinDistance(const S2Cell& cell, S2MaxDistance* min_dist) final; bool VisitContainingShapes(const S2ShapeIndex& query_index, const ShapeVisitor& visitor) final; private: const S2ShapeIndex* index_; std::unique_ptr query_; }; ////////////////// Implementation details follow //////////////////// inline S2MaxDistance S2MaxDistance::Zero() { return S2MaxDistance(S1ChordAngle::Straight()); } inline S2MaxDistance S2MaxDistance::Infinity() { return S2MaxDistance(S1ChordAngle::Negative()); } inline S2MaxDistance S2MaxDistance::Negative() { return S2MaxDistance(S1ChordAngle::Infinity()); } inline bool operator==(S2MaxDistance x, S2MaxDistance y) { return x.distance_ == y.distance_; } inline bool operator<(S2MaxDistance x, S2MaxDistance y) { return x.distance_ > y.distance_; } inline S2MaxDistance operator-(S2MaxDistance x, S1ChordAngle delta) { return S2MaxDistance(x.distance_ + delta); } inline S1ChordAngle S2MaxDistance::GetChordAngleBound() const { return S1ChordAngle::Straight() - distance_; } inline bool S2MaxDistance::UpdateMin(const S2MaxDistance& dist) { if (dist < *this) { *this = dist; return true; } return false; } inline S2MaxDistancePointTarget::S2MaxDistancePointTarget(const S2Point& point) : point_(point) { } inline S2MaxDistanceEdgeTarget::S2MaxDistanceEdgeTarget(const S2Point& a, const S2Point& b) : a_(a), b_(b) { a_.Normalize(); b_.Normalize(); } inline S2MaxDistanceCellTarget::S2MaxDistanceCellTarget(const S2Cell& cell) : cell_(cell) { } #endif // S2_S2MAX_DISTANCE_TARGETS_H_ s2/src/s2/s2error.cc0000644000176200001440000000160013774601521013621 0ustar liggesusers// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2error.h" #include "s2/base/stringprintf.h" void S2Error::Init(Code code, const char* format, ...) { code_ = code; text_.clear(); va_list ap; va_start(ap, format); StringAppendV(&text_, format, ap); va_end(ap); } s2/src/s2/s2builderutil_s2point_vector_layer.h0000644000176200001440000000744114122355625021122 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_S2BUILDERUTIL_S2POINT_VECTOR_LAYER_H_ #define S2_S2BUILDERUTIL_S2POINT_VECTOR_LAYER_H_ #include #include #include "s2/base/logging.h" #include "absl/memory/memory.h" #include "s2/id_set_lexicon.h" #include "s2/mutable_s2shape_index.h" #include "s2/s2builder.h" #include "s2/s2builder_graph.h" #include "s2/s2builder_layer.h" #include "s2/s2error.h" #include "s2/s2point_vector_shape.h" namespace s2builderutil { // A layer type that collects degenerate edges as points. // This layer expects all edges to be degenerate. In case of finding // non-degenerate edges it sets S2Error but it still generates the // output with degenerate edges. class S2PointVectorLayer : public S2Builder::Layer { public: class Options { public: using DuplicateEdges = GraphOptions::DuplicateEdges; Options(); explicit Options(DuplicateEdges duplicate_edges); // DEFAULT: DuplicateEdges::MERGE DuplicateEdges duplicate_edges() const; void set_duplicate_edges(DuplicateEdges duplicate_edges); private: DuplicateEdges duplicate_edges_; }; explicit S2PointVectorLayer(std::vector* points, const Options& options = Options()); using LabelSetIds = std::vector; S2PointVectorLayer(std::vector* points, LabelSetIds* label_set_ids, IdSetLexicon* label_set_lexicon, const Options& options = Options()); // Layer interface: GraphOptions graph_options() const override; void Build(const Graph& g, S2Error* error) override; private: std::vector* points_; LabelSetIds* label_set_ids_; IdSetLexicon* label_set_lexicon_; Options options_; }; // Like S2PointVectorLayer, but adds the points to a MutableS2ShapeIndex (if // the point vector is non-empty). class IndexedS2PointVectorLayer : public S2Builder::Layer { public: using Options = S2PointVectorLayer::Options; explicit IndexedS2PointVectorLayer(MutableS2ShapeIndex* index, const Options& options = Options()) : index_(index), layer_(&points_, options) {} GraphOptions graph_options() const override { return layer_.graph_options(); } void Build(const Graph& g, S2Error* error) override { layer_.Build(g, error); if (error->ok() && !points_.empty()) { index_->Add(absl::make_unique(std::move(points_))); } } private: MutableS2ShapeIndex* index_; std::vector points_; S2PointVectorLayer layer_; }; ////////////////// Implementation details follow //////////////////// inline S2PointVectorLayer::Options::Options() : duplicate_edges_(DuplicateEdges::MERGE) {} inline S2PointVectorLayer::Options::Options(DuplicateEdges duplicate_edges) : duplicate_edges_(duplicate_edges) {} inline S2Builder::GraphOptions::DuplicateEdges S2PointVectorLayer::Options::duplicate_edges() const { return duplicate_edges_; } inline void S2PointVectorLayer::Options::set_duplicate_edges( S2Builder::GraphOptions::DuplicateEdges duplicate_edges) { duplicate_edges_ = duplicate_edges; } } // namespace s2builderutil #endif // S2_S2BUILDERUTIL_S2POINT_VECTOR_LAYER_H_ s2/src/s2/s2min_distance_targets.cc0000644000176200001440000002432214122355625016663 0ustar liggesusers// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2min_distance_targets.h" #include #include "absl/memory/memory.h" #include "s2/s1angle.h" #include "s2/s2cap.h" #include "s2/s2cell.h" #include "s2/s2closest_cell_query.h" #include "s2/s2closest_edge_query.h" #include "s2/s2edge_distances.h" #include "s2/s2shape_index_region.h" S2Cap S2MinDistancePointTarget::GetCapBound() { return S2Cap(point_, S1ChordAngle::Zero()); } bool S2MinDistancePointTarget::UpdateMinDistance( const S2Point& p, S2MinDistance* min_dist) { return min_dist->UpdateMin(S2MinDistance(S1ChordAngle(p, point_))); } bool S2MinDistancePointTarget::UpdateMinDistance( const S2Point& v0, const S2Point& v1, S2MinDistance* min_dist) { return S2::UpdateMinDistance(point_, v0, v1, min_dist); } bool S2MinDistancePointTarget::UpdateMinDistance( const S2Cell& cell, S2MinDistance* min_dist) { return min_dist->UpdateMin(S2MinDistance(cell.GetDistance(point_))); } bool S2MinDistancePointTarget::VisitContainingShapes( const S2ShapeIndex& index, const ShapeVisitor& visitor) { return MakeS2ContainsPointQuery(&index).VisitContainingShapes( point_, [this, &visitor](S2Shape* shape) { return visitor(shape, point_); }); } S2Cap S2MinDistanceEdgeTarget::GetCapBound() { // The following computes a radius equal to half the edge length in an // efficient and numerically stable way. double d2 = S1ChordAngle(a_, b_).length2(); double r2 = (0.5 * d2) / (1 + sqrt(1 - 0.25 * d2)); return S2Cap((a_ + b_).Normalize(), S1ChordAngle::FromLength2(r2)); } bool S2MinDistanceEdgeTarget::UpdateMinDistance( const S2Point& p, S2MinDistance* min_dist) { return S2::UpdateMinDistance(p, a_, b_, min_dist); } bool S2MinDistanceEdgeTarget::UpdateMinDistance( const S2Point& v0, const S2Point& v1, S2MinDistance* min_dist) { return S2::UpdateEdgePairMinDistance(a_, b_, v0, v1, min_dist); } bool S2MinDistanceEdgeTarget::UpdateMinDistance( const S2Cell& cell, S2MinDistance* min_dist) { return min_dist->UpdateMin(S2MinDistance(cell.GetDistance(a_, b_))); } bool S2MinDistanceEdgeTarget::VisitContainingShapes( const S2ShapeIndex& index, const ShapeVisitor& visitor) { // We test the center of the edge in order to ensure that edge targets AB // and BA yield identical results (which is not guaranteed by the API but // users might expect). Other options would be to test both endpoints, or // return different results for AB and BA in some cases. S2MinDistancePointTarget target((a_ + b_).Normalize()); return target.VisitContainingShapes(index, visitor); } S2MinDistanceCellTarget::S2MinDistanceCellTarget(const S2Cell& cell) : cell_(cell) { } S2Cap S2MinDistanceCellTarget::GetCapBound() { return cell_.GetCapBound(); } bool S2MinDistanceCellTarget::UpdateMinDistance(const S2Point& p, S2MinDistance* min_dist) { return min_dist->UpdateMin(S2MinDistance(cell_.GetDistance(p))); } bool S2MinDistanceCellTarget::UpdateMinDistance( const S2Point& v0, const S2Point& v1, S2MinDistance* min_dist) { return min_dist->UpdateMin(S2MinDistance(cell_.GetDistance(v0, v1))); } bool S2MinDistanceCellTarget::UpdateMinDistance( const S2Cell& cell, S2MinDistance* min_dist) { return min_dist->UpdateMin(S2MinDistance(cell_.GetDistance(cell))); } bool S2MinDistanceCellTarget::VisitContainingShapes( const S2ShapeIndex& index, const ShapeVisitor& visitor) { // The simplest approach is simply to return the polygons that contain the // cell center. Alternatively, if the index cell is smaller than the target // cell then we could return all polygons that are present in the // S2ShapeIndexCell, but since the index is built conservatively this may // include some polygons that don't quite intersect the cell. So we would // either need to recheck for intersection more accurately, or weaken the // VisitContainingShapes contract so that it only guarantees approximate // intersection, neither of which seems like a good tradeoff. S2MinDistancePointTarget target(cell_.GetCenter()); return target.VisitContainingShapes(index, visitor); } S2MinDistanceCellUnionTarget::S2MinDistanceCellUnionTarget( S2CellUnion cell_union) : cell_union_(std::move(cell_union)), query_(absl::make_unique(&index_)) { for (S2CellId cell_id : cell_union_) { index_.Add(cell_id, 0); } index_.Build(); } S2MinDistanceCellUnionTarget::~S2MinDistanceCellUnionTarget() { } bool S2MinDistanceCellUnionTarget::use_brute_force() const { return query_->options().use_brute_force(); } void S2MinDistanceCellUnionTarget::set_use_brute_force( bool use_brute_force) { query_->mutable_options()->set_use_brute_force(use_brute_force); } bool S2MinDistanceCellUnionTarget::set_max_error( const S1ChordAngle& max_error) { query_->mutable_options()->set_max_error(max_error); return true; // Indicates that we may return suboptimal results. } S2Cap S2MinDistanceCellUnionTarget::GetCapBound() { return cell_union_.GetCapBound(); } inline bool S2MinDistanceCellUnionTarget::UpdateMinDistance( S2MinDistanceTarget* target, S2MinDistance* min_dist) { query_->mutable_options()->set_max_distance(*min_dist); S2ClosestCellQuery::Result r = query_->FindClosestCell(target); if (r.is_empty()) return false; *min_dist = r.distance(); return true; } bool S2MinDistanceCellUnionTarget::UpdateMinDistance( const S2Point& p, S2MinDistance* min_dist) { S2ClosestCellQuery::PointTarget target(p); return UpdateMinDistance(&target, min_dist); } bool S2MinDistanceCellUnionTarget::UpdateMinDistance( const S2Point& v0, const S2Point& v1, S2MinDistance* min_dist) { S2ClosestCellQuery::EdgeTarget target(v0, v1); return UpdateMinDistance(&target, min_dist); } bool S2MinDistanceCellUnionTarget::UpdateMinDistance( const S2Cell& cell, S2MinDistance* min_dist) { S2ClosestCellQuery::CellTarget target(cell); return UpdateMinDistance(&target, min_dist); } bool S2MinDistanceCellUnionTarget::VisitContainingShapes( const S2ShapeIndex& query_index, const ShapeVisitor& visitor) { for (S2CellId cell_id : cell_union_) { S2MinDistancePointTarget target(cell_id.ToPoint()); if (!target.VisitContainingShapes(query_index, visitor)) { return false; } } return true; } S2MinDistanceShapeIndexTarget::S2MinDistanceShapeIndexTarget( const S2ShapeIndex* index) : index_(index), query_(absl::make_unique(index)) { } S2MinDistanceShapeIndexTarget::~S2MinDistanceShapeIndexTarget() { } bool S2MinDistanceShapeIndexTarget::include_interiors() const { return query_->options().include_interiors(); } void S2MinDistanceShapeIndexTarget::set_include_interiors( bool include_interiors) { query_->mutable_options()->set_include_interiors(include_interiors); } bool S2MinDistanceShapeIndexTarget::use_brute_force() const { return query_->options().use_brute_force(); } void S2MinDistanceShapeIndexTarget::set_use_brute_force( bool use_brute_force) { query_->mutable_options()->set_use_brute_force(use_brute_force); } bool S2MinDistanceShapeIndexTarget::set_max_error( const S1ChordAngle& max_error) { query_->mutable_options()->set_max_error(max_error); return true; // Indicates that we may return suboptimal results. } S2Cap S2MinDistanceShapeIndexTarget::GetCapBound() { return MakeS2ShapeIndexRegion(index_).GetCapBound(); } inline bool S2MinDistanceShapeIndexTarget::UpdateMinDistance( S2MinDistanceTarget* target, S2MinDistance* min_dist) { query_->mutable_options()->set_max_distance(*min_dist); S2ClosestEdgeQuery::Result r = query_->FindClosestEdge(target); if (r.is_empty()) return false; *min_dist = r.distance(); return true; } bool S2MinDistanceShapeIndexTarget::UpdateMinDistance( const S2Point& p, S2MinDistance* min_dist) { S2ClosestEdgeQuery::PointTarget target(p); return UpdateMinDistance(&target, min_dist); } bool S2MinDistanceShapeIndexTarget::UpdateMinDistance( const S2Point& v0, const S2Point& v1, S2MinDistance* min_dist) { S2ClosestEdgeQuery::EdgeTarget target(v0, v1); return UpdateMinDistance(&target, min_dist); } bool S2MinDistanceShapeIndexTarget::UpdateMinDistance( const S2Cell& cell, S2MinDistance* min_dist) { S2ClosestEdgeQuery::CellTarget target(cell); return UpdateMinDistance(&target, min_dist); } bool S2MinDistanceShapeIndexTarget::VisitContainingShapes( const S2ShapeIndex& query_index, const ShapeVisitor& visitor) { // It is sufficient to find the set of chain starts in the target index // (i.e., one vertex per connected component of edges) that are contained by // the query index, except for one special case to handle full polygons. // // TODO(ericv): Do this by merge-joining the two S2ShapeIndexes, and share // the code with S2BooleanOperation. for (S2Shape* shape : *index_) { if (shape == nullptr) continue; int num_chains = shape->num_chains(); // Shapes that don't have any edges require a special case (below). bool tested_point = false; for (int c = 0; c < num_chains; ++c) { S2Shape::Chain chain = shape->chain(c); if (chain.length == 0) continue; tested_point = true; S2Point v0 = shape->chain_edge(c, 0).v0; S2MinDistancePointTarget target(v0); if (!target.VisitContainingShapes(query_index, visitor)) { return false; } } if (!tested_point) { // Special case to handle full polygons. S2Shape::ReferencePoint ref = shape->GetReferencePoint(); if (!ref.contained) continue; S2MinDistancePointTarget target(ref.point); if (!target.VisitContainingShapes(query_index, visitor)) { return false; } } } return true; } s2/src/s2/s2closest_cell_query.cc0000644000176200001440000001155014122355625016374 0ustar liggesusers// Copyright 2018 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2closest_cell_query.h" #include #include "absl/memory/memory.h" #include "s2/s1angle.h" #include "s2/s2cap.h" #include "s2/s2cell.h" #include "s2/s2cell_id.h" #include "s2/s2cell_union.h" #include "s2/s2edge_distances.h" #include "s2/s2region_coverer.h" #include "s2/s2shape_index_region.h" void S2ClosestCellQuery::Options::set_conservative_max_distance( S1ChordAngle max_distance) { set_max_distance(Distance(max_distance.PlusError( S2::GetUpdateMinDistanceMaxError(max_distance)).Successor())); } void S2ClosestCellQuery::Options::set_conservative_max_distance( S1Angle max_distance) { set_conservative_max_distance(S1ChordAngle(max_distance)); } // The thresholds for using the brute force algorithm are generally tuned to // optimize IsDistanceLess (which compares the distance against a threshold) // rather than FindClosest (which actually computes the minimum distance). // This is because the former operation is (1) more common, (2) inherently // faster, and (3) closely related to finding all cells within a given // distance, which is also very common. int S2ClosestCellQuery::PointTarget::max_brute_force_index_size() const { // Break-even points: Point cloud Cap coverings // BM_FindClosest 18 16 // BM_IsDistanceLess 8 9 return 9; } int S2ClosestCellQuery::EdgeTarget::max_brute_force_index_size() const { // Break-even points: Point cloud Cap coverings // BM_FindClosestToLongEdge 14 16 // BM_IsDistanceLessToLongEdge 5 5 return 5; } int S2ClosestCellQuery::CellTarget::max_brute_force_index_size() const { // Break-even points: Point cloud Cap coverings // BM_FindClosestToSmallCell 12 13 // BM_IsDistanceLessToSmallCell 6 6 // // Note that the primary use of CellTarget is to implement CellUnionTarget, // and therefore it is very important to optimize for the case where a // distance limit has been specified. return 6; } int S2ClosestCellQuery::CellUnionTarget::max_brute_force_index_size() const { // Break-even points: Point cloud Cap coverings // BM_FindClosestToSmallCoarseCellUnion 12 10 // BM_IsDistanceLessToSmallCoarseCellUnion 7 6 return 8; } int S2ClosestCellQuery::ShapeIndexTarget::max_brute_force_index_size() const { // Break-even points: Point cloud Cap coverings // BM_FindClosestToSmallCoarseShapeIndex 10 8 // BM_IsDistanceLessToSmallCoarseShapeIndex 7 6 return 7; } S2ClosestCellQuery::S2ClosestCellQuery() { // Prevent inline constructor bloat by defining here. } S2ClosestCellQuery::~S2ClosestCellQuery() { // Prevent inline destructor bloat by defining here. } bool S2ClosestCellQuery::IsDistanceLess(Target* target, S1ChordAngle limit) { static_assert(sizeof(Options) <= 32, "Consider not copying Options here"); Options tmp_options = options_; tmp_options.set_max_results(1); tmp_options.set_max_distance(limit); tmp_options.set_max_error(S1ChordAngle::Straight()); return !base_.FindClosestCell(target, tmp_options).is_empty(); } bool S2ClosestCellQuery::IsDistanceLessOrEqual(Target* target, S1ChordAngle limit) { static_assert(sizeof(Options) <= 32, "Consider not copying Options here"); Options tmp_options = options_; tmp_options.set_max_results(1); tmp_options.set_inclusive_max_distance(limit); tmp_options.set_max_error(S1ChordAngle::Straight()); return !base_.FindClosestCell(target, tmp_options).is_empty(); } bool S2ClosestCellQuery::IsConservativeDistanceLessOrEqual( Target* target, S1ChordAngle limit) { static_assert(sizeof(Options) <= 32, "Consider not copying Options here"); Options tmp_options = options_; tmp_options.set_max_results(1); tmp_options.set_conservative_max_distance(limit); tmp_options.set_max_error(S1ChordAngle::Straight()); return !base_.FindClosestCell(target, tmp_options).is_empty(); } s2/src/s2/s2padded_cell.cc0000644000176200001440000001435013774601521014716 0ustar liggesusers// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2padded_cell.h" #include #include #include "s2/util/bits/bits.h" #include "s2/r1interval.h" #include "s2/s2coords.h" using std::max; using std::min; using S2::internal::kSwapMask; using S2::internal::kInvertMask; using S2::internal::kIJtoPos; using S2::internal::kPosToOrientation; S2PaddedCell::S2PaddedCell(S2CellId id, double padding) : id_(id), padding_(padding) { if (id_.is_face()) { // Fast path for constructing a top-level face (the most common case). double limit = 1 + padding; bound_ = R2Rect(R1Interval(-limit, limit), R1Interval(-limit, limit)); middle_ = R2Rect(R1Interval(-padding, padding), R1Interval(-padding, padding)); ij_lo_[0] = ij_lo_[1] = 0; orientation_ = id_.face() & 1; level_ = 0; } else { int ij[2]; id.ToFaceIJOrientation(&ij[0], &ij[1], &orientation_); level_ = id.level(); bound_ = S2CellId::IJLevelToBoundUV(ij, level_).Expanded(padding); int ij_size = S2CellId::GetSizeIJ(level_); ij_lo_[0] = ij[0] & -ij_size; ij_lo_[1] = ij[1] & -ij_size; } } S2PaddedCell::S2PaddedCell(const S2PaddedCell& parent, int i, int j) : padding_(parent.padding_), bound_(parent.bound_), level_(parent.level_ + 1) { // Compute the position and orientation of the child incrementally from the // orientation of the parent. int pos = kIJtoPos[parent.orientation_][2*i+j]; id_ = parent.id_.child(pos); int ij_size = S2CellId::GetSizeIJ(level_); ij_lo_[0] = parent.ij_lo_[0] + i * ij_size; ij_lo_[1] = parent.ij_lo_[1] + j * ij_size; orientation_ = parent.orientation_ ^ kPosToOrientation[pos]; // For each child, one corner of the bound is taken directly from the parent // while the diagonally opposite corner is taken from middle(). const R2Rect& middle = parent.middle(); bound_[0][1-i] = middle[0][1-i]; bound_[1][1-j] = middle[1][1-j]; } const R2Rect& S2PaddedCell::middle() const { // We compute this field lazily because it is not needed the majority of the // time (i.e., for cells where the recursion terminates). if (middle_.is_empty()) { int ij_size = S2CellId::GetSizeIJ(level_); double u = S2::STtoUV(S2::SiTitoST(2 * ij_lo_[0] + ij_size)); double v = S2::STtoUV(S2::SiTitoST(2 * ij_lo_[1] + ij_size)); middle_ = R2Rect(R1Interval(u - padding_, u + padding_), R1Interval(v - padding_, v + padding_)); } return middle_; } S2Point S2PaddedCell::GetCenter() const { int ij_size = S2CellId::GetSizeIJ(level_); unsigned int si = 2 * ij_lo_[0] + ij_size; unsigned int ti = 2 * ij_lo_[1] + ij_size; return S2::FaceSiTitoXYZ(id_.face(), si, ti).Normalize(); } S2Point S2PaddedCell::GetEntryVertex() const { // The curve enters at the (0,0) vertex unless the axis directions are // reversed, in which case it enters at the (1,1) vertex. unsigned int i = ij_lo_[0]; unsigned int j = ij_lo_[1]; if (orientation_ & kInvertMask) { int ij_size = S2CellId::GetSizeIJ(level_); i += ij_size; j += ij_size; } return S2::FaceSiTitoXYZ(id_.face(), 2 * i, 2 * j).Normalize(); } S2Point S2PaddedCell::GetExitVertex() const { // The curve exits at the (1,0) vertex unless the axes are swapped or // inverted but not both, in which case it exits at the (0,1) vertex. unsigned int i = ij_lo_[0]; unsigned int j = ij_lo_[1]; int ij_size = S2CellId::GetSizeIJ(level_); if (orientation_ == 0 || orientation_ == kSwapMask + kInvertMask) { i += ij_size; } else { j += ij_size; } return S2::FaceSiTitoXYZ(id_.face(), 2 * i, 2 * j).Normalize(); } S2CellId S2PaddedCell::ShrinkToFit(const R2Rect& rect) const { S2_DCHECK(bound().Intersects(rect)); // Quick rejection test: if "rect" contains the center of this cell along // either axis, then no further shrinking is possible. int ij_size = S2CellId::GetSizeIJ(level_); if (level_ == 0) { // Fast path (most calls to this function start with a face cell). if (rect[0].Contains(0) || rect[1].Contains(0)) return id(); } else { if (rect[0].Contains(S2::STtoUV(S2::SiTitoST(2 * ij_lo_[0] + ij_size))) || rect[1].Contains(S2::STtoUV(S2::SiTitoST(2 * ij_lo_[1] + ij_size)))) { return id(); } } // Otherwise we expand "rect" by the given padding() on all sides and find // the range of coordinates that it spans along the i- and j-axes. We then // compute the highest bit position at which the min and max coordinates // differ. This corresponds to the first cell level at which at least two // children intersect "rect". // Increase the padding to compensate for the error in S2::UVtoST(). // (The constant below is a provable upper bound on the additional error.) R2Rect padded = rect.Expanded(padding() + 1.5 * DBL_EPSILON); int ij_min[2]; // Min i- or j- coordinate spanned by "padded" int ij_xor[2]; // XOR of the min and max i- or j-coordinates for (int d = 0; d < 2; ++d) { ij_min[d] = max(ij_lo_[d], S2::STtoIJ(S2::UVtoST(padded[d][0]))); int ij_max = min(ij_lo_[d] + ij_size - 1, S2::STtoIJ(S2::UVtoST(padded[d][1]))); ij_xor[d] = ij_min[d] ^ ij_max; } // Compute the highest bit position where the two i- or j-endpoints differ, // and then choose the cell level that includes both of these endpoints. So // if both pairs of endpoints are equal we choose kMaxLevel; if they differ // only at bit 0, we choose (kMaxLevel - 1), and so on. int level_msb = ((ij_xor[0] | ij_xor[1]) << 1) + 1; int level = S2CellId::kMaxLevel - Bits::FindMSBSetNonZero(level_msb); if (level <= level_) return id(); return S2CellId::FromFaceIJ(id().face(), ij_min[0], ij_min[1]).parent(level); } s2/src/s2/s2shapeutil_build_polygon_boundaries.cc0000644000176200001440000001077014122355625021636 0ustar liggesusers// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2shapeutil_build_polygon_boundaries.h" #include "absl/container/btree_map.h" #include "absl/memory/memory.h" #include "s2/mutable_s2shape_index.h" #include "s2/s2contains_point_query.h" #include "s2/s2shape_index.h" #include "s2/s2shapeutil_contains_brute_force.h" using absl::WrapUnique; using std::vector; namespace s2shapeutil { void BuildPolygonBoundaries(const vector>& components, vector>* polygons) { polygons->clear(); if (components.empty()) return; // Since the loop boundaries do not cross, a loop nesting hierarchy can be // defined by choosing any point on the sphere as the "point at infinity". // Loop A then contains loop B if (1) A contains the boundary of B and (2) // loop A does not contain the point at infinity. // // We choose S2::Origin() for this purpose. The loop nesting hierarchy then // determines the face structure. Here are the details: // // 1. Build an S2ShapeIndex of all loops that do not contain S2::Origin(). // This leaves at most one unindexed loop per connected component // (the "outer loop"). // // 2. For each component, choose a representative vertex and determine // which indexed loops contain it. The "depth" of this component is // defined as the number of such loops. // // 3. Assign the outer loop of each component to the containing loop whose // depth is one less. This generates a set of multi-loop polygons. // // 4. The outer loops of all components at depth 0 become a single face. MutableS2ShapeIndex index; // A map from shape.id() to the corresponding component number. vector component_ids; vector outer_loops; for (int i = 0; i < components.size(); ++i) { const auto& component = components[i]; for (S2Shape* loop : component) { if (component.size() > 1 && !s2shapeutil::ContainsBruteForce(*loop, S2::Origin())) { // Ownership is transferred back at the end of this function. index.Add(WrapUnique(loop)); component_ids.push_back(i); } else { outer_loops.push_back(loop); } } // Check that there is exactly one outer loop in each component. S2_DCHECK_EQ(i + 1, outer_loops.size()) << "Component is not a subdivision"; } // Find the loops containing each component. vector> ancestors(components.size()); auto contains_query = MakeS2ContainsPointQuery(&index); for (int i = 0; i < outer_loops.size(); ++i) { auto loop = outer_loops[i]; S2_DCHECK_GT(loop->num_edges(), 0); ancestors[i] = contains_query.GetContainingShapes(loop->edge(0).v0); } // Assign each outer loop to the component whose depth is one less. // Components at depth 0 become a single face. absl::btree_map> children; for (int i = 0; i < outer_loops.size(); ++i) { S2Shape* ancestor = nullptr; int depth = ancestors[i].size(); if (depth > 0) { for (auto candidate : ancestors[i]) { if (ancestors[component_ids[candidate->id()]].size() == depth - 1) { S2_DCHECK(ancestor == nullptr); ancestor = candidate; } } S2_DCHECK(ancestor != nullptr); } children[ancestor].push_back(outer_loops[i]); } // There is one face per loop that is not an outer loop, plus one for the // outer loops of components at depth 0. polygons->resize(index.num_shape_ids() + 1); for (int i = 0; i < index.num_shape_ids(); ++i) { auto polygon = &(*polygons)[i]; auto loop = index.shape(i); auto itr = children.find(loop); if (itr != children.end()) { *polygon = itr->second; } polygon->push_back(loop); } polygons->back() = children[nullptr]; // Explicitly release the shapes from the index so they are not deleted. for (auto& ptr : index.ReleaseAll()) ptr.release(); } } // namespace s2shapeutil s2/src/s2/s2builderutil_s2polyline_layer.cc0000644000176200001440000000710213774601521020373 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2builderutil_s2polyline_layer.h" #include "s2/s2debug.h" using std::vector; using EdgeType = S2Builder::EdgeType; using Graph = S2Builder::Graph; using GraphOptions = S2Builder::GraphOptions; using Label = S2Builder::Label; using DegenerateEdges = GraphOptions::DegenerateEdges; using DuplicateEdges = GraphOptions::DuplicateEdges; using SiblingPairs = GraphOptions::SiblingPairs; using EdgeId = Graph::EdgeId; using PolylineType = Graph::PolylineType; namespace s2builderutil { S2PolylineLayer::S2PolylineLayer(S2Polyline* polyline, const S2PolylineLayer::Options& options) { Init(polyline, nullptr, nullptr, options); } S2PolylineLayer::S2PolylineLayer( S2Polyline* polyline, LabelSetIds* label_set_ids, IdSetLexicon* label_set_lexicon, const Options& options) { Init(polyline, label_set_ids, label_set_lexicon, options); } void S2PolylineLayer::Init(S2Polyline* polyline, LabelSetIds* label_set_ids, IdSetLexicon* label_set_lexicon, const Options& options) { S2_DCHECK_EQ(label_set_ids == nullptr, label_set_lexicon == nullptr); polyline_ = polyline; label_set_ids_ = label_set_ids; label_set_lexicon_ = label_set_lexicon; options_ = options; if (options_.validate()) { polyline_->set_s2debug_override(S2Debug::DISABLE); } } GraphOptions S2PolylineLayer::graph_options() const { // Remove edges that collapse to a single vertex, but keep duplicate and // sibling edges, since merging duplicates or discarding siblings can make // it impossible to assemble the edges into a single polyline. return GraphOptions(options_.edge_type(), DegenerateEdges::DISCARD, DuplicateEdges::KEEP, SiblingPairs::KEEP); } void S2PolylineLayer::Build(const Graph& g, S2Error* error) { if (g.num_edges() == 0) { polyline_->Init(vector{}); return; } vector edge_polylines = g.GetPolylines(PolylineType::WALK); if (edge_polylines.size() != 1) { error->Init(S2Error::BUILDER_EDGES_DO_NOT_FORM_POLYLINE, "Input edges cannot be assembled into polyline"); return; } const Graph::EdgePolyline& edge_polyline = edge_polylines[0]; vector vertices; // Temporary storage for vertices. vertices.reserve(edge_polyline.size()); vertices.push_back(g.vertex(g.edge(edge_polyline[0]).first)); for (EdgeId e : edge_polyline) { vertices.push_back(g.vertex(g.edge(e).second)); } if (label_set_ids_) { Graph::LabelFetcher fetcher(g, options_.edge_type()); vector(3); // // However, the C++ language defines the set of possible values for an // enumeration to be essentially the range of a bitfield that can represent // all the enumerators, i.e. those within the nearest containing power // of two. In the example above, the nearest positive power of two is 64, // and so the upper bound is 63. The nearest negative power of two is // -32 and so the lower bound is -32 (two's complement), which is upgraded // to match the upper bound, becoming -64. The values within this range // of -64 to 63 are valid, according to the C++ standard. You can cast // values within this range as follows. // // A var = loose_enum_cast(45); // // These casts will log a message if the value does not reside within the // specified range, and will be fatal when in debug mode. // // For those times when an assert too strong, there are test functions. // // bool var = tight_enum_test(3); // bool var = loose_enum_test(45); // // For code that needs to use the enumeration value if and only if // it is good, there is a function that both tests and casts. // // int i = ....; // A var; // if (tight_enum_test_cast(i, &var)) // .... // use valid var with value as indicated by i // else // .... // handle invalid enum cast // // The enum test/cast facility is currently limited to enumerations that // fit within an int. It is also limited to two's complement ints. // ** Implementation Description // // The enum_limits template class captures the minimum and maximum // enumerator. All uses of this template are intended to be of // specializations, so the generic has a field to identify itself as // not specialized. The test/cast templates assert specialization. template class enum_limits { public: static const Enum min_enumerator = 0; static const Enum max_enumerator = 0; static const bool is_specialized = false; }; // Now we define the macro to define the specialization for enum_limits. // The specialization checks that the enumerators fit within an int. // This checking relies on integral promotion. #define MAKE_ENUM_LIMITS(ENUM_TYPE, ENUM_MIN, ENUM_MAX) \ template <> \ class enum_limits { \ public: \ static const ENUM_TYPE min_enumerator = ENUM_MIN; \ static const ENUM_TYPE max_enumerator = ENUM_MAX; \ static const bool is_specialized = true; \ static_assert(ENUM_MIN >= INT_MIN, "enumerator too negative for int"); \ static_assert(ENUM_MAX <= INT_MAX, "enumerator too positive for int"); \ }; // The loose enum test/cast is actually the more complicated one, // because of the problem of finding the bounds. // // The unary upper bound, ub, on a positive number is its positive // saturation, i.e. for a value v within pow(2,k-1) <= v < pow(2,k), // the upper bound is pow(2,k)-1. // // The unary lower bound, lb, on a negative number is its negative // saturation, i.e. for a value v within -pow(2,k) <= v < -pow(2,k-1), // the lower bound is -pow(2,k). // // The actual bounds are (1) the binary upper bound over the maximum // enumerator and the one's complement of a negative minimum enumerator // and (2) the binary lower bound over the minimum enumerator and the // one's complement of the positive maximum enumerator, except that if no // enumerators are negative, the lower bound is zero. // // The algorithm relies heavily on the observation that // // a,b>0 then ub(a,b) == ub(a) | ub(b) == ub(a|b) // a,b<0 then lb(a,b) == lb(a) & lb(b) == lb(a&b) // // Note that the compiler will boil most of this code away // because of value propagation on the constant enumerator bounds. template inline bool loose_enum_test(int e_val) { static_assert(enum_limits::is_specialized, "missing MAKE_ENUM_LIMITS"); const Enum e_min = enum_limits::min_enumerator; const Enum e_max = enum_limits::max_enumerator; static_assert(sizeof(e_val) == 4 || sizeof(e_val) == 8, "unexpected int size"); // Find the unary bounding negative number of e_min and e_max. // Find the unary bounding negative number of e_max. // This would be b_min = e_max < 0 ? e_max : ~e_max, // but we want to avoid branches to help the compiler. int e_max_sign = e_max >> (sizeof(e_val)*8 - 1); int b_min = ~e_max_sign ^ e_max; // Find the binary bounding negative of both e_min and e_max. b_min &= e_min; // However, if e_min is positive, the result will be positive. // Now clear all bits right of the most significant clear bit, // which is a negative saturation for negative numbers. // In the case of positive numbers, this is flush to zero. b_min &= b_min >> 1; b_min &= b_min >> 2; b_min &= b_min >> 4; b_min &= b_min >> 8; b_min &= b_min >> 16; #if INT_MAX > 2147483647 b_min &= b_min >> 32; #endif // Find the unary bounding positive number of e_max. int b_max = e_max_sign ^ e_max; // Find the binary bounding positive number of that // and the unary bounding positive number of e_min. int e_min_sign = e_min >> (sizeof(e_val)*8 - 1); b_max |= e_min_sign ^ e_min; // Now set all bits right of the most significant set bit, // which is a positive saturation for positive numbers. b_max |= b_max >> 1; b_max |= b_max >> 2; b_max |= b_max >> 4; b_max |= b_max >> 8; b_max |= b_max >> 16; #if INT_MAX > 2147483647 b_max |= b_max >> 32; #endif // Finally test the bounds. return b_min <= e_val && e_val <= b_max; } template inline bool tight_enum_test(int e_val) { static_assert(enum_limits::is_specialized, "missing MAKE_ENUM_LIMITS"); const Enum e_min = enum_limits::min_enumerator; const Enum e_max = enum_limits::max_enumerator; return e_min <= e_val && e_val <= e_max; } template inline bool loose_enum_test_cast(int e_val, Enum* e_var) { if (loose_enum_test(e_val)) { *e_var = static_cast(e_val); return true; } else { return false; } } template inline bool tight_enum_test_cast(int e_val, Enum* e_var) { if (tight_enum_test(e_val)) { *e_var = static_cast(e_val); return true; } else { return false; } } // The plain casts require logging, and we get header recursion if // it is done directly. So, we do it indirectly. // The following function is defined in logging.cc. namespace base { namespace internal { void WarnEnumCastError(int value_of_int); } // namespace internal } // namespace base template inline Enum loose_enum_cast(int e_val) { if (!loose_enum_test(e_val)) { base::internal::WarnEnumCastError(e_val); } return static_cast(e_val); } template inline Enum tight_enum_cast(int e_val) { if (!tight_enum_test(e_val)) { base::internal::WarnEnumCastError(e_val); } return static_cast(e_val); } #endif // S2_BASE_CASTS_H_ s2/src/s2/base/spinlock.h0000644000176200001440000000277214122355625014633 0ustar liggesusers// Copyright Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef S2_BASE_SPINLOCK_H_ #define S2_BASE_SPINLOCK_H_ #include class SpinLock { public: SpinLock() = default; ~SpinLock() = default; SpinLock(SpinLock const&) = delete; SpinLock& operator=(SpinLock const&) = delete; inline void Lock() { while (locked_.exchange(true, std::memory_order_acquire)) { // Spin. continue; } } inline void Unlock() { locked_.store(false, std::memory_order_release); } inline bool IsHeld() const { return locked_.load(std::memory_order_relaxed); } private: std::atomic_bool locked_{false}; }; class SpinLockHolder { public: inline explicit SpinLockHolder(SpinLock* l) : lock_(l) { lock_->Lock(); } inline ~SpinLockHolder() { lock_->Unlock(); } SpinLockHolder(const SpinLockHolder&) = delete; SpinLockHolder& operator=(const SpinLockHolder&) = delete; private: SpinLock* lock_; }; #endif // S2_BASE_SPINLOCK_H_ s2/src/s2/base/timer.h0000644000176200001440000000227714122355625014131 0ustar liggesusers// Copyright Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef S2_BASE_TIMER_H_ #define S2_BASE_TIMER_H_ #include #include "s2/base/integral_types.h" class CycleTimer { public: CycleTimer() = default; void Start() { start_ = Now(); } int64 GetInMs() const { using msec = std::chrono::milliseconds; return std::chrono::duration_cast(GetDuration()).count(); } private: using Clock = std::chrono::high_resolution_clock; static Clock::time_point Now() { return Clock::now(); } Clock::duration GetDuration() const { return Now() - start_; } Clock::time_point start_; }; #endif // S2_BASE_TIMER_H_ s2/src/s2/base/stringprintf.h0000644000176200001440000000366214122355625015541 0ustar liggesusers// Copyright Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // NOTE: See third_party/absl/strings for more options. // // As of 2017q4, most use of these routines is considered legacy: use // of absl::StrCat, absl::Substitute, or absl::StrFormat is preferred for // performance and safety reasons. #ifndef S2_BASE_STRINGPRINTF_H_ #define S2_BASE_STRINGPRINTF_H_ #include #include #include #include "s2/base/port.h" // Return a C++ string extern std::string StringPrintf(const char* format, ...) // Tell the compiler to do printf format string checking. ABSL_PRINTF_ATTRIBUTE(1, 2); // Store result into a supplied string and return it extern const std::string& SStringPrintf(std::string* dst, const char* format, ...) // Tell the compiler to do printf format string checking. ABSL_PRINTF_ATTRIBUTE(2, 3); // Append result to a supplied string extern void StringAppendF(std::string* dst, const char* format, ...) // Tell the compiler to do printf format string checking. ABSL_PRINTF_ATTRIBUTE(2, 3); // Lower-level routine that takes a va_list and appends to a specified // string. All other routines are just convenience wrappers around it. // // Implementation note: the va_list is never modified, this implementation // always operates on copies. extern void StringAppendV(std::string* dst, const char* format, va_list ap); #endif // S2_BASE_STRINGPRINTF_H_ s2/src/s2/base/logging.h0000644000176200001440000001266314122355625014437 0ustar liggesusers// Copyright Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef S2_BASE_LOGGING_H_ #define S2_BASE_LOGGING_H_ #include "cpp-compat.h" #ifdef S2_USE_GLOG #include // The names CHECK, etc. are too common and may conflict with other // packages. We use S2_CHECK to make it easier to switch to // something other than GLOG for logging. #define S2_LOG LOG #define S2_LOG_IF LOG_IF #define S2_DLOG_IF DLOG_IF #define S2_CHECK CHECK #define S2_CHECK_EQ CHECK_EQ #define S2_CHECK_NE CHECK_NE #define S2_CHECK_LT CHECK_LT #define S2_CHECK_LE CHECK_LE #define S2_CHECK_GT CHECK_GT #define S2_CHECK_GE CHECK_GE #define S2_DCHECK DCHECK #define S2_DCHECK_EQ DCHECK_EQ #define S2_DCHECK_NE DCHECK_NE #define S2_DCHECK_LT DCHECK_LT #define S2_DCHECK_LE DCHECK_LE #define S2_DCHECK_GT DCHECK_GT #define S2_DCHECK_GE DCHECK_GE #define S2_VLOG VLOG #define S2_VLOG_IS_ON VLOG_IS_ON #else // !defined(S2_USE_GLOG) #include #include "s2/base/log_severity.h" #include "absl/base/attributes.h" #include "absl/base/log_severity.h" class S2LogMessage { public: S2LogMessage(const char* file, int line, absl::LogSeverity severity, std::ostream& stream) : severity_(severity), stream_(stream) { if (enabled()) { stream_ << file << ":" << line << " " << absl::LogSeverityName(severity) << " "; } } ~S2LogMessage() { if (enabled()) stream_ << std::endl; } std::ostream& stream() { return stream_; } // silences an 'unused member' compiler warning absl::LogSeverity severity() { return severity_; } private: bool enabled() const { #ifdef ABSL_MIN_LOG_LEVEL return (static_cast(severity_) >= ABSL_MIN_LOG_LEVEL || severity_ >= absl::LogSeverity::kFatal); #else return true; #endif } absl::LogSeverity severity_; std::ostream& stream_; }; // Same as S2LogMessage, but destructor is marked no-return to avoid // "no return value warnings" in functions that return non-void. class S2FatalLogMessage : public S2LogMessage { public: S2FatalLogMessage(const char* file, int line, absl::LogSeverity severity, std::ostream& stream) ABSL_ATTRIBUTE_COLD : S2LogMessage(file, line, severity, stream) {} ABSL_ATTRIBUTE_NORETURN ~S2FatalLogMessage() { cpp_compat_abort(); } }; // Logging stream that does nothing. struct S2NullStream { template S2NullStream& operator<<(const T& v) { return *this; } }; // Used to suppress "unused value" warnings. struct S2LogMessageVoidify { // Must have precedence lower than << but higher than ?:. void operator&(std::ostream&) {} }; #define S2_LOG_MESSAGE_(LogMessageClass, log_severity) \ LogMessageClass(__FILE__, __LINE__, log_severity, cpp_compat_cerr) #define S2_LOG_INFO \ S2_LOG_MESSAGE_(S2LogMessage, absl::LogSeverity::kInfo) #define S2_LOG_WARNING \ S2_LOG_MESSAGE_(S2LogMessage, absl::LogSeverity::kWarning) #define S2_LOG_ERROR \ S2_LOG_MESSAGE_(S2LogMessage, absl::LogSeverity::kError) #define S2_LOG_FATAL \ S2_LOG_MESSAGE_(S2FatalLogMessage, absl::LogSeverity::kFatal) #ifndef NDEBUG #define S2_LOG_DFATAL S2_LOG_FATAL #else #define S2_LOG_DFATAL S2_LOG_ERROR #endif #define S2_LOG(severity) S2_LOG_##severity.stream() // Implementing this as if (...) {} else S2_LOG(...) will cause dangling else // warnings when someone does if (...) S2_LOG_IF(...), so do this tricky // thing instead. #define S2_LOG_IF(severity, condition) \ !(condition) ? (void)0 : S2LogMessageVoidify() & S2_LOG(severity) #define S2_CHECK(condition) \ S2_LOG_IF(FATAL, ABSL_PREDICT_FALSE(!(condition))) \ << ("Check failed: " #condition " ") #ifndef NDEBUG #define S2_DLOG_IF S2_LOG_IF #define S2_DCHECK S2_CHECK #else // defined(NDEBUG) #define S2_DLOG_IF(severity, condition) \ while (false && (condition)) S2NullStream() #define S2_DCHECK(condition) \ while (false && (condition)) S2NullStream() #endif // defined(NDEBUG) #define S2_CHECK_OP(op, val1, val2) S2_CHECK((val1) op (val2)) #define S2_CHECK_EQ(val1, val2) S2_CHECK_OP(==, val1, val2) #define S2_CHECK_NE(val1, val2) S2_CHECK_OP(!=, val1, val2) #define S2_CHECK_LT(val1, val2) S2_CHECK_OP(<, val1, val2) #define S2_CHECK_LE(val1, val2) S2_CHECK_OP(<=, val1, val2) #define S2_CHECK_GT(val1, val2) S2_CHECK_OP(>, val1, val2) #define S2_CHECK_GE(val1, val2) S2_CHECK_OP(>=, val1, val2) #define S2_DCHECK_OP(op, val1, val2) S2_DCHECK((val1) op (val2)) #define S2_DCHECK_EQ(val1, val2) S2_DCHECK_OP(==, val1, val2) #define S2_DCHECK_NE(val1, val2) S2_DCHECK_OP(!=, val1, val2) #define S2_DCHECK_LT(val1, val2) S2_DCHECK_OP(<, val1, val2) #define S2_DCHECK_LE(val1, val2) S2_DCHECK_OP(<=, val1, val2) #define S2_DCHECK_GT(val1, val2) S2_DCHECK_OP(>, val1, val2) #define S2_DCHECK_GE(val1, val2) S2_DCHECK_OP(>=, val1, val2) // We don't support VLOG. #define S2_VLOG(verbose_level) S2NullStream() #define S2_VLOG_IS_ON(verbose_level) (false) #endif // !defined(S2_USE_GLOG) #endif // S2_BASE_LOGGING_H_ s2/src/s2/base/port.h0000644000176200001440000010134014122355625013764 0ustar liggesusers// Copyright Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef S2_BASE_PORT_H_ #define S2_BASE_PORT_H_ // This file contains things that are not used in third_party/absl but needed by // - Platform specific requirement // - MSVC // - Utility macros // - Endianness // - Hash // - Global variables // - Type alias // - Predefined system/language macros // - Predefined system/language functions // - Performance optimization (alignment) // - Obsolete #include #include #include #include #include "s2/base/integral_types.h" #include "absl/base/config.h" #include "absl/base/port.h" #ifdef SWIG %include "third_party/absl/base/port.h" #endif // ----------------------------------------------------------------------------- // MSVC Specific Requirements // ----------------------------------------------------------------------------- #ifdef _MSC_VER /* if Visual C++ */ #include // Must come before #include #include // _getpid() #include #undef ERROR #undef DELETE #undef DIFFERENCE #define STDIN_FILENO 0 #define STDOUT_FILENO 1 #define STDERR_FILENO 2 #define S_IRUSR 00400 #define S_IWUSR 00200 #define S_IXUSR 00100 #define S_IRGRP 00040 #define S_IWGRP 00020 #define S_IXGRP 00010 #define S_IROTH 00004 #define S_IWOTH 00002 #define S_IXOTH 00001 // This compiler flag can be easily overlooked on MSVC. // _CHAR_UNSIGNED gets set with the /J flag. #ifndef _CHAR_UNSIGNED #error chars must be unsigned! Use the /J flag on the compiler command line. // NOLINT #endif // Allow comparisons between signed and unsigned values. // // Lots of Google code uses this pattern: // for (int i = 0; i < container.size(); ++i) // Since size() returns an unsigned value, this warning would trigger // frequently. Very few of these instances are actually bugs since containers // rarely exceed MAX_INT items. Unfortunately, there are bugs related to // signed-unsigned comparisons that have been missed because we disable this // warning. For example: // const long stop_time = os::GetMilliseconds() + kWaitTimeoutMillis; // while (os::GetMilliseconds() <= stop_time) { ... } #pragma warning(disable : 4018) // level 3 #pragma warning(disable : 4267) // level 3 // Don't warn about unused local variables. // // extension to silence particular instances of this warning. There's no way // to define ABSL_ATTRIBUTE_UNUSED to quiet particular instances of this warning // in VC++, so we disable it globally. Currently, there aren't many false // positives, so perhaps we can address those in the future and re-enable these // warnings, which sometimes catch real bugs. #pragma warning(disable : 4101) // level 3 // Allow initialization and assignment to a smaller type without warnings about // possible loss of data. // // There is a distinct warning, 4267, that warns about size_t conversions to // smaller types, but we don't currently disable that warning. // // Correct code can be written in such a way as to avoid false positives // by making the conversion explicit, but Google code isn't usually that // verbose. There are too many false positives to address at this time. Note // that this warning triggers at levels 2, 3, and 4 depending on the specific // type of conversion. By disabling it, we not only silence minor narrowing // conversions but also serious ones. #pragma warning(disable : 4244) // level 2, 3, and 4 // Allow silent truncation of double to float. // // Silencing this warning has caused us to miss some subtle bugs. #pragma warning(disable : 4305) // level 1 // Allow a constant to be assigned to a type that is too small. // // I don't know why we allow this at all. I can't think of a case where this // wouldn't be a bug, but enabling the warning breaks many builds today. #pragma warning(disable : 4307) // level 2 // Allow passing the this pointer to an initializer even though it refers // to an uninitialized object. // // Some observer implementations rely on saving the this pointer. Those are // safe because the pointer is not dereferenced until after the object is fully // constructed. This could however, obscure other instances. In the future, we // should look into disabling this warning locally rather globally. #pragma warning(disable : 4355) // level 1 and 4 // Allow implicit coercion from an integral type to a bool. // // These could be avoided by making the code more explicit, but that's never // been the style here, so there would be many false positives. It's not // obvious if a true positive would ever help to find an actual bug. #pragma warning(disable : 4800) // level 3 #endif // _MSC_VER // ----------------------------------------------------------------------------- // Utility Macros // ----------------------------------------------------------------------------- // OS_IOS #if defined(__APPLE__) // Currently, blaze supports iOS yet doesn't define a flag. Mac users have // traditionally defined OS_IOS themselves via other build systems, since mac // hasn't been supported by blaze. // TODO(user): Remove this when all toolchains make the proper defines. #include #if defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE #ifndef OS_IOS #define OS_IOS 1 #endif #endif // defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE #endif // defined(__APPLE__) // __GLIBC_PREREQ #if defined __linux__ // GLIBC-related macros. #include #ifndef __GLIBC_PREREQ #define __GLIBC_PREREQ(a, b) 0 // not a GLIBC system #endif #endif // __linux__ // STATIC_ANALYSIS // Klocwork static analysis tool's C/C++ complier kwcc #if defined(__KLOCWORK__) #define STATIC_ANALYSIS #endif // __KLOCWORK__ // SIZEOF_MEMBER, OFFSETOF_MEMBER #define SIZEOF_MEMBER(t, f) sizeof(reinterpret_cast(4096)->f) #define OFFSETOF_MEMBER(t, f) \ (reinterpret_cast(&(reinterpret_cast(16)->f)) - \ reinterpret_cast(16)) // LANG_CXX11 // GXX_EXPERIMENTAL_CXX0X is defined by gcc and clang up to at least // gcc-4.7 and clang-3.1 (2011-12-13). __cplusplus was defined to 1 // in gcc before 4.7 (Crosstool 16) and clang before 3.1, but is // defined according to the language version in effect thereafter. // Microsoft Visual Studio 14 (2015) sets __cplusplus==199711 despite // reasonably good C++11 support, so we set LANG_CXX for it and // newer versions (_MSC_VER >= 1900). #if (defined(__GXX_EXPERIMENTAL_CXX0X__) || __cplusplus >= 201103L || \ (defined(_MSC_VER) && _MSC_VER >= 1900)) // DEPRECATED: Do not key off LANG_CXX11. Instead, write more accurate condition // that checks whether the C++ feature you need is available or missing, and // define a more specific feature macro (GOOGLE_HAVE_FEATURE_FOO). You can check // http://en.cppreference.com/w/cpp/compiler_support for compiler support on C++ // features. // Define this to 1 if the code is compiled in C++11 mode; leave it // undefined otherwise. Do NOT define it to 0 -- that causes // '#ifdef LANG_CXX11' to behave differently from '#if LANG_CXX11'. #define LANG_CXX11 1 #endif // This sanity check can be removed when all references to // LANG_CXX11 is removed from the code base. #if defined(__cplusplus) && !defined(LANG_CXX11) && !defined(SWIG) #error "LANG_CXX11 is required." #endif // GOOGLE_OBSCURE_SIGNAL #if defined(__APPLE__) // No SIGPWR on MacOSX. SIGINFO seems suitably obscure. #define GOOGLE_OBSCURE_SIGNAL SIGINFO #else /* We use SIGPWR since that seems unlikely to be used for other reasons. */ #define GOOGLE_OBSCURE_SIGNAL SIGPWR #endif // ABSL_FUNC_PTR_TO_CHAR_PTR // On some platforms, a "function pointer" points to a function descriptor // rather than directly to the function itself. // Use ABSL_FUNC_PTR_TO_CHAR_PTR(func) to get a char-pointer to the first // instruction of the function func. // TODO(b/30407660): Move this macro into Abseil when symbolizer is released in // Abseil. #if defined(__cplusplus) #if (defined(__powerpc__) && !(_CALL_ELF > 1)) || defined(__ia64) // use opd section for function descriptors on these platforms, the function // address is the first word of the descriptor namespace absl { enum { kPlatformUsesOPDSections = 1 }; } // namespace absl #define ABSL_FUNC_PTR_TO_CHAR_PTR(func) (reinterpret_cast(func)[0]) #else // not PPC or IA64 namespace absl { enum { kPlatformUsesOPDSections = 0 }; } // namespace absl #define ABSL_FUNC_PTR_TO_CHAR_PTR(func) (reinterpret_cast(func)) #endif // PPC or IA64 #endif // __cplusplus // ----------------------------------------------------------------------------- // Utility Functions // ----------------------------------------------------------------------------- // sized_delete #ifdef __cplusplus namespace base { // We support C++14's sized deallocation for all C++ builds, // though for other toolchains, we fall back to using delete. inline void sized_delete(void *ptr, size_t size) { #ifdef GOOGLE_HAVE_SIZED_DELETE ::operator delete(ptr, size); #else (void)size; ::operator delete(ptr); #endif // GOOGLE_HAVE_SIZED_DELETE } inline void sized_delete_array(void *ptr, size_t size) { #ifdef GOOGLE_HAVE_SIZED_DELETEARRAY ::operator delete[](ptr, size); #else (void) size; ::operator delete[](ptr); #endif } } // namespace base #endif // __cplusplus // ----------------------------------------------------------------------------- // Endianness // ----------------------------------------------------------------------------- // IS_LITTLE_ENDIAN, IS_BIG_ENDIAN // Allow compiler -D defines to override detection here // which occasionally fails (e.g., on CRAN Solaris) #if defined(IS_LITTLE_ENDIAN) #undef IS_BIG_ENDIAN #elif defined(IS_BIG_ENDIAN) #undef IS_LITTLE_ENDIAN #else #if defined __linux__ || defined OS_ANDROID || defined(__ANDROID__) // TODO(user): http://b/21460321; use one of OS_ANDROID or __ANDROID__. // _BIG_ENDIAN #include #elif defined(__APPLE__) // BIG_ENDIAN #include // NOLINT(build/include) /* Let's try and follow the Linux convention */ #define __BYTE_ORDER BYTE_ORDER #define __LITTLE_ENDIAN LITTLE_ENDIAN #define __BIG_ENDIAN BIG_ENDIAN #endif // defines __BYTE_ORDER for MSVC #ifdef _MSC_VER #define __BYTE_ORDER __LITTLE_ENDIAN #define IS_LITTLE_ENDIAN #else // define the macros IS_LITTLE_ENDIAN or IS_BIG_ENDIAN // using the above endian definitions from endian.h if // endian.h was included #ifdef __BYTE_ORDER #if __BYTE_ORDER == __LITTLE_ENDIAN #define IS_LITTLE_ENDIAN #endif #if __BYTE_ORDER == __BIG_ENDIAN #define IS_BIG_ENDIAN #endif #else // __BYTE_ORDER #if defined(__LITTLE_ENDIAN__) #define IS_LITTLE_ENDIAN #elif defined(__BIG_ENDIAN__) #define IS_BIG_ENDIAN #endif #endif // __BYTE_ORDER #endif // _MSC_VER #endif // #if defined(IS_LITTLE_ENDIAN) ... #else // byte swap functions (bswap_16, bswap_32, bswap_64). // The following guarantees declaration of the byte swap functions #ifdef _MSC_VER #include // NOLINT(build/include) #define bswap_16(x) _byteswap_ushort(x) #define bswap_32(x) _byteswap_ulong(x) #define bswap_64(x) _byteswap_uint64(x) #elif defined(__APPLE__) // Mac OS X / Darwin features #include #define bswap_16(x) OSSwapInt16(x) #define bswap_32(x) OSSwapInt32(x) #define bswap_64(x) OSSwapInt64(x) #elif defined(__GLIBC__) || defined(__BIONIC__) || defined(__ASYLO__) #include // IWYU pragma: export #else static inline uint16 bswap_16(uint16 x) { #ifdef __cplusplus return static_cast(((x & 0xFF) << 8) | ((x & 0xFF00) >> 8)); #else return (uint16)(((x & 0xFF) << 8) | ((x & 0xFF00) >> 8)); // NOLINT #endif // __cplusplus } #define bswap_16(x) bswap_16(x) static inline uint32 bswap_32(uint32 x) { return (((x & 0xFF) << 24) | ((x & 0xFF00) << 8) | ((x & 0xFF0000) >> 8) | ((x & 0xFF000000) >> 24)); } #define bswap_32(x) bswap_32(x) static inline uint64 bswap_64(uint64 x) { return (((x & 0xFFULL) << 56) | ((x & 0xFF00ULL) << 40) | ((x & 0xFF0000ULL) << 24) | ((x & 0xFF000000ULL) << 8) | ((x & 0xFF00000000ULL) >> 8) | ((x & 0xFF0000000000ULL) >> 24) | ((x & 0xFF000000000000ULL) >> 40) | ((x & 0xFF00000000000000ULL) >> 56)); } #define bswap_64(x) bswap_64(x) #endif // ----------------------------------------------------------------------------- // Hash // ----------------------------------------------------------------------------- #ifdef __cplusplus #ifdef STL_MSVC // not always the same as _MSC_VER #include "absl/base/internal/port_hash.inc" #else struct PortableHashBase {}; #endif // STL_MSVC #endif // __cplusplus // ----------------------------------------------------------------------------- // Global Variables // ----------------------------------------------------------------------------- // PATH_SEPARATOR // Define the OS's path separator // // NOTE: Assuming the path separator at compile time is discouraged. // Prefer instead to be tolerant of both possible separators whenever possible. #ifdef __cplusplus // C won't merge duplicate const variables at link time // Some headers provide a macro for this (GCC's system.h), remove it so that we // can use our own. #undef PATH_SEPARATOR #if defined(_WIN32) const char PATH_SEPARATOR = '\\'; #else const char PATH_SEPARATOR = '/'; #endif // _WIN32 #endif // __cplusplus // ----------------------------------------------------------------------------- // Type Alias // ----------------------------------------------------------------------------- // uint, ushort, ulong #if defined __linux__ // The uint mess: // mysql.h sets _GNU_SOURCE which sets __USE_MISC in // sys/types.h typedefs uint if __USE_MISC // mysql typedefs uint if HAVE_UINT not set // The following typedef is carefully considered, and should not cause // any clashes #if !defined(__USE_MISC) #if !defined(HAVE_UINT) #define HAVE_UINT 1 typedef unsigned int uint; #endif // !HAVE_UINT #if !defined(HAVE_USHORT) #define HAVE_USHORT 1 typedef unsigned short ushort; // NOLINT #endif // !HAVE_USHORT #if !defined(HAVE_ULONG) #define HAVE_ULONG 1 typedef unsigned long ulong; // NOLINT #endif // !HAVE_ULONG #endif // !__USE_MISC #endif // __linux__ #ifdef _MSC_VER /* if Visual C++ */ // VC++ doesn't understand "uint" #ifndef HAVE_UINT #define HAVE_UINT 1 typedef unsigned int uint; #endif // !HAVE_UINT #endif // _MSC_VER #ifdef _MSC_VER // uid_t // MSVC doesn't have uid_t typedef int uid_t; // pid_t // Defined all over the place. typedef int pid_t; #endif // _MSC_VER // mode_t #ifdef _MSC_VER // From stat.h typedef unsigned int mode_t; #endif // _MSC_VER // sig_t #ifdef _MSC_VER typedef void (*sig_t)(int); #endif // _MSC_VER // u_int16_t, int16_t #ifdef _MSC_VER // u_int16_t, int16_t don't exist in MSVC typedef unsigned short u_int16_t; // NOLINT typedef short int16_t; // NOLINT #endif // _MSC_VER // using std::hash #ifdef _MSC_VER #ifdef __cplusplus // Define a minimal set of things typically available in the global // namespace in Google code. ::string is handled elsewhere, and uniformly // for all targets. #include using std::hash; #endif // __cplusplus #endif // _MSC_VER // printf macros // __STDC_FORMAT_MACROS must be defined before inttypes.h inclusion */ #if defined(__APPLE__) /* From MacOSX's inttypes.h: * "C++ implementations should define these macros only when * __STDC_FORMAT_MACROS is defined before is included." */ #ifndef __STDC_FORMAT_MACROS #define __STDC_FORMAT_MACROS #endif /* __STDC_FORMAT_MACROS */ #endif /* __APPLE__ */ // printf macros for size_t, in the style of inttypes.h #if defined(_LP64) || defined(__APPLE__) #define __PRIS_PREFIX "z" #else #define __PRIS_PREFIX #endif // Use these macros after a % in a printf format string // to get correct 32/64 bit behavior, like this: // size_t size = records.size(); // printf("%" PRIuS "\n", size); #define PRIdS __PRIS_PREFIX "d" #define PRIxS __PRIS_PREFIX "x" #define PRIuS __PRIS_PREFIX "u" #define PRIXS __PRIS_PREFIX "X" #define PRIoS __PRIS_PREFIX "o" #define GPRIuPTHREAD "lu" #define GPRIxPTHREAD "lx" #if defined(__APPLE__) #define PRINTABLE_PTHREAD(pthreadt) reinterpret_cast(pthreadt) #else #define PRINTABLE_PTHREAD(pthreadt) pthreadt #endif #ifdef PTHREADS_REDHAT_WIN32 #include // NOLINT(build/include) #include // NOLINT(build/include) // pthread_t is not a simple integer or pointer on Win32 std::ostream &operator<<(std::ostream &out, const pthread_t &thread_id); #endif // ----------------------------------------------------------------------------- // Predefined System/Language Macros // ----------------------------------------------------------------------------- // EXFULL #if defined(__APPLE__) // Linux has this in #define EXFULL ENOMEM // not really that great a translation... #endif // __APPLE__ #ifdef _MSC_VER // This actually belongs in errno.h but there's a name conflict in errno // on WinNT. They (and a ton more) are also found in Winsock2.h, but // if'd out under NT. We need this subset at minimum. #define EXFULL ENOMEM // not really that great a translation... #endif // _MSC_VER // MSG_NOSIGNAL #if defined(__APPLE__) // Doesn't exist on OSX. #define MSG_NOSIGNAL 0 #endif // __APPLE__ // __ptr_t #if defined(__APPLE__) // Linux has this in #define __ptr_t void * #endif // __APPLE__ #ifdef _MSC_VER // From glob.h #define __ptr_t void * #endif // HUGE_VALF #ifdef _MSC_VER #include // for HUGE_VAL #ifndef HUGE_VALF #define HUGE_VALF (static_cast(HUGE_VAL)) #endif #endif // _MSC_VER // MAP_ANONYMOUS #if defined(__APPLE__) // For mmap, Linux defines both MAP_ANONYMOUS and MAP_ANON and says MAP_ANON is // deprecated. In Darwin, MAP_ANON is all there is. #if !defined MAP_ANONYMOUS #define MAP_ANONYMOUS MAP_ANON #endif // !MAP_ANONYMOUS #endif // __APPLE__ // PATH_MAX // You say tomato, I say atotom #ifdef _MSC_VER #define PATH_MAX MAX_PATH #endif // ----------------------------------------------------------------------------- // Predefined System/Language Functions // ----------------------------------------------------------------------------- // strtoq, strtouq, atoll #ifdef _MSC_VER #define strtoq _strtoi64 #define strtouq _strtoui64 #define atoll _atoi64 #endif // _MSC_VER #ifdef _MSC_VER // You say tomato, I say _tomato #define strcasecmp _stricmp #define strncasecmp _strnicmp #define strdup _strdup #define tempnam _tempnam #define chdir _chdir #define getpid _getpid #define getcwd _getcwd #define putenv _putenv #define timezone _timezone #define tzname _tzname #endif // _MSC_VER // random, srandom #ifdef _MSC_VER // You say tomato, I say toma inline int random() { return rand(); } inline void srandom(unsigned int seed) { srand(seed); } #endif // _MSC_VER // bcopy, bzero #ifdef _MSC_VER // You say juxtapose, I say transpose #define bcopy(s, d, n) memcpy(d, s, n) // Really from inline void bzero(void *s, int n) { memset(s, 0, n); } #endif // _MSC_VER // gethostbyname #if defined(_WIN32) || defined(__APPLE__) // gethostbyname() *is* thread-safe for Windows native threads. It is also // safe on Mac OS X and iOS, where it uses thread-local storage, even though the // manpages claim otherwise. For details, see // http://lists.apple.com/archives/Darwin-dev/2006/May/msg00008.html #else // gethostbyname() is not thread-safe. So disallow its use. People // should either use the HostLookup::Lookup*() methods, or gethostbyname_r() #define gethostbyname gethostbyname_is_not_thread_safe_DO_NOT_USE #endif // ----------------------------------------------------------------------------- // Performance Optimization // ----------------------------------------------------------------------------- // Alignment // Unaligned APIs // Portable handling of unaligned loads, stores, and copies. // On some platforms, like ARM, the copy functions can be more efficient // then a load and a store. // // It is possible to implement all of these these using constant-length memcpy // calls, which is portable and will usually be inlined into simple loads and // stores if the architecture supports it. However, such inlining usually // happens in a pass that's quite late in compilation, which means the resulting // loads and stores cannot participate in many other optimizations, leading to // overall worse code. // TODO(user): These APIs are forked in Abseil, see // LLVM, we should reimplement these APIs with functions calling memcpy(), and // maybe publish them in Abseil. // The unaligned API is C++ only. The declarations use C++ features // (namespaces, inline) which are absent or incompatible in C. #if defined(__cplusplus) #if defined(ADDRESS_SANITIZER) || defined(THREAD_SANITIZER) || \ defined(MEMORY_SANITIZER) // Consider we have an unaligned load/store of 4 bytes from address 0x...05. // AddressSanitizer will treat it as a 3-byte access to the range 05:07 and // will miss a bug if 08 is the first unaddressable byte. // ThreadSanitizer will also treat this as a 3-byte access to 05:07 and will // miss a race between this access and some other accesses to 08. // MemorySanitizer will correctly propagate the shadow on unaligned stores // and correctly report bugs on unaligned loads, but it may not properly // update and report the origin of the uninitialized memory. // For all three tools, replacing an unaligned access with a tool-specific // callback solves the problem. // Make sure uint16_t/uint32_t/uint64_t are defined. #include extern "C" { uint16_t __sanitizer_unaligned_load16(const void *p); uint32_t __sanitizer_unaligned_load32(const void *p); uint64_t __sanitizer_unaligned_load64(const void *p); void __sanitizer_unaligned_store16(void *p, uint16_t v); void __sanitizer_unaligned_store32(void *p, uint32_t v); void __sanitizer_unaligned_store64(void *p, uint64_t v); } // extern "C" inline uint16 UNALIGNED_LOAD16(const void *p) { return __sanitizer_unaligned_load16(p); } inline uint32 UNALIGNED_LOAD32(const void *p) { return __sanitizer_unaligned_load32(p); } inline uint64 UNALIGNED_LOAD64(const void *p) { return __sanitizer_unaligned_load64(p); } inline void UNALIGNED_STORE16(void *p, uint16 v) { __sanitizer_unaligned_store16(p, v); } inline void UNALIGNED_STORE32(void *p, uint32 v) { __sanitizer_unaligned_store32(p, v); } inline void UNALIGNED_STORE64(void *p, uint64 v) { __sanitizer_unaligned_store64(p, v); } #elif defined(UNDEFINED_BEHAVIOR_SANITIZER) inline uint16 UNALIGNED_LOAD16(const void *p) { uint16 t; memcpy(&t, p, sizeof t); return t; } inline uint32 UNALIGNED_LOAD32(const void *p) { uint32 t; memcpy(&t, p, sizeof t); return t; } inline uint64 UNALIGNED_LOAD64(const void *p) { uint64 t; memcpy(&t, p, sizeof t); return t; } inline void UNALIGNED_STORE16(void *p, uint16 v) { memcpy(p, &v, sizeof v); } inline void UNALIGNED_STORE32(void *p, uint32 v) { memcpy(p, &v, sizeof v); } inline void UNALIGNED_STORE64(void *p, uint64 v) { memcpy(p, &v, sizeof v); } #elif defined(__x86_64__) || defined(_M_X64) || defined(__i386) || \ defined(_M_IX86) || defined(__ppc__) || defined(__PPC__) || \ defined(__ppc64__) || defined(__PPC64__) // x86 and x86-64 can perform unaligned loads/stores directly; // modern PowerPC hardware can also do unaligned integer loads and stores; // but note: the FPU still sends unaligned loads and stores to a trap handler! #define UNALIGNED_LOAD16(_p) (*reinterpret_cast(_p)) #define UNALIGNED_LOAD32(_p) (*reinterpret_cast(_p)) #define UNALIGNED_LOAD64(_p) (*reinterpret_cast(_p)) #define UNALIGNED_STORE16(_p, _val) (*reinterpret_cast(_p) = (_val)) #define UNALIGNED_STORE32(_p, _val) (*reinterpret_cast(_p) = (_val)) #define UNALIGNED_STORE64(_p, _val) (*reinterpret_cast(_p) = (_val)) #elif defined(__arm__) && !defined(__ARM_ARCH_5__) && \ !defined(__ARM_ARCH_5T__) && !defined(__ARM_ARCH_5TE__) && \ !defined(__ARM_ARCH_5TEJ__) && !defined(__ARM_ARCH_6__) && \ !defined(__ARM_ARCH_6J__) && !defined(__ARM_ARCH_6K__) && \ !defined(__ARM_ARCH_6Z__) && !defined(__ARM_ARCH_6ZK__) && \ !defined(__ARM_ARCH_6T2__) // ARMv7 and newer support native unaligned accesses, but only of 16-bit // and 32-bit values (not 64-bit); older versions either raise a fatal signal, // do an unaligned read and rotate the words around a bit, or do the reads very // slowly (trip through kernel mode). There's no simple #define that says just // “ARMv7 or higher”, so we have to filter away all ARMv5 and ARMv6 // sub-architectures. Newer gcc (>= 4.6) set an __ARM_FEATURE_ALIGNED #define, // so in time, maybe we can move on to that. // // This is a mess, but there's not much we can do about it. // // To further complicate matters, only LDR instructions (single reads) are // allowed to be unaligned, not LDRD (two reads) or LDM (many reads). Unless we // explicitly tell the compiler that these accesses can be unaligned, it can and // will combine accesses. On armcc, the way to signal this is done by accessing // through the type (uint32 __packed *), but GCC has no such attribute // (it ignores __attribute__((packed)) on individual variables). However, // we can tell it that a _struct_ is unaligned, which has the same effect, // so we do that. namespace base { namespace internal { struct Unaligned16Struct { uint16 value; uint8 dummy; // To make the size non-power-of-two. } ABSL_ATTRIBUTE_PACKED; struct Unaligned32Struct { uint32 value; uint8 dummy; // To make the size non-power-of-two. } ABSL_ATTRIBUTE_PACKED; } // namespace internal } // namespace base #define UNALIGNED_LOAD16(_p) \ ((reinterpret_cast(_p))->value) #define UNALIGNED_LOAD32(_p) \ ((reinterpret_cast(_p))->value) #define UNALIGNED_STORE16(_p, _val) \ ((reinterpret_cast< ::base::internal::Unaligned16Struct *>(_p))->value = \ (_val)) #define UNALIGNED_STORE32(_p, _val) \ ((reinterpret_cast< ::base::internal::Unaligned32Struct *>(_p))->value = \ (_val)) // TODO(user): NEON supports unaligned 64-bit loads and stores. // See if that would be more efficient on platforms supporting it, // at least for copies. inline uint64 UNALIGNED_LOAD64(const void *p) { uint64 t; memcpy(&t, p, sizeof t); return t; } inline void UNALIGNED_STORE64(void *p, uint64 v) { memcpy(p, &v, sizeof v); } #else #define NEED_ALIGNED_LOADS // These functions are provided for architectures that don't support // unaligned loads and stores. inline uint16 UNALIGNED_LOAD16(const void *p) { uint16 t; memcpy(&t, p, sizeof t); return t; } inline uint32 UNALIGNED_LOAD32(const void *p) { uint32 t; memcpy(&t, p, sizeof t); return t; } inline uint64 UNALIGNED_LOAD64(const void *p) { uint64 t; memcpy(&t, p, sizeof t); return t; } inline void UNALIGNED_STORE16(void *p, uint16 v) { memcpy(p, &v, sizeof v); } inline void UNALIGNED_STORE32(void *p, uint32 v) { memcpy(p, &v, sizeof v); } inline void UNALIGNED_STORE64(void *p, uint64 v) { memcpy(p, &v, sizeof v); } #endif // The UNALIGNED_LOADW and UNALIGNED_STOREW macros load and store values // of type uword_t. #ifdef _LP64 #define UNALIGNED_LOADW(_p) UNALIGNED_LOAD64(_p) #define UNALIGNED_STOREW(_p, _val) UNALIGNED_STORE64(_p, _val) #else #define UNALIGNED_LOADW(_p) UNALIGNED_LOAD32(_p) #define UNALIGNED_STOREW(_p, _val) UNALIGNED_STORE32(_p, _val) #endif inline void UnalignedCopy16(const void *src, void *dst) { UNALIGNED_STORE16(dst, UNALIGNED_LOAD16(src)); } inline void UnalignedCopy32(const void *src, void *dst) { UNALIGNED_STORE32(dst, UNALIGNED_LOAD32(src)); } inline void UnalignedCopy64(const void *src, void *dst) { if (sizeof(void *) == 8) { UNALIGNED_STORE64(dst, UNALIGNED_LOAD64(src)); } else { const char *src_char = reinterpret_cast(src); char *dst_char = reinterpret_cast(dst); UNALIGNED_STORE32(dst_char, UNALIGNED_LOAD32(src_char)); UNALIGNED_STORE32(dst_char + 4, UNALIGNED_LOAD32(src_char + 4)); } } #endif // defined(__cplusplus), end of unaligned API // aligned_malloc, aligned_free #if defined(__ANDROID__) || defined(__ASYLO__) || defined(_WIN32) #include // for memalign() #endif // __ASYLO__ platform uses newlib without an underlying OS, which provides // memalign, but not posix_memalign. #if defined(__cplusplus) && \ (((defined(__GNUC__) || defined(__APPLE__) || \ defined(__NVCC__)) && \ !defined(SWIG)) || \ ((__GNUC__ >= 3 || defined(__clang__)) && defined(__ANDROID__)) || \ defined(__ASYLO__)) inline void *aligned_malloc(size_t size, size_t minimum_alignment) { #if defined(__ANDROID__) || defined(OS_ANDROID) || defined(__ASYLO__) || defined(_WIN32) || defined(__sun) || defined(sun) # if defined(_WIN32) return _aligned_malloc(size, minimum_alignment); # else return memalign(minimum_alignment, size); # endif #else // !__ANDROID__ && !OS_ANDROID && !__ASYLO__ // posix_memalign requires that the requested alignment be at least // sizeof(void*). In this case, fall back on malloc which should return memory // aligned to at least the size of a pointer. const size_t required_alignment = sizeof(void*); if (minimum_alignment < required_alignment) return malloc(size); void *ptr = nullptr; if (posix_memalign(&ptr, minimum_alignment, size) == 0) return ptr; return nullptr; #endif } inline void aligned_free(void *aligned_memory) { free(aligned_memory); } #elif defined(_MSC_VER) // MSVC inline void *aligned_malloc(size_t size, size_t minimum_alignment) { return _aligned_malloc(size, minimum_alignment); } inline void aligned_free(void *aligned_memory) { _aligned_free(aligned_memory); } #endif // aligned_malloc, aligned_free // ALIGNED_CHAR_ARRAY // // Provides a char array with the exact same alignment as another type. The // first parameter must be a complete type, the second parameter is how many // of that type to provide space for. // // ALIGNED_CHAR_ARRAY(struct stat, 16) storage_; // #if defined(__cplusplus) #undef ALIGNED_CHAR_ARRAY // Because MSVC and older GCCs require that the argument to their alignment // construct to be a literal constant integer, we use a template instantiated // at all the possible powers of two. #ifndef SWIG template struct AlignType { }; template struct AlignType<0, size> { typedef char result[size]; }; #if defined(_MSC_VER) #define BASE_PORT_H_ALIGN_ATTRIBUTE(X) __declspec(align(X)) #define BASE_PORT_H_ALIGN_OF(T) __alignof(T) #elif defined(__GNUC__) || defined(__INTEL_COMPILER) #define BASE_PORT_H_ALIGN_ATTRIBUTE(X) __attribute__((aligned(X))) #define BASE_PORT_H_ALIGN_OF(T) __alignof__(T) #endif #if defined(BASE_PORT_H_ALIGN_ATTRIBUTE) #define BASE_PORT_H_ALIGNTYPE_TEMPLATE(X) \ template struct AlignType { \ typedef BASE_PORT_H_ALIGN_ATTRIBUTE(X) char result[size]; \ } BASE_PORT_H_ALIGNTYPE_TEMPLATE(1); BASE_PORT_H_ALIGNTYPE_TEMPLATE(2); BASE_PORT_H_ALIGNTYPE_TEMPLATE(4); BASE_PORT_H_ALIGNTYPE_TEMPLATE(8); BASE_PORT_H_ALIGNTYPE_TEMPLATE(16); BASE_PORT_H_ALIGNTYPE_TEMPLATE(32); BASE_PORT_H_ALIGNTYPE_TEMPLATE(64); BASE_PORT_H_ALIGNTYPE_TEMPLATE(128); BASE_PORT_H_ALIGNTYPE_TEMPLATE(256); BASE_PORT_H_ALIGNTYPE_TEMPLATE(512); BASE_PORT_H_ALIGNTYPE_TEMPLATE(1024); BASE_PORT_H_ALIGNTYPE_TEMPLATE(2048); BASE_PORT_H_ALIGNTYPE_TEMPLATE(4096); BASE_PORT_H_ALIGNTYPE_TEMPLATE(8192); // Any larger and MSVC++ will complain. #define ALIGNED_CHAR_ARRAY(T, Size) \ typename AlignType::result #undef BASE_PORT_H_ALIGNTYPE_TEMPLATE #undef BASE_PORT_H_ALIGN_ATTRIBUTE #else // defined(BASE_PORT_H_ALIGN_ATTRIBUTE) #define ALIGNED_CHAR_ARRAY \ you_must_define_ALIGNED_CHAR_ARRAY_for_your_compiler_in_base_port_h #endif // defined(BASE_PORT_H_ALIGN_ATTRIBUTE) #else // !SWIG // SWIG can't represent alignment and doesn't care about alignment on data // members (it works fine without it). template struct AlignType { typedef char result[Size]; }; #define ALIGNED_CHAR_ARRAY(T, Size) AlignType::result // Enough to parse with SWIG, will never be used by running code. #define BASE_PORT_H_ALIGN_OF(Type) 16 #endif // !SWIG #else // __cplusplus #define ALIGNED_CHAR_ARRAY ALIGNED_CHAR_ARRAY_is_not_available_without_Cplusplus #endif // __cplusplus #endif // S2_BASE_PORT_H_ s2/src/s2/base/commandlineflags.h0000644000176200001440000000276314122355625016314 0ustar liggesusers// Copyright Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef S2_BASE_COMMANDLINEFLAGS_H_ #define S2_BASE_COMMANDLINEFLAGS_H_ #ifdef S2_USE_GFLAGS #include #else // !defined(S2_USE_GFLAGS) #include #include "s2/base/integral_types.h" #define DEFINE_bool(name, default_value, description) \ bool FLAGS_##name = default_value #define DECLARE_bool(name) \ extern bool FLAGS_##name #define DEFINE_double(name, default_value, description) \ double FLAGS_##name = default_value #define DECLARE_double(name) \ extern double FLAGS_##name #define DEFINE_int32(name, default_value, description) \ int32 FLAGS_##name = default_value #define DECLARE_int32(name) \ extern int32 FLAGS_##name #define DEFINE_string(name, default_value, description) \ std::string FLAGS_##name = default_value #define DECLARE_string(name) \ extern std::string FLAGS_##name #endif // !defined(S2_USE_GFLAGS) #endif // S2_BASE_COMMANDLINEFLAGS_H_ s2/src/s2/base/strtoint.cc0000644000176200001440000000440713774601521015033 0ustar liggesusers// Copyright 2008 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // // Architecture-neutral plug compatible replacements for strtol() friends. // See strtoint.h for details on how to use this component. // #include #include #include #include "s2/base/integral_types.h" #include "s2/base/port.h" #include "s2/base/strtoint.h" // Replacement strto[u]l functions that have identical overflow and underflow // characteristics for both ILP-32 and LP-64 platforms, including errno // preservation for error-free calls. int32 strto32_adapter(const char *nptr, char **endptr, int base) { const int saved_errno = errno; errno = 0; const long result = strtol(nptr, endptr, base); if (errno == ERANGE && result == LONG_MIN) { return std::numeric_limits::min(); } else if (errno == ERANGE && result == LONG_MAX) { return std::numeric_limits::max(); } else if (errno == 0 && result < std::numeric_limits::min()) { errno = ERANGE; return std::numeric_limits::min(); } else if (errno == 0 && result > std::numeric_limits::max()) { errno = ERANGE; return std::numeric_limits::max(); } if (errno == 0) errno = saved_errno; return static_cast(result); } uint32 strtou32_adapter(const char *nptr, char **endptr, int base) { const int saved_errno = errno; errno = 0; const unsigned long result = strtoul(nptr, endptr, base); if (errno == ERANGE && result == ULONG_MAX) { return std::numeric_limits::max(); } else if (errno == 0 && result > std::numeric_limits::max()) { errno = ERANGE; return std::numeric_limits::max(); } if (errno == 0) errno = saved_errno; return static_cast(result); } s2/src/s2/s2padded_cell.h0000644000176200001440000001013614122355625014555 0ustar liggesusers// Copyright 2013 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_S2PADDED_CELL_H_ #define S2_S2PADDED_CELL_H_ #include "s2/_fp_contract_off.h" #include "s2/r2rect.h" #include "s2/s2cell_id.h" // S2PaddedCell represents an S2Cell whose (u,v)-range has been expanded on // all sides by a given amount of "padding". Unlike S2Cell, its methods and // representation are optimized for clipping edges against S2Cell boundaries // to determine which cells are intersected by a given set of edges. // // This class is intended to be copied by value as desired. class S2PaddedCell { public: // Construct an S2PaddedCell for the given cell id and padding. S2PaddedCell(S2CellId id, double padding); // Construct the child of "parent" with the given (i,j) index. The four // child cells have indices of (0,0), (0,1), (1,0), (1,1), where the i and j // indices correspond to increasing u- and v-values respectively. S2PaddedCell(const S2PaddedCell& parent, int i, int j); S2CellId id() const { return id_; } double padding() const { return padding_; } int level() const { return level_; } // Return the bound for this cell (including padding). const R2Rect& bound() const { return bound_; } // Return the "middle" of the padded cell, defined as the rectangle that // belongs to all four children. // // Note that this method is *not* thread-safe, because the return value is // computed on demand and cached. (It is expected that this class will be // mainly useful in the context of single-threaded recursive algorithms.) const R2Rect& middle() const; // Return the (i,j) coordinates for the child cell at the given traversal // position. The traversal position corresponds to the order in which child // cells are visited by the Hilbert curve. void GetChildIJ(int pos, int* i, int* j) const; // Return the smallest cell that contains all descendants of this cell whose // bounds intersect "rect". For algorithms that use recursive subdivision // to find the cells that intersect a particular object, this method can be // used to skip all the initial subdivision steps where only one child needs // to be expanded. // // Note that this method is not the same as returning the smallest cell that // contains the intersection of this cell with "rect". Because of the // padding, even if one child completely contains "rect" it is still // possible that a neighboring child also intersects "rect". // // REQUIRES: bound().Intersects(rect) S2CellId ShrinkToFit(const R2Rect& rect) const; // Return the center of this cell. S2Point GetCenter() const; // Return the vertex where the S2 space-filling curve enters this cell. S2Point GetEntryVertex() const; // Return the vertex where the S2 space-filling curve exits this cell. S2Point GetExitVertex() const; private: S2CellId id_; double padding_; R2Rect bound_; // Bound in (u,v)-space // The rectangle in (u,v)-space that belongs to all four padded children. // It is computed on demand by the middle() accessor method. mutable R2Rect middle_; int ij_lo_[2]; // Minimum (i,j)-coordinates of this cell, before padding int orientation_; // Hilbert curve orientation of this cell (see s2coords.h) int level_; // Level of this cell (see s2coords.h) }; ////////////////// Implementation details follow //////////////////// inline void S2PaddedCell::GetChildIJ(int pos, int* i, int* j) const { int ij = S2::internal::kPosToIJ[orientation_][pos]; *i = ij >> 1; *j = ij & 1; } #endif // S2_S2PADDED_CELL_H_ s2/src/s2/s2cell_union.h0000644000176200001440000004123614122355625014471 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_S2CELL_UNION_H_ #define S2_S2CELL_UNION_H_ #include #include "s2/base/commandlineflags.h" #include "s2/base/integral_types.h" #include "s2/base/logging.h" #include "s2/_fp_contract_off.h" #include "s2/s2cell_id.h" #include "s2/s2region.h" #include "absl/base/macros.h" class Decoder; class Encoder; class S1Angle; class S2Cap; class S2Cell; class S2LatLngRect; DECLARE_bool(s2debug); DECLARE_int32(s2cell_union_decode_max_num_cells); // An S2CellUnion is a region consisting of cells of various sizes. Typically // a cell union is used to approximate some other shape. There is a tradeoff // between the accuracy of the approximation and how many cells are used. // Unlike polygons, cells have a fixed hierarchical structure. This makes // them more suitable for optimizations based on preprocessing. // // An S2CellUnion is represented as a vector of sorted, non-overlapping // S2CellIds. By default the vector is also "normalized", meaning that groups // of 4 child cells have been replaced by their parent cell whenever possible. // S2CellUnions are not required to be normalized, but certain operations will // return different results if they are not (e.g., Contains(S2CellUnion).) // // S2CellUnion is movable and copyable. class S2CellUnion final : public S2Region { public: // Creates an empty cell union. S2CellUnion() {} // Constructs a cell union with the given S2CellIds, then calls Normalize() // to sort them, remove duplicates, and merge cells when possible. (See // FromNormalized if your vector is already normalized.) // // The argument is passed by value, so if you are passing a named variable // and have no further use for it, consider using std::move(). // // A cell union containing a single S2CellId may be constructed like this: // // S2CellUnion example({cell_id}); explicit S2CellUnion(std::vector cell_ids); // Convenience constructor that accepts a vector of uint64. Note that // unlike the constructor above, this one makes a copy of "cell_ids". explicit S2CellUnion(const std::vector& cell_ids); // Constructs a cell union for the whole sphere. static S2CellUnion WholeSphere(); // Constructs a cell union from S2CellIds that have already been normalized // (typically because they were extracted from another S2CellUnion). // // The argument is passed by value, so if you are passing a named variable // and have no further use for it, consider using std::move(). // // REQUIRES: "cell_ids" satisfies the requirements of IsNormalized(). static S2CellUnion FromNormalized(std::vector cell_ids); // Constructs a cell union from a vector of sorted, non-overlapping // S2CellIds. Unlike the other constructors, FromVerbatim does not require // that groups of 4 child cells have been replaced by their parent cell. In // other words, "cell_ids" must satisfy the requirements of IsValid() but // not necessarily IsNormalized(). // // Note that if the cell union is not normalized, certain operations may // return different results (e.g., Contains(S2CellUnion)). // // REQUIRES: "cell_ids" satisfies the requirements of IsValid(). static S2CellUnion FromVerbatim(std::vector cell_ids); // Constructs a cell union that corresponds to a continuous range of cell // ids. The output is a normalized collection of cell ids that covers the // leaf cells between "min_id" and "max_id" inclusive. // // REQUIRES: min_id.is_leaf(), max_id.is_leaf(), min_id <= max_id. static S2CellUnion FromMinMax(S2CellId min_id, S2CellId max_id); // Like FromMinMax() except that the union covers the range of leaf cells // from "begin" (inclusive) to "end" (exclusive), as with Python ranges or // STL iterator ranges. If (begin == end) the result is empty. // // REQUIRES: begin.is_leaf(), end.is_leaf(), begin <= end. static S2CellUnion FromBeginEnd(S2CellId begin, S2CellId end); // Init() methods corresponding to the constructors/factory methods above. // TODO(ericv): Consider deprecating these methods in favor of using the // constructors and move assignment operator. void Init(std::vector cell_ids); void Init(const std::vector& cell_ids); void InitFromMinMax(S2CellId min_id, S2CellId max_id); void InitFromBeginEnd(S2CellId begin, S2CellId end); // Clears the contents of the cell union and minimizes memory usage. void Clear(); // Gives ownership of the vector data to the client without copying, and // clears the content of the cell union. The original data in cell_ids // is lost if there was any. std::vector Release(); // Convenience methods for accessing the individual cell ids. int num_cells() const { return static_cast(cell_ids_.size()); } S2CellId cell_id(int i) const { return cell_ids_[i]; } // Vector-like methods for accessing the individual cell ids. size_t size() const { return cell_ids_.size(); } bool empty() const { return cell_ids_.empty(); } S2CellId operator[](int i) const { return cell_ids_[i]; } // Standard begin/end methods, to allow range-based for loops: // // for (S2CellId id : cell_union) { ... } std::vector::const_iterator begin() const; std::vector::const_iterator end() const; // Direct access to the underlying vector for STL algorithms. const std::vector& cell_ids() const { return cell_ids_; } // Returns true if the cell union is valid, meaning that the S2CellIds are // valid, non-overlapping, and sorted in increasing order. bool IsValid() const; // Returns true if the cell union is normalized, meaning that it is // satisfies IsValid() and that no four cells have a common parent. // Certain operations such as Contains(S2CellUnion) will return a different // result if the cell union is not normalized. bool IsNormalized() const; // Normalizes the cell union by discarding cells that are contained by other // cells, replacing groups of 4 child cells by their parent cell whenever // possible, and sorting all the cell ids in increasing order. // // Returns true if the number of cells was reduced. // TODO(ericv): Change this method to return void. bool Normalize(); // Replaces "output" with an expanded version of the cell union where any // cells whose level is less than "min_level" or where (level - min_level) // is not a multiple of "level_mod" are replaced by their children, until // either both of these conditions are satisfied or the maximum level is // reached. // // This method allows a covering generated by S2RegionCoverer using // min_level() or level_mod() constraints to be stored as a normalized cell // union (which allows various geometric computations to be done) and then // converted back to the original list of cell ids that satisfies the // desired constraints. void Denormalize(int min_level, int level_mod, std::vector* output) const; // If there are more than "excess" elements of the cell_ids() vector that // are allocated but unused, reallocates the array to eliminate the excess // space. This reduces memory usage when many cell unions need to be held // in memory at once. void Pack(int excess = 0); // Returns true if the cell union contains the given cell id. Containment // is defined with respect to regions, e.g. a cell contains its 4 children. // This is a fast operation (logarithmic in the size of the cell union). // // CAVEAT: If you have constructed a non-normalized S2CellUnion using // FromVerbatim, note that groups of 4 child cells are *not* considered to // contain their parent cell. To get this behavior you must use one of the // other constructors or call Normalize() explicitly. bool Contains(S2CellId id) const; // Returns true if the cell union intersects the given cell id. // This is a fast operation (logarithmic in the size of the cell union). bool Intersects(S2CellId id) const; // Returns true if this cell union contains the given other cell union. // // CAVEAT: If you have constructed a non-normalized S2CellUnion using // FromVerbatim, note that groups of 4 child cells are *not* considered to // contain their parent cell. To get this behavior you must use one of the // other constructors or call Normalize() explicitly. bool Contains(const S2CellUnion& y) const; // Returns true if this cell union intersects the given other cell union. bool Intersects(const S2CellUnion& y) const; // Returns the union of the two given cell unions. S2CellUnion Union(const S2CellUnion& y) const; // Returns the intersection of the two given cell unions. S2CellUnion Intersection(const S2CellUnion& y) const; // Specialized version of GetIntersection() that returns the intersection of // a cell union with an S2CellId. This can be useful for splitting a cell // union into pieces. S2CellUnion Intersection(S2CellId id) const; // Returns the difference of the two given cell unions. S2CellUnion Difference(const S2CellUnion& y) const; // Expands the cell union by adding a buffer of cells at "expand_level" // around the union boundary. // // For each cell "c" in the union, we add all neighboring cells at level // "expand_level" that are adjacent to "c". Note that there can be many // such cells if "c" is large compared to "expand_level". If "c" is smaller // than "expand_level", we first add the parent of "c" at "expand_level" and // then add all the neighbors of that cell. // // Note that the size of the output is exponential in "expand_level". For // example, if expand_level == 20 and the input has a cell at level 10, // there will be on the order of 4000 adjacent cells in the output. For // most applications the Expand(min_radius, max_level_diff) method below is // easier to use. void Expand(int expand_level); // Expands the cell union such that it contains all points whose distance to // the cell union is at most "min_radius", but do not use cells that are // more than "max_level_diff" levels higher than the largest cell in the // input. The second parameter controls the tradeoff between accuracy and // output size when a large region is being expanded by a small amount // (e.g. expanding Canada by 1km). For example, if max_level_diff == 4 the // region will always be expanded by approximately 1/16 the width of its // largest cell. Note that in the worst case, the number of cells in the // output can be up to 4 * (1 + 2 ** max_level_diff) times larger than the // number of cells in the input. void Expand(S1Angle min_radius, int max_level_diff); // The number of leaf cells covered by the union. // This will be no more than 6*2^60 for the whole sphere. uint64 LeafCellsCovered() const; // Approximates this cell union's area in steradians by summing the average // area of each contained cell's average area, using the AverageArea method // from the S2Cell class. This is equivalent to the number of leaves covered, // multiplied by the average area of a leaf. Note that AverageArea does not // take into account distortion of cell, and thus may be off by up to a // factor of up to 1.7. // // NOTE: Since this is proportional to LeafCellsCovered(), it is // always better to use that function if all you care about is // the relative average area between objects. double AverageBasedArea() const; // Calculates this cell union's area in steradians by summing the approximate // area for each contained cell, using the ApproxArea method from the S2Cell // class. double ApproxArea() const; // Calculates this cell union's area in steradians by summing the exact area // for each contained cell, using the Exact method from the S2Cell class. double ExactArea() const; // Return true if two cell unions are identical. friend bool operator==(const S2CellUnion& x, const S2CellUnion& y); // Return true if two cell unions are different. friend bool operator!=(const S2CellUnion& x, const S2CellUnion& y); //////////////////////////////////////////////////////////////////////// // S2Region interface (see s2region.h for details): S2CellUnion* Clone() const override; S2Cap GetCapBound() const override; S2LatLngRect GetRectBound() const override; // This is a fast operation (logarithmic in the size of the cell union). bool Contains(const S2Cell& cell) const override; // This is a fast operation (logarithmic in the size of the cell union). bool MayIntersect(const S2Cell& cell) const override; // The point 'p' does not need to be normalized. // This is a fast operation (logarithmic in the size of the cell union). bool Contains(const S2Point& p) const override; // Appends a serialized representation of the S2CellUnion to "encoder". // // REQUIRES: "encoder" uses the default constructor, so that its buffer // can be enlarged as necessary by calling Ensure(int). void Encode(Encoder* const encoder) const; // Decodes an S2CellUnion encoded with Encode(). Returns true on success. bool Decode(Decoder* const decoder); //////////////////////////////////////////////////////////////////////// // Static methods intended for high-performance clients that prefer to // manage their own storage. // Like Normalize(), but works with a vector of S2CellIds. // Equivalent to: // *cell_ids = S2CellUnion(std::move(*cell_ids)).Release(); static bool Normalize(std::vector* cell_ids); // Like Denormalize(), but works with a vector of S2CellIds. // REQUIRES: out != &in static void Denormalize(const std::vector& in, int min_level, int level_mod, std::vector* out); // Like GetIntersection(), but works directly with vectors of S2CellIds, // Equivalent to: // // *out = S2CellUnion(x).Intersection(S2CellUnion(y)).Release() // // except that this method has slightly more relaxed normalization // requirements: the input vectors may contain groups of 4 child cells that // all have the same parent. (In a normalized S2CellUnion, such groups are // always replaced by the parent cell.) static void GetIntersection(const std::vector& x, const std::vector& y, std::vector* out); private: friend class S2CellUnionTestPeer; // For creating invalid S2CellUnions. // Internal constructor that does not check "cell_ids" for validity. enum VerbatimFlag { VERBATIM }; S2CellUnion(std::vector cell_ids, VerbatimFlag verbatim) : cell_ids_(std::move(cell_ids)) {} // Converts a vector of uint64 to a vector of S2CellIds. static std::vector ToS2CellIds(const std::vector& ids); std::vector cell_ids_; }; ////////////////// Implementation details follow //////////////////// inline S2CellUnion::S2CellUnion(std::vector cell_ids) : cell_ids_(std::move(cell_ids)) { Normalize(); } inline S2CellUnion S2CellUnion::FromNormalized(std::vector cell_ids) { S2CellUnion result(std::move(cell_ids), VERBATIM); S2_DCHECK(result.IsNormalized()); return result; } inline S2CellUnion S2CellUnion::FromVerbatim(std::vector cell_ids) { S2CellUnion result(std::move(cell_ids), VERBATIM); S2_DCHECK(!FLAGS_s2debug || result.IsValid()); return result; } inline void S2CellUnion::Init(std::vector cell_ids) { cell_ids_ = std::move(cell_ids); Normalize(); } inline void S2CellUnion::Clear() { // swap() guarantees to reduce the RHS vector's size and capacity // to zero (as opposed to clear(), shrink_to_fit() sequence). std::vector().swap(cell_ids_); } inline std::vector S2CellUnion::Release() { // vector's rvalue reference constructor does not necessarily leave // moved-from value in empty state, so swap instead. std::vector cell_ids; cell_ids_.swap(cell_ids); return cell_ids; } inline std::vector::const_iterator S2CellUnion::begin() const { return cell_ids_.begin(); } inline std::vector::const_iterator S2CellUnion::end() const { return cell_ids_.end(); } #endif // S2_S2CELL_UNION_H_ s2/src/s2/s2latlng_rect.h0000644000176200001440000004573314122355625014646 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_S2LATLNG_RECT_H_ #define S2_S2LATLNG_RECT_H_ #include #include #include #include "s2/base/logging.h" #include "s2/_fp_contract_off.h" #include "s2/r1interval.h" #include "s2/s1angle.h" #include "s2/s1interval.h" #include "s2/s2latlng.h" #include "s2/s2region.h" class Decoder; class Encoder; class S2Cap; class S2Cell; // An S2LatLngRect represents a closed latitude-longitude rectangle. It is // capable of representing the empty and full rectangles as well as single // points. Note that the latitude-longitude space is considered to have a // *cylindrical* topology rather than a spherical one, i.e. the poles have // multiple lat/lng representations. An S2LatLngRect may be defined so that // includes some representations of a pole but not others. Use the // PolarClosure() method if you want to expand a rectangle so that it contains // all possible representations of any contained poles. // // Because S2LatLngRect uses S1Interval to store the longitude range, // longitudes of -180 degrees are treated specially. Except for empty // and full longitude spans, -180 degree longitudes will turn into +180 // degrees. This sign flip causes lng_lo() to be greater than lng_hi(), // indicating that the rectangle will wrap around through -180 instead of // through +179. Thus the math is consistent within the library, but the sign // flip can be surprising, especially when working with map projections where // -180 and +180 are at opposite ends of the flattened map. See the comments // on S1Interval for more details. // // This class is intended to be copied by value as desired. It uses // the default copy constructor and assignment operator, however it is // not a "plain old datatype" (POD) because it has virtual functions. class S2LatLngRect final : public S2Region { public: // Construct a rectangle from minimum and maximum latitudes and longitudes. // If lo.lng() > hi.lng(), the rectangle spans the 180 degree longitude // line. Both points must be normalized, with lo.lat() <= hi.lat(). // The rectangle contains all the points p such that 'lo' <= p <= 'hi', // where '<=' is defined in the obvious way. S2LatLngRect(const S2LatLng& lo, const S2LatLng& hi); // Construct a rectangle from latitude and longitude intervals. The two // intervals must either be both empty or both non-empty, and the latitude // interval must not extend outside [-90, +90] degrees. // Note that both intervals (and hence the rectangle) are closed. S2LatLngRect(const R1Interval& lat, const S1Interval& lng); // The default constructor creates an empty S2LatLngRect. S2LatLngRect(); // Construct a rectangle of the given size centered around the given point. // "center" needs to be normalized, but "size" does not. The latitude // interval of the result is clamped to [-90,90] degrees, and the longitude // interval of the result is Full() if and only if the longitude size is // 360 degrees or more. Examples of clamping (in degrees): // // center=(80,170), size=(40,60) -> lat=[60,90], lng=[140,-160] // center=(10,40), size=(210,400) -> lat=[-90,90], lng=[-180,180] // center=(-90,180), size=(20,50) -> lat=[-90,-80], lng=[155,-155] static S2LatLngRect FromCenterSize(const S2LatLng& center, const S2LatLng& size); // Construct a rectangle containing a single (normalized) point. static S2LatLngRect FromPoint(const S2LatLng& p); // Construct the minimal bounding rectangle containing the two given // normalized points. This is equivalent to starting with an empty // rectangle and calling AddPoint() twice. Note that it is different than // the S2LatLngRect(lo, hi) constructor, where the first point is always // used as the lower-left corner of the resulting rectangle. static S2LatLngRect FromPointPair(const S2LatLng& p1, const S2LatLng& p2); // Accessor methods. S1Angle lat_lo() const { return S1Angle::Radians(lat_.lo()); } S1Angle lat_hi() const { return S1Angle::Radians(lat_.hi()); } S1Angle lng_lo() const { return S1Angle::Radians(lng_.lo()); } S1Angle lng_hi() const { return S1Angle::Radians(lng_.hi()); } const R1Interval& lat() const { return lat_; } const S1Interval& lng() const { return lng_; } R1Interval *mutable_lat() { return &lat_; } S1Interval *mutable_lng() { return &lng_; } S2LatLng lo() const { return S2LatLng(lat_lo(), lng_lo()); } S2LatLng hi() const { return S2LatLng(lat_hi(), lng_hi()); } // The canonical empty and full rectangles, as derived from the Empty // and Full R1 and S1 Intervals. // Empty: lat_lo=1, lat_hi=0, lng_lo=Pi, lng_hi=-Pi (radians) static S2LatLngRect Empty(); // Full: lat_lo=-Pi/2, lat_hi=Pi/2, lng_lo=-Pi, lng_hi=Pi (radians) static S2LatLngRect Full(); // The full allowable range of latitudes and longitudes. static R1Interval FullLat() { return R1Interval(-M_PI_2, M_PI_2); } static S1Interval FullLng() { return S1Interval::Full(); } // Returns true if the rectangle is valid, which essentially just means // that the latitude bounds do not exceed Pi/2 in absolute value and // the longitude bounds do not exceed Pi in absolute value. Also, if // either the latitude or longitude bound is empty then both must be. bool is_valid() const; // Returns true if the rectangle is empty, i.e. it contains no points at all. bool is_empty() const; // Returns true if the rectangle is full, i.e. it contains all points. bool is_full() const; // Returns true if the rectangle is a point, i.e. lo() == hi() bool is_point() const; // Returns true if lng_.lo() > lng_.hi(), i.e. the rectangle crosses // the 180 degree longitude line. bool is_inverted() const { return lng_.is_inverted(); } // Returns the k-th vertex of the rectangle (k = 0,1,2,3) in CCW order // (lower left, lower right, upper right, upper left). For convenience, the // argument is reduced modulo 4 to the range [0..3]. S2LatLng GetVertex(int k) const; // Returns the center of the rectangle in latitude-longitude space // (in general this is not the center of the region on the sphere). S2LatLng GetCenter() const; // Returns the width and height of this rectangle in latitude-longitude // space. Empty rectangles have a negative width and height. S2LatLng GetSize() const; // Returns the surface area of this rectangle on the unit sphere. double Area() const; // Returns the true centroid of the rectangle multiplied by its surface area // (see s2centroids.h for details on centroids). The result is not unit // length, so you may want to normalize it. Note that in general the // centroid is *not* at the center of the rectangle, and in fact it may not // even be contained by the rectangle. (It is the "center of mass" of the // rectangle viewed as subset of the unit sphere, i.e. it is the point in // space about which this curved shape would rotate.) // // The reason for multiplying the result by the rectangle area is to make it // easier to compute the centroid of more complicated shapes. The centroid // of a union of disjoint regions can be computed simply by adding their // GetCentroid() results. S2Point GetCentroid() const; // More efficient version of Contains() that accepts a S2LatLng rather than // an S2Point. The argument must be normalized. bool Contains(const S2LatLng& ll) const; // Returns true if and only if the given point is contained in the interior // of the region (i.e. the region excluding its boundary). The point 'p' // does not need to be normalized. bool InteriorContains(const S2Point& p) const; // More efficient version of InteriorContains() that accepts a S2LatLng // rather than an S2Point. The argument must be normalized. bool InteriorContains(const S2LatLng& ll) const; // Returns true if and only if the rectangle contains the given other // rectangle. bool Contains(const S2LatLngRect& other) const; // Returns true if and only if the interior of this rectangle contains all // points of the given other rectangle (including its boundary). bool InteriorContains(const S2LatLngRect& other) const; // Returns true if this rectangle and the given other rectangle have any // points in common. bool Intersects(const S2LatLngRect& other) const; // Returns true if this rectangle intersects the given cell. (This is an // exact test and may be fairly expensive, see also MayIntersect below.) bool Intersects(const S2Cell& cell) const; // Returns true if and only if the interior of this rectangle intersects // any point (including the boundary) of the given other rectangle. bool InteriorIntersects(const S2LatLngRect& other) const; // Returns true if the boundary of this rectangle intersects the given // geodesic edge (v0, v1). bool BoundaryIntersects(const S2Point& v0, const S2Point& v1) const; // Increase the size of the bounding rectangle to include the given point. // The rectangle is expanded by the minimum amount possible. The S2LatLng // argument must be normalized. void AddPoint(const S2Point& p); void AddPoint(const S2LatLng& ll); // Returns a rectangle that has been expanded by margin.lat() on each side in // the latitude direction, and by margin.lng() on each side in the longitude // direction. If either margin is negative, then shrinks the rectangle on // the corresponding sides instead. The resulting rectangle may be empty. // // As noted above, the latitude-longitude space has the topology of a // cylinder. Longitudes "wrap around" at +/-180 degrees, while latitudes // are clamped to range [-90, 90]. This means that any expansion (positive // or negative) of the full longitude range remains full (since the // "rectangle" is actually a continuous band around the cylinder), while // expansion of the full latitude range remains full only if the margin is // positive. // // If either the latitude or longitude interval becomes empty after // expansion by a negative margin, the result is empty. // // Note that if an expanded rectangle contains a pole, it may not contain // all possible lat/lng representations of that pole (see header above). // Use the PolarClosure() method if you do not want this behavior. // // If you are trying to grow a rectangle by a certain *distance* on the // sphere (e.g. 5km), use the ExpandedByDistance() method instead. S2LatLngRect Expanded(const S2LatLng& margin) const; // If the rectangle does not include either pole, returns it unmodified. // Otherwise expands the longitude range to Full() so that the rectangle // contains all possible representations of the contained pole(s). S2LatLngRect PolarClosure() const; // Returns the smallest rectangle containing the union of this rectangle and // the given rectangle. S2LatLngRect Union(const S2LatLngRect& other) const; // Returns the smallest rectangle containing the intersection of this // rectangle and the given rectangle. Note that the region of intersection // may consist of two disjoint rectangles, in which case a single rectangle // spanning both of them is returned. S2LatLngRect Intersection(const S2LatLngRect& other) const; // Expands this rectangle so that it contains all points within the given // distance of the boundary, and return the smallest such rectangle. If the // distance is negative, then instead shrinks this rectangle so that it // excludes all points within the given absolute distance of the boundary, // and returns the largest such rectangle. // // Unlike Expanded(), this method treats the rectangle as a set of points on // the sphere, and measures distances on the sphere. For example, you can // use this method to find a rectangle that contains all points within 5km // of a given rectangle. Because this method uses the topology of the // sphere, note the following: // // - The full and empty rectangles have no boundary on the sphere. Any // expansion (positive or negative) of these rectangles leaves them // unchanged. // // - Any rectangle that covers the full longitude range does not have an // east or west boundary, therefore no expansion (positive or negative) // will occur in that direction. // // - Any rectangle that covers the full longitude range and also includes // a pole will not be expanded or contracted at that pole, because it // does not have a boundary there. // // - If a rectangle is within the given distance of a pole, the result will // include the full longitude range (because all longitudes are present // at the poles). // // Expansion and contraction are defined such that they are inverses whenver // possible, i.e. // // rect.ExpandedByDistance(x).ExpandedByDistance(-x) == rect // // (approximately), so long as the first operation does not cause a // rectangle boundary to disappear (i.e., the longitude range newly becomes // full or empty, or the latitude range expands to include a pole). S2LatLngRect ExpandedByDistance(S1Angle distance) const; // Returns the minimum distance (measured along the surface of the sphere) to // the given S2LatLngRect. Both S2LatLngRects must be non-empty. S1Angle GetDistance(const S2LatLngRect& other) const; // Returns the minimum distance (measured along the surface of the sphere) // from a given point to the rectangle (both its boundary and its interior). // The latlng must be valid. S1Angle GetDistance(const S2LatLng& p) const; // Returns the (directed or undirected) Hausdorff distance (measured along the // surface of the sphere) to the given S2LatLngRect. The directed Hausdorff // distance from rectangle A to rectangle B is given by // h(A, B) = max_{p in A} min_{q in B} d(p, q). // The Hausdorff distance between rectangle A and rectangle B is given by // H(A, B) = max{h(A, B), h(B, A)}. S1Angle GetDirectedHausdorffDistance(const S2LatLngRect& other) const; S1Angle GetHausdorffDistance(const S2LatLngRect& other) const; // Returns true if two rectangles contains the same set of points. bool operator==(const S2LatLngRect& other) const; // Returns the opposite of what operator == returns. bool operator!=(const S2LatLngRect& other) const; // Returns true if the latitude and longitude intervals of the two rectangles // are the same up to the given tolerance (see r1interval.h and s1interval.h // for details). bool ApproxEquals(const S2LatLngRect& other, S1Angle max_error = S1Angle::Radians(1e-15)) const; // ApproxEquals() with separate tolerances for latitude and longitude. bool ApproxEquals(const S2LatLngRect& other, const S2LatLng& max_error) const; //////////////////////////////////////////////////////////////////////// // S2Region interface (see s2region.h for details): S2LatLngRect* Clone() const override; S2Cap GetCapBound() const override; S2LatLngRect GetRectBound() const override; bool Contains(const S2Cell& cell) const override; // This test is cheap but is NOT exact. Use Intersects() if you want a more // accurate and more expensive test. Note that when this method is used by // an S2RegionCoverer, the accuracy isn't all that important since if a cell // may intersect the region then it is subdivided, and the accuracy of this // method goes up as the cells get smaller. bool MayIntersect(const S2Cell& cell) const override; // The point 'p' does not need to be normalized. bool Contains(const S2Point& p) const override; // Appends a serialized representation of the S2LatLngRect to "encoder". // // REQUIRES: "encoder" uses the default constructor, so that its buffer // can be enlarged as necessary by calling Ensure(int). void Encode(Encoder* const encoder) const; // Decodes an S2LatLngRect encoded with Encode(). Returns true on success. bool Decode(Decoder* const decoder); // Returns true if the edge AB intersects the given edge of constant // longitude. static bool IntersectsLngEdge(const S2Point& a, const S2Point& b, const R1Interval& lat, double lng); // Returns true if the edge AB intersects the given edge of constant // latitude. Requires the vectors to have unit length. static bool IntersectsLatEdge(const S2Point& a, const S2Point& b, double lat, const S1Interval& lng); private: // Helper function. See .cc for description. static S1Angle GetDirectedHausdorffDistance(double lng_diff, const R1Interval& a_lat, const R1Interval& b_lat); // Helper function. See .cc for description. static S1Angle GetInteriorMaxDistance(const R1Interval& a_lat, const S2Point& b); // Helper function. See .cc for description. static S2Point GetBisectorIntersection(const R1Interval& lat, double lng); R1Interval lat_; S1Interval lng_; }; inline S2LatLngRect::S2LatLngRect(const S2LatLng& lo, const S2LatLng& hi) : lat_(lo.lat().radians(), hi.lat().radians()), lng_(lo.lng().radians(), hi.lng().radians()) { S2_DLOG_IF(ERROR, !is_valid()) << "Invalid rect: " << lo << ", " << hi; } inline S2LatLngRect::S2LatLngRect(const R1Interval& lat, const S1Interval& lng) : lat_(lat), lng_(lng) { S2_DLOG_IF(ERROR, !is_valid()) << "Invalid rect: " << lat << ", " << lng; } inline S2LatLngRect::S2LatLngRect() : lat_(R1Interval::Empty()), lng_(S1Interval::Empty()) { } inline S2LatLngRect S2LatLngRect::Empty() { return S2LatLngRect(); } inline S2LatLngRect S2LatLngRect::Full() { return S2LatLngRect(FullLat(), FullLng()); } inline bool S2LatLngRect::is_valid() const { // The lat/lng ranges must either be both empty or both non-empty. return (std::fabs(lat_.lo()) <= M_PI_2 && std::fabs(lat_.hi()) <= M_PI_2 && lng_.is_valid() && lat_.is_empty() == lng_.is_empty()); } inline bool S2LatLngRect::is_empty() const { return lat_.is_empty(); } inline bool S2LatLngRect::is_full() const { return lat_ == FullLat() && lng_.is_full(); } inline bool S2LatLngRect::is_point() const { return lat_.lo() == lat_.hi() && lng_.lo() == lng_.hi(); } inline bool S2LatLngRect::operator==(const S2LatLngRect& other) const { return lat() == other.lat() && lng() == other.lng(); } inline bool S2LatLngRect::operator!=(const S2LatLngRect& other) const { return !operator==(other); } std::ostream& operator<<(std::ostream& os, const S2LatLngRect& r); #endif // S2_S2LATLNG_RECT_H_ s2/src/s2/s2coords.h0000644000176200001440000004265614122355625013642 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) // // This file contains documentation of the various coordinate systems used // throughout the library. Most importantly, S2 defines a framework for // decomposing the unit sphere into a hierarchy of "cells". Each cell is a // quadrilateral bounded by four geodesics. The top level of the hierarchy is // obtained by projecting the six faces of a cube onto the unit sphere, and // lower levels are obtained by subdividing each cell into four children // recursively. Cells are numbered such that sequentially increasing cells // follow a continuous space-filling curve over the entire sphere. The // transformation is designed to make the cells at each level fairly uniform // in size. // // ////////////////////////// S2Cell Decomposition ///////////////////////// // // The following methods define the cube-to-sphere projection used by // the S2Cell decomposition. // // In the process of converting a latitude-longitude pair to a 64-bit cell // id, the following coordinate systems are used: // // (id) // An S2CellId is a 64-bit encoding of a face and a Hilbert curve position // on that face. The Hilbert curve position implicitly encodes both the // position of a cell and its subdivision level (see s2cell_id.h). // // (face, i, j) // Leaf-cell coordinates. "i" and "j" are integers in the range // [0,(2**30)-1] that identify a particular leaf cell on the given face. // The (i, j) coordinate system is right-handed on each face, and the // faces are oriented such that Hilbert curves connect continuously from // one face to the next. // // (face, s, t) // Cell-space coordinates. "s" and "t" are real numbers in the range // [0,1] that identify a point on the given face. For example, the point // (s, t) = (0.5, 0.5) corresponds to the center of the top-level face // cell. This point is also a vertex of exactly four cells at each // subdivision level greater than zero. // // (face, si, ti) // Discrete cell-space coordinates. These are obtained by multiplying // "s" and "t" by 2**31 and rounding to the nearest unsigned integer. // Discrete coordinates lie in the range [0,2**31]. This coordinate // system can represent the edge and center positions of all cells with // no loss of precision (including non-leaf cells). In binary, each // coordinate of a level-k cell center ends with a 1 followed by // (30 - k) 0s. The coordinates of its edges end with (at least) // (31 - k) 0s. // // (face, u, v) // Cube-space coordinates in the range [-1,1]. To make the cells at each // level more uniform in size after they are projected onto the sphere, // we apply a nonlinear transformation of the form u=f(s), v=f(t). // The (u, v) coordinates after this transformation give the actual // coordinates on the cube face (modulo some 90 degree rotations) before // it is projected onto the unit sphere. // // (face, u, v, w) // Per-face coordinate frame. This is an extension of the (face, u, v) // cube-space coordinates that adds a third axis "w" in the direction of // the face normal. It is always a right-handed 3D coordinate system. // Cube-space coordinates can be converted to this frame by setting w=1, // while (u,v,w) coordinates can be projected onto the cube face by // dividing by w, i.e. (face, u/w, v/w). // // (x, y, z) // Direction vector (S2Point). Direction vectors are not necessarily unit // length, and are often chosen to be points on the biunit cube // [-1,+1]x[-1,+1]x[-1,+1]. They can be be normalized to obtain the // corresponding point on the unit sphere. // // (lat, lng) // Latitude and longitude (S2LatLng). Latitudes must be between -90 and // 90 degrees inclusive, and longitudes must be between -180 and 180 // degrees inclusive. // // Note that the (i, j), (s, t), (si, ti), and (u, v) coordinate systems are // right-handed on all six faces. #ifndef S2_S2COORDS_H_ #define S2_S2COORDS_H_ #include #include #include "s2/base/integral_types.h" #include "s2/base/logging.h" #include "s2/r2.h" #include "s2/s2coords_internal.h" #include "s2/s2point.h" #include "s2/util/math/mathutil.h" // S2 is a namespace for constants and simple utility functions that are used // throughout the S2 library. The name "S2" is derived from the mathematical // symbol for the two-dimensional unit sphere (note that the "2" refers to the // dimension of the surface, not the space it is embedded in). namespace S2 { // This is the number of levels needed to specify a leaf cell. This // constant is defined here so that the S2::Metric class and the conversion // functions below can be implemented without including s2cell_id.h. Please // see s2cell_id.h for other useful constants and conversion functions. const int kMaxCellLevel = 30; // The maximum index of a valid leaf cell plus one. The range of valid leaf // cell indices is [0..kLimitIJ-1]. const int kLimitIJ = 1 << kMaxCellLevel; // == S2CellId::kMaxSize // The maximum value of an si- or ti-coordinate. The range of valid (si,ti) // values is [0..kMaxSiTi]. unsigned const int kMaxSiTi = 1U << (kMaxCellLevel + 1); // Convert an s- or t-value to the corresponding u- or v-value. This is // a non-linear transformation from [-1,1] to [-1,1] that attempts to // make the cell sizes more uniform. double STtoUV(double s); // The inverse of the STtoUV transformation. Note that it is not always // true that UVtoST(STtoUV(x)) == x due to numerical errors. double UVtoST(double u); // Convert the i- or j-index of a leaf cell to the minimum corresponding s- // or t-value contained by that cell. The argument must be in the range // [0..2**30], i.e. up to one position beyond the normal range of valid leaf // cell indices. double IJtoSTMin(int i); // Return the i- or j-index of the leaf cell containing the given // s- or t-value. If the argument is outside the range spanned by valid // leaf cell indices, return the index of the closest valid leaf cell (i.e., // return values are clamped to the range of valid leaf cell indices). int STtoIJ(double s); // Convert an si- or ti-value to the corresponding s- or t-value. double SiTitoST(unsigned int si); // Return the si- or ti-coordinate that is nearest to the given s- or // t-value. The result may be outside the range of valid (si,ti)-values. unsigned int STtoSiTi(double s); // Convert (face, u, v) coordinates to a direction vector (not // necessarily unit length). S2Point FaceUVtoXYZ(int face, double u, double v); S2Point FaceUVtoXYZ(int face, const R2Point& uv); // If the dot product of p with the given face normal is positive, // set the corresponding u and v values (which may lie outside the range // [-1,1]) and return true. Otherwise return false. bool FaceXYZtoUV(int face, const S2Point& p, double* pu, double* pv); bool FaceXYZtoUV(int face, const S2Point& p, R2Point* puv); // Given a *valid* face for the given point p (meaning that dot product // of p with the face normal is positive), return the corresponding // u and v values (which may lie outside the range [-1,1]). void ValidFaceXYZtoUV(int face, const S2Point& p, double* pu, double* pv); void ValidFaceXYZtoUV(int face, const S2Point& p, R2Point* puv); // Transform the given point P to the (u,v,w) coordinate frame of the given // face (where the w-axis represents the face normal). S2Point FaceXYZtoUVW(int face, const S2Point& p); // Return the face containing the given direction vector. (For points on // the boundary between faces, the result is arbitrary but repeatable.) int GetFace(const S2Point& p); // Convert a direction vector (not necessarily unit length) to // (face, u, v) coordinates. int XYZtoFaceUV(const S2Point& p, double* pu, double* pv); int XYZtoFaceUV(const S2Point& p, R2Point* puv); // Convert a direction vector (not necessarily unit length) to // (face, si, ti) coordinates and, if p is exactly equal to the center of a // cell, return the level of this cell (-1 otherwise). int XYZtoFaceSiTi(const S2Point& p, int* face, unsigned int* si, unsigned int* ti); // Convert (face, si, ti) coordinates to a direction vector (not necessarily // unit length). S2Point FaceSiTitoXYZ(int face, unsigned int si, unsigned int ti); // Return the right-handed normal (not necessarily unit length) for an // edge in the direction of the positive v-axis at the given u-value on // the given face. (This vector is perpendicular to the plane through // the sphere origin that contains the given edge.) S2Point GetUNorm(int face, double u); // Return the right-handed normal (not necessarily unit length) for an // edge in the direction of the positive u-axis at the given v-value on // the given face. S2Point GetVNorm(int face, double v); // Return the unit-length normal, u-axis, or v-axis for the given face. S2Point GetNorm(int face); S2Point GetUAxis(int face); S2Point GetVAxis(int face); // Return the given axis of the given face (u=0, v=1, w=2). S2Point GetUVWAxis(int face, int axis); // With respect to the (u,v,w) coordinate system of a given face, return the // face that lies in the given direction (negative=0, positive=1) of the // given axis (u=0, v=1, w=2). For example, GetUVWFace(4, 0, 1) returns the // face that is adjacent to face 4 in the positive u-axis direction. int GetUVWFace(int face, int axis, int direction); ////////////////// Implementation details follow //////////////////// // We have implemented three different projections from cell-space (s,t) to // cube-space (u,v): linear, quadratic, and tangent. They have the following // tradeoffs: // // Linear - This is the fastest transformation, but also produces the least // uniform cell sizes. Cell areas vary by a factor of about 5.2, with the // largest cells at the center of each face and the smallest cells in // the corners. // // Tangent - Transforming the coordinates via atan() makes the cell sizes // more uniform. The areas vary by a maximum ratio of 1.4 as opposed to a // maximum ratio of 5.2. However, each call to atan() is about as expensive // as all of the other calculations combined when converting from points to // cell ids, i.e. it reduces performance by a factor of 3. // // Quadratic - This is an approximation of the tangent projection that // is much faster and produces cells that are almost as uniform in size. // It is about 3 times faster than the tangent projection for converting // cell ids to points or vice versa. Cell areas vary by a maximum ratio of // about 2.1. // // Here is a table comparing the cell uniformity using each projection. "Area // ratio" is the maximum ratio over all subdivision levels of the largest cell // area to the smallest cell area at that level, "edge ratio" is the maximum // ratio of the longest edge of any cell to the shortest edge of any cell at // the same level, and "diag ratio" is the ratio of the longest diagonal of // any cell to the shortest diagonal of any cell at the same level. "ToPoint" // and "FromPoint" are the times in microseconds required to convert cell ids // to and from points (unit vectors) respectively. "ToPointRaw" is the time // to convert to a non-unit-length vector, which is all that is needed for // some purposes. // // Area Edge Diag ToPointRaw ToPoint FromPoint // Ratio Ratio Ratio (microseconds) // ------------------------------------------------------------------- // Linear: 5.200 2.117 2.959 0.020 0.087 0.085 // Tangent: 1.414 1.414 1.704 0.237 0.299 0.258 // Quadratic: 2.082 1.802 1.932 0.033 0.096 0.108 // // The worst-case cell aspect ratios are about the same with all three // projections. The maximum ratio of the longest edge to the shortest edge // within the same cell is about 1.4 and the maximum ratio of the diagonals // within the same cell is about 1.7. // // This data was produced using s2cell_test and s2cell_id_test. #define S2_LINEAR_PROJECTION 0 #define S2_TAN_PROJECTION 1 #define S2_QUADRATIC_PROJECTION 2 #define S2_PROJECTION S2_QUADRATIC_PROJECTION #if S2_PROJECTION == S2_LINEAR_PROJECTION inline double STtoUV(double s) { return 2 * s - 1; } inline double UVtoST(double u) { return 0.5 * (u + 1); } #elif S2_PROJECTION == S2_TAN_PROJECTION inline double STtoUV(double s) { // Unfortunately, tan(M_PI_4) is slightly less than 1.0. This isn't due to // a flaw in the implementation of tan(), it's because the derivative of // tan(x) at x=pi/4 is 2, and it happens that the two adjacent floating // point numbers on either side of the infinite-precision value of pi/4 have // tangents that are slightly below and slightly above 1.0 when rounded to // the nearest double-precision result. s = std::tan(M_PI_2 * s - M_PI_4); return s + (1.0 / (int64{1} << 53)) * s; } inline double UVtoST(double u) { volatile double a = std::atan(u); return (2 * M_1_PI) * (a + M_PI_4); } #elif S2_PROJECTION == S2_QUADRATIC_PROJECTION inline double STtoUV(double s) { if (s >= 0.5) return (1/3.) * (4*s*s - 1); else return (1/3.) * (1 - 4*(1-s)*(1-s)); } inline double UVtoST(double u) { if (u >= 0) return 0.5 * std::sqrt(1 + 3*u); else return 1 - 0.5 * std::sqrt(1 - 3*u); } #else #error Unknown value for S2_PROJECTION #endif inline double IJtoSTMin(int i) { S2_DCHECK(i >= 0 && i <= kLimitIJ); return (1.0 / kLimitIJ) * i; } inline int STtoIJ(double s) { return std::max(0, std::min(kLimitIJ - 1, MathUtil::FastIntRound(kLimitIJ * s - 0.5))); } inline double SiTitoST(unsigned int si) { S2_DCHECK_LE(si, kMaxSiTi); return (1.0 / kMaxSiTi) * si; } inline unsigned int STtoSiTi(double s) { // kMaxSiTi == 2^31, so the result doesn't fit in an int32 when s == 1. return static_cast(MathUtil::FastInt64Round(s * kMaxSiTi)); } inline S2Point FaceUVtoXYZ(int face, double u, double v) { switch (face) { case 0: return S2Point( 1, u, v); case 1: return S2Point(-u, 1, v); case 2: return S2Point(-u, -v, 1); case 3: return S2Point(-1, -v, -u); case 4: return S2Point( v, -1, -u); default: return S2Point( v, u, -1); } } inline S2Point FaceUVtoXYZ(int face, const R2Point& uv) { return FaceUVtoXYZ(face, uv[0], uv[1]); } inline void ValidFaceXYZtoUV(int face, const S2Point& p, double* pu, double* pv) { S2_DCHECK_GT(p.DotProd(GetNorm(face)), 0); switch (face) { case 0: *pu = p[1] / p[0]; *pv = p[2] / p[0]; break; case 1: *pu = -p[0] / p[1]; *pv = p[2] / p[1]; break; case 2: *pu = -p[0] / p[2]; *pv = -p[1] / p[2]; break; case 3: *pu = p[2] / p[0]; *pv = p[1] / p[0]; break; case 4: *pu = p[2] / p[1]; *pv = -p[0] / p[1]; break; default: *pu = -p[1] / p[2]; *pv = -p[0] / p[2]; break; } } inline void ValidFaceXYZtoUV(int face, const S2Point& p, R2Point* puv) { ValidFaceXYZtoUV(face, p, &(*puv)[0], &(*puv)[1]); } inline int GetFace(const S2Point& p) { int face = p.LargestAbsComponent(); if (p[face] < 0) face += 3; return face; } inline int XYZtoFaceUV(const S2Point& p, double* pu, double* pv) { int face = GetFace(p); ValidFaceXYZtoUV(face, p, pu, pv); return face; } inline int XYZtoFaceUV(const S2Point& p, R2Point* puv) { return XYZtoFaceUV(p, &(*puv)[0], &(*puv)[1]); } inline bool FaceXYZtoUV(int face, const S2Point& p, double* pu, double* pv) { if (face < 3) { if (p[face] <= 0) return false; } else { if (p[face-3] >= 0) return false; } ValidFaceXYZtoUV(face, p, pu, pv); return true; } inline bool FaceXYZtoUV(int face, const S2Point& p, R2Point* puv) { return FaceXYZtoUV(face, p, &(*puv)[0], &(*puv)[1]); } inline S2Point GetUNorm(int face, double u) { switch (face) { case 0: return S2Point( u, -1, 0); case 1: return S2Point( 1, u, 0); case 2: return S2Point( 1, 0, u); case 3: return S2Point(-u, 0, 1); case 4: return S2Point( 0, -u, 1); default: return S2Point( 0, -1, -u); } } inline S2Point GetVNorm(int face, double v) { switch (face) { case 0: return S2Point(-v, 0, 1); case 1: return S2Point( 0, -v, 1); case 2: return S2Point( 0, -1, -v); case 3: return S2Point( v, -1, 0); case 4: return S2Point( 1, v, 0); default: return S2Point( 1, 0, v); } } inline S2Point GetNorm(int face) { return GetUVWAxis(face, 2); } inline S2Point GetUAxis(int face) { return GetUVWAxis(face, 0); } inline S2Point GetVAxis(int face) { return GetUVWAxis(face, 1); } inline S2Point GetUVWAxis(int face, int axis) { const double* p = internal::kFaceUVWAxes[face][axis]; return S2Point(p[0], p[1], p[2]); } inline int GetUVWFace(int face, int axis, int direction) { S2_DCHECK(face >= 0 && face <= 5); S2_DCHECK(axis >= 0 && axis <= 2); S2_DCHECK(direction >= 0 && direction <= 1); return internal::kFaceUVWFaces[face][axis][direction]; } } // namespace S2 #endif // S2_S2COORDS_H_ s2/src/s2/s2region_intersection.h0000644000176200001440000000522314122355625016407 0ustar liggesusers// Copyright 2006 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #ifndef S2_S2REGION_INTERSECTION_H_ #define S2_S2REGION_INTERSECTION_H_ #include #include #include "s2/base/logging.h" #include "s2/_fp_contract_off.h" #include "s2/s2region.h" #include "absl/base/macros.h" class Decoder; class Encoder; class S2Cap; class S2Cell; class S2LatLngRect; // An S2RegionIntersection represents the intersection of a set of regions. // It is convenient for computing a covering of the intersection of a set of // regions. class S2RegionIntersection final : public S2Region { public: // Creates an empty intersection that should be initialized by calling Init(). // Note: an intersection of no regions covers the entire sphere. S2RegionIntersection() = default; // Create a region representing the intersection of the given regions. explicit S2RegionIntersection(std::vector> regions); ~S2RegionIntersection() override = default; // Initialize region by taking ownership of the given regions. void Init(std::vector> regions); // Releases ownership of the regions of this intersection and returns them, // leaving this region empty. std::vector> Release(); // Accessor methods. int num_regions() const { return regions_.size(); } const S2Region* region(int i) const { return regions_[i].get(); } //////////////////////////////////////////////////////////////////////// // S2Region interface (see s2region.h for details): S2RegionIntersection* Clone() const override; S2Cap GetCapBound() const override; S2LatLngRect GetRectBound() const override; bool Contains(const S2Point& p) const override; bool Contains(const S2Cell& cell) const override; bool MayIntersect(const S2Cell& cell) const override; private: // Internal copy constructor used only by Clone() that makes a deep copy of // its argument. S2RegionIntersection(const S2RegionIntersection& src); std::vector> regions_; void operator=(const S2RegionIntersection&) = delete; }; #endif // S2_S2REGION_INTERSECTION_H_ s2/src/s2/s2region.h0000644000176200001440000001355514122355625013630 0ustar liggesusers// Copyright 2005 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #ifndef S2_S2REGION_H_ #define S2_S2REGION_H_ #include #include "s2/_fp_contract_off.h" #include "s2/s1angle.h" class Decoder; class Encoder; class S2Cap; class S2Cell; class S2CellId; class S2LatLngRect; // An S2Region represents a two-dimensional region over the unit sphere. // It is an abstract interface with various concrete subtypes. // // The main purpose of this interface is to allow complex regions to be // approximated as simpler regions. So rather than having a wide variety // of virtual methods that are implemented by all subtypes, the interface // is restricted to methods that are useful for computing approximations. class S2Region { public: S2Region() = default; S2Region(const S2Region& other) = default; S2Region& operator=(const S2Region&) = default; virtual ~S2Region() {} // Returns a deep copy of the region. // // Note that each subtype of S2Region returns a pointer to an object of its // own type (e.g., S2Cap::Clone() returns an S2Cap*). virtual S2Region* Clone() const = 0; // Returns a bounding spherical cap that contains the region. The bound may // not be tight. virtual S2Cap GetCapBound() const = 0; // Returns a bounding latitude-longitude rectangle that contains the region. // The bound may not be tight. virtual S2LatLngRect GetRectBound() const = 0; // Returns a small collection of S2CellIds whose union covers the region. // The cells are not sorted, may have redundancies (such as cells that // contain other cells), and may cover much more area than necessary. // // This method is not intended for direct use by client code. Clients // should typically use S2RegionCoverer::GetCovering, which has options to // control the size and accuracy of the covering. Alternatively, if you // want a fast covering and don't care about accuracy, consider calling // S2RegionCoverer::GetFastCovering (which returns a cleaned-up version of // the covering computed by this method). // // GetCellUnionBound() implementations should attempt to return a small // covering (ideally 4 cells or fewer) that covers the region and can be // computed quickly. The result is used by S2RegionCoverer as a starting // point for further refinement. // // TODO(ericv): Remove the default implementation. virtual void GetCellUnionBound(std::vector *cell_ids) const; // Returns true if the region completely contains the given cell, otherwise // returns false. virtual bool Contains(const S2Cell& cell) const = 0; // If this method returns false, the region does not intersect the given // cell. Otherwise, either region intersects the cell, or the intersection // relationship could not be determined. // // Note that there is currently exactly one implementation of this method // (S2LatLngRect::MayIntersect) that takes advantage of the semantics above // to be more efficient. For all other S2Region subtypes, this method // returns true if the region intersect the cell and false otherwise. virtual bool MayIntersect(const S2Cell& cell) const = 0; // Returns true if and only if the given point is contained by the region. // The point 'p' is generally required to be unit length, although some // subtypes may relax this restriction. virtual bool Contains(const S2Point& p) const = 0; ////////////////////////////////////////////////////////////////////////// // Many S2Region subtypes also define the following non-virtual methods. ////////////////////////////////////////////////////////////////////////// // Appends a serialized representation of the region to "encoder". // // The representation chosen is left up to the sub-classes but it should // satisfy the following constraints: // - It should encode a version number. // - It should be deserializable using the corresponding Decode method. // - Performance, not space, should be the chief consideration. Encode() and // Decode() should be implemented such that the combination is equivalent // to calling Clone(). // // REQUIRES: "encoder" uses the default constructor, so that its buffer // can be enlarged as necessary by calling Ensure(int). // // void Encode(Encoder* const encoder) const; // Decodes an S2Region encoded with Encode(). Note that this method // requires that an S2Region object of the appropriate concrete type has // already been constructed. It is not possible to decode regions of // unknown type. // // Whenever the Decode method is changed to deal with new serialized // representations, it should be done so in a manner that allows for // older versions to be decoded i.e. the version number in the serialized // representation should be used to decide how to decode the data. // // Returns true on success. // // bool Decode(Decoder* const decoder); // Provides the same functionality as Decode, except that decoded regions // are allowed to point directly into the Decoder's memory buffer rather // than copying the data. This method can be much faster for regions that // have a lot of data (such as polygons), but the decoded region is only // valid within the scope (lifetime) of the Decoder's memory buffer. // // bool DecodeWithinScope(Decoder* const decoder); }; #endif // S2_S2REGION_H_ s2/src/s2/s2projections.h0000644000176200001440000001634614122355625014705 0ustar liggesusers// Copyright 2017 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) // // Defines a few simple map projections. (Clients that need more complex // projections should use a third-party library such as GeographicLib to // implement their own projection subtypes.) #ifndef S2_S2PROJECTIONS_H_ #define S2_S2PROJECTIONS_H_ #include "s2/r2.h" #include "s2/s2latlng.h" #include "s2/s2point.h" namespace S2 { // For the purposes of the S2 library, a projection is a function that maps // between S2Points and R2Points. It can also define the coordinate wrapping // behavior along each axis. class Projection { public: virtual ~Projection() {} // Converts a point on the sphere to a projected 2D point. virtual R2Point Project(const S2Point& p) const = 0; // Converts a projected 2D point to a point on the sphere. // // If wrapping is defined for a given axis (see below), then this method // should accept any real number for the corresponding coordinate. virtual S2Point Unproject(const R2Point& p) const = 0; // Convenience function equivalent to Project(ll.ToPoint()), but the // implementation may be more efficient. virtual R2Point FromLatLng(const S2LatLng& ll) const = 0; // Convenience function equivalent to S2LatLng(Unproject(p)), but the // implementation may be more efficient. virtual S2LatLng ToLatLng(const R2Point& p) const = 0; // Returns the point obtained by interpolating the given fraction of the // distance along the line from A to B. Almost all projections should // use the default implementation of this method, which simply interpolates // linearly in R2 space. Fractions < 0 or > 1 result in extrapolation // instead. // // The only reason to override this method is if you want edges to be // defined as something other than straight lines in the 2D projected // coordinate system. For example, using a third-party library such as // GeographicLib you could define edges as geodesics over an ellipsoid model // of the Earth. (Note that very few data sets define edges this way.) // // Also note that there is no reason to define a projection where edges are // geodesics over the sphere, because this is the native S2 interpretation. virtual R2Point Interpolate(double f, const R2Point& a, const R2Point& b) const; // Defines the coordinate wrapping distance along each axis. If this value // is non-zero for a given axis, the coordinates are assumed to "wrap" with // the given period. For example, if wrap_distance.y() == 360 then (x, y) // and (x, y + 360) should map to the same S2Point. // // This information is used to ensure that edges takes the shortest path // between two given points. For example, if coordinates represent // (latitude, longitude) pairs in degrees and wrap_distance().y() == 360, // then the edge (5:179, 5:-179) would be interpreted as spanning 2 degrees // of longitude rather than 358 degrees. // // If a given axis does not wrap, its wrap_distance should be set to zero. virtual R2Point wrap_distance() const = 0; // Helper function that wraps the coordinates of B if necessary in order to // obtain the shortest edge AB. For example, suppose that A = [170, 20], // B = [-170, 20], and the projection wraps so that [x, y] == [x + 360, y]. // Then this function would return [190, 20] for point B (reducing the edge // length in the "x" direction from 340 to 20). R2Point WrapDestination(const R2Point& a, const R2Point& b) const; }; // PlateCarreeProjection defines the "plate carree" (square plate) projection, // which converts points on the sphere to (longitude, latitude) pairs. // Coordinates can be scaled so that they represent radians, degrees, etc, but // the projection is always centered around (latitude=0, longitude=0). // // Note that (x, y) coordinates are backwards compared to the usual (latitude, // longitude) ordering, in order to match the usual convention for graphs in // which "x" is horizontal and "y" is vertical. class PlateCarreeProjection final : public Projection { public: // Constructs the plate carree projection where the x coordinates // (longitude) span [-x_scale, x_scale] and the y coordinates (latitude) // span [-x_scale/2, x_scale/2]. For example if x_scale==180 then the x // range is [-180, 180] and the y range is [-90, 90]. // // By default coordinates are expressed in radians, i.e. the x range is // [-Pi, Pi] and the y range is [-Pi/2, Pi/2]. explicit PlateCarreeProjection(double x_scale = M_PI); R2Point Project(const S2Point& p) const override; S2Point Unproject(const R2Point& p) const override; R2Point FromLatLng(const S2LatLng& ll) const override; S2LatLng ToLatLng(const R2Point& p) const override; R2Point wrap_distance() const override; private: double x_wrap_; double to_radians_; // Multiplier to convert coordinates to radians. double from_radians_; // Multiplier to convert coordinates from radians. }; // MercatorProjection defines the spherical Mercator projection. Google Maps // uses this projection together with WGS84 coordinates, in which case it is // known as the "Web Mercator" projection (see Wikipedia). This class makes // no assumptions regarding the coordinate system of its input points, but // simply applies the spherical Mercator projection to them. // // The Mercator projection is finite in width (x) but infinite in height (y). // "x" corresponds to longitude, and spans a finite range such as [-180, 180] // (with coordinate wrapping), while "y" is a function of latitude and spans // an infinite range. (As "y" coordinates get larger, points get closer to // the north pole but never quite reach it.) The north and south poles have // infinite "y" values. (Note that this will cause problems if you tessellate // a Mercator edge where one endpoint is a pole. If you need to do this, clip // the edge first so that the "y" coordinate is no more than about 5 * max_x.) class MercatorProjection final : public Projection { public: // Constructs a Mercator projection where "x" corresponds to longitude in // the range [-max_x, max_x] , and "y" corresponds to latitude and can be // any real number. The horizontal and vertical scales are equal locally. explicit MercatorProjection(double max_x); R2Point Project(const S2Point& p) const override; S2Point Unproject(const R2Point& p) const override; R2Point FromLatLng(const S2LatLng& ll) const override; S2LatLng ToLatLng(const R2Point& p) const override; R2Point wrap_distance() const override; private: double x_wrap_; double to_radians_; // Multiplier to convert coordinates to radians. double from_radians_; // Multiplier to convert coordinates from radians. }; } // namespace S2 #endif // S2_S2PROJECTIONS_H_ s2/src/s2/s2builderutil_s2polyline_vector_layer.cc0000644000176200001440000000676413774601521021772 0ustar liggesusers// Copyright 2016 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS-IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // // Author: ericv@google.com (Eric Veach) #include "s2/s2builderutil_s2polyline_vector_layer.h" #include using std::unique_ptr; using std::vector; using EdgeType = S2Builder::EdgeType; using Graph = S2Builder::Graph; using GraphOptions = S2Builder::GraphOptions; using Label = S2Builder::Label; using DegenerateEdges = GraphOptions::DegenerateEdges; using DuplicateEdges = GraphOptions::DuplicateEdges; using SiblingPairs = GraphOptions::SiblingPairs; using EdgeId = Graph::EdgeId; namespace s2builderutil { S2PolylineVectorLayer::S2PolylineVectorLayer( vector>* polylines, const S2PolylineVectorLayer::Options& options) { Init(polylines, nullptr, nullptr, options); } S2PolylineVectorLayer::S2PolylineVectorLayer( vector>* polylines, LabelSetIds* label_set_ids, IdSetLexicon* label_set_lexicon, const Options& options) { Init(polylines, label_set_ids, label_set_lexicon, options); } void S2PolylineVectorLayer::Init(vector>* polylines, LabelSetIds* label_set_ids, IdSetLexicon* label_set_lexicon, const Options& options) { S2_DCHECK_EQ(label_set_ids == nullptr, label_set_lexicon == nullptr); polylines_ = polylines; label_set_ids_ = label_set_ids; label_set_lexicon_ = label_set_lexicon; options_ = options; } GraphOptions S2PolylineVectorLayer::graph_options() const { return GraphOptions(options_.edge_type(), DegenerateEdges::DISCARD, options_.duplicate_edges(), options_.sibling_pairs()); } void S2PolylineVectorLayer::Build(const Graph& g, S2Error* error) { vector edge_polylines = g.GetPolylines( options_.polyline_type()); polylines_->reserve(edge_polylines.size()); if (label_set_ids_) label_set_ids_->reserve(edge_polylines.size()); vector vertices; // Temporary storage for vertices. vector